本文是3D游戏编程与设计第三次作业的博客,内容为游戏“牧师与魔鬼(Priests and Devils)”的编程

目录

  • 游戏规则
    • 游戏脚本
    • 具体内容
  • 游戏设计
    • 列出游戏中提及的事物(Objects)
    • 用表格列出玩家动作表(规则表)
    • 制作对象预制
    • 程序架构
  • 代码展示
    • Controller
    • View
    • Model
  • 成果展示

游戏规则

游戏脚本

Priests and Devils 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)

牧师,魔鬼,船,河流,河岸

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

动作 状态 响应
点击牧师或魔鬼 船在被点击角色的河岸 角色上船
点击船 船上至少有一个角色 船到对岸

制作对象预制

将角色用球体Sphere制作,牧师为白色,魔鬼为红色
将河流、河岸、船用立方体Cube制作

程序架构

游戏采用MVC架构,其中

  • Model:场景中所有的对象及其数据,如Boat.cs、Role.cs。
  • View:展示游戏结果,如UserGUI.cs、IUserAction.cs。
  • Controller:接受输入并调用Model和View去完成用户的需求,如Director.cs、FirstController.cs、SceneController.cs。

此外实现了两个辅助类:

  • Move.cs:检测鼠标点击,辅助对象移动的完成
  • Camera.cs:挂载在main camera上,调整main camera角度

代码展示

源代码较长,故仅依据程序架构展示代码核心部分,如函数实现等细节请见完整代码gitee传送门

Controller

Director.cs:创建一个掌管全局的“导演”,采用单例模式,且继承System.Object,所以不会受 Unity 引擎管理,也不用加载。通过场记SceneController加载、管理资源和场景与管理游戏状态等

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Director : System.Object {private static Director _instance;             //导演类的实例public SceneController scene { get; set; }public static Director GetInstance(){if (_instance == null){_instance = new Director();}return _instance;}
}

SceneController.cs:场记接口,如上所述,实现了加载场景、获取对象位置、移动对象位置、重置游戏的功能

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public interface SceneController
{void LoadResources();                           int getObjPos(string name); void moveObjTo(string name);void Reset();
}

