Skip to content

Instantly share code, notes, and snippets.

@thygrrr
Last active February 5, 2022 12:43
Show Gist options
  • Save thygrrr/4138fdbed9bdaff5bf9d41ee08d3dd04 to your computer and use it in GitHub Desktop.
Save thygrrr/4138fdbed9bdaff5bf9d41ee08d3dd04 to your computer and use it in GitHub Desktop.
A ECS system where a physical object can "cut" through a turn and glide like a ship or a bird.
using Jovian.ECS.Components;
using Jovian.ECS.Components.Space;
using Jovian.ECS.Components.Vessels;
using Jovian.ECS.Systems.Physics;
using Unity.Burst;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;
namespace Jovian.ECS.Systems.Control
{
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(BuildPhysicsWorld))]
public class VesselOpenSpaceControl : PhysicsSystemBase
{
protected override void OnUpdate()
{
var dt = timeStep;
//Ok why is this not needed
Dependency = JobHandle.CombineDependencies(exportPhysicsWorldSystem.GetOutputDependency(), Dependency);
//Hey, this indents kinda nicely for once. :)
Entities
.WithNone<BubbleGroup>()
.ForEach(
(
ref OpenSpaceVelocity velocity,
in ShipControls controls,
in Propulsion propulsion, //TODO: Create custom OS propulsion?
in PhysicsMass mass,
in Rotation rotation
) =>
{
//We don't have transform.rotation in ECS
var rot = rotation.Value;
//We don't have transform.forward etc. in ECS, much simpler for GameObjects
var forward = math.rotate(rot, new float3(0, 0, 1));
var right = math.rotate(rot, new float3(1, 0, 0));
var up = math.rotate(rot, new float3(0, 1, 0));
var linear_thrust =
forward *
controls.translate.z *
propulsion.Thrust.z *
mass.InverseMass * dt;
var linear_magnitude = math.length(velocity.Linear);
if (linear_magnitude > 0)
{
var linear_direction = math.normalize(velocity.Linear);
//Apply trim tensor logic allowing to preserve forward velocity through turns.
var lateral_flow = propulsion.TrimX * math.abs(math.dot(right, linear_direction));
var vertical_flow = propulsion.TrimY * math.abs(math.dot(up, linear_direction));
var flow = (lateral_flow + vertical_flow) * linear_magnitude * dt;
var inert = linear_magnitude - flow; //this is a remainder, so no dt
// "Gliding"
var linear_flow = flow * forward;
// "Sliding"
var linear_inert = inert * linear_direction;
velocity.Linear = linear_flow + linear_inert;
}
//Standard thrust addition.
velocity.Linear += linear_thrust;
//Lerp between different turning speeds based on current speed
var v_effective = math.saturate(math.length(velocity.Linear / propulsion.MaxRequiredVelocity));
var turning = controls.pitch * math.lerp(propulsion.TurnMin, propulsion.TurnMax, v_effective);
velocity.Angular += turning * dt * mass.InverseMass;
}
).ScheduleParallel();
//Ok why is this not needed, and why wasn't it BuildPhysicsWorld
//endFramePhysicsSystem.AddInputDependency(Dependency);
}
}
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(BuildPhysicsWorld))]
[UpdateAfter((typeof(VesselOpenSpaceControl)))]
public class VesselSpaceIntegrator : PhysicsSystemBase
{
[BurstCompile]
protected override void OnUpdate()
{
var dt = timeStep;
Entities
.WithNone<BubbleGroup>()
.ForEach(
(
ref FloatingPosition floating,
ref OpenSpaceVelocity velocity,
ref Rotation rotation,
in PhysicsDamping damping
) =>
{
velocity.Angular *= math.clamp(1.0f - damping.Angular * dt, 0, 1);
velocity.Linear *= math.clamp(1.0f - damping.Linear * dt, 0, 1);
IntegrateOrientation(ref rotation.Value, velocity.Angular, dt);
floating.position += velocity.Linear * Constants.Space2ViewF * dt;
}
).ScheduleParallel();
}
private static void IntegrateOrientation(ref quaternion orientation, float3 angular_velocity, float dt)
{
quaternion dq = IntegrateAngularVelocity(angular_velocity, dt);
quaternion r = math.mul(orientation, dq);
orientation = math.normalize(r);
}
// Returns a non-normalized quaternion that approximates the change in angle angularVelocity * timestep.
private static quaternion IntegrateAngularVelocity(float3 angular_velocity, float dt)
{
var half_delta_time = new float3(dt * 0.5f);
var half_delta_angle = angular_velocity * half_delta_time;
return new quaternion(new float4(half_delta_angle, 1.0f));
}
}
[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(BuildPhysicsWorld))]
public class VesselSpaceTrajectoryMover : PhysicsSystemBase
{
protected override void OnUpdate()
{
var dt = timeStep;
Dependency = JobHandle.CombineDependencies(exportPhysicsWorldSystem.GetOutputDependency(), Dependency);
var up = new float3(0, 1, 0);
//Trajectory Movement
Entities
.WithNone<BubbleGroup>()
.ForEach(
(
ref FlipAndBurnTrajectory trajectory,
ref FloatingPosition floating,
ref Rotation rotation
) =>
{
if (trajectory.time == 0)
{
trajectory.Initialize();
}
trajectory.time += dt / trajectory.duration;
trajectory.Evaluate(out var pos, out var dir, out var accel);
floating.position = pos;
rotation = new Rotation() {Value = quaternion.LookRotation(dir, up)};
}
).ScheduleParallel();
endFramePhysicsSystem.AddInputDependency(Dependency);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment