IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 中山大学软件工程-Unity牧师与魔鬼作业 -> 正文阅读

[游戏开发]中山大学软件工程-Unity牧师与魔鬼作业

牧师与魔鬼小游戏

零、写在前面

  • 自定义的数据类不要继承Unity的MonoBehavior类,一开始在判断游戏角色对象是否为空的时候,即判断是否== null,发现判断的结果一直为true,后来发现是因为Character类继承了MonoBehavior类,此类及其子类均不允许被new出来,Unity重载了UnityEngine命名空间下的Object的==运算符,故导致了==的使用出现了问题。

  • 此次作业运用了MVC架构,体会到了模块之间低耦合度的好处,在修改代码时很方便,只需要找到对应的模块进行修改,修改结果不会对其它模块造成太大的影响。各个模块的明确分工,职责驱动使得整个项目能够方向明确地推进。

  • 利用组件的概念,当对象添加了Moveable组件,则其具有了移动的功能,当对象添加了GUIClick组件,则其具有对鼠标点击做出反应的功能,由此实现了代码的重用。

  • 通过运用facade设计模式也进一步体验到接口是如何实现解耦合的——控制器去完成UserAction接口,即UserAction实现了用户动作和控制器的解耦合。

一、 项目配置

  • 首先创建一个新项目,选择3D模板

  • 新项目的文件结构如下:

    • Assets/Resources下存放的是项目动态加载所需的图片以及预制,预制包括按要求制作成预制的牧师、魔鬼、船、河流和河岸,图片则是用于GUI装饰

    • Assets/Materials

    • Assets/Scripts中则存放的是项目代码,各个类之间遵守MVC架构

  • 由于图片是动态加载,故需要将在Inspector菜单中,将图片设置为可读可写状态,才能保证图片能被正常加载。

  • 最后将FirstController代码拖到Main Camera中,Ctrl+B即可运行。

二、游戏提及的事物

  • 牧师

  • 魔鬼

  • 河流

三、玩家动作表(规则表)

玩家动作前提条件结果
点击岸上的牧师或魔鬼船上有空位被点击的牧师或魔鬼移动到船上
点击船上的牧师或魔鬼被点击的牧师或魔鬼移动到岸上
点击GO按钮船上有乘客船移动到对岸
点击Rstart按钮游戏重置

四、 实现过程和方法

1. 总体设计思路

  • 整个项目使用MVC架构,各个代码文件划分如下

    • Model部分:Character、Boat、Land

    • View部分:GUI

    • Controller部分:Director、SceneController、FirstController

    • UserAction是一个接口,控制器去实现UserAction接口,实现用户动作和游戏逻辑实现的解耦合,也即门面设计模式的应用

    • GUIClick和Moveable则是作为两个组件,通过组合的方式作为游戏对象的组件,实现代码的复用

  • Model部分就是将游戏中出现的事物封装为各个类,管理各自的数据,然后受Controller的调用、控制,最后View部分实现将Model数据可视化通过用户界面的呈现。

2. 模块分析

