游戏开发之Unity学习(四)——牧师与魔鬼(动作管理器版)

使用动作管理器的原因

前一个版本 FirstSceneController中包含了很多动作处理的部分。FirstSceneController类需要处理的事情太多,功能分离的不彻底。使用动作管理器将动作从 FirstSceneController类中分离出来能简化FirstSceneController类的结构,使之功能更加清晰。

动作管理器的构建

动作管理器分离后的最终结构(UML图)

游戏结构
首先明确动作管理器的基本结构,构造动作管理器基类:SSAction。首先是动作能否发生和动作是否完成,在这个游戏中,每个动作的发生还与两个因素有关:一个是触发动作的游戏对象gameObject,另一个是游戏对象的tranform。在每一个动作完成后,可能需要触发某些函数(例如:船移动结束后要判断是否游戏结束),因此添加一个回调函数callback。

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

using System.Collections.Generic;
using UnityEngine;

public class SSActionManager : MonoBehaviour {
    public Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
    public List<SSAction> waitingAdd = new List<SSAction>();
    public 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)
        {
            if (kv.Value.destory)
                waitingDelete.Add(kv.Value.GetInstanceID());
            else if (kv.Value.enable)
                kv.Value.Update();
            else
                waitingDelete.Add(kv.Value.GetInstanceID());
        }
        foreach (int key in waitingDelete)
        {
            DestroyObject(actions[key]);
            actions.Remove(key);
        }
        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();
    }
}

根据整个游戏过程中的动作,分为两个动作类:UserClickAction和CCMoveToAction。
UserClickAction类处理牧师和魔鬼的上船下船动作,CCMoveToAction处理船移动的动作。
UserClickAction类:

using UnityEngine;

public class UserClickAction : SSAction {
    // Use this for initialization

    public static UserClickAction GetSSAction()
    {
        UserClickAction action = CreateInstance<UserClickAction>();
        return action;
    }

    public override void Start()
    {

    }

    public override void Update()
    {
        FirstSceneController sceneController = SSDirector.getInstance().currentSceneController as FirstSceneController;
        if (sceneController.onBoatDevil.Contains(gameObject) || sceneController.onBoatPriest.Contains(gameObject))
        {
            int f = sceneController.boat.transform.position.x < 0 ? 1 : -1;
            int i = gameObject.name[gameObject.name.Length - 1] - '0';
            gameObject.transform.parent = sceneController.transform;
            gameObject.transform.position = new Vector3(f * (-4.9f - i * 0.6f), 1.0f, 0);
            if(gameObject.tag == "devil")
            {
                sceneController.onBoatDevil.Remove(gameObject);
                if (f > 0) sceneController.startDevil.Add(gameObject);
                else sceneController.endDevil.Add(gameObject);
            }
            else
            {
                sceneController.onBoatPriest.Remove(gameObject);
                if (f > 0) sceneController.startPriest.Add(gameObject);
                else sceneController.endPriest.Add(gameObject);
            }
            sceneController.boatCapacity++;
        }
        else if (sceneController.boatCapacity > 0)
        {
            float i = sceneController.boat.transform.position.x * gameObject.transform.position.x;
            if (i < 0) return;
            int f = sceneController.boat.transform.position.x < 0 ? 1 : -1;
            gameObject.transform.parent = sceneController.boat.transform;
            if (sceneController.boatCapacity == 1)
            {
                Vector3 onBoatGameObjectPosition = (sceneController.onBoatPriest.Count == 1 ? sceneController.onBoatPriest[0] : sceneController.onBoatDevil[0]).transform.position;
                gameObject.transform.position = new Vector3(-onBoatGameObjectPosition.x, onBoatGameObjectPosition.y,onBoatGameObjectPosition.z) + 2 * sceneController.boat.transform.position;
            }
            else
                gameObject.transform.position = new Vector3(-0.6f, 0.25f) + sceneController.boat.transform.position;
            if (gameObject.tag == "devil")
            {
                sceneController.onBoatDevil.Add(gameObject);
                if (f > 0) sceneController.startDevil.Remove(gameObject);
                else sceneController.endDevil.Remove(gameObject);
            }
            else
            {
                sceneController.onBoatPriest.Add(gameObject);
                if (f > 0) sceneController.startPriest.Remove(gameObject);
                else sceneController.endPriest.Remove(gameObject);
            }
            sceneController.boatCapacity--;
        }
        this.destory = true;
    }
}

CCMoveToAction类:

using UnityEngine;

public class CCMoveToAction : SSAction
{
    public Vector3 target;
    public float speed;

