枪械系统实现

因为游戏设计时就有枪械切换的功能,这就需要设计枪械系统。通过枪械系统可以查询当前主角使用的是哪把枪,以及这把枪有多少枚子弹。

GunSystem纸上设计

先创建一个GunInfo,用于记录某类枪子弹数量,未来可以增加枪械名字,类型等信息。

在System文件夹中新建一个GunSystem文件夹,在其中新建GunInfo

1
2
3
4
5
6
7
8
9
using FrameWorkDesign;

namespace ShootingEditor2D
{
public class GunInfo
{
public BindableProperty<int> AmmoNum;
}
}

也是在GunSystem文件夹内,新建IGunSystem

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
using FrameWorkDesign;

namespace ShootingEditor2D
{
interface IGunSystem : ISystem
{
GunInfo CurrentGun { get; }
}

public class GunSystem : AbstractSystem, IGunSystem
{
public GunInfo CurrentGun { get; } = new GunInfo()
{
AmmoNum = new BindableProperty<int>()
{
Value = 3
}
};

protected override void OnInit()
{

}
}
}

接着在ShootingEditor2D注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using FrameWorkDesign;

namespace ShootingEditor2D
{
public class ShootingEditor2D : Architecture<ShootingEditor2D>
{
protected override void Init()
{
this.RegisterSystem<IStatSystem>(new StatSystem());
this.RegisterSystem<IGunSystem>(new GunSystem());

this.RegisterModel<IPlayerModel>(new PlayerModel());
}
}
}

添加开枪命令

在Command文件夹中新建ShootCommand

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using FrameWorkDesign;

namespace ShootingEditor2D
{
public class ShootCommand : AbstractCommand
{
public static readonly ShootCommand Instance = new ShootCommand();

protected override void OnExecute()
{
var gunSystem = this.GetSystem<IGunSystem>();
gunSystem.CurrentGun.AmmoNum.Value--;
}
}
}

因为游戏中的开枪命令属于频繁触发的命令,所以为了GC这里声明了一个单例引用。同理使用对象池也可以

注意如果对命令系统做了记录(类似于软件中的Ctrl+Z历史记录),不可以使用单例或对象池

修改Gun脚本

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
43
44
45
46
using UnityEngine;
using FrameWorkDesign;

namespace ShootingEditor2D
{
public class Gun : MonoBehaviour,IController
{
private Bullet m_Bullet;

private GunInfo m_GunInfo;//

private void Awake()
{
m_Bullet = transform.Find("Bullet").GetComponent<Bullet>();

m_GunInfo = this.GetSystem<IGunSystem>().CurrentGun;//
}

public void Shoot()
{
//var bullet = Instantiate(m_Bullet, transform);

if (m_GunInfo.AmmoNum.Value > 0)//
{
//不设置父级生成Bullet
var bullet = Instantiate(m_Bullet, m_Bullet.transform.position, m_Bullet.transform.rotation);
//将生成的Bullet的缩放继承之前Bullet的世界坐标缩放值
bullet.transform.localScale = m_Bullet.transform.lossyScale;

bullet.gameObject.SetActive(true);

this.SendCommand(ShootCommand.Instance);//使用单例触发
}
}

private void OnDestroy()//
{
m_GunInfo = null;//
}

public IArchitecture GetArchitecture()
{
return ShootingEditor2D.Interface;
}
}
}

完成UI

修改UIController脚本,顺便将框架的引用在OnDestroy里面置空

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
using UnityEngine;
using FrameWorkDesign;
using System;

namespace ShootingEditor2D
{
public class UIController : MonoBehaviour,IController
{
//...
private IGunSystem m_GunSystem;

private void Awake()
{
//...
m_GunSystem = this.GetSystem<IGunSystem>();
}

private void OnDestroy()//
{
m_StatSystem = null;
m_PlayerModel = null;
m_GunSystem = null;
}

//...

private void OnGUI()
{
//...
GUI.Label(new Rect(10, 60, 300, 100), $"子弹:{m_GunSystem.CurrentGun.AmmoNum.Value}",m_LableStyle.Value);

}
//...
}
}

一定不要忘了在Controller层引用框架的其他层级时在OnDestroy里面置空,因为框架的代码是依赖C#本身GC的,而不是Unity的GC,不置空会在内存内长时间保留。