文章目录
一、前言
嗨,大家好,我是新发。相信很多人玩过微信小游戏经典的飞机大战,如下:
想重温或体验微信这款经典的飞机大战的同学可以点这里:https://gamemaker.weixin.qq.com/ide#/
在网上已经有一些人已经出了Unity
的制作教程,但是比较陈旧,里面使用了已经弃用的组件和写法,用了很陈旧的NGUI
版本,如果使用Unity 2019
或以上版本打开会各种报错,对新入门Unity
的同学不大友好。
于是,我决定写一篇全新的教程:《使用Unity2019制作仿微信小游戏飞机大战》,会使用最新的写法,并且使用尽量简洁的设计与代码来完成。
我会根据内容点分成多篇文章来讲解,完结后会将工程上传到GitHub
,大家请耐心等待。
二、本篇目标
敌机开炮。
本篇的效果:
三、导入子弹图片资源
将子弹图片导入Unity
工程中。
如下:
四、制作敌机子弹预设
制作成预设enemy_bullet
,注意敌机子弹预设的Tag
设置为Enemy
。
制作子弹预设的方法与之前主角子弹的预设制作方法一致,参见之前这篇文章:《使用Unity 2019制作仿微信小游戏飞机大战(八):主角飞机开炮》
五、敌机子弹脚本:EnemyBullet.cs
封装敌机子弹脚本:EnemyBullet.cs
。将脚本挂到敌机子弹预设上。
代码如下:
// EnemyBullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class EnemyBullet : MonoBehaviour
{
public float speed = 1f;
/// <summary>
/// 自我旋转
/// </summary>
public bool rotateSelf = false;
/// <summary>
/// 回收委托
/// </summary>
public Action backToPoolAction;
private Transform m_selfTrans;
private GameObject m_selfGo;
private Vector3 m_targetDir;
private void Awake()
{
m_selfGo = gameObject;
m_selfTrans = transform;
}
public void SetStartPos(Vector3 pos)
{
m_selfTrans.position = pos;
}
public void SetTargetDir(Vector3 dir)
{
m_targetDir = dir.normalized;
}
public void SetAngles(Vector3 rot)
{
m_selfTrans.eulerAngles = rot;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if("Player" == collision.tag)
{
BackToPool();
}
}
private void Update()
{
// 子弹向上飞行
m_selfTrans.position += m_targetDir * speed * Time.deltaTime;
// 超过屏幕外面
var screenPos = Camera.main.WorldToScreenPoint(m_selfTrans.position);
if (screenPos.y <= -50 || screenPos.x < -50 || screenPos.x > Screen.width + 50)
{
BackToPool();
}
if(rotateSelf)
{
m_selfTrans.Rotate(0, 0, 30);
}
}
/// <summary>
/// 回收
/// </summary>
private void BackToPool()
{
ActiveSelf(false);
if (null != backToPoolAction)
{
backToPoolAction();
}
}
/// <summary>
/// 激活
/// </summary>
public void ActiveSelf(bool active)
{
m_selfGo.SetActive(active);
}
}
六、敌机子弹生成器:EnemyBulletGenerator.cs
封装一个敌机子弹生成器EnemyBulletGenerator.cs
,负责生成敌机子弹和对象池管理。
// EnemyBulletGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyBulletGenerator
{
/// <summary>
/// 根据敌机类型创建子弹
/// </summary>
/// <param name="aircraftType">敌机类型</param>
/// <param name="aircraftPos">敌机坐标</param>
public static void GenerateBulletByAircraftType(AircraftType aircraftType, Vector3 aircraftPos)
{
if (null == s_enemyBulletRoot)
{
var rootObj = new GameObject("EnemyBulletRoot");
s_enemyBulletRoot = rootObj.transform;
}
switch (aircraftType)
{
case AircraftType.Enemy2:
{
GenerateEnemy2Bullet(aircraftPos);
}
break;
case AircraftType.Enemy3:
{
GenerateEnemy3Bullet(aircraftPos);
}
break;
}
}
/// <summary>
/// 创建敌机2的子弹
/// </summary>
/// <param name="aircraftPos"></param>
private static void GenerateEnemy2Bullet(Vector3 aircraftPos)
{
var bullet = CreateBulletObj(aircraftPos);
bullet.rotateSelf = true;
var playerPos = GameMgr.instance.GetPlayerPos();
if(Vector3.zero != playerPos)
bullet.SetTargetDir(playerPos - aircraftPos);
else
bullet.SetTargetDir(-Vector3.up);
}
/// <summary>
/// 创建敌机3的子弹
/// </summary>
/// <param name="aircraftPos"></param>
private static void GenerateEnemy3Bullet(Vector3 aircraftPos)
{
for (int i = 0; i < 8; ++i)
{
var bullet = CreateBulletObj(aircraftPos);
bullet.transform.Rotate(0, 0, 180 - 90f / 2 + 90f / 7 * i);
bullet.SetTargetDir(bullet.transform.up);
bullet.rotateSelf = false;
}
}
/// <summary>
/// 创建子弹物体
/// </summary>
/// <param name="startPos"></param>
/// <returns></returns>
private static EnemyBullet CreateBulletObj(Vector3 startPos)
{
EnemyBullet bullet = null;
if (s_reusePool.Count > 0)
{
bullet = s_reusePool.Dequeue();
}
else
{
var prefab = ResourceMgr.instance.LoadRes<GameObject>("Bullet/enemy_bullet");
var obj = Object.Instantiate(prefab);
obj.transform.SetParent(s_enemyBulletRoot, false);
bullet = obj.GetComponent<EnemyBullet>();
bullet.backToPoolAction = () =>
{
s_reusePool.Enqueue(bullet);
};
}
bullet.speed = 5f;
bullet.SetStartPos(startPos);
bullet.SetAngles(Vector3.zero);
bullet.ActiveSelf(true);
return bullet;
}
/// <summary>
/// 清理
/// </summary>
public static void CLear()
{
if(null != s_enemyBulletRoot)
{
Object.Destroy(s_enemyBulletRoot.gameObject);
s_enemyBulletRoot = null;
}
s_reusePool.Clear();
}
private static Transform s_enemyBulletRoot;
private static Queue<EnemyBullet> s_reusePool = new Queue<EnemyBullet>();
}
子弹会打向主角,其中用到了GameMgr
的一个接口GetPlayerPos
。
// GameMgr.cs
/// <summary>
/// 获取主角飞机的坐标
/// </summary>
/// <returns></returns>
public Vector3 GetPlayerPos()
{
if(null != player && null != player.gameObject)
{
return player.transform.position;
}
return Vector3.zero;
}
七、调用敌机生成器
给敌机脚本EnemyAircraft
添加开炮计时器,并调用敌机生成器的接口。
// EnemyAircraft.cs
// 开炮计时器
private float m_timeToFire = -1;
public void ResetTimeToFire(float t)
{
m_timeToFire = t;
}
private void Update()
{
// ...
if (m_timeToFire > 0)
{
m_timeToFire -= Time.deltaTime;
if (m_timeToFire <= 0)
{
EnemyBulletGenerator.GenerateBulletByAircraftType(aircraftType, m_selfTrans.position);
}
}
}
在敌机生成时重置开炮计时器。
// EnemyGenerator.cs
/// <summary>
/// 随机生成一个敌机
/// </summary>
private void RandomGenerateEnemy()
{
// ...
enemy.ResetTimeToFire(1);
// ...
}
八、运行测试
运行Unity
,测试效果如下:
九、下篇预告
游戏暂停、结束与重新开始。