该项目的目的是同时测试和演示来自 Unity DOTS 技术堆栈的多个新包。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

241 行
12 KiB

using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Jobs.LowLevel.Unsafe;
using Unity.Networking.Transport.Utilities;
using Unity.NetCode;
using Unity.Entities;
[UpdateInGroup(typeof(GhostUpdateSystemGroup))]
public class GameModeGhostUpdateSystem : JobComponentSystem
{
[BurstCompile]
struct UpdateInterpolatedJob : IJobChunk
{
[ReadOnly] public NativeHashMap<int, GhostEntity> GhostMap;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
[NativeDisableContainerSafetyRestriction] public NativeArray<uint> minMaxSnapshotTick;
#pragma warning disable 649
[NativeSetThreadIndex]
public int ThreadIndex;
#pragma warning restore 649
#endif
[ReadOnly] public ArchetypeChunkBufferType<GameModeSnapshotData> ghostSnapshotDataType;
[ReadOnly] public ArchetypeChunkEntityType ghostEntityType;
public ArchetypeChunkComponentType<GameModeData> ghostGameModeDataType;
public uint targetTick;
public float targetTickFraction;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
{
var deserializerState = new GhostDeserializerState
{
GhostMap = GhostMap
};
var ghostEntityArray = chunk.GetNativeArray(ghostEntityType);
var ghostSnapshotDataArray = chunk.GetBufferAccessor(ghostSnapshotDataType);
var ghostGameModeDataArray = chunk.GetNativeArray(ghostGameModeDataType);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
var minMaxOffset = ThreadIndex * (JobsUtility.CacheLineSize/4);
#endif
for (int entityIndex = 0; entityIndex < ghostEntityArray.Length; ++entityIndex)
{
var snapshot = ghostSnapshotDataArray[entityIndex];
#if UNITY_EDITOR || DEVELOPMENT_BUILD
var latestTick = snapshot.GetLatestTick();
if (latestTick != 0)
{
if (minMaxSnapshotTick[minMaxOffset] == 0 || SequenceHelpers.IsNewer(minMaxSnapshotTick[minMaxOffset], latestTick))
minMaxSnapshotTick[minMaxOffset] = latestTick;
if (minMaxSnapshotTick[minMaxOffset + 1] == 0 || SequenceHelpers.IsNewer(latestTick, minMaxSnapshotTick[minMaxOffset + 1]))
minMaxSnapshotTick[minMaxOffset + 1] = latestTick;
}
#endif
GameModeSnapshotData snapshotData;
snapshot.GetDataAtTick(targetTick, targetTickFraction, out snapshotData);
var ghostGameModeData = ghostGameModeDataArray[entityIndex];
ghostGameModeData.gameTimerSeconds = snapshotData.GetGameModeDatagameTimerSeconds(deserializerState);
ghostGameModeData.gameTimerMessage = snapshotData.GetGameModeDatagameTimerMessage(deserializerState);
ghostGameModeData.teamName0 = snapshotData.GetGameModeDatateamName0(deserializerState);
ghostGameModeData.teamName1 = snapshotData.GetGameModeDatateamName1(deserializerState);
ghostGameModeData.teamScore0 = snapshotData.GetGameModeDatateamScore0(deserializerState);
ghostGameModeData.teamScore1 = snapshotData.GetGameModeDatateamScore1(deserializerState);
ghostGameModeDataArray[entityIndex] = ghostGameModeData;
}
}
}
[BurstCompile]
struct UpdatePredictedJob : IJobChunk
{
[ReadOnly] public NativeHashMap<int, GhostEntity> GhostMap;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
[NativeDisableContainerSafetyRestriction] public NativeArray<uint> minMaxSnapshotTick;
#endif
#pragma warning disable 649
[NativeSetThreadIndex]
public int ThreadIndex;
#pragma warning restore 649
[NativeDisableParallelForRestriction] public NativeArray<uint> minPredictedTick;
[ReadOnly] public ArchetypeChunkBufferType<GameModeSnapshotData> ghostSnapshotDataType;
[ReadOnly] public ArchetypeChunkEntityType ghostEntityType;
public ArchetypeChunkComponentType<PredictedGhostComponent> predictedGhostComponentType;
public ArchetypeChunkComponentType<GameModeData> ghostGameModeDataType;
public uint targetTick;
public uint lastPredictedTick;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
{
var deserializerState = new GhostDeserializerState
{
GhostMap = GhostMap
};
var ghostEntityArray = chunk.GetNativeArray(ghostEntityType);
var ghostSnapshotDataArray = chunk.GetBufferAccessor(ghostSnapshotDataType);
var predictedGhostComponentArray = chunk.GetNativeArray(predictedGhostComponentType);
var ghostGameModeDataArray = chunk.GetNativeArray(ghostGameModeDataType);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
var minMaxOffset = ThreadIndex * (JobsUtility.CacheLineSize/4);
#endif
for (int entityIndex = 0; entityIndex < ghostEntityArray.Length; ++entityIndex)
{
var snapshot = ghostSnapshotDataArray[entityIndex];
#if UNITY_EDITOR || DEVELOPMENT_BUILD
var latestTick = snapshot.GetLatestTick();
if (latestTick != 0)
{
if (minMaxSnapshotTick[minMaxOffset] == 0 || SequenceHelpers.IsNewer(minMaxSnapshotTick[minMaxOffset], latestTick))
minMaxSnapshotTick[minMaxOffset] = latestTick;
if (minMaxSnapshotTick[minMaxOffset + 1] == 0 || SequenceHelpers.IsNewer(latestTick, minMaxSnapshotTick[minMaxOffset + 1]))
minMaxSnapshotTick[minMaxOffset + 1] = latestTick;
}
#endif
GameModeSnapshotData snapshotData;
snapshot.GetDataAtTick(targetTick, out snapshotData);
var predictedData = predictedGhostComponentArray[entityIndex];
var lastPredictedTickInst = lastPredictedTick;
if (lastPredictedTickInst == 0 || predictedData.AppliedTick != snapshotData.Tick)
lastPredictedTickInst = snapshotData.Tick;
else if (!SequenceHelpers.IsNewer(lastPredictedTickInst, snapshotData.Tick))
lastPredictedTickInst = snapshotData.Tick;
if (minPredictedTick[ThreadIndex] == 0 || SequenceHelpers.IsNewer(minPredictedTick[ThreadIndex], lastPredictedTickInst))
minPredictedTick[ThreadIndex] = lastPredictedTickInst;
predictedGhostComponentArray[entityIndex] = new PredictedGhostComponent{AppliedTick = snapshotData.Tick, PredictionStartTick = lastPredictedTickInst};
if (lastPredictedTickInst != snapshotData.Tick)
continue;
var ghostGameModeData = ghostGameModeDataArray[entityIndex];
ghostGameModeData.gameTimerSeconds = snapshotData.GetGameModeDatagameTimerSeconds(deserializerState);
ghostGameModeData.gameTimerMessage = snapshotData.GetGameModeDatagameTimerMessage(deserializerState);
ghostGameModeData.teamName0 = snapshotData.GetGameModeDatateamName0(deserializerState);
ghostGameModeData.teamName1 = snapshotData.GetGameModeDatateamName1(deserializerState);
ghostGameModeData.teamScore0 = snapshotData.GetGameModeDatateamScore0(deserializerState);
ghostGameModeData.teamScore1 = snapshotData.GetGameModeDatateamScore1(deserializerState);
ghostGameModeDataArray[entityIndex] = ghostGameModeData;
}
}
}
private ClientSimulationSystemGroup m_ClientSimulationSystemGroup;
private GhostPredictionSystemGroup m_GhostPredictionSystemGroup;
private EntityQuery m_interpolatedQuery;
private EntityQuery m_predictedQuery;
private NativeHashMap<int, GhostEntity> m_ghostEntityMap;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
private NativeArray<uint> m_ghostMinMaxSnapshotTick;
#endif
private GhostUpdateSystemGroup m_GhostUpdateSystemGroup;
private uint m_LastPredictedTick;
protected override void OnCreate()
{
m_GhostUpdateSystemGroup = World.GetOrCreateSystem<GhostUpdateSystemGroup>();
m_ghostEntityMap = m_GhostUpdateSystemGroup.GhostEntityMap;
#if UNITY_EDITOR || DEVELOPMENT_BUILD
m_ghostMinMaxSnapshotTick = m_GhostUpdateSystemGroup.GhostSnapshotTickMinMax;
#endif
m_ClientSimulationSystemGroup = World.GetOrCreateSystem<ClientSimulationSystemGroup>();
m_GhostPredictionSystemGroup = World.GetOrCreateSystem<GhostPredictionSystemGroup>();
m_interpolatedQuery = GetEntityQuery(new EntityQueryDesc
{
All = new []{
ComponentType.ReadWrite<GameModeSnapshotData>(),
ComponentType.ReadOnly<GhostComponent>(),
ComponentType.ReadWrite<GameModeData>(),
},
None = new []{ComponentType.ReadWrite<PredictedGhostComponent>()}
});
m_predictedQuery = GetEntityQuery(new EntityQueryDesc
{
All = new []{
ComponentType.ReadOnly<GameModeSnapshotData>(),
ComponentType.ReadOnly<GhostComponent>(),
ComponentType.ReadOnly<PredictedGhostComponent>(),
ComponentType.ReadWrite<GameModeData>(),
}
});
RequireForUpdate(GetEntityQuery(ComponentType.ReadWrite<GameModeSnapshotData>(),
ComponentType.ReadOnly<GhostComponent>()));
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
if (!m_predictedQuery.IsEmptyIgnoreFilter)
{
var updatePredictedJob = new UpdatePredictedJob
{
GhostMap = m_ghostEntityMap,
#if UNITY_EDITOR || DEVELOPMENT_BUILD
minMaxSnapshotTick = m_ghostMinMaxSnapshotTick,
#endif
minPredictedTick = m_GhostPredictionSystemGroup.OldestPredictedTick,
ghostSnapshotDataType = GetArchetypeChunkBufferType<GameModeSnapshotData>(true),
ghostEntityType = GetArchetypeChunkEntityType(),
predictedGhostComponentType = GetArchetypeChunkComponentType<PredictedGhostComponent>(),
ghostGameModeDataType = GetArchetypeChunkComponentType<GameModeData>(),
targetTick = m_ClientSimulationSystemGroup.ServerTick,
lastPredictedTick = m_LastPredictedTick
};
m_LastPredictedTick = m_ClientSimulationSystemGroup.ServerTick;
if (m_ClientSimulationSystemGroup.ServerTickFraction < 1)
m_LastPredictedTick = 0;
inputDeps = updatePredictedJob.Schedule(m_predictedQuery, JobHandle.CombineDependencies(inputDeps, m_GhostUpdateSystemGroup.LastGhostMapWriter));
m_GhostPredictionSystemGroup.AddPredictedTickWriter(inputDeps);
}
if (!m_interpolatedQuery.IsEmptyIgnoreFilter)
{
var updateInterpolatedJob = new UpdateInterpolatedJob
{
GhostMap = m_ghostEntityMap,
#if UNITY_EDITOR || DEVELOPMENT_BUILD
minMaxSnapshotTick = m_ghostMinMaxSnapshotTick,
#endif
ghostSnapshotDataType = GetArchetypeChunkBufferType<GameModeSnapshotData>(true),
ghostEntityType = GetArchetypeChunkEntityType(),
ghostGameModeDataType = GetArchetypeChunkComponentType<GameModeData>(),
targetTick = m_ClientSimulationSystemGroup.InterpolationTick,
targetTickFraction = m_ClientSimulationSystemGroup.InterpolationTickFraction
};
inputDeps = updateInterpolatedJob.Schedule(m_interpolatedQuery, JobHandle.CombineDependencies(inputDeps, m_GhostUpdateSystemGroup.LastGhostMapWriter));
}
return inputDeps;
}
}
public partial class GameModeGhostSpawnSystem : DefaultGhostSpawnSystem<GameModeSnapshotData>
{
struct SetPredictedDefault : IJobParallelFor
{
public NativeArray<int> predictionMask;
public void Execute(int index)
{
predictionMask[index] = 1;
}
}
protected override JobHandle SetPredictedGhostDefaults(NativeArray<GameModeSnapshotData> snapshots, NativeArray<int> predictionMask, JobHandle inputDeps)
{
var job = new SetPredictedDefault
{
predictionMask = predictionMask,
};
return job.Schedule(predictionMask.Length, 8, inputDeps);
}
}