当前位置: 首页 > news >正文

【Unity】MiniGame编辑器小游戏(六)飞机大战【AirplaneWars】

更新日期:2025年6月25日。
项目源码:获取项目源码

索引

  • 飞机大战【AirplaneWars】
    • 一、游戏最终效果
    • 二、玩法简介
    • 三、正式开始
      • 1.定义游戏窗口类
      • 2.规划游戏窗口、视口区域
      • 3.飞机 Airplane
        • ①.定义飞机类
        • ②.飞机类绘制方法 OnGUI
        • ③.飞机的移动
        • ④.飞机的子弹发射口
        • ⑤.飞机发射子弹
      • 4.子弹 Bullet
        • ①.定义子弹类
        • ②.子弹类绘制方法 OnGUI
        • ③.子弹的移动
        • ④.子弹的生成
        • ⑤.子弹的销毁
      • 5.增益 Buff
        • ①.定义增益类
        • ②.增益类型
        • ③.增益类绘制方法 OnGUI
        • ④.增益的移动
        • ⑤.增益的生成
        • ⑥.增益的销毁
        • ⑦.飞机获得增益
      • 6.敌人 Enemy
        • ①.定义敌人基类
        • ②.敌人类型1
        • ③.敌人类型2
        • ③.敌人类型3
        • ④.敌人类型3发射的子弹
        • ③.敌人类型4
      • 7.碰撞检测配置
        • ①.游戏物体设置为碰撞器
        • ②.游戏窗口启用碰撞检测
        • ③.添加碰撞矩阵
      • 8.飞机的子弹攻击敌人
      • 9.飞机被敌人或敌人的子弹攻击
      • 10.获得分数
      • 11.绘制移动的背景板
      • 12.飞机开火
      • 13.生成敌人的策略
      • 14.暂停游戏、退出游戏

飞机大战【AirplaneWars】

本篇的目标是开发一个飞机大战【AirplaneWars】小游戏。

一、游戏最终效果

Unity编辑器小游戏:飞机大战

二、玩法简介

玩家使用鼠标控制飞机移动,躲避从上方飞来的敌人子弹,同时按鼠标左键A键,可以发射子弹消灭敌人。

游戏过程中还会出现一些武器Buff,飞机碰撞即可拾取,拾取后可相应获得对应的增强武器效果(比如双发弹、三发弹、加速弹等)。

飞机大战是第一个开始使用到简易物理系统(比如碰撞检测)的小游戏。

三、正式开始

1.定义游戏窗口类

首先,定义飞机大战的游戏窗口类MiniGame_AirplaneWars,其继承至MiniGameWindow【小游戏窗口基类】

    /// <summary>/// 飞机大战/// </summary>public class MiniGame_AirplaneWars : MiniGameWindow{}

2.规划游戏窗口、视口区域

通过覆写虚属性实现规划游戏视口区域大小:

        /// <summary>/// 游戏名称/// </summary>public override string Name => "飞机大战 [AirplaneWars]";/// <summary>/// 游戏窗体大小/// </summary>public override Vector2 WindowSize => new Vector2(700, 530);/// <summary>/// 游戏视口区域/// </summary>public override Rect ViewportRect => new Rect(5, 25, 500, 500);/// <summary>/// 游戏视口区域鼠标指针类型/// </summary>public override MouseCursor ViewportCursor => MouseCursor.MoveArrow;

注意:游戏窗体大小必须 > 游戏视口区域。

然后通过代码打开此游戏窗口:

        [MenuItem("MiniGame/飞机大战 [AirplaneWars]", priority = 11)]private static void Open_MiniGame_AirplaneWars(){MiniGameWindow.OpenWindow<MiniGame_AirplaneWars>();}

便可以看到游戏的窗口、视口区域如下(左侧深色凹陷区域为视口区域):

在这里插入图片描述

3.飞机 Airplane

不同于之前的几个游戏,游戏场景几乎都是清一色的方格板,飞机大战开始引入MiniGameObject对象了,其是EditorWindow的小游戏通用对象(类似于运行时的GameObject)。

MiniGameObject对象会自动绘制到游戏视口(只要是激活的,也即是activeSelf = true),其同样存在一个transform属性用于描述其所在位置与尺寸大小,只不过transform的类型为Rect

①.定义飞机类

