using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; using UnityEngine.Rendering; using UnityEngine.UIElements; using Random = UnityEngine.Random; namespace ET { [Timer(TimerType.UpdateBuildingDataTimer)] public class UpdateBuildingDataTimer : ATimer { public override void Run(MapComponent self) { try { if (self.IsDisposed || self.Parent == null) return; if (self.DomainScene() == null) return; self.UpdateBuildingData().Coroutine(); } catch(Exception ex) { Log.Error(ex.ToString()); } } } public class MapComponentAwakeSystem : AwakeSystem { public override void Awake(MapComponent self) { self.Awake(); } } // 以最底部为Map的数组的0,0点 [FriendClass(typeof(MapComponent))] public static class MapComponentSystem { public static void Awake(this MapComponent self) { var gameSceneMgr = self.ZoneScene().CurrentScene().GetComponent(); Transform mainSceneRoot = gameSceneMgr.GetRoot(GameSceneType.Main).transform; var sceneTrans = mainSceneRoot.Find("Scene"); self.NavMesh2d = sceneTrans.GetComponent(); var mapSizeTran = mainSceneRoot.Find("MapSize"); BoxCollider2D collider2D = mapSizeTran.GetComponent(); var bounds = collider2D.bounds; self.MapMinPos = bounds.min; self.MapMaxPos = bounds.max; self.MapSize = collider2D.size; Transform availablePlaceObj = mapSizeTran.Find("AvailablePlaceSize"); collider2D = availablePlaceObj.GetComponent(); bounds = collider2D.bounds; self.AvalablePlaceMinPos = bounds.min; var size = collider2D.size; float width = size.x; float height = width / 2; if (height > size.y) { height = size.y; width = height * 2; } self.TileMapWidth = width; self.TileMapHeight = height; self.HalfTileMapWidth = self.TileMapWidth / 2; self.HalfTileMapHeight = self.TileMapHeight / 2; self.MaxTileX = Mathf.FloorToInt(self.TileMapWidth / self.TileWidth); self.MaxTileY = self.MaxTileX; self.OccupyPlaceMap = new bool[self.MaxTileX, self.MaxTileY]; self.HalfTileHeight = self.TileHeight / 2; self.HalfTileWidth = self.TileWidth / 2; self.BuildingRoot = GameObject.Find("SceneRoot/MainSceneRoot/Scene/Layout/BuildingRoot").transform; self.ResourceRoot = GameObject.Find("SceneRoot/MainSceneRoot/Scene/Layout/ResourceRoot").transform; self.PeopleRoot = GameObject.Find("SceneRoot/MainSceneRoot/PeopleRoot").transform; // add grid self.CreateGrid(); self.HideGrid(); // add indicator self.CreateIndicator(); self.HideIndicator(); self.UpdateBuildingDataTimer = TimerComponent.Instance.NewRepeatedTimer(30000, TimerType.UpdateBuildingDataTimer, self); } public static void CreateGrid(this MapComponent self) { Transform mainSceneRoot = GameObject.Find("MainSceneRoot").transform; var gridMeshObj = mainSceneRoot.Find("GridMesh").gameObject; MeshRenderer meshRenderer = gridMeshObj.AddComponent(); meshRenderer.material = new Material(Shader.Find("Sprites/Default")); meshRenderer.material.color = new Color(0.9f, 0.9f, 0.9f, 0.5f); self.GridMeshFilter = gridMeshObj.AddComponent(); self.GridMesh = new Mesh(); var verticies = new List(); var indicies = new List(); // 以最底部为Map的数组的0,0点 var halfTileWidth = self.HalfTileWidth; var halfTileHeight = self.HalfTileHeight; // for coloum for (int i = 0; i <= self.MaxTileY; i++) { float beginX = i * halfTileWidth; float beginY = -self.HalfTileMapHeight + i * halfTileHeight; float endX = -self.HalfTileMapWidth + i * halfTileWidth; float endY = i * halfTileHeight; Vector3 start = new Vector3(beginX, beginY, 0); Vector3 end = new Vector3(endX, endY, 0); verticies.Add(start); verticies.Add(end); indicies.Add(i * 2); indicies.Add(i*2 + 1); } int offset = verticies.Count; // for row for (int i = 0; i <= self.MaxTileX; i++) { float beginX = -i * halfTileWidth; float beginY = -self.HalfTileMapHeight + i * halfTileHeight; float endX = self.HalfTileMapWidth - i * halfTileWidth; float endY = i * halfTileHeight; Vector3 start = new Vector3(beginX, beginY, 0); Vector3 end = new Vector3(endX, endY, 0); verticies.Add(start); verticies.Add(end); indicies.Add(offset + i * 2); indicies.Add(offset + i * 2 + 1); } self.GridMesh.vertices = verticies.ToArray(); self.GridMesh.SetIndices(indicies.ToArray(), MeshTopology.Lines, 0); self.GridMeshFilter.mesh = self.GridMesh; } public static void ShowGrid(this MapComponent self) { self.GridMeshFilter.gameObject.SetActive(true); } public static void HideGrid(this MapComponent self) { self.GridMeshFilter.gameObject.SetActive(false); } public static Vector2 MapToWorldPos(this MapComponent self, int row, int col) { Vector2 worldPos = new Vector2(); worldPos.x = (col - row) * self.HalfTileWidth; worldPos.y = -self.HalfTileMapHeight + (row + col) * self.HalfTileHeight; return worldPos; } public static Vector2Int WorldToMapPos(this MapComponent self, Vector2 worldPos) { Vector2Int mapPos = new Vector2Int(); var offsetY = worldPos.y + self.HalfTileMapHeight; mapPos.x = Mathf.RoundToInt(offsetY / self.TileHeight - worldPos.x / self.TileWidth); mapPos.y = Mathf.RoundToInt(worldPos.x / self.TileWidth + offsetY / self.TileHeight); mapPos.x = Mathf.Clamp(mapPos.x, 0, self.MaxTileX); mapPos.y = Mathf.Clamp(mapPos.y, 0, self.MaxTileY); return mapPos; } public static void CreateIndicator(this MapComponent self) { self.OccupyPlaceInidcator = BuildingViewHelper.CreateOcupyPlaceInicator("OccupyPlaceIndicator", self.ValidColor); self.OccupyPlaceIndicatorMR = self.OccupyPlaceInidcator.GetComponent();; } public static RectIndices GetUnitRectIndices(this MapComponent self, Vector3 worldPos, Vector2Int size) { RectIndices rectIndices = new(); // 坐标原点在底部中心 Vector2Int bottomPos = self.WorldToMapPos(worldPos); rectIndices.startRow = bottomPos.x; rectIndices.endRow = Mathf.Min(bottomPos.x + size.x, self.MaxTileX); rectIndices.startCol = bottomPos.y; rectIndices.endCol = Mathf.Min(bottomPos.y + size.y, self.MaxTileY); return rectIndices; } // 以传入的点为中心的一个矩形范围 public static RectIndices GetUnitRectIndicesOfCenter(this MapComponent self, Vector3 worldPos, Vector2Int size) { RectIndices rectIndices = new(); // 坐标原点在底部中心 Vector2Int bottomPos = self.WorldToMapPos(worldPos); int halfSizeX = size.x / 2; int halfSizeY = size.y / 2; rectIndices.startRow = Mathf.Max(bottomPos.x - halfSizeX, 0); rectIndices.endRow = Mathf.Min(bottomPos.x + halfSizeX, self.MaxTileX); rectIndices.startCol = Mathf.Max(bottomPos.y - halfSizeY, 0); rectIndices.endCol = Mathf.Min(bottomPos.y + halfSizeY, self.MaxTileY); return rectIndices; } public static bool IsPosInRect(this MapComponent self, Vector2Int pos, RectIndices rectIndices) { if (pos.x >= rectIndices.startRow && pos.x < rectIndices.endRow && pos.y >= rectIndices.startCol && pos.y < rectIndices.endCol) { return true; } return false; } public static bool CheckPlaceValid(this MapComponent self, ref RectIndices rectIndices) { for (int i = rectIndices.startRow; i < rectIndices.endRow; i++) { for (int j = rectIndices.startCol; j < rectIndices.endCol; j++) { if (self.OccupyPlaceMap[i, j]) { return false; } } } return true; } public static bool IsPlaceValid(this MapComponent self, Vector3 worldPos, Vector2Int size) { RectIndices rectIndices = self.GetUnitRectIndices(worldPos, size); return self.CheckPlaceValid(ref rectIndices); } public static void UpdateIndicator(this MapComponent self,Vector3 worldPos, Vector2Int size) { var isValid = self.IsPlaceValid(worldPos, size); if (isValid) { self.OccupyPlaceIndicatorMR.material.color = self.ValidColor; } else { self.OccupyPlaceIndicatorMR.material.color = self.InvalidColor; } BuildingViewHelper.UpdateIndicatorLocal(self.OccupyPlaceInidcator, self, worldPos, size); } public static void ShowIndicator(this MapComponent self) { self.OccupyPlaceInidcator.SetActive(true); } public static void HideIndicator(this MapComponent self) { self.OccupyPlaceInidcator.SetActive(false); } // 对齐坐标到格子上 public static Vector3 AlignPlacePosition(this MapComponent self, Vector3 worldPos, Vector2Int size) { Vector2Int bottomPos = self.WorldToMapPos(worldPos); int x = Mathf.Clamp(bottomPos.x, 0, self.MaxTileX - size.x); int y = Mathf.Clamp(bottomPos.y, 0, self.MaxTileY - size.y); return self.MapToWorldPos(x, y); } public static void MarkPlaceOnGrid(this MapComponent self, Vector3 pos, Vector2Int size, bool marked = true) { RectIndices rectIndices = self.GetUnitRectIndices(pos, size); for (int i = rectIndices.startRow; i < rectIndices.endRow; i++) { Mathf.Clamp(i, 0, self.MaxTileX - 1); for (int j = rectIndices.startCol; j < rectIndices.endCol; j++) { Mathf.Clamp(j, 0, self.MaxTileY - 1); self.OccupyPlaceMap[i, j] = marked; } } } public static void UpdateNavMesh2d(this MapComponent self) { self.NavMesh2d.BuildNavMesh(); } // sprite的局部坐标系的原点为底部中点 public static Vector2Int LocalToMapPos(this MapComponent self, Vector2 topPos) { Vector2Int mapPos = new Vector2Int(); var tileWidth = self.TileWidth; var tileHeight = self.TileHeight; int row = Mathf.RoundToInt(topPos.y / tileHeight - topPos.x / tileWidth); int col = Mathf.RoundToInt(topPos.y / tileHeight + topPos.x / tileWidth); mapPos.x = row; mapPos.y = col; return mapPos; } public static Vector2 MapToLocalPos(this MapComponent self, int row, int col) { Vector2 localPos = new Vector2(); localPos.x = (col - row) * self.HalfTileWidth; localPos.y = (row + col) * self.HalfTileHeight; return localPos; } public static Vector2Int GetSizeBySpriteCollider(this MapComponent self, SpriteRenderer spriteRenderer) { PolygonCollider2D polygonCollider2D = spriteRenderer.GetComponent(); if (polygonCollider2D == null) { Log.Warning("Sprite don't has a polygoncollider"); return Vector2Int.zero; } // 2 约定为多边形碰撞盒的顶点, 由工具生成 Vector2Int size = self.LocalToMapPos(polygonCollider2D.points[2]); return size; } public static async ETTask UpdateBuildingData(this MapComponent self) { Unit unit = UnitHelper.GetMyUnitFromZoneScene(self.ZoneScene()); await BuildingHelper.GetBuildingDurable(unit); } } }