Unity3D学习笔记(4)—— 牧师和魔鬼游戏

        牧师和魔鬼游戏是一款益智类游戏,游戏的目标是将3个牧师和3个魔鬼从河的一端安全地送到河的另一端。在运送过程中,船可以搭载两个人,而且必须有一人掌船。无论何时,只要河一边的魔鬼数量多于牧师的数量,游戏就会以失败结束。想玩玩的话请走传送门:Priests and Devils


        先上个成品草图:

Unity3D学习笔记(4)—— 牧师和魔鬼游戏_第1张图片

        游戏需要的对象:

游戏角色:3个牧师、3个魔鬼

游戏场景:2个河岸、1艘小船

        游戏的架构:

Unity3D学习笔记(4)—— 牧师和魔鬼游戏_第2张图片

        UserInterface用来创建GUI对象接受玩家动作,处理GUI事件,使用 IUserActions 接口控制游戏。

        SceneController是单实例对象,用来处理对象间通信和实现 IUserActions 接口。

        BaseCode用来保存游戏名称和游戏简介,它注册在 SceneController 中。

        GenGameObject用来创建游戏对象和处理对象运动,它也注册在 SceneController 中。IUserActions 接口通过与 GenGameObject 互动完成游戏逻辑。


        想看完整的代码请直接跳至结尾~

一、新建一个3d游戏项目,建立 BaseCode 脚本,并挂载到主摄像机。

        在根目录 Assets 下创建 Scripts 文件夹,在 Scripts 文件夹下创建BaseCode 脚本:

Unity3D学习笔记(4)—— 牧师和魔鬼游戏_第3张图片

        在 BaseCode 脚本里面声明一个新的命名空间 Com.Mygame,并把单实例对象 GameSceneController 定义其中。这样的话,其它脚本要使用 GameSceneController 只需要在脚本开头添加 “using Com.Mygame” 就可以使用对象了,同时命名空间也将我们自己的类和系统类分别开来。BaseCode 脚本的代码如下:


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

namespace Com.Mygame {
	
	public class GameSceneController: System.Object {
		
		private static GameSceneController _instance;
		private BaseCode _base_code;
		
		public static GameSceneController GetInstance() {
			if (null == _instance) {
				_instance = new GameSceneController();
			}
			return _instance;
		}
		
		public BaseCode getBaseCode() {
			return _base_code;
		}
		
		internal void setBaseCode(BaseCode bc) {
			if (null == _base_code) {
				_base_code = bc;
			}
		}
	}
}

public class BaseCode : MonoBehaviour {
	
	public string gameName;
	public string gameRule;
	
	void Start () {
		GameSceneController my = GameSceneController.GetInstance();
		my.setBaseCode(this);
	}
}

        所谓单例类,即是全局中只能存在唯一一个对象的类,通过函数GetInstance()可以获得这个类的实例对象。因为 GameSceneController 单例类直接声明在 Com.Mygame 命名空间里,因此是全局的,在其中注入的对象都可以看作是全局对象,比如 BaseCode 对象。

        这样做的目的是实现代码的MVC架构,通过在单例类中注入对象可以实现对象间的通信,而无需使用 Find、SendMessage 这类破坏程序结构的通讯耦合语句。

        我希望用 BaseCode 记录游戏的名字和游戏的简介,因此在 BaseCode 中声明了两个公有变量 gameName、gameRule,它们会被将来的GUI对象获取并显示。


二、新建脚本GenGameObjects,挂载到主摄相机。新建脚本IUserInterface,挂载到一个Empty对象。


Unity3D学习笔记(4)—— 牧师和魔鬼游戏_第4张图片


三、在 GenGameObjects 中创建长方形、正方形、球及其色彩代表游戏中的对象。并把组件自己注入到 GameSceneController 单例模式对象。

        先在游戏面板创建游戏对象,在 Assets 文件夹下新建 Resources 文件夹,在 Resources 文件夹下新建 Prefabs 文件夹,然后将创建的对象拖入 Prefabs 中成为预设物体。

        我假设牧师为球体、魔鬼为正方体、河岸为长方体、船也为长方体


