E神
我们将游戏场景中,动态创建的一切物体定义为实体。此模块提供管理实体和实体组的功能,如显示隐藏实体、挂接实体(如挂接武器、坐骑,或者抓起另一个实体)等。实体使用结束后可以不立刻销毁,从而等待下一次重新使用。
写在前面
我在第一篇笔记里提到过,在确定了基本的资源管理策略前提下,那么剩下的很多功能模块就是根据该功能所需要的数据行为去定制逻辑,这个过程有点类似于在框架中使用框架。这时有一个问题存在,在框架中使用框架就意味着模块与模块之间的耦合。但实际上在GF的框架的规范使用中,模块与模块之间是解耦的。若是想强行将框架中的一个综合模块在内部逻辑上独立出来,也是可行的,无非是copy代码,但我们一般不需要这样做。
理解实体
在Unity中,实体可以是在游戏世界中客观存在的一切物体。
Unity游戏运行过程中,实体的生成的数据是从内存中直接抓取的,也就是说前面还有步骤会将数据从磁盘加载进入内存,这个步骤就是前一章笔记的资源模块和Unity资源初始化流程帮我们完成的。
所以,实体本质上是游戏运行过程中内存中的一份份数据,这些数据的生成依赖于内存中存在的“镜像数据“。
那么,根据写在前面的内容,我们对实体模块的需求逐渐的清晰了起来:依赖于和资源模块的联动,对生成的实体有良好的管理能力,对实体加载和运行过程中的时间和空间开销有良好的控制。
不多嗦了直接上代码结构
EntityManager
public class EntityManager
{
private readonly Dictionary<int, EntityInfo> m_EntityInfos;//实体信息
private readonly Dictionary<string, EntityGroup> m_EntityGroups;//实体组信息
private readonly Dictionary<int, int> m_EntitiesBeingLoaded;//
private readonly HashSet<int> m_EntitiesToReleaseOnLoad;//
private readonly Queue<EntityInfo> m_RecycleQueue;//
private readonly LoadAssetCallbacks m_LoadAssetCallbacks;
private IObjectPoolManager m_ObjectPoolManager;//实体组对象池
private IResourceManager m_ResourceManager;//资源加载器
private IEntityHelper m_EntityHelper;//对接Untiy的实体辅助器
///...众多获取初始化以获取方法,管理实体组,获取实体各种信息的方法
}
EntityGroup
private class EntityGroup
{
private readonly string m_Name;//实体组名称
private readonly IEntityGroupHelper m_EntityGroupHelper;//对接unity的实体组辅助器
private readonly IObjectPool<EntityInstanceObject> m_InstancePool;//实例化对象池
private readonly GameFrameworkLinkedList<IEntity> m_Entities;//该实体组下所有实体
private LinkedListNode<IEntity> m_CachedNode;//指向一个操作实体节点
///.....初始化方法,管理实体方法,获取实体信息方法
}
DefaultEntityHelper
UGF层大多是GF层代码的方法的搬运工,配合helper使用。
public class DefaultEntityHelper : EntityHelperBase
{
private ResourceComponent m_ResourceComponent = null;
/// <summary>
/// 实例化实体。
/// </summary>
/// <param name="entityAsset">要实例化的实体资源。</param>
/// <returns>实例化后的实体。</returns>
public override object InstantiateEntity(object entityAsset)
{
return Instantiate((Object)entityAsset);
}
/// <summary>
/// 创建实体。
/// </summary>
/// <param name="entityInstance">实体实例。</param>
/// <param name="entityGroup">实体所属的实体组。</param>
/// <param name="userData">用户自定义数据。</param>
/// <returns>实体。</returns>
public override IEntity CreateEntity(object entityInstance, IEntityGroup entityGroup, object userData)
{
GameObject gameObject = entityInstance as GameObject;
if (gameObject == null)
{
Log.Error("Entity instance is invalid.");
return null;
}
Transform transform = gameObject.transform;
transform.SetParent(((MonoBehaviour)entityGroup.Helper).transform);
return gameObject.GetOrAddComponent<Entity>();
}
/// <summary>
/// 释放实体。
/// </summary>
/// <param name="entityAsset">要释放的实体资源。</param>
/// <param name="entityInstance">要释放的实体实例。</param>
public override void ReleaseEntity(object entityAsset, object entityInstance)
{
m_ResourceComponent.UnloadAsset(entityAsset);
Destroy((Object)entityInstance);
}
private void Start()
{
m_ResourceComponent = GameEntry.GetComponent<ResourceComponent>();
if (m_ResourceComponent == null)
{
Log.Fatal("Resource component is invalid.");
return;
}
}
}
}
关键方法
这个回调方法里面体现了实体模块实例化和脚本绑定的流程
private void LoadAssetSuccessCallback(string entityAssetName, object entityAsset, float duration, object userData)
{
ShowEntityInfo showEntityInfo = (ShowEntityInfo)userData;
if (showEntityInfo == null)
{
throw new GameFrameworkException("Show entity info is invalid.");
}
if (m_EntitiesToReleaseOnLoad.Contains(showEntityInfo.SerialId))
{
m_EntitiesToReleaseOnLoad.Remove(showEntityInfo.SerialId);
ReferencePool.Release(showEntityInfo);
m_EntityHelper.ReleaseEntity(entityAsset, null);
return;
}
m_EntitiesBeingLoaded.Remove(showEntityInfo.EntityId);
EntityInstanceObject entityInstanceObject = EntityInstanceObject.Create(entityAssetName, entityAsset, m_EntityHelper.InstantiateEntity(entityAsset), m_EntityHelper);
showEntityInfo.EntityGroup.RegisterEntityInstanceObject(entityInstanceObject, true);
InternalShowEntity(showEntityInfo.EntityId, entityAssetName, showEntityInfo.EntityGroup, entityInstanceObject.Target, true, duration, showEntityInfo.UserData);
ReferencePool.Release(showEntityInfo);
}
使用实践
简单使用脚本
从E神给的例子可以看出来是推荐我们通过表格对实体进行一次数据统计的
简单例子主要是为了快速实际使用一下实体模块,写法就不用那么正规的了
using UnityEngine;
using GameFramework.Resource;
using UnityGameFramework.Runtime;
public class TestNode:MonoBehaviour
{
private LoadAssetSuccessCallback loadAssetSuccessCallback;
private void Start()
{
loadAssetSuccessCallback += ShowEntity;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.N))
{
//可以利用此句先加载 在showentity
StarForce.GameEntry.Resource.LoadAsset("Assets/GameMain/KunNode/Cube.prefab", new GameFramework.Resource.LoadAssetCallbacks(loadAssetSuccessCallback));
}
if (Input.GetKeyDown(KeyCode.A))
{
//添加实体组
StarForce.GameEntry.Entity.AddEntityGroup("Cubes",10,10,10,10);
}
if (Input.GetKeyDown(KeyCode.S))
{
//不加载直接调用showEntity也是可行的 GF会帮助你自动走完加载流程
ShowEntity(null,null,0.1f,null);
}
}
void ShowEntity(string assetName, object asset, float duration, object userData)
{
StarForce.GameEntry.Entity.ShowEntity<Cubes>(0, "Assets/GameMain/KunNode/Cube.prefab", "Cubes");
}
}
/// <summary>
/// GF需要的实体逻辑脚本
/// 在运行时会动态的添加到实体身上
/// </summary>
public class Cubes:EntityLogic
{
}
Comments | NOTHING