private static void PM_FlyDetermineImmediateGeometry( ref Vector3 _vel, ref Vector3 _lastplane, Vector3 _plane, ref int _gflags) { switch (_gflags) { case 0: // plane detected PM_FlyClipVelocity(ref _vel, _plane); _gflags |= (1 << 0); break; case (1 << 0): // potential crease detected if (Mathf.Abs(VectorHeader.Dot(_lastplane, _plane)) < FLY_CREASE_EPSILON) { Vector3 _c = Vector3.Cross(_lastplane, _plane); _c.Normalize(); VectorHeader.ProjectVector(ref _vel, _c); _gflags |= (1 << 1); } else { PM_FlyClipVelocity(ref _vel, _plane); } break; case (1 << 0) | (1 << 1): // corner detected _vel = Vector3.zero; _gflags |= (1 << 2); break; } _lastplane = _plane; }
// This func is vital to preventing undesirable behaviour throughout the lifetime of the // PM_SlideMove() execution loop. This function is responsible for identifying the geometry around // an actor's position throughout the duration of the move. // It is responsible for: // Handling generic velocity clipping // Handling generic crease projecting // Preventing tunneling at corners/creases at any point in our movement. private static void PM_SlideDetermineImmediateGeometry( ref Vector3 _vel, ref Vector3 _lastplane, bool _stability, Vector3 _plane, Vector3 _groundplane, bool _groundstability, Vector3 _up, ref int _gflags) { switch (_gflags) { case 0: // plane detected PM_SlideClipVelocity(ref _vel, _stability, _plane, _groundstability, _groundplane, _up); _gflags |= (1 << 0); break; case (1 << 0): // potential crease detected float _od = Mathf.Abs(VectorHeader.Dot(_lastplane, _plane)); if (!_stability && _od < FLY_CREASE_EPSILON) { Vector3 _c2 = Vector3.Cross(_lastplane, _plane); _c2.Normalize(); VectorHeader.ProjectVector(ref _vel, _c2); _gflags |= (1 << 1); } else { PM_SlideClipVelocity(ref _vel, _stability, _plane, _groundstability, _groundplane, _up); } break; case (1 << 0) | (1 << 1): // multiple creases detected _vel = Vector3.zero; _gflags |= (1 << 2); break; } _lastplane = _plane; }
// 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; }