Unity3d学习之路-牧师与魔鬼


游戏基本介绍

  • 游戏规则:

    Priests and Devils is a puzzle game in which you will help the Priests and Devils to cross the river within the time limit. There are 3 priests and 3 devils at one side of the river. They all want to get to the other side of this river, but there is only one boat and this boat can only carry two persons each time. And there must be one person steering the boat from one side to the other side. In the flash game, you can click on them to move them and click the go button to move the boat to the other direction. If the priests are out numbered by the devils on either side of the river, they get killed and the game is over. You can try it in many > ways. Keep all priests alive! Good luck!

  • 游戏中提及的事物(Objects):
    魔鬼,牧师,船,河流,两边的陆地

  • 用表格列出玩家动作表(规则表):

玩家动作 执行条件 执行结果
点击牧师/魔鬼 游戏未结束,船没有正在移动,与船在相同的一边 牧师/魔鬼移动
点击船 游戏未结束且船上至少有一人 船移动
  • 本游戏架构参考下图:


MVC模式真的很好用,用过都说好,不过本次游戏只创建了三个脚本,所以接口和导演是和GameModels放在同一个脚本的同一命名空间下。

游戏制作流程

  • 搭建场景
    在Unity官方的Asset Store输入需要查找的资源,找到了免费的模型且模型带有动画(Asset Store中有很多免费的资源但是注意版权哦),然后布置场景,把它们放在场景中,记录他们不同状态的时候的坐标,方便接下来使用。

  • 创建各类构建基本的架构

    1.SSDirector
    利用单例模式创建导演,和Cocos2d-x一样一个游戏导演只能有一个,这里继承于System.Object,保持导演类一直存在,不被Unity内存管理而管理,导演类类似于生活中的导演,安排场景,场景切换,都是靠它来指挥。

    public class SSDirector : System.Object{private static SSDirector _instance;             //导演类的实例public ISceneController CurrentScenceController { get; set; }public static SSDirector GetInstance()           {if (_instance == null){_instance = new SSDirector();}return _instance;}}

2.ISceneController
这是一个场景的控制器的接口,算是导演与场景控制器沟通的接口,利用这个接口,得知当前场景是由哪个控制,然后向场景控制器传达要求等。

   public interface ISceneController
{void LoadResources();                                  //加载场景
}

3.IUserAction
用户进行操作后与游戏中发生响应的接口,用户通过键盘、鼠标等对游戏发出指令,这个指令会触发游戏中的一些行为,(比如在这个游戏中,点击角色让角色移动,这个角色移动就是用户动作后触发的行为),由IUserAction来声明

    public interface IUserAction                           //用户互动会发生的事件{void MoveBoat();                                   //移动船void Restart();                                    //重新开始void MoveRole(RoleModel role);                     //移动角色int Check();                                       //检测游戏结束}

4.FirstController
这是一个控制器,对场景中的具体对象进行操作(其实也是使用GameModels给出的函数进行控制),可以看到这个控制器继承了两个接口类并实现了它们的方法,控制器是场景中各游戏对象行为改变的核心

public class Controllor : MonoBehaviour, ISceneController, IUserAction
{public LandModel start_land;            //开始陆地public LandModel end_land;              //结束陆地public BoatModel boat;                  //船private RoleModel[] roles;              //角色UserGUI user_gui;void Start (){SSDirector director = SSDirector.GetInstance();      //得到导演实例director.CurrentScenceController = this;             //设置当前场景控制器user_gui = gameObject.AddComponent<UserGUI>() as UserGUI;  //添加UserGUI脚本作为组件LoadResources();                                     //加载资源}//创建水,陆地,角色,船//移动船//移动角色//重新开始游戏//检测游戏是否结束
}

5.UserGUI
建立用户的交互界面,比如按钮和标签,

public class UserGUI : MonoBehaviour {private IUserAction action;public int sign = 0;bool isShow = false;void Start(){action = SSDirector.GetInstance().CurrentScenceController as IUserAction;}void OnGUI(){GUIStyle text_style;GUIStyle button_style;text_style = new GUIStyle(){fontSize = 30};button_style = new GUIStyle("button"){fontSize = 15};if (GUI.Button(new Rect(10, 10, 60, 30), "Rule", button_style)){if (isShow)isShow = false;elseisShow = true;}if(isShow){GUI.Label(new Rect(Screen.width / 2 - 85, 10, 200, 50), "让全部牧师和恶魔都渡河");GUI.Label(new Rect(Screen.width / 2 - 120, 30, 250, 50), "每一边恶魔数量都不能多于牧师数量");GUI.Label(new Rect(Screen.width / 2 - 85, 50, 250, 50), "点击牧师、恶魔、船移动");}if (sign == 1){GUI.Label(new Rect(Screen.width / 2-90, Screen.height / 2-120, 100, 50), "Gameover!", text_style);if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 100, 50), "Restart", button_style)){action.Restart();sign = 0;}}else if (sign == 2){GUI.Label(new Rect(Screen.width / 2 - 80, Screen.height / 2 - 120, 100, 50), "You Win!", text_style);if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 100, 50), "Restart", button_style)){action.Restart();sign = 0;}}}
}

