您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

546 行
22 KiB

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using Unity.Collections;
using UnityEngine;
using Unity.Entities;
using Unity.Mathematics;
using UnityEditor.Experimental.Rendering;
using UnityEngine.Profiling;
// Added to projectiles that are created locally. System attempts to find a mathcing projectile comming from server.
// If no match is found when server version should have been found the predicted projectile is deleted.
public struct PredictedProjectile : IComponentData
{
public PredictedProjectile(int startTick)
{
this.startTick = startTick;
}
public int startTick;
}
// Added to projectiles that has a clientprojectile
public struct ClientProjectileOwner : IComponentData
{
public Entity clientProjectile;
}
[DisableAutoCreation]
public class HandleClientProjectileRequests : BaseComponentSystem
{
ComponentGroup RequestGroup;
readonly GameObject m_SystemRoot;
readonly BundledResourceManager m_resourceSystem;
readonly ProjectileModuleSettings m_settings;
ClientProjectileFactory m_clientProjectileFactory;
List<ProjectileRequest> requestBuffer = new List<ProjectileRequest>(16);
public HandleClientProjectileRequests(GameWorld world, BundledResourceManager resourceSystem, GameObject systemRoot,
ClientProjectileFactory clientProjectileFactory) : base(world)
{
m_resourceSystem = resourceSystem;
m_SystemRoot = systemRoot;
m_settings = Resources.Load<ProjectileModuleSettings>("ProjectileModuleSettings");
m_clientProjectileFactory = clientProjectileFactory;
}
protected override void OnCreateManager(int capacity)
{
base.OnCreateManager(capacity);
RequestGroup = GetComponentGroup(typeof(ProjectileRequest));
}
protected override void OnDestroyManager()
{
base.OnDestroyManager();
Resources.UnloadAsset(m_settings);
}
protected override void OnUpdate()
{
if (RequestGroup.CalculateLength() == 0)
return;
// Copy requests as spawning will invalidate Group
requestBuffer.Clear();
var requestArray = RequestGroup.GetComponentDataArray<ProjectileRequest>();
var requestEntityArray = RequestGroup.GetEntityArray();
for (var i = 0; i < requestArray.Length; i++)
{
requestBuffer.Add(requestArray[i]);
PostUpdateCommands.DestroyEntity(requestEntityArray[i]);
}
// Handle requests
foreach (var request in requestBuffer)
{
// Create projectile and initialize
var projectileEntity = m_settings.projectileFactory.Create(EntityManager);
var projectileData = EntityManager.GetComponentData<ProjectileData>(projectileEntity);
projectileData.Initialize(request);
projectileData.LoadSettings( m_resourceSystem);
EntityManager.SetComponentData(projectileEntity, projectileData);
if (ProjectileModuleClient.logInfo.IntValue > 0)
GameDebug.Log("New predicted projectile created: " + projectileEntity);
// Add components so projectile is picked by prediction code
EntityManager.AddComponentData(projectileEntity, new ServerEntity());
EntityManager.AddComponentData(projectileEntity, new PredictedProjectile(request.startTick));
// Create client projectile and add ServerEntity so it will use predicted time
var clientProjectileEntity = m_clientProjectileFactory.CreateClientProjectile(projectileEntity);
EntityManager.AddComponentData(clientProjectileEntity, new ServerEntity());
}
}
}
class ProjectilesSystemsClient
{
public static void Update(GameWorld world, EntityCommandBuffer commandBuffer, ClientProjectile clientProjectile)
{
var deltaTime = world.frameDuration;
if (clientProjectile.impacted)
return;
// When projectile disappears we hide clientprojectile
if (clientProjectile.projectile == Entity.Null)
{
clientProjectile.SetVisible(false);
return;
}
var projectileData = world.GetEntityManager().GetComponentData<ProjectileData>(clientProjectile.projectile);
var aliveDuration = world.worldTime.DurationSinceTick(projectileData.startTick);
// Interpolation delay can cause projectiles to be spawned before they should be shown.
if (aliveDuration < 0)
{
return;
}
if (!clientProjectile.IsVisible)
{
clientProjectile.SetVisible(true);
}
var dir = Vector3.Normalize(projectileData.endPos - projectileData.startPos);
var moveDist = aliveDuration * projectileData.settings.velocity;
var pos = (Vector3)projectileData.startPos + dir * moveDist;
var rot = Quaternion.LookRotation(dir);
var worldOffset = Vector3.zero;
if (clientProjectile.offsetScale > 0.0f)
{
clientProjectile.offsetScale -= deltaTime / clientProjectile.offsetScaleDuration;
worldOffset = rot * clientProjectile.startOffset * clientProjectile.offsetScale;
}
if (projectileData.impacted == 1 && !clientProjectile.impacted)
{
clientProjectile.impacted = true;
if (clientProjectile.impactEffect != null)
{
SpatialEffectRequest.Create(commandBuffer, clientProjectile.impactEffect,
projectileData.impactPos, Quaternion.LookRotation(projectileData.impactNormal));
}
clientProjectile.SetVisible(false);
}
clientProjectile.transform.position = pos + worldOffset;
// Debug.DrawLine(projectileData.startPos, pos, Color.red);
// DebugDraw.Sphere(clientProjectile.transform.position, 0.2f, Color.red);
clientProjectile.roll += deltaTime * clientProjectile.rotationSpeed;
var roll = Quaternion.Euler(0, 0, clientProjectile.roll);
clientProjectile.transform.rotation = rot * roll;
if (ProjectileModuleClient.drawDebug.IntValue == 1)
{
DebugDraw.Sphere(clientProjectile.transform.position, 0.1f, Color.cyan, 1.0f);
}
}
}
[DisableAutoCreation]
public class UpdateClientProjectilesPredicted : BaseComponentSystem<ClientProjectile>
{
public UpdateClientProjectilesPredicted(GameWorld world) : base(world)
{
ExtraComponentRequirements = new [] { ComponentType.Create<ServerEntity>() };
}
protected override void Update(Entity entity, ClientProjectile clientProjectile)
{
ProjectilesSystemsClient.Update(m_world, PostUpdateCommands, clientProjectile);
}
}
[DisableAutoCreation]
public class UpdateClientProjectilesNonPredicted : BaseComponentSystem<ClientProjectile>
{
public UpdateClientProjectilesNonPredicted(GameWorld world) : base(world)
{
ExtraComponentRequirements = new [] { ComponentType.Subtractive<ServerEntity>() };
}
protected override void Update(Entity entity, ClientProjectile clientProjectile)
{
ProjectilesSystemsClient.Update(m_world, PostUpdateCommands, clientProjectile);
}
}
[DisableAutoCreation]
[AlwaysUpdateSystemAttribute]
public class HandleProjectileSpawn : BaseComponentSystem
{
readonly GameObject m_SystemRoot;
readonly BundledResourceManager m_resourceSystem;
ComponentGroup PredictedProjectileGroup;
ComponentGroup IncommingProjectileGroup;
private ClientProjectileFactory m_clientProjectileFactory;
private List<Entity> addClientProjArray = new List<Entity>(32);
public HandleProjectileSpawn(GameWorld world, GameObject systemRoot, BundledResourceManager resourceSystem, ClientProjectileFactory projectileFactory) : base(world)
{
m_SystemRoot = systemRoot;
m_resourceSystem = resourceSystem;
m_clientProjectileFactory = projectileFactory;
}
protected override void OnCreateManager(int capacity)
{
base.OnCreateManager(capacity);
PredictedProjectileGroup = GetComponentGroup(typeof(ProjectileData), typeof(PredictedProjectile), ComponentType.Subtractive<DespawningEntity>());
IncommingProjectileGroup = GetComponentGroup(typeof(ProjectileData), ComponentType.Subtractive<ClientProjectileOwner>());
}
protected override void OnUpdate()
{
if (IncommingProjectileGroup.CalculateLength() > 0)
HandleIncommingProjectiles();
}
void HandleIncommingProjectiles()
{
// Run through all incomming projectiles. Attempt to match with predicted projectiles.
// If none is found add to addClientProjArray so a client projectile will be created for projectile
addClientProjArray.Clear();
var inEntityArray = IncommingProjectileGroup.GetEntityArray();
var inProjectileDataArray = IncommingProjectileGroup.GetComponentDataArray<ProjectileData>();
var predictedProjectileArray = PredictedProjectileGroup.GetComponentDataArray<ProjectileData>();
var predictedProjectileEntities = PredictedProjectileGroup.GetEntityArray();
for(var j=0;j<inProjectileDataArray.Length;j++)
{
var inProjectileData = inProjectileDataArray[j];
var inProjectileEntity = inEntityArray[j];
if (ProjectileModuleClient.logInfo.IntValue > 0)
GameDebug.Log(string.Format("Projectile spawn:" + inProjectileEntity));
// var inProjectileEntity = inProjectile.GetComponent<GameObjectEntity>().Entity;
// Initialze new projectile with correct settings
inProjectileData.LoadSettings( m_resourceSystem);
m_world.GetEntityManager().SetComponentData(inProjectileEntity, inProjectileData);
// I new projectile in not predicted, attempt to find a predicted that that should link to it
var matchFound = false;
if (!m_world.GetEntityManager().HasComponent<PredictedProjectile>(inProjectileEntity))
{
for (var i = 0; i < predictedProjectileEntities.Length; i++)
{
var predictedProjetile = predictedProjectileArray[i];
// Attempt to find matching
if (predictedProjetile.projectileTypeRegistryId !=
inProjectileData.projectileTypeRegistryId)
continue;
if (predictedProjetile.projectileOwner != inProjectileData.projectileOwner)
continue;
if (predictedProjetile.startTick != inProjectileData.startTick)
continue;
if (math.distance(predictedProjetile.startPos, inProjectileData.startPos) > 0.1f)
continue;
// Match found
matchFound = true;
var predictedProjectileEntity = predictedProjectileEntities[i];
if (ProjectileModuleClient.logInfo.IntValue > 0)
GameDebug.Log("ProjectileSystemClient. Predicted projectile" + predictedProjectileEntity + " matched with " + inProjectileEntity + " from server. startTick:" +
inProjectileData.startTick);
// Reassign clientprojectile to use new projectile
var clientProjectileOwner =
EntityManager.GetComponentData<ClientProjectileOwner>(predictedProjectileEntity);
var clientProjectile =
EntityManager.GetComponentObject<ClientProjectile>(clientProjectileOwner.clientProjectile);
clientProjectile.projectile = inProjectileEntity;
PostUpdateCommands.AddComponent(inProjectileEntity,clientProjectileOwner);
PostUpdateCommands.RemoveComponent(predictedProjectileEntity,typeof(ClientProjectileOwner));
// Destroy predicted
if (ProjectileModuleClient.logInfo.IntValue > 0)
GameDebug.Log("ProjectileSystemClient. Destroying predicted:" + predictedProjectileEntity);
PostUpdateCommands.AddComponent(predictedProjectileEntity, new DespawningEntity());
break;
}
}
if (ProjectileModuleClient.drawDebug.IntValue == 1)
{
var color = matchFound ? Color.green : Color.yellow;
DebugDraw.Sphere(inProjectileData.startPos, 0.12f, color, 1.0f);
Debug.DrawLine(inProjectileData.startPos, inProjectileData.startPos + inProjectileData.endPos, color, 1.0f);
}
// If match was found the new projectile has already been assigned an existing clientprojectile
if (!matchFound)
addClientProjArray.Add(inProjectileEntity);
}
// Create client projectiles. This is deferred as we cant create
// clientprojectiles while iterating over componentarray
foreach (var projectileEntity in addClientProjArray)
{
if (ProjectileModuleClient.logInfo.IntValue > 0)
{
var projectileData = EntityManager.GetComponentData<ProjectileData>(projectileEntity);
GameDebug.Log("Creating clientprojectile for projectile:" + projectileData);
}
m_clientProjectileFactory.CreateClientProjectile(projectileEntity);
}
}
}
[DisableAutoCreation]
[AlwaysUpdateSystemAttribute]
public class RemoveMispredictedProjectiles : BaseComponentSystem
{
ComponentGroup PredictedProjectileGroup;
public RemoveMispredictedProjectiles(GameWorld world) :
base(world)
{}
protected override void OnCreateManager(int capacity)
{
base.OnCreateManager(capacity);
PredictedProjectileGroup = GetComponentGroup(typeof(PredictedProjectile), ComponentType.Subtractive<DespawningEntity>());
}
protected override void OnUpdate()
{
// Remove all predicted projectiles that should have been acknowledged by now
var predictedProjectileArray = PredictedProjectileGroup.GetComponentDataArray<PredictedProjectile>();
var predictedProjectileEntityArray = PredictedProjectileGroup.GetEntityArray();
for (var i=0;i<predictedProjectileArray.Length;i++)
{
var predictedEntity = predictedProjectileArray[i];
if (predictedEntity.startTick >= m_world.lastServerTick)
continue;
var entity = predictedProjectileEntityArray[i];
PostUpdateCommands.AddComponent(entity, new DespawningEntity());
// var gameObject = EntityManager.GetComponentObject<Transform>(predictedProjectileEntityArray[i]).gameObject;
// m_world.RequestDespawn(gameObject, PostUpdateCommands);
if (ProjectileModuleClient.logInfo.IntValue > 0)
GameDebug.Log(string.Format("<color=red>Predicted projectile {0} destroyed as it was not verified. startTick:{1}]</color>", entity, predictedEntity.startTick));
}
}
}
[DisableAutoCreation]
[AlwaysUpdateSystemAttribute]
public class DespawnClientProjectiles : BaseComponentSystem
{
ComponentGroup DespawningClientProjectileOwnerGroup;
ClientProjectileFactory m_clientProjectileFactory;
public DespawnClientProjectiles(GameWorld world, ClientProjectileFactory clientProjectileFactory) :
base(world)
{
m_clientProjectileFactory = clientProjectileFactory;
}
protected override void OnCreateManager(int capacity)
{
base.OnCreateManager(capacity);
DespawningClientProjectileOwnerGroup = GetComponentGroup(typeof(ClientProjectileOwner), typeof(DespawningEntity));
}
List<Entity> clientProjectiles = new List<Entity>(32);
protected override void OnUpdate()
{
// Remove all client projectiles that are has despawning projectile
var clientProjectileOwnerArray = DespawningClientProjectileOwnerGroup.GetComponentDataArray<ClientProjectileOwner>();
if (clientProjectileOwnerArray.Length > 0)
{
clientProjectiles.Clear();
for(var i=0;i<clientProjectileOwnerArray.Length;i++)
{
var clientProjectileOwner = clientProjectileOwnerArray[i];
clientProjectiles.Add(clientProjectileOwner.clientProjectile);
}
for (var i = 0; i < clientProjectiles.Count; i++)
{
m_clientProjectileFactory.DestroyClientProjectile(clientProjectiles[i], PostUpdateCommands);
if (ProjectileModuleClient.logInfo.IntValue > 0)
GameDebug.Log(string.Format("Projectile despawned so despawn of clientprojectile requested"));
}
}
}
}
public class ClientProjectileFactory
{
class Pool
{
public int poolIndex;
public GameObject prefab;
public List<GameObjectEntity> instances = new List<GameObjectEntity>();
public Queue<int> freeList = new Queue<int>();
}
private Pool[] pools;
public ClientProjectileFactory(GameWorld world, EntityManager entityManager, GameObject systemRoot, BundledResourceManager resourceSystem)
{
m_world = world;
m_entityManager = entityManager;
m_resourceSystem = resourceSystem;
m_systemRoot = systemRoot;
var projectileRegistry = m_resourceSystem.GetResourceRegistry<ProjectileRegistry>();
var typeCount = projectileRegistry.entries.Length;
pools = new Pool[typeCount];
for (var i = 0; i < projectileRegistry.entries.Length; i++)
{
var pool = new Pool();
var entry = projectileRegistry.entries[i];
pool.prefab = (GameObject)m_resourceSystem.LoadSingleAssetResource(entry.definition.clientProjectilePrefab.guid);
pool.poolIndex = i;
Allocate(pool, entry.definition.clientProjectileBufferSize);
pools[i] = pool;
}
}
GameWorld m_world;
EntityManager m_entityManager;
GameObject m_systemRoot;
BundledResourceManager m_resourceSystem;
int Reserve(Pool pool)
{
if (pool.freeList.Count == 0)
Allocate(pool, 5);
var index = pool.freeList.Dequeue();
// GameDebug.Log("Reserve pool:" + pool.poolIndex + " index:" + index + " count:" + pool.freeList.Count);
return index;
}
void Free(Pool pool, int index)
{
// GameDebug.Log("Free pool:" + pool.poolIndex + " index:" + index + " before count:" + pool.freeList.Count);
GameDebug.Assert(!pool.freeList.Contains(index));
pool.freeList.Enqueue(index);
}
void Allocate(Pool pool, int count)
{
var startIndex = pool.instances.Count;
for (var i = 0; i < count; i++)
{
var bufferIndex = startIndex + i;
var projectile = m_world.Spawn<GameObjectEntity>(pool.prefab);
pool.freeList.Enqueue(bufferIndex);
if(m_systemRoot != null)
projectile.transform.SetParent(m_systemRoot.transform);
var entity = projectile.Entity;
var clientProjectile = m_entityManager.GetComponentObject<ClientProjectile>(entity);
clientProjectile.poolIndex = pool.poolIndex;
clientProjectile.bufferIndex = bufferIndex;
clientProjectile.SetVisible(false);
clientProjectile.gameObject.SetActive(false);
pool.instances.Add(projectile);
}
}
public Entity CreateClientProjectile(Entity projectileEntity)
{
var projectileData = m_entityManager.GetComponentData<ProjectileData>(projectileEntity);
// Create client projectile
var projectileRegistry = m_resourceSystem.GetResourceRegistry<ProjectileRegistry>();
var index = projectileRegistry.GetIndexByRegistryId(projectileData.projectileTypeRegistryId);
GameDebug.Assert(index != -1,"Failed to create projectile data. Cant find projectileType:{0} in registry",projectileData.projectileTypeRegistryId);
var pool = pools[index];
var instanceIndex = Reserve(pool);
var gameObjectEntity = pool.instances[instanceIndex];
gameObjectEntity.gameObject.SetActive(true);
var clientProjectileEntity = gameObjectEntity.Entity;
var clientProjectile = m_entityManager.GetComponentObject<ClientProjectile>(clientProjectileEntity);
GameDebug.Assert(clientProjectile.projectile == Entity.Null,"Entity not null");
clientProjectile.projectile = projectileEntity;
clientProjectile.SetVisible(false);
if (ProjectileModuleClient.logInfo.IntValue > 0)
GameDebug.Log(string.Format("Creating clientprojectile {0} for projectile {1}]", clientProjectile, projectileEntity));
// Add clientProjectileOwner to projectile
var clientProjectileOwner = new ClientProjectileOwner
{
clientProjectile = clientProjectileEntity
};
m_entityManager.AddComponentData(projectileEntity,clientProjectileOwner);
return clientProjectileEntity;
}
public void DestroyClientProjectile(Entity clientProjectileEntity, EntityCommandBuffer commandBuffer)
{
var clientProjectile = m_entityManager.GetComponentObject<ClientProjectile>(clientProjectileEntity);
Free(pools[clientProjectile.poolIndex], clientProjectile.bufferIndex);
clientProjectile.gameObject.SetActive(false);
clientProjectile.Reset();
}
}