Unity3D学习(4)之分离动作管理器的3D版牧师与魔鬼

       上次作业做完之后,我一直有一个疑问,场记需要做的事情实在太多了,既需要载入资源,也需要控制数据,显得很臃肿。即使写再多的接口,你的函数实现仍然要在场记里面。到了下一节课,老师讲了动作管理器,将动作从场记里面解放出来了。先放上个类图帮助大家理解下思路。
       一开始我认为每个基本的动作像上船,下船,和船的移动只要动作就行,其他判断动作的逻辑应该放在场记那里,但是这样做的结果与上一次作业做的没什么差别只不过你将它的位置移动取出来而已场记动作仍然很大,然后我就想着吧判断动作的逻辑放在动作管理者那里,这本来也合理但是三个动作的逻辑如果都写在动作管理者那里,依然显得冗余,所以我就把各自动作的逻辑放在各自动作那里,由动作自己判断做什么动作或者不做动作。
       首先,我们实现动作的基类和事件回调接口
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 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();
    }
}
然后实现上船动作
public class CCGetOnTheBoat : SSAction
{
    public GenGameObect sceneController;

    public static CCGetOnTheBoat GetSSAction()
    {
        CCGetOnTheBoat action = ScriptableObject.CreateInstance<CCGetOnTheBoat>();
        return action;
    }
    // Use this for initialization
    public override void Start()
    {
        sceneController = (GenGameObect)SSDirector.getInstance().currentScenceController;
    }

    // Update is called once per frame
    public override void Update()
    {
        if (sceneController.boatCapacity != 0)
        {
            if (sceneController.boat_position == 0)
            {
                for (int i = 0; i < 3; ++i)
                {
                    if (sceneController.devils_start[i] == gameobject)
                    {
                        sceneController.devils_start[i] = null;
                        sceneController.find = 1;
                    }
                    if (sceneController.priests_start[i] == gameobject)
                    {
                        sceneController.priests_start[i] = null;
                        sceneController.find = 1;
                    }
                }
            }
            else if (sceneController.boat_position == 1)
            {
                for (int i = 0; i < 3; ++i)
                {
                    if (sceneController.devils_end[i] == gameobject)
                    {
                        sceneController.devils_end[i] = null;
                        sceneController.find = 1;
                    }
                    if (sceneController.priests_end[i] == gameobject)
                    {
                        sceneController.priests_end[i] = null;
                        sceneController.find = 1;
                    }
                }
            }

            if (sceneController.find == 1)
                gameobject.transform.parent = sceneController.boat_obj.transform;

            if (sceneController.boat[0] == null && sceneController.find == 1)
            {
                sceneController.boat[0] = gameobject;
                sceneController.boat[0].transform.tag = gameobject.transform.tag;
                sceneController.boatCapacity--;
                this.transform.localPosition = new Vector3(0, 1.2f, 0.19f);
            }
            else if (sceneController.boat[1] == null && sceneController.find == 1)
            {
                sceneController.boat[1] = gameobject;
                sceneController.boat[1].transform.tag = gameobject.transform.tag;
                sceneController.boatCapacity--;
                this.transform.localPosition = new Vector3(0, 1.2f, -0.12f);
            }
        }
        sceneController.find = 0;
        this.destroy = true;
        this.callback.SSActionEvent(this);
    }
}

接着实现船移动
public class CCBoatMoveing : SSAction {
    public GenGameObect sceneController;

    public static CCBoatMoveing GetSSAction()
    {
        CCBoatMoveing action = ScriptableObject.CreateInstance<CCBoatMoveing>();
        return action;
    }
    // Use this for initialization
    public override void Start()
    {
        sceneController = (GenGameObect)SSDirector.getInstance().currentScenceController;
    }

    // Update is called once per frame
    public override void Update()
    {
        if (sceneController.boat_position == 1)
        {
            sceneController.boat_position = 0;
            while (this.transform.position != sceneController.boatStartPos)
                this.transform.position = Vector3.MoveTowards(this.transform.position, sceneController.boatStartPos, 1);
        }
        else if (sceneController.boat_position == 0)
        {
            sceneController.boat_position = 1;
            while (this.transform.position != sceneController.boatEndPos)
                this.transform.position = Vector3.MoveTowards(this.transform.position, sceneController.boatEndPos, 1);
        }
        sceneController.check();
        this.destroy = true;
        this.callback.SSActionEvent(this);
    }
}

实现下船的动作
public class CCGetOffBoat : SSAction {

    public int side;
    public GenGameObect sceneController;

    public static CCGetOffBoat GetSSAction(int side)
    {
        CCGetOffBoat action = ScriptableObject.CreateInstance<CCGetOffBoat>();
        action.side = side;
        return action;
    }
    // Use this for initialization
    public override void Start()
    {
        sceneController = (GenGameObect)SSDirector.getInstance().currentScenceController;
    }