6.GameModels
游戏场景中可交互的游戏模型,在这个游戏中我建立了角色模型和船模型以及陆地模型给控制器留出函数供其调用

  • 这里先讲一下关于Tag:
    Tag可以用来识别游戏对象,在Inspector中名字的下面可以看到Tag,官方文档中对于Tag的好处描述得很清楚,这里我是用每个GameObject的name来区分不同的对象的,因为GameObject比较少,如果比较多的话建议还是使用Tag。

  • LandModel(陆地模型)
    1.先来想一下陆地模型应该具备什么属性吧,首先陆地有两块,所以我们需要一个标志位来记录是开始的陆地还是结束陆地,然后陆地的位置(因为陆地被创建后就不动了只用一次,所以我在创建时直接赋值给陆地的position所以没有记录),以及陆地上的角色(用角色模型的数组来记录),每个角色的位置(Vector3的数组来记录)。

        GameObject land;                        //陆地对象Vector3[] positions;                    //保存每个角色放在陆地上的位置int land_sign;                          //到达陆地标志为-1,开始陆地标志为1RoleModel[] roles = new RoleModel[6];   //陆地上有的角色

2.陆地应该有的函数:应该知道在自己陆地上牧师和魔鬼各自的数量,从陆地上移除一个角色,添加一个角色,知道陆地上哪一个是第一个空的位置(角色登陆的时候放上去),游戏结束的重置等。

因为陆地的摆放是按照z轴对称的,所以x坐标是相反数,得到陆地空位置的时候就可以乘陆地的标志就行,这是一个比较巧妙的方法

    public Vector3 GetEmptyPosition()               //得到陆地上空位置{Vector3 pos = positions[GetEmptyNumber()];pos.x = land_sign * pos.x;                  //因为两个陆地是x坐标对称return pos;}
  • BoatModel(船模型)
    1.那再来想一下船模型应该具有什么属性:船在开始/结束陆地旁的位置,在开始/结束陆地旁船上可以载客的两个位置(用Vector3的数组表示),船上载有的角色(用角色模型的数组来记录),标记船在开始陆地还是结束陆地的旁边。
        int boat_sign = 1;                              //船在开始还是结束陆地RoleModel[] roles = new RoleModel[2];           //船上的角色GameObject boat;                                          Vector3[] start_empty_pos;      //船在开始陆地的空位位置Vector3[] end_empty_pos;        //船在结束陆地的空位位置

2.船应该有的函数:与陆地拥有的函数类似,增加了一个移动船的函数,让船到设定的位置

