Unity3D学习笔记(5)—— 牧师和魔鬼游戏改进

        这次的改进主要是要实现游戏动作的简单工厂模式,使动作抽象出来,可以应用到任何游戏对象上。

改进:GenGameObject的动作分离

1.      新增单例类ActionManager:


         说明:简单工厂模式,MoveToYZAction为组合动作,负责游戏对象的上下船动作,利用IU3dActionCompleted和MoveToAction完成YZ轴上的二段运动。

 

2.      修改单例类GameSceneController:

1)  添加了私有变量moving和message(用来显示输赢信息)

2)  添加接口IQueryGameStatus,实现moving和message的查询和设置。

3)  去掉了枚举变量state(由moving和message替代)

 

3.      修改GenGameObject:

1)  修改枚举依赖

2)  修改输赢提示方式为设置message

3)  修改动作实现方式为ActionManage,不在Update中实现。

事实上,由于上一个版本我已经考虑到动作要分离,GenGameObject的所有动作都在Update中根据一个游戏状态枚举变量完成,无形中减少了很多工作量。

 

4.      修改UserInterface:

1)  修改枚举依赖,通过IQueryGameStatus查询

2)  修改输赢提示方式为显示message

 

PS:关于如何阻止玩家在动画过程中的人机交互,是通过UserInterface按钮的隐藏实现的。UserInterface在渲染前会通过IQueryGameStatus查询moving值,moving值为真则不渲染。Moving值在ActionManager中的每个动作完成前会设置成真。




BaseCode.cs

using UnityEngine;
using System.Collections;
using Com.Mygame;

namespace Com.Mygame {
	//-------------------------SceneController-------------------------------//
	public interface IUserActions {
		void priestSOnB();
		void priestEOnB();
		void devilSOnB();
		void devilEOnB();
		void moveBoat();
		void offBoatL();
		void offBoatR();
		void restart();
	}

	public interface IQueryGameStatus  {
		bool isMoving();
		void setMoving(bool state);
		string isMessage();
		void setMessage(string message);
	}

	public class GameSceneController: System.Object, IUserActions, IQueryGameStatus {

		private static GameSceneController _instance;
		private BaseCode _base_code;
		private GenGameObject _gen_game_obj;
		private bool moving = false;
		private string message = "";

		public static GameSceneController GetInstance() {
			if (null == _instance) {
				_instance = new GameSceneController();
			}
			return _instance;
		}

		// registration
		public BaseCode getBaseCode() {
			return _base_code;
		}

		internal void setBaseCode(BaseCode bc) {
			if (null == _base_code) {
				_base_code = bc;
			}
		}

		public GenGameObject getGenGameObject() {
			return _gen_game_obj;
		}
		
		internal void setGenGameObject(GenGameObject ggo) {
			if (null == _gen_game_obj) {
				_gen_game_obj = ggo;
			}
		}

		// IQueryGameStatus
		public bool isMoving() { return moving; }
		public void setMoving(bool state) { this.moving = state; }
		public string isMessage() { return message; }
		public void setMessage(string message) { this.message = message; }

		// IUserActions
		public void priestSOnB() { _gen_game_obj.priestStartOnBoat(); }
		public void priestEOnB() { _gen_game_obj.priestEndOnBoat(); }
		public void devilSOnB() { _gen_game_obj.devilStartOnBoat(); }
		public void devilEOnB() { _gen_game_obj.devilEndOnBoat(); }
		public void moveBoat() { _gen_game_obj.moveBoat(); }
		public void offBoatL() { _gen_game_obj.getOffTheBoat(0); }
		public void offBoatR() { _gen_game_obj.getOffTheBoat(1); }

		public void restart() {
			moving = false;
			message = "";
			Application.LoadLevel(Application.loadedLevelName);
		}
	}

	//----------------------------ActionManager-------------------------------//
	public interface IU3dActionCompleted {
		void OnActionCompleted (U3dAction action);
	}

	public class ActionManager :System.Object {
		private static ActionManager _instance;
		
		public static ActionManager GetInstance(){
			if (_instance == null) {
				_instance = new ActionManager();
			}
			return _instance;
		}

