Exemple #1
0
        public void OnInteract(Actor actor)
        {
            var v = orientation * vec3.UnitX;

            // This determins which side of the door is being opened.
            if (Utilities.Dot(v, actor.Position - position) < 0)
            {
                v *= -1;
            }

            // TODO: Retrieve static map body in a better way.
            var world = Scene.World;
            var body  = world.RigidBodies.First(b => b.BodyType == RigidBodyTypes.Static &&
                                                b.Shape is TriangleMeshShape);

            if (!PhysicsUtilities.Raycast(world, body, position + v, -vec3.UnitY, RaycastLength, out var results))
            {
                Debug.Fail("Door raycast failed (this likely means that the raycast length needs to be extended).");
            }

            // TODO: To animate properly, probably need to mark the body manually-controlled and apply step.
            actor.GroundPosition = results.Position;
            actor.ControllingBody.LinearVelocity = JVector.Zero;

            var player = (PlayerCharacter)actor;

            player.Lock();

            // Attaching a null controller freezes the camera in place while the door opens.
            Scene.Camera.Attach(null);
        }
Exemple #2
0
        public override void PostStep(float step)
        {
            // Floor raycasts are based on step height. The intention is to snap smoothly going both up and down stairs
            // (which should also work fine for normal triangles, which are much less vertically-separated than steps).
            const float RaycastMultiplier = 1.2f;
            const float Epsilon           = 0.001f;

            var body       = Parent.ControllingBody;
            var halfHeight = new vec3(0, Parent.FullHeight / 2, 0);
            var p          = body.Position.ToVec3() - halfHeight;
            var ground     = Parent.Ground;

            // If the projection returns true, that means the actor is still within the current triangle.
            if (ground.Project(p, out vec3 result))
            {
                // This helps prevent very, very slow drift while supposedly stationary (on the order of 0.0001 per
                // second).
                if (Utilities.DistanceSquared(p, result) > Epsilon)
                {
                    body.Position = (result + halfHeight).ToJVector();
                }

                return;
            }

            // TODO: Store a better reference to static meshes (rather than querying the world every step).
            var world  = Parent.Scene.World;
            var map    = world.RigidBodies.First(b => b.Shape is TriangleMeshShape);
            var normal = ground.Normal;
            var offset = PhysicsConstants.StepThreshold * RaycastMultiplier;

            // TODO: Use properties or a constant for the raycast length (and offset).
            // TODO: Sometimes downward step snapping doesn't work. Should be fixed.
            // The raycast needs to be offset enough to catch steps.
            if (PhysicsUtilities.Raycast(world, map, p + normal * offset, -normal, offset * 2, out var results) &&
                results.Triangle != null)
            {
                // This means the actor moved to another triangle.
                var newGround = new SurfaceTriangle(results.Triangle, results.Normal, 0);

                // TODO: Is this projection needed? (since the position is already known from the raycast)
                newGround.Project(results.Position, out result);
                body.Position = (result + halfHeight).ToJVector();

                Parent.OnGroundTransition(newGround);

                return;
            }

            // If the actor has moved past a surface triangle (without transitioning to another one), a very small
            // forgiveness distance is checked before signalling the actor to become airborne. This distance is small
            // enough to not be noticeable during gameplay, but protects against potential floating-point errors near
            // the seams of triangles.
            if (SurfaceTriangle.ComputeForgiveness(p, ground) > EdgeForgiveness)
            {
                Parent.BecomeAirborneFromLedge();
            }
        }
        public override void PostStep(float step)
        {
            // This raycast distance is arbitrary. Should be short, but also long enough to catch transitions from
            // one wall triangle to another.
            const float RaycastLength = 0.5f;

            var player = (PlayerCharacter)Parent;
            var radius = player.CapsuleRadius;
            var body   = Parent.ControllingBody;

            // The starting point of the raycast is pulled back by a small amount in order to increase stability
            // (without this offset, the player frequently clipped through walls).
            var p = body.Position.ToVec3() - flatNormal * (radius - 0.1f);

            if (PhysicsUtilities.Raycast(Parent.Scene.World, wallBody, p, -flatNormal, RaycastLength,
                                         out var results))
            {
                // TODO: Might have to re-examine some of this if sliding along a pseudo-static body is allowed (such as the side of a sphere).
                // While sliding along the map mesh, the current triangle can change.
                if (wall != null)
                {
                    var triangle = results.Triangle;

                    // TODO: Verify that the new triangle isn't acute enough to cause a sideways collision instead.
                    if (!wall.IsSame(triangle))
                    {
                        wall = new SurfaceTriangle(triangle, results.Normal, 0, null, true);
                    }
                }

                // Note that for meshes, the triangle (and its flat normal) might have changed here (due to the
                // condition above).
                var result = (results.Position + flatNormal * radius).ToJVector();

                if (wall != null)
                {
                    body.Position = (results.Position + flatNormal * radius).ToJVector();
                }
                else
                {
                    // TODO: Set orientation as well (for rotation platforms).
                    body.SetPosition(result, step);
                }

                return;
            }

            // TODO: Consider adding edge forgiveness.
            // By this point, the player has moved off the current wall (without transitioning to a new triangle).
            player.BecomeAirborneFromWall();
        }
Exemple #4
0
        /// <summary>
        /// Performs the collision detection method used in the "Corner Alignment" feature.
        /// </summary>
        CornerAlignmentResult CornerAlignCollisions(bool positiveDirection, float cornerDetectionDistance, LayerMask layerMask)
        {
            CornerAlignmentResult result = new CornerAlignmentResult();

            result.Reset();

            CollisionHitInfo hitInfo = new CollisionHitInfo();

            hitInfo.Reset();

            if (cornerDetectionDistance < 0)
            {
                return(result);
            }

            float castDistance = characterBody.SkinWidth + cornerDetectionDistance;


            Vector2 rayOrigin = characterController2D.transform.position +
                                (positiveDirection ? -1 : 1) * characterController2D.transform.right * characterBody.verticalArea / 2 -
                                characterController2D.transform.up * characterBody.SkinWidth;

            hitInfo = PhysicsUtilities.Raycast(
                characterBody.Is3D(),
                rayOrigin,
                (positiveDirection ? -1 : 1) * characterController2D.transform.right,
                castDistance,
                layerMask
                );

            if (hitInfo.collision)
            {
                result.point          = hitInfo.point;
                result.normalRotation = Quaternion.LookRotation(characterBody.bodyTransform.Forward, hitInfo.normal);
                result.success        = true;
            }



            return(result);
        }