Unty3D - 2 牧師與魔鬼
遊戲規則:
- 幫助3個牧師(方塊)和3個魔鬼(圓球)渡河。
- 船上最多能夠載2名遊戲角色。
- 船上有遊戲對象時才能夠移動。
- 當有一側岸的魔鬼數多餘牧師數時(包括船上的魔鬼和牧師),魔鬼就會失去控制,吃掉牧師(若是這一側沒有牧師則不會失敗),遊戲失敗。
- 當全部遊戲角色都到達對岸時,遊戲勝利。
玩家動做 |
發生條件 |
上船 |
船上有空位且點擊想上船的對象 |
下船 |
船上有對象且點擊想下船的對象 |
開船 |
點擊船 |
從新開始 |
點擊restart按鈕 |
遊戲截圖:



遊戲組織結構:
* 導演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;
}
}
* 接口命名空間namespace Interfaces:
因爲MVC模式的要求,爲了下降耦合,必須將須要提供給用戶界面的接口API暴露出來,寫在同一個命名空間裏便於管理:
namespace Interfaces
{
public interface ISceneController
{
void LoadResources();
}
public interface UserAction
{
void MoveBoat();
void ObjectIsClicked(GameObjects characterCtrl);
void Restart();
}
}
* 場記FirstController:
場記FirstController負責實例化接口ISceneController和UserAction,實現加載資源和響應用戶操做:
public void LoadResources()
{
fromCoast = new CoastController("from");
toCoast = new CoastController("to");
boat = new BoatController();
GameObject water = Instantiate(Resources.Load("Perfabs/Water", typeof(GameObject)), new Vector3(0, 0.5F, 0), Quaternion.identity, null) as GameObject;
water.name = "water";
for (int i = 0; i < 3; i++)
{
GameObjects s = new GameObjects("priest");
s.setName("priest" + i);
s.setPosition(fromCoast.getEmptyPosition());
s.getOnCoast(fromCoast);
fromCoast.getOnCoast(s);
GameObjects[i] = s;
}
for (int i = 0; i < 3; i++)
{
GameObjects s = new GameObjects("devil");
s.setName("devil" + i);
s.setPosition(fromCoast.getEmptyPosition());
s.getOnCoast(fromCoast);
fromCoast.getOnCoast(s);
GameObjects[i + 3] = s;
}
}
public void ObjectIsClicked(GameObjects Objects)
{
if (Objects.isOnBoat())
{
CoastController whichCoast;
if (boat.get_State() == -1)
{ // to->-1; from->1
whichCoast = toCoast;
}
else
{
whichCoast = fromCoast;
}
boat.GetOffBoat(Objects.getName());
Objects.moveToPosition(whichCoast.getEmptyPosition());
Objects.getOnCoast(whichCoast);
whichCoast.getOnCoast(Objects);
}
else
{
CoastController whichCoast = Objects.getCoastController(); // obejects on coast
if (boat.getEmptyIndex() == -1)
{
return;
}
if (whichCoast.get_State() != boat.get_State()) // boat is not on the side of character
return;
whichCoast.getOffCoast(Objects.getName());
Objects.moveToPosition(boat.getEmptyPosition());
Objects.getOnBoat(boat);
boat.GetOnBoat(Objects);
}
UserGUI.SetState = Check();
}
- 判斷遊戲處在的狀態:(繼續進行,遊戲勝利,遊戲結束)
int Check()
{ // 0->not finish, 1->lose, 2->win
int 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) // win
return 2;
int[] boatCount = boat.GetobjectsNumber();
if (boat.get_State() == -1)
{ // boat at toCoast
to_priest += boatCount[0];
to_devil += boatCount[1];
}
else
{ // boat at fromCoast
from_priest += boatCount[0];
from_devil += boatCount[1];
}
if (from_priest < from_devil && from_priest > 0)
{ // lose
return 1;
}
if (to_priest < to_devil && to_priest > 0)
{
return 1;
}
return 0; // not finish
}
* 可移動對象的移動控制:
- 移動方法在可移動對象建立的時候會經過Instance.AddComponent(typeof(Move))函數添加到對象中,Move類做爲一個專門的類來控制對象移動,經過SetDestination()函數直接完成移動的設定,提供了移動的簡單方法:
public class Move : MonoBehaviour
{
readonly float Speed = 20;
Vector3 Target;
Vector3 Middle;
int Move_State = 0; // 0->no need to move, 1->object moving , 2->boat moving to dest
bool To_Middle = true;
void Update()
{
if (Move_State == 1)
{
if (To_Middle)
{
transform.position = Vector3.MoveTowards(transform.position, Middle, Speed * Time.deltaTime);
if (transform.position == Middle) To_Middle = false;
}
else
{
transform.position = Vector3.MoveTowards(transform.position, Target, Speed * Time.deltaTime);
if (transform.position == Target) Move_State = 0;
}
}
else if (Move_State == 2)
{
transform.position = Vector3.MoveTowards(transform.position, Target, Speed * Time.deltaTime);
if (transform.position == Target)
{
To_Middle = true;
Move_State = 0;
}
}
}
public void SetDestination(Vector3 Position)
{
if (Move_State != 0) return;
Target = Middle = Position;
To_Middle = true;
if (transform.position.y == Target.y)
{
Move_State = 2;
}
else
{
Move_State = 1;
if (transform.position.y < Target.y)
{
Middle.x = transform.position.x;
}
else if (transform.position.y > Target.y)
{
Middle.y = transform.position.y;
}
}
}
public void Reset()
{
Move_State = 0;
To_Middle = true;
}
}
* 遊戲對象(牧師,魔鬼)的初始化以及提供給其餘類使用的一系列方法:
public class GameObjects
{
readonly GameObject Instance;
readonly Move Move;
readonly ClickGUI clickGUI;
readonly int characterType; // 0->priest, 1->devil
bool _isOnBoat = false;
CoastController coastController;
public GameObjects(string Type)
{
if (Type == "priest")
{
Instance = Object.Instantiate(Resources.Load("Perfabs/Priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
characterType = 0;
}
else
{
Instance = Object.Instantiate(Resources.Load("Perfabs/Devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
characterType = 1;
}
Move = Instance.AddComponent(typeof(Move)) as Move;
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 void moveToPosition(Vector3 destination)
{
Move.SetDestination(destination);
}
public int getType()
{ // 0->priest, 1->devil
return characterType;
}
public string getName()
{
return Instance.name;
}
public void getOnBoat(BoatController boatCtrl)
{
coastController = null;
Instance.transform.parent = boatCtrl.getGameobj().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 void reset()
{
Move.Reset();
coastController = (SSDirector.getInstance().currentScenceController as FirstController).fromCoast;
getOnCoast(coastController);
setPosition(coastController.getEmptyPosition());
coastController.getOnCoast(this);
}
}
* 河岸有兩類,一類是From也就是出發的地方,另外一類是To也就是目標位置。建立了幾個空位置用來存儲對象以及判斷是否有對象已經把該位置給佔了,提供方法給實例用來判斷是否有空位置以及空位置的三維座標:
public class CoastController
{
readonly GameObject coast;
readonly Vector3 from_pos = new Vector3(9, 1, 0);
readonly Vector3 to_pos = new Vector3(-9, 1, 0);
readonly Vector3[] positions;
readonly int State; // to->-1, from->1
GameObjects[] passengerPlaner;
public CoastController(string _State)
{
positions = new Vector3[] {new Vector3(6.5F,2.25F,0), new Vector3(7.5F,2.25F,0), new Vector3(8.5F,2.25F,0),
new Vector3(9.5F,2.25F,0), new Vector3(10.5F,2.25F,0), new Vector3(11.5F,2.25F,0)};
passengerPlaner = new GameObjects[6];
if (_State == "from")
{
coast = Object.Instantiate(Resources.Load("Perfabs/Ston", typeof(GameObject)), from_pos, Quaternion.identity, null) as GameObject;
coast.name = "from";
State = 1;
}
else
{
coast = Object.Instantiate(Resources.Load("Perfabs/Ston", 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(GameObjects ObjectControl)
{
int index = getEmptyIndex();
passengerPlaner[index] = ObjectControl;
}
public GameObjects getOffCoast(string passenger_name)
{ // 0->priest, 1->devil
for (int i = 0; i < passengerPlaner.Length; i++)
{
if (passengerPlaner[i] != null && passengerPlaner[i].getName() == passenger_name)
{
GameObjects charactorCtrl = passengerPlaner[i];
passengerPlaner[i] = null;
return charactorCtrl;
}
}
Debug.Log("cant find passenger on coast: " + passenger_name);
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->devil
count[0]++;
}
else
{
count[1]++;
}
}
return count;
}
public void reset()
{
passengerPlaner = new GameObjects[6];
}
}
* 最後一個就是船的控制函數了,其實和河岸以及遊戲對象的性質都很像,只不過是既有河岸的位置特性也有遊戲對象的移動屬性,按照需求寫出相應的判斷便可:
public class BoatController
{
readonly GameObject boat;
readonly Move Moving;
readonly Vector3 fromPosition = new Vector3(5, 1, 0);
readonly Vector3 toPosition = new Vector3(-5, 1, 0);
readonly Vector3[] from_positions;
readonly Vector3[] to_positions;
int State; // to->-1; from->1
GameObjects[] passenger = new GameObjects[2];
public BoatController()
{
State = 1;
from_positions = new Vector3[] { new Vector3(4.5F, 1.5F, 0), new Vector3(5.5F, 1.5F, 0) };
to_positions = new Vector3[] { new Vector3(-5.5F, 1.5F, 0), new Vector3(-4.5F, 1.5F, 0) };
boat = Object.Instantiate(Resources.Load("Perfabs/Boat", typeof(GameObject)), fromPosition, Quaternion.identity, null) as GameObject;
boat.name = "boat";
Moving = boat.AddComponent(typeof(Move)) as Move;
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(GameObjects ObjectControl)
{
int index = getEmptyIndex();
passenger[index] = ObjectControl;
}
public GameObjects GetOffBoat(string passenger_name)
{
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] != null && passenger[i].getName() == passenger_name)
{
GameObjects charactorCtrl = passenger[i];
passenger[i] = null;
return charactorCtrl;
}
}
Debug.Log("Cant find passenger in boat: " + passenger_name);
return null;
}
public GameObject getGameobj()
{
return boat;
}
public int get_State()
{ // to->-1; from->1
return State;
}
public int[] GetobjectsNumber()
{
int[] count = { 0, 0 };// [0]->priest, [1]->devil
for (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 void Move()
{
if (State == -1)
{
Moving.SetDestination(fromPosition);
State = 1;
}
else
{
Moving.SetDestination(toPosition);
State = -1;
}
}
public void reset()
{
Moving.Reset();
if (State == -1)
{
Move();
}
passenger = new GameObjects[2];
}
}
* GUI分爲直接與用戶進行界面交互的InteracteGUI和響應用戶點擊遊戲對象事件的ClickGUI,而且使用UserAction接口來控制遊戲:
public class InteracteGUI : MonoBehaviour {
UserAction UserAcotionController;
public int SetState { get { return GameState; } set { GameState = value; } }
static int GameState = 0;
// Use this for initialization
void Start () {
UserAcotionController = SSDirector.getInstance().currentScenceController as UserAction;
}
private void OnGUI()
{
if (GameState == 1)
{
GUI.Label(new Rect(Screen.width / 2 -30, Screen.height / 2 - 30, 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.Label(new Rect(Screen.width / 2 - 30, Screen.height / 2 - 30 , 100, 50), "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;
GameObjects GameObjectsInScene;
public void setController(GameObjects characterCtrl)
{
GameObjectsInScene = characterCtrl;
}
void Start()
{
UserAcotionController = SSDirector.getInstance().currentScenceController as UserAction;
}
void OnMouseDown()
{
if (gameObject.name == "boat")
{
UserAcotionController.MoveBoat();
}
else
{
UserAcotionController.ObjectIsClicked(GameObjectsInScene);
}
}
}