原游戏Priests and Devils传送门,有兴趣的可以去玩下:
http://www.flash-game.net/game/2535/priests-and-devils.html
如果需要,可以按如下步骤先设置好预设后,再把文章最后的代码复制进脚本,即可运行察看游戏效果,所用unity版本为5.5
|
|
|
|
|
|
|
|
采用MVC结构,单实例模式,类的UML图如下
各类说明
各类具体代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum State { WIN, LOSE, PAUSE, CONTINUE , START };
public class SSDirector : System.Object
{
//计时器
public int totalSeconds = 60;
public int leaveSeconds;
public bool onCountDown = false;
public string countDownTitle = "Start";
public State state { get; set; }
public 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;
}
public void NextScene()
{
Debug.Log("抱歉,没下一个场景了");
}
public IEnumerator DoCountDown()
{
while (leaveSeconds > 0)
{
yield return new WaitForSeconds(1f);
leaveSeconds--;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
* 玩家动作接口,鼠标点击是唯一动作
*/
public interface IUserAction
{
void clickOne();
}
public class UserGUI : MonoBehaviour
{
private IUserAction action;
float width, height;
void Start()
{
action = SSDirector.getInstance().currentScenceController as IUserAction;
}
float castw(float scale)
{
return (Screen.width - width) / scale;
}
float casth(float scale)
{
return (Screen.height - height) / scale;
}
void OnGUI()
{
width = Screen.width / 12;
height = Screen.height / 12;
GUI.Label(new Rect(castw(2f)+20, casth(6f) - 20, 50, 50), SSDirector.getInstance().leaveSeconds.ToString());// 倒计时秒数 //
if (SSDirector.getInstance().state != State.WIN && SSDirector.getInstance().state != State.LOSE
&& GUI.Button(new Rect(10, 10, 80, 30), SSDirector.getInstance().countDownTitle))
{
if (SSDirector.getInstance().countDownTitle == "Start")
{
SSDirector.getInstance().currentScenceController.Resume();
SSDirector.getInstance().countDownTitle = "Pause";
SSDirector.getInstance().onCountDown = true;
StartCoroutine(SSDirector.getInstance().DoCountDown());
}
else
{
SSDirector.getInstance().currentScenceController.Pause();
SSDirector.getInstance().countDownTitle = "Start";
SSDirector.getInstance().onCountDown = false;
StopAllCoroutines();
}
}
if (SSDirector.getInstance().state == State.WIN)//胜利
{
StopAllCoroutines();
if (GUI.Button(new Rect(castw(2f), casth(6f), Screen.width / 8, height), "Win!"))
{
SSDirector.getInstance().currentScenceController.Restart();
}
}
else if (SSDirector.getInstance().state == State.LOSE)//失败
{
StopAllCoroutines();
if (GUI.Button(new Rect(castw(2f), casth(6f), Screen.width / 7, height), "Lose!"))
{
SSDirector.getInstance().currentScenceController.Restart();
}
}
}
/**
* 检测用户的点击动作
*/
void Update()
{
action.clickOne();
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
* 场记的接口,每个场记都要实现
* 加载场景资源、场景的暂停、恢复和重新布置
*/
public interface ISceneController
{
void LoadResources();
void Pause();
void Resume();
void Restart();
}
public class FirstSceneController : MonoBehaviour, IUserAction, ISceneController {
public FirstSSActionManager actionManager;
List LeftObjList = new List();//存储在左岸的对象
List RightObjList = new List();//存储在右岸的对象
GameObject[] boat = new GameObject[2];
GameObject boat_obj, leftShore_obj, rightShore_obj;
Vector3 LeftShorePos = new Vector3(-12, 0, 0);
Vector3 RightShorePos = new Vector3(12, 0, 0);
Vector3 BoatLeftPos = new Vector3(-4, 0, 0);
Vector3 BoatRightPos = new Vector3(4, 0, 0);
void Awake()
{
SSDirector director = SSDirector.getInstance();
director.setFPS(60);
director.currentScenceController = this;
director.currentScenceController.LoadResources();
director.leaveSeconds = director.totalSeconds;
}
void Start () {
SSDirector.getInstance().state = State.PAUSE;
SSDirector.getInstance().countDownTitle = "Start";
actionManager = GetComponent() as FirstSSActionManager;
}
void Update()
{
Judge();
}
public void LoadResources()
{
GameObject priest_obj, devil_obj;
Camera.main.transform.position = new Vector3(0, 0, -20);
/**
* 加载两岸
* 加载船
*/
leftShore_obj = Instantiate(Resources.Load("Prefabs/Shore"), LeftShorePos, Quaternion.identity) as GameObject;
rightShore_obj = Instantiate(Resources.Load("Prefabs/Shore"), RightShorePos, Quaternion.identity) as GameObject;
leftShore_obj.name = "left_shore";
rightShore_obj.name = "right_shore";
boat_obj = Instantiate(Resources.Load("Prefabs/Boat"), BoatLeftPos, Quaternion.identity) as GameObject;
boat_obj.name = "boat";
/**
* 一开始牧师、魔鬼、船都在左岸,注意:我是通过判断父对象决定物体在两岸还是船上的
*/
boat_obj.transform.parent = leftShore_obj.transform;
/**
* 把牧师与恶魔摆在左岸初始位置
*/
for (int i = 0; i < 3; ++i)
{
priest_obj = Instantiate(Resources.Load("Prefabs/Priest")) as GameObject;
priest_obj.name = i.ToString();//牧师编号0 1 2
priest_obj.transform.position = new Vector3(-16f + 1.5f * Convert.ToInt32(priest_obj.name), 2.7f, 0);
priest_obj.transform.parent = leftShore_obj.transform;
LeftObjList.Add(priest_obj);
devil_obj = Instantiate(Resources.Load("Prefabs/Devil")) as GameObject;
devil_obj.name = (i + 3).ToString();//魔鬼编号3 4 5
devil_obj.transform.position = new Vector3(-16f + 1.5f * Convert.ToInt32(devil_obj.name), 2.7f, 0);
devil_obj.transform.parent = leftShore_obj.transform;
LeftObjList.Add(devil_obj);
}
}
/**
* 实现用户动作接口中的点击对象函数
* 利用射线获取对象
* 对象名字 0—2代表牧师
* 3—5代表魔鬼
* boat代表船
* 若对象名字为其他,或者没点中对象则不处理
*/
public void clickOne()
{
GameObject gameObj = null;
if (Input.GetMouseButtonDown(0) &&
(SSDirector.getInstance().state == State.START || SSDirector.getInstance().state == State.CONTINUE))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
gameObj = hit.transform.gameObject;
}
}
if (gameObj == null) return;
else if (gameObj.name == "0" || gameObj.name == "1" || gameObj.name == "2"
|| gameObj.name == "3" || gameObj.name == "4" || gameObj.name == "5")
{
MovePeople(gameObj);
}
else if(gameObj.name == "boat")
{
MoveBoat();
}
}
/**
* 人的移动分四种情况
* 上船:左岸上左船,右岸上右船
* 上岸:左船上左岸、右船上右岸
* 其他情况人都不会动
*/
void MovePeople(GameObject people)
{
int shoreNum, seatNum;//0为左,1为右,作为参数传给动作管理员
if (people.transform.parent == boat_obj.transform.parent && (boat[0] == null || boat[1] == null))//物体和船都在同一个岸且船有空位才能上船
{
seatNum = boat[0] == null ? 0 : 1;
if (people.transform.parent == leftShore_obj.transform)
{
shoreNum = 0;
for (int i = 0; i < LeftObjList.Count; i++)
{
if (people.name == LeftObjList[i].name)
{
actionManager.getOnBoat(people, shoreNum, seatNum);//让动作管理员执行上船动作
LeftObjList.Remove(LeftObjList[i]);
}
}
}
else
{
shoreNum = 1;
for (int i = 0; i < RightObjList.Count; i++)
{
if (people.name == RightObjList[i].name)
{
actionManager.getOnBoat(people, shoreNum, seatNum);
RightObjList.Remove(RightObjList[i]);
}
}
}
boat[seatNum] = people;
people.transform.parent = boat_obj.transform;
}
else if (people.transform.parent == boat_obj.transform)//若物体在船上就选择上岸
{
shoreNum = boat_obj.transform.parent == leftShore_obj.transform ? 0 : 1;
seatNum = (boat[0] != null && boat[0].name == people.name) ? 0 : 1;
actionManager.getOffBoat(people, shoreNum);//动作管理员去执行上岸动作
boat[seatNum] = null;
if(shoreNum == 0)
{
people.transform.parent = leftShore_obj.transform;
LeftObjList.Add(people);
}
else
{
people.transform.parent = rightShore_obj.transform;
RightObjList.Add(people);
}
}
}
/**
* 若船上有人,则船可驶向对岸
* 让动作管理器去负责移动小船
*/
void MoveBoat()
{
if(boat[0]!=null || boat[1] != null)
{
actionManager.moveBoat(boat_obj);
boat_obj.transform.parent = boat_obj.transform.parent == leftShore_obj.transform ?
rightShore_obj.transform : leftShore_obj.transform;
}
}
/**
* 判断游戏输赢状态并告知导演
* 计算分三步,先统计左岸的牧师、恶魔
* 然后统计右岸的牧师、恶魔
* 再判断船在左岸还是右岸,把船上恶魔与牧师加到对应岸的数量上
* 若某一岸恶魔 > 牧师,则游戏失败
* 若全到右岸,则胜利
* 否则游戏继续
*/
public void Judge()
{
int left_d = 0, left_p = 0, right_d = 0, right_p = 0;
foreach (GameObject element in LeftObjList)
{
if (element.tag == "Priest") left_p++;
if (element.tag == "Devil") left_d++;
}
foreach (GameObject element in RightObjList)
{
if (element.tag == "Priest") right_p++;
if (element.tag == "Devil") right_d++;
}
for (int i = 0; i < 2; i++)
{
if (boat[i] != null && boat_obj.transform.parent == leftShore_obj.transform)//船在左岸
{
if (boat[i].tag == "Priest") left_p++;
else left_d++;
}
if (boat[i] != null && boat_obj.transform.parent == rightShore_obj.transform)//船在右岸
{
if (boat[i].tag == "Priest") right_p++;
else right_d++;
}
}
if ((left_d > left_p && left_p != 0) || (right_d > right_p && right_p != 0) || SSDirector.getInstance().leaveSeconds == 0)
{
SSDirector.getInstance().state = State.LOSE;
}
else if (right_d == right_p && right_d == 3)//全过河,赢了
{
SSDirector.getInstance().state = State.WIN;
}
}
public void Pause()
{
SSDirector.getInstance().state = State.PAUSE;
}
public void Resume()
{
SSDirector.getInstance().state = State.CONTINUE;
}
public void Restart()
{
Application.LoadLevel(Application.loadedLevelName);
SSDirector.getInstance().state = State.START;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISSActionCallback
{
void actionDone(SSAction source);
}
/**
* SSAction是动作的基类,保存要移动的游戏对象属性
*/
public class SSAction : ScriptableObject
{
public bool enable = true;
public bool destroy = false;
public GameObject gameObject { get; set; }
public Transform transform { get; set; }
public ISSActionCallback callback { get; set; }
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
/**
* MoveToAction是SSAction的一个子类,代表平移的动作。
*/
public class MoveToAction : SSAction
{
public Vector3 target;
public float speed;
private MoveToAction() { }
public static MoveToAction getAction(Vector3 target, float speed)
{
MoveToAction action = ScriptableObject.CreateInstance();
action.target = target;
action.speed = speed;
return action;
}
public override void Update()
{
this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
if (this.transform.position == target)
{
this.destroy = true;
this.callback.actionDone(this);
}
}
public override void Start() {}
}
/**
* SequenceAction是SSAction的另一个子类,它代表一系列组合动作。
*/
public class SequenceAction : SSAction, ISSActionCallback
{
public List sequence;
public int repeat = -1; //-1表示无限循环,0表示只执行一遍,repeat> 0 表示重复repeat遍
public int currentAction = 0;//当前动作列表里,执行到的动作序号
public static SequenceAction getAction(int repeat, int currentActionIndex, List sequence)
{
SequenceAction action = ScriptableObject.CreateInstance();
action.sequence = sequence;
action.repeat = repeat;
action.currentAction = currentActionIndex;
return action;
}
public override void Update()
{
if (sequence.Count == 0) return;
if (currentAction < sequence.Count)
{
sequence[currentAction].Update();
}
}
public void actionDone(SSAction source)
{
source.destroy = false;
this.currentAction++;
if (this.currentAction >= sequence.Count)
{
this.currentAction = 0;
if (repeat > 0) repeat--;
if (repeat == 0)
{
this.destroy = true;
this.callback.actionDone(this);
}
}
}
public override void Start()
{
foreach (SSAction action in sequence)
{
action.gameObject = this.gameObject;
action.transform = this.transform;
action.callback = this;
action.Start();
}
}
void OnDestroy()
{
foreach (SSAction action in sequence)
{
DestroyObject(action);
}
}
}
/**
* SSActionManager统筹上面三个动作类
*/
public class SSActionManager : MonoBehaviour
{
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
private List waitingToAdd = new List();
private List<int> watingToDelete = new List<int>();
protected void Update()
{
foreach (SSAction ac in waitingToAdd)
{
actions[ac.GetInstanceID()] = ac;
}
waitingToAdd.Clear();
foreach (KeyValuePair<int, SSAction> kv in actions)
{
SSAction ac = kv.Value;
if (ac.destroy)
{
watingToDelete.Add(ac.GetInstanceID());
}
else if (ac.enable)
{
ac.Update();
}
}
foreach (int key in watingToDelete)
{
SSAction ac = actions[key];
actions.Remove(key);
DestroyObject(ac);
}
watingToDelete.Clear();
}
public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback whoToNotify)
{
action.gameObject = gameObject;
action.transform = gameObject.transform;
action.callback = whoToNotify;
waitingToAdd.Add(action);
action.Start();
}
}
/**
* 专门设计动作并执行
*/
public class FirstSSActionManager : SSActionManager, ISSActionCallback
{
public FirstSceneController scene;
public MoveToAction action1, action2;
public SequenceAction saction;
float speed = 30f;
/**
* 为小船设置水平移动动作
*/
public void moveBoat(GameObject boat)
{
action1 = MoveToAction.getAction((boat.transform.position == new Vector3(4, 0, 0)? new Vector3(-4, 0, 0) : new Vector3(4, 0, 0)), speed);
this.RunAction(boat, action1, this);
}
/**
* 人的上船动作
* 为人设置水平和垂直两个分解动作,然后组合起来执行
*/
public void getOnBoat(GameObject people, int shore, int seat)
{
if (shore == 0 && seat == 0)
{
action1 = MoveToAction.getAction(new Vector3(-5f, 2.7f, 0), speed);//右移
action2 = MoveToAction.getAction(new Vector3(-5f, 1.2f, 0), speed);//下移
}
else if(shore==0 && seat == 1)
{
action1 = MoveToAction.getAction(new Vector3(-3f, 2.7f, 0), speed);
action2 = MoveToAction.getAction(new Vector3(-3f, 1.2f, 0), speed);
}
else if (shore == 1 && seat == 0)
{
action1 = MoveToAction.getAction(new Vector3(3f, 2.7f, 0), speed);
action2 = MoveToAction.getAction(new Vector3(3f, 1.2f, 0), speed);
}
else if (shore == 1 && seat == 1)
{
action1 = MoveToAction.getAction(new Vector3(5f, 2.7f, 0), speed);
action2 = MoveToAction.getAction(new Vector3(5f, 1.2f, 0), speed);
}
SequenceAction saction = SequenceAction.getAction(0, 0, new List { action1, action2 });//组合动作
this.RunAction(people, saction, this);
}
/**
* 人的下船动作
*/
public void getOffBoat(GameObject people, int shoreNum)
{
action1 = MoveToAction.getAction(new Vector3(people.transform.position.x, 2.7f, 0), speed);//上移
if (shoreNum == 0) action2 = MoveToAction.getAction(new Vector3(-16f + 1.5f * Convert.ToInt32(people.name), 2.7f, 0), speed);//左移
else action2 = MoveToAction.getAction(new Vector3(16f - 1.5f * Convert.ToInt32(people.name), 2.7f, 0), speed);//右移
SequenceAction saction = SequenceAction.getAction(0, 0, new List < SSAction >{ action1, action2});//组合动作
this.RunAction(people, saction, this);
}
protected void Start()
{
scene = (FirstSceneController)SSDirector.getInstance().currentScenceController;
scene.actionManager = this;
}
protected new void Update()
{
base.Update();
}
public void actionDone(SSAction source)
{
Debug.Log("Done");
}
}
对类之间的操作还不熟练,自己设计得很粗糙,有出错的地方请大家指正TAT。