Unity3D学习笔记(4)—— 牧师和魔鬼游戏_第5张图片

                Unity3D学习笔记(4)—— 牧师和魔鬼游戏_第6张图片

        

        设置主摄像机的投影模式为正交投影,调整大小,这样看到的所有物体都是平面的了。


        GenGameObect 部分代码:

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;
	public float speed = 100f;

	GameSceneController my;

	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);

	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);


        这部分声明了 GenGameObject  各变量,其中我打算用 Stack 集合类型存储牧师和魔鬼对象,start表示开始岸,end表示目标岸。

        船需要三个变量,boat是一个对象数组,用来存储船所承载的物体,boat_obj为船的实体。Speed为公有变量(方便更改),代表船行驶的速度。

        My是一个 GameSceneController 对象,之所以需要 GameSceneController,是因为游戏逻辑需要“状态”,这些“状态”为枚举类型,并且保存在 GameSceneController 中。

        剩下的一系列 Vector3 类型,均表示物理位置,它们会在牧师、魔鬼、河岸、船预设实例化的时候被使用,或者在游戏物体需要移动时使用。


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


        这部分代码将 GenGameObject 对象注入了 GameSceneController 单实例对象中。当然,要在GameSceneController中定义该函数:


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

        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) {
		priests_start.Push(Instantiate(Resources.Load("Prefabs/Priest")) as GameObject);
		devils_start.Push(Instantiate(Resources.Load("Prefabs/Devil")) as GameObject);
	}
	// light
	Instantiate(Resources.Load("Prefabs/Light"));
}

        其中 light 是后来添加上去的,为了让场景中有光亮。注意,只有在 Resources 文件夹下的资源才能使用 Load 加载。


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


        在继续编写 GenGameObject 脚本之前,我们要明确游戏允许玩家的所有动作。这里,我规定的玩家动作有7个:

项目 条件
开船 船在开始岸、船在结束岸
船的左方下船 船靠岸且船左方有人
船的右方下船 船靠岸且船右方有人
开始岸的牧师上船 船在开始岸,船有空位,开始岸有牧师
开始岸的魔鬼上船 船在开始岸,船有空位,开始岸有魔鬼
结束岸的牧师上船 船在结束岸,船有空位,结束岸有牧师
结束岸的魔鬼上船 船在结束岸,船有空位,结束岸有魔鬼


五、基于规则表完善 GenGameObject 类

        考虑到牧师和魔鬼的位置时刻要根据堆栈的数据变化,因此先定义一个 setCharacterPositions 函数。该函数接受一个stack参数,和一个Vector3坐标。它的作用就是把stack里的object从Vector3坐标开始依次排开:


void setCharacterPositions(Stack<GameObject> stack, Vector3 pos) {
	GameObject[] array = stack.ToArray();
	for (int i = 0; i < stack.Count; ++i) {
		array[i].transform.position = new Vector3(pos.x, pos.y, pos.z + gap*i);
	}
}

        然后在 Update 方法里便可以使用:

