第一批重构,主要是物理迁移脚本和部分命名空间的调整
- 结构上迁移文件,将脚本分成 Base/Runtime/Presentation/Procedure/Editor 五大文件夹 - 调整 DataTable 和 Definition 下脚本的命名空间 - 依据 UI-5层架构设计规范 拆分原先的 UI 到 Runtime 和 Presentation 下
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edd16bf2e418bec43bc774c7fc6c60d6
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026145
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,140 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 游戏入口。
|
||||
/// </summary>
|
||||
public partial class GameEntry : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取游戏基础组件。
|
||||
/// </summary>
|
||||
public static BaseComponent Base { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取配置组件。
|
||||
/// </summary>
|
||||
public static ConfigComponent Config { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据结点组件。
|
||||
/// </summary>
|
||||
public static DataNodeComponent DataNode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据表组件。
|
||||
/// </summary>
|
||||
public static DataTableComponent DataTable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取调试组件。
|
||||
/// </summary>
|
||||
public static DebuggerComponent Debugger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取下载组件。
|
||||
/// </summary>
|
||||
public static DownloadComponent Download { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取实体组件。
|
||||
/// </summary>
|
||||
public static EntityComponent Entity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取事件组件。
|
||||
/// </summary>
|
||||
public static EventComponent Event { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件系统组件。
|
||||
/// </summary>
|
||||
public static FileSystemComponent FileSystem { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取有限状态机组件。
|
||||
/// </summary>
|
||||
public static FsmComponent Fsm { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取本地化组件。
|
||||
/// </summary>
|
||||
public static LocalizationComponent Localization { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络组件。
|
||||
/// </summary>
|
||||
public static NetworkComponent Network { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象池组件。
|
||||
/// </summary>
|
||||
public static ObjectPoolComponent ObjectPool { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取流程组件。
|
||||
/// </summary>
|
||||
public static ProcedureComponent Procedure { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源组件。
|
||||
/// </summary>
|
||||
public static ResourceComponent Resource { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取场景组件。
|
||||
/// </summary>
|
||||
public static SceneComponent Scene { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取配置组件。
|
||||
/// </summary>
|
||||
public static SettingComponent Setting { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取声音组件。
|
||||
/// </summary>
|
||||
public static SoundComponent Sound { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取界面组件。
|
||||
/// </summary>
|
||||
public static UIComponent UI { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取网络组件。
|
||||
/// </summary>
|
||||
public static WebRequestComponent WebRequest { get; private set; }
|
||||
|
||||
private static void InitBuiltinComponents()
|
||||
{
|
||||
Base = UnityGameFramework.Runtime.GameEntry.GetComponent<BaseComponent>();
|
||||
Config = UnityGameFramework.Runtime.GameEntry.GetComponent<ConfigComponent>();
|
||||
DataNode = UnityGameFramework.Runtime.GameEntry.GetComponent<DataNodeComponent>();
|
||||
DataTable = UnityGameFramework.Runtime.GameEntry.GetComponent<DataTableComponent>();
|
||||
Debugger = UnityGameFramework.Runtime.GameEntry.GetComponent<DebuggerComponent>();
|
||||
Download = UnityGameFramework.Runtime.GameEntry.GetComponent<DownloadComponent>();
|
||||
Entity = UnityGameFramework.Runtime.GameEntry.GetComponent<EntityComponent>();
|
||||
Event = UnityGameFramework.Runtime.GameEntry.GetComponent<EventComponent>();
|
||||
FileSystem = UnityGameFramework.Runtime.GameEntry.GetComponent<FileSystemComponent>();
|
||||
Fsm = UnityGameFramework.Runtime.GameEntry.GetComponent<FsmComponent>();
|
||||
Localization = UnityGameFramework.Runtime.GameEntry.GetComponent<LocalizationComponent>();
|
||||
Network = UnityGameFramework.Runtime.GameEntry.GetComponent<NetworkComponent>();
|
||||
ObjectPool = UnityGameFramework.Runtime.GameEntry.GetComponent<ObjectPoolComponent>();
|
||||
Procedure = UnityGameFramework.Runtime.GameEntry.GetComponent<ProcedureComponent>();
|
||||
Resource = UnityGameFramework.Runtime.GameEntry.GetComponent<ResourceComponent>();
|
||||
Scene = UnityGameFramework.Runtime.GameEntry.GetComponent<SceneComponent>();
|
||||
Setting = UnityGameFramework.Runtime.GameEntry.GetComponent<SettingComponent>();
|
||||
Sound = UnityGameFramework.Runtime.GameEntry.GetComponent<SoundComponent>();
|
||||
UI = UnityGameFramework.Runtime.GameEntry.GetComponent<UIComponent>();
|
||||
WebRequest = UnityGameFramework.Runtime.GameEntry.GetComponent<WebRequestComponent>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98387342fa09ebd46a2ab54100d80c2f
|
||||
timeCreated: 1528026146
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using CustomComponent;
|
||||
using Simulation;
|
||||
using StarForce;
|
||||
using UI;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏入口。
|
||||
/// </summary>
|
||||
public partial class GameEntry : MonoBehaviour
|
||||
{
|
||||
public static BuiltinDataComponent BuiltinData { get; private set; }
|
||||
|
||||
public static HPBarComponent HPBar { get; private set; }
|
||||
|
||||
public static DamageTextComponent DamageText { get; private set; }
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
public static RuntimeDebugPanelComponent RuntimeDebugPanel { get; private set; }
|
||||
#endif
|
||||
|
||||
public static EnemyManagerComponent EnemyManager { get; private set; }
|
||||
|
||||
public static SimulationWorld SimulationWorld { get; private set; }
|
||||
|
||||
public static SpriteCacheComponent SpriteCache { get; private set; }
|
||||
|
||||
public static UIRouterComponent UIRouter { get; private set; }
|
||||
|
||||
private static void InitCustomComponents()
|
||||
{
|
||||
BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
|
||||
HPBar = UnityGameFramework.Runtime.GameEntry.GetComponent<HPBarComponent>();
|
||||
DamageText = UnityGameFramework.Runtime.GameEntry.GetComponent<DamageTextComponent>();
|
||||
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
RuntimeDebugPanel = UnityGameFramework.Runtime.GameEntry.GetComponent<RuntimeDebugPanelComponent>();
|
||||
#endif
|
||||
|
||||
EnemyManager = UnityGameFramework.Runtime.GameEntry.GetComponent<EnemyManagerComponent>();
|
||||
SimulationWorld = UnityGameFramework.Runtime.GameEntry.GetComponent<SimulationWorld>();
|
||||
if (SimulationWorld == null && Base != null)
|
||||
{
|
||||
SimulationWorld = Base.gameObject.AddComponent<SimulationWorld>();
|
||||
}
|
||||
SpriteCache = UnityGameFramework.Runtime.GameEntry.GetComponent<SpriteCacheComponent>();
|
||||
UIRouter = UnityGameFramework.Runtime.GameEntry.GetComponent<UIRouterComponent>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f95ee7b97daca3f44a2f5cf743bb39ac
|
||||
timeCreated: 1528026146
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏入口。
|
||||
/// </summary>
|
||||
public partial class GameEntry : MonoBehaviour
|
||||
{
|
||||
private void Start()
|
||||
{
|
||||
InitBuiltinComponents();
|
||||
InitCustomComponents();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d09b8a3e8c0e7e4dbcfd2fa52bf69ea
|
||||
timeCreated: 1528026145
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -100
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e9af22513f1eed428b9f3fac6320543
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0da7117df4943347ad5ceaff9f70602
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026155
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework.Localization;
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
/// <summary>
|
||||
/// XML 格式的本地化辅助器。
|
||||
/// </summary>
|
||||
public class XmlLocalizationHelper : DefaultLocalizationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 解析字典。
|
||||
/// </summary>
|
||||
/// <param name="dictionaryString">要解析的字典字符串。</param>
|
||||
/// <param name="userData">用户自定义数据。</param>
|
||||
/// <returns>是否解析字典成功。</returns>
|
||||
public override bool ParseData(ILocalizationManager localizationManager, string dictionaryString, object userData)
|
||||
{
|
||||
try
|
||||
{
|
||||
string currentLanguage = GameEntry.Localization.Language.ToString();
|
||||
XmlDocument xmlDocument = new XmlDocument();
|
||||
xmlDocument.LoadXml(dictionaryString);
|
||||
XmlNode xmlRoot = xmlDocument.SelectSingleNode("Dictionaries");
|
||||
XmlNodeList xmlNodeDictionaryList = xmlRoot.ChildNodes;
|
||||
for (int i = 0; i < xmlNodeDictionaryList.Count; i++)
|
||||
{
|
||||
XmlNode xmlNodeDictionary = xmlNodeDictionaryList.Item(i);
|
||||
if (xmlNodeDictionary.Name != "Dictionary")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string language = xmlNodeDictionary.Attributes.GetNamedItem("Language").Value;
|
||||
if (language != currentLanguage)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
XmlNodeList xmlNodeStringList = xmlNodeDictionary.ChildNodes;
|
||||
for (int j = 0; j < xmlNodeStringList.Count; j++)
|
||||
{
|
||||
XmlNode xmlNodeString = xmlNodeStringList.Item(j);
|
||||
if (xmlNodeString.Name != "String")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = xmlNodeString.Attributes.GetNamedItem("Key").Value;
|
||||
string value = xmlNodeString.Attributes.GetNamedItem("Value").Value;
|
||||
if (!localizationManager.AddRawString(key, value))
|
||||
{
|
||||
Log.Warning("Can not add raw string with key '{0}' which may be invalid or duplicate.", key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Warning("Can not parse dictionary data with exception '{0}'.", exception.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44a338771c89a5348b00b9b0c13c1907
|
||||
timeCreated: 1528026156
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 592f0282ee9175f489b89f82c448c9f5
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026156
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public abstract class CSPacketBase : PacketBase
|
||||
{
|
||||
public override PacketType PacketType
|
||||
{
|
||||
get
|
||||
{
|
||||
return PacketType.ClientToServer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cb68d4d5daf52a4a9305c8d5598419f
|
||||
timeCreated: 1528026156
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public sealed class CSPacketHeader : PacketHeaderBase
|
||||
{
|
||||
public override PacketType PacketType
|
||||
{
|
||||
get
|
||||
{
|
||||
return PacketType.ClientToServer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c73747d4a0a4e14599e43950c085856
|
||||
timeCreated: 1528026156
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,280 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework;
|
||||
using GameFramework.Event;
|
||||
using GameFramework.Network;
|
||||
using ProtoBuf;
|
||||
using ProtoBuf.Meta;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public class NetworkChannelHelper : INetworkChannelHelper
|
||||
{
|
||||
private readonly Dictionary<int, Type> m_ServerToClientPacketTypes = new Dictionary<int, Type>();
|
||||
private readonly MemoryStream m_CachedStream = new MemoryStream(1024 * 8);
|
||||
private INetworkChannel m_NetworkChannel = null;
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息包头长度。
|
||||
/// </summary>
|
||||
public int PacketHeaderLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return sizeof(int);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化网络频道辅助器。
|
||||
/// </summary>
|
||||
/// <param name="networkChannel">网络频道。</param>
|
||||
public void Initialize(INetworkChannel networkChannel)
|
||||
{
|
||||
m_NetworkChannel = networkChannel;
|
||||
|
||||
// 反射注册包和包处理函数。
|
||||
Type packetBaseType = typeof(SCPacketBase);
|
||||
Type packetHandlerBaseType = typeof(PacketHandlerBase);
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
Type[] types = assembly.GetTypes();
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
{
|
||||
if (!types[i].IsClass || types[i].IsAbstract)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (types[i].BaseType == packetBaseType)
|
||||
{
|
||||
PacketBase packetBase = (PacketBase)Activator.CreateInstance(types[i]);
|
||||
Type packetType = GetServerToClientPacketType(packetBase.Id);
|
||||
if (packetType != null)
|
||||
{
|
||||
Log.Warning("Already exist packet type '{0}', check '{1}' or '{2}'?.", packetBase.Id.ToString(), packetType.Name, packetBase.GetType().Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
m_ServerToClientPacketTypes.Add(packetBase.Id, types[i]);
|
||||
}
|
||||
else if (types[i].BaseType == packetHandlerBaseType)
|
||||
{
|
||||
IPacketHandler packetHandler = (IPacketHandler)Activator.CreateInstance(types[i]);
|
||||
m_NetworkChannel.RegisterHandler(packetHandler);
|
||||
}
|
||||
}
|
||||
|
||||
GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkConnectedEventArgs.EventId, OnNetworkConnected);
|
||||
GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkClosedEventArgs.EventId, OnNetworkClosed);
|
||||
GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs.EventId, OnNetworkMissHeartBeat);
|
||||
GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkErrorEventArgs.EventId, OnNetworkError);
|
||||
GameEntry.Event.Subscribe(UnityGameFramework.Runtime.NetworkCustomErrorEventArgs.EventId, OnNetworkCustomError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭并清理网络频道辅助器。
|
||||
/// </summary>
|
||||
public void Shutdown()
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkConnectedEventArgs.EventId, OnNetworkConnected);
|
||||
GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkClosedEventArgs.EventId, OnNetworkClosed);
|
||||
GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs.EventId, OnNetworkMissHeartBeat);
|
||||
GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkErrorEventArgs.EventId, OnNetworkError);
|
||||
GameEntry.Event.Unsubscribe(UnityGameFramework.Runtime.NetworkCustomErrorEventArgs.EventId, OnNetworkCustomError);
|
||||
|
||||
m_NetworkChannel = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 准备进行连接。
|
||||
/// </summary>
|
||||
public void PrepareForConnecting()
|
||||
{
|
||||
m_NetworkChannel.Socket.ReceiveBufferSize = 1024 * 64;
|
||||
m_NetworkChannel.Socket.SendBufferSize = 1024 * 64;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送心跳消息包。
|
||||
/// </summary>
|
||||
/// <returns>是否发送心跳消息包成功。</returns>
|
||||
public bool SendHeartBeat()
|
||||
{
|
||||
m_NetworkChannel.Send(ReferencePool.Acquire<CSHeartBeat>());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化消息包。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">消息包类型。</typeparam>
|
||||
/// <param name="packet">要序列化的消息包。</param>
|
||||
/// <param name="destination">要序列化的目标流。</param>
|
||||
/// <returns>是否序列化成功。</returns>
|
||||
public bool Serialize<T>(T packet, Stream destination) where T : Packet
|
||||
{
|
||||
PacketBase packetImpl = packet as PacketBase;
|
||||
if (packetImpl == null)
|
||||
{
|
||||
Log.Warning("Packet is invalid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packetImpl.PacketType != PacketType.ClientToServer)
|
||||
{
|
||||
Log.Warning("Send packet invalid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_CachedStream.SetLength(m_CachedStream.Capacity); // 此行防止 Array.Copy 的数据无法写入
|
||||
m_CachedStream.Position = 0L;
|
||||
|
||||
CSPacketHeader packetHeader = ReferencePool.Acquire<CSPacketHeader>();
|
||||
Serializer.Serialize(m_CachedStream, packetHeader);
|
||||
ReferencePool.Release(packetHeader);
|
||||
|
||||
Serializer.SerializeWithLengthPrefix(m_CachedStream, packet, PrefixStyle.Fixed32);
|
||||
ReferencePool.Release((IReference)packet);
|
||||
|
||||
m_CachedStream.WriteTo(destination);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化消息包头。
|
||||
/// </summary>
|
||||
/// <param name="source">要反序列化的来源流。</param>
|
||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
||||
/// <returns>反序列化后的消息包头。</returns>
|
||||
public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData)
|
||||
{
|
||||
// 注意:此函数并不在主线程调用!
|
||||
customErrorData = null;
|
||||
return (IPacketHeader)RuntimeTypeModel.Default.Deserialize(source, ReferencePool.Acquire<SCPacketHeader>(), typeof(SCPacketHeader));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化消息包。
|
||||
/// </summary>
|
||||
/// <param name="packetHeader">消息包头。</param>
|
||||
/// <param name="source">要反序列化的来源流。</param>
|
||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
||||
/// <returns>反序列化后的消息包。</returns>
|
||||
public Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData)
|
||||
{
|
||||
// 注意:此函数并不在主线程调用!
|
||||
customErrorData = null;
|
||||
|
||||
SCPacketHeader scPacketHeader = packetHeader as SCPacketHeader;
|
||||
if (scPacketHeader == null)
|
||||
{
|
||||
Log.Warning("Packet header is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Packet packet = null;
|
||||
if (scPacketHeader.IsValid)
|
||||
{
|
||||
Type packetType = GetServerToClientPacketType(scPacketHeader.Id);
|
||||
if (packetType != null)
|
||||
{
|
||||
packet = (Packet)RuntimeTypeModel.Default.DeserializeWithLengthPrefix(source, ReferencePool.Acquire(packetType), packetType, PrefixStyle.Fixed32, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("Can not deserialize packet for packet id '{0}'.", scPacketHeader.Id.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("Packet header is invalid.");
|
||||
}
|
||||
|
||||
ReferencePool.Release(scPacketHeader);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private Type GetServerToClientPacketType(int id)
|
||||
{
|
||||
Type type = null;
|
||||
if (m_ServerToClientPacketTypes.TryGetValue(id, out type))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnNetworkConnected(object sender, GameEventArgs e)
|
||||
{
|
||||
UnityGameFramework.Runtime.NetworkConnectedEventArgs ne = (UnityGameFramework.Runtime.NetworkConnectedEventArgs)e;
|
||||
if (ne.NetworkChannel != m_NetworkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("Network channel '{0}' connected, local address '{1}', remote address '{2}'.", ne.NetworkChannel.Name, ne.NetworkChannel.Socket.LocalEndPoint.ToString(), ne.NetworkChannel.Socket.RemoteEndPoint.ToString());
|
||||
}
|
||||
|
||||
private void OnNetworkClosed(object sender, GameEventArgs e)
|
||||
{
|
||||
UnityGameFramework.Runtime.NetworkClosedEventArgs ne = (UnityGameFramework.Runtime.NetworkClosedEventArgs)e;
|
||||
if (ne.NetworkChannel != m_NetworkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("Network channel '{0}' closed.", ne.NetworkChannel.Name);
|
||||
}
|
||||
|
||||
private void OnNetworkMissHeartBeat(object sender, GameEventArgs e)
|
||||
{
|
||||
UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs ne = (UnityGameFramework.Runtime.NetworkMissHeartBeatEventArgs)e;
|
||||
if (ne.NetworkChannel != m_NetworkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("Network channel '{0}' miss heart beat '{1}' times.", ne.NetworkChannel.Name, ne.MissCount.ToString());
|
||||
|
||||
if (ne.MissCount < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ne.NetworkChannel.Close();
|
||||
}
|
||||
|
||||
private void OnNetworkError(object sender, GameEventArgs e)
|
||||
{
|
||||
UnityGameFramework.Runtime.NetworkErrorEventArgs ne = (UnityGameFramework.Runtime.NetworkErrorEventArgs)e;
|
||||
if (ne.NetworkChannel != m_NetworkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("Network channel '{0}' error, error code is '{1}', error message is '{2}'.", ne.NetworkChannel.Name, ne.ErrorCode.ToString(), ne.ErrorMessage);
|
||||
|
||||
ne.NetworkChannel.Close();
|
||||
}
|
||||
|
||||
private void OnNetworkCustomError(object sender, GameEventArgs e)
|
||||
{
|
||||
UnityGameFramework.Runtime.NetworkCustomErrorEventArgs ne = (UnityGameFramework.Runtime.NetworkCustomErrorEventArgs)e;
|
||||
if (ne.NetworkChannel != m_NetworkChannel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93389fd43ee07b54980956fe99a706c2
|
||||
timeCreated: 1528026156
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfccba061fa0c774bb6a7c677dc006f3
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026156
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
[Serializable, ProtoContract(Name = @"CSHeartBeat")]
|
||||
public class CSHeartBeat : CSPacketBase
|
||||
{
|
||||
public CSHeartBeat()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bae20fa3c2aff8147a2e6dfa010dd09f
|
||||
timeCreated: 1528026157
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
[Serializable, ProtoContract(Name = @"SCHeartBeat")]
|
||||
public class SCHeartBeat : SCPacketBase
|
||||
{
|
||||
public SCHeartBeat()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a60e9cafc538ac4ba7b50d7ff811796
|
||||
timeCreated: 1528026157
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework.Network;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public abstract class PacketBase : Packet, IExtensible
|
||||
{
|
||||
private IExtension m_ExtensionObject;
|
||||
|
||||
public PacketBase()
|
||||
{
|
||||
m_ExtensionObject = null;
|
||||
}
|
||||
|
||||
public abstract PacketType PacketType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IExtension IExtensible.GetExtensionObject(bool createIfMissing)
|
||||
{
|
||||
return Extensible.GetExtensionObject(ref m_ExtensionObject, createIfMissing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 527942a1a30fe9c4bb08a3392208d012
|
||||
timeCreated: 1528026157
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 892cfad5ac0cd9d41a4679ac3a607e2a
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026157
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework.Network;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public class SCHeartBeatHandler : PacketHandlerBase
|
||||
{
|
||||
public override int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Handle(object sender, Packet packet)
|
||||
{
|
||||
SCHeartBeat packetImpl = (SCHeartBeat)packet;
|
||||
Log.Info("Receive packet '{0}'.", packetImpl.Id.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28aefb1e04aa1e0478d3a457a949bd68
|
||||
timeCreated: 1528026157
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework.Network;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public abstract class PacketHandlerBase : IPacketHandler
|
||||
{
|
||||
public abstract int Id
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract void Handle(object sender, Packet packet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51e8e8623bdfcd541a121039aac975ee
|
||||
timeCreated: 1528026157
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework;
|
||||
using GameFramework.Network;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public abstract class PacketHeaderBase : IPacketHeader, IReference
|
||||
{
|
||||
public abstract PacketType PacketType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int PacketLength
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return PacketType != PacketType.Undefined && Id > 0 && PacketLength >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Id = 0;
|
||||
PacketLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21eed1206493a8640a380bca665dbabe
|
||||
timeCreated: 1528026157
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public enum PacketType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 未定义。
|
||||
/// </summary>
|
||||
Undefined = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 客户端发往服务器的包。
|
||||
/// </summary>
|
||||
ClientToServer,
|
||||
|
||||
/// <summary>
|
||||
/// 服务器发往客户端的包。
|
||||
/// </summary>
|
||||
ServerToClient,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1da4fb1de540044ca7c949941e1dd02
|
||||
timeCreated: 1528026157
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public abstract class SCPacketBase : PacketBase
|
||||
{
|
||||
public override PacketType PacketType
|
||||
{
|
||||
get
|
||||
{
|
||||
return PacketType.ServerToClient;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a21b258aa45363b4e83261f25c1b4e55
|
||||
timeCreated: 1528026158
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public sealed class SCPacketHeader : PacketHeaderBase
|
||||
{
|
||||
public override PacketType PacketType
|
||||
{
|
||||
get
|
||||
{
|
||||
return PacketType.ServerToClient;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16c16cfb1455603479adbdae83002fed
|
||||
timeCreated: 1528026158
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ecda907b9217e5499e31cb81a154ff4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using SepCore.Definition;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class AbsorbComponent : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private float _absorbRangeBase = 3f;
|
||||
[SerializeField] private float _absorbSpeed = 15f;
|
||||
[SerializeField] private float _collectDistance = 0.35f;
|
||||
[SerializeField] private int _maxDetectCount = 64;
|
||||
[SerializeField] private LayerMask _detectLayerMask = ~0;
|
||||
|
||||
private Player _player;
|
||||
private Transform _target;
|
||||
private StatComponent _statComponent;
|
||||
private Collider[] _buffer;
|
||||
|
||||
private StatProperty _absorbRangeStat;
|
||||
private Action<StatModifier, bool> _absorbRangeStatCallback;
|
||||
|
||||
private float AbsorbRange => Mathf.Max(0f, (_absorbRangeBase + _absorbRangeStat.Value) * _absorbRangeStat.Percent);
|
||||
|
||||
public void OnInit(Player player, StatComponent statComponent = null)
|
||||
{
|
||||
_player = player;
|
||||
_target = player != null ? player.CachedTransform : null;
|
||||
_statComponent = statComponent;
|
||||
_buffer = new Collider[Mathf.Max(1, _maxDetectCount)];
|
||||
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_absorbRangeStat = _statComponent.GetStat(StatType.AbsorbRange);
|
||||
_absorbRangeStatCallback = (modifier, isApply) =>
|
||||
_statComponent.UpdateStat(_absorbRangeStat, modifier, isApply);
|
||||
_statComponent.Subscribe(StatType.AbsorbRange, _absorbRangeStatCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
_absorbRangeStat = new StatProperty();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
if (_player == null || !_player.Available || _target == null || _buffer == null) return;
|
||||
|
||||
int hitCount = Physics.OverlapSphereNonAlloc(
|
||||
_target.position,
|
||||
AbsorbRange,
|
||||
_buffer,
|
||||
_detectLayerMask,
|
||||
QueryTriggerInteraction.Collide
|
||||
);
|
||||
|
||||
for (int i = 0; i < hitCount; i++)
|
||||
{
|
||||
Collider hit = _buffer[i];
|
||||
if (hit == null) continue;
|
||||
|
||||
CoinEntity coin = hit.GetComponent<CoinEntity>();
|
||||
if (coin != null && coin.Available)
|
||||
{
|
||||
AbsorbDrop(coin.CachedTransform, elapseSeconds);
|
||||
coin.TryCollect(_player, _collectDistance);
|
||||
continue;
|
||||
}
|
||||
|
||||
ExpEntity exp = hit.GetComponent<ExpEntity>();
|
||||
if (exp != null && exp.Available)
|
||||
{
|
||||
AbsorbDrop(exp.CachedTransform, elapseSeconds);
|
||||
exp.TryCollect(_player, _collectDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_statComponent.Unsubscribe(StatType.AbsorbRange, _absorbRangeStatCallback);
|
||||
_absorbRangeStatCallback = null;
|
||||
}
|
||||
|
||||
_statComponent = null;
|
||||
_target = null;
|
||||
_player = null;
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
private void AbsorbDrop(Transform drop, float deltaTime)
|
||||
{
|
||||
if (drop == null || _target == null) return;
|
||||
drop.position = Vector3.MoveTowards(drop.position, _target.position, _absorbSpeed * deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46f46350fed64d8389ebb4ae72b65796
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Entity;
|
||||
using Entity.Weapon;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class AttackComponent : MonoBehaviour
|
||||
{
|
||||
private bool _isAttacking = false;
|
||||
private List<WeaponBase> _weapons;
|
||||
|
||||
public void SetIsAttacking(bool isAttacking) => _isAttacking = isAttacking;
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapsedTime, float realElapseSeconds)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2b35b5ae4185cb4cab2d84b0569db3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,104 @@
|
||||
using System.Collections.Generic;
|
||||
using SepCore.Definition;
|
||||
using Entity.Weapon;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class BackpackComponent : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// 武器槽容量,由DRRole决定。
|
||||
/// </summary>
|
||||
private int _weaponCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// 玩家类引用。
|
||||
/// </summary>
|
||||
private Player _player;
|
||||
|
||||
/// <summary>
|
||||
/// 武器列表。
|
||||
/// </summary>
|
||||
private List<WeaponBase> _weapons;
|
||||
|
||||
/// <summary>
|
||||
/// 道具列表。
|
||||
/// </summary>
|
||||
private List<PropItem> _props;
|
||||
|
||||
/// <summary>
|
||||
/// 玩家状态组件引用,用于道具效果的应用。
|
||||
/// </summary>
|
||||
private StatComponent _statComponent;
|
||||
|
||||
/// <summary>
|
||||
/// 武器装备槽,在 Editor 中进行配置,表示武器在世界中的显示位置。
|
||||
/// </summary>
|
||||
[SerializeField] private Transform[] _weaponSlots;
|
||||
|
||||
public IReadOnlyList<WeaponBase> Weapons => _weapons;
|
||||
public IReadOnlyList<PropItem> Props => _props;
|
||||
public int WeaponCapacity => _weaponCapacity;
|
||||
|
||||
public void OnInit(Player player, int weaponCapacity)
|
||||
{
|
||||
_weaponCapacity = weaponCapacity;
|
||||
_player = player;
|
||||
|
||||
_weapons = new List<WeaponBase>(_weaponCapacity);
|
||||
_props = new List<PropItem>();
|
||||
_statComponent = GetComponent<StatComponent>();
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_weapons.Clear();
|
||||
_props.Clear();
|
||||
}
|
||||
|
||||
public bool AttachWeapon(WeaponBase weapon)
|
||||
{
|
||||
if (_weapons.Count == _weaponCapacity) return false;
|
||||
GameEntry.Entity.AttachEntity(weapon.Id, _player.Id, _weaponSlots[_weapons.Count]);
|
||||
_weapons.Add(weapon);
|
||||
weapon.SetEnabled(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DetachWeapon(WeaponBase weapon)
|
||||
{
|
||||
bool result = _weapons.Remove(weapon);
|
||||
if (result)
|
||||
{
|
||||
GameEntry.Entity.DetachEntity(weapon.Id, _player.Id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool AttachProp(PropItem prop)
|
||||
{
|
||||
_props.Add(prop);
|
||||
prop.OnAttach(_statComponent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DetachProp(PropItem prop)
|
||||
{
|
||||
_props.Remove(prop);
|
||||
prop.OnDetach(_statComponent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetWeaponState(bool state)
|
||||
{
|
||||
if (_weapons == null) return;
|
||||
foreach (var weapon in _weapons)
|
||||
{
|
||||
weapon.SetEnabled(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2980af28a40c84247a688f6b684dd4c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using CustomEvent;
|
||||
using SepCore.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class HealthComponent : MonoBehaviour
|
||||
{
|
||||
private int _maxHealthBase;
|
||||
private int _currentHealth;
|
||||
private bool _isDead => _currentHealth <= 0;
|
||||
|
||||
private StatComponent _statComponent;
|
||||
|
||||
private StatProperty _maxHealthStat;
|
||||
private Action<StatModifier, bool> _maxHealthStatCallback;
|
||||
public StatProperty MaxHealthStat => _maxHealthStat;
|
||||
|
||||
private StatProperty _defenseStat;
|
||||
private Action<StatModifier, bool> _defenseStatCallback;
|
||||
public StatProperty DefenseStat => _defenseStat;
|
||||
|
||||
private StatProperty _dodgeStat;
|
||||
private Action<StatModifier, bool> _dodgeStatCallback;
|
||||
public StatProperty DodgeStat => _dodgeStat;
|
||||
|
||||
private bool _isPlayer => _statComponent != null;
|
||||
|
||||
public float HealthRatio => MaxHealth > 0 ? (float)CurrentHealth / MaxHealth : 0f;
|
||||
|
||||
public int MaxHealth => Mathf.CeilToInt((_maxHealthBase + _maxHealthStat.Value) * _maxHealthStat.Percent);
|
||||
|
||||
public int CurrentHealth
|
||||
{
|
||||
get => _currentHealth;
|
||||
set => _currentHealth = value;
|
||||
}
|
||||
|
||||
#region FSM
|
||||
|
||||
public void OnInit(int maxHealth, StatComponent statComponent = null)
|
||||
{
|
||||
_maxHealthBase = maxHealth;
|
||||
_currentHealth = maxHealth;
|
||||
_statComponent = statComponent;
|
||||
|
||||
_statComponent = statComponent;
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_maxHealthStat = _statComponent.GetStat(StatType.MaxHealth);
|
||||
_maxHealthStatCallback = (modifier, isApply) =>
|
||||
{
|
||||
_statComponent.UpdateStat(_maxHealthStat, modifier, isApply);
|
||||
GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(0, CurrentHealth, MaxHealth));
|
||||
};
|
||||
_statComponent.Subscribe(StatType.MaxHealth, _maxHealthStatCallback);
|
||||
|
||||
_defenseStat = _statComponent.GetStat(StatType.Defense);
|
||||
_defenseStatCallback = (modifier, isApply) =>
|
||||
_statComponent.UpdateStat(_defenseStat, modifier, isApply);
|
||||
_statComponent.Subscribe(StatType.Defense, _defenseStatCallback);
|
||||
|
||||
_dodgeStat = _statComponent.GetStat(StatType.Dodge);
|
||||
_dodgeStatCallback = (modifier, isApply) =>
|
||||
_statComponent.UpdateStat(_dodgeStat, modifier, isApply);
|
||||
_statComponent.Subscribe(StatType.Dodge, _dodgeStatCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
_maxHealthStat = new StatProperty();
|
||||
_defenseStat = new StatProperty();
|
||||
_dodgeStat = new StatProperty();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_statComponent.Unsubscribe(StatType.MaxHealth, _maxHealthStatCallback);
|
||||
_maxHealthStatCallback = null;
|
||||
|
||||
_statComponent.Unsubscribe(StatType.Defense, _defenseStatCallback);
|
||||
_defenseStatCallback = null;
|
||||
|
||||
_statComponent.Unsubscribe(StatType.Dodge, _dodgeStatCallback);
|
||||
_dodgeStatCallback = null;
|
||||
}
|
||||
|
||||
_statComponent = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
if (_isDead) return;
|
||||
|
||||
if (_dodgeStat.Value > UnityEngine.Random.value)
|
||||
{
|
||||
damage = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int defenseValue = (int)_defenseStat.Value;
|
||||
float defensePercent = _defenseStat.Percent;
|
||||
damage -= defenseValue;
|
||||
damage = Mathf.CeilToInt(damage / defensePercent);
|
||||
}
|
||||
|
||||
_currentHealth -= damage;
|
||||
CauseDamage(damage);
|
||||
if (_isPlayer)
|
||||
GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(damage, CurrentHealth, MaxHealth));
|
||||
}
|
||||
|
||||
private void CauseDamage(int damage)
|
||||
{
|
||||
if (damage <= 0 || GameEntry.DamageText == null) return;
|
||||
GameEntry.DamageText.ShowDamage(transform.position + Vector3.up * 1.1f, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce4964a40efccaa4fb22df63713b567b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class InputComponent : MonoBehaviour
|
||||
{
|
||||
private PlayerInputActions _inputActions;
|
||||
[SerializeField] private bool _isListening = false;
|
||||
|
||||
[SerializeField] private Vector3 _direction = Vector3.zero;
|
||||
public Vector3 Direction => _direction;
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
_inputActions = new();
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
if (_isListening && _inputActions != null)
|
||||
{
|
||||
ReadInput();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_inputActions = null;
|
||||
_isListening = false;
|
||||
}
|
||||
|
||||
private void ReadInput()
|
||||
{
|
||||
Vector2 rawDir = _inputActions.Game.Move.ReadValue<Vector2>();
|
||||
_direction = new Vector3(rawDir.x, 0, rawDir.y);
|
||||
}
|
||||
|
||||
public void SetListening(bool isListening)
|
||||
{
|
||||
_isListening = isListening;
|
||||
if (_isListening)
|
||||
{
|
||||
_inputActions.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputActions.Disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f754cc5f339dcfa4aaec3aa32260c4b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using SepCore.Definition;
|
||||
using Entity;
|
||||
using Simulation;
|
||||
using UnityEngine;
|
||||
using CustomDebugger;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class MovementComponent : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private bool _isMoving;
|
||||
[SerializeField] private Vector3 _direction;
|
||||
[SerializeField] private Transform _cachedTransform;
|
||||
[SerializeField] private bool _avoidEnemyOverlap;
|
||||
[SerializeField] private float _enemyBodyRadius = 0.45f;
|
||||
[SerializeField] private int _separationIterations = 2;
|
||||
|
||||
public float Speed => (_speedBase + _movementStat.Value) * _movementStat.Percent;
|
||||
public bool AvoidEnemyOverlap => _avoidEnemyOverlap;
|
||||
public float EnemyBodyRadius => _enemyBodyRadius;
|
||||
public int SeparationIterations => _separationIterations;
|
||||
public bool IsMoving => _isMoving;
|
||||
public Vector3 Direction => _direction;
|
||||
public Transform CachedTransform => _cachedTransform;
|
||||
[SerializeField] private float _speedBase;
|
||||
|
||||
private StatComponent _statComponent;
|
||||
|
||||
private StatProperty _movementStat;
|
||||
private Action<StatModifier, bool> _movementStatCallback;
|
||||
|
||||
public void OnInit(float speed, Transform target, StatComponent statComponent = null,
|
||||
bool avoidEnemyOverlap = false, float enemyBodyRadius = 0.45f, int separationIterations = 2)
|
||||
{
|
||||
_speedBase = speed;
|
||||
_cachedTransform = target;
|
||||
_direction = Vector3.forward;
|
||||
_avoidEnemyOverlap = avoidEnemyOverlap;
|
||||
_enemyBodyRadius = Mathf.Max(0.01f, enemyBodyRadius);
|
||||
_separationIterations = Mathf.Max(1, separationIterations);
|
||||
|
||||
_statComponent = statComponent;
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_movementStat = _statComponent.GetStat(StatType.MovementSpeed);
|
||||
_movementStatCallback = (modifier, isApply) =>
|
||||
{
|
||||
_statComponent.UpdateStat(_movementStat, modifier, isApply);
|
||||
SyncToSimulationWorld();
|
||||
};
|
||||
_statComponent.Subscribe(StatType.MovementSpeed, _movementStatCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
_movementStat = new StatProperty();
|
||||
}
|
||||
|
||||
SyncToSimulationWorld();
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
SyncToSimulationWorld();
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
Transform transformToUnregister = _cachedTransform;
|
||||
|
||||
_speedBase = 0;
|
||||
_cachedTransform = null;
|
||||
_direction = Vector3.zero;
|
||||
_isMoving = false;
|
||||
_avoidEnemyOverlap = false;
|
||||
_enemyBodyRadius = 0.45f;
|
||||
_separationIterations = 2;
|
||||
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_statComponent.Unsubscribe(StatType.MovementSpeed, _movementStatCallback);
|
||||
_movementStatCallback = null;
|
||||
}
|
||||
|
||||
_statComponent = null;
|
||||
|
||||
if (transformToUnregister != null)
|
||||
{
|
||||
var simulationWorld = GameEntry.SimulationWorld;
|
||||
simulationWorld?.UnregisterPlayerMovement(transformToUnregister);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMove(bool isMoving)
|
||||
{
|
||||
_isMoving = isMoving;
|
||||
SyncToSimulationWorld();
|
||||
}
|
||||
|
||||
public void SetDirection(Vector3 direction)
|
||||
{
|
||||
_direction = direction;
|
||||
SyncToSimulationWorld();
|
||||
}
|
||||
|
||||
private void SyncToSimulationWorld()
|
||||
{
|
||||
using (CustomProfilerMarker.Movement_Update.Auto())
|
||||
{
|
||||
if (_cachedTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SimulationWorld simulationWorld = GameEntry.SimulationWorld;
|
||||
if (simulationWorld == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 direction = _direction;
|
||||
direction.y = 0f;
|
||||
if (direction.sqrMagnitude > Mathf.Epsilon)
|
||||
{
|
||||
direction.Normalize();
|
||||
}
|
||||
|
||||
if (TryGetComponent(out Player _))
|
||||
{
|
||||
simulationWorld.SyncPlayerMovementInput(_cachedTransform, _isMoving, direction, Speed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryGetComponent(out EnemyBase enemy))
|
||||
{
|
||||
simulationWorld.SyncEnemyMovementInput(enemy.Id, _isMoving, direction, Speed,
|
||||
_avoidEnemyOverlap, _enemyBodyRadius, _separationIterations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9baf591df06b9bb44ba5313457ba84b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SepCore.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class StatComponent : MonoBehaviour
|
||||
{
|
||||
private Dictionary<StatType, List<StatModifier>> _statModifiers;
|
||||
private Dictionary<StatType, Action<StatModifier, bool>> _statModifierCallbacks;
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
_statModifiers = new Dictionary<StatType, List<StatModifier>>();
|
||||
_statModifierCallbacks = new Dictionary<StatType, Action<StatModifier, bool>>();
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_statModifiers.Clear();
|
||||
_statModifierCallbacks.Clear();
|
||||
}
|
||||
|
||||
public void AddModifier(StatModifier modifier)
|
||||
{
|
||||
if (!_statModifiers.TryGetValue(modifier.StatType, out var modifiers))
|
||||
{
|
||||
_statModifiers.Add(modifier.StatType, modifiers = new List<StatModifier>());
|
||||
modifiers.Add(modifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
modifiers.Add(modifier);
|
||||
}
|
||||
|
||||
if (_statModifierCallbacks.TryGetValue(modifier.StatType, out var callbacks))
|
||||
callbacks?.Invoke(modifier, true);
|
||||
}
|
||||
|
||||
public void RemoveModifier(StatModifier modifier)
|
||||
{
|
||||
if (_statModifiers.TryGetValue(modifier.StatType, out var modifiers))
|
||||
{
|
||||
_statModifiers[modifier.StatType].Remove(modifier);
|
||||
}
|
||||
|
||||
if (_statModifierCallbacks.TryGetValue(modifier.StatType, out var callbacks))
|
||||
callbacks?.Invoke(modifier, false);
|
||||
}
|
||||
|
||||
public void Subscribe(StatType statType, Action<StatModifier, bool> callback)
|
||||
{
|
||||
if (!_statModifierCallbacks.TryAdd(statType, callback))
|
||||
{
|
||||
_statModifierCallbacks[statType] += callback;
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(StatType statType, Action<StatModifier, bool> callback)
|
||||
{
|
||||
if (!_statModifierCallbacks.TryGetValue(statType, out var callbacks))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks -= callback;
|
||||
if (callbacks == null)
|
||||
{
|
||||
_statModifierCallbacks.Remove(statType);
|
||||
}
|
||||
}
|
||||
|
||||
public StatProperty GetStat(StatType statType)
|
||||
{
|
||||
var result = new StatProperty();
|
||||
|
||||
if (!_statModifiers.TryGetValue(statType, out var modifiers)) return result;
|
||||
|
||||
foreach (var modifier in modifiers)
|
||||
{
|
||||
if (modifier.IsPercent) result.Percent += modifier.Value;
|
||||
else result.Value += modifier.Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void UpdateStat(StatProperty stat, StatModifier modifier, bool isApply)
|
||||
{
|
||||
float coef = isApply ? 1f : -1f;
|
||||
if (modifier.IsPercent)
|
||||
{
|
||||
stat.Percent += modifier.Value * coef;
|
||||
}
|
||||
else
|
||||
{
|
||||
stat.Value += modifier.Value * coef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5befb68803e86e248873416ce662184b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bce93539c13d79d4fb88e16597c05a27
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33ba0562010e2da46883baefb271f573
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026146
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using SepCore.Definition;
|
||||
using GameFramework;
|
||||
using StarForce;
|
||||
using UI;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class BuiltinDataComponent : GameFrameworkComponent
|
||||
{
|
||||
[SerializeField]
|
||||
private TextAsset m_BuildInfoTextAsset = null;
|
||||
|
||||
[SerializeField]
|
||||
private TextAsset m_DefaultDictionaryTextAsset = null;
|
||||
|
||||
[SerializeField]
|
||||
private UpdateResourceForm m_UpdateResourceFormTemplate = null;
|
||||
|
||||
private BuildInfo m_BuildInfo = null;
|
||||
|
||||
public BuildInfo BuildInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_BuildInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public UpdateResourceForm UpdateResourceFormTemplate
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_UpdateResourceFormTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
public void InitBuildInfo()
|
||||
{
|
||||
if (m_BuildInfoTextAsset == null || string.IsNullOrEmpty(m_BuildInfoTextAsset.text))
|
||||
{
|
||||
Log.Info("Build info can not be found or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_BuildInfo = Utility.Json.ToObject<BuildInfo>(m_BuildInfoTextAsset.text);
|
||||
if (m_BuildInfo == null)
|
||||
{
|
||||
Log.Warning("Parse build info failure.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void InitDefaultDictionary()
|
||||
{
|
||||
if (m_DefaultDictionaryTextAsset == null || string.IsNullOrEmpty(m_DefaultDictionaryTextAsset.text))
|
||||
{
|
||||
Log.Info("Default dictionary can not be found or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GameEntry.Localization.ParseData(m_DefaultDictionaryTextAsset.text))
|
||||
{
|
||||
Log.Warning("Parse default dictionary failure.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cfb0b001fcb30fe45ac95ddb8c28ba57
|
||||
timeCreated: 1528026146
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6befdfdd5f3e430a864a90bf445000e8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.ObjectPool;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class DamageTextComponent : GameFrameworkComponent
|
||||
{
|
||||
[SerializeField] private int _instancePoolCapacity = 256;
|
||||
|
||||
[SerializeField] private string _poolName = "DamageTextItem";
|
||||
|
||||
[SerializeField] private RectTransform _instanceRoot;
|
||||
|
||||
[SerializeField] private Canvas _canvas;
|
||||
|
||||
[SerializeField] private GameObject _damageTextItemPrefab;
|
||||
|
||||
private IObjectPool<DamageTextItemObject> _damageTextItemPool;
|
||||
|
||||
private readonly List<DamageTextItem> _activeDamageTextItems = new();
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_damageTextItemPool =
|
||||
GameEntry.ObjectPool.CreateSingleSpawnObjectPool<DamageTextItemObject>(_poolName,
|
||||
_instancePoolCapacity);
|
||||
}
|
||||
|
||||
public void ShowDamage(Vector3 worldPosition, int damage)
|
||||
{
|
||||
if (damage <= 0 || _damageTextItemPool == null || _canvas == null) return;
|
||||
|
||||
DamageTextItem item = CreateDamageTextItem();
|
||||
if (item == null) return;
|
||||
|
||||
item.Show(worldPosition, damage, _canvas, OnItemComplete);
|
||||
_activeDamageTextItems.Add(item);
|
||||
}
|
||||
|
||||
private DamageTextItem CreateDamageTextItem()
|
||||
{
|
||||
if (_activeDamageTextItems.Count == _instancePoolCapacity)
|
||||
{
|
||||
_instancePoolCapacity = Mathf.Min(_instancePoolCapacity * 2, 1024);
|
||||
_damageTextItemPool.Capacity = _instancePoolCapacity;
|
||||
}
|
||||
|
||||
DamageTextItemObject itemObject = _damageTextItemPool.Spawn();
|
||||
if (itemObject != null)
|
||||
{
|
||||
return (DamageTextItem)itemObject.Target;
|
||||
}
|
||||
|
||||
GameObject itemGo = Instantiate(_damageTextItemPrefab, _instanceRoot, false);
|
||||
|
||||
DamageTextItem item = itemGo.GetComponent<DamageTextItem>();
|
||||
_damageTextItemPool.Register(DamageTextItemObject.Create(item), true);
|
||||
return item;
|
||||
}
|
||||
|
||||
private void OnItemComplete(DamageTextItem item)
|
||||
{
|
||||
if (item == null) return;
|
||||
item.ResetItem();
|
||||
_activeDamageTextItems.Remove(item);
|
||||
_damageTextItemPool.Unspawn(item);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_activeDamageTextItems.Clear();
|
||||
_damageTextItemPool.Release();
|
||||
_damageTextItemPool = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 366eb88b821a4048891b7fb01a1b8d42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using DG.Tweening;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class DamageTextItem : MonoBehaviour
|
||||
{
|
||||
private const float RiseDistance = 80f;
|
||||
private const float RiseDuration = 0.55f;
|
||||
private const float FadeDelay = 0.2f;
|
||||
private const float FadeDuration = 0.35f;
|
||||
private static readonly Color NormalColor = new Color(1f, 0.32f, 0.23f, 1f);
|
||||
|
||||
[SerializeField] private RectTransform _cachedTransform;
|
||||
|
||||
[SerializeField] private CanvasGroup _canvasGroup;
|
||||
|
||||
[SerializeField] private TMP_Text _text;
|
||||
|
||||
private Sequence _sequence;
|
||||
private Action<DamageTextItem> _onComplete;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_cachedTransform == null) _cachedTransform = GetComponent<RectTransform>();
|
||||
if (_canvasGroup == null) _canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (_text == null) _text = GetComponent<TMP_Text>();
|
||||
}
|
||||
|
||||
public void Show(Vector3 worldPosition, int damage, Canvas canvas, Action<DamageTextItem> onComplete)
|
||||
{
|
||||
if (canvas == null || GameEntry.Scene.MainCamera == null) return;
|
||||
|
||||
_onComplete = onComplete;
|
||||
gameObject.SetActive(true);
|
||||
KillSequence();
|
||||
|
||||
_canvasGroup.alpha = 1f;
|
||||
_cachedTransform.localScale = Vector3.one;
|
||||
_text.color = NormalColor;
|
||||
_text.text = damage.ToString();
|
||||
|
||||
Vector3 screenPos = GameEntry.Scene.MainCamera.WorldToScreenPoint(worldPosition);
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)canvas.transform, screenPos,
|
||||
canvas.worldCamera, out Vector2 localPos);
|
||||
localPos.x += UnityEngine.Random.Range(-24f, 24f);
|
||||
_cachedTransform.anchoredPosition = localPos;
|
||||
|
||||
_sequence = DOTween.Sequence();
|
||||
_sequence.Append(_cachedTransform.DOAnchorPosY(localPos.y + RiseDistance, RiseDuration)
|
||||
.SetEase(Ease.OutQuad));
|
||||
_sequence.Join(_cachedTransform.DOPunchScale(new Vector3(0.15f, 0.15f, 0f), 0.18f, 6, 0.75f));
|
||||
_sequence.Join(_canvasGroup.DOFade(0f, FadeDuration).SetDelay(FadeDelay));
|
||||
_sequence.OnComplete(() => _onComplete?.Invoke(this));
|
||||
}
|
||||
|
||||
public void ResetItem()
|
||||
{
|
||||
KillSequence();
|
||||
_onComplete = null;
|
||||
_canvasGroup.alpha = 1f;
|
||||
_text.text = string.Empty;
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
KillSequence();
|
||||
}
|
||||
|
||||
private void KillSequence()
|
||||
{
|
||||
if (_sequence == null) return;
|
||||
_sequence.Kill();
|
||||
_sequence = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 501e678f9cb14af6ac45e8d2050388d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
using GameFramework;
|
||||
using GameFramework.ObjectPool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class DamageTextItemObject : ObjectBase
|
||||
{
|
||||
public static DamageTextItemObject Create(object target)
|
||||
{
|
||||
DamageTextItemObject itemObject = ReferencePool.Acquire<DamageTextItemObject>();
|
||||
itemObject.Initialize(target);
|
||||
return itemObject;
|
||||
}
|
||||
|
||||
protected override void Release(bool isShutdown)
|
||||
{
|
||||
DamageTextItem item = (DamageTextItem)Target;
|
||||
if (item == null) return;
|
||||
Object.Destroy(item.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4aba1230dece448eba1fa7579ca7a13d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a11ef2e359b24bfdb6c9120cc2ec81b8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+527
@@ -0,0 +1,527 @@
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Components;
|
||||
using CustomEvent;
|
||||
using SepCore.DataTable;
|
||||
using SepCore.Definition;
|
||||
using Entity;
|
||||
using CustomUtility;
|
||||
using Procedure;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class RuntimeDebugPanelComponent : GameFrameworkComponent
|
||||
{
|
||||
private const float MinSpawnRate = 0.1f;
|
||||
private const float CornerTapWindow = 0.6f;
|
||||
private const int RequiredCornerTapCount = 3;
|
||||
private const int DebugHealAmount = 200;
|
||||
|
||||
[Header("Window Content")]
|
||||
[SerializeField] private bool _showBuffSection = true;
|
||||
[SerializeField] private bool _showBattleOverview = true;
|
||||
[SerializeField] private bool _showCollisionStats = true;
|
||||
[SerializeField] private bool _showSpawnControls = true;
|
||||
[SerializeField] private bool _showBattleDurationControls = true;
|
||||
[SerializeField] private bool _showPlayerWeaponControls = true;
|
||||
[SerializeField] private bool _showPlayerHealthControls = true;
|
||||
[SerializeField] private bool _showTips = true;
|
||||
|
||||
private Rect _windowRect = new Rect(20f, 60f, 460f, 800f);
|
||||
private bool _isPanelVisible;
|
||||
private int _windowId;
|
||||
|
||||
private string _searchText = string.Empty;
|
||||
private int _selectedIndex;
|
||||
private int _addCount = 1;
|
||||
private Vector2 _buffScroll;
|
||||
|
||||
private float _spawnRateScaleInput = 1f;
|
||||
private float _extendDurationSeconds = 30f;
|
||||
|
||||
private DRProp[] _allProps = Array.Empty<DRProp>();
|
||||
private DRProp[] _filteredProps = Array.Empty<DRProp>();
|
||||
private string[] _displayNames = Array.Empty<string>();
|
||||
|
||||
private int _cornerTapCount;
|
||||
private float _lastCornerTapTime = -10f;
|
||||
private bool _lockPlayerHealthToMax;
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_windowId = GetInstanceID();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (IsTogglePressed())
|
||||
{
|
||||
_isPanelVisible = !_isPanelVisible;
|
||||
}
|
||||
|
||||
HandleCornerTapGesture();
|
||||
if (_lockPlayerHealthToMax)
|
||||
{
|
||||
KeepPlayerHealthAtMax();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
DrawToggleButton();
|
||||
if (!_isPanelVisible) return;
|
||||
|
||||
_windowRect = GUI.Window(_windowId, _windowRect, DrawWindow, "Runtime Debug Panel");
|
||||
}
|
||||
|
||||
private void DrawToggleButton()
|
||||
{
|
||||
const float width = 64f;
|
||||
const float height = 30f;
|
||||
Rect buttonRect = new Rect(Screen.width - width - 12f, 10f, width, height);
|
||||
if (GUI.Button(buttonRect, "DEBUG"))
|
||||
{
|
||||
_isPanelVisible = !_isPanelVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawWindow(int windowId)
|
||||
{
|
||||
if (_showBuffSection)
|
||||
{
|
||||
EnsurePropList();
|
||||
}
|
||||
|
||||
GUILayout.BeginVertical();
|
||||
|
||||
bool hasPreviousSection = false;
|
||||
if (_showBuffSection)
|
||||
{
|
||||
DrawBuffSection();
|
||||
hasPreviousSection = true;
|
||||
}
|
||||
|
||||
if (HasVisibleBattleSection())
|
||||
{
|
||||
if (hasPreviousSection)
|
||||
{
|
||||
GUILayout.Space(8f);
|
||||
GUILayout.Label(string.Empty, GUI.skin.horizontalSlider);
|
||||
GUILayout.Space(8f);
|
||||
}
|
||||
|
||||
DrawBattleSection();
|
||||
hasPreviousSection = true;
|
||||
}
|
||||
|
||||
if (_showTips)
|
||||
{
|
||||
if (hasPreviousSection)
|
||||
{
|
||||
GUILayout.Space(8f);
|
||||
}
|
||||
|
||||
GUILayout.Label("Tips: press `F8` or tap top-left corner 3 times to toggle.", GUILayout.Height(20f));
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUI.DragWindow(new Rect(0, 0, 10000, 22));
|
||||
}
|
||||
|
||||
private void DrawBuffSection()
|
||||
{
|
||||
GUILayout.Label("Buff Debug");
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Search", GUILayout.Width(52f));
|
||||
_searchText = GUILayout.TextField(_searchText);
|
||||
if (GUILayout.Button("Refresh", GUILayout.Width(80f)))
|
||||
{
|
||||
EnsurePropList(true);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
ApplyFilter(_searchText);
|
||||
if (_displayNames.Length == 0)
|
||||
{
|
||||
GUILayout.Label("No Buff data. Enter battle and try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, _displayNames.Length - 1);
|
||||
_buffScroll = GUILayout.BeginScrollView(_buffScroll, GUILayout.Height(120f));
|
||||
_selectedIndex = GUILayout.SelectionGrid(_selectedIndex, _displayNames, 1);
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
DRProp selectedProp = GetSelectedProp();
|
||||
if (selectedProp == null) return;
|
||||
|
||||
GUILayout.Label($"Selected: {selectedProp.Title} ({selectedProp.Rarity})");
|
||||
GUILayout.Label(ItemDescUtility.CreatePropDescription(selectedProp.Modifiers), GUILayout.Height(40f));
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Count", GUILayout.Width(52f));
|
||||
string addCountText = GUILayout.TextField(_addCount.ToString(), GUILayout.Width(60f));
|
||||
if (!int.TryParse(addCountText, out _addCount)) _addCount = 1;
|
||||
_addCount = Mathf.Clamp(_addCount, 1, 99);
|
||||
|
||||
if (GUILayout.Button("Add Buff To Player", GUILayout.Height(24f)))
|
||||
{
|
||||
AddSelectedBuffToPlayer(selectedProp, _addCount);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawBattleSection()
|
||||
{
|
||||
GUILayout.Label("Battle Debug");
|
||||
ProcedureGame procedure = GameEntry.Procedure.CurrentProcedure as ProcedureGame;
|
||||
EnemyManagerComponent enemyManager = GameEntry.EnemyManager;
|
||||
Player player = FindPlayer();
|
||||
HealthComponent playerHealth = player != null ? player.GetComponent<HealthComponent>() : null;
|
||||
|
||||
if (enemyManager == null)
|
||||
{
|
||||
GUILayout.Label("EnemyManager unavailable.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (procedure == null)
|
||||
{
|
||||
GUILayout.Label("ProcedureGame unavailable.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_showBattleOverview)
|
||||
{
|
||||
GUILayout.Label($"Spawn Rate: {enemyManager.SpawnRateScale:F2}");
|
||||
GUILayout.Label($"Battle Time: {enemyManager.ElapsedBattleTime:F1}s / {enemyManager.BattleDuration:F1}s");
|
||||
GUILayout.Label($"Enemy Count: {enemyManager.CurrentEnemyCount}");
|
||||
}
|
||||
|
||||
Simulation.SimulationWorld simulationWorld = GameEntry.SimulationWorld;
|
||||
if (_showCollisionStats && simulationWorld != null)
|
||||
{
|
||||
GUILayout.Space(4f);
|
||||
GUILayout.Label(
|
||||
$"Collision Queries: total {simulationWorld.LastCollisionQueryCount} (Projectile {simulationWorld.LastProjectileCollisionQueryCount} / Area {simulationWorld.LastAreaCollisionQueryCount})");
|
||||
GUILayout.Label(
|
||||
$"Collision Candidates: total {simulationWorld.LastCollisionCandidateCount} (Projectile {simulationWorld.LastProjectileCollisionCandidateCount} / Area {simulationWorld.LastAreaCollisionCandidateCount})");
|
||||
GUILayout.Label(
|
||||
$"Area Resolve: hits {simulationWorld.LastResolvedAreaHitCount}");
|
||||
GUILayout.Label(
|
||||
$"Broad Phase: cell {simulationWorld.LastCollisionCellSize:F2}, hasEnemyTargets {(simulationWorld.LastCollisionHasEnemyTargets ? "Yes" : "No")}");
|
||||
if (simulationWorld.LastCollisionCandidateCount != 0)
|
||||
{
|
||||
Log.Info($"LastCollisionCandidateCount:{simulationWorld.LastCollisionCandidateCount}");
|
||||
}
|
||||
|
||||
if (simulationWorld.LastResolvedAreaHitCount != 0)
|
||||
{
|
||||
Log.Info($"LastResolvedAreaHitCount:{simulationWorld.LastResolvedAreaHitCount}");
|
||||
}
|
||||
}
|
||||
|
||||
if (_showSpawnControls)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Rate", GUILayout.Width(52f));
|
||||
string rateText = GUILayout.TextField(_spawnRateScaleInput.ToString("F2"), GUILayout.Width(60f));
|
||||
if (float.TryParse(rateText, out float parsedRate))
|
||||
{
|
||||
_spawnRateScaleInput = Mathf.Clamp(parsedRate, MinSpawnRate, 50f);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Apply", GUILayout.Width(70f)))
|
||||
{
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("x0.5", GUILayout.Width(60f)))
|
||||
{
|
||||
_spawnRateScaleInput = Mathf.Max(MinSpawnRate, enemyManager.SpawnRateScale * 0.5f);
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("x2", GUILayout.Width(60f)))
|
||||
{
|
||||
_spawnRateScaleInput = enemyManager.SpawnRateScale * 2f;
|
||||
enemyManager.SetSpawnRateScale(_spawnRateScaleInput);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
if (_showBattleDurationControls)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Add Sec", GUILayout.Width(52f));
|
||||
string durationText = GUILayout.TextField(_extendDurationSeconds.ToString("F0"), GUILayout.Width(60f));
|
||||
if (float.TryParse(durationText, out float parsedDuration))
|
||||
{
|
||||
_extendDurationSeconds = Mathf.Clamp(parsedDuration, 1f, 3600f);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Extend Battle", GUILayout.Height(24f)))
|
||||
{
|
||||
if (procedure.CurrentGameState is GameStateBattle gameState)
|
||||
{
|
||||
gameState.AddBattleDuration(_extendDurationSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
if (_showPlayerWeaponControls)
|
||||
{
|
||||
GUILayout.Label(
|
||||
$"Player Weapon: {(player == null ? "Player not found" : (player.WeaponEnabled ? "Enabled" : "Disabled"))}");
|
||||
GUILayout.BeginHorizontal();
|
||||
GUI.enabled = player != null;
|
||||
if (GUILayout.Button("Disable Weapons", GUILayout.Height(24f)))
|
||||
{
|
||||
player.SetWeaponEnabled(false);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Enable Weapons", GUILayout.Height(24f)))
|
||||
{
|
||||
player.SetWeaponEnabled(true);
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
if (_showPlayerHealthControls)
|
||||
{
|
||||
GUILayout.Space(4f);
|
||||
GUILayout.Label(
|
||||
$"Player HP: {(playerHealth == null ? "Unavailable" : $"{playerHealth.CurrentHealth}/{playerHealth.MaxHealth}")}");
|
||||
GUILayout.BeginHorizontal();
|
||||
GUI.enabled = playerHealth != null;
|
||||
if (GUILayout.Button($"+{DebugHealAmount} HP", GUILayout.Height(24f)))
|
||||
{
|
||||
AddPlayerHealth(playerHealth, DebugHealAmount);
|
||||
}
|
||||
|
||||
if (GUILayout.Button(_lockPlayerHealthToMax ? "GodMode: ON" : "GodMode: OFF", GUILayout.Height(24f)))
|
||||
{
|
||||
_lockPlayerHealthToMax = !_lockPlayerHealthToMax;
|
||||
if (_lockPlayerHealthToMax)
|
||||
{
|
||||
RestorePlayerHealthToMax(playerHealth);
|
||||
}
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasVisibleBattleSection()
|
||||
{
|
||||
return _showBattleOverview ||
|
||||
_showCollisionStats ||
|
||||
_showSpawnControls ||
|
||||
_showBattleDurationControls ||
|
||||
_showPlayerWeaponControls ||
|
||||
_showPlayerHealthControls;
|
||||
}
|
||||
|
||||
private void EnsurePropList(bool force = false)
|
||||
{
|
||||
if (!force && _allProps != null && _allProps.Length > 0) return;
|
||||
|
||||
if (GameEntry.DataTable == null)
|
||||
{
|
||||
_allProps = Array.Empty<DRProp>();
|
||||
_filteredProps = Array.Empty<DRProp>();
|
||||
_displayNames = Array.Empty<string>();
|
||||
return;
|
||||
}
|
||||
|
||||
var table = GameEntry.DataTable.GetDataTable<DRProp>();
|
||||
_allProps = table != null ? table.ToArray() : Array.Empty<DRProp>();
|
||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, Mathf.Max(0, _allProps.Length - 1));
|
||||
ApplyFilter(_searchText);
|
||||
}
|
||||
|
||||
private void ApplyFilter(string keyword)
|
||||
{
|
||||
if (_allProps == null || _allProps.Length == 0)
|
||||
{
|
||||
_filteredProps = Array.Empty<DRProp>();
|
||||
_displayNames = Array.Empty<string>();
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
_filteredProps = _allProps;
|
||||
}
|
||||
else
|
||||
{
|
||||
string search = keyword.Trim();
|
||||
_filteredProps = _allProps.Where(p =>
|
||||
p != null &&
|
||||
(p.Title?.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
p.Id.ToString().Contains(search))).ToArray();
|
||||
}
|
||||
|
||||
_displayNames = _filteredProps.Select(p => $"[{p.Id}] {p.Title} ({p.Rarity})").ToArray();
|
||||
if (_displayNames.Length == 0) _selectedIndex = 0;
|
||||
else _selectedIndex = Mathf.Clamp(_selectedIndex, 0, _displayNames.Length - 1);
|
||||
}
|
||||
|
||||
private DRProp GetSelectedProp()
|
||||
{
|
||||
if (_filteredProps == null || _filteredProps.Length == 0) return null;
|
||||
_selectedIndex = Mathf.Clamp(_selectedIndex, 0, _filteredProps.Length - 1);
|
||||
return _filteredProps[_selectedIndex];
|
||||
}
|
||||
|
||||
private static Player FindPlayer()
|
||||
{
|
||||
return UnityEngine.Object.FindObjectOfType<Player>();
|
||||
}
|
||||
|
||||
private void KeepPlayerHealthAtMax()
|
||||
{
|
||||
Player player = FindPlayer();
|
||||
if (player == null) return;
|
||||
|
||||
HealthComponent playerHealth = player.GetComponent<HealthComponent>();
|
||||
if (playerHealth == null) return;
|
||||
|
||||
RestorePlayerHealthToMax(playerHealth);
|
||||
}
|
||||
|
||||
private static void AddPlayerHealth(HealthComponent playerHealth, int amount)
|
||||
{
|
||||
if (playerHealth == null || amount <= 0) return;
|
||||
if (playerHealth.CurrentHealth <= 0) return;
|
||||
|
||||
int maxHealth = playerHealth.MaxHealth;
|
||||
if (maxHealth <= 0) return;
|
||||
|
||||
int nextHealth = Mathf.Clamp(playerHealth.CurrentHealth + amount, 0, maxHealth);
|
||||
if (nextHealth == playerHealth.CurrentHealth) return;
|
||||
|
||||
playerHealth.CurrentHealth = nextHealth;
|
||||
PublishPlayerHealthChanged(playerHealth);
|
||||
}
|
||||
|
||||
private static void RestorePlayerHealthToMax(HealthComponent playerHealth)
|
||||
{
|
||||
if (playerHealth == null) return;
|
||||
if (playerHealth.CurrentHealth <= 0) return;
|
||||
|
||||
int maxHealth = playerHealth.MaxHealth;
|
||||
if (maxHealth <= 0 || playerHealth.CurrentHealth >= maxHealth) return;
|
||||
|
||||
playerHealth.CurrentHealth = maxHealth;
|
||||
PublishPlayerHealthChanged(playerHealth);
|
||||
}
|
||||
|
||||
private static void PublishPlayerHealthChanged(HealthComponent playerHealth)
|
||||
{
|
||||
if (playerHealth == null || GameEntry.Event == null) return;
|
||||
|
||||
GameEntry.Event.Fire(null,
|
||||
PlayerHealthChangeEventArgs.Create(0, playerHealth.CurrentHealth, playerHealth.MaxHealth));
|
||||
}
|
||||
|
||||
private static void AddSelectedBuffToPlayer(DRProp prop, int count)
|
||||
{
|
||||
Player player = FindPlayer();
|
||||
if (player == null || prop == null || prop.Modifiers == null) return;
|
||||
|
||||
int applyCount = Mathf.Clamp(count, 1, 99);
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
player.AddProp(new PropItem(prop.Modifiers, prop.Rarity, prop.Title, prop.IconAssetName));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCornerTapGesture()
|
||||
{
|
||||
if (!TryGetTouchReleased(out Vector2 touchPosition)) return;
|
||||
if (touchPosition.x > 80f || touchPosition.y < Screen.height - 80f) return;
|
||||
|
||||
float now = Time.unscaledTime;
|
||||
if (now - _lastCornerTapTime > CornerTapWindow)
|
||||
{
|
||||
_cornerTapCount = 0;
|
||||
}
|
||||
|
||||
_lastCornerTapTime = now;
|
||||
_cornerTapCount++;
|
||||
|
||||
if (_cornerTapCount >= RequiredCornerTapCount)
|
||||
{
|
||||
_cornerTapCount = 0;
|
||||
_isPanelVisible = !_isPanelVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTogglePressed()
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
Keyboard keyboard = Keyboard.current;
|
||||
if (keyboard == null) return false;
|
||||
return keyboard.backquoteKey.wasPressedThisFrame || keyboard.f8Key.wasPressedThisFrame;
|
||||
#else
|
||||
return Input.GetKeyDown(KeyCode.BackQuote) || Input.GetKeyDown(KeyCode.F8);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static bool TryGetTouchReleased(out Vector2 touchPosition)
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
Touchscreen touchscreen = Touchscreen.current;
|
||||
if (touchscreen == null)
|
||||
{
|
||||
touchPosition = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var touch = touchscreen.primaryTouch;
|
||||
if (!touch.press.wasReleasedThisFrame)
|
||||
{
|
||||
touchPosition = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
touchPosition = touch.position.ReadValue();
|
||||
return true;
|
||||
#else
|
||||
if (Input.touchCount <= 0)
|
||||
{
|
||||
touchPosition = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
Touch touch = Input.GetTouch(0);
|
||||
if (touch.phase != TouchPhase.Ended)
|
||||
{
|
||||
touchPosition = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
touchPosition = touch.position;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d8ada5157a04921a6e543a040e57960
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 259253e364fe32a4195e182f78b8cc54
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
using System.Collections.Generic;
|
||||
using SepCore.DataTable;
|
||||
using SepCore.Definition;
|
||||
using Entity;
|
||||
using Entity.EntityData;
|
||||
using GameFramework.Event;
|
||||
using Procedure;
|
||||
using StarForce;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class EnemyManagerComponent : GameFrameworkComponent
|
||||
{
|
||||
private const float MinSpawnRateScale = 0.1f;
|
||||
private const string EnemyGroupName = "Enemy";
|
||||
|
||||
private EntityComponent _entity;
|
||||
|
||||
private List<EntityBase> _enemies;
|
||||
private Dictionary<int, EntityBase> _enemyById;
|
||||
|
||||
public List<EntityBase> Enemies => _enemies;
|
||||
|
||||
private float _spawnEnemyTimer;
|
||||
|
||||
[SerializeField] private int _spawnEnemyMaxCount = 5000;
|
||||
|
||||
private int _currentEnemyCount;
|
||||
|
||||
[SerializeField] private int _spawnDistanceFromPlayer = 20;
|
||||
|
||||
private int _currentSpawnEnemyId;
|
||||
|
||||
private int _currentLevel;
|
||||
|
||||
private float[] _baseSpawnEnemyIntervals;
|
||||
private float[] _spawnEnemyIntervals;
|
||||
private EnemyType[] _spawnEnemyTypes;
|
||||
private int[] _spawnEnemyCounts;
|
||||
private float _duration;
|
||||
private float _baseDuration;
|
||||
|
||||
private float[] _nextSpawnTimes;
|
||||
private float _spawnRateScale = 1f;
|
||||
|
||||
private Transform _player;
|
||||
|
||||
public float SpawnRateScale => _spawnRateScale;
|
||||
public float BattleDuration => _duration;
|
||||
public float ElapsedBattleTime => _spawnEnemyTimer;
|
||||
public int CurrentEnemyCount => _currentEnemyCount;
|
||||
|
||||
#region FSM
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_entity = GameEntry.Entity;
|
||||
_enemies = new List<EntityBase>();
|
||||
_enemyById = new Dictionary<int, EntityBase>();
|
||||
|
||||
GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
||||
GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (GameEntry.Event != null)
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
||||
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||
}
|
||||
|
||||
_enemies = null;
|
||||
_enemyById = null;
|
||||
_entity = null;
|
||||
}
|
||||
|
||||
public void OnInit(DRLevel level)
|
||||
{
|
||||
_baseSpawnEnemyIntervals = (float[])level.Intervals.Clone();
|
||||
_spawnEnemyIntervals = (float[])_baseSpawnEnemyIntervals.Clone();
|
||||
_spawnEnemyTypes = level.EntityTypes;
|
||||
_spawnEnemyCounts = level.EntityCounts;
|
||||
_baseDuration = level.Duration;
|
||||
_duration = _baseDuration;
|
||||
|
||||
SetSpawnRateScale(_spawnRateScale);
|
||||
|
||||
_currentEnemyCount = 0;
|
||||
_currentSpawnEnemyId = 0;
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
_spawnEnemyTimer += elapseSeconds;
|
||||
|
||||
for (int i = 0; i < _nextSpawnTimes.Length; i++)
|
||||
{
|
||||
float nextSpawnTime = _nextSpawnTimes[i];
|
||||
if (_spawnEnemyTimer < nextSpawnTime) continue;
|
||||
for (int j = 0; j < _spawnEnemyCounts[i]; j++)
|
||||
{
|
||||
SpawnEnemy(_spawnEnemyTypes[i]);
|
||||
}
|
||||
|
||||
_nextSpawnTimes[i] += _spawnEnemyIntervals[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_spawnEnemyTimer = 0;
|
||||
_currentSpawnEnemyId = 0;
|
||||
_currentLevel = 0;
|
||||
|
||||
_baseSpawnEnemyIntervals = null;
|
||||
_spawnEnemyIntervals = null;
|
||||
_spawnEnemyTypes = null;
|
||||
_spawnEnemyCounts = null;
|
||||
_baseDuration = 0;
|
||||
_duration = 0;
|
||||
|
||||
_nextSpawnTimes = null;
|
||||
|
||||
ClearEnemies();
|
||||
|
||||
_currentEnemyCount = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void SpawnEnemy(EnemyType enemyType)
|
||||
{
|
||||
if (_player == null) return;
|
||||
|
||||
if (_currentEnemyCount >= _spawnEnemyMaxCount) return;
|
||||
int entityPoolId = _currentSpawnEnemyId % _spawnEnemyMaxCount;
|
||||
var enemyData = new EnemyData(entityPoolId, enemyType, _currentLevel)
|
||||
{
|
||||
Position = GetRandomPosition()
|
||||
};
|
||||
_entity.ShowEnemy(enemyData);
|
||||
_currentSpawnEnemyId++;
|
||||
}
|
||||
|
||||
private Vector3 GetRandomPosition()
|
||||
{
|
||||
float x = Random.Range(-1f, 1f);
|
||||
float z = Random.Range(-1f, 1f);
|
||||
Vector3 dir = new Vector3(x, 0, z).normalized;
|
||||
return _player.position + dir * _spawnDistanceFromPlayer;
|
||||
}
|
||||
|
||||
public void ClearEnemies()
|
||||
{
|
||||
foreach (var enemy in _enemies)
|
||||
{
|
||||
if (enemy == null || !enemy.Available) continue;
|
||||
_entity.HideEntity(enemy);
|
||||
}
|
||||
|
||||
_enemies.Clear();
|
||||
_enemyById?.Clear();
|
||||
}
|
||||
|
||||
public bool TryGetEnemy(int entityId, out EntityBase enemy)
|
||||
{
|
||||
enemy = null;
|
||||
if (_enemyById == null || !_enemyById.TryGetValue(entityId, out EntityBase cachedEnemy))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cachedEnemy == null || !cachedEnemy.Available)
|
||||
{
|
||||
_enemyById.Remove(entityId);
|
||||
return false;
|
||||
}
|
||||
|
||||
enemy = cachedEnemy;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetSpawnRateScale(float scale)
|
||||
{
|
||||
float newScale = Mathf.Max(MinSpawnRateScale, scale);
|
||||
if (_baseSpawnEnemyIntervals == null || _baseSpawnEnemyIntervals.Length == 0)
|
||||
{
|
||||
_spawnRateScale = newScale;
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasRuntimeState = _nextSpawnTimes != null && _spawnEnemyIntervals != null &&
|
||||
_nextSpawnTimes.Length == _baseSpawnEnemyIntervals.Length &&
|
||||
_spawnEnemyIntervals.Length == _baseSpawnEnemyIntervals.Length;
|
||||
float oldScale = _spawnRateScale;
|
||||
_spawnRateScale = newScale;
|
||||
|
||||
if (!hasRuntimeState)
|
||||
{
|
||||
for (int i = 0; i < _baseSpawnEnemyIntervals.Length; i++)
|
||||
{
|
||||
_spawnEnemyIntervals[i] = GetScaledInterval(_baseSpawnEnemyIntervals[i], _spawnRateScale);
|
||||
}
|
||||
|
||||
_nextSpawnTimes = (float[])_spawnEnemyIntervals.Clone();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _baseSpawnEnemyIntervals.Length; i++)
|
||||
{
|
||||
float oldInterval = GetScaledInterval(_baseSpawnEnemyIntervals[i], oldScale);
|
||||
float newInterval = GetScaledInterval(_baseSpawnEnemyIntervals[i], _spawnRateScale);
|
||||
|
||||
float remainTime = Mathf.Max(0f, _nextSpawnTimes[i] - _spawnEnemyTimer);
|
||||
float remainRatio = oldInterval > Mathf.Epsilon ? Mathf.Clamp01(remainTime / oldInterval) : 0f;
|
||||
|
||||
_spawnEnemyIntervals[i] = newInterval;
|
||||
_nextSpawnTimes[i] = _spawnEnemyTimer + newInterval * remainRatio;
|
||||
}
|
||||
}
|
||||
|
||||
private static float GetScaledInterval(float baseInterval, float scale)
|
||||
{
|
||||
float safeScale = Mathf.Max(MinSpawnRateScale, scale);
|
||||
return baseInterval / safeScale;
|
||||
}
|
||||
|
||||
#region Event Handler
|
||||
|
||||
private void OnShowEntitySuccess(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is ShowEntitySuccessEventArgs ne)) return;
|
||||
|
||||
string entityGroupName = ne.Entity?.EntityGroup?.Name;
|
||||
|
||||
if (entityGroupName == EnemyGroupName && ne.Entity.Logic is EnemyBase enemy)
|
||||
{
|
||||
_currentEnemyCount++;
|
||||
enemy.SetTarget(_player);
|
||||
RemoveEnemyFromCache(enemy.Id);
|
||||
_enemies.Add(enemy);
|
||||
_enemyById[enemy.Id] = enemy;
|
||||
}
|
||||
|
||||
if (ne.EntityLogicType == typeof(Player))
|
||||
{
|
||||
_player = ne.Entity.transform;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
||||
{
|
||||
if (e is HideEntityCompleteEventArgs ne)
|
||||
{
|
||||
string entityGroupName = ne.EntityGroup.Name;
|
||||
if (entityGroupName == EnemyGroupName)
|
||||
{
|
||||
if (_currentEnemyCount > 0)
|
||||
{
|
||||
_currentEnemyCount--;
|
||||
}
|
||||
|
||||
RemoveEnemyFromCache(ne.EntityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveEnemyFromCache(int entityId)
|
||||
{
|
||||
if (_enemyById != null)
|
||||
{
|
||||
_enemyById.Remove(entityId);
|
||||
}
|
||||
|
||||
for (int i = _enemies.Count - 1; i >= 0; i--)
|
||||
{
|
||||
EntityBase cachedEnemy = _enemies[i];
|
||||
if (cachedEnemy == null || cachedEnemy.Id == entityId)
|
||||
{
|
||||
if (cachedEnemy != null && _enemyById != null)
|
||||
{
|
||||
_enemyById.Remove(cachedEnemy.Id);
|
||||
}
|
||||
|
||||
_enemies.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d840df1b53db744cb522b82426f2cd5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f75cb14d28565904e89eb5765829c210
|
||||
folderAsset: yes
|
||||
timeCreated: 1528026155
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,125 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework.ObjectPool;
|
||||
using System.Collections.Generic;
|
||||
using Entity;
|
||||
using StarForce;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace CustomComponent
|
||||
{
|
||||
public class HPBarComponent : GameFrameworkComponent
|
||||
{
|
||||
[SerializeField] private HPBarItem _hpBarItemTemplate = null;
|
||||
|
||||
[SerializeField] private Transform _hpBarInstanceRoot = null;
|
||||
|
||||
[SerializeField] private int _instancePoolCapacity = 16;
|
||||
|
||||
private IObjectPool<HPBarItemObject> _hpBarItemObjectPool = null;
|
||||
private List<HPBarItem> _activeHPBarItems = null;
|
||||
private Canvas _cachedCanvas = null;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (_hpBarInstanceRoot == null)
|
||||
{
|
||||
Log.Error("You must set HP bar instance root first.");
|
||||
return;
|
||||
}
|
||||
|
||||
_cachedCanvas = _hpBarInstanceRoot.GetComponent<Canvas>();
|
||||
_hpBarItemObjectPool =
|
||||
GameEntry.ObjectPool.CreateSingleSpawnObjectPool<HPBarItemObject>("HPBarItem", _instancePoolCapacity);
|
||||
_activeHPBarItems = new List<HPBarItem>();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
for (int i = _activeHPBarItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
HPBarItem hpBarItem = _activeHPBarItems[i];
|
||||
if (hpBarItem.Refresh())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HideHPBar(hpBarItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowHPBar(EntityBase entity, float fromHPRatio, float toHPRatio)
|
||||
{
|
||||
if (entity == null)
|
||||
{
|
||||
Log.Warning("Entity is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
HPBarItem hpBarItem = GetActiveHPBarItem(entity);
|
||||
if (hpBarItem == null)
|
||||
{
|
||||
hpBarItem = CreateHPBarItem(entity);
|
||||
_activeHPBarItems.Add(hpBarItem);
|
||||
}
|
||||
|
||||
hpBarItem.Init(entity, _cachedCanvas, fromHPRatio, toHPRatio);
|
||||
}
|
||||
|
||||
private void HideHPBar(HPBarItem hpBarItem)
|
||||
{
|
||||
hpBarItem.Reset();
|
||||
_activeHPBarItems.Remove(hpBarItem);
|
||||
_hpBarItemObjectPool.Unspawn(hpBarItem);
|
||||
}
|
||||
|
||||
private HPBarItem GetActiveHPBarItem(EntityBase entity)
|
||||
{
|
||||
if (entity == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _activeHPBarItems.Count; i++)
|
||||
{
|
||||
if (_activeHPBarItems[i].Owner == entity)
|
||||
{
|
||||
return _activeHPBarItems[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HPBarItem CreateHPBarItem(EntityBase entity)
|
||||
{
|
||||
HPBarItem hpBarItem = null;
|
||||
HPBarItemObject hpBarItemObject = _hpBarItemObjectPool.Spawn();
|
||||
if (hpBarItemObject != null)
|
||||
{
|
||||
hpBarItem = (HPBarItem)hpBarItemObject.Target;
|
||||
}
|
||||
else
|
||||
{
|
||||
hpBarItem = Instantiate(_hpBarItemTemplate);
|
||||
Transform transform = hpBarItem.GetComponent<Transform>();
|
||||
transform.SetParent(_hpBarInstanceRoot);
|
||||
transform.localScale = Vector3.one;
|
||||
_hpBarItemObjectPool.Register(HPBarItemObject.Create(hpBarItem), true);
|
||||
}
|
||||
|
||||
return hpBarItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 879e921586721514f9f85de777050af4
|
||||
timeCreated: 1528026155
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,122 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using System.Collections;
|
||||
using Entity;
|
||||
using UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public class HPBarItem : MonoBehaviour
|
||||
{
|
||||
private const float AnimationSeconds = 0.3f;
|
||||
private const float KeepSeconds = 0.4f;
|
||||
private const float FadeOutSeconds = 0.3f;
|
||||
|
||||
[SerializeField]
|
||||
private Slider m_HPBar = null;
|
||||
|
||||
private Canvas m_ParentCanvas = null;
|
||||
private RectTransform m_CachedTransform = null;
|
||||
private CanvasGroup m_CachedCanvasGroup = null;
|
||||
private EntityBase m_Owner = null;
|
||||
private int m_OwnerId = 0;
|
||||
|
||||
public EntityBase Owner
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_Owner;
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(EntityBase owner, Canvas parentCanvas, float fromHPRatio, float toHPRatio)
|
||||
{
|
||||
if (owner == null)
|
||||
{
|
||||
Log.Error("Owner is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_ParentCanvas = parentCanvas;
|
||||
|
||||
gameObject.SetActive(true);
|
||||
StopAllCoroutines();
|
||||
|
||||
m_CachedCanvasGroup.alpha = 1f;
|
||||
if (m_Owner != owner || m_OwnerId != owner.Id)
|
||||
{
|
||||
m_HPBar.value = fromHPRatio;
|
||||
m_Owner = owner;
|
||||
m_OwnerId = owner.Id;
|
||||
}
|
||||
|
||||
Refresh();
|
||||
|
||||
StartCoroutine(HPBarCo(toHPRatio, AnimationSeconds, KeepSeconds, FadeOutSeconds));
|
||||
}
|
||||
|
||||
public bool Refresh()
|
||||
{
|
||||
if (m_CachedCanvasGroup.alpha <= 0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_Owner != null && Owner.Available && Owner.Id == m_OwnerId)
|
||||
{
|
||||
Vector3 worldPosition = m_Owner.CachedTransform.position + Vector3.forward;
|
||||
Vector3 screenPosition = GameEntry.Scene.MainCamera.WorldToScreenPoint(worldPosition);
|
||||
|
||||
Vector2 position;
|
||||
if (RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)m_ParentCanvas.transform, screenPosition,
|
||||
m_ParentCanvas.worldCamera, out position))
|
||||
{
|
||||
m_CachedTransform.localPosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
StopAllCoroutines();
|
||||
m_CachedCanvasGroup.alpha = 1f;
|
||||
m_HPBar.value = 1f;
|
||||
m_Owner = null;
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
m_CachedTransform = GetComponent<RectTransform>();
|
||||
if (m_CachedTransform == null)
|
||||
{
|
||||
Log.Error("RectTransform is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_CachedCanvasGroup = GetComponent<CanvasGroup>();
|
||||
if (m_CachedCanvasGroup == null)
|
||||
{
|
||||
Log.Error("CanvasGroup is invalid.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator HPBarCo(float value, float animationDuration, float keepDuration, float fadeOutDuration)
|
||||
{
|
||||
yield return m_HPBar.SmoothValue(value, animationDuration);
|
||||
yield return new WaitForSeconds(keepDuration);
|
||||
yield return m_CachedCanvasGroup.FadeToAlpha(0f, fadeOutDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bcc0608685184647b097f4838a8e9f6
|
||||
timeCreated: 1528026155
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
//------------------------------------------------------------
|
||||
// Game Framework
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
// Feedback: mailto:ellan@gameframework.cn
|
||||
//------------------------------------------------------------
|
||||
|
||||
using GameFramework;
|
||||
using GameFramework.ObjectPool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace StarForce
|
||||
{
|
||||
public class HPBarItemObject : ObjectBase
|
||||
{
|
||||
public static HPBarItemObject Create(object target)
|
||||
{
|
||||
HPBarItemObject hpBarItemObject = ReferencePool.Acquire<HPBarItemObject>();
|
||||
hpBarItemObject.Initialize(target);
|
||||
return hpBarItemObject;
|
||||
}
|
||||
|
||||
protected override void Release(bool isShutdown)
|
||||
{
|
||||
HPBarItem hpBarItem = (HPBarItem)Target;
|
||||
if (hpBarItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Object.Destroy(hpBarItem.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e6d2d8655da0094bb6063ce38ed582d
|
||||
timeCreated: 1528026155
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f519a6e4d7b4cb4ab5eabf95fb55e1b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f28ff313e954720a04246191b7f9ddd
|
||||
timeCreated: 1771901064
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
#region Collision Transient
|
||||
|
||||
public int CollisionCandidateCount => _collisionCandidates.IsCreated ? _collisionCandidates.Length : 0;
|
||||
public int PendingAreaCollisionRequestCount => _areaCollisionRequests.Count;
|
||||
public int LastCollisionQueryCount => _lastCollisionQueryCount;
|
||||
public int LastProjectileCollisionQueryCount => _lastProjectileCollisionQueryCount;
|
||||
public int LastAreaCollisionQueryCount => _lastAreaCollisionQueryCount;
|
||||
public int LastCollisionCandidateCount => _lastCollisionCandidateCount;
|
||||
public int LastProjectileCollisionCandidateCount => _lastProjectileCollisionCandidateCount;
|
||||
public int LastAreaCollisionCandidateCount => _lastAreaCollisionCandidateCount;
|
||||
public int LastResolvedAreaHitCount => _lastResolvedAreaHitCount;
|
||||
public float LastCollisionCellSize => _lastCollisionCellSize;
|
||||
public bool LastCollisionHasEnemyTargets => _lastCollisionHasEnemyTargets;
|
||||
|
||||
private void ResetCollisionRuntimeStats()
|
||||
{
|
||||
_lastCollisionQueryCount = 0;
|
||||
_lastProjectileCollisionQueryCount = 0;
|
||||
_lastAreaCollisionQueryCount = 0;
|
||||
_lastCollisionCandidateCount = 0;
|
||||
_lastProjectileCollisionCandidateCount = 0;
|
||||
_lastAreaCollisionCandidateCount = 0;
|
||||
_lastResolvedAreaHitCount = 0;
|
||||
_lastCollisionCellSize = 0f;
|
||||
_lastCollisionHasEnemyTargets = false;
|
||||
}
|
||||
|
||||
private void PrepareCollisionQueryAndCandidateChannels(int queryCount, int expectedCandidateCount,
|
||||
int bucketCapacity)
|
||||
{
|
||||
InitializeJobDataChannels();
|
||||
EnsureCapacity(ref _collisionQueryInputs, queryCount);
|
||||
EnsureCapacity(ref _collisionCandidates, expectedCandidateCount);
|
||||
EnsureCapacity(ref _enemyCollisionBuckets, bucketCapacity);
|
||||
|
||||
_collisionQueryInputs.Clear();
|
||||
_collisionCandidates.Clear();
|
||||
_enemyCollisionBuckets.Clear();
|
||||
}
|
||||
|
||||
private void AddProjectileCollisionQuery(int queryId, in ProjectileJobOutputData projectile, float radius,
|
||||
int maxTargets = 1)
|
||||
{
|
||||
if (!_collisionQueryInputs.IsCreated || radius <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_collisionQueryInputs.Add(new CollisionQueryData
|
||||
{
|
||||
QueryId = queryId,
|
||||
SourceType = CollisionSourceTypeProjectile,
|
||||
SourceEntityId = projectile.EntityId,
|
||||
SourceOwnerEntityId = projectile.OwnerEntityId,
|
||||
SourceWasActiveAtQueryTime = true,
|
||||
Position = projectile.Position,
|
||||
Radius = radius,
|
||||
MaxTargets = math.max(1, maxTargets),
|
||||
ShapeType = CollisionShapeCircle,
|
||||
Direction = new float3(0f, 0f, 1f),
|
||||
HalfAngleDeg = 180f,
|
||||
HalfWidth = 0f,
|
||||
HalfLength = 0f
|
||||
});
|
||||
}
|
||||
|
||||
private void AddAreaCollisionQuery(int queryId, int sourceEntityId, int sourceOwnerEntityId,
|
||||
bool sourceWasActiveAtQueryTime, in Vector3 center, float radius, int maxTargets, int shapeType,
|
||||
in Vector3 direction, float halfAngleDeg, float halfWidth, float halfLength)
|
||||
{
|
||||
if (!_collisionQueryInputs.IsCreated || radius <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 normalizedDirection = direction;
|
||||
normalizedDirection.y = 0f;
|
||||
if (normalizedDirection.sqrMagnitude <= Mathf.Epsilon)
|
||||
{
|
||||
normalizedDirection = Vector3.forward;
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedDirection.Normalize();
|
||||
}
|
||||
|
||||
_collisionQueryInputs.Add(new CollisionQueryData
|
||||
{
|
||||
QueryId = queryId,
|
||||
SourceType = CollisionSourceTypeArea,
|
||||
SourceEntityId = sourceEntityId,
|
||||
SourceOwnerEntityId = sourceOwnerEntityId,
|
||||
SourceWasActiveAtQueryTime = sourceWasActiveAtQueryTime,
|
||||
Position = new float3(center.x, center.y, center.z),
|
||||
Radius = radius,
|
||||
MaxTargets = math.max(1, maxTargets),
|
||||
ShapeType = shapeType,
|
||||
Direction = new float3(normalizedDirection.x, normalizedDirection.y, normalizedDirection.z),
|
||||
HalfAngleDeg = Mathf.Clamp(halfAngleDeg, 0f, 180f),
|
||||
HalfWidth = Mathf.Max(0f, halfWidth),
|
||||
HalfLength = Mathf.Max(0f, halfLength)
|
||||
});
|
||||
}
|
||||
|
||||
private void AddCollisionCandidate(int queryId, int sourceType, int sourceEntityId, int sourceOwnerEntityId,
|
||||
int targetEntityId, float sqrDistance)
|
||||
{
|
||||
if (!_collisionCandidates.IsCreated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_collisionCandidates.Add(new CollisionCandidateData
|
||||
{
|
||||
QueryId = queryId,
|
||||
SourceType = sourceType,
|
||||
SourceEntityId = sourceEntityId,
|
||||
SourceOwnerEntityId = sourceOwnerEntityId,
|
||||
TargetEntityId = targetEntityId,
|
||||
SqrDistance = sqrDistance
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b0eaf024db24511988ba3bde25359e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
#region Enemy Separation Temporal
|
||||
|
||||
private void PrepareEnemySeparationJobBuffers(int enemyCount, int bucketCapacity)
|
||||
{
|
||||
InitializeJobDataChannels();
|
||||
EnsureCapacity(ref _enemyJobSeparationOutputs, enemyCount);
|
||||
_enemyJobSeparationOutputs.Clear();
|
||||
if (enemyCount > 0)
|
||||
{
|
||||
_enemyJobSeparationOutputs.ResizeUninitialized(enemyCount);
|
||||
}
|
||||
|
||||
EnsureCapacity(ref _enemySeparationBuckets, bucketCapacity);
|
||||
_enemySeparationBuckets.Clear();
|
||||
|
||||
EnsureCapacity(ref _enemySeparationPreviousPushes, enemyCount);
|
||||
EnsureCapacity(ref _enemySeparationCurrentPushes, enemyCount);
|
||||
|
||||
if (_enemySeparationPreviousPushes.Length < enemyCount)
|
||||
{
|
||||
int oldLength = _enemySeparationPreviousPushes.Length;
|
||||
_enemySeparationPreviousPushes.ResizeUninitialized(enemyCount);
|
||||
for (int i = oldLength; i < enemyCount; i++)
|
||||
{
|
||||
_enemySeparationPreviousPushes[i] = float2.zero;
|
||||
}
|
||||
}
|
||||
else if (_enemySeparationPreviousPushes.Length > enemyCount)
|
||||
{
|
||||
_enemySeparationPreviousPushes.ResizeUninitialized(enemyCount);
|
||||
}
|
||||
|
||||
_enemySeparationCurrentPushes.Clear();
|
||||
if (enemyCount > 0)
|
||||
{
|
||||
_enemySeparationCurrentPushes.ResizeUninitialized(enemyCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void CommitEnemySeparationTemporalBuffers(int enemyCount)
|
||||
{
|
||||
if (!_enemySeparationPreviousPushes.IsCreated || !_enemySeparationCurrentPushes.IsCreated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int copyCount = math.min(enemyCount,
|
||||
math.min(_enemySeparationPreviousPushes.Length, _enemySeparationCurrentPushes.Length));
|
||||
for (int i = 0; i < copyCount; i++)
|
||||
{
|
||||
_enemySeparationPreviousPushes[i] = _enemySeparationCurrentPushes[i];
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnemyAddedToSeparationTemporalBuffers()
|
||||
{
|
||||
if (_enemySeparationPreviousPushes.IsCreated)
|
||||
{
|
||||
_enemySeparationPreviousPushes.Add(float2.zero);
|
||||
}
|
||||
|
||||
if (_enemySeparationCurrentPushes.IsCreated)
|
||||
{
|
||||
_enemySeparationCurrentPushes.Add(float2.zero);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnemyRemovedFromSeparationTemporalBuffers(int removedIndex)
|
||||
{
|
||||
if (_enemySeparationPreviousPushes.IsCreated && removedIndex >= 0 &&
|
||||
removedIndex < _enemySeparationPreviousPushes.Length)
|
||||
{
|
||||
_enemySeparationPreviousPushes.RemoveAtSwapBack(removedIndex);
|
||||
}
|
||||
|
||||
if (_enemySeparationCurrentPushes.IsCreated && removedIndex >= 0 &&
|
||||
removedIndex < _enemySeparationCurrentPushes.Length)
|
||||
{
|
||||
_enemySeparationCurrentPushes.RemoveAtSwapBack(removedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2c8675358384b21925e6f660ce61a79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
// Shared channel constants plus compatibility fields still reflected by tests.
|
||||
private const int CollisionSourceTypeProjectile = 1;
|
||||
private const int CollisionSourceTypeArea = 2;
|
||||
private const int CollisionShapeCircle = 0;
|
||||
private const int CollisionShapeSector = 1;
|
||||
private const int CollisionShapeRectangle = 2;
|
||||
|
||||
// Kept as top-level fields because current regression tests reflect them directly.
|
||||
private NativeList<CollisionQueryData> _collisionQueryInputs;
|
||||
private readonly List<AreaCollisionRequestData> _areaCollisionRequests = new(16);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20926de0e6e14c7b818779593f1f87bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
#region Job Data Conversion
|
||||
|
||||
private void SyncSimulationStateToJobInputs()
|
||||
{
|
||||
InitializeJobDataChannels();
|
||||
EnsureCapacity(ref _enemyJobInputs, _enemies.Count);
|
||||
EnsureCapacity(ref _projectileJobInputs, _projectiles.Count);
|
||||
|
||||
_enemyJobInputs.Clear();
|
||||
_projectileJobInputs.Clear();
|
||||
|
||||
foreach (EnemySimData data in _enemies)
|
||||
{
|
||||
_enemyJobInputs.Add(ConvertToEnemyJobInput(data));
|
||||
}
|
||||
|
||||
foreach (ProjectileSimData data in _projectiles)
|
||||
{
|
||||
_projectileJobInputs.Add(ConvertToProjectileJobInput(data));
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareEnemyJobOutputBuffer(int enemyCount)
|
||||
{
|
||||
InitializeJobDataChannels();
|
||||
EnsureCapacity(ref _enemyJobOutputs, enemyCount);
|
||||
_enemyJobOutputs.Clear();
|
||||
|
||||
if (enemyCount > 0)
|
||||
{
|
||||
_enemyJobOutputs.ResizeUninitialized(enemyCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareProjectileJobOutputBuffer(int projectileCount)
|
||||
{
|
||||
InitializeJobDataChannels();
|
||||
EnsureCapacity(ref _projectileJobOutputs, projectileCount);
|
||||
_projectileJobOutputs.Clear();
|
||||
|
||||
if (projectileCount > 0)
|
||||
{
|
||||
_projectileJobOutputs.ResizeUninitialized(projectileCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void CopyProjectileInputsToOutputs()
|
||||
{
|
||||
for (int i = 0; i < _projectileJobInputs.Length; i++)
|
||||
{
|
||||
ProjectileJobInputData input = _projectileJobInputs[i];
|
||||
_projectileJobOutputs[i] = new ProjectileJobOutputData
|
||||
{
|
||||
EntityId = input.EntityId,
|
||||
OwnerEntityId = input.OwnerEntityId,
|
||||
Position = input.Position,
|
||||
Forward = input.Forward,
|
||||
Velocity = input.Velocity,
|
||||
Speed = input.Speed,
|
||||
LifeTime = input.LifeTime,
|
||||
Age = input.Age,
|
||||
Active = input.Active,
|
||||
RemainingLifetime = input.RemainingLifetime,
|
||||
State = input.State
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static EnemyJobInputData ConvertToEnemyJobInput(in EnemySimData enemy)
|
||||
{
|
||||
return new EnemyJobInputData
|
||||
{
|
||||
EntityId = enemy.EntityId,
|
||||
Position = new float3(enemy.Position.x, enemy.Position.y, enemy.Position.z),
|
||||
Forward = new float3(enemy.Forward.x, enemy.Forward.y, enemy.Forward.z),
|
||||
Rotation = new quaternion(enemy.Rotation.x, enemy.Rotation.y, enemy.Rotation.z, enemy.Rotation.w),
|
||||
Speed = enemy.Speed,
|
||||
AttackRange = enemy.AttackRange,
|
||||
AvoidEnemyOverlap = enemy.AvoidEnemyOverlap,
|
||||
EnemyBodyRadius = enemy.EnemyBodyRadius,
|
||||
SeparationIterations = enemy.SeparationIterations,
|
||||
TargetType = enemy.TargetType,
|
||||
State = enemy.State
|
||||
};
|
||||
}
|
||||
|
||||
private static EnemySimData ConvertToEnemySimData(in EnemyJobOutputData enemy)
|
||||
{
|
||||
return new EnemySimData
|
||||
{
|
||||
EntityId = enemy.EntityId,
|
||||
Position = new Vector3(enemy.Position.x, enemy.Position.y, enemy.Position.z),
|
||||
Forward = new Vector3(enemy.Forward.x, enemy.Forward.y, enemy.Forward.z),
|
||||
Rotation = new Quaternion(enemy.Rotation.value.x, enemy.Rotation.value.y, enemy.Rotation.value.z,
|
||||
enemy.Rotation.value.w),
|
||||
Speed = enemy.Speed,
|
||||
AttackRange = enemy.AttackRange,
|
||||
AvoidEnemyOverlap = enemy.AvoidEnemyOverlap,
|
||||
EnemyBodyRadius = enemy.EnemyBodyRadius,
|
||||
SeparationIterations = enemy.SeparationIterations,
|
||||
TargetType = enemy.TargetType,
|
||||
State = enemy.State
|
||||
};
|
||||
}
|
||||
|
||||
private static ProjectileJobInputData ConvertToProjectileJobInput(in ProjectileSimData projectile)
|
||||
{
|
||||
return new ProjectileJobInputData
|
||||
{
|
||||
EntityId = projectile.EntityId,
|
||||
OwnerEntityId = projectile.OwnerEntityId,
|
||||
Position = new float3(projectile.Position.x, projectile.Position.y, projectile.Position.z),
|
||||
Forward = new float3(projectile.Forward.x, projectile.Forward.y, projectile.Forward.z),
|
||||
Velocity = new float3(projectile.Velocity.x, projectile.Velocity.y, projectile.Velocity.z),
|
||||
Speed = projectile.Speed,
|
||||
LifeTime = projectile.LifeTime,
|
||||
Age = projectile.Age,
|
||||
Active = projectile.Active,
|
||||
RemainingLifetime = projectile.RemainingLifetime,
|
||||
State = projectile.State
|
||||
};
|
||||
}
|
||||
|
||||
private static ProjectileSimData ConvertToProjectileSimData(in ProjectileJobOutputData projectile)
|
||||
{
|
||||
return new ProjectileSimData
|
||||
{
|
||||
EntityId = projectile.EntityId,
|
||||
OwnerEntityId = projectile.OwnerEntityId,
|
||||
Position = new Vector3(projectile.Position.x, projectile.Position.y, projectile.Position.z),
|
||||
Forward = new Vector3(projectile.Forward.x, projectile.Forward.y, projectile.Forward.z),
|
||||
Velocity = new Vector3(projectile.Velocity.x, projectile.Velocity.y, projectile.Velocity.z),
|
||||
Speed = projectile.Speed,
|
||||
LifeTime = projectile.LifeTime,
|
||||
Age = projectile.Age,
|
||||
Active = projectile.Active,
|
||||
RemainingLifetime = projectile.RemainingLifetime,
|
||||
State = projectile.State
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57a3f582d9c04803908205e0cd4b5b75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
#region Job Data Lifecycle
|
||||
|
||||
private void InitializeJobDataChannels()
|
||||
{
|
||||
if (AreJobDataChannelsUsable())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DisposeJobDataChannels();
|
||||
_enemyJobInputs = new NativeList<EnemyJobInputData>(64, Allocator.Persistent);
|
||||
_enemyJobOutputs = new NativeList<EnemyJobOutputData>(64, Allocator.Persistent);
|
||||
_enemyJobSeparationOutputs = new NativeList<EnemyJobOutputData>(64, Allocator.Persistent);
|
||||
_enemySeparationPreviousPushes = new NativeList<float2>(64, Allocator.Persistent);
|
||||
_enemySeparationCurrentPushes = new NativeList<float2>(64, Allocator.Persistent);
|
||||
_projectileJobInputs = new NativeList<ProjectileJobInputData>(64, Allocator.Persistent);
|
||||
_projectileJobOutputs = new NativeList<ProjectileJobOutputData>(64, Allocator.Persistent);
|
||||
_collisionQueryInputs = new NativeList<CollisionQueryData>(64, Allocator.Persistent);
|
||||
_collisionCandidates = new NativeList<CollisionCandidateData>(128, Allocator.Persistent);
|
||||
_enemySeparationBuckets = new NativeParallelMultiHashMap<long, int>(256, Allocator.Persistent);
|
||||
_enemyCollisionBuckets = new NativeParallelMultiHashMap<long, int>(256, Allocator.Persistent);
|
||||
InitializeEnemyTargetSpatialIndex();
|
||||
}
|
||||
|
||||
private void DisposeJobDataChannels()
|
||||
{
|
||||
if (_enemyJobInputs.IsCreated)
|
||||
{
|
||||
_enemyJobInputs.Dispose();
|
||||
}
|
||||
|
||||
_enemyJobInputs = default;
|
||||
|
||||
if (_enemyJobOutputs.IsCreated)
|
||||
{
|
||||
_enemyJobOutputs.Dispose();
|
||||
}
|
||||
|
||||
_enemyJobOutputs = default;
|
||||
|
||||
if (_enemyJobSeparationOutputs.IsCreated)
|
||||
{
|
||||
_enemyJobSeparationOutputs.Dispose();
|
||||
}
|
||||
|
||||
_enemyJobSeparationOutputs = default;
|
||||
|
||||
if (_enemySeparationPreviousPushes.IsCreated)
|
||||
{
|
||||
_enemySeparationPreviousPushes.Dispose();
|
||||
}
|
||||
|
||||
_enemySeparationPreviousPushes = default;
|
||||
|
||||
if (_enemySeparationCurrentPushes.IsCreated)
|
||||
{
|
||||
_enemySeparationCurrentPushes.Dispose();
|
||||
}
|
||||
|
||||
_enemySeparationCurrentPushes = default;
|
||||
|
||||
if (_projectileJobInputs.IsCreated)
|
||||
{
|
||||
_projectileJobInputs.Dispose();
|
||||
}
|
||||
|
||||
_projectileJobInputs = default;
|
||||
|
||||
if (_projectileJobOutputs.IsCreated)
|
||||
{
|
||||
_projectileJobOutputs.Dispose();
|
||||
}
|
||||
|
||||
_projectileJobOutputs = default;
|
||||
|
||||
if (_collisionQueryInputs.IsCreated)
|
||||
{
|
||||
_collisionQueryInputs.Dispose();
|
||||
}
|
||||
|
||||
_collisionQueryInputs = default;
|
||||
|
||||
if (_collisionCandidates.IsCreated)
|
||||
{
|
||||
_collisionCandidates.Dispose();
|
||||
}
|
||||
|
||||
_collisionCandidates = default;
|
||||
|
||||
if (_enemySeparationBuckets.IsCreated)
|
||||
{
|
||||
_enemySeparationBuckets.Dispose();
|
||||
}
|
||||
|
||||
_enemySeparationBuckets = default;
|
||||
|
||||
if (_enemyCollisionBuckets.IsCreated)
|
||||
{
|
||||
_enemyCollisionBuckets.Dispose();
|
||||
}
|
||||
|
||||
_enemyCollisionBuckets = default;
|
||||
|
||||
DisposeEnemyTargetSpatialIndex();
|
||||
_areaCollisionRequests.Clear();
|
||||
_areaCollisionHitEvents.Clear();
|
||||
_areaCollisionHitDedupKeys.Clear();
|
||||
ResetCollisionRuntimeStats();
|
||||
}
|
||||
|
||||
private void ClearJobDataChannels()
|
||||
{
|
||||
if (!AreJobDataChannelsUsable())
|
||||
{
|
||||
_areaCollisionRequests.Clear();
|
||||
_areaCollisionHitEvents.Clear();
|
||||
_areaCollisionHitDedupKeys.Clear();
|
||||
ResetCollisionRuntimeStats();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_enemyJobInputs.IsCreated)
|
||||
{
|
||||
_enemyJobInputs.Clear();
|
||||
}
|
||||
|
||||
if (_enemyJobOutputs.IsCreated)
|
||||
{
|
||||
_enemyJobOutputs.Clear();
|
||||
}
|
||||
|
||||
if (_projectileJobInputs.IsCreated)
|
||||
{
|
||||
_projectileJobInputs.Clear();
|
||||
}
|
||||
|
||||
if (_projectileJobOutputs.IsCreated)
|
||||
{
|
||||
_projectileJobOutputs.Clear();
|
||||
}
|
||||
|
||||
if (_collisionQueryInputs.IsCreated)
|
||||
{
|
||||
_collisionQueryInputs.Clear();
|
||||
}
|
||||
|
||||
if (_collisionCandidates.IsCreated)
|
||||
{
|
||||
_collisionCandidates.Clear();
|
||||
}
|
||||
|
||||
if (_enemyJobSeparationOutputs.IsCreated)
|
||||
{
|
||||
_enemyJobSeparationOutputs.Clear();
|
||||
}
|
||||
|
||||
if (_enemySeparationPreviousPushes.IsCreated)
|
||||
{
|
||||
_enemySeparationPreviousPushes.Clear();
|
||||
}
|
||||
|
||||
if (_enemySeparationCurrentPushes.IsCreated)
|
||||
{
|
||||
_enemySeparationCurrentPushes.Clear();
|
||||
}
|
||||
|
||||
if (_enemySeparationBuckets.IsCreated)
|
||||
{
|
||||
_enemySeparationBuckets.Clear();
|
||||
}
|
||||
|
||||
if (_enemyCollisionBuckets.IsCreated)
|
||||
{
|
||||
_enemyCollisionBuckets.Clear();
|
||||
}
|
||||
|
||||
ClearEnemyTargetSpatialIndex();
|
||||
_areaCollisionRequests.Clear();
|
||||
_areaCollisionHitEvents.Clear();
|
||||
_areaCollisionHitDedupKeys.Clear();
|
||||
ResetCollisionRuntimeStats();
|
||||
}
|
||||
|
||||
private bool AreJobDataChannelsUsable()
|
||||
{
|
||||
return IsNativeListUsable(_enemyJobInputs) &&
|
||||
IsNativeListUsable(_enemyJobOutputs) &&
|
||||
IsNativeListUsable(_enemyJobSeparationOutputs) &&
|
||||
IsNativeListUsable(_enemySeparationPreviousPushes) &&
|
||||
IsNativeListUsable(_enemySeparationCurrentPushes) &&
|
||||
IsNativeListUsable(_projectileJobInputs) &&
|
||||
IsNativeListUsable(_projectileJobOutputs) &&
|
||||
IsNativeListUsable(_collisionQueryInputs) &&
|
||||
IsNativeListUsable(_collisionCandidates) &&
|
||||
IsNativeMultiHashMapUsable(_enemySeparationBuckets) &&
|
||||
IsNativeMultiHashMapUsable(_enemyCollisionBuckets);
|
||||
}
|
||||
|
||||
private static void EnsureCapacity<T>(ref NativeList<T> nativeList, int targetCount) where T : unmanaged
|
||||
{
|
||||
if (!nativeList.IsCreated || targetCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (nativeList.Capacity < targetCount)
|
||||
{
|
||||
nativeList.Capacity = targetCount;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureCapacity(ref NativeParallelMultiHashMap<long, int> hashMap, int targetCount)
|
||||
{
|
||||
if (!hashMap.IsCreated || targetCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (hashMap.Capacity < targetCount)
|
||||
{
|
||||
hashMap.Capacity = targetCount;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNativeListUsable<T>(NativeList<T> nativeList) where T : unmanaged
|
||||
{
|
||||
if (!nativeList.IsCreated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ = nativeList.Length;
|
||||
return true;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNativeMultiHashMapUsable(NativeParallelMultiHashMap<long, int> hashMap)
|
||||
{
|
||||
if (!hashMap.IsCreated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ = hashMap.Count();
|
||||
return true;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2e46cc42cdd45f5badd02bd4635ba97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
#region Job Output Commit
|
||||
|
||||
private void ApplyJobOutputsToSimulationState()
|
||||
{
|
||||
int enemyCount = Mathf.Min(_enemies.Count, _enemyJobOutputs.Length);
|
||||
bool hasEnemyPositionChanged = false;
|
||||
for (int i = 0; i < enemyCount; i++)
|
||||
{
|
||||
EnemyJobOutputData output = _enemyJobOutputs[i];
|
||||
if (!hasEnemyPositionChanged)
|
||||
{
|
||||
Vector3 currentPosition = _enemies[i].Position;
|
||||
hasEnemyPositionChanged = currentPosition.x != output.Position.x ||
|
||||
currentPosition.z != output.Position.z;
|
||||
}
|
||||
|
||||
_enemies[i] = ConvertToEnemySimData(output);
|
||||
}
|
||||
|
||||
if (hasEnemyPositionChanged)
|
||||
{
|
||||
MarkEnemyTargetSpatialIndexDirty();
|
||||
}
|
||||
|
||||
int projectileCount = Mathf.Min(_projectiles.Count, _projectileJobOutputs.Length);
|
||||
for (int i = 0; i < projectileCount; i++)
|
||||
{
|
||||
_projectiles[i] = ConvertToProjectileSimData(_projectileJobOutputs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59ae2375305641a6a36d1bea0bf1abc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed class EntityBinding
|
||||
{
|
||||
private readonly Dictionary<int, int> _entityIdToSimulationIndex = new Dictionary<int, int>();
|
||||
private readonly Dictionary<int, int> _simulationIndexToEntityId = new Dictionary<int, int>();
|
||||
|
||||
public int Count => _entityIdToSimulationIndex.Count;
|
||||
|
||||
public void Bind(int entityId, int simulationIndex)
|
||||
{
|
||||
if (_entityIdToSimulationIndex.TryGetValue(entityId, out int oldSimulationIndex))
|
||||
{
|
||||
_simulationIndexToEntityId.Remove(oldSimulationIndex);
|
||||
}
|
||||
|
||||
if (_simulationIndexToEntityId.TryGetValue(simulationIndex, out int oldEntityId))
|
||||
{
|
||||
_entityIdToSimulationIndex.Remove(oldEntityId);
|
||||
}
|
||||
|
||||
_entityIdToSimulationIndex[entityId] = simulationIndex;
|
||||
_simulationIndexToEntityId[simulationIndex] = entityId;
|
||||
}
|
||||
|
||||
public void RemapIndex(int entityId, int newSimulationIndex)
|
||||
{
|
||||
if (!_entityIdToSimulationIndex.TryGetValue(entityId, out int oldSimulationIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_simulationIndexToEntityId.TryGetValue(newSimulationIndex, out int oldEntityId) &&
|
||||
oldEntityId != entityId)
|
||||
{
|
||||
_entityIdToSimulationIndex.Remove(oldEntityId);
|
||||
}
|
||||
|
||||
_simulationIndexToEntityId.Remove(oldSimulationIndex);
|
||||
_entityIdToSimulationIndex[entityId] = newSimulationIndex;
|
||||
_simulationIndexToEntityId[newSimulationIndex] = entityId;
|
||||
}
|
||||
|
||||
public bool TryGetSimulationIndex(int entityId, out int simulationIndex)
|
||||
{
|
||||
return _entityIdToSimulationIndex.TryGetValue(entityId, out simulationIndex);
|
||||
}
|
||||
|
||||
public bool TryGetEntityId(int simulationIndex, out int entityId)
|
||||
{
|
||||
return _simulationIndexToEntityId.TryGetValue(simulationIndex, out entityId);
|
||||
}
|
||||
|
||||
public bool UnbindByEntityId(int entityId)
|
||||
{
|
||||
if (!_entityIdToSimulationIndex.TryGetValue(entityId, out int simulationIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_entityIdToSimulationIndex.Remove(entityId);
|
||||
_simulationIndexToEntityId.Remove(simulationIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool UnbindBySimulationIndex(int simulationIndex)
|
||||
{
|
||||
if (!_simulationIndexToEntityId.TryGetValue(simulationIndex, out int entityId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_simulationIndexToEntityId.Remove(simulationIndex);
|
||||
_entityIdToSimulationIndex.Remove(entityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_entityIdToSimulationIndex.Clear();
|
||||
_simulationIndexToEntityId.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4f6d93561db4b9092ab61182f2983d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f549ca9decb4f38933329abacbf78ac
|
||||
timeCreated: 1771901120
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
private struct AreaCollisionHitEventData
|
||||
{
|
||||
public int QueryId;
|
||||
public int SourceEntityId;
|
||||
public int SourceOwnerEntityId;
|
||||
public int TargetEntityId;
|
||||
public float SqrDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5140d989cba042e6bcb217893491273d
|
||||
timeCreated: 1771901785
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
private struct AreaCollisionRequestData
|
||||
{
|
||||
public int SourceEntityId;
|
||||
public int SourceOwnerEntityId;
|
||||
public bool SourceWasActiveAtQueryTime;
|
||||
public Vector3 Center;
|
||||
public float Radius;
|
||||
public int MaxTargets;
|
||||
public int ShapeType;
|
||||
public Vector3 Direction;
|
||||
public float HalfAngleDeg;
|
||||
public float HalfWidth;
|
||||
public float HalfLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e32f4e0724314c70882f5ed043a4dca4
|
||||
timeCreated: 1771901749
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed partial class SimulationWorld
|
||||
{
|
||||
[BurstCompile]
|
||||
private struct BuildEnemySeparationBucketsBurstJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<EnemyJobOutputData> Inputs;
|
||||
public NativeParallelMultiHashMap<long, int>.ParallelWriter Buckets;
|
||||
public float CellSize;
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
BuildEnemySeparationBucket(
|
||||
index,
|
||||
Inputs,
|
||||
Buckets,
|
||||
CellSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void BuildEnemySeparationBucket(
|
||||
int index,
|
||||
NativeArray<EnemyJobOutputData> inputs,
|
||||
NativeParallelMultiHashMap<long, int>.ParallelWriter buckets,
|
||||
float cellSize
|
||||
)
|
||||
{
|
||||
EnemyJobOutputData output = inputs[index];
|
||||
if (!output.AvoidEnemyOverlap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float3 position = output.Position;
|
||||
position.y = 0f;
|
||||
int cellX = (int)math.floor(position.x / cellSize);
|
||||
int cellZ = (int)math.floor(position.z / cellSize);
|
||||
buckets.Add(SeparationCellKey(cellX, cellZ), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e833e91677bb49a4a458c9c8b90099e9
|
||||
timeCreated: 1771901500
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user