		// ApplyMoveToAction
		public U3dAction ApplyMoveToAction(GameObject obj, Vector3 target, float speed, IU3dActionCompleted completed){
			MoveToAction ac = obj.AddComponent <MoveToAction> ();
			ac.setting (target, speed, completed);
			return ac;
		}
		
		public U3dAction ApplyMoveToAction(GameObject obj, Vector3 target, float speed) {
			return ApplyMoveToAction (obj, target, speed, null);
		}

		// ApplyMoveToYZAction
		public U3dAction ApplyMoveToYZAction(GameObject obj, Vector3 target, float speed, IU3dActionCompleted completed){
			MoveToYZAction ac = obj.AddComponent <MoveToYZAction> ();
			ac.setting (obj, target, speed, completed);
			return ac;
		}
		
		public U3dAction ApplyMoveToYZAction(GameObject obj, Vector3 target, float speed) {
			return ApplyMoveToYZAction (obj, target, speed, null);
		}
	}
	
	public class U3dActionException : System.Exception {}
	
	public class U3dAction : MonoBehaviour {
		public void Free() {
			Destroy(this);
		}
	}
	
	public class U3dActionAuto : U3dAction {}
	
	public class U3dActionMan : U3dAction {}
	
	public class MoveToAction :  U3dActionAuto {
		public Vector3 target;
		public float speed;
		
		private IU3dActionCompleted monitor = null;
		
		public void setting(Vector3 target, float speed, IU3dActionCompleted monitor){
			this.target = target;
			this.speed = speed;
			this.monitor = monitor;
			GameSceneController.GetInstance().setMoving(true);
		}
		
		void Update () {
			float step = speed * Time.deltaTime;
			transform.position = Vector3.MoveTowards(transform.position, target, step);

			// Auto Destroy After Completed
			if (transform.position == target) { 
				GameSceneController.GetInstance().setMoving(false);
				if (monitor != null) {
					monitor.OnActionCompleted(this);
				}
				Destroy(this);
			}
		}
	}

	/* MoveToYZAction is a combination of two MoveToAction(s)
	 * It moves on a single shaft(Y or Z) each time
	 */
	public class MoveToYZAction : U3dActionAuto, IU3dActionCompleted {
		public GameObject obj;
		public Vector3 target;
		public float speed;

		private IU3dActionCompleted monitor = null;

		public void setting(GameObject obj, Vector3 target, float speed, IU3dActionCompleted monitor) {
			this.obj = obj;
			this.target = target;
			this.speed = speed;
			this.monitor = monitor;
			GameSceneController.GetInstance().setMoving(true);

			/* If obj is higher than target, move to target.z first, then move to target.y
			 * If obj is lower than target, move to target.y first, then move to target.z
			 * The turn is implemented through callback function
			 */
			if (target.y < obj.transform.position.y) {
				Vector3 targetZ = new Vector3(target.x, obj.transform.position.y, target.z);
				ActionManager.GetInstance().ApplyMoveToAction(obj, targetZ, speed, this);
			} else {
				Vector3 targetY = new Vector3(target.x, target.y, obj.transform.position.z);
				ActionManager.GetInstance().ApplyMoveToAction(obj, targetY, speed, this);
			}
		}

		// Implement the turn
		public void OnActionCompleted (U3dAction action) {
			// Note not calling this callback again!
			ActionManager.GetInstance().ApplyMoveToAction(obj, target, speed, null);
		}

		void Update() {
			// Auto Destroy After Completed
			if (obj.transform.position == target) { 
				GameSceneController.GetInstance().setMoving(false);
				if (monitor != null) {
					monitor.OnActionCompleted(this);
				}
				Destroy(this);
			}
		}
	}
}

public class BaseCode : MonoBehaviour {
	
	public string gameName;
	public string gameRule;

	void Start () {
		GameSceneController my = GameSceneController.GetInstance();
		my.setBaseCode(this);
		gameName = "Priests and Devils";
		gameRule = "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!             Sphere -- Priest	Cube -- Devil";
	}
}


GenGameObject.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Com.Mygame;

public class GenGameObject : MonoBehaviour {

	Stack<GameObject> priests_start = new Stack<GameObject>();
	Stack<GameObject> priests_end = new Stack<GameObject>();
	Stack<GameObject> devils_start = new Stack<GameObject>();
	Stack<GameObject> devils_end = new Stack<GameObject>();