    public static CCMoveToAction GetSSAction(Vector3 target, float speed)
    {
        CCMoveToAction action = CreateInstance<CCMoveToAction>();
        action.target = target;
        action.speed = speed;
        return action;
    }

    public override void Start()
    {

    }
    public override void Update()
    {
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed);
        this.callback.SSActionEvent(this);
        if (this.transform.position == target)
        {
            this.enable = false;
            this.destory = true;
            this.callback.CheckEvent(this);
        }
    }
}

最后我们需要一个调用这些动作,进行管理并和FirstSceneController通信的类:CCActionManager

using UnityEngine;

public class CCActionManager : SSActionManager, ISSActionCallback {
    public FirstSceneController sceneController;
    public CCMoveToAction moveToAction,moveToActionB;
    public UserClickAction userClickAction;

    protected new void Start()
    {
        sceneController = (FirstSceneController)SSDirector.getInstance().currentSceneController;
        sceneController.actionManager = this;
    }
    protected new void Update()
    {
        if (Input.GetMouseButtonDown(0) && sceneController.flag == 0)
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hitGameObject;
            if (Physics.Raycast(ray, out hitGameObject))
            {
                GameObject gameObject = hitGameObject.collider.gameObject;
                if (gameObject.tag == "devil" || gameObject.tag == "priest")
                {
                    userClickAction = UserClickAction.GetSSAction();
                    this.RunAction(gameObject, userClickAction, this);
                }
                else if (gameObject.transform.parent.name == "boat" && sceneController.boatCapacity < 2 && (moveToAction == null || !moveToAction.enable))
                {
                    moveToAction = CCMoveToAction.GetSSAction(-gameObject.transform.parent.transform.position, 10*Time.deltaTime);
                    this.RunAction(gameObject.transform.parent.gameObject, moveToAction, this);
                }
            }
        }
        base.Update();
    }
    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed, int intParam = 0, string strParam = null, Object objParam = null)
    {
        if(source == moveToAction)
        {
            userClickAction.enable = !source.enable;
        }
    }
    public void CheckEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed, int intParam = 0, string strParam = null, Object objParam = null)
    {
        if (source == moveToAction)
        {
            int startDevilNum = sceneController.startDevil.Count + (source.transform.position.x < 0 ? sceneController.onBoatDevil.Count : 0);
            int startPriestNum = sceneController.startPriest.Count + (source.transform.position.x < 0 ? sceneController.onBoatPriest.Count : 0);
            if (startDevilNum > startPriestNum && startPriestNum != 0)
                sceneController.flag = 2;
            else if (startPriestNum + startDevilNum == 0)
                sceneController.flag = 1;
        }
    }
}

当这些类都完成后,动作已经可以很好地从FirstSceneController中分离出来了,现在处理一下FirstSceneController类吧。

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

public class FirstSceneController : MonoBehaviour, IUserAction, ISceneController{
    public CCActionManager actionManager;
    public GameObject boat;
    public List<GameObject> startPriest = new List<GameObject>();
    public List<GameObject> startDevil = new List<GameObject>();
    public List<GameObject> endPriest = new List<GameObject>();
    public List<GameObject> endDevil = new List<GameObject>();
    public List<GameObject> onBoatPriest = new List<GameObject>();
    public List<GameObject> onBoatDevil = new List<GameObject>();
    public int boatCapacity = 2;
    public int flag = 0;

    private void Awake()
    {
        SSDirector director = SSDirector.getInstance();
        director.setFPS(60);
        director.currentSceneController = this;
        director.currentSceneController.GenGameObjects();
    }
    private void Start()
    {
    }
    public void GenGameObjects ()
    {
        GameObject river = Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/river"), Vector3.zero, Quaternion.identity);
        river.name = "river";
        river.transform.parent = this.transform;
        boat = Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/boat"));
        boat.name = "boat";
        boat.transform.parent = this.transform;
        boat.transform.position = new Vector3(-3.5f,0);
        for (int i=1;i<=3;++i)
        {
            GameObject person = Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/Priest"));
            person.name = "Priest" + i;
            person.transform.parent = this.transform;
            person.transform.position = new Vector3(- 4.9f - i*0.6f, 1.0f, 0);
            startPriest.Add(person);
        }
        for (int i = 1; i <= 3; ++i)
        {
            GameObject person = Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/Devils"));
            person.name = "Devils" + (i+3);
            person.transform.parent = this.transform;
            person.transform.position = new Vector3(-4.9f - (i+3) * 0.6f, 1.0f, 0);
            startDevil.Add(person);
        }
    }
}

更详细的内容请戳:传送门

游戏的最终效果和游戏场景

这里写图片描述

猜你喜欢

转载自blog.csdn.net/JC2474223242/article/details/79880709