飞机类Airplane继承至MiniGameObject

        /// <summary>/// 飞机/// </summary>public class Airplane : MiniGameObject{//默认构造函数public Airplane(MiniGameWindow window, Rect rect) : base(window, rect){//不允许此对象的位置超出游戏视口范围(飞机不能飞到屏幕外)isAllowOutOfViewport = false;}}
②.飞机类绘制方法 OnGUI

OnGUI方法中,基于飞机所在坐标transform偏移一定值,以绘制出飞机机头、翅膀等:

OnGUI方法用于描述如何绘制此MiniGameObject

        /// <summary>/// 飞机/// </summary>public class Airplane : MiniGameObject{public override void OnGUI(){base.OnGUI();//机身Rect rect = new Rect();rect.Set(transform.x + 20, transform.y, 10, 40);GUI.Box(rect, "", "button");//左翼rect.Set(transform.x, transform.y + 20, 20, 10);GUI.Box(rect, "", "button");//右翼rect.Set(transform.x + 30, transform.y + 20, 20, 10);GUI.Box(rect, "", "button");//尾翼rect.Set(transform.x + 15, transform.y + 40, 20, 10);GUI.Box(rect, "", "button");}}

然后我们在游戏开始时创建一个飞机对象,飞机就能出现在场景中了:

        private Airplane _airplane;/// <summary>/// 开始游戏/// </summary>private void StartGame(){//飞机_airplane = new Airplane(this, new Rect(ViewportRect.width * 0.5f - 25, ViewportRect.height - 55, 50, 50));}

在这里插入图片描述

虽然有点丑,但也可以理解为一种风格化。

③.飞机的移动