void Update() {
	setCharacterPositions(priests_start, priestStartPos);
	setCharacterPositions(priests_end, priestEndPos);
	setCharacterPositions(devils_start, devilStartPos);
	setCharacterPositions(devils_end, devilEndPos);

        现在,我们来考虑规则抽象的行为,分为3种: 上船开船下船

1.  上船:把一个游戏对象设为船的子对象。

        定义 getOnTheBoat 函数,接受一个游戏对象为参数,只要船上有空位,就把游戏对象设置为船的子对象,这样游戏对象便能跟着船移动:


void getOnTheBoat(GameObject obj) {
	if (boatCapacity() != 0) {
		obj.transform.parent = boat_obj.transform;
		if (boat[0] == null) {
			boat[0] = obj;
			obj.transform.localPosition = new Vector3(0, 1.2f, -0.3f);
		} else {
			boat[1] = obj;
			obj.transform.localPosition = new Vector3(0, 1.2f, 0.3f);
		}
	}
}

2.  开船:根据游戏“状态”,把船从一方移动到另一方。

        这里,我们讨论到了游戏状态,我们需要游戏状态了解船当前的位置。游戏状态作为枚举类型声明在 BaseCode 脚本中:


public enum State { BSTART, BSEMOVING, BESMOVING, BEND, WIN, LOSE };
/*
 * BSTART:	boat stops on start shore
 * BEND:	boat stops on end shore
 * BSEMOVING:	boat is moving from start shore to end shore
 * BESMOVING:	boat is moving from end shore to start shore
 * WIN:		win
 * LOSE:	lose
 */

        有了游戏状态,只需要定义一个 moveBoat 函数,修改游戏状态为MOVING即可,剩下的动作均在Update函数中完成:


public void moveBoat() {
	if (boatCapacity() != 2) {
		if (my.state == State.BSTART) {
			my.state = State.BSEMOVING;
		}
		else if (my.state == State.BEND) {
			my.state = State.BESMOVING;
		}
	}
}

3.  下船:取消船和游戏对象的父子关系,并且根据游戏“状态”将游戏对象压入stack。

        定义 getOffTheBoat 函数,接受一个整型变量为参数,该变量可以为0或1:


public void getOffTheBoat(int side) {
	if (boat[side] != null) {
		boat[side].transform.parent = null;
		if (my.state == State.BEND) {
			if (boat[side].tag == "Priest") {
				priests_end.Push(boat[side]);
			}
			else if (boat[side].tag == "Devil") {
				devils_end.Push(boat[side]);
			}
		}
		else if (my.state == State.BSTART) {
			if (boat[side].tag == "Priest") {
				priests_start.Push(boat[side]);
			}
			else if (boat[side].tag == "Devil") {
				devils_start.Push(boat[side]);
			}
		}
		boat[side] = null;
	}
}

        注意到,为了区分出牧师和魔鬼,我给牧师和魔鬼预设分别添加了 Tag。Tag需要在控制面板添加。

Unity3D学习笔记(4)—— 牧师和魔鬼游戏_第7张图片    


        除此以外,还需要判断游戏的输赢,定义一个 check 函数:


void check() {
	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) {
		my.state = State.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 (my.state == State.BSTART) {
		priests_s = priests_start.Count + pOnb;
		devils_s = devils_start.Count + dOnb;
		priests_e = priests_end.Count;
		devils_e = devils_end.Count;
	}
	else if (my.state == State.BEND) {
		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)) {
		my.state = State.LOSE;
	}
}

        修改 Update 函数,加入船的移动,游戏结束条件的判断:


void Update() {
	setCharacterPositions(priests_start, priestStartPos);
	setCharacterPositions(priests_end, priestEndPos);
	setCharacterPositions(devils_start, devilStartPos);
	setCharacterPositions(devils_end, devilEndPos);

	if (my.state == State.BSEMOVING) {
		boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatEndPos, speed*Time.deltaTime);
		if (boat_obj.transform.position == boatEndPos) {
			my.state = State.BEND;
		}
	}
	else if (my.state == State.BESMOVING) {
		boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatStartPos, speed*Time.deltaTime);
		if (boat_obj.transform.position == boatStartPos) {
			my.state = State.BSTART;
		}
	}
	else check();
}

        OK,游戏的基本动作已经全部实现了!

       

        不过,为了与玩家规则表对应,还需要定义4个函数:

        priestStartOnBoat、priestEndOnBoat、devilStartOnBoat、devilEndOnBoat

        

        它们的作用是调用相应的 getOnTheBoat 函数,把玩家指定的对象放到船上:


public void priestStartOnBoat() {
	if (priests_start.Count != 0 && boatCapacity() != 0 && my.state == State.BSTART)
		getOnTheBoat(priests_start.Pop());
}

