Unity3D学习笔记(四)牧师和魔鬼游戏改进

在上次做的牧师和魔鬼的游戏中,场记基本负责了所有的工作,加载资源,移动游戏对象,很明显这样的游戏结构很难维护,很难拓展,很不面向对象,所以这次的工作就是利用工厂模式来生产动作。弥补了上次没有上船动作的缺点。


先上UML图:
这里写图片描述

UML图实在是花的丑,有空再改。

工厂模式解释:

CCAction, CCSequenceAction等就是工厂,他的产品就是SSAction,由SSActionManager统一管理。具体的动作都是继承SSAction进行自定义。这里我定义了两个动作,一个是MoveToAction 用于船的移动,因为船是直线移动同时确定终点起点,第二个是MoveAction,用于上船的运动,因为魔鬼和牧师出发的位置不一定,所以要根据具体位置进行计算重点,但是其移动的距离和方向却是确定的,再利用CCSequenceAction进行简单动作的组合。

每个SSAction都包含一个callback属性,在完成后通知动作的管理者。

值得说明的是,在编写这个游戏的过程中,我遇到了一个bug解决了两天。就是一个动作只能执行一次,下一次就不能正常的执行了。我排查了许久,终于发现是action.destroy 和 action.enable没有进行初始化。

关键代码讲解:
SSActionManager.cs

public class SSActionManager : MonoBehaviour {
    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction> ();
    private List<SSAction> waitingAdd = new List<SSAction>();
    private List<int> waitingDelete = new List<int>();