这里采用飞机跟随鼠标位置进行移动的方式,同样的,需要在OnGamePlayingEvent方法中编写事件相关逻辑:

        protected override void OnGamePlayingEvent(Event e, Vector2 mousePosition){base.OnGamePlayingEvent(e, mousePosition);if (e.type == EventType.Repaint){//获取当前飞机位置Rect rect = _airplane.transform;//将飞机位置限制在视口底部 -30 至 -80 之间rect.center = new Vector2(mousePosition.x, Mathf.Clamp(mousePosition.y, ViewportRect.height - 80, ViewportRect.height - 30));//设置新的飞机位置_airplane.transform = rect;//飞机每次移动后,执行碰撞检测CollisionDetection(_airplane);}}

可以看到这里飞机每次移动后都会执行碰撞检测,主动调用CollisionDetection是执行碰撞检测的一种方式。

理论上,碰撞检测存在于移动物体其他物体之间,所以在某个物体移动后才进行碰撞检测,可以有效节省性能开销。

④.飞机的子弹发射口

定义飞机的3个子弹发射口位置:

            /// <summary>/// 机头子弹发射口/// </summary>public Vector2 HeadFirePort{get{return new Vector2(transform.x + 25, transform.y);}}/// <summary>/// 左翼子弹发射口/// </summary>public Vector2 LeftFirePort{get{return new Vector2(transform.x, transform.y);}}/// <summary>/// 右翼子弹发射口/// </summary>public Vector2 RightFirePort{get{return new Vector2(transform.x + 50, transform.y);}}

这三个位置如下:

在这里插入图片描述

默认情况下,只有机头发射口可以发射子弹,通过拾取Buff(双发弹、三发弹)从而激活全部发射口。

⑤.飞机发射子弹
            /// <summary>/// 是否拥有加速弹Buff/// </summary>public bool HasSpeedUp { get; private set; } = false;/// <summary>/// 同时发射子弹数量(1,2,3)(双发弹、三发弹)/// </summary>public int BulletNumber { get; private set; } = 1;/// <summary>/// 发射子弹/// </summary>public void Fire(){if (BulletNumber == 1){GenerateBullet(HeadFirePort);}else if (BulletNumber == 2){GenerateBullet(LeftFirePort);GenerateBullet(RightFirePort);}else if (BulletNumber == 3){GenerateBullet(HeadFirePort);GenerateBullet(LeftFirePort);GenerateBullet(RightFirePort);}}

GenerateBullet为生成子弹的方法,这里我们先跳过,后续引入子弹类后再填充。

4.子弹 Bullet

①.定义子弹类

子弹类Bullet继承至MiniGameObject

        /// <summary>/// 子弹/// </summary>public class Bullet : MiniGameObject{/// <summary>/// 能量/// </summary>public int Power { get; private set; }/// <summary>/// 飞行速度/// </summary>public float Speed { get; set; }public Bullet(MiniGameWindow window, Rect rect, int power, float speed) : base(window, rect){isAllowOutOfViewport = false;Power = power;Speed = speed;}}
②.子弹类绘制方法 OnGUI

GUI风格horizontalsliderthumb能够直接绘制一个圆形,看起来正好像一个子弹:

            public override void OnGUI(){base.OnGUI();GUI.Box(transform, "", "horizontalsliderthumb");}
③.子弹的移动

子弹一经发射后,自身便会不停向前移动(在这里即是向上),所以直接在OnUpdate中调用MiniGameObject的移动方法即可:

OnUpdate为模拟帧循环回调方法,类似于MonoBehaviourUpdate方法,只不过由于EditorWindow的刷新频率很不固定,所以这个方法回调时机不太稳定。

        /// <summary>/// 子弹/// </summary>public class Bullet : MiniGameObject{//模拟帧循环public override void OnUpdate(){base.OnUpdate();MoveVertical(-Speed, true);}}

MoveVertical为垂直移动方法,第二个参数true,将在移动过程中进行碰撞检测,这也是进行碰撞检测的另一种方法。

④.子弹的生成

子弹采用对象池模式生成:

        private Queue<Bullet> _bulletPool = new Queue<Bullet>();private const int _bulletPower = 100;private const float _bulletSpeed = 0.3f;/// <summary>/// 生成子弹/// </summary>/// <param name="pos">子弹生成位置</param>private void GenerateBullet(Vector2 pos){Rect rect = new Rect(0, 0, 10, 10);rect.center = pos;Bullet bullet = null;if (_bulletPool.Count > 0){bullet = _bulletPool.Dequeue();bullet.transform = rect;//飞机拥有加速弹buff,则速度加倍bullet.Speed = _bulletSpeed * (_airplane.HasSpeedUp ? 2 : 1);}else{bullet = new Bullet(this, rect, _bulletPower, _bulletSpeed * (_airplane.HasSpeedUp ? 2 : 1));}//激活子弹bullet.activeSelf = true;}
⑤.子弹的销毁

销毁子弹时会回收到对象池

        /// <summary>/// 回收子弹/// </summary>/// <param name="bullet">子弹</param>private void RecycleBullet(Bullet bullet){bullet.activeSelf = false;_bulletPool.Enqueue(bullet);}

同时,子弹是往上移动的,所以子弹在碰到上边界后会主动销毁:

OnCollisionUpSide为物体自身碰到游戏视口上边界时回调方法,同理分别有碰到下边界、左边界、右边界。

            protected override void OnCollisionUpSide(){base.OnCollisionUpSide();RecycleBullet(this);}

在这里插入图片描述

5.增益 Buff

①.定义增益类

增益类Buff继承至MiniGameObject

        /// <summary>/// 增益/// </summary>public class Buff : MiniGameObject{/// <summary>/// 增益类型/// </summary>public int BuffType { get; private set; }/// <summary>/// 飞行速度/// </summary>public float Speed { get; private set; }public Buff(MiniGameWindow window, Rect rect, float speed) : base(window, rect){Speed = speed;}}
②.增益类型

我们暂时定义3种增益类型:

增益类型增强功效
加速弹子弹飞行速度加倍
双发弹可以同时发射2颗子弹
三发弹可以同时发射3颗子弹

在增益物品诞生后,便随机生成增益类型:

            /// <summary>/// 随机生成增益类型(1:加速弹,2:双发弹,3:三发弹)/// </summary>public void RandomType(){BuffType = Random.Range(1, 4);}
③.增益类绘制方法 OnGUI

分别根据增益的类型,在OnGUI绘制增益物品:

            public override void OnGUI(){base.OnGUI();GUI.color = Color.green;switch (BuffType){case 1:GUI.Box(transform, "", "SoloToggle");break;case 2:GUI.Box(transform, "", "BypassToggle");break;case 3:GUI.Box(transform, "", "MuteToggle");break;}GUI.color = Color.white;}

加速弹绘制出来如下:

在这里插入图片描述

双发弹绘制出来如下:

在这里插入图片描述

三发弹绘制出来如下:

在这里插入图片描述

④.增益的移动

增益物品在出现后会从上方掉落下来:

            public override void OnUpdate(){base.OnUpdate();MoveVertical(Speed);}
⑤.增益的生成

全局同时只能存在一个增益物品,所以只需要创建一个对象重复使用即可:

        private Buff _buff;private const float _buffSpeed = 0.1f;/// <summary>/// 开始游戏/// </summary>private void StartGame(){            //增益_buff = new Buff(this, new Rect(10, 10, 20, 20), _buffSpeed);_buff.activeSelf = false;}/// <summary>/// 生成增益/// </summary>private void GenerateBuff(){if (!_buff.activeSelf){_buff.transform = new Rect(Random.Range(0, ViewportRect.width - 20), -20, 20, 20);_buff.activeSelf = true;_buff.RandomType();}}
⑥.增益的销毁

销毁增益物品时,只是将其设置为activeSelf = false

        /// <summary>/// 销毁增益/// </summary>private void DestroyBuff(){_buff.activeSelf = false;}

同时,增益物品是往下移动的,所以在碰到下边界后会主动销毁:

            protected override void OnCollisionDownSide(){base.OnCollisionDownSide();DestroyBuff();}
⑦.飞机获得增益

我们可以看到增益的移动过程是不带有碰撞检测的,因为我们的飞机在移动时会主动进行碰撞检测,所以增益的碰撞检测就省了,以降低开销:

OnCollisionEnter方法为MiniGameObject物体在碰撞到其他物体时回调,从未碰撞状态进入到碰撞状态时回调1次,也即是碰撞开始。

        /// <summary>/// 飞机/// </summary>public class Airplane : MiniGameObject{public override void OnCollisionEnter(MiniGameObject other){base.OnCollisionEnter(other);//如果碰到了增益 Buffif (other is Buff){//根据增益类型,获得增强,同时销毁增益物品Buff buff = other as Buff;if (buff.BuffType == 1){HasSpeedUp = true;}else if (buff.BuffType == 2){BulletNumber = 2;}else if (buff.BuffType == 3){BulletNumber = 3;}DestroyBuff();}}}

6.敌人 Enemy

①.定义敌人基类

敌人基类Enemy继承至MiniGameObject,由于我们会设计多种敌人,所以这里先设计敌人的基类,以归纳不同敌人的相似机制。

敌人相似机制如下:

1.自动向下移动(也可能不);
2.抵达下边界后死亡(也可能不);
3.拥有指定血量,血量见底后死亡;

        /// <summary>/// 敌人基类/// </summary>public abstract class Enemy : MiniGameObject{private int _hp = 0;/// <summary>/// 自动向下移动/// </summary>public virtual bool IsAutoMove { get; } = true;/// <summary>/// 抵达下边界后死亡/// </summary>public virtual bool IsDeadToSide { get; } = true;/// <summary>/// 移动速度/// </summary>public float Speed { get; protected set; }/// <summary>/// 血量/// </summary>public int HP{get{return _hp;}set{_hp = value;if (_hp <= 0){//回收敌人RecycleEnemy(this);}}}public Enemy(MiniGameWindow window, Rect rect, float speed, int hp) : base(window, rect){Speed = speed;HP = hp;}public override void OnUpdate(){base.OnUpdate();//自动向下移动//不主动进行碰撞检测//因为敌人对象较多,主动进行碰撞检测开销过大,让飞机来进行针对敌人的碰撞检测即可if (IsAutoMove) MoveVertical(Speed);}protected override void OnCollisionDownSide(){base.OnCollisionDownSide();if (IsDeadToSide) HP = 0;}}
②.敌人类型1

敌人类型1我们决定就做成一个方块,简单且脆弱,一击必杀:

        /// <summary>/// 敌人1/// </summary>public class Enemy1 : Enemy{public Enemy1(MiniGameWindow window, Rect rect, float speed, int hp) : base(window, rect, speed, hp){}public override void OnGUI(){base.OnGUI();GUI.color = Color.yellow;GUI.Box(transform, "", "button");GUI.color = Color.white;}}

将敌人统一绘制为黄色,以进行视觉上的区分。

生成、回收敌人1(均采用对象池):

        private Queue<Enemy1> _enemy1Pool = new Queue<Enemy1>();private const float _enemy1Speed = 0.05f;//敌人1血量100,与飞机子弹的攻击力100相等private const int _enemy1HP = 100;/// <summary>/// 生成敌人1/// </summary>private void GenerateEnemy1(){Rect rect = new Rect(Random.Range(0, ViewportRect.width - 20), -20, 20, 20);Enemy1 enemy = null;if (_enemy1Pool.Count > 0){enemy = _enemy1Pool.Dequeue();enemy.transform = rect;enemy.HP = _enemy1HP;}else{enemy = new Enemy1(this, rect, _enemy1Speed, _enemy1HP);}enemy.activeSelf = true;}/// <summary>/// 回收敌人/// </summary>/// <param name="enemy">敌人</param>private void RecycleEnemy(Enemy enemy){enemy.activeSelf = false;if (enemy is Enemy1){_enemy1Pool.Enqueue(enemy as Enemy1);}}

在这里插入图片描述

③.敌人类型2

敌人类型2我们稍作加强,绘制3个方块,看起来像个飞行器:

        /// <summary>/// 敌人2/// </summary>public class Enemy2 : Enemy{public Enemy2(MiniGameWindow window, Rect rect, float speed, int hp) : base(window, rect, speed, hp){}public override void OnGUI(){base.OnGUI();GUI.color = Color.yellow;Rect rect = new Rect();rect.Set(transform.x, transform.y, 20, 20);GUI.Box(rect, "", "button");rect.Set(transform.x + 20, transform.y, 20, 20);GUI.Box(rect, "", "button");rect.Set(transform.x + 10, transform.y + 20, 20, 20);GUI.Box(rect, "", "button");GUI.color = Color.white;}}

生成、回收敌人2与敌人1同理,这里不做赘述。

只是敌人2的血量获得了增强,同时移速降低:

        private const float _enemy2Speed = 0.03f;private const int _enemy2HP = 300;

在这里插入图片描述

③.敌人类型3

敌人类型3我们继续加强,其将能够发射子弹攻击主角飞机:

        /// <summary>/// 敌人3/// </summary>public class Enemy3 : Enemy{private Vector2 _ballDis = new Vector2(0, -35);private float _ball1Rot = 0;private float _ball2Rot = 90;private float _ball3Rot = 180;private float _ball4Rot = 270;private float _timer = 0;public Enemy3(MiniGameWindow window, Rect rect, float speed, int hp) : base(window, rect, speed, hp){}public override void OnGUI(){base.OnGUI();GUI.color = Color.yellow;Rect rect = new Rect();rect.width = 20;rect.height = 20;rect.center = transform.center;GUI.Box(rect, "", "button");//周围围绕飞行着4个球体Vector2 ballPos = Quaternion.Euler(0, 0, _ball1Rot) * _ballDis;rect.width = 10;rect.height = 10;rect.center = transform.center + ballPos;GUI.Box(rect, "", "horizontalsliderthumb");ballPos = Quaternion.Euler(0, 0, _ball2Rot) * _ballDis;rect.width = 10;rect.height = 10;rect.center = transform.center + ballPos;GUI.Box(rect, "", "horizontalsliderthumb");ballPos = Quaternion.Euler(0, 0, _ball3Rot) * _ballDis;rect.width = 10;rect.height = 10;rect.center = transform.center + ballPos;GUI.Box(rect, "", "horizontalsliderthumb");ballPos = Quaternion.Euler(0, 0, _ball4Rot) * _ballDis;rect.width = 10;rect.height = 10;rect.center = transform.center + ballPos;GUI.Box(rect, "", "horizontalsliderthumb");GUI.color = Color.white;}public override void OnUpdate(){base.OnUpdate();//4个球体旋转围绕飞行_ball1Rot += 0.3f;if (_ball1Rot > 360) _ball1Rot = 0;_ball2Rot += 0.3f;if (_ball2Rot > 360) _ball2Rot = 0;_ball3Rot += 0.3f;if (_ball3Rot > 360) _ball3Rot = 0;_ball4Rot += 0.3f;if (_ball4Rot > 360) _ball4Rot = 0;if (_timer > 0){_timer -= gameWindow.DeltaTime;}else{//每隔20个时间单位(不等于秒),发射一次子弹攻击飞机_timer = 20;GenerateEnemyBullet(transform.center + new Vector2(0, 40));}}}

gameWindow.DeltaTime类似于Time.deltaTime,只不过前者只是模拟帧流逝时间,后者是运行时的精确的帧流逝时间。

生成、回收敌人3与敌人1同理,这里不做赘述。

只是敌人3的血量获得了增强,同时移速降低:

        private const float _enemy3Speed = 0.03f;private const int _enemy3HP = 1000;

请添加图片描述

④.敌人类型3发射的子弹

同样的,敌人3发射的子弹有自身的逻辑,所以其也需要单独定义:

        /// <summary>/// 敌人的子弹/// </summary>public class EnemyBullet : MiniGameObject{public float Speed { get; set; }private Vector2 _dir;public EnemyBullet(MiniGameWindow window, Rect rect, float speed) : base(window, rect){isAllowOutOfViewport = false;Speed = speed;}public override void OnGUI(){base.OnGUI();GUI.color = Color.yellow;GUI.Box(transform, "", "horizontalsliderthumb");GUI.color = Color.white;}public override void OnUpdate(){base.OnUpdate();Move(_dir);}}

Move方法为向指定方向移动,同样不进行碰撞检测。

在子弹发射之初纠正一下发射方向,使其瞄准飞机

        /// <summary>/// 生成敌人子弹/// </summary>/// <param name="pos">子弹生成位置</param>private void GenerateEnemyBullet(Vector2 pos){Rect rect = new Rect(0, 0, 10, 10);rect.center = pos;EnemyBullet bullet = null;if (_enemyBulletPool.Count > 0){bullet = _enemyBulletPool.Dequeue();bullet.transform = rect;}else{bullet = new EnemyBullet(this, rect, _enemyBulletSpeed);}bullet.activeSelf = true;bullet.CorrectDirection(_airplane, pos);}/// <summary>/// 纠正子弹方向,以瞄准飞机/// </summary>public void CorrectDirection(Airplane airplane, Vector2 startPos){_dir = airplane.transform.center - startPos;_dir = _dir.normalized * Speed;}

敌人子弹在碰到下、左、右边界时,都会主动销毁:

            protected override void OnCollisionLeftSide(){base.OnCollisionLeftSide();RecycleEnemyBullet(this);}protected override void OnCollisionRightSide(){base.OnCollisionRightSide();RecycleEnemyBullet(this);}protected override void OnCollisionDownSide(){base.OnCollisionDownSide();RecycleEnemyBullet(this);}
③.敌人类型4

敌人类型4做出一些改变:

        /// <summary>/// 敌人4/// </summary>public class Enemy4 : Enemy{//不自动向下移动public override bool IsAutoMove => false;//抵达下边界也不死亡public override bool IsDeadToSide => false;public Enemy4(MiniGameWindow window, Rect rect, float speed, int hp) : base(window, rect, speed, hp){}}

敌人4的行为就像一个激光柱一样,从上方出现后,喷发至窗口底部,然后自动消失:

            private bool _isExplosion = false;private float _expCountdown = 0;private float _expExpand = 0;private float _expNarrowing = 0;public override void OnGUI(){base.OnGUI();GUI.color = Color.red;//喷发中if (_isExplosion){Rect rect = transform;//高度不断拉长if (_expExpand < gameWindow.ViewportRect.height){rect.height = _expExpand;}else{Vector2 center = rect.center;rect.width = _expNarrowing;rect.center = center;}transform = rect;}GUI.Box(transform, "", "WhiteBackground");GUI.color = Color.white;}public override void OnUpdate(){base.OnUpdate();//喷发中if (_isExplosion){//高度拉长中if (_expExpand < gameWindow.ViewportRect.height){_expExpand += gameWindow.DeltaTime * 60;}//抵达底部,一段时间后自身死亡else{_expNarrowing -= gameWindow.DeltaTime * 20;if (_expNarrowing <= 0){HP = 0;}}}else{//未喷发中,一段时间后开始喷发if (_expCountdown > 0){_expCountdown -= gameWindow.DeltaTime;}else{_isExplosion = true;_expExpand = transform.height;_expNarrowing = 40;}}}

由于碰撞检测是基于物体的transform进行的(无需添加额外的碰撞盒),所以这里敌人4在拉高自身高度时,是设置的transform属性,使得碰撞范围同步增大。

敌人4的血量更多,同时不需要移动:

        private const int _enemy4HP = 2000;

请添加图片描述

7.碰撞检测配置

想要游戏物体间的碰撞检测生效,还需要进行如下几步配置。

①.游戏物体设置为碰撞器
    /// <summary>/// 小游戏的游戏对象/// </summary>public abstract class MiniGameObject{/// <summary>/// 是否为碰撞器/// </summary>public bool collider { get; set; } = true;}

这个属性默认为true,不需要碰撞检测的可以关闭。

②.游戏窗口启用碰撞检测

每个游戏窗口有一个变量,控制是否启用碰撞检测:

        /// <summary>/// 开始游戏/// </summary>private void StartGame(){//启用碰撞检测IsEnableCollisionDetection = true;}
③.添加碰撞矩阵

同样为了性能考虑(碰撞检测在EditorWindow中比较昂贵),只有事先已指定的碰撞层(碰撞类型)之间才支持碰撞检测:

        /// <summary>/// 开始游戏/// </summary>private void StartGame(){//启用碰撞检测IsEnableCollisionDetection = true;AddCollisionMatrix("Bullet", "Enemy");AddCollisionMatrix("Airplane", "Buff");AddCollisionMatrix("Airplane", "Enemy");AddCollisionMatrix("Airplane", "EnemyBullet");}

AddCollisionMatrix为添加碰撞层到碰撞矩阵,比如AddCollisionMatrix("Bullet", "Enemy"),将启用Bullet层与Enemy层之间的碰撞检测。

为此,每个MiniGameObject均可以设置其所在的碰撞层,默认为其类型全称:

    /// <summary>/// 小游戏的游戏对象/// </summary>public abstract class MiniGameObject{private string _collisionType;/// <summary>/// 碰撞类型(默认为类型全称)/// </summary>public virtual string CollisionType{get{if (string.IsNullOrEmpty(_collisionType)){_collisionType = GetType().FullName;}return _collisionType;}}}

8.飞机的子弹攻击敌人

飞机的子弹能够攻击敌人,但我们的敌人定义了多个类,所以为了方便统一处理,所有敌人的碰撞层均设置为Enemy

        /// <summary>/// 敌人基类/// </summary>public abstract class Enemy : MiniGameObject{public override string CollisionType => "Enemy";}

子弹的碰撞层为Bullet

        /// <summary>/// 子弹/// </summary>public class Bullet : MiniGameObject{public override string CollisionType => "Bullet";}

AddCollisionMatrix("Bullet", "Enemy")将使得子弹能够碰撞到敌人,所以在子弹的碰撞回调方法中处理:

            public override void OnCollisionEnter(MiniGameObject other){base.OnCollisionEnter(other);if (other is Enemy){//敌人扣血(other as Enemy).HP -= Power;//回收子弹RecycleBullet(this);}}

9.飞机被敌人或敌人的子弹攻击

同理,定义飞机的碰撞层为Airplane

        /// <summary>/// 飞机/// </summary>public class Airplane : MiniGameObject{public override string CollisionType => "Airplane";}

敌人子弹的碰撞层为EnemyBullet

        /// <summary>/// 敌人的子弹/// </summary>public class EnemyBullet : MiniGameObject{public override string CollisionType => "EnemyBullet";}

在飞机的碰撞回调方法中处理:

            public override void OnCollisionEnter(MiniGameObject other){base.OnCollisionEnter(other);if (other is Buff){Buff buff = other as Buff;if (buff.BuffType == 1){HasSpeedUp = true;}else if (buff.BuffType == 2){BulletNumber = 2;}else if (buff.BuffType == 3){BulletNumber = 3;}DestroyBuff();}else if (other is Enemy){//碰到敌人即游戏失败gameWindow.IsGameOvered = true;}else if (other is EnemyBullet){//碰到敌人子弹即游戏失败gameWindow.IsGameOvered = true;}}

10.获得分数

当敌人死亡时获得分数:

        /// <summary>/// 回收敌人/// </summary>/// <param name="enemy">敌人</param>private void RecycleEnemy(Enemy enemy){enemy.activeSelf = false;if (enemy is Enemy1){_enemy1Pool.Enqueue(enemy as Enemy1);_score += 1;}else if (enemy is Enemy2){_enemy2Pool.Enqueue(enemy as Enemy2);_score += 3;}else if (enemy is Enemy3){_enemy3Pool.Enqueue(enemy as Enemy3);_score += 10;}else if (enemy is Enemy4){enemy.activeSelf = false;_score += 10;}}

11.绘制移动的背景板

绘制一个向下移动的背景板,以突出飞机正在向上飞行

        protected override void OnInit(){base.OnInit();_background = EditorGUIUtility.IconContent("StateMachineEditor.Background").image;_background.wrapMode = TextureWrapMode.Repeat;}protected override void OnGameViewportGUI(){//背景板向下移动_backPos += DeltaTime * 0.02f;GUI.DrawTextureWithTexCoords(new Rect(0, 0, ViewportRect.width, ViewportRect.height), _background, new Rect(0, _backPos, ViewportRect.width / 256, ViewportRect.height / 128));base.OnGameViewportGUI();}

请添加图片描述

12.飞机开火

OnGamePlayingEvent方法中,按下鼠标左键A键为开火:

        protected override void OnGamePlayingEvent(Event e, Vector2 mousePosition){base.OnGamePlayingEvent(e, mousePosition);if (e.type == EventType.Repaint){Rect rect = _airplane.transform;rect.center = new Vector2(mousePosition.x, Mathf.Clamp(mousePosition.y, ViewportRect.height - 80, ViewportRect.height - 30));_airplane.transform = rect;CollisionDetection(_airplane);}//_fireCooling 开火冷却时间else if (e.type == EventType.MouseDown){if (e.button == 0 && _fireCooling <= 0){_airplane.Fire();_fireCooling = 1;}}else if (e.type == EventType.KeyDown){if (e.keyCode == KeyCode.A && _fireCooling <= 0){_airplane.Fire();_fireCooling = 1;}}}

13.生成敌人的策略

生成敌人的策略采用固定时间轮询的形式,每隔一段时间有几率生成指定敌人:

        private float _emitEnemyCooling = 10;protected override void OnGamePlayingUpdate(){base.OnGamePlayingUpdate();if (_timer > 0){_timer -= DeltaTime;}else{//每隔10个单位时间生成一次敌人_timer = _emitEnemyCooling;//10%概率生成一个buffif (Utility.IsTriggerProbability(10)) GenerateBuff();//80%概率生成一个敌人1if (Utility.IsTriggerProbability(80)) GenerateEnemy1();//50%概率生成一个敌人2if (Utility.IsTriggerProbability(50)) GenerateEnemy2();//10%概率生成一个敌人3if (Utility.IsTriggerProbability(10)) GenerateEnemy3();//10%概率生成一个敌人4if (Utility.IsTriggerProbability(10)) GenerateEnemy4();}}

至此,一个简单的飞机大战小游戏就完成了,试玩效果如下:飞机大战【AirplaneWars】。

14.暂停游戏、退出游戏

同俄罗斯方块。

相关文章:

  • 如何做网站动态图标网络推广公司哪里好
  • 同ip多域名做网站有哪些免费推广软件
  • 网站建设 网站推广800元做小程序网站
  • 地推拉新app推广接单平台免费萌新seo
  • 网站备案查询中心长沙网站推广智投未来
  • 微信企业号网站开发软件nba最新排名榜
  • Linux远程机器无法连接-------解决方案
  • 【GPU RAM】实时监控GPU内存分配(一)
  • 八股文——JAVA基础:说一下C++与java的区别
  • 工业级3D设计理念:如何平衡功能性与美学的矛盾点?
  • el-upload的before-upload中请求写法
  • 【Docker基础】Docker容器管理:docker pause、stop、kill区别
  • PDF24 Creator绿色便携版v11.26.0
  • 系统思考:预防重于治疗
  • CVPR-2025 | 上交拥挤无序环境下的具身导航最新基准!RoboSense:以机器人为中心的具身感知与导航大规模数据集
  • 通过pyqt5学习MVC
  • nn.Embedding 和 word2vec 的区别
  • Boosting:从理论到实践——集成学习中的偏差征服者
  • 【番外篇】TLS指纹
  • 设计模式-桥接模式、组合模式
  • 龙虎榜——20250625
  • CSP-J 题单
  • 数据赋能(323)——安全与合规——诚信原则
  • Ruoyi-Vue 升级JDK21、Springboot3、Mybatis3
  • 【GStreamer】减小延时的参数设置、从RTP中获取时间戳
  • 鸿蒙ArkUI---基础组件Tabs(Tabbar)