Model部分

  • Character

    • 牧师和魔鬼就是通过实现Character类,其管理的数据的主要为一个GameObject对象,通过Object.Instantiate函数动态加载预制中的牧师或魔鬼模型。

    • Character类的功能主要有实现离岸、上岸、上船、下船的数据更新,以及通过moveable组件控制GameObject对象的移动。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    ?
    public class Character {
     ? ?readonly GameObject character;
     ? ?readonly int type; //0-priest 1-devil
     ? ?readonly Moveable moveableScript;// 移动脚本组件
     ? ?readonly GUIClick _GUIClick;// 响应鼠标点击组件
    ?
     ? ?private bool isOnBoat;// 是否在船上
     ? ?private bool isFinished;//是否已经过河
    ?
     ? ?// 构造函数,根据传入的字符串选择创建牧师对象或者魔鬼对象
     ? ?public Character(string sel){
     ? ? ? ?// 动态加载预制
     ? ? ? ?if(sel == "priest"){
     ? ? ? ? ? ?character = Object.Instantiate(Resources.Load("Prefabs/priest", typeof(GameObject)),Vector3.zero, Quaternion.identity, null) as GameObject;
     ? ? ? ? ? ?type = 0;
     ? ? ?  }
     ? ? ? ?else{
     ? ? ? ? ? ?character = Object.Instantiate(Resources.Load("Prefabs/devil", typeof(GameObject)),Vector3.zero, Quaternion.identity, null) as GameObject;
     ? ? ? ? ? ?type = 1;
     ? ? ?  }
     ? ? ? ?// 为对象添加移动和点击响应组件
     ? ? ? ?moveableScript = character.AddComponent (typeof(Moveable)) as Moveable;
     ? ? ? ?_GUIClick = character.AddComponent(typeof(GUIClick)) as GUIClick;
     ? ? ? ?_GUIClick.bindCharacter(this);
     ? ? ? ?// 初始化变量
     ? ? ? ?isOnBoat = false;
     ? ? ? ?isFinished = false;
     ?  }
    ?
     ? ?public int getType(){
     ? ? ? ?return type;
     ?  }
    ?
     ? ?public void setName(string name) {
     ? ? ? ?character.name = name;
     ?  }
    ?
     ? ?public string getName() {
            return character.name;
        }
    ?
     ? ?public void getOnBoat(Boat boat){
     ? ? ? ?// 设置transform为船的子组件,这样当船移动的时候就可以随着船移动
     ? ? ? ?character.transform.parent = boat.getGameobj().transform;
     ? ? ? ?isOnBoat = true;
     ?  }
    ?
     ? ?public void getOffBoat(){
     ? ? ? ?character.transform.parent = null;
     ? ? ? ?isOnBoat = false;
     ?  }
    ?
     ? ?public void getOnLand(Land land){
     ? ? ? ?if(land.getType() == 0){
     ? ? ? ? ? ?isFinished = false;
     ? ? ?  }
     ? ? ? ?else{
     ? ? ? ? ? ?isFinished = true;
     ? ? ?  }
     ? ? ? ?isOnBoat = false;
     ?  }
    ?
     ? ?public bool getIsOnBoat(){
     ? ? ? ?return isOnBoat;
     ?  }
    ?
     ? ?public void setPosition(Vector3 pos){
     ? ? ? ?character.transform.position = pos;
     ?  }
    ?
     ? ?public void moveToPosition(Vector3 pos){
     ? ? ? ?moveableScript.setDestination(pos);
     ?  }
    ?
     ? ?public bool getIsFinished(){
     ? ? ? ?return isFinished;
     ?  }
    ?
     ? ?// 重置对象
     ? ?public void reset(){
     ? ? ? ?moveableScript.reset();
     ? ? ? ?// 如果对象已经过河
     ? ? ? ?if(isFinished){
     ? ? ? ? ? ?// 通过导演对象获取当前场景的控制器,取得其控制的Land对象
     ? ? ? ? ? ?Land endLand = (Director.getInstance ().currentSceneController as FirstController).endLand;
     ? ? ? ? ? ?endLand.leaveLand(this);
     ? ? ? ? ? ?Land startLand = (Director.getInstance ().currentSceneController as FirstController).startLand;
     ? ? ? ? ? ?getOnLand(startLand);
     ? ? ? ? ? ?setPosition(startLand.getOnLand(this));
     ? ? ?  }
     ? ? ? ?// 如果对象在船上
     ? ? ? ?if(isOnBoat){
     ? ? ? ? ? ?getOffBoat();
     ? ? ? ? ? ?Land startLand = (Director.getInstance ().currentSceneController as FirstController).startLand;
     ? ? ? ? ? ?Boat boat = (Director.getInstance ().currentSceneController as FirstController).boat;
     ? ? ? ? ? ?boat.removePassenger(this);
     ? ? ? ? ? ?getOnLand(startLand);
     ? ? ? ? ? ?setPosition(startLand.getOnLand(this));
     ? ? ?  }
     ? ? ? ?character.transform.parent = null; ? ? ? ?
     ?  }
    ?
    }
  • Boat

    • 主要就是实现船对象,其主要的数据是一个GameObject对象,通过Object.Instantiate函数动态加载预制中的船模型。

    • 通过维护一个Character类数组,实现船上乘客的更新。

    • 主要功能包括:添加乘客、移除乘客、获得当前船上牧师和魔鬼的数量,以及通过moveable组件控制GameObject对象的移动。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    ?
    public class Boat{
     ? ?readonly GameObject boat;
     ? ?readonly Moveable moveableScript;
     ? ?readonly Vector3 startPos = new Vector3(-1.95F,0.68F,0);// 在起始岸边的位置
     ? ?readonly Vector3 endPos = new Vector3(1.95F,0.68F,0);// 在终点岸边的位置
     ? ?readonly Vector3 startFirstPos = new Vector3(-2.3F,1.15F,0);// 在起始岸边船的第一个座位位置
     ? ?readonly Vector3 startSecondPos = new Vector3(-1.54F,1.15F,0);// 在起始岸边船的第二个座位位置
     ? ?readonly Vector3 endFirstPos = new Vector3(1.54F,1.15F,0);// 在终点岸边船的第一个座位位置
     ? ?readonly Vector3 endSecondPos = new Vector3(2.37F,1.15F,0);// 在终点岸边船的第二个座位位置
    ?
     ? ?private Character[] passenger;// 乘客对象数组,存放船上的对象
     ? ?private int curPosition;//当前船在哪个岸边 0-start  1-end
     ? ?private int count;// 船上人数
    ?
     ? ?public Boat(){
     ? ? ? ?// 初始化对象数组,一开始船上无人,故置为空
     ? ? ? ?passenger = new Character[2];
     ? ? ? ?passenger[0] = null;
     ? ? ? ?passenger[1] = null;
     ? ? ? ?// 动态加载船的预制
     ? ? ? ?boat = Object.Instantiate (Resources.Load ("Prefabs/boat", typeof(GameObject)), startPos, Quaternion.identity, null) as GameObject;
            boat.name = "boat";
     ? ? ? ?// 添加moveable组件使得船可以移动
     ? ? ? ?moveableScript = boat.AddComponent (typeof(Moveable)) as Moveable;
     ? ? ? ?// 初始变量
     ? ? ? ?curPosition = 0;
     ? ? ? ?count = 0;
     ?  }
    ?
     ? ?public void move(){
     ? ? ? ?if(curPosition == 0){
     ? ? ? ? ? ?moveableScript.setDestination(endPos);
     ? ? ? ? ? ?curPosition = 1;
     ? ? ?  }
     ? ? ? ?else{
     ? ? ? ? ? ?moveableScript.setDestination(startPos);
     ? ? ? ? ? ?curPosition = 0;
     ? ? ?  }
     ?  }
    ?
     ? ?public bool isEmpty(){
     ? ? ? ?return count == 0;
     ?  }
    ?
     ? ?public bool isFull(){
     ? ? ? ?return count == 2;
     ?  }
    ?
     ? ?public bool addPassenger(Character ch){
     ? ? ? ?if(count == 2){
     ? ? ? ? ? ?return false;
     ? ? ?  }
     ? ? ? ?// 找到空位置
     ? ? ? ?for(int i = 0;i < 2;++i){
     ? ? ? ? ? ?if(passenger[i] == null){
     ? ? ? ? ? ? ? ?passenger[i] = ch;
     ? ? ? ? ? ? ? ?break;
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?count++;
     ? ? ? ?return true;
     ?  }
    ?
     ? ?public bool removePassenger(Character ch){
     ? ? ? ?if(count == 0){
     ? ? ? ? ? ?return false;
     ? ? ?  }
     ? ? ? ?// 找到要删除的乘客
     ? ? ? ?for(int i = 0;i < 2;++i){
     ? ? ? ? ? ?if(passenger[i] != null && passenger[i].getName() == ch.getName()){
     ? ? ? ? ? ? ? ?passenger[i] = null;
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?count--;
     ? ? ? ?return true;
     ?  }
    ?
     ? ?public int getCurPosition(){
     ? ? ? ?return curPosition;
     ?  }
    ?
     ? ?public Vector3 getEmptyPosition(){
     ? ? ? ?// 若船已满,则返回坐标(0,0,0)
     ? ? ? ?if(count == 2){
     ? ? ? ? ? ?return new Vector3(0,0,0);
     ? ? ?  }
     ? ? ? ?if(curPosition == 0){
     ? ? ? ? ? ?if(passenger[0] == null){
     ? ? ? ? ? ? ? ?return startFirstPos;
     ? ? ? ? ?  }
     ? ? ? ? ? ?else{
     ? ? ? ? ? ? ? ?return startSecondPos;
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?else{
     ? ? ? ? ? ?if(passenger[0] == null){
     ? ? ? ? ? ? ? ?return endFirstPos;
     ? ? ? ? ?  }
     ? ? ? ? ? ?else{
     ? ? ? ? ? ? ? ?return endSecondPos;
     ? ? ? ? ?  }
     ? ? ?  }
     ?  }
    ?
     ? ?public GameObject getGameobj(){
     ? ? ? ?return boat;
     ?  }
    ?
    ?
     ? ?public int getNumOfPriest(){
     ? ? ? ?int res = 0;
     ? ? ? ?for(int i = 0;i < 2;++i){
     ? ? ? ? ? ?if(passenger[i] != null && passenger[i].getType() == 0){
     ? ? ? ? ? ? ? ?res++;
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?return res;
     ?  }
    ?
     ? ?public int getNumOfDevil(){
     ? ? ? ?int res = 0;
     ? ? ? ?for(int i = 0;i < 2;++i){
     ? ? ? ? ? ?if(passenger[i] != null && passenger[i].getType() == 1){
     ? ? ? ? ? ? ? ?res++;
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?return res;
     ?  }
    ?
     ? ?public void reset(){
     ? ? ? ?count = 0;
     ? ? ? ?passenger[0] = null;
     ? ? ? ?passenger[1] = null;
     ? ? ? ?// 若船在终点,则回到起始岸
     ? ? ? ?if(curPosition == 1){
     ? ? ? ? ? ?moveableScript.setDestination(startPos);
     ? ? ? ? ? ?curPosition = 0;
     ? ? ?  }
     ?  }
    }
  • Land

    • 实现的是河岸对象,包含了一个GameObject对象,t对象,通过Object.Instantiate函数动态加载预制中的河岸模型。

    • Land类主要通过维护三个数组:当前河岸上的对象数组、位置数组、位置状态数组,这三者的下标一一对应。河岸上有六个位置,用于放置游戏角色,当某个位置上有角色的时候,即该位置被占有,此时位置状态数组相同下标对应的值会由变为1.

    • 因此,当有新的角色要上岸时,会先通过查找位置状态数组找到空位置,然后返回相同下标的位置数组的值,并将该对象存入相同下标的对象数组中,实际上就是一个哈希的过程。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    ?
    public class Land{
     ? ?private GameObject land;
     ? ?readonly Vector3 startPos = new Vector3(-6.6F,0,0);// 起始岸的位置
        readonly Vector3 endPos = new Vector3(6.6F,0,0);// 终点岸的位置
     ? ?
     ? ?private int type;//河岸类型 0-start 1-end
     ? ?private int count;// 岸上人数
     ? ?private Character[] curCharacter;// 当前岸上的角色
     ? ?readonly Vector3[] place;// 岸上的六个位置坐标 角色数组下标和位置数组下标保持一致
     ? ?private int[] emptyPlace;// 岸上六个位置的情况 0-empty 1-not empty
    ?
     ? ?public Land(string sel){
     ? ? ? ?place = new Vector3[6];
     ? ? ? ?if(sel == "start"){
     ? ? ? ? ? ?land = Object.Instantiate(Resources.Load("Prefabs/land", typeof(GameObject)),startPos, Quaternion.identity, null) as GameObject;
     ? ? ? ? ? ?land.name = "start";
     ? ? ? ? ? ?type = 0;
     ? ? ? ? ? ?count = 0;
     ? ? ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ? ? ?place[i] = new Vector3(-3.5F-0.8F*i,1.5F,0);
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?else{
     ? ? ? ? ? ?land = Object.Instantiate(Resources.Load("Prefabs/land", typeof(GameObject)),endPos, Quaternion.identity, null) as GameObject;
     ? ? ? ? ? ?land.name = "end";
     ? ? ? ? ? ?type = 1;
     ? ? ? ? ? ?count = 0;
     ? ? ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ? ? ?place[i] = new Vector3(3.5F+0.8F*i,1.5F,0);
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?curCharacter = new Character[6];
     ? ? ? ?emptyPlace = new int[6];
     ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ?curCharacter[i] = null;
     ? ? ?  }
     ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ?emptyPlace[i] = 0;
     ? ? ?  }
     ?  }
    ?
     ? ?public int getType(){
     ? ? ? ?return type;
     ?  }
    ?
     ? ?public int getCount(){
     ? ? ? ?return count;
     ?  }
    ?
     ? ?public Vector3 getOnLand(Character ch){
     ? ? ? ?int index = 0;
     ? ? ? ?// 找到空位置,将角色放入对应下标的角色数组中,方便后面查找删除
     ? ? ? ?while(true){
     ? ? ? ? ? ?if(emptyPlace[index] == 0){
     ? ? ? ? ? ? ? ?emptyPlace[index] = 1;
     ? ? ? ? ? ? ? ?curCharacter[index] = ch;
     ? ? ? ? ? ? ? ?count++;
     ? ? ? ? ? ? ? ?break;
     ? ? ? ? ?  }
     ? ? ? ? ? ?else{
     ? ? ? ? ? ? ? ?index++;
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?// 返回角色所在位置
     ? ? ? ?return place[index];
     ?  }
    ?
     ? ?public void leaveLand(Character ch){
     ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ?if( curCharacter[i] != null && curCharacter[i].getName() == ch.getName()){
     ? ? ? ? ? ? ? ?curCharacter[i] = null;
     ? ? ? ? ? ? ? ?emptyPlace[i] = 0;
     ? ? ? ? ? ? ? ?count--;
     ? ? ? ? ? ? ? ?return;
     ? ? ? ? ?  }
     ? ? ?  } ?
     ?  }
    ?
     ? ?public Vector3 getEmptyPosition(){
     ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ?if(emptyPlace[i] == 0){
     ? ? ? ? ? ? ? ?return place[i];
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?return new Vector3(0,0,0);
     ?  }
    ?
     ? ?public int getNumOfPriest(){
     ? ? ? ?int res = 0;
     ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ?if(curCharacter[i] == null){
     ? ? ? ? ? ? ? ?continue;
     ? ? ? ? ?  }
     ? ? ? ? ? ?if(curCharacter[i].getType() == 0){
     ? ? ? ? ? ? ? ?res++;
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?return res;
     ?  }
    ?
     ? ?public int getNumOfDevil(){
     ? ? ? ?int res = 0;
     ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ?if(curCharacter[i] == null){
     ? ? ? ? ? ? ? ?continue;
     ? ? ? ? ?  }
     ? ? ? ? ? ?if(curCharacter[i].getType() == 1){
     ? ? ? ? ? ? ? ?res++;
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?return res;
     ?  }
    ?
     ? ?public void reset(){
     ? ? ? ?if(type == 0){
     ? ? ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ? ? ?emptyPlace[i] = 1;
     ? ? ? ? ?  }
     ? ? ? ? ? ?count = 6;
     ? ? ?  }
     ? ? ? ?else{
     ? ? ? ? ? ?for(int i = 0;i < 6;++i){
     ? ? ? ? ? ? ? ?emptyPlace[i] = 0;
     ? ? ? ? ?  }
     ? ? ? ? ? ?count = 0;
     ? ? ?  }
     ?  }
    }

View部分

  • _GUI

    • 此类通过将用户的行为传递给控制器,从而修改model中的数据,并不断呈现最新的数据

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    ?
    public class _GUI : MonoBehaviour{
     ? ?private SceneController sc;
     ? ?private UserAction ac;
     ? ?private GUIStyle style;
     ? ?private int status;
     ? ?private Texture2D priest;
     ? ?private Texture2D devil;
     ? ?private Texture2D rule;
    ?
     ? ?void Start(){
     ? ? ? ?style = new GUIStyle();
            style.fontSize = 40;
     ? ? ? ?// 获得控制器,通过控制器更新model部分的数据
     ? ? ? ?sc = Director.getInstance ().currentSceneController;
     ? ? ? ?ac = Director.getInstance().currentSceneController as UserAction;
     ? ? ? ?status = (Director.getInstance ().currentSceneController as FirstController).status;
     ? ? ? ?// 动态加载图片
     ? ? ? ?priest = Instantiate (Resources.Load ("Pictures/牧师", typeof(Texture2D))) as Texture2D;
     ? ? ? ?devil = Instantiate (Resources.Load ("Pictures/恶魔", typeof(Texture2D))) as Texture2D;
     ? ? ? ?rule = Instantiate (Resources.Load ("Pictures/规则", typeof(Texture2D))) as Texture2D;
    ?
     ? ? ? ?// Debug.Log("Screen.width"+Screen.width);
     ? ? ? ?// Debug.Log(" Screen.height"+ Screen.height);
     ?  }
    ?
    ?
     ? ?void OnGUI(){
     ? ? ? ?// 同步游戏状态
     ? ? ? ?status = (Director.getInstance ().currentSceneController as FirstController).status;
     ? ? ? ?if(GUI.Button(new Rect(795,490, 120, 120),"GO")){
     ? ? ? ? ? ?if(status == 0){
     ? ? ? ? ? ? ? ?ac.goButtonIsClicked();
     ? ? ? ? ?  }
     ? ? ?  }
     ? ? ? ?if(GUI.Button(new Rect(1095,490, 120, 120), "Restart")){
     ? ? ? ? ? ?ac.restart();
     ? ? ?  }
     ? ? ? ?if(status == 1){
     ? ? ? ? ? ?GUI.Label(new Rect(800,100,300,250),devil);
     ? ? ? ? ? ?GUI.Label(new Rect(247, 50, 100, 50), "You lose!",style);
     ? ? ?  }
     ? ? ? ?if(status == 2){
     ? ? ? ? ? ?GUI.Label(new Rect(800,100,300,250),priest);
     ? ? ? ? ? ?GUI.Label(new Rect(247, 50, 100, 50), "You win!",style);
     ? ? ?  }
     ? ? ? ?GUI.Label(new Rect(0,800,1500,1500),rule);
     ?  }
    }

Controller部分

这一部分基本就是按照课程网站上的指示做

  • Director

    • Director继承自C#根对象,因此不受unity引擎的管理,无需加载,且通过应用单例模式保证全局只有一个实例对象

    • 其主要实现:获取当前游戏的场景、管理游戏全局状态

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    ?
    public class Director : System.Object{
     ? ?private static Director _instance;
     ? ?public SceneController currentSceneController { get; set; }
    ?
     ? ?public static Director getInstance() {
     ? ? ? ?if (_instance == null) {
     ? ? ? ? ? ?_instance = new Director ();
     ? ? ?  }
     ? ? ? ?return _instance;
     ?  }
    }
    ?
  • SceneController

    • SceneController是一个接口,由具体的场记来实现,对于这个游戏,此接口只有两个函数分别用于实现加载游戏资源和检查游戏状态

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    ?
    public interface SceneController {
     ? ?void loadResources ();
     ? ?void checkGameStatus();
    }
  • FirstController

    • FirstController是此游戏唯一的一个场记,负责实现GUI和模型数据之间的同步。

    • 其主要就是存放各个对象,并实现SceneController和UserAction接口,实现对模型对象的操作

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    ?
    public class FirstController : MonoBehaviour, SceneController, UserAction{
     ? ?readonly Vector3 water_pos = new Vector3(0,-0.4F,0);// 河流预制的位置
     ? ?
        public Land startLand;// 起始河岸
     ? ?public Land endLand;// 终点河岸
        public Boat boat;// 船
        public int status;//游戏状态 0-gaming 1-lose 2-win 
    ?
        private Character[] characters;// 六个游戏角色
        private _GUI userGUI;// GUI组件
    ?
     ? ?
        void Awake() {
            // 将自身设置为当前场记
            Director director = Director.getInstance ();
            director.currentSceneController = this;
            // 将GUI作为一个组件
            userGUI = gameObject.AddComponent <_GUI>() as _GUI;
            characters = new Character[6];
            // 加载游戏资源
            loadResources();
        }
    ?
        // 实现SceneController接口,加载游戏资源
     ? ?public void loadResources() {
            GameObject water = Instantiate (Resources.Load ("Prefabs/river", typeof(GameObject)), water_pos, Quaternion.identity, null) as GameObject;
            water.name = "water";
            startLand = new Land ("start");
            endLand = new Land ("end");
            boat = new Boat();
            loadCharacter ();
        }
    ?
        // 实现SceneController接口,检查游戏状态 0->not finish, 1->lose, 2->win
        public void checkGameStatus() { 
            // 船在出发点
            int startNumOfPriest = startLand.getNumOfPriest();
            int startNumOfDevil = startLand.getNumOfDevil();
            int endNumOfPriest = endLand.getNumOfPriest();
            int endNumOfDevil = endLand.getNumOfDevil();
            int boatNumOfPriest = boat.getNumOfPriest();
            int boatNumOfDevil = boat.getNumOfDevil();
            if(endNumOfPriest + endNumOfDevil + boatNumOfPriest + boatNumOfDevil == 6){
                status = 2;
                return ;
            }
            if(boat.getCurPosition() == 1){
                if((startNumOfPriest ?< startNumOfDevil && startNumOfPriest > 0)||(endNumOfPriest + ?boatNumOfPriest < endNumOfDevil + boatNumOfDevil && endNumOfPriest + ?boatNumOfPriest > 0)){
                    status = 1;
                    return ;
                }
            }
            else{
                if((startNumOfPriest + boatNumOfPriest < startNumOfDevil + boatNumOfDevil && startNumOfPriest + boatNumOfPriest > 0) || (endNumOfPriest < endNumOfDevil && endNumOfPriest > 0)){
                    status = 1;
                    return ;
                }
            }
            status = 0;
        }
    ?
        // 加载游戏对象
     ? ?private void loadCharacter() {
            for (int i = 0; i < 3; i++) {
                Character ch = new Character("priest");
                ch.setName("priest" + i);
                ch.getOnLand(startLand);
                ch.setPosition (startLand.getOnLand(ch));
                characters[i] = ch;
            }
    ?
            for (int i = 0; i < 3; i++) {
                Character ch = new Character("devil");
                ch.setName("devil" + i);
                ch.setPosition (startLand.getOnLand(ch));
                ch.getOnLand(startLand);
                characters [i+3] = ch;
            }
        }
    ?
        // 实现UserAction接口的goButtonIsClicked函数
        public void goButtonIsClicked(){
            if(!boat.isEmpty()){
                boat.move();
                checkGameStatus();
            }
        }
    ?
        // 实现UserAction接口的characterIsClicked函数
        public void characterIsClicked(Character ch){
            // 角色在船上
            if(ch.getIsOnBoat()){
                if(boat.getCurPosition() == 0){
                    ch.moveToPosition(startLand.getOnLand(ch));
                    ch.getOnLand(startLand);
                }
                else{
                    ch.moveToPosition(endLand.getOnLand(ch));
                    ch.getOnLand(endLand);
                }
                ch.getOffBoat();
                boat.removePassenger(ch);
            }
            // 角色在岸上
            else{
                if(!boat.isFull()){
                    if(ch.getIsFinished() && boat.getCurPosition() == 1){
                        ch.getOnBoat(boat);
                        endLand.leaveLand(ch);
                        ch.moveToPosition(boat.getEmptyPosition());
                        boat.addPassenger(ch);
                    }
                    if(!ch.getIsFinished() && boat.getCurPosition() == 0){
                        ch.getOnBoat(boat);
                        startLand.leaveLand(ch);
                        ch.moveToPosition(boat.getEmptyPosition());
                        boat.addPassenger(ch);
                    }
                }
    ?
            }
        }
    ?
        // 重置游戏
        public void restart(){
            status = 0;
            for(int i = 0;i < 6;++i){
                characters[i].reset();
            }
            boat.reset();
            startLand.reset();
            endLand.reset();        
        }
    }

组件部分

  • GUIClick

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
?
public class GUIClick : MonoBehaviour{
 ? ?UserAction action;
 ? ?Character bindingCharacter;// 组件当前绑定的角色对象
 ? ?SceneController sc;
 ? ?int status;// 游戏状态
?
 ? ?public void bindCharacter(Character ch){
 ? ? ? ?bindingCharacter = ch;
 ? ? ? ?status = (Director.getInstance ().currentSceneController as FirstController).status;
 ?  }
?
 ? ?void Start(){
 ? ? ? ?action = Director.getInstance().currentSceneController as UserAction;
 ? ? ? ?sc = Director.getInstance().currentSceneController;
 ?  }
?
 ? ?void OnMouseDown(){
 ? ? ? ?// 同步游戏状态
 ? ? ? ?status = (Director.getInstance ().currentSceneController as FirstController).status;
 ? ? ? ?// 只有在游戏中点击才有效
 ? ? ? ?if(status == 0){
 ? ? ? ? ? ?action.characterIsClicked(bindingCharacter);
 ? ? ?  }
 ?  }
}
  • Moveable

    此代码参考了师兄博客的实现,由于河岸和船不在同一水平线,故让游戏角色在上下船的时候以折线移动

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
?
public class Moveable : MonoBehaviour{
 ? ?readonly float speed = 10;
?
 ? ?int moving_status;  // 0->not moving, 1->moving to middle, 2->moving to dest
 ? ?Vector3 dest;
 ? ?Vector3 middle;
?
 ? ?void Update() {
 ? ? ? ?if (moving_status == 1) {
 ? ? ? ? ? ?transform.position = Vector3.MoveTowards (transform.position, middle, speed * Time.deltaTime);
 ? ? ? ? ? ?if (transform.position == middle) {
 ? ? ? ? ? ? ? ?moving_status = 2;
 ? ? ? ? ?  }
 ? ? ?  } 
 ? ? ? ?else if (moving_status == 2) {
 ? ? ? ? ? ?transform.position = Vector3.MoveTowards (transform.position, dest, speed * Time.deltaTime);
 ? ? ? ? ? ?if (transform.position == dest) {
 ? ? ? ? ? ? ? ?moving_status = 0;
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
?
 ? ?public void setDestination(Vector3 _dest) {
 ? ? ? ?dest = _dest;
 ? ? ? ?middle = _dest;
 ? ? ? ?if (_dest.y == transform.position.y) {  // boat moving
 ? ? ? ? ? ?moving_status = 2;
 ? ? ?  }
 ? ? ? ?else if (_dest.y < transform.position.y) {  // character from coast to boat
 ? ? ? ? ? ?middle.y = transform.position.y;
 ? ? ?  } else {                                // character from boat to coast
 ? ? ? ? ? ?middle.x = transform.position.x;
 ? ? ?  }
 ? ? ? ?moving_status = 1;
 ?  }
?
 ? ?public void reset() {
 ? ? ? ?moving_status = 0;
 ?  }
}

五、效果展示

[]: Unity3D作业-牧师与魔鬼过河演示视频_哔哩哔哩_bilibili

六、项目地址

huanghj78/Unity-Homework - 码云 - 开源中国 (gitee.com)

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-10-09 16:36:13  更:2021-10-09 16:37:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 2:37:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码