public void priestEndOnBoat() {
	if (priests_end.Count != 0 && boatCapacity() != 0 && my.state == State.BEND)
		getOnTheBoat(priests_end.Pop());
}

public void devilStartOnBoat() {
	if (devils_start.Count != 0 && boatCapacity() != 0 && my.state == State.BSTART)
		getOnTheBoat(devils_start.Pop());
}

public void devilEndOnBoat() {
	if (devils_end.Count != 0 && boatCapacity() != 0 && my.state == State.BEND)
		getOnTheBoat(devils_end.Pop());
}

六、修改 BaseCode 脚本,添加 IUserActions 接口,并在 GameSceneController 中实现。


        回到 BaseCode 脚本,在命名空间 Com.Mygame 里添加 IUserActions 接口:


public interface IUserActions {
	void priestSOnB();
	void priestEOnB();
	void devilSOnB();
	void devilEOnB();
	void moveBoat();
	void offBoatL();
	void offBoatR();
	void restart();
}

        在 GameSceneController 中添加接口的实现方法:


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() {
	Application.LoadLevel(Application.loadedLevelName);
	state = State.BSTART;
}

        注意,多了一个 Restart 方法哦~


七、修改 UserInterface 脚本,创建GUI对象,处理GUI事件。


        这里就不多讲了,主要是编写OnGUI函数,在合适的位置添加按钮,并且调用按钮所对应的接口函数:


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();
}

        当然啦,真正的代码不止这么少,比如要考虑游戏简介的显示(点击左上角会弹出简介框),还要考虑什么时候要隐藏用户操作的按钮(船在行驶的时候和游戏结束的时候)。


        到此为止,游戏已经基本完成了!

        整个游戏代码中没有出现 Find、SendMessage 等破坏程序结构的通讯耦合语句,场景中除了主摄像机和一个 Empty 对象外,所有其他的游戏对象都由代码动态生成。


        事实上,你可以把场景弄得更好看些:




 

完整代码

BaseCode.cs

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

namespace Com.Mygame {
	public enum State { BSTART, BSEMOVING, BESMOVING, BEND, WIN, LOSE };
	/*
	 * BSTART:	boat stops on start shore
	 * BEND:	boat stops on end shore
	 * BSEMOVING:	boat is moving from start shore to end shore
	 * BESMOVING:	boat is moving from end shore to start shore
	 * WIN:		win
	 * LOSE:	lose
	 */
	
	public interface IUserActions {
		void priestSOnB();
		void priestEOnB();
		void devilSOnB();
		void devilEOnB();
		void moveBoat();
		void offBoatL();
		void offBoatR();
		void restart();
	}

	public class GameSceneController: System.Object, IUserActions {

