首先回顾一下牧师与魔鬼的小游戏的要求:

在河的一边有三个牧师和三个恶魔。他们都想去这条河的另一边,但是只有一条船,而且这条船每次只能载两个人。一定有一个人把船从一边开到另一边。在flash游戏中,你可以点击它们来移动它们,点击go按钮来移动河两岸的恶魔,它们被杀死,游戏结束。你可以用很多方式来尝试。让所有的牧师都活着!

玩家动作 执行条件 执行结果
点击牧师/魔鬼 游戏未结束,船没有正在移动,与船在相同的一边 牧师/魔鬼移动
点击船 游戏未结束,船上至少有一个角色 船移动到河流的另一头
点击Restart 游戏结束(win or lose) 各对象归位,游戏重新开始

和上一个版本不同的是,这次的牧师与魔鬼要求动作分离。在我的理解中,就是把所有游戏对象的动作都抽取出来,通过这个动作管理器统一安排动作的执行。上一个版本中,每个游戏对象有自己的控制器,如RoleModel类控制游戏角色(包括牧师和魔鬼)的动作,比如上下船;LandModel类控制河岸的动作,比如提供空位置给游戏角色。比如Move类控制牧师和魔鬼的移动。

这样,各个动作的脚本是挂载在各自的游戏对象身上执行的,表面看起来井井有条,实则不利于管理。使用动作分离器,就是将这些动作都安放在一个总的控制管理器中,由它统一安排动作的执行。

先放游戏截图(白色球体代表牧师,黑色方块代表魔鬼):



下面结合代码介绍实现。
· SSDirector
导演类。导演使用单例模式保证导演实例有且仅有一个,只负责在场景初始化时控制对应场景的场记。

public class SSDirector : System.Object
{private static SSDirector _instance;public ISceneController currentScenceController { get; set; }public bool running { get; set; }public static SSDirector getInstance(){if (_instance == null){_instance = new SSDirector();}return _instance;}public int getFPS(){return Application.targetFrameRate;}public void setFPS(int fps){Application.targetFrameRate = fps;}
}

· Interface
接口命名空间。提供场景控制、玩家动作控制的接口,只给用户界面提供API,降低耦合性,体现了良好的封装。

namespace Interfaces
{public interface ISceneController{void LoadResources();}public interface UserAction{void MoveBoat();void ObjectIsClicked(Character characterCtrl);void Restart();}public enum SSActionEventType : int { Started, Completed }public interface SSActionCallback{void SSActionCallback(SSAction source);}
}

· FirstSceneActionManager
动作控制器,管理船和游戏角色的移动,这个类体现了动作分离的思想,脚本不需要挂载到对象身上运行,集中体现了和上一个版本的区别。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Interfaces;public class FirstSceneActionManager : SSActionManager, SSActionCallback
{public SSActionEventType Complete = SSActionEventType.Completed;public void BoatMove(BoatController Boat){Complete = SSActionEventType.Started;CCMoveToAction action = CCMoveToAction.getAction(Boat.GetDestination(), Boat.GetMoveSpeed());addAction(Boat.GetGameObject(), action, this);Boat.ChangeState();}public void CharacterMove(Character GameObject, Vector3 Destination){Complete = SSActionEventType.Started;Vector3 CurrentPos = GameObject.GetPosition();Vector3 MiddlePos = CurrentPos;if (Destination.y > CurrentPos.y){MiddlePos.y = Destination.y;}else{MiddlePos.x = Destination.x;}SSAction action1 = CCMoveToAction.getAction(MiddlePos, GameObject.GetMoveSpeed());SSAction action2 = CCMoveToAction.getAction(Destination, GameObject.GetMoveSpeed());SSAction seqAction = CCSequenceAction.getAction(1, 0, new List<SSAction> { action1, action2 });this.addAction(GameObject.GetGameobject(), seqAction, this);}public void SSActionCallback(SSAction source){Complete = SSActionEventType.Completed;}
}

· Main
主类,负责实例化接口ISceneController和UserAction,实现加载资源和响应用户操作。当裁判类返回一个游戏结束的信息,则场记通知玩家交互类InteractGUI显示结束信息。

