在Resources——Picture文件夹中新建Battle——Player文件夹。

将Player的子弹图片资源放到Player文件夹内。这里的子弹命名非常简单,直接是数字,每个数字对应一种飞机ID。

0号子弹的Pixels Per Unit :1000

1号子弹的Pixels Per Unit :600

2号子弹的Pixels Per Unit :300

3号子弹的Pixels Per Unit :500

创建一个Prefab,命名为Bullet,在其中挂载SpriteRenderer组件。将其放在Resources——Prefab——Game文件夹内。

准备

在Player的Prefab中新建一个子节点,命名为“BulletRoot”,并调整一下它的位置,放在飞机头部,注意千万不要修改子节点的Z坐标(保持是0即可),否则它发出的子弹不会和其他飞机碰撞。

Pathes

1
2
3
4
public const string PREFAB_BULLET = PREFAB_GAME_FOLDER + "Bullet";

public const string BULLET_PICTURE_FOLDER = "Picture/Battle/";
public const string PLAYER_BULLET_FOLDER = BULLET_PICTURE_FOLDER + "Player/";

GameConfig

在GameConfig.json文件中指明子弹速度

1
"bulletSpeed": 5

Consts

在其中定义基础攻速的常量值。还有攻击的持续之间和CD时间。

1
2
3
4
5
6
7
8
9
10
11
12
/// <summary>
/// 开火的基础时间
/// </summary>
public const float FIRE_BASE_TIME = 10f;
/// <summary>
/// 开火的冷却时间
/// </summary>
public const float FIRE_CD_TIME = 1f;
/// <summary>
/// 开火的持续时间
/// </summary>
public const float FIRE_DURATION = 1f;

我们已经在InitPlane.json文件中配置了每个飞机在各个阶段当中的攻速,这里的计算公式是:

FIRE_BASE_TIME / 配置中定义的攻速值

BulletMgr

在Logic文件夹中新建Bullet文件夹,并在其中新建BulletMgr,每个场景中需要发射子弹的飞机都会动态挂载这个脚本。它挂载在飞机的子节点“BulletRoot”上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletMgr : MonoBehaviour
{
void Start()
{
CoroutineMgr.Instance.ExecuteOnce(Fire());
}
private IEnumerator Fire()
{
float time = GetFireTime();
while (true)
{
yield return new WaitForSeconds(time);
SpawnBullet();
}
}
private float GetFireTime()
{
string key = KeysUtil.GetPropertyItemKey(PropertyItem.ItemKey.value, PlaneProperty.Property.fireRate.ToString());
var ratio = DataMgr.Instance.Get<int>(key);
return Consts.FIRE_BASE_TIME / (float)ratio;
}
private void SpawnBullet()
{
var bulletGo = PoolMgr.Instance.Spawn(Pathes.PREFAB_BULLET);
var bullet = bulletGo.AddOrGet<Bullet>();
bullet.SetPos(transform);
}
}

为了实现通用性,这个脚本之后要重构

修改PlayerView

PlayerView挂载BulletMgr脚本

1
2
3
4
5
private void InitComponent()
{
//...
transform.AddCo<BulletMgr>("BulletRoot");
}

修改PoolConfig

子弹需要对象池管理,在PoolConfig中写明子弹对象池的配置

1
2
3
4
5
6
7
8
9
10
11
12
public class PoolConfig
{
public List<PoolData> PoolDatas = new List<PoolData>()
{
new PoolData
{
Path = Pathes.PREFAB_BULLET,
PreloadCount = 15,
AutoDestroy = false,
}
};
}

修改ExtendUtil

在其中添加GetOrAdd组件的拓展方法,因为对象池中的对象可能已经挂载了某些组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static T AddOrGet<T>(this Transform target) where T : Component
{
if (!target.TryGetComponent(out T t))
{
t = target.AddCo<T>();
}
return t;
}
public static T AddOrGet<T>(this GameObject target) where T : Component
{
if (!target.TryGetComponent(out T t))
{
t = target.AddComponent<T>();
}
return t;
}

Bullet

每个子弹Prefab都会动态挂载的脚本。在Logic——Bullet文件夹中新建Bullet脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using UnityEngine;

public class Bullet : MonoBehaviour, IUpdate
{
private float _speed;
private MoveComponent _moveCo;
private void Start()
{
int id = GameStateModel.Instance.SelectedPlaneId;
Sprite sprite = LoadMgr.Instance.Load<Sprite>(Pathes.PLAYER_BULLET_FOLDER + id);
GetComponent<SpriteRenderer>().sprite = sprite;

var reader = ReaderMgr.Instance.GetReader(Pathes.GAME_CONFIG);
reader["bulletSpeed"].Get<float>(value =>
{
_speed = value;
_moveCo = transform.AddCo<MoveComponent>();
_moveCo.Init(_speed);
});
}
private void OnEnable()
{
LifeCycleMgr.Instance.Add(LifeName.UPDATE, this);
}

public void UpdateFun()
{
if (_moveCo != null) { _moveCo.Move(Vector2.up); }
}
private void OnDisable()
{
LifeCycleMgr.Instance.Remove(LifeName.UPDATE, this);
}
public void SetPos(Transform parent)
{
transform.position = parent.position;
}
private void OnBecameInvisible()
{
PoolMgr.Instance.Despawn(gameObject);
}
}

对象池生成的对象默认放在场景中的PoolMgr——BulletPool对象下

为了实现通用性,这个脚本之后要重构