FirstController.cs:场记接口的实现,游戏内行为的实际操作者,管理所有对象的数据,响应外部输入、协调游戏对象通讯、游戏对象行为执行、游戏状态检查
资源初始化:注入导演,切换场景后加载资源,调用制作好的预制生成对象,为其初始化名字等属性方便调用

 FirstController(){Priests = new Role[3];Devils = new Role[3];}void Start() {Director director = Director.GetInstance();//设置导演director.scene = this;                     //设置场景this.LoadResources();                      //加载资源}public void LoadResources(){state = -1;// 创建UIgui = new UserGUI();// 加载河岸GroundFrom = Object.Instantiate(Resources.Load("Objects/Ground", typeof(GameObject)), new Vector3(0, -5, 15), Quaternion.identity, null) as GameObject;GroundFrom.name = "groundfrom";GroundTo = Object.Instantiate(Resources.Load("Objects/Ground", typeof(GameObject)), new Vector3(0, -5, -15), Quaternion.identity, null) as GameObject;GroundTo.name = "groundto";// 加载河流River = Object.Instantiate(Resources.Load("Objects/River", typeof(GameObject)), new Vector3(0, -6, 0), Quaternion.identity, null) as GameObject;River.name = "river";// 加载船boat = new Boat();// 加载牧师对象fromNum_priest = 3;toNum_priest = 0;for (int i = 0;i < 3; i++){Role role_temp = new Role("Objects/Priest");role_temp.setName("Priest"+ i);Priests[i] = role_temp;float p_z = (float)(9 + i * 1.5);Priests[i].setFromPos(new Vector3(0, -1, p_z));Priests[i].setToPos(new Vector3(0, -1, -p_z));}// 加载魔鬼对象fromNum_devil = 3;toNum_devil = 0;for (int i = 0; i < 3; i++){Role role_temp = new Role("Objects/Devil");role_temp.setName("Devil" + i);Devils[i] = role_temp;float p_z = (float)(14 + i * 1.5);Devils[i].setFromPos(new Vector3(0, -1, p_z));Devils[i].setToPos(new Vector3(0, -1, -p_z));}}

游戏行为执行与状态检查:变量movestate记录着船的移动状态,通过储存船的位置以及船停泊点座位的位置,来实现船与角色在场景中的移动,通过检查两岸角色的数量来判定游戏状态

    void Update () {if(boat.movestate > 0){//船移动Vector3 currentPos = boat.boat_obj.transform.position;Vector3[] boat_park = { new Vector3(0, -4, -5), new Vector3(0, -4, 5) };Vector3 park_now = boat_park[boat.movestate - 1];int boat_pos= boat.movestate - 1;if (currentPos == park_now){boat.movestate = 0;}boat.boat_obj.transform.position = Vector3.MoveTowards(currentPos, park_now, 7f * Time.deltaTime);//球(随船)移动Vector3[,] seats = { { new Vector3(0, -3, -6), new Vector3(0, -3, -5) },{ new Vector3(0, -3, 5), new Vector3(0, -3, 6) }};for (int i = 0;i < 2;i++){int seat_pos = i;Role role_t = MapNumToRole(boat.BoatState[i]);if (role_t != null){Vector3 cur = role_t.obj.transform.position;role_t.obj.transform.position = Vector3.MoveTowards(cur, seats[boat_pos,seat_pos], 7f * Time.deltaTime);}}}//胜负检查if ((fromNum_priest != 0 && fromNum_devil > fromNum_priest) || (toNum_priest != 0 && toNum_devil > toNum_priest)){state = 0;gui.setLose();}if (toNum_devil == 3 && toNum_priest == 3 && boat.num == 0){state = 1;gui.setWin();}}

对象移动:先检测船是否正在移动或者游戏是否已结束,当移动对象为船时,检查船的位置来决定其移动方向,同时依据船上角色的信息更新两岸角色的信息;当移动对象为角色时,检查角色位置决定其上岸还是上船,移动角色位置并更新船上乘客的信息,但此时不需要更新岸上角色的数量信息,因为规则中船靠岸后与移动前,船上角色将被统计为岸上角色判定胜负

    public void moveObjTo(string name){// 船不是静止状态下或游戏结束后不能操作if(boat.movestate != 0||state != -1){return;}//船移动if(name == "boat"){// 船从from到toif(boat.position == 0 && boat.num > 0){boat.position = 1;boat.movestate = 1;foreach(int num in boat.BoatState){if(num > 0 && num < 4){fromNum_priest--;toNum_priest++;}else if(num >= 4){fromNum_devil--;toNum_devil++;}} }// 船从to到fromelse if(boat.position == 1 && boat.num > 0){boat.position = 0;boat.movestate = 2;foreach (int num in boat.BoatState){if (num > 0 && num < 4){toNum_priest--;fromNum_priest++;}else if (num >= 4){toNum_devil--;fromNum_devil++;}}}}//角色移动else{Role obj = findObjRole(name);// 角色在河岸,上船if(obj.position == 0 && obj.position == boat.position && boat.num < 2){boat.num++;if(boat.BoatState[0] == 0){obj.MoveToBoat(1);//1,2,3,4代表船的两个停泊位的各两个座位boat.BoatState[0] = findObjOrder(name);}else{obj.MoveToBoat(2);boat.BoatState[1] = findObjOrder(name);}}else if(obj.position == 1 && obj.position == boat.position && boat.num < 2){boat.num++;if (boat.BoatState[0] == 0){obj.MoveToBoat(4);boat.BoatState[0] = findObjOrder(name);}else{obj.MoveToBoat(3);boat.BoatState[1] = findObjOrder(name);}}// 角色在船上,下船else if(obj.position == 2){if(boat.position == 0){boat.num--;boat.BoatState[obj.boatPos] = 0;obj.MoveGroundFrom();}else{boat.num--;boat.BoatState[obj.boatPos] = 0;obj.MoveGroundTo();}}}}

游戏重置:删除之前的所有对象,然后重新加载

public void Reset(){for(int i = 0;i < 3;i ++){Destroy(Priests[i].obj);Destroy(Devils[i].obj);Destroy(boat.boat_obj);Destroy(GroundFrom);Destroy(GroundTo);Destroy(River);}LoadResources();}

View

UserGUI.cs:游戏结果展示

 void OnGUI(){//初始化UIGUIStyle style = new GUIStyle{fontSize = 45,};style.normal.textColor = new Color(200 / 255f, 200 / 255f, 150 / 255f);    GUI.Label(new Rect(UnityEngine.Screen.width/4, UnityEngine.Screen.height/10, 180, 70), "Priests and Devils", style);//三种状态下UI提示的设置if (GUI.Button(new Rect(UnityEngine.Screen.width / 10, UnityEngine.Screen.height / 10, 100, 50), "Reset")){Director.GetInstance().scene.Reset();}if (state == 0){style.normal.textColor = new Color(255 / 255f, 0 / 255f, 0 / 255f);GUI.Label(new Rect(UnityEngine.Screen.width / 3, UnityEngine.Screen.height/1.3f, 180, 70), "Lose!", style);}if (state == 1){style.normal.textColor = new Color(255 / 255f, 0 / 255f, 0 / 255f);GUI.Label(new Rect(UnityEngine.Screen.width/3, UnityEngine.Screen.height / 1.3f, 180, 70), "Win!", style);}}

Model

Boat.cs和Role.cs:游戏中的对象(牧师、魔鬼、船)及其数据

public class Boat{public GameObject boat_obj;public Move move;public int movestate;   // 船的移动状态,0表示静止,1表示从from到to,2表示从to到frompublic int[] BoatState;  // 两个位置的状态,0代表无,1-3代表Priest1-Priest3,4-6代表Devil1-Devil3public int num;         // 船上人数public int position;        // 船的位置,0代表from,1代表topublic Boat(){//初始化船的objectboat_obj = Object.Instantiate(Resources.Load("Objects/Boat", typeof(GameObject)), new Vector3(0, -4, 5), Quaternion.identity, null) as GameObject;boat_obj.name = "boat";var addComp = boat_obj.AddComponent(typeof(Move)) as Move;boat_obj.AddComponent<BoxCollider>();boat_obj.transform.rotation = Quaternion.Euler(-90, 0, 0);//设置状态movestate = 0;num = 0;position = 0;BoatState = new int[2];BoatState[0] = 0;BoatState[1] = 0;}}

public class Role {public GameObject obj;public int position;//角色位置,0代表from,1代表to,2代表boatpublic int boatPos;//船位置,0为from,1为toprivate Move move;private Vector3 PosFrom;//角色在From河岸上的位置private Vector3 PosTo;//角色在To河岸上的位置public Role(string name){obj = Object.Instantiate(Resources.Load(name, typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;position = 0;move = obj.AddComponent(typeof(Move)) as Move;}
}

成果展示

牧师为白球,魔鬼为红球
开始

魔鬼多于牧师,失败

魔鬼不多于牧师,继续进行

全部到达对岸,胜利

3D-03-牧师与魔鬼相关推荐

  1. Unity3D学习(3)之基于鼠标点击的3D版牧师与魔鬼

    这次实验让我们用Unity3D来做一个牧师与魔鬼的游戏,不过这可是3D版本哦听起来就有点小兴奋有木有.        牧师和魔鬼游戏是一款益智类游戏,游戏的目标是将3个牧师和3个魔鬼从河的一端安全地送 ...

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

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

  3. 【3D游戏编程与设计】四 游戏对象与图形基础 : 构建游戏场景+牧师与魔鬼 动作分离版

    [3D游戏编程与设计]四 游戏对象与图形基础 : 构建游戏场景+牧师与魔鬼 动作分离版 基本操作演练 下载 Fantasy Skybox FREE, 构建自己的游戏场景 下载 Fantasy Skyb ...

  4. 3D游戏编程 作业三 牧师与魔鬼

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.任务要求 二. 任务分析 1.游戏规则 2.游戏对象 3.动作表 二. 项目展示 1.mvc架构 导演 SSDir ...

  5. 基于Unity3D开发的3D小游戏牧师与魔鬼

    3D小游戏牧师与魔鬼 作业要求 编程实践,3D小游戏:牧师与魔鬼 列出游戏中提及的事物(Objects) 用表格列出玩家动作表(规则表),注意,动作越少越好 请将游戏中对象做成预制,并在 GenGam ...

  6. Unity 3D游戏三:牧师与魔鬼

    前言 中山大学数据科学与计算机学院3D游戏课程学习记录博客. 游戏代码:gitee 游戏视频:bilibili 参考师兄的博客: 师兄博客 游戏要求 列出游戏中提及的事物(Objects) 对象:牧师 ...

  7. 3D游戏编程与设计作业4-Skybox_牧师与魔鬼进阶版

    基本操作演练 下载Fantasy Skybbox FREE,构建自己的游戏场景 直接在上一次作业的Priests and Devils游戏场景中添加天空盒和地形构建场景.首先在Unity Assets ...

  8. 3D游戏 | 场景的搭建 + 牧师与魔鬼

    基本操作演练 下载 Fantasy Skybox FREE, 构建自己的游戏场景 在Assert Store搜索Fantasy Skybox FREE并下载,下好之后import进项目中. Fanta ...

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

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

  10. 牧师与魔鬼 -- version2 动作分离

    目录 一.基本操作演练 1.下载 Fantasy Skybox FREE, 构建自己的游戏场景 2.写一个简单的总结,总结游戏对象的使用 二.编程实践 1.牧师与魔鬼 动作分离版 面向对象的游戏编程 ...

最新文章

  1. python3 +改进HTMLTestRunner.py 测试报告显示
  2. Vue——基本的代码结构和插值表达式、v-cloak||Vue指令之v-text和v-html||v-bind的三种用法||Vue指令之v-on
  3. 火的不要不要的公有云和私有云都是什么鬼?
  4. 算法知识之最长公共子序列问题(动态规划)
  5. Windows Server 笔记之活动目录,域的联系和区别
  6. vb中调用aspx页面
  7. 离岸人民币兑美元升破6.50关口
  8. 伪随机数生成算法-梅森旋转(Mersenne Twister/MT)
  9. 品读大话数据结构之六-----线性表(中)
  10. SQL获得当前时间函数(MySQL)
  11. 贴片电阻字码阻值对照表
  12. [转]外贸出口流程图
  13. A4张的尺寸大小是多大?像素又是多少呢?
  14. 中国天气网 城市代号
  15. matlab rlc串联,rlc串联电路的零状态响应 matlab
  16. js设置北京时区_JavaScript 实现北京时间转其他时区时间,根据系统对对应时区转换...
  17. C嵌入式编程设计模式
  18. c语言结构体平面向量加法公式,高中平面向量学不好怎么办?这些公式帮你秒杀向量题目...
  19. python自动生成word报告_利用Python实现报告自动生成
  20. 量化策略及其中国市场容量

热门文章

  1. Android 拦截或屏蔽返回键
  2. ubuntu 定时 执行 python 脚本
  3. Mac nginx运行php文件 File not found.
  4. C语言练习题:企业发放的奖金根据利润提成。利润 I 低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成
  5. 泛微OA-测试环境更新所有密码为1
  6. 华为实习传送接入产品线面试经历(已通过二面)
  7. 手机版个人中心html,手机版个人中心页面.html
  8. 【Hack The Box】windows练习-- acute
  9. 蓝桥杯java_B_2013_01 世纪末的星期
  10. IPv4网络地址转换(NAT)