XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

AshfallFrontier

【Unity】灰烬边境,一款生存策略游戏

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
using System.Linq;
using Ashfall.Core;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Ashfall.Runtime
{
    /// <summary>Per-floor physics mirrors. No visual visibility toggle disables simulation.</summary>
    public sealed class FloorPhysics : IDisposable
    {
        private sealed class Entry
        {
            public Scene scene;
            public PhysicsScene2D physics;
            public GameObject root;
            public int revision = -1;
        }
        private readonly Dictionary<string, Entry> entries = new Dictionary<string, Entry>();
        private readonly string scenePrefix = "Physics." + Guid.NewGuid().ToString("N") + ".";
        private int nextScene;
        private float accumulator;
        public void Synchronize(WorldState world, int revision)
        {
            var active = new HashSet<string>(world.players.Where(p => p.connected).Select(p => p.location.spaceId + ":" + p.location.floor));
            active.Add("world:0");
            foreach (var space in world.spaces) foreach (var floor in space.floors)
            {
                string key = space.id + ":" + floor.index; if (!active.Contains(key)) continue;
                if (!entries.TryGetValue(key, out var entry))
                {
                    var scene = SceneManager.CreateScene(scenePrefix + nextScene++ + "." + key, new CreateSceneParameters(LocalPhysicsMode.Physics2D));
                    entry = new Entry { scene = scene, physics = scene.GetPhysicsScene2D() }; entries.Add(key, entry);
                }
                if (entry.revision == revision) continue;
                if (entry.root != null) UnityEngine.Object.Destroy(entry.root);
                entry.root = new GameObject(key + ".collisions"); SceneManager.MoveGameObjectToScene(entry.root, entry.scene); entry.revision = revision;
                foreach (var wall in floor.walls.Where(w => !w.open)) AddBox(entry.root, wall.id, wall.bounds);
                foreach (var piece in world.pieces.Where(b => b.kind == PieceKind.Wall && b.location.spaceId == space.id && b.location.floor == floor.index))
                    AddBox(entry.root, piece.id, new Box2(piece.location.position, piece.rotation % 2 == 0 ? new V2(1, .18f) : new V2(.18f, 1)));
                if (space.kind != SpaceKind.Exterior)
                {
                    var b = floor.bounds; float x = b.size.x / 2, y = b.size.y / 2;
                    AddBox(entry.root, "north", new Box2(new V2(0, y + .25f), new V2(x * 2, .5f)));
                    AddBox(entry.root, "south", new Box2(new V2(0, -y - .25f), new V2(x * 2, .5f)));
                    AddBox(entry.root, "east", new Box2(new V2(x + .25f, 0), new V2(.5f, y * 2)));
                    AddBox(entry.root, "west", new Box2(new V2(-x - .25f, 0), new V2(.5f, y * 2)));
                }
            }
            foreach (var key in entries.Keys.Where(k => !active.Contains(k)).ToArray())
            { SceneManager.UnloadSceneAsync(entries[key].scene); entries.Remove(key); }
        }
        private static void AddBox(GameObject root, string name, Box2 box)
        {
            var go = new GameObject(name); go.transform.SetParent(root.transform);
            go.transform.localPosition = new Vector3(box.center.x, box.center.y, 0);
            var collider = go.AddComponent<BoxCollider2D>(); collider.size = new Vector2(box.size.x, box.size.y);
        }
        public void Simulate(float dt)
        {
            accumulator += Mathf.Min(dt, .1f);
            while (accumulator >= GameSimulation.TickSeconds)
            { accumulator -= GameSimulation.TickSeconds; foreach (var entry in entries.Values) entry.physics.Simulate(GameSimulation.TickSeconds); }
        }
        public bool Blocked(Location l, float radius)
        { return entries.TryGetValue(l.spaceId + ":" + l.floor, out var entry) && entry.physics.OverlapCircle(new Vector2(l.position.x, l.position.y), radius) != null; }
        public void Dispose()
        { foreach (var entry in entries.Values) if (entry.scene.isLoaded) SceneManager.UnloadSceneAsync(entry.scene); entries.Clear(); }
    }
}