现在也是处于失业状态,碰巧看到个面试题是要用unity生成个随机地牢,就把做题过程中的思路和代码记录一下吧。

做完了以后我又想了一下,发现其实根本不需要这么麻烦,果然demo里的代码对我的思路影响还是有点大。demo里的c++代码为了展示地牢的墙壁,在二维数组中加上了wall这个东西表示墙壁。事实上用unity来做的话,只需要考虑地板的位置,然后根据邻接的地板有没有东西来判断是否生成墙壁即可。

Demo 使用素材以及题目地址:http://pan.baidu.com/s/1c2l3RFE 密码:aseh

首先用一个枚举类型代表地牢迷宫中的各个元素:

public enum Tile
{
Default,
DirtFloor,// 房间地板
Wall_w,//上方的墙
Wall_s,//下方的墙
Wall_a,//左方的墙
Wall_d,//右方的墙
Corridor_ad,//横向走廊
Corridor_ws,//纵向走廊
Door,// 房门
UpStairs,// 入口
DownStairs// 出口
}

然后考虑使用二维数组来保存地牢元素的信息。既然是用unity来做,先不考虑随机地牢的逻辑数组要怎么生成,先把二维数组转化为实体的方法写出来:

建立一个test脚本,用于测试生成用的creat_dungeon方法是否好用,并在里面定义一个测试用的二维数组:

