前言
这是中山大学软件工程学院2023年3D游戏编程与设计的第五次作业,也欢迎大家学习参考交流
github个人主页: innitinnit (github.com)
目录
博客内容概述
本次作业要求在上一次作业的基础上使用集成 CCAction的方法,使得动作管理从场景控制器中分离并利用接口消息传递机制,设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束,实现游戏结束判定从场景控制器中分离 。
上一次作业的链接:使用unity实现《魔鬼与牧师》小游戏-CSDN博客
游戏内容简述
游戏目标:将3个牧师和3个魔鬼从河的一端安全地送到河的另一端。
游戏规则:在运送过程中,船可以搭载两个人,而且必须有一人掌船。
游戏结果:无论何时,只要河一边的魔鬼数量多于牧师的数量,游戏就会以失败结束。当三个牧师与魔鬼都平安到达河的另一端时胜利。
游戏对象的行为
角色:上下船、划船
魔鬼:当两岸任意一边人数多于牧师时杀死牧师,游戏结束
船:有人时可以动
实现逻辑与代码区别
其改变部分主要在于改变了controller中move的调用方式:
public void moveCharacters(dpGame.CharacterController chr)
{
if (u.status == 0) return;
if (forbidEvent()) return;
if (chr.getState() == 1)
{
BankController bank;
if (boat.getLR() == 0)
{
bank = leftBank;
}
else
{
bank = rightBank;
}
boat.outOfBoat(chr.getTag());
chr.moveToBank(bank);
//原版
//chr.goMoving(bank.getPos(chr.getTag()));
//bank.moveToBank(chr);
// 动作分离版,改变了move的调用方式
actionManager.moveCharacter(chr.getObj(), bank.getPos(chr.getTag()), speed);
bank.moveToBank(chr);
}
else
{
BankController bank = chr.getBank();
if (boat.getLR() == bank.getLR())
{
if (!boat.isFull())
{
bank.outOfBank(chr.getTag());
chr.moveToBoat(boat);
//原版
//chr.goMoving(boat.getSeat());
//boat.moveToBoat(chr);
// 动作分离版,改变了move的调用方式
actionManager.moveCharacter(chr.getObj(), boat.getSeat(), speed);
boat.moveToBoat(chr);
}
}
}
u.status = checkGame();
}
以及不再在controller中判断游戏是否达到结束条件,而是另写一个Judger脚本
public class Judger : MonoBehaviour {
public int checkGame()
{
Controller controller = Director.getInstance().currentSceneController as Controller;
int leftP = 0, rightP = 0, leftD = 0, rightD = 0;
int[] LCount = controller.leftBank.getCount();
leftP += LCount[1];
leftD += LCount[0];
int[] RCount = controller.rightBank.getCount();
rightD += RCount[0];
rightP += RCount[1];
int[] Bcount = controller.boat.getCount();
Debug.Log(Bcount[0] + " " + Bcount[1]);
if (rightD + rightP == 6) // win
return 2;
// 计算上船上的人
if (controller.boat.getLR() == 0) {
leftP += Bcount[1];
leftD += Bcount[0];
}
else if (controller.boat.getLR() == 1) {
rightP += Bcount[1];
rightD += Bcount[0];
}
Debug.Log("LP: " + leftP + " LD: " + leftD); //测试用
// 如果魔鬼数量大于牧师,并且牧师数量不为0就失败
if (leftP < leftD && leftP != 0) {
return 0;
}
if (rightP < rightD && rightP != 0) {
return 0;
}
return 1;
}
public void setForbid(bool b) {
Controller controller = Director.getInstance().currentSceneController as Controller;
controller.forbid = b;
}
}
后去调用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();
}
}
public interface ISSActionCallback
{
void SSActionEvent(SSAction action);
}
public class SSMoveToAction : SSAction
{
public Vector3 target;
public float speed;
private SSMoveToAction() { }
public static SSMoveToAction GetSSAction(Vector3 target, float speed)
{
SSMoveToAction action = ScriptableObject.CreateInstance<SSMoveToAction>();
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.SSActionEvent(this);
}
}
public override void Start()
{
}
}
public class CCSequenceAction : SSAction, ISSActionCallback
{
public List<SSAction> sequence;
public int repeat = -1;
public int start = 0;
public static CCSequenceAction GetSSAcition(int repeat, int start, List<SSAction> sequence)
{
CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
action.repeat = repeat;
action.sequence = sequence;
action.start = start;
return action;
}
public override void Update()
{
if (sequence.Count == 0) return;
if (start < sequence.Count)
{
sequence[start].Update();
}
}
public void SSActionEvent(SSAction action)
{
action.destroy = false;
this.start++;
if (this.start >= sequence.Count)
{
this.start = 0;
if (repeat > 0) repeat--;
if (repeat == 0)
{
this.destroy = true;
this.callBack.SSActionEvent(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)
{
Destroy(action);
}
}
}
public class SSActionManager : MonoBehaviour
{
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
private List<SSAction> waitingForAdd = new List<SSAction>();
private List<int> waitingForDelete = new List<int>();
protected void Update()
{
foreach (SSAction action in waitingForAdd)
{
actions[action.GetInstanceID()] = action;
}
waitingForAdd.Clear();
foreach (KeyValuePair<int,SSAction> pair in actions)
{
SSAction action = pair.Value;
if (action.destroy)
{
waitingForDelete.Add(action.GetInstanceID());
} else if (action.enable)
{
action.Update();
}
}
foreach (int key in waitingForDelete)
{
SSAction action = actions[key];
actions.Remove(key);
Destroy(action);
}
waitingForDelete.Clear();
}
public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback callback)
{
action.gameObject = gameObject;
action.transform = gameObject.transform;
action.callBack = callback;
waitingForAdd.Add(action);
action.Start();
}
}