/**********************************************************************************/ // определяем правила соединения блоков перед генерацией карты // /**********************************************************************************/ protected void BuildRoadRules(int xSize, int ySize, float chanseOfConnection = 0.1f) { // проверки параметров if (xSize <= 0 || ySize <= 0 || chanseOfConnection < 0) { Debug.LogError("BuildRoadRules: Wrong arguments! " + "xSize " + xSize + " ySize " + ySize + "chanseOfConnection " + chanseOfConnection); return; } // генерируем новые правила соединений for (int i = 0; i < xSize; i++) { for (int z = 0; z < ySize; z++) { for (int direction = (int)Base.DIREC.DOWN; direction <= (int)Base.DIREC.LEFT; direction++) { float currectConnectionChance = Random.Range(0.0f, 1.0f); if (currectConnectionChance <= chanseOfConnection) { BlockDescriptorImitation bd = m_blockRoadRulesDescriptor[i, z]; if (bd.RoadConnections[direction] != ROAD_CONNECTION_STATUS.BLOCKED) { bd.RoadConnections[direction] = ROAD_CONNECTION_STATUS.NEEDED; } UpdateRulesForBlock(i, z, bd.RoadConnections); } } } } }
/**********************************************************************************/ // BlockDescriptorImitation копирующий конструктор // /**********************************************************************************/ public BlockDescriptorImitation(BlockDescriptorImitation other) { RoadConnections = new ROAD_CONNECTION_STATUS[(int)Base.DIREC.NUM_OF_DIRECTIONS]; for (int direction = (int)Base.DIREC.DOWN; direction < (int)Base.DIREC.NUM_OF_DIRECTIONS; direction++) { RoadConnections[direction] = other.RoadConnections[direction]; } }
/**********************************************************************************/ // функция передающая информацию о соединениях блоков друг с другом // /**********************************************************************************/ public void ApplyBlockConnectionData(BlockDescriptorImitation[,] blockConnectionData) { for (int x = 0; x < m_xSizeOfMap; x++) { for (int y = 0; y < m_ySizeOfMap; y++) { m_blockConnectionData[x, y] = new BlockDescriptorImitation(blockConnectionData[x, y]); } } }
/**********************************************************************************/ // записываем правила дорог в дескриптор блока // как правило это используется для генерируемых блоков // /**********************************************************************************/ public void SetRoadRulesToBlock(BlockDescriptor descriptor, int x, int y) { // проверки параметров if (x < 0 || y < 0 || descriptor == null) { Debug.LogError("SetRoadRulesToBlock: Wrong arguments! " + "x " + x + " y " + y + "descriptor is null? " + (descriptor == null).ToString()); return; } BlockDescriptorImitation bd = m_blockRoadRulesDescriptor[x, y]; for (int direction = (int)Base.DIREC.DOWN; direction < (int)Base.DIREC.NUM_OF_DIRECTIONS; direction++) { descriptor.RoadConnections[direction] = bd.RoadConnections[direction]; } }
/**********************************************************************************/ // маркируем клетку по статусу проходимости // /**********************************************************************************/ private void MarkCellWayStatus(int xPoint, int yPoint, bool cellIsFree) { // маркеруем соседние проходы в соответсвии со статусом текущей клетки ROAD_CONNECTION_STATUS status = ROAD_CONNECTION_STATUS.POSSIBLE; if (cellIsFree == false) { status = ROAD_CONNECTION_STATUS.BLOCKED; } // верхняя точка BlockDescriptorImitation bdi = GetGlobalConnectionCell(xPoint, yPoint + 1); if (bdi != null) { bdi.RoadConnections[(int)Base.DIREC.DOWN] = status; } // нижняя точка bdi = GetGlobalConnectionCell(xPoint, yPoint - 1); if (bdi != null) { bdi.RoadConnections[(int)Base.DIREC.UP] = status; } // левая точка bdi = GetGlobalConnectionCell(xPoint - 1, yPoint); if (bdi != null) { bdi.RoadConnections[(int)Base.DIREC.RIGHT] = status; } // правая точка bdi = GetGlobalConnectionCell(xPoint + 1, yPoint); if (bdi != null) { bdi.RoadConnections[(int)Base.DIREC.LEFT] = status; } }
/**********************************************************************************/ // возвращаем информацию о возможных переходах между кдетками // /**********************************************************************************/ BlockDescriptorImitation GetGlobalConnectionCell(int x, int y) { // проверяем координаты if (x >= m_connectionGlobalMap.GetLength(0) || x < 0) { return(null); } if (y >= m_connectionGlobalMap.GetLength(1) || y < 0) { return(null); } // если BDI ещё не использовался(создавался) - создаём if (m_connectionGlobalMap[x, y] == null) { m_connectionGlobalMap[x, y] = new BlockDescriptorImitation(); } BlockDescriptorImitation bdi = m_connectionGlobalMap[x, y]; return(bdi); }
/**********************************************************************************/ // проверяем, подойдёт ли блок в эту позицию // /**********************************************************************************/ public bool IsBlockOk(GameObject block, int x, int y) { // проверка параметров if (block == null || x <= 0 || y <= 0) { Debug.LogError("IsBlockOk: wrong argument " + "block is null? " + (block == null).ToString() + " x " + x + " y " + y); return(false); } BlockDescriptor bd = block.GetComponent <BlockDescriptor>(); if (bd == null) { Debug.LogError("bd descriptor is null!"); return(false); } BlockDescriptorImitation bdToCompaire = m_blockRoadRulesDescriptor[x, y]; if (bdToCompaire == null) { Debug.LogError("bdToCompaire descriptor is null!"); return(false); } // проверяем все стороны на возможность подключения дорог for (int direction = (int)Base.DIREC.DOWN; direction < (int)Base.DIREC.NUM_OF_DIRECTIONS; direction++) { if ((bdToCompaire.RoadConnections[direction] == ROAD_CONNECTION_STATUS.BLOCKED || bd.RoadConnections[direction] == ROAD_CONNECTION_STATUS.BLOCKED) && (bdToCompaire.RoadConnections[direction] == ROAD_CONNECTION_STATUS.NEEDED || bd.RoadConnections[direction] == ROAD_CONNECTION_STATUS.NEEDED)) { return(false); } } return(true); }
/**********************************************************************************/ // Test3_3 // тест для BuildRoadRules функции // /**********************************************************************************/ string Test3_3(ref bool result) { string Name = "BuildRoadRules 3"; // тест int xSize = 9; int ySize = 9; m_blockRoadRulesDescriptor = new BlockDescriptorImitation[xSize, ySize]; // подготавливаем структуры для теста for (int x = 0; x < xSize; x++) { for (int y = 0; y < ySize; y++) { BlockDescriptorImitation bd = new BlockDescriptorImitation(); // для начала устанавливаем, что все соединения возможны bd.RoadConnections[(int)Base.DIREC.DOWN] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.LEFT] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.UP] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.RIGHT] = ROAD_CONNECTION_STATUS.POSSIBLE; // выставляем ограничители от краёв карты // блоки у краёв карты не должны соединяться с лесом if (x == 0) { bd.RoadConnections[(int)Base.DIREC.LEFT] = ROAD_CONNECTION_STATUS.BLOCKED; } if (y == 0) { bd.RoadConnections[(int)Base.DIREC.DOWN] = ROAD_CONNECTION_STATUS.BLOCKED; } if (x == xSize - 1) { bd.RoadConnections[(int)Base.DIREC.RIGHT] = ROAD_CONNECTION_STATUS.BLOCKED; } if (y == ySize - 1) { bd.RoadConnections[(int)Base.DIREC.UP] = ROAD_CONNECTION_STATUS.BLOCKED; } this.m_blockRoadRulesDescriptor[x, y] = bd; } } this.BuildRoadRules(xSize, ySize); // проверяем, что при стандартных настройках мы вносим какое-то кол-во рандома в карту // для успешного прохождения теста достаточно всего одного коннекшена bool isFine = false; for (int x = 1; x < xSize - 1; x++) { for (int y = 1; y < ySize - 1; y++) { if (this.m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)Base.DIREC.DOWN] == ROAD_CONNECTION_STATUS.NEEDED || this.m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)Base.DIREC.LEFT] == ROAD_CONNECTION_STATUS.NEEDED || this.m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)Base.DIREC.RIGHT] == ROAD_CONNECTION_STATUS.NEEDED || this.m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)Base.DIREC.UP] == ROAD_CONNECTION_STATUS.NEEDED ) { isFine = true; } } } result = isFine; return(Name); }
/**********************************************************************************/ // Test3_2 // тест для BuildRoadRules функции // /**********************************************************************************/ string Test3_2(ref bool result) { string Name = "BuildRoadRules 2"; // тест int xSize = 11; int ySize = 11; m_blockRoadRulesDescriptor = new BlockDescriptorImitation[xSize, ySize]; // подготавливаем структуры для теста for (int x = 0; x < xSize; x++) { for (int y = 0; y < ySize; y++) { BlockDescriptorImitation bd = new BlockDescriptorImitation(); // для начала устанавливаем, что все соединения возможны bd.RoadConnections[(int)Base.DIREC.DOWN] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.LEFT] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.UP] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.RIGHT] = ROAD_CONNECTION_STATUS.POSSIBLE; // выставляем ограничители от краёв карты // блоки у краёв карты не должны соединяться с лесом if (x == 0) { bd.RoadConnections[(int)Base.DIREC.LEFT] = ROAD_CONNECTION_STATUS.BLOCKED; } if (y == 0) { bd.RoadConnections[(int)Base.DIREC.DOWN] = ROAD_CONNECTION_STATUS.BLOCKED; } if (x == xSize - 1) { bd.RoadConnections[(int)Base.DIREC.RIGHT] = ROAD_CONNECTION_STATUS.BLOCKED; } if (y == ySize - 1) { bd.RoadConnections[(int)Base.DIREC.UP] = ROAD_CONNECTION_STATUS.BLOCKED; } this.m_blockRoadRulesDescriptor[x, y] = bd; } } this.BuildRoadRules(xSize, ySize, 1.0f); // проверяем, что все до одного правила в центральной части карты были установлены bool isFine = true; for (int x = 1; x < xSize - 1; x++) { for (int y = 1; y < ySize - 1; y++) { if (this.m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)Base.DIREC.DOWN] != ROAD_CONNECTION_STATUS.NEEDED || this.m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)Base.DIREC.LEFT] != ROAD_CONNECTION_STATUS.NEEDED || this.m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)Base.DIREC.RIGHT] != ROAD_CONNECTION_STATUS.NEEDED || this.m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)Base.DIREC.UP] != ROAD_CONNECTION_STATUS.NEEDED ) { isFine = false; } } } result = isFine; return(Name); }
/**********************************************************************************/ // обновляем правила дорог для блока // если этого не сделать, то возможны колизии в соседних блоках, особенно это касается предопределённых блоков // /**********************************************************************************/ public void UpdateRulesForBlock(int x, int y, ROAD_CONNECTION_STATUS[] roadConnections) { // проверки параметров if (x < 0 || y < 0 || roadConnections == null || roadConnections.Length != (int)Base.DIREC.NUM_OF_DIRECTIONS) { Debug.LogError("UpdateRulesForBlock: Wrong arguments! " + "x " + x + " y " + y + "roadConnections is null? " + (roadConnections == null).ToString() + " roadConnections.Length " + roadConnections.Length); return; } // обновляем правила для текущего и соседних блоков for (int i = 0; i < (int)Base.DIREC.NUM_OF_DIRECTIONS; i++) { if (roadConnections[i] != ROAD_CONNECTION_STATUS.BLOCKED) { m_blockRoadRulesDescriptor[x, y].RoadConnections[i] = ROAD_CONNECTION_STATUS.NEEDED; roadConnections[i] = ROAD_CONNECTION_STATUS.NEEDED; } else { m_blockRoadRulesDescriptor[x, y].RoadConnections[i] = ROAD_CONNECTION_STATUS.BLOCKED; } } // проходим по окрестным блокам и обновляем условия // правила должны быть синхронизированы в соседних блоках for (int direction = (int)Base.DIREC.DOWN; direction < (int)Base.DIREC.NUM_OF_DIRECTIONS; direction++) { Point blockToUpdateCor = new Point(x, y); switch (direction) { case (int)Base.DIREC.DOWN: blockToUpdateCor.y--; break; case (int)Base.DIREC.UP: blockToUpdateCor.y++; break; case (int)Base.DIREC.LEFT: blockToUpdateCor.x--; break; case (int)Base.DIREC.RIGHT: blockToUpdateCor.x++; break; default: Debug.LogError("PrepareBluprintOfRoads: wrong direction!"); break; } // проверяем координыты if (blockToUpdateCor.x < 0 || blockToUpdateCor.x >= m_blockRoadRulesDescriptor.GetLength(0) || blockToUpdateCor.y < 0 || blockToUpdateCor.y >= m_blockRoadRulesDescriptor.GetLength(1)) { continue; } BlockDescriptorImitation blockToUpdates = m_blockRoadRulesDescriptor[blockToUpdateCor.x, blockToUpdateCor.y]; Base.DIREC connectionToUpdate = Base.InvertDirection(direction); // синхронизируем blockToUpdates.RoadConnections[(int)connectionToUpdate] = m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)direction]; } }
/**********************************************************************************/ // проверка доступности блоков // если какие-то правила мешают доступности блоков - это необходимо исправить // /**********************************************************************************/ protected bool CheckRoadRules(int xSize, int ySize) { // проверки параметров if (xSize <= 0 || ySize <= 0) { Debug.LogError("CheckRoadRules: Wrong size of map! " + xSize.ToString() + ":" + ySize.ToString()); return(false); } // начинаем проверку соединений с левого нижнего угла // волновой алгорим Point startPoint = new Point(0, 0); LinkedList <Point> pointsToProcess = new LinkedList <Point>(); pointsToProcess.AddFirst(startPoint); // вспомогательные структуры для подсчёта кол-ва соединений int linkedBlocksCounter = 0; bool[,] connectedBlocks = new bool[xSize, ySize]; for (int i = 0; i < xSize; i++) { for (int y = 0; y < ySize; y++) { connectedBlocks[i, y] = false; } } connectedBlocks[0, 0] = true; linkedBlocksCounter++; while (pointsToProcess.Count > 0) { Point pointToCheck = pointsToProcess.First.Value; pointsToProcess.RemoveFirst(); BlockDescriptorImitation bd = m_blockRoadRulesDescriptor[pointToCheck.x, pointToCheck.y]; if (bd.RoadConnections[(int)Base.DIREC.DOWN] != ROAD_CONNECTION_STATUS.BLOCKED) { Point newPointToCheck = new Point(pointToCheck.x, pointToCheck.y - 1); // если точка ещё не была достижима - увеличиваем счётчик и маркируем её как достижимую if (!connectedBlocks[newPointToCheck.x, newPointToCheck.y]) { connectedBlocks[newPointToCheck.x, newPointToCheck.y] = true; linkedBlocksCounter++; pointsToProcess.AddLast(newPointToCheck); } } if (bd.RoadConnections[(int)Base.DIREC.UP] != ROAD_CONNECTION_STATUS.BLOCKED) { Point newPointToCheck = new Point(pointToCheck.x, pointToCheck.y + 1); // если точка ещё не была достижима - увеличиваем счётчик и маркируем её как достижимую if (!connectedBlocks[newPointToCheck.x, newPointToCheck.y]) { connectedBlocks[newPointToCheck.x, newPointToCheck.y] = true; linkedBlocksCounter++; pointsToProcess.AddLast(newPointToCheck); } } if (bd.RoadConnections[(int)Base.DIREC.LEFT] != ROAD_CONNECTION_STATUS.BLOCKED) { Point newPointToCheck = new Point(pointToCheck.x - 1, pointToCheck.y); // если точка ещё не была достижима - увеличиваем счётчик и маркируем её как достижимую if (!connectedBlocks[newPointToCheck.x, newPointToCheck.y]) { connectedBlocks[newPointToCheck.x, newPointToCheck.y] = true; linkedBlocksCounter++; pointsToProcess.AddLast(newPointToCheck); } } if (bd.RoadConnections[(int)Base.DIREC.RIGHT] != ROAD_CONNECTION_STATUS.BLOCKED) { Point newPointToCheck = new Point(pointToCheck.x + 1, pointToCheck.y); // если точка ещё не была достижима - увеличиваем счётчик и маркируем её как достижимую if (!connectedBlocks[newPointToCheck.x, newPointToCheck.y]) { connectedBlocks[newPointToCheck.x, newPointToCheck.y] = true; linkedBlocksCounter++; pointsToProcess.AddLast(newPointToCheck); } } } if (linkedBlocksCounter != xSize * ySize) { return(false); } else { return(true); } }
/**********************************************************************************/ // ресетим карту дорог и карту зависимостей // устанавливаем новые рандомные зависимости между блоками // /**********************************************************************************/ public void ResetRoadMap(int xSize, int ySize, int SizeOfBlock) { // проверки параметров if (xSize <= 0 || ySize <= 0) { Debug.LogError("ResetRoadMap: Wrong size of map! " + xSize.ToString() + ":" + ySize.ToString()); return; } // сбрасываем старые правила соединений m_blockRoadRulesDescriptor = new BlockDescriptorImitation[xSize, ySize]; bool mapIsFine = false; while (!mapIsFine) { for (int x = 0; x < xSize; x++) { for (int y = 0; y < ySize; y++) { BlockDescriptorImitation bd = new BlockDescriptorImitation(); // для начала устанавливаем, что все соединения возможны bd.RoadConnections[(int)Base.DIREC.DOWN] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.LEFT] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.UP] = ROAD_CONNECTION_STATUS.POSSIBLE; bd.RoadConnections[(int)Base.DIREC.RIGHT] = ROAD_CONNECTION_STATUS.POSSIBLE; // выставляем ограничители от краёв карты // блоки у краёв карты не должны соединяться с лесом if (x == 0) { bd.RoadConnections[(int)Base.DIREC.LEFT] = ROAD_CONNECTION_STATUS.BLOCKED; } if (y == 0) { bd.RoadConnections[(int)Base.DIREC.DOWN] = ROAD_CONNECTION_STATUS.BLOCKED; } if (x == xSize - 1) { bd.RoadConnections[(int)Base.DIREC.RIGHT] = ROAD_CONNECTION_STATUS.BLOCKED; } if (y == ySize - 1) { bd.RoadConnections[(int)Base.DIREC.UP] = ROAD_CONNECTION_STATUS.BLOCKED; } m_blockRoadRulesDescriptor[x, y] = bd; } } // определяем правила соединения блоков перед генерацией карты BuildRoadRules(xSize, ySize); // проверка доступности блоков и правка правил mapIsFine = CheckRoadRules(xSize, ySize); } // обновляем массив "дорожных" пометок m_roadObjectMap = new bool[xSize * SizeOfBlock, ySize *SizeOfBlock]; }