/* * @Author: 白哉 * @Description: 地图生成器 * @Date: 2025年04月01日 星期二 11:04:43 * @Modify: */ using System.Collections.Generic; using UnityEngine; using System; using System.Linq; using RuntimeData; using TMPro.Examples; using Unity.VisualScripting; namespace Logic { public class MapGenerator { private Main _main; private MapData _mapData; //海陆层参数,柏林噪音 private const float Scale = 20f; // 噪声的缩放因子 private float _landThreshold; // 陆地的阈值 private float _mountainThreshold = 0.7f; // 山地的阈值 private float[,] _heightMap; private const int SmoothIterations = 3; //平滑次数 private uint _width, _height; private List _tribes; public List PlayerCivOri; // 初始化地图 public MapGenerator(Main main,MapData mapData) { _main = main; _mapData = mapData; } public void GenerateMap(MapData mapData) { _width = mapData.MapConfig.Width; _height = mapData.MapConfig.Height; _heightMap = new float[_width, _height]; if (DebugCenter.Instance.DebugLandThreshold < 0) _landThreshold = new System.Random().Next(200, 400) / 1000f; else _landThreshold = DebugCenter.Instance.DebugLandThreshold; _tribes = new List(); //用柏林噪音生成地图高度 GenerateHeightMap(); //平滑地图 SmoothHeightMap(); //归一化处理地图,均匀分布0~1范围 NormalizeHeightMap(); //在连续的大片海洋上生成岛屿 GenerateIsland(); //生成所有tribe GenerateTribes(); //创建文明摇篮城市(每个玩家的原始首都) GenerateCradleCity(mapData); //初始化所有Grid信息 InitGridMapData(mapData); //初始化所有玩家的初始视野信息 InitAllPlayerSight(mapData); //初始化Debug的参数 InitDebugInfo(mapData); } //海陆层使用的地形高度数据 private void GenerateHeightMap() { for (var x = 0; x < _width; x++) { for (var y = 0; y < _height; y++) { float seda = new System.Random().Next(1, 30); float sedb = new System.Random().Next(2, 40); // 使用种子值来改变噪声的生成,保证每次生成的噪声不同 var xCoord = (float)x / _width * Scale / seda; var yCoord = (float)y / _height * Scale / sedb; _heightMap[x, y] = Mathf.PerlinNoise(xCoord, yCoord); //Debug.Log($"sed = {seda}\\{sedb} xC = {xCoord} yC = {yCoord} height = {heightMap[x, y]}"); } } } //海陆层使用的地形平滑工具 private void SmoothHeightMap() { for (var iteration = 0; iteration < SmoothIterations; iteration++) { var newHeightMap = new float[_width, _height]; for (int x = 0; x < _width; x++) { for (int y = 0; y < _height; y++) { // 计算该位置的邻域平均值 var average = GetAverageHeight(x, y); newHeightMap[x, y] = average; } } _heightMap = newHeightMap; } } // 获取该位置的邻域平均高度值 private float GetAverageHeight(int x, int y) { var total = 0f; var count = 0; // 获取周围的邻域高度值 for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { var nx = x + dx; var ny = y + dy; if (nx < 0 || nx >= _width || ny < 0 || ny >= _height) continue; total += _heightMap[nx, ny]; count++; // 确保在有效范围内 } } return total / count; } //归一化处理高度 private void NormalizeHeightMap() { var minHeight = Mathf.Infinity; var maxHeight = -Mathf.Infinity; // 计算最小值和最大值 for (var x = 0; x < _width; x++) { for (int y = 0; y < _height; y++) { if (_heightMap[x, y] < minHeight) minHeight = _heightMap[x, y]; if (_heightMap[x, y] > maxHeight) maxHeight = _heightMap[x, y]; } } // 将高度图归一化到 [0, 1] 范围 for (var x = 0; x < _width; x++) { for (var y = 0; y < _height; y++) { _heightMap[x, y] = (_heightMap[x, y] - minHeight) / (maxHeight - minHeight); } } } //在连续大片海洋中生成岛屿 private void GenerateIsland() { for (var x = 1; x < _width - 1; x++) for (var y = 1; y < _height - 1; y++) { var t = GenerateTerrain(x, y); var canBeIsland = false; if (t == TerrainType.DeepSea) { canBeIsland = true; var landNearby = 0; for (var i = -2; i <= 2; i++) for (var j = -2; j <= 2; j++) if (x + i >= 0 && x + i < _width && y + j >= 0 && y + j < _height && GenerateTerrain(x + i, y + j) != TerrainType.DeepSea) landNearby++; if (landNearby > 9) canBeIsland = false; } if (canBeIsland && UnityEngine.Random.Range(0, 100) < 50) _heightMap[x, y] = _landThreshold + 1; } } //生成所有tirbes private void GenerateTribes() { var cant = new bool[_width, _height]; var count = _width * _height; //剩余多少格子能用 var cityNum = (int)(count / 12); Array.Clear(cant, 0, cant.Length); for (var x = 0; x < _width; x++) for (var y = 0; y < _height; y++) if (_heightMap[x, y] <= _landThreshold || x == 0 || x == _width - 1 || y == 0 || y == _height - 1) { cant[x, y] = true; count--; } for (int c = 0; count > 0 && c <= cityNum; c++) { var choose = (int)UnityEngine.Random.Range(1, count); //每次随机一个可用的格子(序号) //Debug.Log("this time " + count + " choose : " + choose); var tmp = 0; for (int x = 0; x < _width; x++) { for (int y = 0; y < _height; y++) { if (cant[x, y]) continue; tmp++; if (tmp != choose) continue; _tribes.Add(new MapPosition(x,y)); for (var xx = -2; xx <= 2; xx++) for (var yy = -2; yy <= 2; yy++) { var nx = x + xx; var ny = y + yy; if (nx < 0 || nx >= _width || ny < 0 || ny >= _height || cant[nx, ny]) continue; cant[nx, ny] = true; // 把新城市方圆2距离的格子都ban了 count--; } break; } if (tmp == choose) break; } if (count <= 0) break; } //List LandCenters = new List(); //_map.cityCount = map.cityCenters.Count; } private void InitGridMapData(MapData mapData) { for (int x = 0; x < _width; x++) { for (int y = 0; y < _height; y++) { // 生成海陆层(陆地、浅海、深海) var terrain = GenerateTerrain(x, y); // 生成地形层(是否有山) var feature = (terrain == TerrainType.Land) ? GenerateFeature(x, y) : TerrainFeature.None; //如果是摇篮城市,terrain设置为road if (mapData.GridMap.GetGridDataByPos(x, y, out var gg) && mapData.GetCityDataByGid(gg.Id,out var cc)) feature = TerrainFeature.Road; // 生成植被层(是否有树木) var vegetation = (feature == TerrainFeature.None && terrain == TerrainType.Land) ? GenerateVegetation(x, y) : Vegetation.None; // 生成资源层 var resource = GenerateResource(x, y, terrain, feature, vegetation); //生成四个角的塔 if (HasTower(x, y)) { feature = TerrainFeature.None; resource = ResourceType.Tower; vegetation = Vegetation.None; } // 生成文明层 var civId = GenerateCivilization(mapData,x, y); // 设置这个地块的所有层信息 //Debug.Log(map.grid[x,y]); if (mapData.GridMap.GetGridDataByPos(x, y, out GridData g)) g.SetGridData(terrain,feature,vegetation,resource,civId); } } } private bool HasTower(int x, int y) { if (x == 0 && y == 0) return true; if (x == 0 && y == _height - 1) return true; if (x == _width - 1 && y == 0) return true; if (x == _width - 1 && y == _height - 1) return true; return false; } //生成文明摇篮城市(每个玩家的原始首都) private void GenerateCradleCity(MapData mapData) { //制作文明摇篮地点候选列表 var cityIndices = new List(); foreach (var c in _tribes) { var nearbyLand = 0; var cx = (int)c.PosId / 1000; var cy = (int)c.PosId % 1000; for (int x = cx - 1; x <= cx + 1; x++) for (int y = cy - 1; y <= cy + 1; y++) if (x >= 0 && x < _width && y >= 0 && y < _height && _heightMap[x, y] > _landThreshold) nearbyLand++; if (nearbyLand > 0) cityIndices.Add(new MapPosition(cx,cy)); } if (cityIndices.Count < mapData.MapConfig.PlayerCount) mapData.MapConfig.PlayerCount = (uint)cityIndices.Count; // 从制作列表随机排序并取前m个 PlayerCivOri = cityIndices .OrderBy(x => Guid.NewGuid()) // 使用 Guid 确保随机性 .Take((int)mapData.MapConfig.PlayerCount) .ToList(); //建立玩家档案,建立具体城市,建立初始单位 int rk = 0; foreach (var p in mapData.PlayerMap.PlayerDataList) { GridData g; mapData.GridMap.GetGridDataByPos(PlayerCivOri[rk].X, PlayerCivOri[rk].Y, out g); //建设一个新城市 CityData c = mapData.AddCityData(g.Id, p.Id); //设置该城市为首都 Main.CityLogic.SetCapital(mapData, c.Id); //设置该玩家的文明摇篮城市为该城市 p.CradleCityId = c.Id; Main.CityLogic.SetCradle(mapData, c.Id); //建立文明的开国单位,并额外赋予初始行动点 Main.UnitLogic.AddMPAPCP(mapData, mapData.AddUnitData(g.Id,c.Id,UnitType.Warrior)); rk++; } } //返回x,y位置的海陆层信息 private TerrainType GenerateTerrain(int x, int y) { // 走柏林噪音判断 var heightValue = _heightMap[x, y]; if (heightValue > _landThreshold) return TerrainType.Land; if (x + 1 < _width && _heightMap[x + 1, y] > _landThreshold) return TerrainType.ShallowSea; if (x - 1 > 0 && _heightMap[x - 1, y] > _landThreshold) return TerrainType.ShallowSea; if (y + 1 < _height && _heightMap[x, y + 1] > _landThreshold) return TerrainType.ShallowSea; if (y - 1 > 0 && _heightMap[x, y - 1] > _landThreshold) return TerrainType.ShallowSea; return TerrainType.DeepSea; } //返回x,y位置的地形层信息 private TerrainFeature GenerateFeature(int x, int y) { if (_tribes.Any(p => p.X == x && p.Y == y)) return TerrainFeature.None;//如果是城市中心那必没有山 var heightValue = _heightMap[x, y]; if (heightValue > _mountainThreshold) return UnityEngine.Random.Range(0, 100) < 30 ? TerrainFeature.Mountain : TerrainFeature.None; return UnityEngine.Random.Range(0, 100) < 10 ? TerrainFeature.Mountain : TerrainFeature.None; } //返回x,y位置的植被层信息 private Vegetation GenerateVegetation(int x, int y) { if (_tribes.Any(p => p.X == x && p.Y == y)) return Vegetation.None;//如果是城市中心那必没有树 return UnityEngine.Random.Range(0, 100) < 30 ? Vegetation.Trees : Vegetation.None; } //返回x,y位置的资源层信息 private ResourceType GenerateResource(int x, int y, TerrainType terrain, TerrainFeature feature, Vegetation vegetation) { var treasurePossible = true; if (_tribes.Any(p => p.X == x && p.Y == y)) return ResourceType.CityCenter; if (_tribes.Any(p => Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y) <= 2)) treasurePossible = false; if (treasurePossible && UnityEngine.Random.Range(0, 100) < 5) return ResourceType.Treasure; if (terrain == TerrainType.ShallowSea) return UnityEngine.Random.Range(0, 100) < 40 ? ResourceType.Fish : ResourceType.None; if (terrain == TerrainType.ShallowSea) return UnityEngine.Random.Range(0, 100) < 10 ? ResourceType.Starfish : ResourceType.None; if (terrain == TerrainType.DeepSea) return UnityEngine.Random.Range(0, 100) < 15 ? ResourceType.Starfish : ResourceType.None; if (feature == TerrainFeature.Mountain) return UnityEngine.Random.Range(0, 100) < 60 ? ResourceType.Metal : ResourceType.None; if (vegetation == Vegetation.Trees) return UnityEngine.Random.Range(0, 100) < 30 ? ResourceType.Animal : ResourceType.None; if (UnityEngine.Random.Range(0, 100) < 50) return UnityEngine.Random.Range(0, 100) < 50 ? ResourceType.Fruit : ResourceType.Crop; return ResourceType.None; } //返回x,y位置的文化层信息 private uint GenerateCivilization(MapData mapData,int x, int y) { // 简单的随机分配一个文明,或者使用更复杂的算法进行分布 var minDis = 2139062143; var civSeed = -1; var tt = 0; //Debug.Log(PlayerCivOri.Count); foreach (var p in PlayerCivOri) { if (Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y) < minDis) { minDis = (int)(Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y)); civSeed = (int)_mapData.PlayerMap.PlayerDataList[tt].PlayerCivId; } else if ((Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y) == minDis) && UnityEngine.Random.Range(0, 2) == 1) { minDis = (int)(Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y)); civSeed = (int)_mapData.PlayerMap.PlayerDataList[tt].PlayerCivId; } else if ((Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y) == minDis + 1) && UnityEngine.Random.Range(0, 3) == 1) { minDis = (int)(Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y)); civSeed = (int)_mapData.PlayerMap.PlayerDataList[tt].PlayerCivId; } tt++; } if (civSeed == -1) return (uint)UnityEngine.Random.Range(1, mapData.MapConfig.PlayerCount + 1); return (uint)civSeed; } //初始化所有玩家的视野信息 private void InitAllPlayerSight(MapData mapData) { foreach (var cityData in mapData.CityMap.CityList) { mapData.GetPlayerDataByCityId(cityData.Id, out var playerData); mapData.GetGridDataByCityId(cityData.Id, out var gridData); Main.PlayerLogic.UpdateSightByRadius(mapData, playerData, gridData, 2); } } private void InitDebugInfo(MapData mapData) { if (DebugCenter.Instance.DebugSelfPlayerAllSight) { List tt = new List(); foreach (var gridData in mapData.GridMap.GridList) tt.Add(gridData.Id); Main.PlayerLogic.UpdateSight(mapData,mapData.PlayerMap.SelfPlayerData,tt); } if (DebugCenter.Instance.DebugAIAllTech) { foreach (var playerData in mapData.PlayerMap.PlayerDataList) { if (playerData == mapData.PlayerMap.SelfPlayerData) continue; playerData.TechTree.LearnTech(TechType.Fishing); playerData.TechTree.LearnTech(TechType.Organization); playerData.TechTree.LearnTech(TechType.Farming); playerData.TechTree.LearnTech(TechType.Mining); playerData.TechTree.LearnTech(TechType.Forestry); playerData.TechTree.LearnTech(TechType.Hunting); playerData.TechTree.LearnTech(TechType.Smithery); playerData.TechTree.LearnTech(TechType.Mathematics); playerData.TechTree.LearnTech(TechType.FreeSpirit); playerData.TechTree.LearnTech(TechType.Chivalry); playerData.TechTree.LearnTech(TechType.Sailing); playerData.TechTree.LearnTech(TechType.Navigation); playerData.TechTree.LearnTech(TechType.Ramming); playerData.TechTree.LearnTech(TechType.Riding); playerData.TechTree.LearnTech(TechType.Archery); playerData.TechTree.LearnTech(TechType.Spiritualism); playerData.TechTree.LearnTech(TechType.Construction); playerData.TechTree.LearnTech(TechType.Meditation); playerData.TechTree.LearnTech(TechType.Aquatism); } } if (DebugCenter.Instance.DebugAIMoreMoney) { foreach (var playerData in mapData.PlayerMap.PlayerDataList) { if (playerData == mapData.PlayerMap.SelfPlayerData) continue; playerData.PlayerWealth += 10; } } } } }