public void BoatMove()      //封装了一个MovePosition,传进要移动到的位置就可以移动啦
{if (boat_sign == -1){move.MovePosition(new Vector3(38, -3.9F, -2.9F));boat_sign = 1;}else{move.MovePosition(new Vector3(-1.4F, -4, -3));boat_sign = -1;}
}
  • RoleModel(角色模型)
    1.一个角色的属性:标志角色是牧师还是恶魔,标志是否在船上
    2.角色模型的函数:去到陆地/船上(其实就是把哪个作为父节点,并且修改是否在船上标志),其他就是基本的get/set函数。
    3.这里讲解一下如何加载预制体吧:

    • 首先用Resources.Load函数,第一个参数是你的预制体的路径(注意:路径名使用正斜杠“/”,如果使用反斜杠“\”会不正常运行),第二个参数是要返回的对象类型,当然你也可以只写第一个参数但是需要强制转换成为其他对象类型。Resources.Load函数让我们用一个对象来保存我们加载好的预制体,但预制体没有载入到场景中,还未进行实例化。
    • 然后使用Object.Instantiate函数,它传入一个你想要初始化的对象,可以设置位置,角度,和父节点的Transform等,这样一个实例化的预制体就出现在场景中了,更多的参数了解请移步官方文档
public class RoleModel
{GameObject role;int role_sign;             //0为牧师,1为恶魔Click click;               //点击触发的脚本bool on_boat;              //是否在船上       Move move;                 //移动的脚本PlayAnimation play_ani;    //动画脚本LandModel land_model = (SSDirector.GetInstance().CurrentScenceController as Controllor).start_land;     //所在陆地模型public RoleModel(string role_name){if (role_name == "priest"){role = Object.Instantiate(Resources.Load("Priest", typeof(GameObject)), Vector3.zero, Quaternion.Euler(0, -90, 0)) as GameObject;role_sign = 0;}else{role = Object.Instantiate(Resources.Load("Devil", typeof(GameObject)), Vector3.zero, Quaternion.Euler(0, -90, 0)) as GameObject;role_sign = 1;}move = role.AddComponent(typeof(Move)) as Move;click = role.AddComponent(typeof(Click)) as Click;play_ani = role.AddComponent(typeof(PlayAnimation)) as PlayAnimation;click.SetRole(this);}public int GetSign() { return role_sign;}public LandModel GetLandModel(){return land_model;}public string GetName() { return role.name; }public bool IsOnBoat() { return on_boat; }public void SetName(string name) { role.name = name; }public void SetPosition(Vector3 pos) { role.transform.position = pos; }public void PlayGameOver(){play_ani.Play();}public void PlayIdle(){play_ani.NotPlay();}public void Move(Vector3 vec){move.MovePosition(vec);}public void GoLand(LandModel land){  role.transform.parent = null;land_model = land;on_boat = false;}public void GoBoat(BoatModel boat){role.transform.parent = boat.GetBoat().transform;land_model = null;          on_boat = true;}public void Reset(){land_model = (SSDirector.GetInstance().CurrentScenceController as Controllor).start_land;GoLand(land_model);SetPosition(land_model.GetEmptyPosition());land_model.AddRole(this);}
}

