本文主要是为了实现各种随机大小不一样的方块基础房间,便于 Roguelike 游戏地图使用,希望对各位独立游戏的新手有所帮助,同时也希望各位大牛可以给出宝贵意见。
由于我的思路是地图=基础房间+特殊房间(宝箱房、挑战房、任务房、Boss房),本文暂不涉及特殊房间以及个性化的多边形房间。
房间要求
- 房间大小基本符合一定的长宽比;
- 单个房间不会有重叠;
- 房间间保留一定间隙,方便后续填充围墙与走廊;
- 整体房间分布不是线性的,方便后续连通的多样性;
核心思路
- 根据游戏的操作,定下一个房间的长宽比;(本文的房间长宽比为1.6-2.0)
- 根据长宽比,取得1号房间的长与宽X1,Y1;
- 取得1号房间的中心点O1;
- 对O1进行一个四向的随机偏移,得到O2;
- 取得2号房间的长于宽X2,Y2;
- 利用射线检测,若无重叠则可生成,若有,重复4、5、6步骤直到可以生成;
- 铺设地毯、围墙等基本装饰物,完成。
效果图
代码
C#代码如下,包含三个主体方法:取得房间半径、取得房间中心点和生成房间。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MapManager : MonoBehaviour { // Use this for initialization private float roomRadii;//房间的长度半径 private float roomRatio;//由宽决定长的比值 public float roomX;//房间的长 public float roomY;//房间的宽 public float roomXi;//房间i的长 public float roomYi;//房间i的宽 public int roomXMin = 10; public int roomXMax = 16; public Vector2 posO;//房间中心点 int roomIndex = 0; public List roomPos = new List();//创建储存房间有效位置的二维表 public List offset = new List();//创建储存房间中心点的二维表 public GameObject[] floorArray; public Transform roomHolder; void Start() { roomPos.Clear();//后续房间内的所有可用位置都会添加到这个二维表里 SetRoomXY(); posO = new Vector2(0, 0); //生成12个房间进行检验 CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); CreateRoom(posO); } void SetRoomXY()//取得房间长宽半径 { roomRadii = Random.Range(roomXMin, roomXMax);//标准值取随机值 roomRatio = Random.Range(16, 20); roomRatio = roomRatio / 10; roomX = 2 * roomRadii;//房间的长是标准值的两倍,因此就是偶数 roomY = roomX / roomRatio;//房间的宽由长来决定 roomY = Mathf.Round(roomY);//对Y取整数 if (roomY % 2 != 0)//对Y进行判断,取偶数 { roomY += 1; } } Vector2 CreateCenter(Vector2 pos)//对中心点进行偏移 { int x = Random.Range(2 * roomXMin + 1, 2 * roomXMax + 1); offset.Clear(); for (int i = 2 * roomXMin + 1; i <= 2 * roomXMax + 1; i++) { for (int j = 30; j <= 50; j++) { offset.Add(new Vector2(i, j)); } } //现在取出一个符合基本规则的中心点偏移量 int index = Random.Range(0, offset.Count); float offsetX = offset[index].x; float offsetY = offset[index].y; Vector2 pos2 = new Vector2(0, 0); int o = Random.Range(0, 4); if (o == 0) { pos2 = pos + new Vector2(offsetX, offsetY); } if (o == 1) { pos2 = pos + new Vector2(offsetX, -offsetY); } if (o == 2) { pos2 = pos + new Vector2(-offsetX, offsetY); } if (o == 3) { pos2 = pos + new Vector2(-offsetX, -offsetY); } //现在取得了下一个房间中心 posO = pos2; return posO; } void CreateRoom(Vector2 pos)//生成基础房间(铺设地板) { CreateCenter(pos);//对上一个房间的中心点进行偏移,取得本次房间的中心点 pos = posO; SetRoomXY();//取得当前要生成房间的长宽半径 roomXi = roomX; roomYi = roomY; int isRoom = 0; //对预生成的房间先进行是否有房间的检测(基于房间九点:左上,中上,右上,左中……) Vector2 checkPos1 = new Vector2(pos.x - roomXi, pos.y - roomYi) + new Vector2(-4, -4); Vector2 checkPos2 = new Vector2(pos.x - roomXi, pos.y + roomYi) + new Vector2(-4, 4); Vector2 checkPos3 = new Vector2(pos.x + roomXi, pos.y - roomYi) + new Vector2(-4, 4); Vector2 checkPos4 = new Vector2(pos.x + roomXi, pos.y + roomYi) + new Vector2(4, 4); Vector2 checkPos6 = new Vector2(pos.x, pos.y - roomYi) + new Vector2(0, -4); Vector2 checkPos7 = new Vector2(pos.x, pos.y + roomYi) + new Vector2(0, 4); Vector2 checkPos8 = new Vector2(pos.x + roomXi, pos.y) + new Vector2(4, 0); Vector2 checkPos9 = new Vector2(pos.x - roomXi, pos.y) + new Vector2(-4, 0); RaycastHit2D hit1 = Physics2D.Linecast(checkPos1, checkPos1, 1 << LayerMask.NameToLayer("floor")); RaycastHit2D hit2 = Physics2D.Linecast(checkPos2, checkPos2, 1 << LayerMask.NameToLayer("floor")); RaycastHit2D hit3 = Physics2D.Linecast(checkPos3, checkPos3, 1 << LayerMask.NameToLayer("floor")); RaycastHit2D hit4 = Physics2D.Linecast(checkPos4, checkPos4, 1 << LayerMask.NameToLayer("floor")); RaycastHit2D hit5 = Physics2D.Linecast(pos, pos, 1 << LayerMask.NameToLayer("floor")); RaycastHit2D hit6 = Physics2D.Linecast(checkPos6, checkPos6, 1 << LayerMask.NameToLayer("floor")); RaycastHit2D hit7 = Physics2D.Linecast(checkPos7, checkPos7, 1 << LayerMask.NameToLayer("floor")); RaycastHit2D hit8 = Physics2D.Linecast(checkPos8, checkPos8, 1 << LayerMask.NameToLayer("floor")); RaycastHit2D hit9 = Physics2D.Linecast(checkPos9, checkPos9, 1 << LayerMask.NameToLayer("floor")); if (hit1.collider == null && hit2.collider == null && hit3.collider == null && hit4.collider == null && hit5.collider == null && hit6.collider == null && hit7.collider == null && hit8.collider == null && hit9.collider == null) { isRoom += 1; } if (isRoom >= 0) { if (isRoom == 0) { CreateRoom(pos); } else { roomIndex += 1;//可以生成房间的时候,给房间记上标记 roomHolder = new GameObject("Room" + roomIndex).transform;//给父类添加名字 //为地板进行二维数组赋值 for (float x = pos.x - roomXi; x <= pos.x + roomXi; x++) { for (float y = pos.y - roomYi; y <= pos.y + roomYi; y++) { Vector2 posFloor = new Vector2(x, y); roomPos.Add(new Vector2(x, y)); int i = Random.Range(0, floorArray.Length); GameObject go = Instantiate(floorArray[i], posFloor, Quaternion.identity); go.transform.SetParent(roomHolder); } } } } } }
关于连通与特殊房,我的想法是因为生成的时候给房间以及进行序号标记了(1-12),先依次对中心点进行走廊生成,以及门生成,确保主干道连通,再取随机房间进行连通丰富路线(如2和4,6和8),在这个基础上加入预设的特殊房。(特殊房应该有足够的设计量,因此不适宜用随机。)
嗨呀,没想到被推荐了,这种分享的成就感和喜悦还是很大的,在此谢谢编辑大大。
写这篇文章主要的目的是当时为了想要实现房间生成的时候,网上找了大半天的都没有合适使用的,由于自己目前还是新手上路,网上能找到的对我来讲又晦涩难懂……自己完成后就分享出来,希望可以给有需要的人带来帮助。
另外希望各位有经验的大大可以分享下关于 [实现联通] 的一些经验,同时希望IndieNova的讨论氛围可以越来越活跃。
@sTarrr_/A/:感谢您的投稿!咱们慢慢讨论!
题图赞,Nethack啊
题图是nethack的城堡层吗..