【Unity3D开发】打飞碟小游戏

本文同时发布至我的个人博客,点击进入我的个人博客阅读。本博客供技术交流与经验分享,可自由转载。转载请在评论区或私信简单通知,感谢!

游戏简介

打飞碟小游戏是一款射击类小游戏,游戏规则如下:

  • 每回合开始,玩家按“空格键”发射若干飞碟(飞碟数量随回合数增加而增加),玩家通过鼠标控制点击,击中飞碟时获得10分。
  • 若飞碟直到掉落在地还未被玩家击中,则玩家扣除10分。
  • 若玩家分数低于或等于0分时,游戏失败。
  • 玩家分数每达到一定分数(20分、40分等),则关卡升级。发射飞碟数量增多,发射速度加快。

游戏展示

设计思路

​ 这次游戏设计继续采用MVC架构,根据自顶向下的设计思路,我首先考虑最顶层的控制类GameScenceController,它应该以单实例形式存在,并且通过对其它类提供功能不同的接口来实现向类转达信息。所以从功能考虑,GameScenceController实现了IUserInterface(用户操作接口), IQueryStatus(状态查询接口), setStatus(状态设置接口), IScore(分数相关功能接口),这样我们就完成了Control层的设计。

Model层负责游戏逻辑,这次游戏逻辑较为简单,所以我用GameModel类来实现控制游戏分数、判断游戏输赢的逻辑功能。

View层较为复杂。首先需要一个场景类Scence设置飞碟的数量大小速度等属性、控制发射飞碟和隐藏飞碟,并且它要负责判断场景中的飞碟状态并告知控制类。其次,为了实现飞碟对象的重复使用,使用工厂类DiskFactory创建和回收飞碟。最后,UI部分需要提供两个类:一个UserInterface类负责处理用户的操作UI类负责实现游戏的UI视图

开发过程

(一)理解“接口”与使用接口

​ 由于之前做的更多是一些C和C++的小项目,对于C#这种纯面向对象语言中“接口”的概念总觉得没办法很好地理解,相信也有许多新手和我有相同的困惑。通过这次项目的开发经历,我觉得自己已经能较完整的理解“接口”这个概念了。下面结合代码来谈谈我个人对“接口”设计的理解:

public class GameScenceController : IUserInterface, IQueryStatus, setStatus, IScore
{
  //...
}

public class UserInterface : MonoBehaviour
{
    //...
    void Start()
    {
        //将控制类实例转化为特定的接口类型
        _uerInterface = GameScenceController.getGSController() as IUserInterface;
        _queryStatus = GameScenceController.getGSController() as IQueryStatus;
        _changeScore = GameScenceController.getGSController() as IScore;
    }
}

​ 像许多新手一样,此前我对接口的理解停留在重要性上——使用接口可以使代码可读性更强,结构更加清晰。其实,使用接口在架构设计的层面上看,有一定的必要性。就像上面的代码所示,对于UserInterface类,他需要向控制类获取游戏状态、分数的相关信息,同时需要向控制类传达用户操作信息,但是它并不需要也不应该得到设置游戏状态的功能。像这种,需要向一个类提供部分功能的情况,使用接口就能容易地实现,只要将控制类实例转化为特定的接口类型,就能限制其提供的功能。这符合面向对象设计的封装性原则。

(二)关于Unity刚体—— Rigidbody

​ 当Unity中的一个对象被添加了Rigidbody组件,它便成为了一个“刚体”,即Unity会对其应用物理引擎,于是对象便会像现实物体一样存在受到力的作用。这次开发过程在设置Disk时简单的使用了一下Rigidbody组件。

public void sendDisk(List<GameObject> usingDisks)
{
    this.usingDisks = usingDisks;
    this.Reset(round);
    for (int i = 0; i < usingDisks.Count; i++)
    {
        var localScale = usingDisks[i].transform.localScale;
        usingDisks[i].transform.localScale = new Vector3(localScale.x * diskScale, localScale.y * diskScale, localScale.z * diskScale);
        usingDisks[i].GetComponent<Renderer>().material.color = diskColor;
        usingDisks[i].transform.position = new Vector3(startPosition.x, startPosition.y + i, startPosition.z);
        //使用Rigidbody
        Rigidbody rigibody;
        rigibody = usingDisks[i].GetComponent<Rigidbody>();
        rigibody.WakeUp();
        rigibody.useGravity = true;
        rigibody.AddForce(startDiretion * Random.Range(diskSpeed * 5, diskSpeed * 8) / 5, ForceMode.Impulse);
        GameScenceController.getGSController().setScenceStatus(ScenceStatus.shooting);
    }
}

public void destroyDisk(GameObject disk)
{
    disk.GetComponent<Rigidbody>().Sleep();
    disk.GetComponent<Rigidbody>().useGravity = false;
    disk.transform.position = new Vector3(0f, -99f, 0f);
}

​ 结合这次的工厂回收机制,一个Disk使用完毕后并不是将它销毁,而是暂时隐藏等待再次使用。这样的情况下,我们将Disk暂时保存时,需要暂时让其Rigidbody不工作。原因很简单,如果不停用其Rigidbody,那么Disk会一直受到力的作用而不停的运动,这显然是会消耗游戏性能的,所以我们不希望这种情况出现。这里我选择了使用Rigidbody.Sleep()Rigidbody.WakeUp()来停用和启用Rigidbody组件。

(三)再谈工厂模式

​ 上次制作简单工厂项目时曾小试过一次工厂模式的使用,这次在项目中我同样也引入工厂模式来创建和回收飞碟。

public class DiskFactory : System.Object
{
    public GameObject diskPrefab;

    private static DiskFactory _diskFactory;
    List<GameObject> usingDisks;
    List<GameObject> uselessDisks;

    public static DiskFactory getFactory()
    {
        if (_diskFactory == null)
        {
            _diskFactory = new DiskFactory();
            _diskFactory.uselessDisks = new List<GameObject>();
            _diskFactory.usingDisks = new List<GameObject>();
        }
        return _diskFactory;
    }

    public List<GameObject> prepareDisks(int diskCount)
    {
        for (int i = 0; i < diskCount; i++)
        {
            if (uselessDisks.Count == 0)
            {
                GameObject disk = GameObject.Instantiate<GameObject>(diskPrefab);
                usingDisks.Add(disk);
            }
            else
            {
                GameObject disk = uselessDisks[0];
                uselessDisks.RemoveAt(0);
                usingDisks.Add(disk);
            }
        }
        return this.usingDisks;
    }

    public void recycleDisk(GameObject disk)
    {
        int index = usingDisks.FindIndex(x => x == disk);
        uselessDisks.Add(disk);
        usingDisks.RemoveAt(index);
    }
}

​ 这次的工厂模式实现较为完整,使用两个List——usingDisksuselessDisks来分别储存正在使用和使用完毕的Disk。需要注意的是,由于DiskFactory并非是继承MonoBehaviour的类,所以无法直接导入预设,只能在其它类中导入Disk预设再将其传给DiskFactory

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容