using UnityEngine;
using System.Collections;
using System;
public class test : MonoBehaviour {
void Start () {
Action _Instantiate=(t,v)=>{Instantiate(t,v,t.rotation);};//这里考虑不当,理论上不应该在test脚本中写Instantiate方法,而是应该在creat_dungeon中用。不过既然写出来了也能显示我懂一些委托的知识...将错就错好了
Tile[,] _map=new Tile[,];
_map [, ] = Tile.Wall_s;
_map [, ] = Tile.Wall_s;
_map [, ] = Tile.DirtFloor;
_map [, ] = Tile.DirtFloor;
_map [, ] = Tile.DirtFloor;
_map [, ] = Tile.DirtFloor;
_map [, ] = Tile.Corridor_ws;
_map [, ] = Tile.Wall_w;
_map [, ] = Tile.Corridor_ad;
_map [, ] = Tile.Wall_d;
_map [, ] = Tile.Corridor_ad;
_map [, ] = Tile.Wall_a;//自定义一个地图
creat_dungeon.Instance.creat (_map,_Instantiate);//调用生成函数
}

接下去就是考虑怎么写creat_dungeon这个类了,首先这个类只用于生成实体,所以不应该存在多份,所以设计成单例:

public class creat_dungeon{
private static creat_dungeon _instance;
private creat_dungeon(){}
public static creat_dungeon Instance{
get
{
if (_instance == null)
{
_instance = new creat_dungeon ();
}
return _instance;
}
}
}

然后这个类中肯定需要动态加载预设的资源,这里用resources.load方法加载,在初始化函数中加上加载资源的代码:

private Transform floor,pillar,wall_ws,wall_ad;
private creat_dungeon(){
floor = Resources.Load ("Floor_1_Prefab",typeof (Transform)) as Transform;
pillar = Resources.Load ("Pillar_1_Prefab",typeof (Transform))as Transform;
wall_ws = Resources.Load ("Wall_w", typeof(Transform))as Transform;
wall_ad = Resources.Load ("Wall_a", typeof(Transform))as Transform; }

然后根据资源模型的大小,定义一个常量:

private float floor_length=1.518111f;

最后就是其中生成用的代码:

/// 根据二维数组实例化地牢
public void creat(Tile[,] map,Action Instantiate){
for (int i = ; i < map.GetLength (); i++)
for (int j = ; j < map.GetLength (); j++) {
switch (map [i, j]) {
case (Tile.DirtFloor)://地板的场合
Instantiate (floor,new Vector3(i*floor_length,,j*floor_length));
break;
case (Tile.Wall_s)://墙壁的场合
Instantiate (wall_ws,new Vector3((i+0.5f)*floor_length,,j*floor_length));
Instantiate (pillar,new Vector3((i+0.5f)*floor_length,,(j+0.5f)*floor_length));//在墙壁对应的位置生成柱子,由于最终生成的是一个封闭的空间,所以墙壁和柱子的数目必定是相等的。
break;
case (Tile.Wall_w):
Instantiate (wall_ws,new Vector3((i-0.5f)*floor_length,,j*floor_length));
Instantiate (pillar,new Vector3((i-0.5f)*floor_length,,(j-0.5f)*floor_length));
break;
case (Tile.Wall_a):
Instantiate (wall_ad,new Vector3((i)*floor_length,,(j+0.5f)*floor_length));
Instantiate (pillar,new Vector3((i-0.5f)*floor_length,,(j+0.5f)*floor_length));
break;
case (Tile.Wall_d):
Instantiate (wall_ad,new Vector3((i)*floor_length,,(j-0.5f)*floor_length));
Instantiate (pillar,new Vector3((i+0.5f)*floor_length,,(j-0.5f)*floor_length));
break;
case (Tile.Corridor_ad)://走廊的话,则需要额外生成两面墙和两根柱子,不妨想想为什么要这么做
Instantiate (floor,new Vector3(i*floor_length,,j*floor_length));
Instantiate (wall_ws,new Vector3((i+0.5f)*floor_length,,j*floor_length));
Instantiate (wall_ws,new Vector3((i-0.5f)*floor_length,,j*floor_length));
Instantiate (pillar,new Vector3((i-0.5f)*floor_length,,(j+0.5f)*floor_length));
Instantiate (pillar,new Vector3((i+0.5f)*floor_length,,(j-0.5f)*floor_length));
break;
case (Tile.Corridor_ws):
Instantiate (floor,new Vector3(i*floor_length,,j*floor_length));
Instantiate (wall_ad,new Vector3(i*floor_length,,(j+0.5f)*floor_length));
Instantiate (wall_ad,new Vector3(i*floor_length,,(j-0.5f)*floor_length));
Instantiate (pillar,new Vector3((i+0.5f)*floor_length,,(j+0.5f)*floor_length));
Instantiate (pillar,new Vector3((i-0.5f)*floor_length,,(j-0.5f)*floor_length));
break;
default:
break;
}
}
}

最后运行效果如下:

可以看到三个走廊没有封上口,因为这里预定走廊两端必须要连接房间,不能出现死胡同的情况,这里可以看到左侧和右侧走廊的柱子是可以对上的。

好,这样的话转化为实体的方法就写好了,现在考虑怎么生成逻辑地形。

思路是这样的:第一次生成房间时,直接在地图最中间生成,之后每次生成一个房间和一个走廊,把走廊和之前造出来的东西连起来。考虑到不想让走廊拐弯,所以每次先生成走廊再生成房间。

最终map类的代码如下:

using UnityEngine;
using System.Collections;
using System.Collections.Generic; public class map
{
private Tile[,] full_map;//地图
private int room_max_length, room_max_width, room_min_length, room_min_width, map_max_length, map_max_width, room_num,min_corridor_len,max_corridor_len;//房间的最大最小宽度,地图的最大长宽,房间的个数
private bool _first;
private static map _instance; private map ()
{
} /// 地图的单例
public static map Instance {
get {
if (null == _instance) {
_instance = new map ();
}
return _instance;
}
} /// 初始化函数,全部赋予default
public void Init (int room_max_length,int room_max_width,int room_min_length,int room_min_width,int map_max_length,int map_max_width,int room_num,int min_corridor_len,int max_corridor_len)
{
full_map = new Tile[map_max_width, map_max_length];
_first = true;
this.room_max_length = room_max_length;
this.room_max_width = room_max_width;
this.room_min_length = room_min_length;
this.room_min_width = room_min_width;
this.map_max_length = map_max_length;
this.map_max_width = map_max_width;
this.room_num = room_num;
this.min_corridor_len = min_corridor_len;
this.max_corridor_len = max_corridor_len;
} /// 判断某一块是否在地图区域中
private bool IsInBounds_x (int x)
{
if ((x < ) || (x > map_max_width - ))
return false;
else
return true;
} /// 判断某一块是否在地图区域中
private bool IsInBounds_y (int y)
{
if ((y < ) || (y > map_max_length - ))
return false;
else
return true;
} /// 将一块区域设置为指定类型块
private void SetCells (int xStart, int yStart, int xEnd, int yEnd, Tile cellType)
{
for (int i = xStart; i <= xEnd; i++)
for (int j = yStart; j <= yEnd; j++) {
full_map [i, j] = cellType;
}
} /// 判断一个区域是否被使用过
private bool IsAreaUnused (int xStart, int yStart, int xEnd, int yEnd)
{
for (int i = xStart; i <= xEnd; i++)
for (int j = yStart; j <= yEnd; j++)
if (full_map [i, j] != Tile.Default)
return false;
return true;
} /// 创建单个房间
private void Creat_room(int xStart, int yStart, int xEnd, int yEnd){
for (int i = xStart + ; i < xEnd ; i++)
for (int j = yStart + ; j < yEnd; j++)
full_map [i, j] = Tile.DirtFloor;
for (int i = xStart + ; i < xEnd ; i++) {
full_map [i, yStart] = Tile.Wall_a;
full_map [i, yEnd] = Tile.Wall_d;
}
for (int j = yStart + ; j < yEnd; j++) {
full_map [xStart, j] = Tile.Wall_s;
full_map [xEnd, j] = Tile.Wall_w;
}
} /// 创建单个走廊
private void Creat_Corridor(int xStart, int yStart, int xEnd, int yEnd,Tile t){
for (int i = xStart; i <= xEnd ; i++)
for (int j = yStart; j <= yEnd; j++)
full_map [i, j] = t;
} private Tile[] tiles = System.Enum.GetValues (typeof(Tile)) as Tile[];
/// 创建走廊与房间
private bool MakeRoomAndCorridor(int x,int y){
int xStart = -, xEnd = -, yStart = -, yEnd = -, width, length;
width = Random.Range (room_min_width, room_max_width );
length = Random.Range (room_min_length, room_min_length );//随机长宽
if (_first) {
xStart = map_max_width / - width / ;
yStart = map_max_length / - length / ;
xEnd = xStart + width;
yEnd = yStart + length; if (IsInBounds_x (xStart) && IsInBounds_x (xEnd) && IsInBounds_y (yStart) && (IsInBounds_y (yEnd))) {
if (IsAreaUnused (xStart, yStart, xEnd, yEnd)) {
_first = false;//如果是第一次创建房间的话,就在地图中间生成一个
Creat_room (xStart, yStart, xEnd, yEnd);
return true;
} else
return false;
}
} else {
if ((full_map [x, y] == Tile.Wall_a) || (full_map [x, y] == Tile.Wall_w) || (full_map [x, y] == Tile.Wall_s) || (full_map [x, y] == Tile.Wall_d)) {
//生成走廊与房间
int corridor_length = Random.Range (min_corridor_len - , max_corridor_len - );
int c_xStart = -, c_xEnd = -, c_yStart = -, c_yEnd = -;
int away = Random.Range (, length - );//偏移量
Tile type=Tile.Default;
//根据打穿的墙壁类型决定房间和走廊的位置
switch (full_map [x, y]) {
case(Tile.Wall_a):
xStart = x - away;
xEnd = x + width;
yEnd = y - corridor_length - ;
yStart = yEnd - length;
c_yEnd = y;
c_yStart = y - corridor_length - ;
c_xEnd = x;
c_xStart = x;
type = Tile.Corridor_ad;
break;
case(Tile.Wall_d):
xStart = x - away;
xEnd = x + width;
yStart = y + corridor_length + ;
yEnd = yStart + length;
c_yStart = y;
c_yEnd = y + corridor_length + ;
c_xEnd = x;
c_xStart = x;
type = Tile.Corridor_ad;
break;
case(Tile.Wall_w):
yStart = y - away;
yEnd = yStart + length;
xStart = x + corridor_length + ;
xEnd = xStart + width;
c_xStart = x;
c_xEnd = x + corridor_length + ;
c_yStart = y;
c_yEnd = y;
type = Tile.Corridor_ws;
break;
case(Tile.Wall_s):
yStart = y - away;
yEnd = yStart + length;
xEnd = x - corridor_length - ;
xStart = xEnd - width;
c_xEnd = x;
c_xStart = x - corridor_length - ;
c_yStart = y;
c_yEnd = y;
type = Tile.Corridor_ws;
break;
default:
break;
}
if (IsAreaUnused (xStart, yStart, xEnd, yEnd)) {
Creat_room (xStart, yStart, xEnd, yEnd);
Creat_Corridor (c_xStart, c_yStart, c_xEnd, c_yEnd,type);//判断是否在地图内,然后生成
return true;
} else
return false;
} else
return false;
} return true;
}
public void make_dungeon(int step){
int x, y;
int num=;
for (int i = ; i < step; i++) {
x = Random.Range (,map_max_width);
y = Random.Range (,map_max_length);
if (MakeRoomAndCorridor(x,y)){
num++;
}
if (num==room_num){
break;
}
}
if (num
Debug.Log ("无法生成指定个数的房间!请确认数据的合法性或加大步数");
}
}
public Tile[,] getmap(){
return(full_map);
}
}
然后是用于控制生成的类,绑在摄像机上就能运行:
using UnityEngine;
using System.Collections;
using System; public class gameDriver : MonoBehaviour {
private Transform dungeon;
public int random_seed=;
public int room_max_length=,room_max_width=,room_min_length=,room_min_width=,map_max_length=,map_max_width=,room_num=,min_corridor_len=,max_corridor_len=,step=;//房间的最大最小宽度,地图的最大长宽,房间的个数
// Use this for initialization
void Start () {
dungeon = GameObject.Find ("dungeon").transform;//在地图上事先创建了一个放实体的空物体
Action _Instantiate=(t,v)=>{object g=Instantiate(t,v,t.rotation);((Transform)g).SetParent(dungeon);};
UnityEngine.Random.InitState (random_seed);//初始化随机种子
map.Instance.Init (room_max_length, room_max_width, room_min_length, room_min_width, map_max_length, map_max_width, room_num,min_corridor_len,max_corridor_len);//初始化参数
map.Instance.make_dungeon(step);//生成地牢
creat_dungeon.Instance.creat (map.Instance.getmap (), _Instantiate);//实体化地牢
}
// Update is called once per frame
void Update () {
}
}

最终的效果预览:

当然这个地牢还有很多东西没加上去,比如我在枚举类中定义的门(可以加在走廊的两端),出入口。还有装饰用的灯啊,小物品等,暂时就先这样吧,也没想到用这个后续搞点事情。

补发工程源码:

链接:https://pan.baidu.com/s/1jIuh36m 密码:hx9b

unity3d随机地牢生成代码的更多相关文章