    // Update is called once per frame
    public override void Update()
    {
        if (sceneController.boat[side] != null)
        {
            sceneController.boat[side].transform.parent = null;
            if (sceneController.boat_position == 1)
            {
                
                if (sceneController.boat[side].transform.tag == "Priest")
                {
                    for (int i = 0; i < 3; i++)
                    {
                        if (sceneController.priests_end[i] == null)
                        {
                            sceneController.priests_end[i] = sceneController.boat[side];
                            sceneController.boatCapacity++;
                            break;
                        }
                    }
                }
                else if (sceneController.boat[side].transform.tag == "Devil")
                {
                    for (int i = 0; i < 3; i++)
                    {
                        if (sceneController.devils_end[i] == null)
                        {
                            sceneController.devils_end[i] = sceneController.boat[side];
                            sceneController.boatCapacity++;
                            break;
                        }
                    }
                }
            }
            else if (sceneController.boat_position == 0)
            {
                if (sceneController.boat[side].transform.tag == "Priest")
                {
                    for (int i = 0; i < 3; i++)
                    {
                        if (sceneController.priests_start[i] == null)
                        {
                            sceneController.priests_start[i] = sceneController.boat[side];
                            sceneController.boatCapacity++;
                            break;
                        }
                    }
                }
                else if (sceneController.boat[side].transform.tag == "Devil")
                {
                    for (int i = 0; i < 3; i++)
                    {
                        if (sceneController.devils_start[i] == null)
                        {
                            sceneController.devils_start[i] = sceneController.boat[side];
                            sceneController.boatCapacity++;
                            break;
                        }
                    }
                }
            }
            sceneController.boat[side] = null;
        }
        sceneController.check();
        this.destroy = true;
        this.callback.SSActionEvent(this);
    }
}

接着再实现动作管理者基类
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>();

    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    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.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 class CCActionManager : SSActionManager, ISSActionCallback
{
    public GenGameObect sceneController;
    public CCGetOnTheBoat getonA;
    public CCGetOffBoat getoffB;
    public CCBoatMoveing boatmovingC;

    // Use this for initialization
    protected void Start () {
        sceneController = (GenGameObect)SSDirector.getInstance().currentScenceController;
        sceneController.actionManager = this;
    }
	
	// Update is called once per frame
	protected new void Update () {
        if (Input.GetMouseButtonDown(0) && sceneController.game == 0)
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.transform.tag == "Devil" || hit.transform.tag == "Priest")
                {
                    if (hit.collider.gameObject == sceneController.boat[0] || hit.collider.gameObject == sceneController.boat[1])
                    {
                        if (hit.collider.gameObject == sceneController.boat[0])
                        {
                            getoffB = CCGetOffBoat.GetSSAction(0);
                            this.RunAction(hit.collider.gameObject, getoffB, this);
                        }
                        else
                        {
                            getoffB = CCGetOffBoat.GetSSAction(1);
                            this.RunAction(hit.collider.gameObject, getoffB, this);
                        }
                    }
                    else
                    {
                        getonA = CCGetOnTheBoat.GetSSAction();
                        this.RunAction(hit.collider.gameObject, getonA, this);
                    }
                }
                else if (hit.transform.tag == "Boat" && sceneController.boatCapacity != 2)
                {
                    print(hit.transform.tag);
                    boatmovingC = CCBoatMoveing.GetSSAction();
                    this.RunAction(hit.collider.gameObject, boatmovingC, this);
                }
            }
        }
        base.Update();
    }

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

最后你做的就是把场记的动作去掉就行了
public class GenGameObect : MonoBehaviour,ISceneController,IUserAction{
    public SSActionManager actionManager { get; set; }
    public  GameObject[] priests_start = new GameObject[3];
    public GameObject[] priests_end = new GameObject[3];
    public GameObject[] devils_start = new GameObject[3];
    public GameObject[] devils_end = new GameObject[3];
    //用栈存储在两岸的牧师与魔鬼的gameobject
    public GameObject[] boat = new GameObject[2];
    //用数组存储在船上的gameobject
    public GameObject boat_obj;
    //获取船的gameobject
    public Vector3 shoreStartPos = new Vector3(-14, 0, 39.5f);
    //起点的岸的坐标
    public Vector3 shoreEndPos = new Vector3(-14, 0, -22.6f);
    //终点的岸的坐标
    public Vector3 boatStartPos = new Vector3(-14, 0, 22.7f);
    public Vector3 boatEndPos = new Vector3(-14, 0, -5.3f);
    //记录船的两个位置
    public float gap = 2.0f;
    public int boatCapacity = 2;
    //纪录船的容量
    public int boat_position = 0;
    //纪录船的位置
    public int game = 0;
    public int find = 0;