7.其他辅助脚本
这些脚本挂载预制体上(挂载方式是使用AddComponent函数),帮助实现一些动作

  • Move脚本
    先让角色水平移动再垂直移动或是先垂直移动再实现角色水平移动,通过每一帧检测是否移动
   public class Move : MonoBehaviour{float move_speed = 250;                   //移动速度int move_sign = 0;                        //0是不动,1水平移动,2竖直移动Vector3 end_pos;Vector3 middle_pos;void Update(){if (move_sign == 1){transform.position = Vector3.MoveTowards(transform.position, middle_pos, move_speed * Time.deltaTime);if (transform.position == middle_pos)move_sign = 2;}else if (move_sign == 2){transform.position = Vector3.MoveTowards(transform.position, end_pos, move_speed * Time.deltaTime);if (transform.position == end_pos)move_sign = 0;           }}public void MovePosition(Vector3 position){end_pos = position;if (position.y == transform.position.y)         //船只会水平移动{  move_sign = 2;}else if (position.y < transform.position.y)      //角色从陆地到船{middle_pos = new Vector3(position.x, transform.position.y, position.z);}else                                          //角色从船到陆地{middle_pos = new Vector3(transform.position.x, position.y, position.z);}move_sign = 1;}}
  • Click脚本
    检测船和角色是否被点击(这里别忘了,只有物体加上了Collider才能实现检测点击事件发生哦),人形模型推荐使用Capsule Collider,船的话我使用的是Box Collider,关于Collider的介绍请移步官方文档,这里多说一句,因为当鼠标被按下的时候,unity会在你鼠标点击的那个点,向游戏场景内发送一条射线,这条射线如果和碰撞检测器也就是Collider触碰的时候就知道你是点击到拥有这个Collider组件的游戏对象上。
public class Click : MonoBehaviour
{IUserAction action;RoleModel role = null;BoatModel boat = null;public void SetRole(RoleModel role){this.role = role;}public void SetBoat(BoatModel boat){this.boat = boat;}void Start(){action = SSDirector.GetInstance().CurrentScenceController as IUserAction;}void OnMouseDown(){if (boat == null && role == null) return;if (boat != null)action.MoveBoat();else if(role != null)action.MoveRole(role);}
}
  • PlayAnimation脚本
    进行动画播放
public class PlayAnimation : MonoBehaviour
{public void Play()                                  //播放gameover状态的动画{Animator anim = this.GetComponent<Animator>();anim.SetBool("isgameover", true);}public void NotPlay()                              //播放Idle状态的动画{Animator anim = this.GetComponent<Animator>();anim.SetBool("isgameover", false);}
}
  • 完成MVC的搭建和函数实现,游戏就基本完成了,把Controllor.cs挂载到相机上,然后点击运行,一个牧师与魔鬼的游戏就实现啦

Animator Controllor

我从Asset Store下载的魔鬼模型和牧师模型以及自带了Animator组件,而且动作已经有了,可是缺少Animator Controllor,那么怎样去实现动画播放呢,这里放上一个不负责任的链接供大家参考:http://www.cnblogs.com/hammerc/p/4828774.html


实现截图



具体的代码实现可以去我的Github:传送门

鸣谢之前师兄留下的博客供我参考:传送门

Unity3d学习之路-牧师与魔鬼相关推荐

  1. Unity3D项目四:牧师与魔鬼(动作分离版)

    Unity3D项目四:牧师与魔鬼(动作分离版) 基本介绍 动作管理是游戏的重要内容,全部都放在游戏对象里显得十分笨重,所以本次项目需要将动作从对象中提取出来写成单独的动作控制器.动作控制器来管理控制所 ...

  2. Unity3D学习之路——AI小坦克

    Unity3D学习之路--AI小坦克 作业要求: 坦克对战游戏 AI 设计 从商店下载游戏:"Kawaii" Tank 或 其他坦克模型,构建 AI 对战坦克.具体要求 使用&qu ...

  3. Unity3D项目三:牧师与魔鬼

    Unity3D项目三:牧师与魔鬼 基本介绍 列出游戏中提及的事物(Objects) 牧师,恶魔,船,河流,左侧陆地,右侧陆地 用表格列出玩家动作表(规则表),注意,动作越少越好 动作 条件 结果 点击 ...

  4. unity3d学习笔记(三)魔鬼与牧师

    游戏规则 * 你要运用智慧帮助3个牧师(方块)和3个魔鬼(圆球)渡河. * 船最多可以载2名游戏角色. * 船上有游戏角色时,你才可以点击这个船,让船移动到对岸. * 当有一侧岸的魔鬼数多余牧师数时( ...

  5. Unity3D学习之路

    1.准备C#的开发环境 VS2015, Unity3D 5.5.1 2.准备通信协议 protobuf 3.3.0 具体请参考:Protobuf 3.3 使用总结 3.引入日志系统 :C#日志系统 L ...

  6. Unity3d学习之路-简单AR游戏

    简单AR游戏 简单AR游戏 游戏规则 游戏实现 游戏场景的搭建 游戏逻辑的实现 游戏脚本挂载 游戏打包到安卓平台 实现效果 小结 游戏规则 识别指定图片,显示玩家和防御塔,点击按键对玩家进行上下左右移 ...

  7. Unity3D学习之路Homework4—— 飞碟射击游戏

    简单打飞碟小游戏 游戏规则与要求 规则 鼠标点击飞碟,即可获得分数,不同飞碟分数不一样,飞碟的初始位置与飞行速度随机,随着分数增加,游戏难度增加.初始时每个玩家都有6条生命,漏打飞碟扣除一条生命,直到 ...

  8. 3D游戏设计-牧师与魔鬼_动作分离版

    天空盒 天空盒 前情提要 3D游戏设计-牧师与魔鬼 游戏改进 将每个需要移动的游戏对象的移动方法提取出来,建立一个动作管理器来管理这些移动方法. 优点: 程序更能适应需求变化 对象更容易被复用 程序更 ...

  9. 3D游戏设计-牧师与魔鬼

    游戏简介 3个牧师和3个恶魔要过河,可小船每次只能搭乘2个人.可怕的是无论在岸的哪边,如果牧师的人数比恶魔少,牧师就会被恶魔杀死.发挥你的才智,帮他们想出一条过河的办法. 游戏规则设计 初始时3个牧师 ...

最新文章

  1. click事件在什么时候出发_超全的js事件机制amp;事件委托
  2. M. Monster Hunter(树形dp)
  3. java option请求_java – 如何说服spring 4.2将OPTIONS请求传递给控制器
  4. python实现自动打电话软件_全自动手势联系软件 让你轻轻松松打电话
  5. Thrift 教程 开发 笔记 原理 资料 使用 范例 示例 应用
  6. h5 先加载小图_萌宝学诗|读诗、画诗、唱诗,尽在小图姐姐的《九月九日忆山东兄弟》中!...
  7. R语言中文社区2018年终文章整理(类型篇)
  8. 32位电脑适合装W ndows10,32位再见?微软将停止支持32位Win10系统
  9. POI Excel合并 分页符的复制
  10. Python实现历史记录功能
  11. [c#]使用Fleck实现简单的WebSocket含兼容低版本IE
  12. 王道数据结构课代表 - 考研数据结构 第三章 栈和队列 究极精华总结笔记
  13. Photoshop插件--证件照--1寸裁剪--1寸排版--脚本开发--PS插件
  14. 怎么批量下载哗哩哗哩B站视频
  15. 前端小动画之飘落的小雪花
  16. 免费思维导图软件, 简单脑图工具,支持导出图片
  17. 基于STC8G2K64S4单片机控制小车循迹(直流电机和步进电机)
  18. python爬取网易云音乐排行榜数据
  19. 九段刀客 vue-router实现原理
  20. H国身份证号码【多组实例测试】

热门文章

  1. 无缘蜂鸣器音量大小音调高低调节-求答案
  2. element ui登录界面_vue vue-router vuex element-ui axios 写一个代理平台的学习笔记(四)美化头部和登录界面...
  3. 常见jvm调优参数总结
  4. Win11 禁用 Win+G 开启 XBox Gaming Bar
  5. 关于iic、spi通信协议的概述
  6. 智慧城市的由来与发展现状
  7. 线性回归-最小二乘法
  8. 开机logo和开关机动画
  9. Eclipse 如何调整java和xml文件字体大小
  10. freenas 当网站服务器,如何配置安装FreeNAS服务器