unity编程实践-牧师与魔鬼动作分离版

作业要求

牧师与魔鬼 动作分离版

【2019开始的新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束

目标:建立动作管理器,使动作抽象出来,可以应用到任何游戏对象上,以此提高代码复用性。

部署工作

  • 给摄像机添加RayCaster部件,可以实现追踪监听
    在这里插入图片描述

  • 船添加部件
    在这里插入图片描述

  • 牧师与魔鬼添加部件
    在这里插入图片描述

设计思路

课程网站介绍设计思路主要是:

  • 通过门面模式(控制器模式)输出组合好的几个动作,共原来程序调用。

    好处,动作如何组合变成动作模块内部的事务
    这个门面就是 CCActionManager

  • 通过组合模式实现动作组合,按组合模式设计方法

    必须有一个抽象事物表示该类事物的共性,例如 SSAction,表示动作,不管是基本动作或是组合后动作
    基本动作,用户设计的基本动作类。 例如:CCMoveToAction
    组合动作,由(基本或组合)动作组合的类。例如:CCSequenceAction

  • 接口回调(函数回调)实现管理者与被管理者解耦

    如组合对象实现一个事件抽象接口(ISSCallback),作为监听器(listener)监听子动作的事件
    被组合对象使用监听器传递消息给管理者。至于管理者如何处理就是实现这个监听器的人说了算了
    例如:每个学生做完作业通过邮箱发消息给学委,学委是谁,如何处理,学生就不用操心了

  • 通过模板方法,让使用者减少对动作管理过程细节的要求

    SSActionManager 作为 CCActionManager 基类

重要代码分析

SSDirector.cs

首先,GameObject是不可以在类之间的函数来传递的,但是可以通过在生成GameObject的类赋值到SSDirector的变量FirstController中,然后获取SSDdirctor的唯一实例来,使用生成GameObject的类,然后使用GameObject。FirstController的设置在FirstController.cs中

利用作业二,把之前分给船和人物部件控制的运动函数,交给ActionManager,利用ActionManager类对物体对象的动作进行管理。将船和人物的状态变量保留下来。如果触发了点击函数,就通知SSDirector。SSDirector通过各种状态判断船和人物应该进行的动作,然后通知ActionManager进行相应的动作。

动作管理器的类
  • SSAction.cs

整个游戏动作的基类,继承自ScriptableObject,其他的所有的物体的动作都继承这个类,主要是两个纯虚函数Start、Update

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

// Update is called once per frame
public virtual void Update()
{
    throw new System.NotImplementedException();
}
  • CCMoveToAction.cs

作用是将一个物体移动到目标位置,并通知人物完成

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;
    }
    // Start is called before the first frame update
    public override void Start()
    {
        
    }

    // Update is called once per frame
    public override void Update()
    {
        this.transform.position = Vector3.MoveTowards(this.transform.localPosition, target, Time.deltaTime * speed);
        if (this.transform.position == target)
        {
            // waiting for destory
            this.destory = true;
            this.callback.SSActionEvent(this);
        }
    }
}
  • CCSequenceAction.cs

