第五周 天空盒及牧师与魔鬼动作分离版

5 篇文章 0 订阅
4 篇文章 0 订阅

天空盒

通过组件中的skybox可以创建天空盒。老师在上课的时候提到过天空盒是一个球状贴图,通常用6面贴图来表示。但是我所使用的天空盒似乎只有一面,不知道是不是哪里弄错了。
示例图

游戏对象总结

在Unity中游戏对象十分有用,这是从很多方面来讲的。
从查找的角度来说,使用GameObject的Find方法可以很轻松地通过路径来获取其他对象。虽然对于active=false的对象无法使用,而且频繁使用会导致代码十分混乱,但还是值得考虑的方法。其他的像是FindWithTag等方法也可以。
第二个是从transform的角度上来讲,通过transform的position属性可以很轻松地对对象的位置进行修改,产生移动的效果。
第三是从结构的角度上来讲的,通过对象的transform.parent可以组织对象之间的上下关系,可以比较方便地进行各种操作,像是批量移动、失效等。

牧师与魔鬼动作分离版

参考代码:PPT上的代码
https://github.com/csr632/Priests-and-devils/tree/Improvement2
其实相比于上周而言,动作分离版的并不算太难。UML图我画的不好,就不画了。
我是通过逐步将上周代码中的动作进行分离最终完成的作业,所以可能有些地方会显得很奇怪,因为是逐步修改过来的。
* 所有的新代码放在Action.cs中。我将新定义的类分为两类,一类是动作类,包括了BaseAction(基本动作类)、LineAction(简单动作类)、SequenceAction(复杂动作类);另一类是管理类,包括BaseActionManager(基本管理类)、FirstSceneActionManager(特化管理类)
* BaseAction是动作类的基类,所有动作类都从它这里继承。它本身是从Unity的ScriptableObject中继承而来的,就我个人对老师PPT上代码的理解而言这是为了方便内存管理。LineAction实现的是单个直线型动作,而SequenceAction内置了一个列表来保存动作,实现的是复数个动作的集合。
* BaseActionManager实现的是范用的动作管理类的模板,而FirstSceneActionManager从上一个中继承而来,实现了很多与这个游戏相关的内容。
就抽象、封装等层次而言这份代码写的不是很好,有空会理一下UML图。
枚举类和接口类其实用的不是特别多,没什么提的必要:

    public enum BaseActionEventType : int { Started, Completed }
    public interface ActionCallback
    {
        void actionDone(BaseAction source);
    }

