Unity3d——打飞碟游戏(终极版)

前段时间比较忙,囫囵吞枣学了个大概,作业写的也很烂,趁最近有空整理一下,认真写一次打飞碟游戏(主要是总结一下导演场记动作管理这些面向对象设计)


放个UML图
这里写图片描述

1.SSDirection,导演对象负责

  • 获取当前游戏的场景
  • 控制场景运行、切换、入栈与出栈
  • 暂停、恢复、退出
  • 管理游戏全局状态
  • 设定游戏的配置
  • 设定游戏全局视图

因为导演只能有一个,所以一般搭配着单例模式使用
现在来分析一下导演负责的内容:
导演需要控制场景,和下面要提到的场记“XXSceneController”交互,所以需要定义一个接口:

public interface ISceneController

{
    void StartGame();

    void Restart();

    void Pause();

    void Resume();

}

导演真正需要做的事情很少,只是起到一个调度的作用,所以代码也很短,在其他地方如果需要用到导演,直接利用getInstance()即可
这里写图片描述

2.SceneController,场景控制器(场记)
场记的职责:

  • 管理本次场景所有的游戏对象
  • 协调游戏对象(预制件级别)之间的通讯
  • 响应外部输入事件
  • 管理本场次的规则(裁判)
  • 杂务

    如果用这次打飞碟游戏进行说明,那就是需要在SceneController.cs中实现:

  • 运动模式切换

  • 加载资源
  • 具体实现上面接口定义的暂停、恢复、开始、重启等操作
  • 接收用户点击,并作出相应反应(回收飞碟、加分)
  • 进入到下一轮游戏

其中,像飞碟的回收、分数的增加这些,还涉及到其他的类,这时候需要在相应的类代码中实例化场景控制器,通过这个唯一的场景控制器来进行相应的操作。

3.UserGUI,人机交互部分
也就是MVC模式中的V,把后台的数据显示给User,接收User的指令传给后台。
这个就比较简单,但是如果需要考虑到用户习惯之类还是值得好好设计的。这一次我仅仅实现了几个简单的按钮,start开始游戏,restart重新开始游戏,nextRound进入下一关。
在UserGUI中,也可以利用和场景控制器交互的接口,然后在类中声明一个SceneController变量,就可以调用StartGame(),reStartGame()这些函数,比如


            if (GUI.Button(new Rect(width + 120, height, 60, 30), "Restart"))
            {

               action.Restart();

            }

            if (GUI.Button(new Rect(width + 200, height, 80, 30), "Change"))
            {

               action.ChangeActionManager();

                action.Restart();

            }

4.SSAction,动作基类
(这里直接照搬课件)
这里写图片描述
所有的动作都应该继承自SSAction。其中,ISSActionCallback指的是

public interface ISSActionCallback

{

    void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Compeleted,

                        int intParam = 0,

                        string strParam = null,

                        Object objectParam = null);

}

5.SSActionManager,动作管理基类
(这里直接照搬课件)
这里写图片描述
说明注释写得很清楚了。

6.具体的某个Action

//CCMoveToAction.cs
void Start(){
//确定起止位置、方向
}
void Update(){
//每一帧通过position使得飞碟看起来在运动
}
public static CCMoveToAction GetSSAction(float speed)

    {

        CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();

        action.Speed = speed;

        return action;

    }

7.具体的某个ActionManager

void Awake()
 {
 //加载场景控制器和factory,利用单例模式
 }    
 public CCMoveToAction MoveToAction(GameObject obj, float speed)
    {

        CCMoveToAction action = CCMoveToAction.GetSSAction(speed);

        base.RunAction(obj, action, this);

        return action;
        }

    public void moveDisk()
    {

        GameObject diskObj = factory.getDiskCountObject(factory.getDiskCount(sceneController.round));

        this.MoveToAction(diskObj, sceneController.getSpeed());

    }

