ShieldShieldControllerBombBombController的创建略。

GameUIView中添加挂载ShieldBomb的逻辑

1
2
3
4
5
6
7
protected override void InitChild()
{
UIUtil.Get("Life").AddCo<Life>();
UIUtil.Get("Bomb").AddCo<Bomb>();
UIUtil.Get("Shield").AddCo<Shield>();
//...
}

修改ExtendUtil和UIUtil

ExtendUtil中添加更加便捷的动态挂载脚本的transform拓展方法。方便Controller动态挂载脚本。

1
2
3
4
5
6
7
8
public static T AddCo<T>(this Transform target) where T : Component
{
return target.gameObject.AddComponent<T>();
}
public static T AddCo<T>(this Transform target, string path) where T : Component
{
return target.Find(path).gameObject.AddComponent<T>();
}

然后再在GameUIController中添加动态挂载ShieldControllerBombController的逻辑

1
2
3
4
5
6
7
8
9
10
protected override void InitChild()
{
transform.AddCo<LifeController>("Life");
transform.AddCo<ShieldController>("Shield");
transform.AddCo<BombController>("Bomb");
transform.ButtonAction("Pause", () =>
{
UIMgr.Instance.Show(Pathes.PAUSE_VIEW);
});
}

修改UIUtil.UIUtilDataAddCo方法,让它返回添加的组件。

1
2
3
4
5
6
7
8
9
10
public T AddCo<T>() where T : UnityEngine.Component
{
if (Go != null)
return Go.AddComponent<T>();
else
{
Debug.LogError("当前Go为空");
return null;
}
}

GameModel

修改GameModel,添加炸弹数量和护盾数量。并添加重置方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class GameModel : NormalSingleton<GameModel> 
{
public GameModel()
{
Life = Consts.LIFE_MAX;
}
public int Life { get; set; }
public int Score { get; set; }
public int Stars { get; set; }
public int ShieldCount { get; set; }
public int BombCount { get; set; }
public void Clear()
{
m_Instance = new();
}
}

注意这里的Clear方法,使用新的instance实例来重置。

GameModel重置

我们的GameModel是普通单例类,可以重新赋值单例来重置GameModel

修改单例模板

1
2
3
4
5
public class NormalSingleton<T> where T : class,new()
{
protected static T m_Instance;
public static T Instance => m_Instance??= new T();
}

特效

护盾和炸弹按钮在可以点击的情况下会显示一个转圈特效。

我们将UI特效做成一个prefab,等到使用时动态加载这个特效。这个prefab就是一个Image组件,使用Dotween不断旋转。注意除了Image组件,UI特效的prefab还需要挂载Canvas组件,子Canvas不会触发主Canvas的Mesh重建。

将这个prefab命名为ItemEffect,放在Resources——Prefab——Game——Effect文件夹下。

Pathes中指定Prefab的路径

1
2
3
//特效文件路径
public const string PREFAB_EFFECT_FOLDER = "Prefab/Game/Effect/";
public const string EFFECT_SHIELDEFFECT = PREFAB_EFFECT_FOLDER + "ItemEffect";

ItemEffect

在Scripts——UI文件夹中新建Effect文件夹,在其中新建ItemEffect,它是挂载在特效prefab上的脚本,控制特效的表现,它比较独立,不接入UI系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using DG.Tweening;

public class ItemEffect : MonoBehaviour
{
private void OnEnable()
{
transform.DORotate(Vector3.forward * -90, 0.2f).SetEase(Ease.Linear).SetLoops(-1, LoopType.Incremental);
}
private void OnDisable()
{
transform.DOKill();
transform.localEulerAngles = Vector3.zero;
}
public void SetActive(bool active)
{
gameObject.SetActive(active);
}
}

ItemCDEffect

用来控制UICD动画

先在Consts中添加CD时长

1
public const float CD_EFFECT_TIME = 2.0f;

然后修改ItemCDEffect

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
using UnityEngine;
using UnityEngine.UI;

public class ItemCDEffect : MonoBehaviour,IUpdate
{
private Image _image;
private Image Image//使用属性,解决Start内赋值太晚的问题
{
get
{
if(_image == null)
_image = GetComponent<Image>();
return _image;
}
}
private System.Action _cdEndCallback;
public void SetShow()
{
Image.fillAmount = 0;
}
public void SetMask()
{
Image.fillAmount = 1;
}
public void StartCD(System.Action callback)
{
Image.fillAmount = 1;
LifeCycleMgr.Instance.Add(LifeName.UPDATE, this);
_cdEndCallback = callback;
}

public void UpdateFun()
{
Image.fillAmount -= Time.deltaTime / Consts.CD_EFFECT_TIME;
if (Image.fillAmount <= 0)
{
LifeCycleMgr.Instance.Remove(LifeName.UPDATE,this);
_cdEndCallback?.Invoke();
}
}
}