下面是动作类部分,其中我到后期debug时才明白动作类中的actionDone调用的是管理器的actionDone,然而我已经写好了其他框架,为了间接只好恢复,十分难受,望引以为鉴。

    public class BaseAction : ScriptableObject
    {
        public bool enable = true;
        public bool destroy = false;

        public GameObject gameobject { get; set; }
        public Transform transform { get; set; }
        public ActionCallback callback { get; set; }

        protected BaseAction() { }

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

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

    //简单动作类
    public class LineAction : BaseAction
    {
        public Vector3 target;
        public float speed;

        //由Unity来创建动作类,保证内存正确回收
        public static LineAction GetBaseAction(Vector3 target,float speed)
        {
            LineAction action = ScriptableObject.CreateInstance<LineAction>();
            action.target = target;
            action.speed = speed;
            return action;
        }

        public override void Update()
        {
            //Debug.Log(this.transform == null);
            //Debug.Log("In Line Update");
            //Debug.Log("aim position:"+this.transform.position.ToString());
            //Debug.Log("target:"+target.ToString() + " speed:" + speed.ToString());
            this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
            if(this.transform.position == target)
            {
                this.destroy = true;
                this.callback.actionDone(this);
            }
        }

        public override void Start()
        {
            //暂时不实现
        }
    }

    //组合动作实现,例如折线动作
    public class SequenceAction : BaseAction, ActionCallback
    {
        public List<BaseAction> sequence;
        public int repeat = 1;//1 is act once, -1 is act forever
        public int current = 0;//pointer that points to the beginning

        //同简单动作
        public static SequenceAction GetBaseAction(int repeat,int point,List<BaseAction> sequence)
        {
            SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>();
            action.repeat = repeat;
            action.sequence = sequence;
            action.current = point;
            return action;
        }

        public override void Update()
        {
            if (sequence.Count == 0) return;
            if(current<sequence.Count)//执行当前动作
            {
                sequence[current].Update();
            }
        }
        public void actionDone(BaseAction source)
        {
            //Debug.Log("actionDone in SequenceAction");
            source.destroy = false;
            this.current++;
            if (this.current >= sequence.Count)
            {
                this.current = 0;
                if ( repeat > 0 ) repeat--;
                if ( repeat == 0 )
                {
                    this.destroy = true;
                    this.callback.actionDone(this);
                }
            }
        }

        //开始的时候注册所有动作
        public override void Start()
        {
            foreach(BaseAction action in sequence)
            {
                action.gameobject = this.gameobject;
                action.transform = this.transform;
                action.callback = this;
                action.Start();
            }
        }

        void OnDestroy()
        {
            foreach(BaseAction action in sequence)
            {
                DestroyObject(action);
            }
        }
    }

基础的动作管理类,十分经典

    public class BaseActionManager : MonoBehaviour
    {
        public Dictionary<int, BaseAction> actions = new Dictionary<int, BaseAction>();
        private List<BaseAction> addList = new List<BaseAction>();
        private List<int> deleteList = new List<int>();

        //动作做完之后自动回收动作
        protected void Update()
        {
            foreach(BaseAction action in addList)
            {
                actions[action.GetInstanceID()] = action;
            }
            addList.Clear();

            foreach(KeyValuePair<int,BaseAction> keyValue in actions)
            {
                BaseAction action = keyValue.Value;

                if (action.destroy)
                {
                    deleteList.Add(action.GetInstanceID());
                }
                else if(action.enable)
                {
                    action.Update();
                }
            }

            foreach(int key in deleteList)
            {
                BaseAction action = actions[key];
                actions.Remove(key);
                DestroyObject(action);
            }
            deleteList.Clear();
        }

        public void addAction(GameObject gameobject,BaseAction action,ActionCallback manager)
        {
            action.gameobject = gameobject;
            action.transform = gameobject.transform;
            action.callback = manager;
            action.Start();
            addList.Add(action);
        }

        protected void Start()
        {
            //nothing to do in start
        }

    }

特化的本地管理类,因为方便起见我使用了很多原来的代码,移植了许多原来散步在各个类中的常量。
总体结构大概分为两部分:moveBoat和clickCharacter。moveBoat自然不用多说,clickCharacter中因为移动的位置比较麻烦还调用了characterAction这么一个函数来简化结构。
此外还实现了reset来实现重置操作,以及actionDone函数。关于actionDone函数有一些要提的。一开始的时候我认为actionDone函数不是很必要,因为直接在注册动作的时候修改状态也是可以的,但是我发现有些状态只能在动作完成之后修改,但是动作只要在状态修改之前完成都不会受到影响,为了统一起见所有相关的状态修改都放在了actionDone中,可能有一些因为迁移的缘故还残留在其他地方,会逐一修改。

    public class FirstSceneActionManager : BaseActionManager, ActionCallback
    {
        private float boatSpeed = 20f;
        private float personSpeed = 30f;
        private float resetSpeed = 100f;
        private FirstController firstController;
        private ICharacterController characterCache;
        private CoastController coastCache;
        int status = 0;//0为初始化状态,1为船的运动状态,2为人物运动状态
        readonly Vector3 frontmiddle1 = new Vector3(3.5f, 1.25f, 0);
        readonly Vector3 frontmiddle2 = new Vector3(3.5f, 0.5f, 0);
        readonly Vector3 backmiddle1 = new Vector3(8.5f, 1.25f, 0);
        readonly Vector3 backmiddle2 = new Vector3(8.5f, 0.5f, 0);

        //两个相对向量,表示相对于船的两个成员的位置(以from岸为基础)
        readonly Vector3 front = new Vector3(0.5f, 0.5f, 0);
        readonly Vector3 back = new Vector3(-0.5f, 0.5f, 0);

        public bool canClick;
        protected new void Start()
        {
            firstController = Director.getInstance().currentSceneController as FirstController;
            canClick = true;
        }

        public void moveBoat()
        {
            BoatController boat = firstController.boat;
            if (firstController.isBoatMove() == false||boat.boatEmpty()==true)
                return;

            status = 1;
            this.canClick = false;
            LineAction action = LineAction.GetBaseAction(boat.getDestination(),boatSpeed);
            this.addAction(boat.boat, action, this);
        }

        public void clickCharacter(ICharacterController character)
        {
            bool ok=firstController.isCharacterMove(character);
            BoatController boat = firstController.boat;
            if (ok == false)
                return;

            this.canClick = false;
            status = 2;
            characterCache = character;
            coastCache = firstController.getCharacterCoast();

            if (character.onBoat == false)//上船的过程
            {
                // Debug.Log("function clickCharacter ready to go on boat with parameter:" + charctrl.character.name);

                this.characterAction(coastCache,character,false);
            }
            else
            {
                // Debug.Log("function clickCharacter ready to go off boat with parameter:" + charctrl.character.name);

                this.characterAction(coastCache, character, true);
            }
            firstController.checkGameover();
        }

        //status为false时表示上船,status为true时表示下船
        public void characterAction(CoastController whichCoast,ICharacterController character,bool status)
        {
            List<BaseAction> actionList = new List<BaseAction>();
            BoatController boat = firstController.boat;
            LineAction action1=null, action2=null, targetAction=null;
            //制作序列表格actionList
            if (status == false)//上船
            {
                Vector3 relativeVec = this.getBoatEmpty(boat);
                //Debug.Log(relativeVec.ToString());
                if(boat.boatStatus == 0)
                {
                    action1 = LineAction.GetBaseAction(frontmiddle1, personSpeed);
                    action2 = LineAction.GetBaseAction(frontmiddle2, personSpeed);

                }
                else
                {
                    action1 = LineAction.GetBaseAction(backmiddle1, personSpeed);
                    action2 = LineAction.GetBaseAction(backmiddle2, personSpeed);

                }
                targetAction = LineAction.GetBaseAction(boat.boat.transform.position+relativeVec,personSpeed);
            }
            else if(status == true)
            {
                Vector3 relativeVec;
                int pos = whichCoast.getEmpty();
                if (whichCoast.coast.name == "from_coast")
                {
                    relativeVec = new Vector3(2.5f - pos, 1.25f, 0);
                }
                else
                {
                    relativeVec = new Vector3(-2.5f + pos, 1.25f, 0);
                }

                if (boat.boatStatus == 0)
                {
                    action1 = LineAction.GetBaseAction(frontmiddle2, personSpeed);
                    action2 = LineAction.GetBaseAction(frontmiddle1, personSpeed);

                }
                else
                {
                    action1 = LineAction.GetBaseAction(backmiddle2, personSpeed);
                    action2 = LineAction.GetBaseAction(backmiddle1, personSpeed);

                }
                targetAction = LineAction.GetBaseAction(whichCoast.coast.transform.position + relativeVec, personSpeed);
            }
            //注册复杂事件
            /*
            Debug.Log("register information:");
            Debug.Log("action1:" + action1.target.ToString());
            Debug.Log("action2:" + action2.target.ToString());*/
            //Debug.Log("targetAction:" + targetAction.target.ToString());
            actionList.Add(action1);
            actionList.Add(action2);
            actionList.Add(targetAction);
            SequenceAction action = SequenceAction.GetBaseAction(1, 0, actionList);
            /*
            this.addAction(character.character, action1, this);
            this.addAction(character.character, action2, this);
            this.addAction(character.character, targetAction, this);*/
            this.addAction(character.character, action, this);

        }

        public Vector3 getBoatEmpty(BoatController boat)
        {
            if(boat.boatStatus == 0)
            {
                if (boat.frontCharacter == null)
                {
                    return front;
                }

                else
                {
                    return back;
                }

            }
            else
            {
                if (boat.backCharacter == null)
                    return back;
                else
                    return front;
            }
        }

        public void reset()
        {
            BoatController boat = firstController.boat;
            LineAction action = LineAction.GetBaseAction(new Vector3(4, 0, 0), boatSpeed);
            this.addAction(boat.boat, action, this);

            ICharacterController[] characters = firstController.characters;
            for(int i=0;i<characters.Length;i++)
            {
                Vector3 relativeVec = new Vector3(2.5f - i, 1.25f, 0);
                LineAction chaAction = LineAction.GetBaseAction(firstController.fromCoast.coast.transform.position + relativeVec, resetSpeed);
                this.addAction(characters[i].character, chaAction, this);
            }
            this.canClick = true;
            this.status = 0;
        }

        public void actionDone(BaseAction source)
        {
            //Debug.Log("actionDone in FirstSceneActionManager");
            if(status == 1)
            {
                BoatController boat = firstController.boat;
                boat.switchBoatStatus();
            }
            else if(status == 2)
            {
                BoatController boat = firstController.boat;
                if (characterCache.onBoat == false)//上船
                {
                    boat.OnBoat(characterCache);
                    coastCache.OffCoast(characterCache);//离岸
                    characterCache.character.transform.parent = boat.boat.transform;
                }
                else
                {
                    boat.OffBoat(characterCache);
                    coastCache.OnCoast(characterCache, boat.boatStatus);//下船上岸
                    characterCache.character.transform.parent = null;
                }
            }
            canClick = true;
            firstController.checkGameover();
            status = 0;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值