unity3d随机地牢生成代码
做完了以后我又想了一下,发现其实根本不需要这么麻烦,果然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随机地牢生成代码的更多相关文章
- 用Python写一个随机数字生成代码,5行代码超简单
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 第一步,安装 random 库 random库是使用随机数的Python标准库 ...
- VBA随机地牢生成
无聊啊--于是,我想做一个随机地图. 但是我很懒,不想做. 但是身体很诚实. 这次是直接在Excel中制作的地图,但是,VB的执行效率很慢,我代码的效率也很慢,导致,一旦地图长宽稍大,就会出现好几分钟 ...
- unity3d 场景配置文件生成代码
using UnityEngine; using UnityEditor; using System.IO; using System; using System.Text; using System ...
- Unity3d 随机地图生成
2D解析图: 3D地形: 嘿嘿.
- roguelike地牢生成算法
文章原地址 上一个地图生成算法,这一次是一个地牢的生成算法,是一个国外的人写的算法,用dart语言写,我把它改成了unity-c#. 原作者博客地址:Rooms and Mazes: A Proced ...
- 一个比较全面的java随机数据生成工具包
最近,由于一个项目的原因需要使用一些随机数据做测试,于是写了一个随机数据生成工具,ExtraRanom.可以看成是Java官方Random类的扩展,主要用于主要用于测试程序.生成密码.设计抽奖程序等情 ...
- 利用Java随机,生成随机学生数据
为模拟向数据库中大量插入学生数据(注:此处应该用PreparedStatement.batchUpdate等批处理提高效率)的情形,通过Java随机来生成学生数据. 一.要生成的学生数据 studen ...
- 随机数据生成与对拍【c++版,良心讲解】
10.7更新:见最下面 离NOIP2018没剩多长时间了,我突然发现我连对拍还不会,于是赶紧到网上找资料,找了半天发现了一个特别妙的程序,用c++写的! 不过先讲讲随机数据生成吧. 很简单,就是写一个 ...
- Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架
前言: Annotation注解在Android的开发中的使用越来越普遍,例如EventBus.ButterKnife.Dagger2等,之前使用注解的时候需要利用反射机制势必影响到运行效率及性能,直 ...
随机推荐
- Application_Error VS OnException 遇到的坑
在工作中遇到一个巨坑,就是关于Application_Error和OnException, 本身我的应用程序设置了全局异常OnException处理,手动抛出异常,OnExcep ...
- css3几个新属性
1.text-shadow 文字阴影 p{ text-shadow:2px 2px 10px #000; } 四个参数,依次: a:水平偏移 b:垂直偏移 c:阴影程度 d:阴影颜色 2.word- ...
- 控制反转IOC的依赖注入方式
引言: 项目中遇到关于IOC的一些内容,因为和正常的逻辑代码比较起来,IOC有点反常.因此本文记录IOC的一些基础知识,并附有相应的简单实例,而在实际项目中再复杂的应用也只是在基本应用的基础上扩展而来 ...
- 1.初识Shell脚本语言
PS:在做Linux下STM8固件升级项目中,需要让CPU通过I2C总线给STM8传输数据,刚开始一个一个的敲,很浪费时间,用shell脚本大大提高了数据传输效率,它是用户与内核进行交互操作的一种接口 ...
- cocos2dx 3.x(获得父类的node型指针调用父类函数this->getParent())
void CenterLayer::zhanzheng(CCObject* pSender){ ((GameScene*)this->getParent())->showLayer(Gam ...
- 基于spring的aop实现读写分离与事务配置
项目开发中经常会遇到读写分离等多数据源配置的需求,在Java项目中可以通过Spring AOP来实现多数据源的切换. 一.Spring事务开启流程 Spring中通常通过@Transactional来 ...
- PPT图片双屏抽奖系统现场主要操作流程介绍
目录 第一步:前期准备工作 第二步:现场预备与辅助展示工作 第三步:现场正式抽取工作 PPT图片双屏抽奖系统-现场抽奖视频实录 第一步:前期准备工作 把第二个步骤优化处理制作好的PPT文件 [图片.p ...
- kali 下文件操作
记得看到一片文章中说要学习linux 不要用kali.. 不感兴趣的东西,还指望我去搞个Ubuntu.... Ctrl+I 清屏 CD命令: cd 进入用户主目录: cd ~ 进入用户主目录: cd ...
- JSP基本语法小结
jsp表达式:<%=???%> 在jsp页面嵌入java代码<%Java代码%>可以用多个<% %>分割代码段 jsp声明:<%!用这样的方法可以声明java ...
- WPF绑定xml数据源
1.界面 <UserControl x:Class="HKDCMS.Client.Demo.UIViews.UIControls.AboutUsControl" xmlns= ...