public class FirstController : MonoBehaviour, ISceneController, UserAction
{public InteractGUI UserGUI;public CoastController fromCoast;public CoastController toCoast;public BoatController boat;//新增的裁判类//裁判类需要用角色、船的控制器来初始化,以获取游戏进行的信息public Judge judge;private Character[] Character;private FirstSceneActionManager FSAmanager;void Awake(){SSDirector director = SSDirector.getInstance();director.currentScenceController = this;UserGUI = gameObject.AddComponent<InteractGUI>() as InteractGUI;//存储6个游戏角色Character = new Character[6];LoadResources();}void Start(){FSAmanager = GetComponent<FirstSceneActionManager>();}public void LoadResources(){fromCoast = new CoastController("from");toCoast = new CoastController("to");boat = new BoatController();//初始化裁判类judge = new Judge(fromCoast, toCoast, boat);GameObject river = Instantiate(Resources.Load("Prefabs/river", typeof(GameObject)), new Vector3(0, -7, 10), Quaternion.identity, null) as GameObject;river.name = "river";//加载牧师for (int i = 0; i < 3; i++){Character p = new Character("priest");p.setName("priest" + i);p.setPosition(fromCoast.getEmptyPosition());p.getOnCoast(fromCoast);fromCoast.getOnCoast(p);Character[i] = p;}//加载魔鬼for (int i = 0; i < 3; i++){Character d = new Character("devil");d.setName("devil" + i);d.setPosition(fromCoast.getEmptyPosition());d.getOnCoast(fromCoast);fromCoast.getOnCoast(d);Character[i + 3] = d;}}//鼠标点击事件public void ObjectIsClicked(Character Objects){if (FSAmanager.Complete == SSActionEventType.Started) return;if (Objects.isOnBoat()){CoastController whichCoast;if (boat.get_State() == -1){ whichCoast = toCoast;}else{whichCoast = fromCoast;}//下船动作boat.GetOffBoat(Objects.getName());FSAmanager.CharacterMove(Objects, whichCoast.getEmptyPosition());//上岸动作Objects.getOnCoast(whichCoast);whichCoast.getOnCoast(Objects);}else{CoastController whichCoast = Objects.getCoastController(); if (boat.getEmptyIndex() == -1){return;}if (whichCoast.get_State() != boat.get_State())  return;//上船动作whichCoast.getOffCoast(Objects.getName());FSAmanager.CharacterMove(Objects, boat.getEmptyPosition());Objects.getOnBoat(boat);boat.GetOnBoat(Objects);}//通知controller游戏结束,显示win或loseUserGUI.SetState = judge.Check();}//船移动的动作public void MoveBoat(){if (FSAmanager.Complete == SSActionEventType.Started || boat.isEmpty()) return;FSAmanager.BoatMove(boat);//新增的裁判类UserGUI.SetState = judge.Check();}//游戏结束,玩家重新开始的动作public void Restart(){fromCoast.reset();toCoast.reset();foreach (Character gameobject in Character){gameobject.reset();}boat.reset();}
}

· Judge
新增的裁判类,通过简单的计数,判断游戏是否结束