    public Vector3 priestStartPos = new Vector3(-14, 2.2f, 31);
    public Vector3 priestEndPos = new Vector3(-14, 2.2f, -16f);
    public Vector3 devilStartPos = new Vector3(-14, 2, 37);
    public Vector3 devilEndPos = new Vector3(-14, 2, -22f);

    public Vector3 waterPos = new Vector3(0,0,0);
    public Vector3 waterPos1 = new Vector3(50,0,0); 

    void Awake()
    //创建导演实例并载入资源
    {
        SSDirector director = SSDirector.getInstance();
        director.setFPS(60);
        director.currentScenceController = this;
        director.currentScenceController.LoadResources();
    }

    public void LoadResources()
    //载入资源
    {
        // shore  
        Instantiate(Resources.Load("prefabs/begin"), shoreStartPos, Quaternion.identity);
        Instantiate(Resources.Load("prefabs/end"), shoreEndPos, Quaternion.identity);
        Instantiate(Resources.Load("prefabs/water"), waterPos, Quaternion.identity);
        Instantiate(Resources.Load("prefabs/water"), waterPos1, Quaternion.identity);
        // boat  
        boat_obj = Instantiate(Resources.Load("prefabs/Capsule"), boatStartPos, Quaternion.identity) as GameObject;
        // priests & devils  
        for (int i = 0; i < 3; ++i)
        {
            priests_start[i] = (Instantiate(Resources.Load("prefabs/Priest")) as GameObject);
            priests_end[i] = null;
            devils_start[i] = (Instantiate(Resources.Load("prefabs/Devil")) as GameObject);
            devils_end[i] = null;
        }
    }

    void setCharacterPositions(GameObject[] array, Vector3 pos)
    //设置人物位置
    {
        for (int i = 0; i < 3; ++i)
        {
            if(array[i] != null)
            array[i].transform.position = new Vector3(pos.x, pos.y, pos.z + gap * i);
        }
    }

    public void Restart()
    {
        SceneManager.LoadScene("task2");
    }

    public void ShowDetail()
    {
        GUI.Label(new Rect(220, 20, 350, 250), "Priests and Devils is a puzzle game in which you will help the Priests and Devils to cross the river. There are 3 priests and 3 devils at one side of the river. They all want to get to the other side of this river, but there is only one boat and this boat can only carry two persons each time. And there must be one person steering the boat from one side to the other side. In the flash game, you can click on them to move them and click to move the boat to the other direction. If the priests are out numbered by the devils on either side of the river, they get killed and the game is over. You can try it in many ways. Keep all priests alive! Good luck!");
    }

    public void Pause()
    {
        if (game == 0)
        {
            game = 3;
        }
        else if(game == 3)
        {
            game = 0;
        }
    }

    private void OnGUI()
    {
        GUIStyle fontstyle1 = new GUIStyle();
        fontstyle1.fontSize = 50;
        fontstyle1.normal.textColor = new Color(255, 255, 255);
        if (game == 1)
        {
            GUI.Label(new Rect(260, 180, 100, 100), "YOU LOSE!!!", fontstyle1);
        }
        else if(game == 2)
        {
            GUI.Label(new Rect(260, 180, 100, 100), "YOU WIN!!!", fontstyle1);
        }
    }
    public void check()
    //检查游戏是否结束
    {
        int priests_s = 0, devils_s = 0, priests_e = 0, devils_e = 0;
        for(int i = 0; i < 3; i++)
        {
            if(priests_start[i] != null)
            {
                priests_s++;
            }
            if(devils_start[i] != null)
            {
                devils_s++;
            }
            if(priests_end[i] != null)
            {
                priests_e++;
            }
            if(devils_end[i] != null)
            {
                devils_e++;
            }
        }
        if(((priests_s < devils_s) && (priests_s != 0))||((priests_e < devils_e) && (priests_e != 0)))
        {
            print("you lose");
            game = 1;
        }
        else if (priests_s == 0 && devils_s == 0)
        {
            print("you win!!!");
            game = 2;
        }
    }
    // Use this for initialization
    void Start () {
        
    }
	
	// Update is called once per frame
	void Update () {
        setCharacterPositions(priests_start, priestStartPos);
        setCharacterPositions(priests_end, priestEndPos);
        setCharacterPositions(devils_start, devilStartPos);
        setCharacterPositions(devils_end, devilEndPos);
    }
}

这周作业就结束了,其实这周作业不难,重点是理解清楚动作管理器的结构,深层次讲我们为什么要搞动作管理器,场记,导演这些东西,首先这是面对对象思想,更便于理解,还有有清楚的架构便于我们团队工作。(最可怕的是实训已经开始了 微笑 微笑 微笑,希望潘老师手下留情,下节课作业别太难)



  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值