  1. 用Python写一个随机数字生成代码,5行代码超简单

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 第一步,安装 random 库 random库是使用随机数的Python标准库 ...

  2. VBA随机地牢生成

    无聊啊--于是,我想做一个随机地图. 但是我很懒,不想做. 但是身体很诚实. 这次是直接在Excel中制作的地图,但是,VB的执行效率很慢,我代码的效率也很慢,导致,一旦地图长宽稍大,就会出现好几分钟 ...

  3. unity3d 场景配置文件生成代码

    using UnityEngine; using UnityEditor; using System.IO; using System; using System.Text; using System ...

  4. Unity3d 随机地图生成

    2D解析图: 3D地形: 嘿嘿.

  5. roguelike地牢生成算法

    文章原地址 上一个地图生成算法,这一次是一个地牢的生成算法,是一个国外的人写的算法,用dart语言写,我把它改成了unity-c#. 原作者博客地址:Rooms and Mazes: A Proced ...

  6. 一个比较全面的java随机数据生成工具包

    最近,由于一个项目的原因需要使用一些随机数据做测试,于是写了一个随机数据生成工具,ExtraRanom.可以看成是Java官方Random类的扩展,主要用于主要用于测试程序.生成密码.设计抽奖程序等情 ...

  7. 利用Java随机,生成随机学生数据