	GameObject[] boat = new GameObject[2];
	GameObject boat_obj;
	int side = 1;				// side records where boat docks
	public float speed = 5f;

	Vector3 shoreStartPos = new Vector3(0, 0, -12);
	Vector3 shoreEndPos = new Vector3(0, 0, 12);
	Vector3 boatStartPos = new Vector3(0, 0, -4);
	Vector3 boatEndPos = new Vector3(0, 0, 4);
	Vector3 leftBoatPos = new Vector3(0, 1.2f, -1.2f);
	Vector3 rightBoatPos = new Vector3(0, 1.2f, 1.2f);

	float gap = 1.5f;
	Vector3 priestStartPos = new Vector3(0, 2.7f, -11f);
	Vector3 priestEndPos = new Vector3(0, 2.7f, 8f);
	Vector3 devilStartPos = new Vector3(0, 2.7f, -16f);
	Vector3 devilEndPos = new Vector3(0, 2.7f, 13f);

	void Start () {
		GameSceneController.GetInstance().setGenGameObject(this);
		loadSrc();
	}

	void loadSrc() {
		// shore
		Instantiate(Resources.Load("Prefabs/Shore"), shoreStartPos, Quaternion.identity);
		Instantiate(Resources.Load("Prefabs/Shore"), shoreEndPos, Quaternion.identity);
		// boat
		boat_obj = Instantiate(Resources.Load("Prefabs/Boat"), boatStartPos, Quaternion.identity) as GameObject;
		// priests & devils
		for (int i = 0; i < 3; ++i) {
			GameObject priest = Instantiate(Resources.Load("Prefabs/Priest")) as GameObject;
			priest.transform.position = getCharacterPosition(priestStartPos, i);
			priest.tag = "Priest";
			priests_start.Push(priest);
			GameObject devil = Instantiate(Resources.Load("Prefabs/Devil")) as GameObject;
			devil.transform.position = getCharacterPosition(devilStartPos, i);
			devil.tag = "Devil";
			devils_start.Push(devil);
		}
		// light
		Instantiate(Resources.Load("Prefabs/Light"));
	}

	int boatCapacity() {
		int capacity = 0;
		for (int i = 0; i < 2; ++i) {
			if (boat[i] == null) capacity++;
		}
		return capacity;
	}

	void getOnTheBoat(GameObject obj) {
		if (boatCapacity() != 0) {
			obj.transform.parent = boat_obj.transform;
			Vector3 target = new Vector3();
			if (boat[0] == null) {
				boat[0] = obj;
				target = boat_obj.transform.position + leftBoatPos;
			} else {
				boat[1] = obj;
				target = boat_obj.transform.position + rightBoatPos;
			}
			ActionManager.GetInstance().ApplyMoveToYZAction(obj, target, speed);
		}
	}

	public void moveBoat() {
		if (boatCapacity() != 2) {
			if (side == 1) {
				ActionManager.GetInstance().ApplyMoveToAction(boat_obj, boatEndPos, speed);
				side = 2;
			}
			else if (side == 2) {
				ActionManager.GetInstance().ApplyMoveToAction(boat_obj, boatStartPos, speed);
				side = 1;
			}
		}
	}

	public void getOffTheBoat(int bside) {
		if (boat[bside] != null) {
			boat[bside].transform.parent = null;
			Vector3 target = new Vector3();
			if (side == 1) {
				if (boat[bside].tag == "Priest") {
					priests_start.Push(boat[bside]);
					target = getCharacterPosition(priestStartPos, priests_start.Count - 1);
				}
				else if (boat[bside].tag == "Devil") {
					devils_start.Push(boat[bside]);
					target = getCharacterPosition(devilStartPos, devils_start.Count - 1);
				}
			}
			else if (side == 2) {
				if (boat[bside].tag == "Priest") {
					priests_end.Push(boat[bside]);
					target = getCharacterPosition(priestEndPos, priests_end.Count - 1);
				}
				else if (boat[bside].tag == "Devil") {
					devils_end.Push(boat[bside]);
					target = getCharacterPosition(devilEndPos, devils_end.Count - 1);
				}
			}
			ActionManager.GetInstance().ApplyMoveToYZAction(boat[bside], target, speed);
			boat[bside] = null;
		}
	}