    protected void Update() {
        foreach (SSAction ac in waitingAdd)
        {
            actions[ac.GetInstanceID()] = ac;
        }
        waitingAdd.Clear();

        foreach (KeyValuePair<int, SSAction> kv in actions) {
            SSAction ac = kv.Value;
            if (ac.destory) {
                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;
        // just let it move relative to their father
        action.transform = gameobject.transform;
        action.callback = manager;
        /* !!!!!!!!!!!!!!!!!!!!!!!!!!
         * bug happen here --20170320 2306
         * if this action is used twice, we need to reset
         * action.destory and action.enable
         * But this is not a good convention
         * -- by BowenWu
         */
        action.destory = false;
        action.enable = true;
        waitingAdd.Add(action);
        action.Start();
    }

    protected void Start() {}
}

RunAction方法:

这个方法负责所有动作的执行,其他动作在需要执行的时候就调用这个方法。这个方法会将需要执行的动作放入集合中,在每个Update统一管理,调用Update。这是因为其他动作的类并没有继承MonoBehavior并不会在每帧自动执行,而这个manger是会在每帧自动执行。

waitingAdd队列:

由于动作可能在任何时候进入,同时他进入的时候并不一定会是在一帧刚好结束时,这其中情况很多,不可预料,所以将其先放入waitingAdd队列中,然后在update下一次执行时再使其加入到actions字典中。

waitingDelete队列:

当游戏规模变大时,游戏资源、对象相关的依赖性增加,如果立即销毁对象,可能导致离散引擎并发的行为之间依赖关系产生不可预知错误。所以在渲染前才删去。


以下代码为这次的内容,上次的代码也需要进行少部分改变,那些内容不是很难,如果需要的可以给我留言。
将CCActionManger和GenGameobject挂在空对象上,就可以运行啦!


SSAction.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject {
    public bool enable = true;
    public bool destory = 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();
    }
}

SSActionManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSActionManager : MonoBehaviour {
    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction> ();
    private List<SSAction> waitingAdd = new List<SSAction>();
    private List<int> waitingDelete = new List<int>();

    protected void Update() {
        foreach (SSAction ac in waitingAdd)
        {
            actions[ac.GetInstanceID()] = ac;
        }
        waitingAdd.Clear();

        foreach (KeyValuePair<int, SSAction> kv in actions) {
            SSAction ac = kv.Value;
            if (ac.destory) {
                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;
        // just let it move relative to their father
        action.transform = gameobject.transform;
        action.callback = manager;
        /* !!!!!!!!!!!!!!!!!!!!!!!!!!
         * bug happen here --20170320 2306
         * if this action is used twice, we need to reset
         * action.destory and action.enable
         * But this is not a good convention
         * -- by BowenWu
         */
        action.destory = false;
        action.enable = true;
        waitingAdd.Add(action);
        action.Start();
    }

    protected void Start() {}
}

ISSActionCallback.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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

CCActionManager.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCActionManager : SSActionManager, ISSActionCallback {
    public CCMoveToAction boat_to_end, boat_to_begin;
    // simple moves used to make a sequence move
    public CCMoveAction move_up, move_down;
    public CCMoveToAction move_to_boat_left_begin, move_to_boat_right_begin;
    public CCMoveToAction move_to_boat_left_end, move_to_boat_right_end;

    public CCSequenceAction get_on_boat_left_begin, get_on_boat_right_begin;
    public CCSequenceAction get_on_boat_left_end, get_on_boat_right_end;
    // public CCSequenceAction get_off_boat;

    private float object_speed;
    private float boat_speed;
    public GenGameObjects sceneController;
    protected new void Start() {
        object_speed = 4.0f;
        boat_speed = 4.0f;
        sceneController = (GenGameObjects)SSDirector.getInstance().currentSceneController;
        sceneController.actionManager = this;

        move_up = CCMoveAction.GetSSAction(new Vector3(0, 1, 0), object_speed);
        move_down = CCMoveAction.GetSSAction(new Vector3(0, -1, 0), object_speed);

        move_to_boat_left_begin = CCMoveToAction.GetSSAction(new Vector3(-2.3f, 2, 0), object_speed);
        move_to_boat_right_begin = CCMoveToAction.GetSSAction(new Vector3(-1.2f, 2, 0), object_speed);

        move_to_boat_left_end = CCMoveToAction.GetSSAction (new Vector3 (0.7f, 2, 0), object_speed);
        move_to_boat_right_end = CCMoveToAction.GetSSAction (new Vector3 (1.8f, 2, 0), object_speed);

        get_on_boat_left_begin = CCSequenceAction.GetSSAction(0, 0, new List<SSAction> {
            move_up, move_to_boat_left_begin, move_down
        });
        get_on_boat_right_begin = CCSequenceAction.GetSSAction(0, 0, new List<SSAction> {
            move_up, move_to_boat_right_begin, move_down
        });

        get_on_boat_left_end = CCSequenceAction.GetSSAction (0, 0, new List<SSAction> {
            move_up,
            move_to_boat_left_end,
            move_down
        });
        get_on_boat_right_end = CCSequenceAction.GetSSAction (0, 0, new List<SSAction> {
            move_up,
            move_to_boat_right_end,
            move_down
        });

        boat_to_end = CCMoveToAction.GetSSAction (new Vector3 (1.7f, 0, 0), boat_speed);
        boat_to_begin = CCMoveToAction.GetSSAction (new Vector3 (-1.7f, 0, 0), boat_speed);

    }

    protected new void Update() {
        base.Update();
    }

    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
                        int intParam = 0, string strParam = null, Object objectParam = null) {
        Debug.Log ("change back Game_state");
        sceneController.game_state = GenGameObjects.State.normal;
        // sceneController.game_state = sceneController.State.moving;
    }
}

CCMoveToAction.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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) {
            this.destory = true;
            this.callback.SSActionEvent(this);
        }
    }

    public override void Start() {
        Debug.Log ("MoveToAction, target is " + target);
        // make move on relative cordinate
    }
}

CCMoveAction.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCMoveAction : SSAction {
    // this kind of action move towards the Vector3
    // but not move to a target
    public Vector3 distance;
    private Vector3 target;
    public float speed;

    public static CCMoveAction GetSSAction(Vector3 distance, float speed) {
        CCMoveAction action = ScriptableObject.CreateInstance<CCMoveAction>();
        action.distance = distance;
        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.destory = true;
            this.callback.SSActionEvent(this);
        }
    }

    public override void Start() {
        target = this.transform.position + distance;
        Debug.Log ("on MoveAction Start!");
        Debug.Log ("target is :" + target);
    }
}

CCSequenceAction.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCSequenceAction : SSAction, ISSActionCallback {
    public List<SSAction> sequence;
    public int repeat = -1;
    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;
    }

    public override void Update() {
        /*if (sequence.Count == 0) return;
        if (start < sequence.Count) {
            sequence[start].Update();
        }*/
        sequence [start].Update ();
    }

    public override void Start() {
        Debug.Log ("on CCSequenceActionStart");
        // Debug.Log (this.transform.parent);
        foreach (SSAction action in sequence)
        {
            action.gameobject = this.gameobject;
            action.transform = this.transform;
            action.callback = this;
        }
        start = 0;
        sequence [0].Start ();
    }
    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);
                        }
        } else {
            sequence [start].Start ();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值