public class Judge : MonoBehaviour
{CoastController fromCoast;CoastController toCoast;public BoatController boat;public Judge(CoastController c1,CoastController c2, BoatController b){fromCoast = c1;toCoast = c2;boat = b;}public int Check(){   // 0->not finish, 1->lose, 2->winint from_priest = 0;int from_devil = 0;int to_priest = 0;int to_devil = 0;int[] fromCount = fromCoast.GetobjectsNumber();from_priest += fromCount[0];from_devil += fromCount[1];int[] toCount = toCoast.GetobjectsNumber();to_priest += toCount[0];to_devil += toCount[1];if (to_priest + to_devil == 6)      // winreturn 2;int[] boatCount = boat.GetobjectsNumber();if (boat.get_State() == -1){   // boat at toCoastto_priest += boatCount[0];to_devil += boatCount[1];}else{   // boat at fromCoastfrom_priest += boatCount[0];from_devil += boatCount[1];}if (from_priest < from_devil && from_priest > 0){       // losereturn 1;}if (to_priest < to_devil && to_priest > 0){return 1;}return 0;           // not finish}
}

· InteractGUI
UI交互,实现用户的点击事件,以及在游戏结束后显示结果

public class InteractGUI : MonoBehaviour
{UserAction UserAcotionController;public int SetState { get { return GameState; } set { GameState = value; } }static int GameState = 0;// Use this for initializationvoid Start(){UserAcotionController = SSDirector.getInstance().currentScenceController as UserAction;}private void OnGUI(){if (GameState == 1){GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2-50, 100, 50), "Gameover!");if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart")){GameState = 0;UserAcotionController.Restart();}}else if (GameState == 2){GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2-50, 100, 50), "You Win!");if (GUI.Button(new Rect(Screen.width / 2 - 70, Screen.height / 2, 140, 70), "Restart")){GameState = 0;UserAcotionController.Restart();}}}
}public class ClickGUI : MonoBehaviour
{UserAction UserAcotionController;Character GameObjectsInScene;public void setController(Character characterCtrl){GameObjectsInScene = characterCtrl;}void Start(){UserAcotionController = SSDirector.getInstance().currentScenceController as UserAction;}void OnMouseDown(){if (gameObject.name == "boat"){UserAcotionController.MoveBoat();}else{UserAcotionController.ObjectIsClicked(GameObjectsInScene);}}
}

· Character
游戏角色控制,简单来说有下岸、上船、下船、上岸四个动作。需要记录位置信息(是否在岸上,是在左岸还是右岸)以及移动信息(是否处于移动状态,是则无法响应点击事件)。

public class Character
{CoastController coastController;readonly GameObject Instance;readonly ClickGUI clickGUI;readonly int characterType; // 0->priest, 1->devilint MovingState = -1; // Move = 1;Not Move = -1;bool _isOnBoat = false;public Character(string Type){MovingState = -1;if (Type == "priest"){Instance = Object.Instantiate(Resources.Load("Prefabs/priests", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;characterType = 0;}else{Instance = Object.Instantiate(Resources.Load("Prefabs/devils", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;characterType = 1;}clickGUI = Instance.AddComponent(typeof(ClickGUI)) as ClickGUI;clickGUI.setController(this);}public void setName(string name){Instance.name = name;}public void setPosition(Vector3 pos){Instance.transform.position = pos;}public int getType(){   // 0->priest, 1->devilreturn characterType;}public string getName(){return Instance.name;}public void getOnBoat(BoatController boatCtrl){coastController = null;Instance.transform.parent = boatCtrl.GetGameObject().transform;_isOnBoat = true;}public void getOnCoast(CoastController coastCtrl){coastController = coastCtrl;Instance.transform.parent = null;_isOnBoat = false;}public bool isOnBoat(){return _isOnBoat;}public CoastController getCoastController(){return coastController;}public Vector3 GetPosition(){return Instance.transform.position;}public int GetMoveSpeed(){return 20;}public GameObject GetGameobject(){return Instance;}public void reset(){coastController = (SSDirector.getInstance().currentScenceController as FirstController).fromCoast;getOnCoast(coastController);setPosition(coastController.getEmptyPosition());coastController.getOnCoast(this);MovingState = -1;}public int GetMovingState(){return MovingState;}public void ChangeMovingstate(){MovingState = -MovingState;}
}

· CoastController
河岸控制器,主要职能是返回河岸上空余的位置,以便游戏角色落脚。

public class CoastController
{readonly GameObject coast;readonly Vector3 from_pos = new Vector3(-16, -6, 10);readonly Vector3 to_pos = new Vector3(16, -6, 10);readonly Vector3[] positions;readonly int State;    // to->-1, from->1Character[] passengerPlaner;public CoastController(string _State){positions = new Vector3[] {new Vector3(-21,-2.5F,10), new Vector3(-19,-2.5F,10), new Vector3(-17,-2.5F,10),new Vector3(-15,-2.5F,10), new Vector3(-13,-2.5F,10), new Vector3(-11,-2.5F,10)};passengerPlaner = new Character[6];if (_State == "from"){coast = Object.Instantiate(Resources.Load("Prefabs/land1", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;coast.name = "from";State = 1;}else{coast = Object.Instantiate(Resources.Load("Prefabs/land2", typeof(GameObject)), to_pos, Quaternion.identity, null) as GameObject;coast.name = "to";State = -1;}}public int getEmptyIndex(){for (int i = 0; i < passengerPlaner.Length; i++){if (passengerPlaner[i] == null){return i;}}return -1;}public Vector3 getEmptyPosition(){Vector3 pos = positions[getEmptyIndex()];pos.x *= State;return pos;}public void getOnCoast(Character ObjectControl){int index = getEmptyIndex();passengerPlaner[index] = ObjectControl;}public Character getOffCoast(string passenger_name){   // 0->priest, 1->devilfor (int i = 0; i < passengerPlaner.Length; i++){if (passengerPlaner[i] != null && passengerPlaner[i].getName() == passenger_name){Character charactorCtrl = passengerPlaner[i];passengerPlaner[i] = null;return charactorCtrl;}}return null;}public int get_State(){return State;}public int[] GetobjectsNumber(){int[] count = { 0, 0 };for (int i = 0; i < passengerPlaner.Length; i++){if (passengerPlaner[i] == null)continue;if (passengerPlaner[i].getType() == 0){   // 0->priest, 1->devilcount[0]++;}else{count[1]++;}}return count;}public void reset(){passengerPlaner = new Character[6];}}

· BoatController
船的控制类,提供船上的空余位置,协助实现角色的上下船

public class BoatController
{readonly GameObject boat;readonly Vector3 fromPosition = new Vector3(-8, -5, 10);readonly Vector3 toPosition = new Vector3(8, -5, 10);readonly Vector3[] from_positions;readonly Vector3[] to_positions;int State; // to->-1; from->1Character[] passenger = new Character[2];int Speed = 15;int MovingState = -1; // Move = 1;Not Move = -1;public BoatController(){State = 1;MovingState = -1;from_positions = new Vector3[] { new Vector3(-9, -3.5F, 10), new Vector3(-7, -3.5F, 10) };to_positions = new Vector3[] { new Vector3(7, -3.5F, 10), new Vector3(9, -3.5F, 10) };boat = Object.Instantiate(Resources.Load("Prefabs/boat", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject;boat.name = "boat";boat.AddComponent(typeof(ClickGUI));}public int getEmptyIndex(){for (int i = 0; i < passenger.Length; i++){if (passenger[i] == null){return i;}}return -1;}public bool isEmpty(){for (int i = 0; i < passenger.Length; i++){if (passenger[i] != null){return false;}}return true;}public Vector3 getEmptyPosition(){Vector3 pos;int emptyIndex = getEmptyIndex();if (State == -1){pos = to_positions[emptyIndex];}else{pos = from_positions[emptyIndex];}return pos;}public void GetOnBoat(Character ObjectControl){int index = getEmptyIndex();passenger[index] = ObjectControl;}public Character GetOffBoat(string passenger_name){for (int i = 0; i < passenger.Length; i++){if (passenger[i] != null && passenger[i].getName() == passenger_name){Character charactorCtrl = passenger[i];passenger[i] = null;return charactorCtrl;}}return null;}public GameObject GetGameObject(){return boat;}public void ChangeState(){State = -State;}public int get_State(){ // to->-1; from->1return State;}public int[] GetobjectsNumber(){int[] count = { 0, 0 };// [0]->priest, [1]->devilfor (int i = 0; i < passenger.Length; i++){if (passenger[i] == null)continue;if (passenger[i].getType() == 0){count[0]++;}else{count[1]++;}}return count;}public Vector3 GetDestination(){if (State == 1) return toPosition;else return fromPosition;}public int GetMoveSpeed(){return Speed;}public void reset(){State = 1;boat.transform.position = fromPosition;passenger = new Character[2];MovingState = -1;}public int GetMovingState(){return MovingState;}public void ChangeMovingstate(){MovingState = -MovingState;}
}

最后,放上GitHub传送门

魔鬼与牧师——动作分离版相关推荐