	public void priestStartOnBoat() {
		if (priests_start.Count != 0 && boatCapacity() != 0 && side == 1)
			getOnTheBoat(priests_start.Pop());
	}

	public void priestEndOnBoat() {
		if (priests_end.Count != 0 && boatCapacity() != 0 && side == 2)
			getOnTheBoat(priests_end.Pop());
	}

	public void devilStartOnBoat() {
		if (devils_start.Count != 0 && boatCapacity() != 0 && side == 1)
			getOnTheBoat(devils_start.Pop());
	}

	public void devilEndOnBoat() {
		if (devils_end.Count != 0 && boatCapacity() != 0 && side == 2)
			getOnTheBoat(devils_end.Pop());
	}

	Vector3 getCharacterPosition(Vector3 pos, int index) {
		return new Vector3(pos.x, pos.y, pos.z + gap*index);
	}

	void check() {
		GameSceneController scene = GameSceneController.GetInstance();
		int pOnb = 0, dOnb = 0;
		int priests_s = 0, devils_s = 0, priests_e = 0, devils_e = 0;

		if (priests_end.Count == 3 && devils_end.Count == 3) {
			scene.setMessage("Win!");
			return;
		}

		for (int i = 0; i < 2; ++i) {
			if (boat[i] != null && boat[i].tag == "Priest") pOnb++;
			else if (boat[i] != null && boat[i].tag == "Devil") dOnb++;
		}
		if (side == 1) {
			priests_s = priests_start.Count + pOnb;
			devils_s = devils_start.Count + dOnb;
			priests_e = priests_end.Count;
			devils_e = devils_end.Count;
		}
		else if (side == 2) {
			priests_s = priests_start.Count;
			devils_s = devils_start.Count;
			priests_e = priests_end.Count + pOnb;
			devils_e = devils_end.Count + dOnb;
		}
		if ((priests_s != 0 && priests_s < devils_s) || (priests_e != 0 && priests_e < devils_e)) {
			scene.setMessage("Lose!");
		}
	}

	void Update() {
		check();
	}

}


UserInterface.cs

using UnityEngine;
using System.Collections;
using Com.Mygame;

public class UserInterface : MonoBehaviour {

	GameSceneController scene;
	IQueryGameStatus state;
	IUserActions action;

	float width, height;

	float castw(float scale) {
		return (Screen.width - width) / scale;
	}

	float casth(float scale) {
		return (Screen.height - height) / scale;
	}

	void Start() {
		scene = GameSceneController.GetInstance();
		state = GameSceneController.GetInstance() as IQueryGameStatus;
		action = GameSceneController.GetInstance() as IUserActions;
	}

	void OnGUI() {
		width = Screen.width / 12;
		height = Screen.height / 12;

		string message = state.isMessage();
		if (message != "") {
			if (GUI.Button(new Rect(castw(2f), casth(6f), width, height), message)) {
				action.restart();
			}
		}
		else {
			if (GUI.RepeatButton(new Rect(10, 10, 120, 20), scene.getBaseCode().gameName)) {
				GUI.TextArea(new Rect(10, 40, Screen.width - 20, Screen.height/2), scene.getBaseCode().gameRule);
			}
			else if (!state.isMoving()) {
				if (GUI.Button(new Rect(castw(2f), casth(6f), width, height), "Go")) {
					action.moveBoat();
				}
				if (GUI.Button(new Rect(castw(10.5f), casth(4f), width, height), "On")) {
					action.devilSOnB();
				}
				if (GUI.Button(new Rect(castw(4.3f), casth(4f), width, height), "On")) {
					action.priestSOnB();
				}
				if (GUI.Button(new Rect(castw(1.1f), casth(4f), width, height), "On")) {
					action.devilEOnB();
				}
				if (GUI.Button(new Rect(castw(1.3f), casth(4f), width, height), "On")) {
					action.priestEOnB();
				}
				if (GUI.Button(new Rect(castw(2.5f), casth(1.3f), width, height), "Off")) {
					action.offBoatL();
				}
				if (GUI.Button(new Rect(castw(1.7f), casth(1.3f), width, height), "Off")) {
					action.offBoatR();
				}
			}
		}
	}
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值