作业要求
牧师与魔鬼 动作分离版
【2019开始的新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束
目标:建立动作管理器,使动作抽象出来,可以应用到任何游戏对象上,以此提高代码复用性。
部署工作
-
给摄像机添加RayCaster部件,可以实现追踪监听
-
船添加部件
-
牧师与魔鬼添加部件
设计思路
课程网站介绍设计思路主要是:
-
通过门面模式(控制器模式)输出组合好的几个动作,共原来程序调用。
好处,动作如何组合变成动作模块内部的事务
这个门面就是 CCActionManager -
通过组合模式实现动作组合,按组合模式设计方法
必须有一个抽象事物表示该类事物的共性,例如 SSAction,表示动作,不管是基本动作或是组合后动作
基本动作,用户设计的基本动作类。 例如:CCMoveToAction
组合动作,由(基本或组合)动作组合的类。例如:CCSequenceAction -
接口回调(函数回调)实现管理者与被管理者解耦
如组合对象实现一个事件抽象接口(ISSCallback),作为监听器(listener)监听子动作的事件
被组合对象使用监听器传递消息给管理者。至于管理者如何处理就是实现这个监听器的人说了算了
例如:每个学生做完作业通过邮箱发消息给学委,学委是谁,如何处理,学生就不用操心了 -
通过模板方法,让使用者减少对动作管理过程细节的要求
SSActionManager 作为 CCActionManager 基类
重要代码分析
SSDirector.cs
首先,GameObject是不可以在类之间的函数来传递的,但是可以通过在生成GameObject的类赋值到SSDirector的变量FirstController中,然后获取SSDdirctor的唯一实例来,使用生成GameObject的类,然后使用GameObject。FirstController的设置在FirstController.cs中
利用作业二,把之前分给船和人物部件控制的运动函数,交给ActionManager,利用ActionManager类对物体对象的动作进行管理。将船和人物的状态变量保留下来。如果触发了点击函数,就通知SSDirector。SSDirector通过各种状态判断船和人物应该进行的动作,然后通知ActionManager进行相应的动作。
动作管理器的类
- SSAction.cs
整个游戏动作的基类,继承自ScriptableObject,其他的所有的物体的动作都继承这个类,主要是两个纯虚函数Start、Update
public virtual void Start()
{
throw new System.NotImplementedException();
}
// Update is called once per frame
public virtual void Update()
{
throw new System.NotImplementedException();
}
- CCMoveToAction.cs
作用是将一个物体移动到目标位置,并通知人物完成
public class CCMoveToAction : SSAction
{
public Vector3 target;
public float speed;
public static CCMoveToAction GetSSAction(Vector3 target, float speed)
{
CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
action.target = target;
action.speed = speed;
return action;
}
// Start is called before the first frame update
public override void Start()
{
}
// Update is called once per frame
public override void Update()
{
this.transform.position = Vector3.MoveTowards(this.transform.localPosition, target, Time.deltaTime * speed);
if (this.transform.position == target)
{
// waiting for destory
this.destory = true;
this.callback.SSActionEvent(this);
}
}
}
- CCSequenceAction.cs
CCMoveToAction 只是一个能够直线运动的模版,这个类可以将上面的类组合起来,由于model的动作都是直线运动,所以无需再创建其他类,对于人物来说他们的动作是折线运动,组合一下就好
public class CCSequenceAction : SSAction, ISSActionCallback
{
public List<SSAction> sequence;
public int repeat = -1; //repeat forever
public int start = 0;
public static CCSequenceAction GetSSAction(int repeat, int start, List<SSAction> sequence)
{
CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
action.repeat = repeat;
action.sequence = sequence;
action.start = start;
return action;
}
// Start is called before the first frame update
public override void Start()
{
foreach (SSAction action in sequence)
{
action.gameobject = this.gameobject;
action.transform = this.transform;
action.callback = this;
action.Start();
}
}
// Update is called once per frame
public override void Update()
{
if (sequence.Count == 0) return;
if (start < sequence.Count)
{
sequence[start].Update();
}
}
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null)
{
source.destory = false;
this.start++;
if (this.start >= sequence.Count)
{
this.start = 0;
if (repeat > 0) repeat--;
if (repeat == 0)
{
this.destory = true;
this.callback.SSActionEvent(this);
}
}
}
}
-
SSActionManager.cs
对SSActionManager的修改,当动作结束的时候要修改船和人物的运动状态。SSActionManager.cs相当于是对外的接口,作用是控制运动状态:当人物在运动的时候,船不能动。当船动的时候,不能让人物动。Update完成主要任务
(1)通过对应的动作ID,将动作加入到字典中,便于后续的操作;(2)对于每一个动作,查看他们的完成情况,如果完成了那么就加入到删除队列,否则就完成这个动作;(3)删除完成了的动作
RunAction完成动作的添加
protected void Update()
{
foreach (SSAction ac in waitingAdd) {
actions.Add(ac.GetInstanceID(), ac);
}
waitingAdd.Clear();
//SSAction ack = actions[num];
foreach (KeyValuePair<int, SSAction> kv in actions)
{
SSAction ac = kv.Value;
if (ac.destory)
{
waitingDelete.Add(ac.GetInstanceID()); //release action
} else if (ac.enable){
ac.Update(); //update action
}
}
foreach (int key in waitingDelete)
{
SSAction ac = actions[key];
// 停止移动成功
GameObject gb = ac.gameobject;
FirstController fc = (FirstController)SSDirector.getInstance().firstcontroller;
if (ac.gameobject.transform.position == fc.boat.transform.position)
{
fc.boat.GetComponent<EventClick>().Finished();
}
else if (ac.gameobject.transform.position == fc.priestOne.transform.position)
{
fc.priestOne.GetComponent<EventClickPeoPle>().Finished();
}
else if (ac.gameobject.transform.position == fc.priestTwo.transform.position)
{
fc.priestTwo.GetComponent<EventClickPeoPle>().Finished();
}
else if (ac.gameobject.transform.position == fc.priestThree.transform.position)
{
fc.priestThree.GetComponent<EventClickPeoPle>().Finished();
}
else if (ac.gameobject.transform.position == fc.demonOne.transform.position)
{
fc.demonOne.GetComponent<EventClickPeoPle>().Finished();
}
else if (ac.gameobject.transform.position == fc.demonTwo.transform.position)
{
fc.demonTwo.GetComponent<EventClickPeoPle>().Finished();
}
else if (ac.gameobject.transform.position == fc.demonThree.transform.position)
{
fc.demonThree.GetComponent<EventClickPeoPle>().Finished();
}
actions.Remove(key);
if (actions.Count == 0)
{
SSDirector.getInstance().Check();
}
Destroy(ac);
}
waitingDelete.Clear();
//检查游戏是否成功
}
public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager)
{
action.gameobject = gameobject;
action.transform = gameobject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
- ISSActionCallback
这是一个接口类,主要有一个函数void SSActionEvent(SSAction source)放在了controller中的interfaces中
public interface ISSActionCallback
{
void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0,
string strParam = null,
Object objectParam = null);
}
- CCActionManager.cs
对动作进行组合
public class CCActionManager : SSActionManager, ISSActionCallback
{
public FirstController sceneController;
// Start is called before the first frame update
protected void Start()
{
sceneController = (FirstController)SSDirector.getInstance().firstcontroller;
SSDirector.getInstance().ccam = this;
}
// Update is called once per frame
protected new void Update()
{
base.Update();
}
#region ISSActionCallback implementation
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null)
{
}
#endregion
public void MoveTheBoatAcrossRiver()
{
if (sceneController.boat.GetComponent<EventClick>().leftSide)
{
CCMoveToAction MoveToLeft = CCMoveToAction.GetSSAction(new Vector3((float)1.3, (float)0.4, (float)-1), 3);
this.RunAction(sceneController.boat, MoveToLeft, this);
} else
{
CCMoveToAction MoveToRight = CCMoveToAction.GetSSAction(new Vector3((float)-1.3, (float)0.4, (float)-1), 3);
this.RunAction(sceneController.boat, MoveToRight, this);
}
}
// 如果人物处于运动状态中,则运动
public void CheckPeoPleAndMove()
{
if (!sceneController.priestOne.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.priestOne);
if (!sceneController.priestTwo.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.priestTwo);
if (!sceneController.priestThree.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.priestThree);
if (!sceneController.demonOne.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.demonOne);
if (!sceneController.demonTwo.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.demonTwo);
if (!sceneController.demonThree.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.demonThree);
}
private void PeopleMove(ref GameObject gobj)
{
CCMoveToAction destinationA, destinationB, destinationC, destinationD;
EventClickPeoPle ecp = gobj.GetComponent<EventClickPeoPle>();
//到岸上,或者到船上
if (ecp.rightToBoat)
{
destinationA = CCMoveToAction.GetSSAction(ecp.rightFirstDestination, 3);
destinationB = CCMoveToAction.GetSSAction(ecp.rightMidDestination, 3);
destinationC = CCMoveToAction.GetSSAction(ecp.rightFinalDestination, 3);
destinationD = CCMoveToAction.GetSSAction(ecp.onBoatPosition, 3);
CCSequenceAction css = CCSequenceAction.GetSSAction(1, 0, new List<SSAction> { destinationA, destinationB, destinationC, destinationD });
this.RunAction(gobj, css, this);
ecp.rightToBoatFinished();
}
else if (ecp.boatToRight)
{
destinationA = CCMoveToAction.GetSSAction(ecp.rightFirstDestination, 3);
destinationB = CCMoveToAction.GetSSAction(ecp.rightMidDestination, 3);
destinationC = CCMoveToAction.GetSSAction(ecp.rightFinalDestination, 3);
destinationD = CCMoveToAction.GetSSAction(ecp.rightStartPosition, 3);
CCSequenceAction css = CCSequenceAction.GetSSAction(1, 0, new List<SSAction> { destinationC, destinationB, destinationA, destinationD });
this.RunAction(gobj, css, this);
ecp.boatToRightFinished();
}
else if (ecp.leftToBoat)
{
destinationA = CCMoveToAction.GetSSAction(ecp.leftFirstDestination, 3);
destinationB = CCMoveToAction.GetSSAction(ecp.leftMidDestination, 3);
destinationC = CCMoveToAction.GetSSAction(ecp.leftFinalDestination, 3);
destinationD = CCMoveToAction.GetSSAction(ecp.onBoatPosition, 3);
CCSequenceAction css = CCSequenceAction.GetSSAction(1, 0, new List<SSAction> { destinationA, destinationB, destinationC, destinationD });
this.RunAction(gobj, css, this);
ecp.leftToBoatFinished();
}
else if (ecp.boatToLeft)
{
destinationA = CCMoveToAction.GetSSAction(ecp.leftFirstDestination, 3);
destinationB = CCMoveToAction.GetSSAction(ecp.leftMidDestination, 3);
destinationC = CCMoveToAction.GetSSAction(ecp.leftFinalDestination, 3);
destinationD = CCMoveToAction.GetSSAction(ecp.leftStartPosition, 3);
CCSequenceAction css = CCSequenceAction.GetSSAction(1, 0, new List<SSAction> { destinationC, destinationB, destinationA, destinationD });
this.RunAction(gobj, css, this);
ecp.boatToLeftFinished();
}
//跨越河流
if (ecp.leftAcrossRiver)
{
CCMoveToAction Move = CCMoveToAction.GetSSAction(ecp.onBoatPosition, 3);
this.RunAction(gobj, Move, this);
ecp.leftAcrossRiverFinished();
}
else if (ecp.rightAcrossRiver)
{
CCMoveToAction Move = CCMoveToAction.GetSSAction(ecp.onBoatPosition, 3);
this.RunAction(gobj, Move, this);
ecp.rightAcrossRiverFinished();
}
}
}
以上为项目的部分代码,项目全部代码指路:https://gitee.com/zipzhou/priest-and-devil-actionseperated
视频演示链接:https://www.bilibili.com/video/BV1HQ4y1z7Rn?spm_id_from=333.999.0.0