您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
341 行
12 KiB
341 行
12 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using CollisionLib;
|
|
using Primitives;
|
|
using static Primitives.primlib;
|
|
using Unity.Entities;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
using UnityEngine.Jobs;
|
|
using UnityEngine.Profiling;
|
|
|
|
[DisallowMultipleComponent]
|
|
[RequireComponent(typeof(HitCollisionOwner))]
|
|
public class HitCollisionHistory : MonoBehaviour
|
|
{
|
|
// Collider data
|
|
public struct ColliderData
|
|
{
|
|
public Vector3 localPosition;
|
|
public Quaternion localRotation;
|
|
public Collider collider;
|
|
}
|
|
|
|
// State data
|
|
public struct State
|
|
{
|
|
public int tick;
|
|
public Vector3[] bonePositions;
|
|
public Quaternion[] boneRotations;
|
|
}
|
|
|
|
[Serializable]
|
|
public class Settings
|
|
{
|
|
public GameObject collisionSetup;
|
|
public float boundsRadius = 2.0f;
|
|
public float boundsHeight = 1.0f;
|
|
}
|
|
|
|
[NonSerialized] public Settings settings;
|
|
[NonSerialized] public GameObject collidersRoot;
|
|
[NonSerialized] public HitCollisionOwner hitCollisionOwner;
|
|
[NonSerialized] public Entity entity;
|
|
[NonSerialized] public ColliderData[] colliders;
|
|
[NonSerialized] public TransformAccessArray colliderParents;
|
|
|
|
[NonSerialized] public bool collidersEnabled;
|
|
|
|
[NonSerialized] public int lastTick = -1;
|
|
[NonSerialized] public int lastIndex = -1;
|
|
[NonSerialized] public int bufferSize;
|
|
|
|
[NonSerialized] public int lastRollbackTick;
|
|
|
|
[NonSerialized] public State[] buffer;
|
|
[NonSerialized] public float3[] boundsCenterBuffer;
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnDisable()
|
|
{
|
|
Shutdown();
|
|
}
|
|
#endif
|
|
|
|
public void Shutdown()
|
|
{
|
|
if(colliderParents.isCreated)
|
|
colliderParents.Dispose();
|
|
|
|
if (colliders == null)
|
|
return;
|
|
|
|
for (var i = 0; i < colliders.Length; i++)
|
|
{
|
|
var go = colliders[i].collider.gameObject;
|
|
DestroyImmediate(go);
|
|
}
|
|
DestroyImmediate(collidersRoot);
|
|
colliders = null;
|
|
}
|
|
|
|
|
|
public int GetStateIndex(int tick)
|
|
{
|
|
// If we exceed buffersize we should always use last value (if player latency to high no rollback is performed)
|
|
var roolbackTicks = lastTick - tick;
|
|
if (roolbackTicks >= bufferSize || tick > lastTick)
|
|
roolbackTicks = 0;
|
|
|
|
var index = lastIndex - roolbackTicks;
|
|
while (index < 0)
|
|
index += buffer.Length;
|
|
return index;
|
|
}
|
|
|
|
public void EnableCollisionForIndex(int index)
|
|
{
|
|
GameDebug.Assert(colliders != null, "No collider hitcollisioncollection:{0}",gameObject.name);
|
|
|
|
collidersEnabled = true;
|
|
|
|
if (buffer[index].tick == lastRollbackTick)
|
|
{
|
|
//GameDebug.Log("skipping rollback");
|
|
return;
|
|
}
|
|
|
|
Profiler.BeginSample("EnableCollisionForIndex");
|
|
|
|
for (var i = 0; i < colliders.Length; i++)
|
|
{
|
|
if(colliders[i].collider.gameObject.layer != HitCollisionModule.HitCollisionLayer)
|
|
colliders[i].collider.gameObject.layer = HitCollisionModule.HitCollisionLayer;
|
|
}
|
|
|
|
lastRollbackTick = buffer[index].tick;
|
|
|
|
GameDebug.Assert(index >= 0 && index < bufferSize, "Rollback index out of bounds");
|
|
|
|
for (var i = 0; i < colliders.Length; i++)
|
|
{
|
|
var bonePosition = buffer[index].bonePositions[i];
|
|
var boneRotation = buffer[index].boneRotations[i];
|
|
|
|
var worldPos = boneRotation * colliders[i].localPosition + bonePosition;
|
|
var worldRot = boneRotation * colliders[i].localRotation;
|
|
|
|
colliders[i].collider.transform.position = worldPos;
|
|
colliders[i].collider.transform.rotation = worldRot;
|
|
|
|
|
|
if (HitCollisionModule.ShowDebug.IntValue > 0)
|
|
{
|
|
DebugPrimitiveModule.ClearChannel(HitCollisionModule.PrimDebugChannel);
|
|
|
|
CapsuleCollider capsuleCollider = colliders[i].collider as CapsuleCollider;
|
|
if (capsuleCollider != null)
|
|
{
|
|
var center = capsuleCollider.transform.TransformPoint(capsuleCollider.center);
|
|
var v = capsuleCollider.transform.rotation*Vector3.up;
|
|
var L = capsuleCollider.height - capsuleCollider.radius*2;
|
|
var pA = center - v*L*0.5f;
|
|
var pB = center + v*L*0.5f;
|
|
DebugPrimitiveModule.CreateCapsulePrimitive(HitCollisionModule.PrimDebugChannel, pA, pB, capsuleCollider.radius, Color.green, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
Profiler.EndSample();
|
|
}
|
|
|
|
public void DisableHitCollision()
|
|
{
|
|
if (colliders == null)
|
|
return;
|
|
|
|
Profiler.BeginSample("DisableHitCollision");
|
|
|
|
collidersEnabled = false;
|
|
|
|
for (var i = 0; i < colliders.Length; i++)
|
|
{
|
|
if(colliders[i].collider.gameObject.layer != HitCollisionModule.DisabledHitCollisionLayer)
|
|
colliders[i].collider.gameObject.layer = HitCollisionModule.DisabledHitCollisionLayer;
|
|
}
|
|
|
|
Profiler.EndSample();
|
|
}
|
|
|
|
public void DrawStates(HitCollisionHistory colliderCollection, Color color, float duration)
|
|
{
|
|
for (int i = 0; i < bufferSize; i++)
|
|
DrawStateAtIndex(i, colliderCollection, color, duration);
|
|
}
|
|
|
|
public void DrawStateAtIndex(int stateIndex, HitCollisionHistory colliderCollection, Color color, float duration)
|
|
{
|
|
for (var i = 0; i < colliderCollection.colliders.Length; i++)
|
|
{
|
|
var bonePosition = buffer[stateIndex].bonePositions[i];
|
|
var boneRotation = buffer[stateIndex].boneRotations[i];
|
|
|
|
var worldPos = boneRotation * colliderCollection.colliders[i].localPosition + bonePosition;
|
|
var worldRot = boneRotation * colliderCollection.colliders[i].localRotation;
|
|
|
|
|
|
var capsuleCollider = colliderCollection.colliders[i].collider as CapsuleCollider;
|
|
if (capsuleCollider != null)
|
|
DebugDraw.Capsule(worldPos, worldRot * Vector3.up, capsuleCollider.radius, capsuleCollider.height, color, duration);
|
|
}
|
|
}
|
|
|
|
static bool IsRelevant(HitCollisionHistory hitCollHistory, int flagMask, Entity forceExcluded, Entity forceIncluded)
|
|
{
|
|
|
|
if (hitCollHistory.hitCollisionOwner == null)
|
|
{
|
|
GameDebug.LogError("HitCollisionHistory:" + hitCollHistory + " has a null hitCollisionOwner");
|
|
return false;
|
|
}
|
|
|
|
if (hitCollHistory.colliders == null)
|
|
return false;
|
|
|
|
Profiler.BeginSample("IsRelevant");
|
|
|
|
var valid = (forceIncluded != Entity.Null && forceIncluded == hitCollHistory.entity) ||
|
|
(hitCollHistory.hitCollisionOwner.collisionEnabled &&
|
|
(hitCollHistory.hitCollisionOwner.colliderFlags & flagMask) != 0 &&
|
|
!(forceExcluded != Entity.Null && forceExcluded == hitCollHistory.entity));
|
|
|
|
Profiler.EndSample();
|
|
|
|
return valid;
|
|
}
|
|
|
|
public static void PrepareColliders(ref ComponentArray<HitCollisionHistory> collections, int tick, int mask, Entity forceExcluded, Entity forceIncluded, ray ray, float rayDist)
|
|
{
|
|
Profiler.BeginSample("HitCollisionHistory.PrepareColliders [Ray]");
|
|
|
|
// Rollback
|
|
for (var i = 0; i < collections.Length; i++)
|
|
{
|
|
var collection = collections[i];
|
|
if(!IsRelevant(collection, mask, forceExcluded, forceIncluded))
|
|
{
|
|
collection.DisableHitCollision();
|
|
continue;
|
|
}
|
|
|
|
var stateIndex = collection.GetStateIndex(tick);
|
|
|
|
Profiler.BeginSample("-raycast");
|
|
|
|
var sphere = primlib.sphere(collection.boundsCenterBuffer[stateIndex], collection.settings.boundsRadius);
|
|
var boundsHit = coll.RayCast(sphere, ray, rayDist);
|
|
|
|
Profiler.EndSample();
|
|
|
|
if (boundsHit)
|
|
collection.EnableCollisionForIndex(stateIndex);
|
|
else
|
|
collection.DisableHitCollision();
|
|
|
|
if (HitCollisionModule.ShowDebug.IntValue > 0)
|
|
{
|
|
DebugPrimitiveModule.ClearChannel(HitCollisionModule.PrimDebugChannel);
|
|
DebugPrimitiveModule.CreateLinePrimitive(HitCollisionModule.PrimDebugChannel, ray.origin, ray.origin + ray.direction*rayDist, Color.yellow, 5);
|
|
DebugPrimitiveModule.CreateSpherePrimitive(HitCollisionModule.PrimDebugChannel, sphere.center, sphere.radius,
|
|
boundsHit ? Color.yellow : Color.gray, 5);
|
|
}
|
|
}
|
|
|
|
Profiler.EndSample();
|
|
}
|
|
|
|
public static void PrepareColliders(ref ComponentArray<HitCollisionHistory> collections, int tick, int mask, Entity forceExcluded, Entity forceIncluded, ray ray, float rayDist, float radius)
|
|
{
|
|
Profiler.BeginSample("HitCollisionHistory.PrepareColliders [SphereCast]");
|
|
|
|
for (var i = 0; i < collections.Length; i++)
|
|
{
|
|
var collection = collections[i];
|
|
if(!IsRelevant(collection, mask, forceExcluded, forceIncluded))
|
|
{
|
|
collection.DisableHitCollision();
|
|
continue;
|
|
}
|
|
|
|
var stateIndex = collection.GetStateIndex(tick);
|
|
|
|
Profiler.BeginSample("-capsule test");
|
|
var boundCenter = collection.boundsCenterBuffer[stateIndex];
|
|
|
|
var rayEnd = ray.origin + ray.direction * rayDist;
|
|
var closestPointOnRay = coll.ClosestPointOnLineSegment(ray.origin, rayEnd, boundCenter);
|
|
var dist = math.distance(closestPointOnRay, boundCenter);
|
|
var boundsHit = dist < collection.settings.boundsRadius + radius;
|
|
|
|
Profiler.EndSample();
|
|
|
|
if (boundsHit)
|
|
collection.EnableCollisionForIndex(stateIndex);
|
|
else
|
|
collection.DisableHitCollision();
|
|
|
|
if (HitCollisionModule.ShowDebug.IntValue > 0)
|
|
{
|
|
DebugPrimitiveModule.ClearChannel(HitCollisionModule.PrimDebugChannel);
|
|
DebugPrimitiveModule.CreateCapsulePrimitive(HitCollisionModule.PrimDebugChannel,
|
|
ray.origin + ray.direction * radius, ray.origin + ray.direction * (rayDist - radius), radius, Color.yellow, 5);
|
|
DebugPrimitiveModule.CreateSpherePrimitive(HitCollisionModule.PrimDebugChannel, boundCenter,
|
|
collection.settings.boundsRadius,
|
|
boundsHit ? Color.yellow : Color.gray, 5);
|
|
}
|
|
}
|
|
|
|
Profiler.EndSample();
|
|
}
|
|
|
|
public static void PrepareColliders(ref ComponentArray<HitCollisionHistory> collections, int tick, int mask, Entity forceExcluded, Entity forceIncluded, sphere sphere)
|
|
{
|
|
Profiler.BeginSample("HitCollisionHistory.PrepareColliders [Sphere]");
|
|
|
|
for (var i = 0; i < collections.Length; i++)
|
|
{
|
|
var collection = collections[i];
|
|
if(!IsRelevant(collection, mask, forceExcluded, forceIncluded))
|
|
{
|
|
collection.DisableHitCollision();
|
|
continue;
|
|
}
|
|
|
|
var stateIndex = collection.GetStateIndex(tick);
|
|
|
|
var boundsCenter = collection.boundsCenterBuffer[stateIndex];
|
|
var boundsRadius = collection.settings.boundsRadius;
|
|
var dist = math.distance(sphere.center, boundsCenter);
|
|
|
|
var boundsHit = dist < sphere.radius + boundsRadius;
|
|
|
|
if (boundsHit)
|
|
collection.EnableCollisionForIndex(stateIndex);
|
|
else
|
|
collection.DisableHitCollision();
|
|
|
|
if (HitCollisionModule.ShowDebug.IntValue > 0)
|
|
{
|
|
DebugPrimitiveModule.ClearChannel(HitCollisionModule.PrimDebugChannel);
|
|
DebugPrimitiveModule.CreateSpherePrimitive(HitCollisionModule.PrimDebugChannel, sphere.center, sphere.radius,
|
|
Color.yellow, 5);
|
|
DebugPrimitiveModule.CreateSpherePrimitive(HitCollisionModule.PrimDebugChannel, boundsCenter,
|
|
boundsRadius,
|
|
boundsHit ? Color.yellow : Color.gray, 5);
|
|
}
|
|
}
|
|
|
|
Profiler.EndSample();
|
|
}
|
|
}
|
|
|