  1. 魔鬼与牧师动作分离版

    关于动作分离.可以通过对游戏对象增加可执行的动作,可以划分出很多的基础动作,如上船,下船,前进后退等.所以我们可以通过提取基础动作,然后使用类的方法来执行游戏对象的动作,同时通过配备动作管理者,让其去 ...

  2. 牧师与魔鬼——动作分离版

    牧师与魔鬼--动作分离版 在上周的作业中,牧师与魔鬼游戏中的各个事件,都是写在Director中,并且都是继承Monobehavior的.在这周动作分离的设计中,我将上船.下船以及船的移动都分离出来. ...

  3. Unity实战之牧师与魔鬼(动作分离版)

    Unity实战之牧师与魔鬼(动作分离版) 项目链接 整体描述 本次项目在第一版牧师与魔鬼的基础上,将动作从场记中分离出来,并设计一个裁判类实时监测游戏进行的情况.这样改进的优点有很多: 降低了不同功能 ...

  4. Unity牧师与魔鬼小游戏(动作分离版)

    Unity牧师与魔鬼小游戏(动作分离版) 前言 这是中大计算机学院3D游戏编程课的一次作业,在这里分享一下设计思路. 主要代码上传到了gitee上,请按照后文的操作运行. 项目地址:https://g ...

