开发了这么多辣鸡小游戏,天天胡乱调用来调用去也不像话,多少得有点可以复用的设计思路。先简单记录下自己的经验,等下再去看看其他大佬的代码~

外部调用

无论使用哪种模式,进行外部调用时不应该有差异。

  • 引用实体时,引用EntityParams
  • 调用EntityParams中的方法来访问和修改参数
  • 在EntityParams上尝试获取待调用的接口

内部实现

实体的各种逻辑应该分布在多个不同的脚本,包括且不限于

  • 攻击等行为
  • 动画控制
  • 响应输入

这些脚本统一继承EntityBehaviour,他们彼此之间通过EntityParams这个中介联系在一起。EntityParams必须挂载在实体上,EntityBehaviour则在实体或其子物体上
根据项目的复杂度、实体的复杂度、完成度需求和后续扩展的可能,应该选择不同的内部实现方法。下面设计了三种模式

  • 简单信息模式:EntityParams的修改参数方法会监控参数的变化值,状态变化时,使用信息推送。用于状态简单且性能要求不高的情况。
  • 简单委托模式:EntityParams中定义各种状态切换的公共委托,其他EntityBehavior在获取EntityParams引用的同时注册委托事件。EntityParams的修改参数方法会监控参数的变化值,状态变化时,触发委托。用于状态简单且需要优化性能的情况。
  • 状态机模式:实体使用有限状态机。有限状态机每帧直接调用EntityParams检查关心的值,在值符合条件时在状态机内部完成状态切换。用于状态复杂的实体。

EntityParams

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 System.Collections.Generic;
using UnityEngine;
using System;

public abstract class EntityParams : MonoBehaviour
{
private Dictionary<Type, EntityBehaviour> behaviours = new Dictionary<Type, EntityBehaviour>();
/// <summary>
/// 注册实体行为
/// </summary>
/// <param name="behaviour">实体行为对象</param>
public void RegisterBehaviour(EntityBehaviour behaviour)
{
behaviours.Add(behaviour.GetType(), behaviour);
}
/// <summary>
/// 获取实体行为
/// </summary>
/// <typeparam name="T">实体行为类型</typeparam>
/// <returns>需要的实体类型对象</returns>
public T GetEntityBehaviour<T>() where T : EntityBehaviour
{
return (T)behaviours[typeof(T)];
}
}

EntityBehaviour
1
2
3
4
5
6
7
8
9
10
11
using UnityEngine;

public abstract class EntityBehaviour : MonoBehaviour
{
protected EntityParams entityParams;
private void Awake()
{
entityParams = GetComponentInParent<EntityParams>();
entityParams?.RegisterBehaviour(this);
}
}