コード例 #1
0
        // 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;
        }
コード例 #2
0
        // PM_FlyMove() is one of the Move() variants packaged with the Actor sub-package found in the
        // decoupling GitHub repository. It's purpose is to allow the player to 'fly' around the physics scene
        // whilst also keeping into account the colliders and geometric planes that represent your levels. Use
        // this method primarily if you are dealing with a sort of 'spectating' or 'flying' mechanic for your
        // actors.
        public static void PM_FlyMove(
            IActorReceiver _rec,
            Actor _actor,
            ref Vector3 _pos,
            ref Vector3 _vel,
            Quaternion _orient,
            LayerMask _filter,
            float _fdt)
        {
            // STEPS:
            // RUN :
            // OVERLAP -> PUSHBACK -> CONVEX HULL NORMAL (NEARBY PLANE DETECTION) -> GENERATE GEOMETRY BITMASK -> TRACING -> REPEAT

            /*
             *  I've thought long and hard about providing documentation and comments to explain the fuckery that occurs
             *  in these lines of code, and I think it'd be best I do so.
             */

            /*
             *  Initializing our local variables at the very top of our function. I'd like to maintain this codebase to be as
             *  functional as possible, starting with the ActorHeader class.
             */

            /*
             *
             *  Here i'll be summarizing the usage of each field found below before our discrete resolution loop:
             *
             *  Archetype - The Archetype class allows us to access each primitive's Archetype class which is required to be bundled
             *  with the Monobehaviour Actor variants. Each Archetype contains its own primitive implementation provided by the Unity Physics API,
             *  and allows us to interface with each primitive without explicitly hardcoding each type's movement.
             *
             *  Colliders[] - The Colliders[] array is a crucial part to our discrete resolution loop, as it stores all potential colliders discovered in
             *  our archetype's Overlap() query. By storing these Colliders, it allows us to act upon them and attempt to compute a penetration vector and distance.
             *  This penetration vector and distance allows us to push our actor outside of any potentially overlapping colliders.
             *
             *  Vector3[] Normals - The Normals[] array isn't necessarily important as of this moment. However, I'm thinking I reuse this in some callback for the
             *  IActor interface.
             *
             *  RaycastHit[] - The RaycastHit[] traces array is crucial for the Continuous Collision Detection (CCD) portion of our resolution loop.
             *  Unity's Physics API uses the RaycastHit structure as the main output component for interfacing with its various casting methods.
             *  In our case, we don't want to generate garbage during our trace, so we use the NonAlloc() variant for our linear cast.
             *
             *  We then operate on this array and filter it to find the closest valid trace intersection for our interception resolution.
             *
             *  Vector3 TracePos - We create a copy of our actor's position to operate upon during our movement. This is implemented just in case any last minute
             *  'safety' detections need to run and determine if the newly calculated position is safe to warp to.
             *
             *  Vector3 LastPlane - We create a local Vector3 to store the previous plane we've encountered during our resolution loop.
             *  I'd initially settled with Q1's "clip and pray" approach to dealing with creases, followed by a for loop looking for a crease.
             *  However, Q1's initial crease/corner detection loop was is kinda clunky and ugly and I'm not even sure it works 100% of the time.
             *
             *  I must confess, this method of detection found in PM_DetermineNearbyTopology() isn't pretty either, but it gets the job done perfectly.
             *
             *  float TimeLeft - I use a fraction instead of the actual distance required, as its generally much cleaner to express what is occuring then to iteratively
             *  subtract the traced distance. 1 = trace has just begun, 0 = trace is finished. Any value in between is a percentage of how much further we need to travel.
             *
             *  float Skin - the skin variable is important as it prevents tunneling when the Actor gets asymptotically close to the surface of any infinite plane
             *  detected in either the discrete pushback phase or continuous tracing phase.
             *
             *  int BumpCount - stores the amount of bump iterations that have occured during our resolution loop.
             *
             *  int PushbackCount - stores the amount of discrete pushbacks that have occured during our resolution loop.
             *
             *  int GeometryFlags - stores the current state of our Actor's immediate surroundings. 0 = Discovered Plane, 1 = Discovered Crease, 3 = Discovered Corner
             */

            ArchetypeHeader.Archetype _arc = _actor.GetArchetype();
            Collider[] _colliders          = _actor.Colliders;
            Collider   _self = _arc.Collider();

            Vector3[]    _normals = _actor.Normals;
            RaycastHit[] _traces  = _actor.Hits;

            Vector3 _tracepos  = _pos;
            Vector3 _lastplane = Vector3.zero;

            float _tf   = 1F;
            float _skin = ArchetypeHeader.GET_SKINEPSILON(_arc.PrimitiveType());

            int _bumpcount     = 0;
            int _pushbackcount = 0;
            int _gflags        = 0;

            // Attempt an Overlap Pushback at this current position:
            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);

                            if (VectorHeader.Dot(_vel, _normal) < 0F) // In this overlap, we  want the immediate normals
                            {
                                PM_FlyDetermineImmediateGeometry(ref _vel, ref _lastplane, _normal, 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;
                    break;
                }
                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,
                        ArchetypeHeader.GET_TRACEBIAS(_arc.PrimitiveType()),
                        _self,
                        _traces);

                    if (_i0 <= -1)      // nothing discovered:::
                    {
                        _tf        = 0; // end move
                        _tracepos += _trace;
                        break;
                    }
                    else // discovered an obstruction:::
                    {
                        RaycastHit _closest = _traces[_i0];
                        float      _rto     = _closest.distance / _tracelen;
                        _tf -= _rto;

                        float _dis = Mathf.Max(_closest.distance - _skin, 0F);
                        _tracepos += (_trace / _tracelen) * _dis;                                                 // move back along the trace line!

                        PM_FlyDetermineImmediateGeometry(ref _vel, ref _lastplane, _closest.normal, ref _gflags); // determine our topology state
                        continue;
                    }
                }
            }

            _pos = _tracepos;
        }