在上一版牧师与魔鬼的基础上,将游戏对象中的move组件分离出来,通过一个专门的动作管理器管理所有动作。区分与每个游戏对象都拥有一个move脚本,该版本的牧师与魔鬼通过场景控制器来专门控制游戏对象的移动。另外根据要求还需要设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。
UML图如下:
动作管理类集成在同一个C#文件下
代码分析:
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;}
protected SSAction() {}
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
动作基类,其它所有的动作都继承于该类。其继承于ScriptableObject类,代表不需要绑定 GameObject 对象的可编程基类,这些对象受 Unity 引擎场景管理。
CCMoveToAction(管理移动动作的实现)
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;
}
public override void Update()
{
this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
if (this.transform.position == target)
{
//waiting for destroy
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
public override void Start(){}
}
通过设置速度和目的地,使得游戏对象可以以一定的速度向目的地移动
CCSequenceAction(顺序动作组合类实现)
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 source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null)
{
source.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()
{
//TODO: something
}
}
实现一个动作组合序列,顺序播放动作,通过继承SSAction类和ISSActionCallback类,使得游戏对象完成一个动作时可以去处理下一个动作。通过设置SSActionEvent()函数,则当前动作执行完成时,推下一个动作,如果完成一次循环,减次数。当次数减为0时,通知动作管理器动作已完成。
ISSActionCallback(动作事件接口)
通过该接口,当动作完成时,动作会调用该接口使得动作管理器去完成下一个动作。
SSActionManager(动作管理基类)
public class SSActionManager : MonoBehaviour, ISSActionCallback //action管理器
{
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); //将执行的动作的字典集合,int为key,SSAction为value
private List<SSAction> waitingAdd = new List<SSAction>(); //等待去执行的动作列表
private List<int> waitingDelete = new List<int>(); //等待删除的动作的key
public int Moving = 0;
protected void Update()
{
foreach (SSAction ac in waitingAdd)
{
actions[ac.GetInstanceID()] = ac; //获取动作实例的ID作为key
}
waitingAdd.Clear();
foreach (KeyValuePair<int, SSAction> kv in actions)
{
SSAction ac = kv.Value;
if (ac.destroy)
{
waitingDelete.Add(ac.GetInstanceID());
}
else if (ac.enable)
{
ac.Update();
}
}
foreach (int key in waitingDelete)
{
SSAction ac = actions[key];
actions.Remove(key);
DestroyObject(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();
Moving++;
}
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null)
{
Moving--;
}
}
管理所有基本动作,由于其继承了ISSActionCallback接口,使得一个动作完成时,可以告知动作管理器去处理下一个动作。
MySceneActionManager类
public MySceneActionManager actionManager;
public Judge judge;
void Start()
{
SSDirector director = SSDirector.GetInstance();
director.CurrentScenceController = this;
User = gameObject.AddComponent<UserGUI>() as UserGUI;
LoadResources();
actionManager = gameObject.AddComponent<MySceneActionManager>() as MySceneActionManager;
judge = gameObject.AddComponent<Judge>() as Judge;
}
public void MoveBoat(){
if(boat.IsEmpty())return;
if(actionManager.Moving > 0)return;
actionManager.move_boat(boat,boat.BoatMoveToPosition());
User.check = judge.check();
}
public void MoveRole(Role role){
if(role.GetOnBank() == -boat.Get_boat_bank()) return;
if(actionManager.Moving > 0)return;
Vector3 pos;
if(role.GetOnBank() == 1 || role.GetOnBank() == -1){ //move role to boat
int temp = 0;
temp = boat.SetRoles(role); //返回一个空位置,若没有则返回-1
if(temp == -1)return; //boat is full
pos = boat.GetBoatPosition();
if(temp == 1 && role.GetOnBank() == 1) pos.x += 1;
if(temp == 0 && role.GetOnBank() == -1) pos.x -= 1;
//role.RoleMove(pos);
actionManager.move_role_to_boat(role.getGameObject(),pos); //动作分离版本
if(role.GetOnBank() == 1) right_bank.DeleteRole(role);
else left_bank.DeleteRole(role);
role.SetOnBank(2);
}
else if(role.GetOnBank() == 2){ //move role to bank
if(boat.Get_boat_bank() == 1){
pos = right_bank.GetPos();
role.SetOnBank(1);
right_bank.AddRole(role);
boat.DeleteRole(role);
//role.RoleMove2(pos);
actionManager.move_role_to_bank(role.getGameObject(),pos);//动作分离版本
}
else {
pos = left_bank.GetPos();
role.SetOnBank(-1);
left_bank.AddRole(role);
boat.DeleteRole(role);
//role.RoleMove2(pos);
actionManager.move_role_to_bank(role.getGameObject(),pos);//动作分离版本
}
User.check = judge.check();
}
}
游戏对象不再通过自身的move脚本来移动,而是统一通过动作管理器来控制移动
裁判类的实现
由于需要设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。所以将原先控制器中的check()函数用一个专门的裁判类替换,代替控制器中的check()函数。
public class Judge : MonoBehaviour{
public Controll mainController;
public Boat boat;
public Bank left_bank;
public Bank right_bank;
public MySceneActionManager actionManager;
void Start()
{
mainController = (Controll)SSDirector.GetInstance().CurrentScenceController;
this.boat = mainController.boat;
this.left_bank = mainController.left_bank;
this.right_bank = mainController.right_bank;
this.actionManager = mainController.actionManager;
}
public int check(){
if(left_bank.GetPriestSum() == 3 && left_bank.GetDevilSum() == 3)return 1;
if(boat.Get_boat_bank() == -1){
if((left_bank.GetDevilSum() + boat.GetDevilSum() > left_bank.GetPriestSum() + boat.GetPriestSum()) && (left_bank.GetPriestSum() + boat.GetPriestSum()) != 0 && actionManager.Moving > 0)return 2;
else if((left_bank.GetDevilSum() + boat.GetDevilSum() > left_bank.GetPriestSum() + boat.GetPriestSum()) && (left_bank.GetPriestSum() + boat.GetPriestSum()) != 0 && actionManager.Moving == 0)return 3;
if(right_bank.GetDevilSum() > right_bank.GetPriestSum() && right_bank.GetPriestSum() != 0 && actionManager.Moving > 0) return 2;
else if(right_bank.GetDevilSum() > right_bank.GetPriestSum() && right_bank.GetPriestSum() != 0 && actionManager.Moving == 0) return 3;
}
else if(boat.Get_boat_bank() == 1){
if((right_bank.GetDevilSum() + boat.GetDevilSum() > right_bank.GetPriestSum() + boat.GetPriestSum()) && (right_bank.GetPriestSum() + boat.GetPriestSum()) != 0 && actionManager.Moving > 0)return 2;
else if((right_bank.GetDevilSum() + boat.GetDevilSum() > right_bank.GetPriestSum() + boat.GetPriestSum()) && (right_bank.GetPriestSum() + boat.GetPriestSum()) != 0 && actionManager.Moving == 0)return 3;
if(left_bank.GetDevilSum() > left_bank.GetPriestSum() && left_bank.GetPriestSum() != 0 && actionManager.Moving > 0) return 2;
else if(left_bank.GetDevilSum() > left_bank.GetPriestSum() && left_bank.GetPriestSum() != 0 && actionManager.Moving == 0) return 3;
}
return 0;
}
}
裁判类继承了MonoBehaviour。MonoBehaviour是每个脚本的基类,又继承于Behaviours。只有继承了MonoBehaviour的类才可以作为组件挂载到游戏对象上,在unity 中所有继承MonoBehaviour的类是不可以实例化的,因为unity都会自动为其创建实例,所以我们需要调用该类的时候不使用new,而是使用AddComponent来调用。
因此,通过在需要用到check函数的类里面声明一个裁判类并将其添加到游戏对象中就可以调用里面的check方法。实现对游戏结果的判断。
视频效果与之前类似,更改了一些材质: