Irondust | Дата: Вторник, 15 Января 2013, 12:35 | Сообщение # 1 |
участник
Сейчас нет на сайте
| Работал над генератором подземелий для обучения, чуть больше недели. Результаты вот:
Играбельная демка Сначала выбираете размер лабиринта, потом выбираете либо просмотр сверху, либо побегать от первого лица. Самый большой лабиринт может генерироваться очень долго.
Код using UnityEngine; using System.Collections; using System.Collections.Generic; public class LevelGenerator : MonoBehaviour { public int blockSize; private int size = 5; private Cell[,] grid; private GameObject[] cubes; private class Block { public int startX, endX, startY, endY, bdirection; public bool roomPlaced; public bool[] corridorPlaced; public float RandomNumber; public Block parentBlock, twinBlock; } int splitIterations = 0; private List<Block> blocks = new List<Block>(); public GameObject wallPrefab, borderPrefab; private Block startBlock = new Block(); public enum LevelSize {tiny = 10, small = 20, medium = 50, large = 80, huge = 160} public LevelSize levelSize = LevelSize.medium; public float squarity = 0.4F; // Use this for initialization void Start () { } public void ClearCubes () { foreach( GameObject c in cubes){ Destroy(c); } } public void StartGeneration () { StopAllCoroutines(); blocks.Clear(); startBlock = new Block(); size = (int)levelSize; grid = new Cell[size,size]; cubes = new GameObject[size*size]; for (int i = 0; i<size; i++){ for (int j = 0; j<size; j++){ grid[i,j] = new Cell(); grid[i,j].type = Cell.Type.wall; } } startBlock.startX = 0; startBlock.startY = 0; startBlock.endX = size; startBlock.endY = size; startBlock.RandomNumber = Random.Range(0.0F, 100.0F); startBlock.parentBlock = new Block(); SplitPrepare(); StartCoroutine(WaitForCalc()); } void SplitPrepare () { switch (levelSize){ case LevelSize.tiny: splitIterations = 2; break; case LevelSize.small: splitIterations = 3; break; case LevelSize.medium: splitIterations = 4; break; case LevelSize.large: splitIterations = 5; break; case LevelSize.huge: splitIterations = 7; break; } float sizeOfMassive = Mathf.Pow(2.0F,splitIterations); SplitLevel(startBlock, splitIterations); } void SplitLevel (Block bTS, int iter) { int blockN=-1; if(iter>1){ Block[] pro = Splitting(bTS, iter-1); foreach (Block b in pro){ SplitLevel(b,iter-1); } } } Block[] Splitting (Block splitBlock, int iter) { int direction = Random.Range(0,2); int position; Block[] retBlock = new Block[2]; int pogr, kl; int length = splitBlock.endY-splitBlock.startY; int width = splitBlock.endX-splitBlock.startX; if(direction==0){ kl = blocks.Count; for (int l = 0; l<kl; l++){ if(splitBlock.RandomNumber==blocks[l].RandomNumber){ blocks.RemoveAt(l); kl--; } } pogr = (int)(width*squarity); position = Random.Range(splitBlock.startX+pogr, splitBlock.endX-pogr); for (int i = splitBlock.startY; i<splitBlock.endY; i++){ grid[position,i].isBorder = true; } retBlock[0] = new Block{ endX = position, endY = splitBlock.endY, startX = splitBlock.startX, startY = splitBlock.startY, RandomNumber = Random.Range(0.0F, 100.0F), parentBlock = splitBlock, corridorPlaced = new bool[splitIterations-1], bdirection = direction}; retBlock[1] = new Block{startX = position, endX = splitBlock.endX,startY = splitBlock.startY, endY = splitBlock.endY, RandomNumber = Random.Range(0.0F, 100.0F),parentBlock = splitBlock, corridorPlaced = new bool[splitIterations-1], bdirection = direction}; retBlock[0].twinBlock = retBlock[1]; retBlock[1].twinBlock = retBlock[0]; blocks.Add(retBlock[0]); blocks.Add(retBlock[1]); } else { kl = blocks.Count; for (int l = 0; l<kl; l++){ if(splitBlock.RandomNumber==blocks[l].RandomNumber){ blocks.RemoveAt(l); kl--; } } pogr = (int)(length*squarity); position = Random.Range(splitBlock.startY+pogr, splitBlock.endY-pogr); for (int i = splitBlock.startX; i<splitBlock.endX; i++){ grid[i,position].isBorder = true; } retBlock[0] = new Block{startX = splitBlock.startX, endX = splitBlock.endX, startY = splitBlock.startY, endY = position, RandomNumber = Random.Range(0.0F, 100.0F), parentBlock = splitBlock, corridorPlaced = new bool[splitIterations], bdirection = direction}; retBlock[1] = new Block{startX = splitBlock.startX, endX = splitBlock.endX, startY = position, endY = splitBlock.endY, RandomNumber = Random.Range(0.0F, 100.0F), parentBlock = splitBlock, corridorPlaced = new bool[splitIterations], bdirection = direction}; retBlock[0].twinBlock = retBlock[1]; retBlock[1].twinBlock = retBlock[0]; blocks.Add(retBlock[0]); blocks.Add(retBlock[1]);
} return retBlock; } IEnumerator WaitForCalc () { yield return new WaitForSeconds(0.5F); PlaceRooms(); yield return new WaitForSeconds(0.5F); BuildCorridors(); yield return new WaitForSeconds(0.5F); BuildLevel (); }
void BuildCorridors () { foreach(Block jB in blocks){ BuildCorridor(jB,splitIterations-2); } } void PlaceRooms () { for (int u = 0; u<blocks.Count;u++){ int length = (blocks[u].endY-blocks[u].startY)/2; int width = (blocks[u].endX-blocks[u].startX)/2; int positionX = width+blocks[u].startX; int positionY = length+blocks[u].startY; int roomSizeFinalX = (int)(width*Random.Range(0.65F,0.8F)); int roomSizeFinalY = (int)(length*Random.Range(0.65F,0.8F)); if(roomSizeFinalX==0)roomSizeFinalX=1; if(roomSizeFinalY==0)roomSizeFinalY=1; for (int i = 0; i<roomSizeFinalX;i++){ for (int j = 0; j<roomSizeFinalY;j++){ grid[positionX+i, positionY+j].type = Cell.Type.room; grid[positionX-i, positionY-j].type = Cell.Type.room; grid[positionX+i, positionY-j].type = Cell.Type.room; grid[positionX-i, positionY+j].type = Cell.Type.room; } } } } void BuildLevel () { int k = 0; for (int i = 0; i<size; i++){ for (int j = 0; j<size; j++){ if(grid[i,j].type == Cell.Type.room || grid[i,j].type==Cell.Type.corridor){ cubes[k] = Instantiate(borderPrefab,new Vector3(i*blockSize,0.0F,j*blockSize),Quaternion.identity) as GameObject; } else { cubes[k] = Instantiate(wallPrefab,new Vector3(i*blockSize,0.0F,j*blockSize),Quaternion.identity) as GameObject; } grid[i,j].number = k; k++; } } for (int i=1; i<size-1; i++){ for (int j = 1; j<size-1;j++){ if(grid[i,j].type == Cell.Type.wall){ if(checkNe(i,j)){ Debug.Log("destroy"); Destroy(cubes[grid[i,j].number]); } } } } } bool checkNe (int i, int j){ bool[] ne = new bool[8]; ne[0] = grid[i,j+1].type==grid[i,j].type; ne[1] = grid[i,j-1].type==grid[i,j].type; ne[2] = grid[i+1,j+1].type==grid[i,j].type; ne[3] = grid[i+1,j-1].type==grid[i,j].type; ne[4] = grid[i+1,j].type==grid[i,j].type; ne[5] = grid[i-1,j+1].type==grid[i,j].type; ne[6] = grid[i-1,j-1].type==grid[i,j].type; ne[7] = grid[i-1,j].type==grid[i,j].type; bool neA=true; for (int o = 0; o<8; o++){ if(!ne[o]){ neA = false; break; } } return neA; } void BuildCorridor (Block joinBlock, int iteration) { bool going=true; int nedost=0; if(iteration>=0){ if(!joinBlock.corridorPlaced[iteration]){ ///Building corridor if(joinBlock.bdirection == 0){ int length = ((joinBlock.endY - joinBlock.startY)/2); int corY = (length+joinBlock.startY) + (int)(length*Random.Range(-0.3F,0.3F)); if(joinBlock.startX<=joinBlock.twinBlock.startX){ //right nedost = joinBlock.endX; } else { //left nedost = joinBlock.twinBlock.endX; } going = true; int i = 0; while (going&&(nedost+i)<size) { if(grid[nedost+i,corY].type == Cell.Type.wall){ grid[nedost+i,corY].type = Cell.Type.corridor; i++; } else { going = false; } } i=1; going = true; while (going&&(nedost-i)>=0) { if(grid[nedost-i,corY].type == Cell.Type.wall|| grid[nedost-i,corY].type == Cell.Type.corridor){ grid[nedost-i,corY].type = Cell.Type.corridor; i++; } else { going = false; } } } else { int width = ((joinBlock.endX - joinBlock.startX)/2); int corX = (width+joinBlock.startX) + (int)(width*Random.Range(-0.4F,0.4F)); if(joinBlock.startY<=joinBlock.twinBlock.startY){ //up nedost = joinBlock.endY; } else { //down nedost = joinBlock.twinBlock.endY; } int i = 0; going = true; while (going&&(nedost+i)<size) { if(grid[corX,nedost+i].type == Cell.Type.wall || grid[corX,nedost+i].type == Cell.Type.corridor){ grid[corX,nedost+i].type = Cell.Type.corridor; i++; } else { going = false; } } i=1; going = true; while (going&&(nedost-i)>=0) { if(grid[corX,nedost-i].type == Cell.Type.wall){ grid[corX,nedost-i].type = Cell.Type.corridor; i++; } else { going = false; } } } ///Building/ joinBlock.corridorPlaced[iteration] = true; joinBlock.twinBlock.corridorPlaced[iteration] = true; } BuildCorridor(joinBlock.parentBlock, iteration-1); } }
}
public class Cell { public enum Type {clear, wall, room, entrance, corridor} public Type type; public bool isBorder = false; public int number; } Комментарии писать было влом, спрашивайте если что непонятно.
Сообщение отредактировал Irondust - Среда, 16 Января 2013, 09:25 |
|
| |