游戏事物:
3牧师,3恶魔,2河岸,河,船。
游戏故事:3牧师和3恶魔需要用一艘船全部到达彼岸,但是船上和岸上都不能出现恶魔比牧师多的情形,否则恶魔会把牧师K.O,玩家输掉比赛;直到所有牧师恶魔都到达对岸,玩家取得胜利。
MVC架构:
IUserAction:是个接口,定义了行为的类型。
UserGUI:创建GUI对象,实现玩家互动,处理玩家操作,并通过IUserAction接口实现具体行为。
ISceneController:是个接口,定义了加载资源的方法。
SSDirector:是个单实例对象,不受Unity内存管理,只有返回实例化对象的方法,也声明了一个ISceneController方便各个接口调用。
FirstController:实现了加载资源,处理对象运动,互动处理的所有具体方法。通过ISceneController和IUserAction两个接口实现具体互动。
规则表(列出玩家动作的表)
脚本和预制
游戏界面:(没有优化UI 有点吃藕)
游戏初始状态,左上方是所有操作按钮,分别对应牧师上下船,魔鬼上下船,由预制可知,方块是牧师,球是魔鬼:
上船:
GO:
(至于颜色为什么不一样的了,场景用的是实时光,要烘焙之后,重新加载才会保留之前的效果,我这里没有进行烘焙,并且这是我玩了好几次不断重新加载后再截的图,重新加载的意思是我游戏写的restart()调用的重新加载场景函数)
下船:
胜利:
(隐藏了之前的所有操作按钮,只显示Win!提示和Restart!按钮,点击Win!不会发生任何动作,点击Restart!会重新加载场景,游戏可以重新进行,如前文所讲,没有烘培,光照变了)输掉比赛:
按钮设置与【胜利】类似
实现思路:
使用栈进行存储两个河岸的牧师和恶魔。比如A岸有3个牧师就push3个牧师进栈A,当牧师上传,则pop掉1个牧师,并把这个牧师的父对象设为船,位置变成相对位置后,能实现在船上跟着船移动。
实现代码:
FirstController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstController : MonoBehaviour, ISceneController, IUserAction {
SSDirector my;
public State state = State.BSTART;
public enum State { BSTART, BSEMOVING, BESMOVING, BEND, WIN, LOSE };
/* 借鉴别人的想法,进行枚举,列出一共6种状态,实际上也可以用123456表示,但这里可读性强。
* 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
*/
GameObject[] boat = new GameObject[2]; // 船的容量是2,并且每个容量都是一个对象,用来存储实际的对象
GameObject boat_obj; // 船本身
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>();
/*
使用栈进行存储两个河岸的牧师和恶魔。比如A岸有3个牧师就push3个牧师进栈A,
当牧师上传,则pop掉1个牧师,并把这个牧师的父对象设为船,位置变成相对位置
后,能实现在船上跟着船移动。
*/
float gap = 1f; // 牧师恶魔每个对象的间隔,不加f Unity编译不过
Vector3 priestStartPos = new Vector3(-8f, 0, 0);//以下均为预设位置
Vector3 priestEndPos = new Vector3(8f, 0, 0);
Vector3 devilStartPos = new Vector3(-5f, 0, 0);
Vector3 devilEndPos = new Vector3(5f, 0, 0);
Vector3 boatStartPos = new Vector3(-1.5f, 0, 0);
Vector3 boatEndPos = new Vector3(1.5f, 0, 0);
public float speed = 20; // 默认速度
void Awake() {
SSDirector director = SSDirector.getInstance();
director.currentSceneController = this; // 设置“场记”,和“导演”联系在一起
director.currentSceneController.LoadResources();
}
public void LoadResources () {
GameObject myGame = Instantiate<GameObject> (Resources.Load<GameObject> ("prefabs/main"),
Vector3.zero, Quaternion.identity);
myGame.name = "main";
//Debug.Log("load main...");
boat_obj = Instantiate(Resources.Load("prefabs/Boat"), new Vector3(-1.5f, 0f, 0f), Quaternion.identity) as GameObject;
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);
}
}
int boatCapacity() { // 检测船是否有容量
int capacity = 0;
for (int i = 0; i < 2; i++) {
if(boat[i] == null) capacity++;
}
return capacity;
}
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 + gap*i, pos.y, pos.z);
}
}
/*
这里下面是具体的动作规则实现,具体和我文档里规则表可以对的上
*/
public void priestOnBoat() { // 牧师上船,这个是没参数的,方便UserGUI调用。
if(priests_start.Count != 0 && boatCapacity() != 0 && this.state == State.BSTART) //船在左岸,有牧师在岸上,船有位置
priestOnBoat_(priests_start.Pop());
if(priests_end.Count != 0 && boatCapacity() != 0 && this.state == State.BEND)
priestOnBoat_(priests_end.Pop());
}
public void priestOnBoat_(GameObject obj) {
if (boatCapacity() != 0) {
obj.transform.parent = boat_obj.transform;
if (boat[0] == null) {
boat[0] = obj;
obj.transform.localPosition = new Vector3(-0.2f, 1.2f, 0f);
} else {
boat[1] = obj;
obj.transform.localPosition = new Vector3(0.2f, 1.2f, 0f);
}
}
}
public void priestOffBoat() {
for (int i = 0; i < 2; i++) {
if (boat[i] != null) {
if (this.state == State.BEND) {
if (boat[i].name == "Priest(Clone)") {
priests_end.Push(boat[i]);
boat[i].transform.parent = null;
boat[i] = null;
break;
}
}
else if (this.state == State.BSTART) {
if (boat[i].name == "Priest(Clone)") {
priests_start.Push(boat[i]);
boat[i].transform.parent = null;
boat[i] = null;
break;
}
}
}
}
}
public void devilOnBoat() {
if(devils_start.Count != 0 && boatCapacity() != 0 && this.state == State.BSTART)
devilOnBoat_(devils_start.Pop());
if(devils_end.Count != 0 && boatCapacity() != 0 && this.state == State.BEND)
devilOnBoat_(devils_end.Pop());
}
public void devilOnBoat_(GameObject obj) {
if (boatCapacity() != 0) {
obj.transform.parent = boat_obj.transform;
if (boat[0] == null) {
boat[0] = obj;
obj.transform.localPosition = new Vector3(-0.2f, 1.2f, 0f);
} else {
boat[1] = obj;
obj.transform.localPosition = new Vector3(0.2f, 1.2f, 0f);
}
}
}
public void devilOffBoat() {
for (int i = 0; i < 2; i++) {
if (boat[i] != null) {
if (this.state == State.BEND) {
if (boat[i].name == "Devil(Clone)") {
devils_end.Push(boat[i]);
boat[i].transform.parent = null;
boat[i] = null;
break;
}
}
else if (this.state == State.BSTART) {
if (boat[i].name == "Devil(Clone)") {
devils_start.Push(boat[i]);
boat[i].transform.parent = null;
boat[i] = null;
break;
}
}
}
}
}
public void moveBoat() {
if(boatCapacity() != 2) {
if(this.state == State.BSTART) {
this.state = State.BSEMOVING;
}
else if (this.state == State.BEND) {
this.state = State.BESMOVING;
}
}
}
public void restart() {
Application.LoadLevel (Application.loadedLevelName); // 重新加载
state = State.BSTART; // 预设参数
}
void check() { //检查状态是处于停靠还是运动或是胜利或是输掉
int priests_s = 0, devils_s = 0, priests_e = 0, devils_e = 0;
int pOnBoat = 0, dOnBoat = 0;
if (priests_end.Count == 3 && devils_end.Count == 3) {
this.state = State.WIN;
return;
}
for (int i = 0; i < 2; ++i) {
if (boat[i] != null && boat[i].name == "Priest(Clone)") pOnBoat++;
else if (boat[i] != null && boat[i].name == "Devil(Clone)") dOnBoat++;
}
//Debug.Log(pOnBoat);
if (this.state == State.BSTART) {
priests_s = priests_start.Count + pOnBoat;
devils_s = devils_start.Count + dOnBoat;
priests_e = priests_end.Count;
devils_e = devils_end.Count;
}
else if (this.state == State.BEND) {
priests_s = priests_start.Count;
devils_s = devils_start.Count;
priests_e = priests_end.Count + pOnBoat;
devils_e = devils_end.Count + dOnBoat;
}
if ((priests_s != 0 && priests_s < devils_s) || (priests_e != 0 && priests_e < devils_e)) {
this.state = State.LOSE;
}
}
public bool isWin() { //赢了的话
if(this.state == State.WIN) return true;
return false;
}
public bool isLose() { //输了的话
if(this.state == State.LOSE) return true;
return false;
}
void Start() {}
void Update() { //设置牧师和恶魔的位置,并且开船的时候实现船移动
setCharacterPositions(priests_start, priestStartPos);
setCharacterPositions(priests_end, priestEndPos);
setCharacterPositions(devils_start, devilStartPos);
setCharacterPositions(devils_end, devilEndPos);
if (this.state == State.BSEMOVING) {
boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatEndPos, speed*Time.deltaTime);
if (boat_obj.transform.position == boatEndPos) {
this.state = State.BEND;
}
}
else if (this.state == State.BESMOVING) {
boat_obj.transform.position = Vector3.MoveTowards(boat_obj.transform.position, boatStartPos, speed*Time.deltaTime);
if (boat_obj.transform.position == boatStartPos) {
this.state = State.BSTART;
}
} else {
check();
}
}
}
ISceneController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISceneController {
void LoadResources();
}
IUserAction.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IUserAction {
//void priestOnBoat(GameObject obj);
void priestOnBoat();
void priestOffBoat();
void devilOnBoat();
void devilOffBoat();
void moveBoat();
void restart();
bool isWin();
bool isLose();
}
SSDirector.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSDirector : System.Object {
private static SSDirector _instance;
public ISceneController currentSceneController {get; set;}
public static SSDirector getInstance() {
if (_instance == null) {
_instance = new SSDirector();
}
return _instance;
}
}
UserGUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UserGUI : MonoBehaviour {
private IUserAction action;
void Start () {
action = SSDirector.getInstance().currentSceneController as IUserAction;
}
// Update is called once per frame
void OnGUI () {
float width = Screen.width / 6;
float height = Screen.height / 12;
if(action.isWin()) { //赢了的话
GUI.Button(new Rect(0,Screen.height-3f*height, Screen.width, height), "Win!");
if(GUI.Button(new Rect(0,Screen.height-height, Screen.width, height), "Restart!")) {
action.restart();
}
} else if(action.isLose()) { //输了的话
GUI.Button(new Rect(0,Screen.height-3f*height, Screen.width, height), "Lose!");
if(GUI.Button(new Rect(0,Screen.height-height, Screen.width, height), "Restart!")) {
action.restart();
}
} else { //游戏还在进行中
if(GUI.Button(new Rect(0, 0, width, height), "PriestOnBoat")) {
action.priestOnBoat();
}
if(GUI.Button(new Rect(0+width, 0, width, height), "PriestOffBoat")) {
action.priestOffBoat();
}
if(GUI.Button(new Rect(0, 0+height, width, height), "DevilOnBoat")) {
action.devilOnBoat();
}
if(GUI.Button(new Rect(0+width, 0+height, width, height), "DevilOffBoat")) {
action.devilOffBoat();
}
if(GUI.Button(new Rect(0+2*width, 0, width, height), "GO")) {
action.moveBoat();
}
}
}
}