Unity3d牧师与魔鬼动作分离版

关于牧师与魔鬼动作分离版,这里只介绍动作管理器的部分,有需要者请结合我的上一篇博客:牧师与魔鬼,加深理解。

动作管理器的设计:

一开始,我们先创建一下几个类:
1. 动作基类:public class SSAction : ScriptableObject{}
2. 简单动作实现类: public class SSMoveToAction : SSAction {}
3. 组合动作实现类: public class SequenceAction: SSAction, ISSActionCallback{}
4. 动作管理基类:public class SSActionManager: MonoBehaviour, ISSActionCallback{}
以及,我们还要建立一个动作事件接口:public interface ISSActionCallback{}

动作管理器解析:

动作基类:
public class SSAction : ScriptableObject            
{

    public bool enable = true;                      
    public bool destroy = false;                    

    public GameObject gameobject;                   
    public Transform transform;                    
    public ISSActionCallback callback;              

    protected SSAction() {}                        

    public virtual void Start()                    
    {
        throw new System.NotImplementedException();
    }

    public virtual void Update()
    {
        throw new System.NotImplementedException();
    }
}

基类创建了动作管理器的基本元素,它继承了Unity的ScriptableObject类,该类是不需要绑定GameObject对象的可编程类。其中的属性包括enable(动作是否进行),destroy(动作是否该被销毁)。同时利用接口实现游戏的通信:callback。

简单动作实现类:
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() 
    {
    }
}

顾名思义,该类实现单个动作,实现物体的移动。其中GetSSAction函数通过获取物体运动的目的地和速度,让unity自己建立一个动作类。当动作完成时销毁该动作并告诉管理者动作已经完成,这以功能在UpDate中实现,毕竟要通过每一帧来判断是否该结束动作。

组合动作实现类:
public class SequenceAction: SSAction, ISSActionCallback
{           
    public List<SSAction> sequence;    //动作的列表
    public int repeat = -1;            //-1就是无限循环做组合中的动作
    public int start = 0;              //当前做的动作的索引

    public static SequenceAction GetSSAcition(int repeat, int start, List<SSAction> sequence) 
    {
        SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>();//让unity自己创建一个SequenceAction实例
        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() 
    {

    }
}

这个类先像简单动作类一样创建一个动作类,有所不同的是这里创建的是动作序列,因为要实现的是组合动作嘛。在UpDate中每一帧完成当前动作,当这一序列的动作完成时,停止并通过回调函数告诉管理者该序列动作已完成,并删除序列。在执行动作前,我们需要为每一个动作注入游戏对象,在Start中实现。

动作管理基类:
public class SSActionManager: MonoBehaviour, ISSActionCallback                      
{   

    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                

    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();
    }

    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,  
        int intParam = 0, string strParam = null, Object objectParam = null)
    {
        //实现事件处理的空接口。
    }
}

首先建立MonoBehavior管理动作,动作做完自动回收,在这之中必然有需要等待的动作和已经做完的动作,显然,需要等待的动作加入等待列表,需要删除的动作加入删除列表。提供了运行一个新动作的方法RunAction。该方法把游戏对象与动作绑定,并绑定该动作事件的消息接收者。

动作事件接口:
public enum SSActionEventType : int { Started, Competeted }  

public interface ISSActionCallback  
{  
    void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,  
        int intParam = 0, string strParam = null, Object objectParam = null);  
} 

定义了事件处理接口,所有事件管理者都必须实现这个接口,来实现
事件调度。所以,组合事件需要实现它,事件管理器也必须实现它。

实战管理:

public class MySceneActionManager:SSActionManager  
{                  
    private SSMoveToAction move_boat;     
    private SequenceAction move_role;     

    public MySceneController sceneController;

    protected new void Start()
    {
        sceneController = (MySceneController)Director.get_Instance().curren;
        sceneController.actionManager = this;
    }
    public void moveBoat(GameObject boat, Vector3 target, float speed)
    {
        move_boat = SSMoveToAction.GetSSAction(target, speed);
        this.RunAction(boat, move_boat, this);
    }

    public void moveRole(GameObject role, Vector3 middle_pos, Vector3 end_pos,float speed)
    {
        SSAction action1 = SSMoveToAction.GetSSAction(middle_pos, speed);
        SSAction action2 = SSMoveToAction.GetSSAction(end_pos, speed);
        move_role = SequenceAction.GetSSAcition(1, 0, new List<SSAction>{action1, action2});
        this.RunAction(role, move_role, this);
    }
}

实现之前moveable类中的船运动和角色运动的两个函数。
完整代码github地址:wangld5

猜你喜欢

转载自blog.csdn.net/hellowangld/article/details/79882484