    为模拟向数据库中大量插入学生数据(注:此处应该用PreparedStatement.batchUpdate等批处理提高效率)的情形,通过Java随机来生成学生数据. 一.要生成的学生数据 studen ...

  8. 随机数据生成与对拍【c++版,良心讲解】

    10.7更新:见最下面 离NOIP2018没剩多长时间了,我突然发现我连对拍还不会,于是赶紧到网上找资料,找了半天发现了一个特别妙的程序,用c++写的! 不过先讲讲随机数据生成吧. 很简单,就是写一个 ...

  9. Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架

    前言: Annotation注解在Android的开发中的使用越来越普遍,例如EventBus.ButterKnife.Dagger2等,之前使用注解的时候需要利用反射机制势必影响到运行效率及性能,直 ...

随机推荐

  1. NodeJs并发异步的回调处理

    这里说并发异步,并不准确,应该说连续异步.NodeJs单线程异步的特性,直接导致多个异步同时进行时,无法确定最后的执行结果来回调.举个简单的例子: for(var i = 0; i < 5; i ...

  2. Spring @Autowired注解在utils静态工具类

    [转] 使用场景:在一个静态方法中,如何使用以下注入: @Autowired private ItemMapper itemMapper; @Component public class TestUt ...

  3. C# 动态调用webservice

    最近项目中,用到动态调用webservice的内容,此处记录下来,留着以后COPY(我们只需要在XML,config文件,或者数据库中配置webservice连接地址和方法名即可使用): using ...

  4. https://blog.helong.info/blog/2015/03/13/jump_consistent_hash/

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179     一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...

  5. 如果让你来做HashMap扩容,如何实现在不影响读写的情况下扩容?

    我觉得逼格高,不是体现在问题多刁钻,知识点多深,而是一个非常明确,无歧义的问题,能考察出面试者多方面的能力.这个问题背后:1.了解java中,HashMap的实现:如果一个面试者了解这一点,说明至少他 ...

  6. jquery复习笔记

    Jquery基础 让一个按钮灰掉 $("button").("disabled","true"); ance desc选择器(ance代表祖 ...

  7. h5移动版云胶片系统

    h5移动版云胶片系统. 最近开了一个医疗用的云胶片,可以对图片放大.图片缩小,图片移动,图片调窗,图片切换,图片播放,图片变灰等等功能.如下图:

  8. OC中协议, 类目, 时间, 延展, 属性

    只有继承和协议需要引IMPORT "头文件"; 必须接受marryprotocol协议, id<marryprotocol>基于类型的限定, 才能给实例变量赋值 @pr ...

  9. php try catch throw 用法

    1.try catch 捕捉不到fatal error致命错误 2.只有抛出异常才能被截获,如果异常抛出了却没有被捕捉到,就会产生一个fatal error. 3.父类可以捕获抛出的子类异常,Exce ...

  10. Quartz2D 编程指南(三)渐变、透明层 、数据管理

    概览 图形上下文 路径 颜色与颜色空间 变换 图案 阴影 渐变 透明层 Quartz 2D 中的数据管理 位图与图像遮罩 CoreGraphics 绘制 Layer 渐变 简介 渐变是从一个颜色到另外 ...