public static void PM_FlyClipVelocity(ref Vector3 _velocity, Vector3 _plane) { float _m = _velocity.magnitude; if (_m <= 0F) // preventing NaN generation { return; } else if (VectorHeader.Dot(_velocity / _m, _plane) < 0F) // only clip if we're piercing into the infinite plane { VectorHeader.ClipVector(ref _velocity, _plane); } }
// The velocity 'clipping' algorithm that is ran any time a plane is detected throughout // the PM_SlideMove() func execution. // It is responsible for: // Handling velocity orientation along stable planes // Handling velocity clipping along unstable 'wall' planes public static void PM_SlideClipVelocity( ref Vector3 _velocity, bool _stability, Vector3 _plane, bool _groundstability, Vector3 _groundplane, Vector3 _up) { float _m = _velocity.magnitude; if (_m <= 0F) // preventing NaN generation { return; } else { if (VectorHeader.Dot(_velocity / _m, _plane) < 0F) // only clip if we're piercing into the infinite plane { if (_stability) // if stable, just orient and maintain magnitude { // anyways just orient along the newly discovered stable plane //VectorHeader.CrossProjection(ref _velocity, _up, _groundplane); VectorHeader.ClipVector(ref _velocity, _plane); } else { if (_groundstability) // clip along the surface of the ground { // clip normally VectorHeader.ClipVector(ref _velocity, _plane); // orient velocity to ground plane VectorHeader.CrossProjection(ref _velocity, _up, _groundplane); // i'd originally used this but when orienting velocities above certain planes, // issues would arise where velocities would be clipped and projected in the opposite // direction of where the character should be moving, so I'm resorting to orienting // in this particular scenario.... // VectorHeader.ClipVector(ref _vel, _groundplane); } else // wall clip { VectorHeader.ClipVector(ref _velocity, _plane); } } } else { return; } } }
public override void Simulate(ActorArgs _args) { ActorHeader.Actor Actor = _args.Actor; ActorHeader.GroundHit Ground = Actor.Ground; ActorHeader.GroundHit LastGround = Actor.LastGround; Vector3 Velocity = Actor._velocity; Vector3 Wish = _args.ViewWishDir; bool Grounded = Actor.SnapEnabled && Ground.stable; if (Grounded && !LastGround.stable) // Landing { VectorHeader.ClipVector(ref Velocity, Ground.normal); } // Orient Wish Velocity to grounding plane if (Grounded && OrientVelocityToGroundPlane) { VectorHeader.CrossProjection(ref Wish, new Vector3(0, 1, 0), Ground.normal); } else { // Clip Wish Velocity along upward plane if we're not orienting/stable as we may be able to fight gravity if not done VectorHeader.ClipVector(ref Wish, new Vector3(0, 1, 0)); Wish.Normalize(); } //if (Grounded) // Subtract max speed based on stability // BehaviourHeader.DetermineWishVelocity(ref Velocity, Wish, MaximumGroundMoveSpeed, GroundAcceleration * GlobalTime.FDT); //else // BehaviourHeader.DetermineWishVelocity(ref Velocity, Wish, MaximumAirMoveSpeed, AirAcceleration * GlobalTime.FDT); if (Grounded) { BehaviourHeader.ApplyAcceleration(ref Velocity, Wish, MaximumGroundMoveSpeed, GroundAcceleration); } else { BehaviourHeader.ApplyAcceleration(ref Velocity, Wish, MaximumAirMoveSpeed, AirAcceleration); } Actor.SetVelocity(Velocity); return; }
public override void Simulate(ActorArgs _args) { ActorHeader.Actor Actor = _args.Actor; ActorHeader.GroundHit Ground = Actor.Ground; Vector3 Forward = _args.ActorView.forward; Vector3 Wish = _args.ViewWishDir; bool Grounded = Actor.SnapEnabled && Ground.stable; bool DashRequest = (GlobalTime.T - DashRequestSnapshot) > 0.1F && DashStamina >= DashCost && (_args.ActionFlags & (1 << 3)) != 0; if (DashRequest) // valid dash { bool UseViewDir = Wish.sqrMagnitude == 0; // Completely eradicate all velocity Actor.SetVelocity(Vector3.zero); DashStamina -= DashCost; Vector3 DashDirection = UseViewDir ? Forward : Wish; VectorHeader.ClipVector(ref DashDirection, new Vector3(0, 1, 0)); DashDirection.Normalize(); DashRequestSnapshot = GlobalTime.T; _args.AssignHold(ApplyDashDuration); Actor.SetVelocity(DashDirection * DashVelocityDelta); DashEvents?.Invoke(_args, DashState.Enter); } else if (Grounded) // If we aren't actually dashing, let's rejuvenate our stamina if we're grounded. { DashStamina += DashGrowthRate * GlobalTime.FDT; DashStamina = Mathf.Min(DashStamina, DashCap); } }
// PM_SlideMove() is one of the several variant Move() funcs available standard with the // Actor package provided. It's entire purpose is to 'slide' and 'snap' the Actor on 'stable' // surfaces whilst also dealing with the conventional issue of movement into and along blocking // planes in the physics scene. Use this method primarily if you plan on keeping your actor level // with the floor. public static void PM_SlideMove( IActorReceiver _rec, Actor _actor, ref Vector3 _pos, ref Vector3 _vel, Quaternion _orient, LayerMask _filter, float _fdt) { /* BASE CASES IN WHICH WE SHOULDN'T MOVE AT ALL */ if (_rec == null) { return; } /* STEPS: * RUN: * GROUND TRACE & GROUND SNAP -> OVERLAP -> PUSHBACK -> CONVEX HULL NORMAL (NEARBY PLANE DETECTION) -> GENERATE GEOMETRY BITMASK -> TRACING -> REPEAT */ ArchetypeHeader.Archetype _arc = _actor.GetArchetype(); SlideSnapType _snaptype = _actor.SnapType; Collider[] _colliders = _actor.Colliders; Collider _self = _arc.Collider(); Vector3[] _normals = _actor.Normals; RaycastHit[] _traces = _actor.Hits; Vector3 _tracepos = _pos; Vector3 _groundtracepos = _pos; Vector3 _lastplane = Vector3.zero; Vector3 _groundtracedir = _orient * new Vector3(0, -1, 0); Vector3 _up = _orient * new Vector3(0, 1, 0); float _tf = 1F; float _skin = ArchetypeHeader.GET_SKINEPSILON(_arc.PrimitiveType()); float _bias = ArchetypeHeader.GET_TRACEBIAS(_arc.PrimitiveType()); int _bumpcount = 0; int _groundbumpcount = 0; int _pushbackcount = 0; int _gflags = 0; GroundHit _ground = _actor.Ground; GroundHit _lastground = _actor.LastGround; _lastground.actorpoint = _ground.actorpoint; _lastground.normal = _ground.normal; _lastground.point = _ground.point; _lastground.stable = _ground.stable; _lastground.snapped = _ground.snapped; _lastground.distance = _ground.distance; _ground.Clear(); float _groundtracelen = (_lastground.stable && _lastground.snapped) ? 0.1F : 0.05F; while (_groundbumpcount++ < MAX_GROUNDBUMPS && _groundtracelen > 0F) { // trace along dir // if detected // if stable : // end trace and determine whether a snap is to occur // else : // clip along floor // continue // else : // break out of loop as no floor was detected _arc.Trace(_groundtracepos + (_up * 0.01F), _groundtracedir, _groundtracelen, _orient, _filter, 0F, QueryTriggerInteraction.Ignore, _traces, out int _groundtraces); ArchetypeHeader.TraceFilters.FindClosestFilterInvalids( ref _groundtraces, out int _i0, _bias, _self, _traces); if (_i0 >= 0) // an intersection has occured, but we aren't sure its ground yet { RaycastHit _closest = _traces[_i0]; _ground.distance = _closest.distance; _ground.normal = _closest.normal; _ground.actorpoint = _groundtracepos; _ground.stable = _actor.DetermineGroundStability(_vel, _closest, _filter); _groundtracepos += _groundtracedir * (_closest.distance); // warp regardless of stablility. We'll only be setting our trace position // to our ground trace position if a stable floor has been determined, and snapping is enabled. if (_ground.stable) { bool _cansnap = _snaptype == SlideSnapType.Always; switch (_snaptype) { case SlideSnapType.Never: _cansnap = false; break; case SlideSnapType.Toggled: _cansnap = _actor.SnapEnabled; break; } if (_cansnap) { _ground.snapped = true; } _rec.OnGroundHit(_ground, _lastground, _filter); // gonna keep the typo bc pog // shoot up check for snap availability _arc.Trace( _groundtracepos, _up, _skin + 0.1F, _orient, _filter, 0F, QueryTriggerInteraction.Ignore, _traces, out int _stepcunt); ArchetypeHeader.TraceFilters.FindClosestFilterInvalids(ref _stepcunt, out int _i1, _bias, _self, _traces); if (_i1 >= 0) { RaycastHit _snap = _traces[_i1]; Vector3 _c = Vector3.Cross(_snap.normal, _ground.normal); _c.Normalize(); Vector3 _f = Vector3.Cross(_up, _c); _f.Normalize(); if (VectorHeader.Dot(_vel, _f) <= 0F) { if (VectorHeader.Dot(_vel, _snap.normal) < 0F) { _rec.OnTraceHit(_snap, _groundtracepos, _vel); } _gflags |= (1 << 1); VectorHeader.ProjectVector(ref _vel, _c); } _groundtracepos += _up * Mathf.Max( Mathf.Min(_snap.distance - _skin, _skin), 0F); } else { _groundtracepos += _up * (_skin); } if (_ground.snapped) { _tracepos = _groundtracepos; _lastplane = _ground.normal; _gflags |= (1 << 0); VectorHeader.ClipVector(ref _vel, _ground.normal); //VectorHeader.CrossProjection(ref _vel, _up, _ground.normal); } _groundtracelen = 0F; } else { // clip, normalize, and continue: VectorHeader.ClipVector(ref _groundtracedir, _closest.normal); _groundtracedir.Normalize(); _groundtracelen -= _closest.distance; } } else // nothing discovered, end out of our ground loop. { _groundtracelen = 0F; } } while (_pushbackcount++ < ActorHeader.MAX_PUSHBACKS) { _arc.Overlap( _tracepos, _orient, _filter, 0F, QueryTriggerInteraction.Ignore, _colliders, out int _overlapsfound); ArchetypeHeader.OverlapFilters.FilterSelf( ref _overlapsfound, _self, _colliders); if (_overlapsfound == 0) // nothing ! { break; } else { for (int _colliderindex = 0; _colliderindex < _overlapsfound; _colliderindex++) { Collider _other = _colliders[_colliderindex]; Transform _otherT = _other.GetComponent <Transform>(); if (Physics.ComputePenetration(_self, _tracepos, _orient, _other, _otherT.position, _otherT.rotation, out Vector3 _normal, out float _distance)) { _tracepos += _normal * (_distance + _skin); PM_SlideDetermineImmediateGeometry(ref _vel, ref _lastplane, _actor.DeterminePlaneStability(_normal, _other), _normal, _ground.normal, _ground.stable && _ground.snapped, _up, ref _gflags); break; } } } } while (_bumpcount++ < ActorHeader.MAX_BUMPS && _tf > 0) { // Begin Trace Vector3 _trace = _vel * _fdt; float _tracelen = _trace.magnitude; // IF unable to trace any further, break and end if (_tracelen <= MIN_DISPLACEMENT) { _tf = 0; } else { _arc.Trace(_tracepos, _trace / _tracelen, _tracelen + _skin, _orient, _filter, 0F, QueryTriggerInteraction.Ignore, _traces, out int _tracecount); ArchetypeHeader.TraceFilters.FindClosestFilterInvalids( ref _tracecount, out int _i0, _bias, _self, _traces); if (_i0 <= -1) // nothing discovered ::: { _tf = 0; // end move _tracepos += _trace; break; } else // discovered an obstruction::: { RaycastHit _closest = _traces[_i0]; Vector3 _normal = _closest.normal; float _rto = _closest.distance / _tracelen; _tf -= _rto; float _dis = _closest.distance - _skin; _tracepos += (_trace / _tracelen) * _dis; // move back along the trace line! _rec.OnTraceHit(_closest, _tracepos, _vel); PM_SlideDetermineImmediateGeometry(ref _vel, ref _lastplane, _actor.DeterminePlaneStability(_normal, _closest.collider), _normal, _ground.normal, _ground.stable && _ground.snapped, _up, ref _gflags); continue; } } } _pos = _tracepos; }