using System; using System.Collections.Generic; using UnityEngine; using RuntimeData; using Animancer; using Logic; using Logic.AI; using Logic.CrashSight; using NUnit.Framework; using TH1_Logic.Core; using TH1Resource; using TMPro.Examples; using Unity.IO.LowLevel.Unsafe; using Object = UnityEngine.Object; using Random = UnityEngine.Random; using TMPro; using Unity.VisualScripting; using UnityEngine.UI; namespace TH1Renderer { public class GridRenderer { private MapData _mapData; private GameObject _ROGrid; private uint _gridId; private bool _cityWallMark = false; //-------- 表现层数据Rdata --------// public bool IsSelectHighlight = false; public bool IsMoveHighlight = false; public bool IsAttackHighlight = false; //--------- 瞬间更新的动画 -------- public int NowCityBuildlingLevel; public CivEnum NowCityCiv; public CivEnum UpdateCityCiv; public int UpdateCityBuildingLevel; public bool UpdateGrid; public float RandomWait; //-------- Update缓存数据 --------// private GridData _gridData; private bool _freeland; private CityData _cityData; private PlayerData _playerData; private GameObject _land, _mountain, _forest, _road, _resource, _cityBuilding, _cityWall, _fog, _borderUpLeft, _borderUpRight, _borderDownLeft, _borderDownRight, _cityBorderUpLeft, _cityBorderUpRight, _cityBorderDownLeft, _cityBorderDownRight, _selectHighlight, _moveHighlight, _attackHighlight, _effect, _RODebugText; private TextMeshPro _debugText; public int x, y; string forcesName, civName; bool needBounce = false, isBounceDown = true,levelupBounce = false; Vector3 bounceUpPos, bounceDownPos; private float _bounceWaitTime = 0f; float bounceTime = 0f; float bounceDownFullTime = 0.13f; float bounceUpFullTime = 0.07f; float levelupBounceDownFullTime = 0.33f; float levelupBounceUpFullTime = 0.07f; //-------- VFX管理器 -------- public GridVFXManager GridVFXManager; private Sprite _dieHint; private Sprite _counterDieHint; bool renderVeteran = false; public GridRenderer(GameObject prefab,Transform father, uint gridId, MapData mapData, Main main) { _mapData = mapData; _gridId = gridId; mapData.GridMap.GetGridDataByGid(_gridId,out _gridData); _freeland = !_mapData.GetCityDataByTerritoryGid(_gridId,out _cityData); if(!_freeland) _mapData.GetPlayerDataByCityId(_cityData.Id,out _playerData); if (!(_cityData == null) && !(_playerData == null)) { _mapData.GridMap.GetGridDataByGid(gridId, out var g1); } Vector3 tpos = Table.Instance.GridToWorld(_gridData); _ROGrid = GameObject.Instantiate(prefab,tpos, Quaternion.identity, father); Init(_gridData); } public GameObject GetROGrid() { return _ROGrid; } public GridData Grid() { return _gridData; } public void InstantUpdateGrid(bool v = true) { UpdateGrid = v; } public void InstantUpdateCityBuilding(int v,CivEnum civ) { UpdateCityBuildingLevel = v; UpdateCityCiv = civ; } public bool IsBelongSelfPlayer() { return _gridData.Player(Main.MapData).IsSelfPlayer(); } private void ClearHistoryRenderMark() { GridVFXManager.Clear(); } //判断各自是否要bounce 并处理bounce动画 private void UpdateBounceAnim() { var downFullTime = levelupBounce? levelupBounceDownFullTime : bounceUpFullTime; var upFullTime = levelupBounce ? levelupBounceUpFullTime : bounceUpFullTime; if (needBounce) { if (_bounceWaitTime > 0) _bounceWaitTime -= Time.deltaTime; else { if (isBounceDown) { bounceTime += Time.deltaTime / downFullTime; if (levelupBounce) { float easedT = 1f - Mathf.Pow(1f - bounceTime, 3); _ROGrid.transform.position = Vector3.Lerp(bounceUpPos, bounceDownPos, bounceTime); } else _ROGrid.transform.position = Vector3.Lerp(bounceUpPos, bounceDownPos, bounceTime); if (bounceTime >= 1f) { isBounceDown = false; bounceTime = 0f; } } else { bounceTime += Time.deltaTime / upFullTime; _ROGrid.transform.position = Vector3.Lerp(bounceDownPos, bounceUpPos, bounceTime); if (bounceTime >= 1f) needBounce = false; } } } } //处理VFXManagerDict下每个Effect的Update public void Update() { //step #1 每帧更新每个grid相关的player city的缓存数据 _mapData.GridMap.GetGridDataByGid(_gridId, out _gridData); _freeland = !_mapData.GetCityDataByTerritoryGid(_gridId, out _cityData); if (!_freeland) _mapData.GetPlayerDataByCityId(_cityData.Id, out _playerData); //step #2 处理需要瞬间更新gridData视觉的情况 if (UpdateGrid) { UpdateGrid = false; RenderUpdateGridWithoutCityBuilding(); } if (UpdateCityBuildingLevel != NowCityBuildlingLevel) { NowCityBuildlingLevel = UpdateCityBuildingLevel; RenderUpdateCityBuilding(_cityData.Id); } if (NowCityCiv != UpdateCityCiv) { NowCityCiv = UpdateCityCiv; RenderUpdateCityBuilding(_cityData.Id); } //step #3 查看城市边界的更新情况,这个没有视野也会显示的,目前都是瞬间更新 UpdateCityBorder(); //step #4 判断各自是否要bounce UpdateBounceAnim(); //step #5 驱动VFXManager的所有VFX对象的update GridVFXManager.Update(); } public void FirstShowGrid() { //Step #1 播放雾气消失动画 PlayVFX(new GridVFXParams(GridVFXType.Fog)); //Step #2 呈现格子画面 RenderUpdateGridWithoutCityBuilding(); //Step #3 如果有city,更新city情况 var city = _gridData.CityOnGrid(Main.MapData); if (city != null) { RenderUpdateCityBuilding(_cityData.Id); _cityData.CityInfoRenderer(Main.MapData)?.InstantUpdateCityInfo(); } //Step #4 如果有unit,更新unit的表现,更新unit的位置 var unit = _gridData.Unit(Main.MapData); if (unit != null) { //unit.RenderMark = true; unit.Renderer(Main.MapData)?.InstantUpdateUnit(true); unit.Renderer(Main.MapData)?.InstantUpdateUnitPos(); //更新该单位周围其他单位的高亮状态 MapRenderer.Instance.UpdateAroundHighlight(Main.MapData,_gridData); } } public void PlayVFX(GridVFXParams param, GridVFXPlayType playType = GridVFXPlayType.PlayOnce) { //当前玩家有该格子的视野,才播放该动画 if(Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id)) GridVFXManager.PlayVFX(param,playType); } private void RenderUpdateDebug() { if (DebugCenter.Instance.DebugMode) { if(!_RODebugText.activeSelf) _RODebugText.SetActive(true); } else { _RODebugText.SetActive(false); return; } _debugText.text = ""; _debugText.text += $"ID={_gridId}"; _debugText.text += $" pos={_gridData.Pos.X} {_gridData.Pos.Y}\n"; if(_gridData.buildingLevel > 0) _debugText.text += $"lv = {_gridData.buildingLevel}\n"; if(_gridData.Feature == TerrainFeature.Road) _debugText.text += $"road\n"; } private void Init(GridData t) { x = (int)t.Pos.X; y = (int)t.Pos.Y; //Vector3 pos = transform.position; //pos.z = GridData.pos.x + GridData.pos.y; //transform.position = pos; _land = _ROGrid.transform.Find("Land").gameObject; _road = _ROGrid.transform.Find("Road").gameObject; _mountain = _ROGrid.transform.Find("Mountain").gameObject; _forest = _ROGrid.transform.Find("Forest").gameObject; _resource = _ROGrid.transform.Find("Resource").gameObject; _cityBuilding = _ROGrid.transform.Find("cityBuilding").gameObject; _cityWall = _ROGrid.transform.Find("cityWall").gameObject; _fog = _ROGrid.transform.Find("Fog").gameObject; _borderUpLeft = _ROGrid.transform.Find("BorderUpLeft").gameObject; _borderUpRight = _ROGrid.transform.Find("BorderUpRight").gameObject; _borderDownLeft = _ROGrid.transform.Find("BorderDownLeft").gameObject; _borderDownRight = _ROGrid.transform.Find("BorderDownRight").gameObject; _cityBorderUpLeft = _ROGrid.transform.Find("CityBorderUpLeft").gameObject; _cityBorderUpRight = _ROGrid.transform.Find("CityBorderUpRight").gameObject; _cityBorderDownLeft = _ROGrid.transform.Find("CityBorderDownLeft").gameObject; _cityBorderDownRight = _ROGrid.transform.Find("CityBorderDownRight").gameObject; _selectHighlight = _ROGrid.transform.Find("SelectHighlight").gameObject; _moveHighlight = _ROGrid.transform.Find("MoveHighlight").gameObject; _attackHighlight = _ROGrid.transform.Find("AttackHighlight").gameObject; _effect = _ROGrid.transform.Find("Effect").gameObject; _RODebugText = _ROGrid.transform.Find("DebugText").gameObject; _debugText = _RODebugText.GetComponent(); foreach(Transform child in _effect.transform) child.gameObject.SetActive(false); civName = Table.Instance.QueryCivsName(_gridData.CivId); //创建特效管理器Dict GridVFXManager = new GridVFXManager(_effect); RenderUpdateGridWithoutCityBuilding(); //UpdateBorder();fog自带updateborder } private void RenderUpdateGridWithoutCityBuilding() { UpdateLand(); UpdateRoad(); UpdateMountain(); UpdateForest(); UpdateResource(); UpdateCityWall(); UpdateCityBorder(); UpdateFog(); UpdateGridVFX(); RenderUpdateDebug(); //UpdateBorder();fog自带updateborder } public void RenderUpdateCityBuilding(uint cityId) { _mapData.CityMap.GetCityById(cityId,out CityData cityData); _mapData.GetPlayerDataByCityId(cityId, out PlayerData playerData); uint civId = playerData.PlayerCivId; int citylevel = cityData.Level; int houseEdge = Table.Instance.cityLevel2HouseEdge[citylevel]; int houseSize = houseEdge * houseEdge; int houseCount = Table.Instance.cityLevel2HouseNumber[citylevel]; int houseHeight = Table.Instance.cityLevel2HouseHeight[citylevel]; int[] housePosHeight = new int[houseSize]; int[,] textureType = new int[houseHeight, houseCount]; for (int k = 0; k < houseSize; k++) { for (int l = 0; l < houseHeight; l++) textureType[l, k] = 0; //初始化 housePosHeight[k] = 1; textureType[0, k] = Random.Range(0, Table.Instance.cityLevel2HouseType[citylevel]); } for (int k = houseSize; k < houseCount; k++) { int randPos = Random.Range(0, houseSize); if (housePosHeight[randPos] >= houseHeight) for (randPos = houseSize - 1; randPos >= 0; randPos--) if (housePosHeight[randPos] < houseHeight) break; textureType[housePosHeight[randPos]++, randPos] = Random.Range(0, Table.Instance.cityLevel2HouseType[citylevel]); } //清空原来的城市图像 foreach (UnityEngine.Transform child in _cityBuilding.transform) Object.Destroy(child.gameObject); //开始设置城市图像 for (int x = 0; x < houseEdge; x++) for (int y = 0; y < houseEdge; y++) for (int z = 0; z < housePosHeight[x * houseEdge + y]; z++) { GameObject house = new GameObject($"house{x}_{y}_{z}"); SpriteRenderer sr = house.AddComponent(); string forcesName = Table.Instance.QueryForcesName(playerData.PlayerForceId); string civName = Table.Instance.QueryCivsName(playerData.PlayerCivId); //Debug.Log($"ArtResources/TH1Buildings/TH1Buildings{forcesName}/{civName}/{forcesName}_{civName}_House_{z + 1}"); sr.sprite = Resources.Load( $"ArtResources/TH1Buildings/TH1Buildings{forcesName}/{civName}/{forcesName}_{civName}_House_{textureType[z,x*houseEdge +y] + 1}"); house.transform.parent = _cityBuilding.transform; Vector3 tpos = _ROGrid.transform.position; //每层的层高 float levelHeight = 1.2f; tpos = new Vector3(tpos.x + 1.22f * (x - y), tpos.y + 0.7f * (x + y) + z * levelHeight - 7.5f + 0.7f * (4 - houseEdge), tpos.z + 0.9f - (30 + z - x - y) * 0.001f); house.transform.position = tpos; } } public void RenderUpdataHighlight() { _moveHighlight.SetActive(IsMoveHighlight); _attackHighlight.SetActive(IsAttackHighlight); _selectHighlight.SetActive(IsSelectHighlight); } public void SetSelectHighlight(bool v) { if (IsSelectHighlight == v) return; IsSelectHighlight = v; MapRenderer.Instance.HighlightGridIdSet.Add(_gridId); MapRenderer.Instance.HighlightGridIdSetRenderMark = true; } public void SetMoveHighlight(bool v) { if (IsMoveHighlight == v) return; IsMoveHighlight = v; MapRenderer.Instance.HighlightGridIdSet.Add(_gridId); MapRenderer.Instance.HighlightGridIdSetRenderMark = true; } public void SetAttackHighlight(bool v) { if (IsAttackHighlight == v) return; IsAttackHighlight = v; MapRenderer.Instance.HighlightGridIdSet.Add(_gridId); MapRenderer.Instance.HighlightGridIdSetRenderMark = true; } private void UpdateLand() { var oldSprite = _land?.GetComponent()?.sprite; if (oldSprite == null) return; var newSprite = Table.Instance.GridAndResourceDataAssets.GetTerrainSprite(_gridData); _land.GetComponent().sprite = newSprite; //_land.GetComponent().sprite = Table.Instance.GridAndResourceDataAssets.GetTerrainSprite(_gridData); if (oldSprite != newSprite && _gridData.HasSpType(GridSpType.RemiliaGrid)) { PlayVFX(new GridVFXParams(GridVFXType.RedMistCreate)); } } private void UpdateRoad() { //如果当前的feature没有road if (_gridData.Feature != TerrainFeature.Road) { for (int i = 1; i <= 9; i++) { int dirForHuman = i; int dirForComputer = dirForHuman - 1; var t = _road.transform.Find($"Road{dirForHuman}"); if (t != null) GameObject.Destroy(t.gameObject); } return; } //TODO 最好把dir做成enum for (int i = 1; i <= 9; i++) { int dirForHuman = i; int dirForComputer = dirForHuman - 1; var t = _road.transform.Find($"Road{dirForHuman}"); if (!_mapData.CheckIfNearByGridRoadCanConnenct(_gridData, dirForComputer)) { if (t != null) GameObject.Destroy(t.gameObject); continue; } if (t == null) { GameObject road = new GameObject($"Road{dirForHuman}"); SpriteRenderer sr = road.AddComponent(); ResourceCache.Instance.SpriteCache.Roads.TryGetValue((_gridData.Terrain == TerrainType.Land ? "Road" : "WaterRoad") + dirForComputer,out var tmpSprite); sr.sprite = tmpSprite; road.transform.parent = _road.transform; road.transform.localPosition = new Vector3(0, 0, 0); } } } private void UpdateMountain() { _mountain.GetComponent().sprite = Table.Instance.GridAndResourceDataAssets.GetMountainSprite(_gridData); } private void UpdateForest() { _forest.GetComponent().sprite = Table.Instance.GridAndResourceDataAssets.GetVegetationSprite(_gridData); } private void UpdateResource() { if(!_freeland) forcesName = Table.Instance.QueryForcesName(_playerData.PlayerForceId); bool glow = (!_freeland && _playerData.Id == _mapData.PlayerMap.SelfPlayerData.Id && ((_gridData.Resource == ResourceType.Fruit && _playerData.TechTree.CheckIfHasTechAtom(TechAtom.GainFruit)) || (_gridData.Resource == ResourceType.Animal && _playerData.TechTree.CheckIfHasTechAtom(TechAtom.GainAnimal)) || (_gridData.Resource == ResourceType.Animal && _playerData.TechTree.CheckIfHasTechAtom(TechAtom.TrainUnitKaguyaFrenchAnimalWarrior)) || (_gridData.Resource == ResourceType.Fish && _playerData.TechTree.CheckIfHasTechAtom(TechAtom.GainFish)) || (_gridData.Resource == ResourceType.Crop && _playerData.TechTree.CheckIfHasTechAtom(TechAtom.BuildFarm)) || (_gridData.Resource == ResourceType.Metal && _playerData.TechTree.CheckIfHasTechAtom(TechAtom.BuildMine)))); if (_gridData.Resource == ResourceType.Starfish && _mapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTech(TechType.Navigation)) glow = true; bool hasCity = _mapData.GetCityDataByTerritoryGid(_gridId, out var cityData); //处理隐藏没有科技情况下 metal、starfish、crop的情况 bool skip = false; if (_gridData.Resource == ResourceType.Crop) if (!_mapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTech(TechType.Organization) && !_mapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTech(TechType.Farming) && !_mapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTech(TechType.RemiliaFarming)) skip = true; if (_gridData.Resource == ResourceType.Metal) if (!_mapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTechAtom(TechAtom.BuildMine) && !_mapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTechAtom(TechAtom.UnitSkillMOUNTAINMOVE)) skip = true; if (_gridData.Resource == ResourceType.Starfish) if (!_mapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTechAtom(TechAtom.UnitActionGather) && !_mapData.PlayerMap.SelfPlayerData.TechTree.CheckIfHasTechAtom(TechAtom.UnitSkillOCEANMOVE)) skip = true; if (skip) { _resource.GetComponent().sprite = null; return; } //注意 playerData可能是null 对于中立格子来说 var resource = Table.Instance.GridAndResourceDataAssets.GetResourceSprite(_gridData.Resource,_playerData,_gridData); //如果是桥梁 处理mirror的情况 if (_gridData.Resource == ResourceType.Bridge) { if(_mapData.GridMap.CalcBridgeMirror(_gridData)) resource = Table.Instance.GridAndResourceDataAssets.GetResourceSprite(_gridData.Resource,_playerData,_gridData,mirror:true); } //如果是citycenter并且有城市了,就变为null if (_gridData.Resource == ResourceType.CityCenter && hasCity) resource = null; //如果是奇观,替换为对应奇观 if (_gridData.Resource == ResourceType.Wonder) resource = Table.Instance.GridAndResourceDataAssets.GetWonderSprite(_gridData.Wonder, _playerData); if (_resource.GetComponent().sprite != resource) _resource.GetComponent().sprite = resource; //处理高光效果 _resource.GetComponent().material = glow ? ResourceCache.Instance.MatCache.TH1URPShaders_Sprite_Glow: ResourceCache.Instance.MatCache.TH1URPShaders_Default; } private void UpdateCityWall() { if (_cityWall.activeSelf) return; if (!_mapData.GetCityDataByGid(_gridId, out var cityData)) return; if (!cityData.CityWall) return; _cityWallMark = true; _cityWall.SetActive(true); } public void UpdateBorder() { //如果不是城市领土,不需要生成border if (_freeland) { _borderDownLeft.SetActive(false); _borderDownRight.SetActive(false); _borderUpLeft.SetActive(false); _borderUpRight.SetActive(false); return; } if (Table.Instance.PlayerDataAssets.GetPlayerInfo(_playerData, out var playerInfo)) { var c = playerInfo.Color; //c.a = 0.8f; _borderDownLeft.GetComponent().color = c; _borderDownRight.GetComponent().color = c; _borderUpRight.GetComponent().color = c; _borderUpLeft.GetComponent().color = c; } _borderDownLeft.SetActive(!_mapData.CheckIfNearByGridSamePlayer(_gridData,4)); _borderDownRight.SetActive(!_mapData.CheckIfNearByGridSamePlayer(_gridData,2)); _borderUpRight.SetActive(!_mapData.CheckIfNearByGridSamePlayer(_gridData,6)); _borderUpLeft.SetActive(!_mapData.CheckIfNearByGridSamePlayer(_gridData,8)); } private void UpdateCityBorder() { //如果没有rendermark 直接return if (!_gridData.CityBorderRenderMark) { _cityBorderDownLeft.SetActive(false); _cityBorderDownRight.SetActive(false); _cityBorderUpLeft.SetActive(false); _cityBorderUpRight.SetActive(false); return; } if (!_mapData.GetPlayerDataByTerritoryGridId(_gridData.Id, out var player)) return; var color = Color.red; if(player.Id == _mapData.PlayerMap.SelfPlayerId) color = Color.green; _cityBorderDownLeft.GetComponent().color = color; _cityBorderDownRight.GetComponent().color = color; _cityBorderUpRight.GetComponent().color = color; _cityBorderUpLeft.GetComponent().color = color; _cityBorderDownLeft.SetActive(!_mapData.CheckIfNearByGridSameCity(_gridData,4)); _cityBorderDownRight.SetActive(!_mapData.CheckIfNearByGridSameCity(_gridData,2)); _cityBorderUpRight.SetActive(!_mapData.CheckIfNearByGridSameCity(_gridData,6)); _cityBorderUpLeft.SetActive(!_mapData.CheckIfNearByGridSameCity(_gridData,8)); } private void UpdateFog() { bool isFog = !_mapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(_gridData.Id); if (_fog.activeSelf != isFog) { if (isFog) { _fog.SetActive(true); } else { PlayVFX(new GridVFXParams(GridVFXType.Fog)); //ClearHistoryRenderMark(); Timer.Instance.TimerRegister(this, () => { _fog.SetActive(false); if(_gridData.Resource == ResourceType.CityCenter || _gridData.Resource == ResourceType.Treasure) PlayVFX(new GridVFXParams(GridVFXType.Treasure)); },RandomWait,"GridRenderer_UpdateFog"); } } _land.SetActive(!isFog); _road.SetActive(!isFog); _mountain.SetActive(!isFog); _forest.SetActive(!isFog); _cityWall.SetActive(!isFog&&_cityWallMark); _cityBuilding.SetActive(!isFog); _resource.SetActive(!isFog); if (isFog) { _borderDownLeft.SetActive(false); _borderDownRight.SetActive(false); _borderUpLeft.SetActive(false); _borderUpRight.SetActive(false); } else UpdateBorder(); } private void UpdateGridVFX() { //目前仅仅处理Fire这一种VFX var city = _gridData.CityOnGrid(Main.MapData); if (city == null) return; GridVFXPlayType playType = GridVFXPlayType.Play; var unit = _gridData.Unit(Main.MapData); if(unit == null || unit.Player(Main.MapData) == city.Player(Main.MapData)) playType = GridVFXPlayType.Stop; PlayVFX(new GridVFXParams(GridVFXType.Fire),playType); } public void SetBounceAnim(bool NeedRandomWait=false, bool isLevelupBounce = false) { if (!_ROGrid) return; if (needBounce) return; levelupBounce = isLevelupBounce; if(!NeedRandomWait) _bounceWaitTime = 0f; else { _bounceWaitTime = Random.Range(0,0.3f); } needBounce = true; isBounceDown = true; bounceTime = 0f; bounceUpPos = _ROGrid.transform.position; if(isLevelupBounce) bounceDownPos = new Vector3(_ROGrid.transform.position.x, _ROGrid.transform.position.y - 1.5f, _ROGrid.transform.position.z); else bounceDownPos = new Vector3(_ROGrid.transform.position.x, _ROGrid.transform.position.y - 0.5f, _ROGrid.transform.position.z); } public Vector3 GridVector3() { if (_gridData != null) return _gridData.V3(); return Vector3.zero; } } }