游戏角色:3个牧师、3个魔鬼
游戏场景:2个河岸、1艘小船,河
游戏的架构:
UserInterface 用来创建GUI对象接受玩家动作,处理GUI事件,使用 IUserActions 接口控制游戏。
GenGameObjec 作为场记用于载入资源,用来处理对象间通信和实现 IUserActions 接口和ISceneController接口
SSDirector 作为导演,含有场记对象及设置帧
首先我们先实现SSDirector
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISceneController
{
void LoadResources();
//void Pause ();
//void Resume ();
}
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;
}
}
void Awake()
//创建导演实例并载入资源
{
SSDirector director = SSDirector.getInstance();
director.setFPS(60);
director.currentScenceController = this;
director.currentScenceController.LoadResources();
}
public void LoadResources() //载入资源 { // shore Instantiate(Resources.Load("prefabs/begin"), shoreStartPos, Quaternion.identity); Instantiate(Resources.Load("prefabs/end"), shoreEndPos, Quaternion.identity); Instantiate(Resources.Load("prefabs/water"), waterPos, Quaternion.identity); Instantiate(Resources.Load("prefabs/water"), waterPos1, Quaternion.identity); // boat boat_obj = Instantiate(Resources.Load("prefabs/Capsule"), boatStartPos, Quaternion.identity) as GameObject; // priests & devils for (int i = 0; i < 3; ++i) { priests_start[i] = (Instantiate(Resources.Load("prefabs/Priest")) as GameObject); priests_end[i] = null; devils_start[i] = (Instantiate(Resources.Load("prefabs/Devil")) as GameObject); devils_end[i] = null; } }
begin为开始岸,end为结束岸,water为水,capsule为船,priest为牧师,devil为魔鬼。boat_obj为船的实体。priests_start[],priests_end[],devils_start[],devils_end[]分别是存储开始岸和结束岸的牧师对象,魔鬼对象。
在继续编写 GenGameObject 脚本之前,我们要明确游戏允许玩家的所有动作。这里,我规定的玩家动作有7个:
项目 | 条件 |
开船 | 船在开始岸、船在结束岸 |
船的左方下船 | 船靠岸且船左方有人 |
船的右方下船 | 船靠岸且船右方有人 |
开始岸的牧师上船 | 船在开始岸,船有空位,开始岸有牧师 |
开始岸的魔鬼上船 | 船在开始岸,船有空位,开始岸有魔鬼 |
结束岸的牧师上船 | 船在结束岸,船有空位,结束岸有牧师 |
结束岸的魔鬼上船 | 船在结束岸,船有空位,结束岸有魔鬼 |
考虑到牧师和魔鬼的位置时刻要变化,因此先定义一个 setCharacterPositions 函数。该函数接受一个array[]参数,和一个Vector3坐标。我说明下这里为什么不使用栈,因为栈只能按顺序的pop和push,而我想实现的是通过鼠标点击响应,可能有乱序,所以我这里用array[].
void setCharacterPositions(GameObject[] array, Vector3 pos)
//设置人物位置
{
for (int i = 0; i < 3; ++i)
{
if(array[i] != null)
array[i].transform.position = new Vector3(pos.x, pos.y, pos.z + gap * i);
}
}
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)
//上船
{
print(obj.name);
if (boatCapacity != 0)
{
if (boat_position == 0)
{
for (int i = 0; i < 3; ++i)
{
if (devils_start[i] == obj)
{
devils_start[i] = null;
find = 1;
}
if (priests_start[i] == obj)
{
priests_start[i] = null;
find = 1;
}
}
}
else if (boat_position == 1)
{
for (int i = 0; i < 3; ++i)
{
if (devils_end[i] == obj)
{
devils_end[i] = null;
find = 1;
}
if (priests_end[i] == obj)
{
priests_end[i] = null;
find = 1;
}
}
}
if(find == 1)
obj.transform.parent = boat_obj.transform;
if (boat[0] == null && find == 1)
{
boat[0] = obj;
boat[0].transform.tag = obj.transform.tag;
boatCapacity--;
obj.transform.localPosition = new Vector3(0, 1.2f, 0.19f);
}
else if(boat[1] == null && find == 1)
{
boat[1] = obj;
boat[1].transform.tag = obj.transform.tag;
boatCapacity--;
obj.transform.localPosition = new Vector3(0, 1.2f, -0.12f);
}
}
find = 0;
}
getOnTheBoat接受一个GameObject,为鼠标点击的物体。首先判断boatCapacity是否不为0,即船上有空位。然后判断船的位置,boat_position是0(在开始岸),或是1(在结束岸)。然后检查该物体是否在所在岸上的牧师或者魔鬼数组里,若找到find为1.若是1,将该gameobject挂在船的子对象里,然后判断boat[0]或者boat[1]是否为空(boat数组用来存储在船上的对象),找到合适的位置,装进去,并且设置boat[i]的tag(用于鼠标点击)。boatCapacity减1,设置其相对位置(设置位置是最TMTMTM麻烦的,不断微调,自己去体验下吧)。
void Boatmoving(GameObject obj)
//船的移动
{
if(boat_position == 1)
{
boat_position = 0;
while (obj.transform.position != boatStartPos)
obj.transform.position = Vector3.MoveTowards(obj.transform.position, boatStartPos, 1);
}
else if(boat_position == 0)
{
boat_position = 1;
while (obj.transform.position != boatEndPos)
obj.transform.position = Vector3.MoveTowards(obj.transform.position, boatEndPos, 1);
}
}
void getOffTheBoat(int side)
//下船
{
if (boat[side] != null)
{
boat[side].transform.parent = null;
if (boat_position == 1)
{
print(side);
if (boat[side].transform.tag == "Priest")
{
for(int i = 0; i < 3; i++)
{
if(priests_end[i] == null)
{
priests_end[i] = boat[side];
boatCapacity++;
break;
}
}
}
else if (boat[side].transform.tag == "Devil")
{
for (int i = 0; i < 3; i++)
{
if (devils_end[i] == null)
{
devils_end[i] = boat[side];
boatCapacity++;
break;
}
}
}
}
else if (boat_position == 0)
{
if (boat[side].transform.tag == "Priest")
{
for (int i = 0; i < 3; i++)
{
if (priests_start[i] == null)
{
priests_start[i] = boat[side];
boatCapacity++;
break;
}
}
}
else if (boat[side].transform.tag == "Devil")
{
for (int i = 0; i < 3; i++)
{
if (devils_start[i] == null)
{
devils_start[i] = boat[side];
boatCapacity++;
break;
}
}
}
}
boat[side] = null;
}
check();
}
传入参数side,指明是在船上的哪一边。判断如果船上的那一边对象为非空,首先取消父子关系。判断船的位置和对象的tag压入数组里面的非空的位置,boat[side]置空,然后检查游戏是否结束了。
除此以外,还需要判断游戏的输赢,定义一个 check 函数:
void check()
//检查游戏是否结束
{
int priests_s = 0, devils_s = 0, priests_e = 0, devils_e = 0;
for(int i = 0; i < 3; i++)
{
if(priests_start[i] != null)
{
priests_s++;
}
if(devils_start[i] != null)
{
devils_s++;
}
if(priests_end[i] != null)
{
priests_e++;
}
if(devils_end[i] != null)
{
devils_e++;
}
}
if(((priests_s < devils_s) && (priests_s != 0))||((priests_e < devils_e) && (priests_e != 0)))
{
print("you lose");
game = 1;
}
else if (priests_s == 0 && devils_s == 0)
{
print("you win!!!");
game = 2;
}
}
if (Input.GetMouseButtonDown(0) && game == 0)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.transform.tag == "Devil" || hit.transform.tag == "Priest")
{
if(hit.collider.gameObject == boat[0] || hit.collider.gameObject == boat[1])
{
if(hit.collider.gameObject == boat[0])
getOffTheBoat(0);
else getOffTheBoat(1);
}
else
{
print(hit.transform.tag);
getOnTheBoat(hit.collider.gameObject);
}
}
else if (hit.transform.tag == "Boat" && boatCapacity != 2)
{
print(hit.transform.tag);
Boatmoving(hit.collider.gameObject);
check();
}
}
}
Input.GetMouseButtonDown(0)是响应鼠标左键按下,game = 0表示游戏正在进行中,game = 1表示输,game = 2表示赢,game = 3表示暂停。鼠标点击是通过从鼠标位置引一条射线,如果遇到碰撞框即可获取该物体。判断hit.transform.tag == Devil 或者是 Priest 然后判断是否是在船上,在船上就下船,不在船上就下船(这里有一个小小的bug,假如你点击了对岸的devil或者是priest呢?难道跳上船?)如果hit.transform.tag == boat且船上有人就移动船,并且检查输赢。到此基本完成场景控制。
public interface IUserAction
{
void Restart();
void ShowDetail();
void Pause();
}
private IUserAction action;
// Use this for initialization
void Start () {
action = SSDirector.getInstance().currentScenceController as IUserAction;
}
void OnGUI() {
GUIStyle fontstyle1 = new GUIStyle();
fontstyle1.fontSize = 50;
fontstyle1.normal.textColor = new Color(255, 255, 255);
if (GUI.Button(new Rect(0, 80, 80, 60), "RESTART"))
{
action.Restart();
}
if (GUI.RepeatButton(new Rect(0, 0, 120, 60), "Priests and Devils"))
{
action.ShowDetail();
}
if(GUI.Button(new Rect(0, 160, 80, 60), "Pause"))
{
action.Pause();
}
}
public void Restart()
{
SceneManager.LoadScene("task2");
}
public void ShowDetail()
{
GUI.Label(new Rect(220, 20, 350, 250), "Priests and Devils is a puzzle game in which you will help the Priests and Devils to cross the river. 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 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!");
}
public void Pause()
{
if (game == 0)
{
game = 3;
}
else if(game == 3)
{
game = 0;
}
}
private void OnGUI()
{
GUIStyle fontstyle1 = new GUIStyle();
fontstyle1.fontSize = 50;
fontstyle1.normal.textColor = new Color(255, 255, 255);
if (game == 1)
{
GUI.Label(new Rect(260, 180, 100, 100), "YOU LOSE!!!", fontstyle1);
}
else if(game == 2)
{
GUI.Label(new Rect(260, 180, 100, 100), "YOU WIN!!!", fontstyle1);
}
}
自此游戏完成,虽然已经完成,但是感觉架构还是不很清晰,等待慢慢学习,慢慢理解。感觉还是有一段很长的路要走。欢迎大家回复提建议帮我改进。
有兴趣的同学请去GitHub看,这是传送门