文章目录
零、教程目录
使用Unity 2019制作仿微信小游戏飞机大战教程已完结。
文章目录如下:
《第一篇:开始游戏界面》
《第二篇:搭建基础游戏框架》
《第三篇:战斗界面UI》
《第四篇:主角飞机序列帧动画》
《第五篇:主角飞机的飞行控制》
《第六篇:根据配置随机生成敌机》
《第七篇:主角飞机碰撞与爆炸》
《第八篇:主角飞机开炮》
《第九篇:敌机受击与爆炸》
《第十篇:敌机血量与得分》
《第十一篇:核弹掉落与全屏炸机》
《第十二篇:敌机开炮》
《第十三篇:游戏暂停、结束与重新开始》
一、前言
嗨,大家好,我是新发。相信很多人玩过微信小游戏经典的飞机大战,如下:
想重温或体验微信这款经典的飞机大战的同学可以点这里:https://gamemaker.weixin.qq.com/ide#/
在网上已经有一些人已经出了Unity
的制作教程,但是比较陈旧,里面使用了已经弃用的组件和写法,用了很陈旧的NGUI
版本,如果使用Unity 2019
或以上版本打开会各种报错,对新入门Unity
的同学不大友好。
于是,我决定写一个全新的教程:《使用Unity2019制作仿微信小游戏飞机大战》,会使用最新的写法,并且使用尽量简洁的设计与代码来完成。
本教程的工程已上传到Github
,感兴趣的同学自行下载学习。
喜欢的同学记得给个星星~
Github
地址:https://github.com/linxinfa/UnityAircraftFight
对Unity
游戏开发有任何问题的,都欢迎在评论区留言,我都会看到的,并会进行认真解答,希望能帮助到想学Unity
开发的同学,共勉。
二、本篇目标
核弹掉落与全屏炸机。
本篇的效果:
三、导入核弹图片资源
将核弹图片导入Unity
工程中。
如下:
四、制作核弹预设
将核弹图片拖到场景中,此时会以SpriteRenderer
的方式显示。
为其添加碰撞体BoxCollider2D
和刚体Rigidbody2D
组件。注意BoxCollider2D
勾选Is Trigger
,Rigidbody2D
设置Gravity Scale
为0。
将核弹保存为预设supply_bomb
,存放在Resources/Bullet
文件夹中。
五、核弹掉落与核弹数量显示
1、核弹生成器:SuperBombGenerator.cs
由于核弹出现的频率不高,所以我们就不做对象池了。
封装一个核弹生成器SuperBombGenerator.cs
,代码如下:
// SuperBombGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 核弹生成器
/// </summary>
public class SuperBombGenerator
{
public void Init()
{
ResetTargetInterval();
}
public void Update()
{
m_timer += Time.deltaTime;
if (m_timer >= m_targetInterval)
{
ResetTargetInterval();
if (Random.Range(0, 100) >= 30)
{
// 70%的概率掉落核弹
GenerateSuperBomb();
}
}
}
/// <summary>
/// 销毁根节点
/// </summary>
public void DestroyRoot()
{
if(null != m_bombRoot)
{
Object.Destroy(m_bombRoot.gameObject);
m_bombRoot = null;
}
}
/// <summary>
/// 重置目标时间间隔
/// </summary>
private void ResetTargetInterval()
{
m_timer = 0;
m_targetInterval = Random.Range(MIN_GEN_INTERVAL, MAX_GEN_INTERVAL);
}
private void GenerateSuperBomb()
{
if (null == m_bombRoot)
{
var rootObj = new GameObject("SuperBombRoot");
m_bombRoot = rootObj.transform;
}
var prefab = ResourceMgr.instance.LoadRes<GameObject>("Bullet/supply_bomb");
var obj = Object.Instantiate(prefab);
obj.transform.SetParent(m_bombRoot, false);
// 随机一个初始坐标
var randomPos = new Vector3(Random.Range(100, Screen.width - 100), Screen.height + 100, 5);
obj.transform.position = Camera.main.ScreenToWorldPoint(randomPos);
}
private float m_timer;
private float m_targetInterval = MIN_GEN_INTERVAL;
/// <summary>
/// 最小生成间隔
/// </summary>
private const float MIN_GEN_INTERVAL = 5f;
/// <summary>
/// 最大生成间隔
/// </summary>
private const float MAX_GEN_INTERVAL = 15f;
private Transform m_bombRoot;
}
2、核弹脚本:SuperBomb.cs
核弹有个下落的逻辑,创建核弹脚本SuperBomb.cs
,代码如下:
// SuperBomb.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 核弹
/// </summary>
public class SuperBomb : MonoBehaviour
{
public float dropSpeed = 5f;
Transform m_selfTrans;
private void Awake()
{
m_selfTrans = transform;
}
void Update()
{
m_selfTrans.position -= new Vector3(0, dropSpeed * Time.deltaTime, 0);
// 超过屏幕下面
if (Camera.main.WorldToScreenPoint(m_selfTrans.position).y <= -100)
{
DestroySelf();
}
}
void DestroySelf()
{
Destroy(gameObject);
}
}
3、游戏管理器调用核弹生成器
将核弹生成器SuperBombGenerator
作为游戏管理器GameMgr
的成员变量,并添加相应的调用。
// GameMgr.cs
/// <summary>
/// 开始游戏
/// </summary>
public void StartGame()
{
// ...
// 初始化核弹生成器
m_superBombGenerator.Init();
gameState = GameState.Playing;
}
public void Update()
{
if (GameState.Playing == gameState)
{
// ...
m_superBombGenerator.Update();
}
}
private SuperBombGenerator m_superBombGenerator = new SuperBombGenerator();
4、GameMgr添加核弹数量属性
我们需要有核弹数量数据,在GameMgr
添加核弹数量属性。
// GameMgr.cs
/// <summary>
/// 核弹数量
/// </summary>
public int BombCnt
{
get { return m_bombCnt; }
set
{
m_bombCnt = value;
EventDispatcher.instance.DispatchEvent(EventDef.EVENT_UPDATE_BOMB_CNT);
}
}
private int m_bombCnt;
其中EVENT_UPDATE_BOMB_CNT
是核弹数量更新事件。
// EventDef.cs
public class EventDef
{
// ...
/// <summary>
/// 更新核弹数量
/// </summary>
public const string EVENT_UPDATE_BOMB_CNT = "EVENT_UPDATE_BOMB_CNT";
}
5、核弹碰撞处理
添加一个SuperBomb
的Tag
。
将核弹预设的Tag
设置为SuperBomb
。
在PlayerAircraft
中编写核弹碰撞逻辑。
// PlayerAircraft.cs
/// <summary>
/// 碰撞检测
/// </summary>
/// <param name="other"></param>
public override void OnTriggerEnter2D(Collider2D other)
{
switch(other.tag)
{
case "Enemy":
{
// 爆炸
Explode();
}
break;
case "SuperBomb":
{
// 销毁核弹物体
Destroy(other.gameObject);
++GameMgr.instance.BombCnt;
}
break;
}
}
6、核弹数量显示
在MainGamePanel.cs
中监听核弹数量更新事件并显示核弹数量。
// MainGamePanel.cs
protected override void OnShow()
{
// ...
EventDispatcher.instance.Regist(EventDef.EVENT_UPDATE_BOMB_CNT, OnEventUpdateBombCnt);
}
protected override void OnHide()
{
// ...
EventDispatcher.instance.UnRegist(EventDef.EVENT_UPDATE_BOMB_CNT, OnEventUpdateBombCnt);
}
/// <summary>
/// 更新炸弹数量显示
/// </summary>
/// <param name="bombCnt"></param>
private void UpdateBombCntText(int bombCnt)
{
m_bombCntText.text = bombCnt.ToString();
}
private void OnEventUpdateBombCnt(params object[] args)
{
UpdateBombCntText(GameMgr.instance.BombCnt);
}
六、全屏炸机
我们需执行全屏炸机,得先知道屏幕中的所有敌机,所以需要把屏幕中的敌机存起来。
在EnemyGenerator
中将屏幕中的敌机存起来,并封装一个全屏炸机的接口。
// EnemyGenerator.cs
/// <summary>
/// 随机生成一个敌机
/// </summary>
private void RandomGenerateEnemy()
{
EnemyAircraft enemy = null;
var config = m_enemyRandom.Next();
var aircraftType = (AircraftType)config.Index;
if (m_reusePool.ContainsKey(aircraftType) && m_reusePool[aircraftType].Count > 0)
{
enemy = m_reusePool[aircraftType].Dequeue();
enemy.ActiveSelf(true);
}
else
{
enemy = (EnemyAircraft)AircraftFactory.CreateAircraft((AircraftType)config.Index);
enemy.backToPoolAction = () =>
{
// 对象回收
if (!m_reusePool.ContainsKey(aircraftType))
{
m_reusePool[aircraftType] = new Queue<EnemyAircraft>();
}
m_reusePool[aircraftType].Enqueue(enemy);
// 从屏幕中的敌机容器中移除
if (m_aliveEnemy.Contains(enemy))
m_aliveEnemy.Remove(enemy);
};
}
enemy.blood = config.Blood;
enemy.moveSpeed = Random.Range(config.MinSpeed, config.MaxSpeed);
enemy.RandomStartPos();
// 存到屏幕中的敌机容器中
if (!m_aliveEnemy.Contains(enemy))
m_aliveEnemy.Add(enemy);
}
/// <summary>
/// 全屏炸机
/// </summary>
public void KillAllEnemy()
{
for (int i = 0; i < m_aliveEnemy.Count; ++i)
{
m_aliveEnemy[i].Explode();
}
m_aliveEnemy.Clear();
}
/// <summary>
/// 清空对象池
/// </summary>
public void ClearAll()
{
m_reusePool.Clear();
m_aliveEnemy.Clear();
}
/// <summary>
/// 屏幕中的敌机
/// </summary>
private List<EnemyAircraft> m_aliveEnemy = new List<EnemyAircraft>();
然后在GameMgr
中封装一个KillAllEnemy
接口。
// GameMgr.cs
/// <summary>
/// 全屏炸机
/// </summary>
public void KillAllEnemy()
{
if (BombCnt <= 0) return;
--BombCnt;
m_enemyGenerator.KillAllEnemy();
}
最后,在游戏界面脚本MainGamePanel
中,核弹按钮的点击响应中调用这个KillAllEnemy
接口。
// MainGamePanel.cs
public override void SetUi(PrefabSlot slot)
{
// ...
slot.SetButton("BombBtn", (btn) =>
{
// 炸弹按钮被点击
GameMgr.instance.KillAllEnemy();
});
}
七、运行测试
运行Unity
,测试效果如下:
八、下篇预告
敌机开炮。