Skip to content

Commit

Permalink
More efficient projectile raycasting
Browse files Browse the repository at this point in the history
  • Loading branch information
ari-steas committed Mar 17, 2024
1 parent e233672 commit 9c202fb
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using Heart_Module.Data.Scripts.HeartModule.ExceptionHandler;
using ParallelTasks;
using Sandbox.Game.Entities;
using Sandbox.ModAPI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VRage.Game.Entity;
using VRage.ModAPI;

namespace Heart_Module.Data.Scripts.HeartModule.Projectiles
{
Expand All @@ -21,6 +24,7 @@ internal class ParallelProjectileThread
/// Thread safe buffer list for projectiles to close.
/// </summary>
List<Projectile> ProjectilesToClose = new List<Projectile>();
HashSet<IMyEntity> EntityBuffer = new HashSet<IMyEntity>();

public float DeltaTick = 0;

Expand All @@ -46,6 +50,9 @@ public void Update()
ActiveProjectiles = ProjectileManager.I.ActiveProjectiles.Values.ToArray();
ProjectileManager.I.QueuedCloseProjectiles.AddRange(ProjectilesToClose);
ProjectilesToClose.Clear();
EntityBuffer.Clear();
MyAPIGateway.Entities.GetEntities(EntityBuffer, (ent) => ent.Physics != null);

thisTask = MyAPIGateway.Parallel.StartBackground(DoWork);
}
}
Expand All @@ -68,7 +75,7 @@ void DoWork()

void UpdateSingleProjectile(Projectile projectile)
{
projectile.TickUpdate(DeltaTick);
projectile.TickUpdate(DeltaTick, EntityBuffer);

if (projectile.QueuedDispose)
ProjectilesToClose.Add(projectile);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using Heart_Module.Data.Scripts.HeartModule.ErrorHandler;
using Heart_Module.Data.Scripts.HeartModule.Projectiles.GuidanceHelpers;
using Heart_Module.Data.Scripts.HeartModule.Projectiles.StandardClasses;
using Sandbox.Game.Entities;
using Sandbox.ModAPI;
using System;
using System.Collections.Generic;
using System.Drawing;
using VRage.Game.Entity;
using VRage.Game.ModAPI;
using VRage.ModAPI;
using VRageMath;
using static Sandbox.Engine.Physics.MyPhysics.CollisionLayers;

Expand Down Expand Up @@ -146,7 +148,7 @@ public Projectile(int DefinitionId, Vector3D Position, Vector3D Direction, IMyCu
Definition.LiveMethods.OnSpawn?.Invoke(Id, (MyEntity)MyAPIGateway.Entities.GetEntityById(Firer));
}

public void TickUpdate(float delta)
public void TickUpdate(float delta, HashSet<IMyEntity> entities = null)
{
if ((Definition.PhysicalProjectile.MaxTrajectory != -1 && Definition.PhysicalProjectile.MaxTrajectory < DistanceTravelled) || (Definition.PhysicalProjectile.MaxLifetime != -1 && Definition.PhysicalProjectile.MaxLifetime < Age))
QueueDispose();
Expand All @@ -162,7 +164,7 @@ public void TickUpdate(float delta)
{
Guidance?.RunGuidance(delta);

CheckHits();
CheckHits(entities);

// Apply gravity as an acceleration
float gravityMultiplier = Definition.PhysicalProjectile.GravityInfluenceMultiplier;
Expand Down Expand Up @@ -210,7 +212,7 @@ public void TickUpdate(float delta)

if (RemainingImpacts > 0)
{
MaxBeamLength = CheckHits(); // Set visual beam length
CheckHits(entities); // Set visual beam length
if (MaxBeamLength == -1)
MaxBeamLength = Definition.PhysicalProjectile.MaxTrajectory;
}
Expand All @@ -222,14 +224,14 @@ public void TickUpdate(float delta)
UpdateAudio();
}

public float CheckHits()
public void CheckHits(HashSet<IMyEntity> entities)
{
if (NextMoveStep == Vector3D.Zero)
return -1;
return;

double len = IsHitscan ? Definition.PhysicalProjectile.MaxTrajectory : Vector3D.Distance(Position, NextMoveStep);
double dist = -1;

// Run projectile collision checks
if (MyAPIGateway.Session.IsServer && RemainingImpacts > 0 && Definition.Damage.DamageToProjectiles > 0)
{
List<Projectile> hittableProjectiles = new List<Projectile>();
Expand All @@ -251,12 +253,12 @@ public float CheckHits()
double? intersectDist = ray.Intersects(box);
if (intersectDist != null)
{
dist = intersectDist.Value;
MaxBeamLength = (float) intersectDist.Value;
projectile.Health -= Definition.Damage.DamageToProjectiles;

damageToProjectilesInAoE += Definition.Damage.DamageToProjectiles;

Vector3D hitPos = Position + Direction * dist;
Vector3D hitPos = Position + Direction * MaxBeamLength;

if (MyAPIGateway.Session.IsServer)
PlayImpactAudio(hitPos); // Audio is global
Expand All @@ -275,27 +277,44 @@ public float CheckHits()
projectile.Health -= damageToProjectilesInAoE;
}

dist = PerformRaycastRecursive(len);
// Check if a raycast is needed, and if it is, perform a physics cast.
RayD travelLine = new RayD(Position, Direction);
double checkDistSq = (NextMoveStep - Position).LengthSquared();

foreach (var entity in entities)
{
double? dist = entity.WorldVolume.Intersects(travelLine); // This seems to be the cheapest form of line checking
if (!dist.HasValue || dist * dist > checkDistSq)
continue;

PerformRaycastRecursive(len);
break;
}


if (RemainingImpacts <= 0)
QueueDispose();

return (float)dist;
}

private double PerformRaycastRecursive(double length)
private void PerformRaycastRecursive(double length)
{
MaxBeamLength = -1;

if (RemainingImpacts <= 0)
return -1;
return;

double dist = -1;
BoundingSphereD sphere = new BoundingSphereD(Position, NextMoveStep.Length());
List<MyEntity> entities = new List<MyEntity>();
MyGamePruningStructure.GetAllTopMostEntitiesInSphere(ref sphere, entities);
if (entities.Count == 0)
return;

MyAPIGateway.Physics.CastRayParallel(ref Position, ref NextMoveStep, NoVoxelCollisionLayer, (hitInfo) =>
{
if (RemainingImpacts <= 0 || hitInfo.HitEntity.EntityId == Firer)
return;

dist = hitInfo.Fraction * length;
MaxBeamLength = (float) (hitInfo.Fraction * length);

if (MyAPIGateway.Session.IsServer)
{
Expand All @@ -316,7 +335,6 @@ private double PerformRaycastRecursive(double length)
});

//if (dist == -1)
return dist;

//double nextDist = PerformRaycastRecursive(length);
//
Expand Down

0 comments on commit 9c202fb

Please sign in to comment.