CCMoveToAction 只是一个能够直线运动的模版,这个类可以将上面的类组合起来,由于model的动作都是直线运动,所以无需再创建其他类,对于人物来说他们的动作是折线运动,组合一下就好

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

    // Start is called before the first frame update
    public override void Start()
    {
        foreach (SSAction action in sequence)
        {
            action.gameobject = this.gameobject;
            action.transform = this.transform;
            action.callback = this;
            action.Start();
        }
    }

    // Update is called once per frame
    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.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);
            }
        }
    }

}
  • SSActionManager.cs
    对SSActionManager的修改,当动作结束的时候要修改船和人物的运动状态。SSActionManager.cs相当于是对外的接口,作用是控制运动状态:当人物在运动的时候,船不能动。当船动的时候,不能让人物动。

    Update完成主要任务
    (1)通过对应的动作ID,将动作加入到字典中,便于后续的操作;(2)对于每一个动作,查看他们的完成情况,如果完成了那么就加入到删除队列,否则就完成这个动作;(3)删除完成了的动作
    RunAction完成动作的添加

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

    foreach (KeyValuePair<int, SSAction> kv in actions)
    {
        SSAction ac = kv.Value;
        if (ac.destory)
        {
            waitingDelete.Add(ac.GetInstanceID());  //release action
        } else if (ac.enable){
            ac.Update();                            //update action
        }
    }

    foreach (int key in waitingDelete)
    {
       
        SSAction ac = actions[key];
        // 停止移动成功
        GameObject gb = ac.gameobject;

        FirstController fc = (FirstController)SSDirector.getInstance().firstcontroller;
        if (ac.gameobject.transform.position == fc.boat.transform.position)
        {
            fc.boat.GetComponent<EventClick>().Finished();
        }
        else if (ac.gameobject.transform.position == fc.priestOne.transform.position) 
        {
            fc.priestOne.GetComponent<EventClickPeoPle>().Finished();
        }
        else  if (ac.gameobject.transform.position == fc.priestTwo.transform.position)                                     
        {
            fc.priestTwo.GetComponent<EventClickPeoPle>().Finished();
        }
        else if (ac.gameobject.transform.position == fc.priestThree.transform.position)
        {
            fc.priestThree.GetComponent<EventClickPeoPle>().Finished();
        }
        else if (ac.gameobject.transform.position == fc.demonOne.transform.position)
        {
            fc.demonOne.GetComponent<EventClickPeoPle>().Finished();
        }
        else if (ac.gameobject.transform.position == fc.demonTwo.transform.position)
        {
            fc.demonTwo.GetComponent<EventClickPeoPle>().Finished();
        }
        else if (ac.gameobject.transform.position == fc.demonThree.transform.position)
        {
            fc.demonThree.GetComponent<EventClickPeoPle>().Finished();
        }

        actions.Remove(key);
        if (actions.Count == 0)
        {
            SSDirector.getInstance().Check();
        }

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

这是一个接口类,主要有一个函数void SSActionEvent(SSAction source)放在了controller中的interfaces中

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

对动作进行组合

public class CCActionManager : SSActionManager, ISSActionCallback
{
    public FirstController sceneController;

    // Start is called before the first frame update
    protected void Start()
    {
        sceneController = (FirstController)SSDirector.getInstance().firstcontroller;
        SSDirector.getInstance().ccam = this;
    }

    // Update is called once per frame
    protected new void Update()
    {
        base.Update();
    }

    #region ISSActionCallback implementation
    public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null)
    {

    }
    #endregion

    public void MoveTheBoatAcrossRiver()
    {
        
        if (sceneController.boat.GetComponent<EventClick>().leftSide)
        {
            CCMoveToAction MoveToLeft = CCMoveToAction.GetSSAction(new Vector3((float)1.3, (float)0.4, (float)-1), 3);
            this.RunAction(sceneController.boat, MoveToLeft, this);
        } else
        {
            CCMoveToAction MoveToRight = CCMoveToAction.GetSSAction(new Vector3((float)-1.3, (float)0.4, (float)-1), 3);
            this.RunAction(sceneController.boat, MoveToRight, this);
        }
    }

    // 如果人物处于运动状态中,则运动
    public void CheckPeoPleAndMove()
    {
        if (!sceneController.priestOne.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.priestOne);
        if (!sceneController.priestTwo.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.priestTwo);
        if (!sceneController.priestThree.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.priestThree);
        if (!sceneController.demonOne.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.demonOne);
        if (!sceneController.demonTwo.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.demonTwo);
        if (!sceneController.demonThree.GetComponent<EventClickPeoPle>().pause) PeopleMove(ref sceneController.demonThree);
    }


    private void PeopleMove(ref GameObject gobj)
    {
        CCMoveToAction destinationA, destinationB, destinationC, destinationD;
        EventClickPeoPle ecp = gobj.GetComponent<EventClickPeoPle>();
        //到岸上,或者到船上
        if (ecp.rightToBoat)
        {
            destinationA = CCMoveToAction.GetSSAction(ecp.rightFirstDestination, 3);
            destinationB = CCMoveToAction.GetSSAction(ecp.rightMidDestination, 3);
            destinationC = CCMoveToAction.GetSSAction(ecp.rightFinalDestination, 3);
            destinationD = CCMoveToAction.GetSSAction(ecp.onBoatPosition, 3);
            CCSequenceAction css = CCSequenceAction.GetSSAction(1, 0, new List<SSAction> { destinationA, destinationB, destinationC, destinationD });
            this.RunAction(gobj, css, this);
            ecp.rightToBoatFinished();
        }
        else if (ecp.boatToRight)
        {
            destinationA = CCMoveToAction.GetSSAction(ecp.rightFirstDestination, 3);
            destinationB = CCMoveToAction.GetSSAction(ecp.rightMidDestination, 3);
            destinationC = CCMoveToAction.GetSSAction(ecp.rightFinalDestination, 3);
            destinationD = CCMoveToAction.GetSSAction(ecp.rightStartPosition, 3);
            CCSequenceAction css = CCSequenceAction.GetSSAction(1, 0, new List<SSAction> { destinationC, destinationB, destinationA, destinationD });
            this.RunAction(gobj, css, this);
            ecp.boatToRightFinished();
        }
        else if (ecp.leftToBoat)
        {
            destinationA = CCMoveToAction.GetSSAction(ecp.leftFirstDestination, 3);
            destinationB = CCMoveToAction.GetSSAction(ecp.leftMidDestination, 3);
            destinationC = CCMoveToAction.GetSSAction(ecp.leftFinalDestination, 3);
            destinationD = CCMoveToAction.GetSSAction(ecp.onBoatPosition, 3);
            CCSequenceAction css = CCSequenceAction.GetSSAction(1, 0, new List<SSAction> { destinationA, destinationB, destinationC, destinationD });
            this.RunAction(gobj, css, this);
            ecp.leftToBoatFinished();
        }
        else if (ecp.boatToLeft)
        {
            destinationA = CCMoveToAction.GetSSAction(ecp.leftFirstDestination, 3);
            destinationB = CCMoveToAction.GetSSAction(ecp.leftMidDestination, 3);
            destinationC = CCMoveToAction.GetSSAction(ecp.leftFinalDestination, 3);
            destinationD = CCMoveToAction.GetSSAction(ecp.leftStartPosition, 3);
            CCSequenceAction css = CCSequenceAction.GetSSAction(1, 0, new List<SSAction> { destinationC, destinationB, destinationA, destinationD });
            this.RunAction(gobj, css, this);
            ecp.boatToLeftFinished();
        }
        //跨越河流
        if (ecp.leftAcrossRiver)
        {
            CCMoveToAction Move = CCMoveToAction.GetSSAction(ecp.onBoatPosition, 3);
            this.RunAction(gobj, Move, this);
            ecp.leftAcrossRiverFinished();
        } 
        else if (ecp.rightAcrossRiver)
        {
            CCMoveToAction Move = CCMoveToAction.GetSSAction(ecp.onBoatPosition, 3);
            this.RunAction(gobj, Move, this);
            ecp.rightAcrossRiverFinished();
        }
    }
}

以上为项目的部分代码,项目全部代码指路:https://gitee.com/zipzhou/priest-and-devil-actionseperated

视频演示链接:https://www.bilibili.com/video/BV1HQ4y1z7Rn?spm_id_from=333.999.0.0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值