说明一下MVC框架的应用。
比如用户点击“Start”按钮,UserGUI接收到用户点击事件,调用interface ISceneController中的用户接口函数void StartGame();这个函数具体在FirstSceneController中实现。场记受到导演控制,导演会确定此时应该是哪一个场记负责这个动作,因此在UserGUI中,需要SSDirector.getInstance().currentSceneController as ISceneController;然后再action.StartGame()即可完成游戏的”开始”操作。

再说一下动作管理器部分。
定义CCMoveToAction.cs,在Start()中确定飞碟出现和消失的位置、运动方向;在Update中实现每一帧更新飞碟位置,写一个函数GetSSAction()实例化一个CCMoveToAction,使得Start和Update被调用;然后在CCActionManager.cs中,实现MoveToAction函数,函数生成一个CCMoveToActions实例,然后利用这个实例调用GetSSAction,再把这个action实例作为参数传到父类SSActionManager中,利用base.RunAction真正串起整个调用过程。

至于Adapter模式。
一开始觉得很复杂,但是仔细研究一下也挺简单的。其实就是判断此时Action是CC还是Physics,然后分别调用不同的ActionManager就可以了,两个的Manager代码很相似,只是物理运动需要加上刚体处理的部分。PhysicsMoveToAction.cs中,基本思想和CC是一样的,不同的是物理运动不需要手工计算每一帧的位置,而只需要给他施加一个力就可以了,具体看代码实现即可,没有什么技术难度。

工厂模式部分。
第一次作业写的其实有一点问题,实际应该做到我使用了某一个飞碟,然后它就需要被添加到待使用的队伍中。因此需要给每一个飞碟确定不同的ID,这里我选择给每一个飞碟以不同的名字,其实就是Disk加上序号,然后用这个ID在队列中寻找相应的飞碟,进行释放或者再利用。


这次作业完成后大概算是真的了解这整个结构了吧,就游戏实现来说还是有一些小bug,比如因为刚体所以需要FixedUpdate但是切换之后CCMoveTo却不需要,如果使用会有一种卡顿的感觉,所以在SSActionManager中就要写两次,不知道这样是不是正确的。有时候还会出现诡异的短暂停留现象,不知道是因为卡机还是Update调用顺序问题。

偶尔会出现,如果没有点击飞碟,让它自由坠落到“地面”,飞碟不会消失的诡异情况,但并不是所有时候都会出现,所以不知道怎么解决。

没有用上个星期判断“落地”的代码,因为发现这个“在Camera”范围内的判断其实是有一点问题的,它除了判断角度之外,还有距离的限制,所以有时候明明还在视线范围内却已经无法点击,但是也不太好改,所以直接利用飞碟y坐标来判断是否落地了。但是具体数字的选择就很纠结,试了好几个,只能尽量找一个出错少的数字,没有一个科学的计算,感觉不太对。

除此之外还有一开始做作业时遇到的那些问题,飞碟位置随机参数的设定、飞行方向、施加的作用力这些,都没有什么依据,都是试着觉得可以了就用这个,也不知道什么时候会触发“不可以”的状态。


最后说一点感想,以前写cocos2d虽然也用到了动作管理,但是感觉没有unity3d这么麻烦,同样是CCActionManager类,直接addAction,removAction,pauseTarget……当CCNode执行runAction时,会把动作通过动作管理类的addAction函数将对象传递给CCActionManager的单例,该实例再把这个动作添加到自己的动作序列中。导演只需要->getActionManager()->pauseTarget(Node)就可以。具体的语法记得也不是很清楚了,印象中不需要自己写这么多类来进行动作的执行的操作(大概是因为直接封装好了)
查了一下资料感觉业内写游戏也并不苛求MVC框架,游戏对象之间大量的交互会让C的工作变得很繁重。当然,一定程度的代码分离是有必要的,在没有学会更有效的框架之前还是老老实实MVC吧,虽然我个人觉得这样debug真的很麻烦,特别是脚本还不能next step进行调试,在好几个类之间跳来跳去真的随时失去耐心。

游戏截图就不放了,和上次没什么区别。上次的博客链接

猜你喜欢

转载自blog.csdn.net/qq_32335095/article/details/80146932