物理系统与碰撞

作业要求

  • 改进飞碟(Hit UFO)游戏:
  • 游戏内容要求:
    • 按 adapter模式 设计图修改飞碟游戏
    • 使它同时支持物理运动与运动学(变换)运动

初始飞碟版本

在上一次作业中,游戏的UML图如下
在这里插入图片描述
关于Action的说明:

  • ISSActionCallback为动作接口
  • SSAction为动作父类,规定所有Action的属性和方法
  • DiskAction继承自SSAction,规定了一个Disk的动作,动作即飞碟的移动,由两个参数决定:受力角度和受力大小。构造函数初始化初始速度,Update函数模拟物体的坐标移动过程,当disk在画面之外(通过坐标判定),就callback通知动作做完。
  • SequenceAction函数不用改动,由于Disk是直线运动也用不到。
  • SSActionManager函数。动作管理器,管理Action List中的每个Action(不用关心是单一Action还是连续Action)。
  • FirstActionManager。这个函数封装了Action的相关类的操作,使得FirstController可以简单地调用FirstActionManager中的函数。

在之前的设计中,FirstActionManager继承SSActionManager,实现如下:

    public class FirstActionManager : SSActionManager
    {
		...
        public FirstController scene_controller;             //当前场景的场景控制器

        protected void Start()
        {
            ...
        }

        //飞碟飞行
        public void diskFly(GameObject disk, float angle, float power)
        {
        	...
        }
    }

新设计

根据要求设计后的UML如下:
在这里插入图片描述
重新设计之后,FirstActionManager中的成员变量和函数放入SSActionManager中,同时实现物理引擎的类PhysicActionManager,它们实现同一个接口IActionManager,通过这个接口FirstController能够以固定的格式调用动作/物理引擎,即完成Adapter模式。

其实更严谨的设计是在SSActionManagerIActionManagerPhysicActionManagerIActionManager之间再加一层专门的Adapter来解决diskFly函数不一样的问题(如果事先没有沟通的两个人分别实现两个部分,就可能出现这个问题),比如函数名、参数顺序、参数个数等不一样。但是在这里SSActionManagerPhysicActionManager里面的diskFly就是按照函数接口实现的,所以省去了这一步。

下面是函数的介绍:

IActionManager接口

统一SSActionManager和PhysicActionManager

namespace myGame
{
    public interface IActionManager
    {
        void diskFly(GameObject disk, float angle, float power);
    }
}
PhysicActionManager

新增加的PhysicActionManager采用Rigidbody组件的方法实现Disk的运动,而不是通过坐标变换的方法,所以这个类其实与SSAction和ISSActionCallback接口并没有什么关系。

在这个类中使用的是FixedUpdate:

  • FixedUpdate
    在固定的时间间隔内执行此方法,不受游戏帧率的影响,所以处理Rigidbody的时候最好使用FixedUpdate。
  • Update
    在每一帧的时候调用,不同设备渲染帧的时间不同,所以每次执行Update的时候相隔的时间是不一定的

使用Rigidbody实现运动:

  1. 由于想让飞碟的飞行方向不是固定向下的,所以令useGravity = false
  2. diskFly传进来的angle(float)是用于位移变化的,由于飞行方向本来就是随机的,所以为了方便,不使用angle,重新ramdom
  3. 飞碟飞行速度不同,用Rigidbody的属性drag实现,power越大,阻力越小,速度越快。但是这个drag的数值很难把控,因为它会让disk运动一段时间后停止。比如下面这样:
    使用Rigidbody物理引擎的游戏效果
    public class PhysicActionManager : MonoBehaviour, IActionManager
    {
        public DiskFlyAction fly;                            //飞碟飞行的动作
        public FirstController scene_controller;             //当前场景的场景控制器

        private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();    //将执行的动作的字典集合
        private List<SSAction> waitingAdd = new List<SSAction>();                       //等待去执行的动作列表
        private List<int> waitingDelete = new List<int>();                              //等待删除的动作的key                

        protected void Start()
        {
            scene_controller = (FirstController)Director.GetInstance().currentSceneController;
            scene_controller.actionManager = this;

        }

        protected void FixedUpdate()
        {
            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 SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed,
            int intParam = 0, string strParam = null, Object objectParam = null)
        {
        }

        //飞碟飞行
        public void diskFly(GameObject disk, float angle, float power)
        {
            disk.GetComponent<Rigidbody>().velocity = new Vector3(Random.Range(-10, 10), Random.Range(-10, 10), Random.Range(-10, 10));
            disk.GetComponent<Rigidbody>().useGravity = false;
            disk.GetComponent<Rigidbody>().drag = (30 - power) / 20;   //round越大,power越大,阻力越小,速度越快
        }
    }
SSActionManager

和上次作业的实现一样。不用封装多一个类。

    public class SSActionManager : MonoBehaviour, ISSActionCallback, IActionManager
    {
        public DiskFlyAction fly;                            //飞碟飞行的动作
        public FirstController scene_controller;             //当前场景的场景控制器

        private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();    //将执行的动作的字典集合
        private List<SSAction> waitingAdd = new List<SSAction>();                       //等待去执行的动作列表
        private List<int> waitingDelete = new List<int>();                              //等待删除的动作的key                

        protected void Start()
        {
            scene_controller = (FirstController)Director.GetInstance().currentSceneController;
            scene_controller.actionManager = this;
        }

        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 void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Completed,
            int intParam = 0, string strParam = null, Object objectParam = null)
        {
        }

        //飞碟飞行
        public void diskFly(GameObject disk, float angle, float power)
        {
            fly = DiskFlyAction.GetSSAction(angle, power); //disk.GetComponent<Disk>().direction, angle, power);
            this.RunAction(disk, fly, this);
        }
    }
FirstController

原来SSActionManager的地方改成接口,然后实例化的时候解释是哪个类

public class FirstController : MonoBehaviour, ISceneController, UserAction
{
	...
    public IActionManager actionManager;


    void Awake()
    {
       ...
        //如果是物理引擎则actionManager = gameObject.AddComponent<PhysicActionManager>() as IActionManager;
        actionManager = gameObject.AddComponent<SSActionManager>() as IActionManager;
    }
DiskFactory

生产Disk的时候加一个刚体

newDisk.AddComponent<Rigidbody>();
newDisk.GetComponent<Rigidbody>().useGravity = false;

其他函数和上次作业一样
github传送门

游戏效果没有变

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值