		private static GameSceneController _instance;
		private BaseCode _base_code;
		private GenGameObject _gen_game_obj;
		public State state = State.BSTART;

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

		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;
			}
		}

		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() {
			Application.LoadLevel(Application.loadedLevelName);
			state = State.BSTART;
		}
	}
}

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;
	public float speed = 100f;

	GameSceneController my;

	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);

	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 () {
		my = GameSceneController.GetInstance();
		my.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) {
			priests_start.Push(Instantiate(Resources.Load("Prefabs/Priest")) as GameObject);
			devils_start.Push(Instantiate(Resources.Load("Prefabs/Devil")) as GameObject);
		}
		// 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;
			if (boat[0] == null) {
				boat[0] = obj;
				obj.transform.localPosition = new Vector3(0, 1.2f, -0.3f);
			} else {
				boat[1] = obj;
				obj.transform.localPosition = new Vector3(0, 1.2f, 0.3f);
			}
		}
	}

	public void moveBoat() {
		if (boatCapacity() != 2) {
			if (my.state == State.BSTART) {
				my.state = State.BSEMOVING;
			}
			else if (my.state == State.BEND) {
				my.state = State.BESMOVING;
			}
		}
	}

	public void getOffTheBoat(int side) {
		if (boat[side] != null) {
			boat[side].transform.parent = null;
			if (my.state == State.BEND) {
				if (boat[side].tag == "Priest") {
					priests_end.Push(boat[side]);
				}
				else if (boat[side].tag == "Devil") {
					devils_end.Push(boat[side]);
				}
			}
			else if (my.state == State.BSTART) {
				if (boat[side].tag == "Priest") {
					priests_start.Push(boat[side]);
				}
				else if (boat[side].tag == "Devil") {
					devils_start.Push(boat[side]);
				}
			}
			boat[side] = null;
		}
	}

	public void priestStartOnBoat() {
		if (priests_start.Count != 0 && boatCapacity() != 0 && my.state == State.BSTART)
			getOnTheBoat(priests_start.Pop());
	}

	public void priestEndOnBoat() {
		if (priests_end.Count != 0 && boatCapacity() != 0 && my.state == State.BEND)
			getOnTheBoat(priests_end.Pop());
	}

	public void devilStartOnBoat() {
		if (devils_start.Count != 0 && boatCapacity() != 0 && my.state == State.BSTART)
			getOnTheBoat(devils_start.Pop());
	}

	public void devilEndOnBoat() {
		if (devils_end.Count != 0 && boatCapacity() != 0 && my.state == State.BEND)
			getOnTheBoat(devils_end.Pop());
	}

	void setCharacterPositions(Stack<GameObject> stack, Vector3 pos) {
		GameObject[] array = stack.ToArray();
		for (int i = 0; i < stack.Count; ++i) {
			array[i].transform.position = new Vector3(pos.x, pos.y, pos.z + gap*i);
		}
	}

	void check() {
		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) {
			my.state = State.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 (my.state == State.BSTART) {
			priests_s = priests_start.Count + pOnb;
			devils_s = devils_start.Count + dOnb;
			priests_e = priests_end.Count;
			devils_e = devils_end.Count;
		}
		else if (my.state == State.BEND) {
			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)) {
			my.state = State.LOSE;
		}
	}

	void Update() {
		setCharacterPositions(priests_start, priestStartPos);
		setCharacterPositions(priests_end, priestEndPos);
		setCharacterPositions(devils_start, devilStartPos);
		setCharacterPositions(devils_end, devilEndPos);

		if (my.state == State.BSEMOVING) {
			boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatEndPos, speed*Time.deltaTime);
			if (boat_obj.transform.position == boatEndPos) {
				my.state = State.BEND;
			}
		}
		else if (my.state == State.BESMOVING) {
			boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatStartPos, speed*Time.deltaTime);
			if (boat_obj.transform.position == boatStartPos) {
				my.state = State.BSTART;
			}
		}
		else check();
	}
}



UserInterface.cs

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

public class UserInterface : MonoBehaviour {

	GameSceneController my;
	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() {
		my = GameSceneController.GetInstance();
		action = GameSceneController.GetInstance() as IUserActions;
	}

	void OnGUI() {
		width = Screen.width / 12;
		height = Screen.height / 12;
		print (my.state);
		if (my.state == State.WIN) {
			if (GUI.Button(new Rect(castw(2f), casth(6f), width, height), "Win!")) {
				action.restart();
			}
		}
		else if (my.state == State.LOSE) {
			if (GUI.Button(new Rect(castw(2f), casth(6f), width, height), "Lose!")) {
				action.restart();
			}
		}
		else {
			if (GUI.RepeatButton(new Rect(10, 10, 120, 20), my.getBaseCode().gameName)) {
				GUI.TextArea(new Rect(10, 40, Screen.width - 20, Screen.height/2), my.getBaseCode().gameRule);
			}
			else if (my.state == State.BSTART || my.state == State.BEND) {
				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();
				}
			}
		}
	}
}


你可能感兴趣的:(C#,unity3d)