  5. unity编程实践-牧师与魔鬼动作分离版

    作业要求 牧师与魔鬼 动作分离版 [2019开始的新要求]:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束 目标:建立动作管理器,使动作抽象出来,可以应用到任何游戏对象上,以此提高代码复 ...

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

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

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

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

  8. unity实现牧师与魔鬼2.0(动作分离版) 基本操作演练

    unity实现牧师与魔鬼2.0(动作分离版)& 基本操作演练 & 材料与渲染联系 基本操作演练 下载 Fantasy Skybox FREE, 构建自己的游戏场景 在Window-&g ...

  9. 牧师与魔鬼 动作分离版

    1.基本操作演练 下载 Fantasy Skybox FREE 在unityAssetStore中下载Fantasy Skybox FREE 构建游戏场景: GameObject->3D Obj ...

最新文章

  1. Entity Framework学习三:查询、插入、更新和删除操作
  2. 人工智能是人性的罗夏测试
  3. 自对齐(self-aligned)
  4. 使用cx_freeze打包Python程序
  5. ES6新特性之扩展运算符
  6. 快速构建Windows 8风格应用10-设备方向
  7. Node爬虫,爬取传播客新闻列表
  8. 软件项目管理实用教程(人民邮电出版)第二章课后习题
  9. AWS Toolkit for Eclipse环境配置
  10. python爬虫框架论文开题报告范文_基于Web爬虫系统设计开题报告
  11. 自媒体文章原创度检测,提高文章通过率!
  12. 趋势科技2014校园招聘笔试题
  13. 动态分析Android App之动态调试(一)
  14. 怎么把ppt弄成链接的形式_如何将ppt转换成html网页格式
  15. 分布式锁的实现【转载】
  16. 零空间维数的几何意义
  17. 享受知识饕餮盛宴,尽在近期课程安排
  18. 锐捷ac怎么发现局域网ap_【实战】锐捷AC+AP配置WLAN基本服务系列
  19. 选择与放弃决定着你的生命
  20. java excel解析:poi与jxl的区别(excel版本问题:xls,xlsx)

热门文章

  1. OSChina 周六乱弹 —— 女友是啥子哟?生命的最大负载?
  2. php支持连接sql server数据库
  3. 安规之电气间隙和爬电距离
  4. easyUI前端框架的tree(树)前台展示(树形菜单二)——java
  5. mm_cas登入失败
  6. MOOC翁恺老师零基础学Java语言课程编程题——第六周
  7. 企业人事信息管理系统1.0
  8. Remove Duplicates
  9. 活猫还是死猫?| 薛定谔的猫 | 儿童故事
  10. 汇编语言rep movsd 的使用