炸弹和护盾

添加数量更新事件

修改MsgEvent

1
2
3
4
public const int EVENT_SHIELD = 2;
public const int EVENT_BOMB = 3;
public const int EVENT_USE_SHIELD = 4;
public const int EVENT_USE_BOMB = 5;

修改Shield和Bomb脚本

Shield

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
47
48
49
50
51
52
53
54
55
public class Shield : ViewBase, IReceiver
{
private ItemCDEffect _cdEffect;
private ItemEffect _itemEffect;
private int _shieldCount;
protected override void InitChild()
{
var effect = LoadMgr.Instance.LoadPrefabAndInstantiate(Pathes.EFFECT_ITEMEFFECT, transform);
_itemEffect = effect.AddComponent<ItemEffect>();
_cdEffect = UIUtil.Get("Mask").AddCo<ItemCDEffect>();
_cdEffect.SetShow();
}
public override void Show()
{
base.Show();
MessageMgr.Instance.AddListener(MsgEvent.EVENT_SHIELD, this);
MessageMgr.Instance.AddListener(MsgEvent.EVENT_USE_SHIELD, this);
_shieldCount = GameModel.Instance.ShieldCount;
UpdateCount();
UpdateState();
}
public override void Hide()
{
base.Hide();
MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_SHIELD, this);
MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_USE_SHIELD,this);
}
private void UpdateCount()
{
UIUtil.Get("Num").SetText(GameModel.Instance.ShieldCount);
}

public void ReceiveMessage(params object[] args)
{
UpdateCount();
UpdateState();
}
private void UpdateState()
{
if(GameModel.Instance.ShieldCount == 0)
{
_cdEffect.SetMask();
_itemEffect.SetActive(false);
}
else
{
if (_shieldCount > GameModel.Instance.ShieldCount)
{
_cdEffect.StartCD(() => _itemEffect.SetActive(true));
_shieldCount = GameModel.Instance.ShieldCount;
_itemEffect.SetActive(false);
}
}
}
}

Bomb

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
47
48
49
50
51
52
53
54
public class Bomb : ViewBase, IReceiver
{
private ItemCDEffect _cdEffect;
private ItemEffect _itemEffect;
private int _bombCount;
protected override void InitChild()
{
var effect = LoadMgr.Instance.LoadPrefabAndInstantiate(Pathes.EFFECT_ITEMEFFECT, transform);
_itemEffect = effect.AddComponent<ItemEffect>();
_cdEffect = UIUtil.Get("Mask").AddCo<ItemCDEffect>();
_cdEffect.SetShow();
}
public override void Show()
{
base.Show();
MessageMgr.Instance.AddListener(MsgEvent.EVENT_BOMB, this);
MessageMgr.Instance.AddListener(MsgEvent.EVENT_USE_BOMB, this);
_bombCount = GameModel.Instance.BombCount;
UpdateCount();
UpdateState();
}
public override void Hide()
{
base.Hide();
MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_BOMB, this);
MessageMgr.Instance.RemoveListener(MsgEvent.EVENT_USE_BOMB, this);
}
private void UpdateCount()
{
UIUtil.Get("Num").SetText(GameModel.Instance.BombCount);
}
public void ReceiveMessage(params object[] args)
{
UpdateCount();
UpdateState();
}
private void UpdateState()
{
if (GameModel.Instance.BombCount == 0)
{
_cdEffect.SetMask();
_itemEffect.SetActive(false);
}
else
{
if (_bombCount > GameModel.Instance.BombCount)
{
_cdEffect.StartCD(() => _itemEffect.SetActive(true));
_bombCount = GameModel.Instance.BombCount;
_itemEffect.SetActive(false);
}
}
}
}

修改各自的Controller

ShieldController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ShieldController : ControllerBase
{
protected override void InitChild()
{
transform.ButtonAction("Icon", Shield);
}
private void Shield()
{
if(GameModel.Instance.ShieldCount > 0)
{
GameModel.Instance.ShieldCount--;
}
MessageMgr.Instance.DispatchMsg(MsgEvent.EVENT_USE_SHIELD);
}
}

BombController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BombController : ControllerBase
{
protected override void InitChild()
{
transform.ButtonAction("Icon", Click);
}
private void Click()
{
if(GameModel.Instance.BombCount > 0)
{
GameModel.Instance.BombCount--;
}
MessageMgr.Instance.DispatchMsg(MsgEvent.EVENT_USE_BOMB);
}
}