Posts Tagged Grappling Hook

Grappling Hook prototype III

Now that I’ve got the mechanics for the grappling hook roughly where I want them, I’ll try and describe the approach I used to build it. Note that this isn’t a perfect implementation by any means – it interacts badly with obstructions in some cases, doesn’t work well when attached to vertical surfaces, and doesn’t use any sort of rope or spring simulation to model the chain. However, the grappling hook in my platformer is intended to be used to swing from specifically placed ‘grapple points’ which will mostly be on horizontal surfaces, so it meets my needs.

The first step for implementing the grappling hook was to build a simple representation of the hook itself, so the player could throw it in a given direction and see it stop when it hit something. For this purpose, I created a simple GrapplingHook class to represent the position and velocity of the hook, along with other bits of state like whether or not it has made contact with a surface:

    public class GrapplingHook : IGameComponent, IUpdateable, IDrawable {
        protected Game _Game;
        public Vector2 Position, Velocity;
        public bool Retracting = false;
        public bool Stopped = false;
        public LevelGeometry AttachedTo = null;
        public float AttachY = 0;

        public GrapplingHook (Game game, Vector2 initialPosition, Vector2 direction) {
            _Game = game;
            Position = initialPosition;
            Velocity = direction * Game.GrapplingHookSpeed;
        }

The significance of some of those other variables will be made clear later. To throw the hook, the Player class constructs a hook object at the player’s throwing position and assigns it a direction to move in, based on the direction the player was aiming in when he threw the hook.

The player’s throwing position is simply the player’s actual position with half of his height subtracted, so that the hook is thrown from roughly the middle of his torso (close to his hands). This is also the position used when swinging from the hook.

        protected void LaunchHook () {
            DetachHook();
            if (Hook != null)
                return;

            var dir = (AimingWithRightStick) ? LastAimDirection : AimDirection;
            if (dir.LengthSquared() <= 0)
                return;

            var AimStart = Position + new Vector2(0.0f, -Height / 2.0f);

            Hook = new GrapplingHook(Game, AimStart, dir);
            Game.Components.Add(Hook);
        }

The direction is computed in a separate piece of logic. You can see a few other pieces of logic here – I allow you to aim and throw a grappling hook by pressing and releasing the right stick, as an alternative to using the button on the controller/keyboard. This allows you to use the left and right sticks in concert to use the grappling hook to swing between surfaces and traverse longer distances, without having to use the left stick to aim. I make sure to ignore any direction vectors with no length, since those indicate that the player released the aiming button without pushing in a direction. Likewise, if a grappling hook is already in existence, I bail out in order to avoid having multiple grappling hooks in midair at once. The DetachHook call at the top ensures that pressing the grappling hook button will retract any existing grappling hook, in order for you to be able to launch a new one.

The code responsible for aiming is fairly simple: It comes up with a direction vector based on the player’s input, and casts a ray from the player’s throwing position in that direction. The closest surface that ray makes contact with (if any) is treated as the ‘aim position’ and highlighted with a bright crosshair to show the player that the hook will land there if thrown. This is similar to the logic used by the hook itself to determine when it’s made contact with a surface and needs to stop.

With these bits of code combined, the player can now toss a grappling hook in a given direction, and the game knows how to draw the hook and chain in the correct location. The logic for allowing the player to swing from the hook is a bit more complicated.

One of the key changes I had to make was representing the player’s position while swinging using polar coordinates, instead of traditional cartesian coordinates. What this means is that instead of using X and Y axes, I’m storing the player’s location as a radius and an angle. This allows the basic motions possible on the hook to be represented simply and makes it easier to model acceleration and deceleration based on the player’s position on the chain.

Grappling hook diagram

Grappling hook diagram

As shown by this artfully constructed and COMPLETELY FLAWLESS diagram, I then map the player’s existing Velocity property to these new axes so that I can reuse sections of the existing input and motion code. Velocity on the X axis becomes velocity on the angle, and velocity on the Y axis becomes velocity on the radius. This means that the input code will do the right thing with only minor modifications – left and right to swing on the chain, up and down to move up and down on the chain. (Perceptive readers may note that this approach completely breaks if the chain is not vertical.)

While the player is swinging from a chain, the code used to perform motion and collision detection is altered (though fundamentally the same). Velocity is applied by converting the player’s current Position (in cartesian coordinates) to a position in the same format used by the player’s velocity. The velocity is then applied to that polar position to compute a new position in polar format, and then the resulting polar position is converted back to cartesian coordinates. After doing that ridiculous song and dance, I now have a pair of cartesian coordinates that I can feed into the standard collision detection algorithm, ensuring that the player at least does not clip through walls while swinging on the chain.

    public struct HookPosition {
        public Vector2 Center;
        public float Rotation;
        public float Distance;

        public static HookPosition FromXY (Vector2 center, Vector2 xy) {
            var result = new HookPosition {
                Center = center,
                Rotation = (float)Math.Atan2(xy.Y - center.Y, xy.X - center.X),
                Distance = (xy - center).Length()
            };

            return result;
        }

        public Vector2 ToXY () {
            return new Vector2(
                Center.X + ((float)Math.Cos(Rotation) * Distance),
                Center.Y + ((float)Math.Sin(Rotation) * Distance)
            );
        }
    }

To allow launching yourself from the chain in order to make it more useful, I also take the extra step of ‘converting’ the player’s velocity. When you first grab onto the chain, I convert your current velocity into polar coordinates, so that your momentum is mostly preserved. Likewise, when you let go of the chain I perform the reverse conversion, translating your momentum on the chain into normal momentum. This allows you to launch yourself up into the air a short distance by letting go while you’re at the apex of a swing, and multiplying the resulting velocity by 2 or so ensures that you get a satisfying amount of momentum out of it.

I also do some work to constrain the range of the player’s movement while on the chain. The two main constraints are that the chain cannot swing vertically above the hook, and that the chain’s length must be within a certain range. This prevents the player from horrifically exploiting the game’s geometry by using the chain to descend long distances, ascend to distant ceilings, or ‘flip’ himself up over a ledge by swinging the chain until it is nearly vertical.

Ensuring that the momentum of the player on the chain ‘feels’ right also requires a few other details – I have to scale the player’s X velocity based on the current length of the chain, so that it feels like your momentum on the chain is relatively equivalent regardless of how long it is. This is a downside to using polar coordinates instead of cartesian coordinates – the actual distance travelled by increasing your angle by 2 is determined by your current radius, so you have to take the radius into account when converting to/from cartesian coordinates. I also have to tweak the speed at which you move up and down the chain to stay consistent with what the player would expect from gravity, and make it harder to accidentally move up or down the chain while trying to swing back and forth, since it’s fairly easy to accidentally generate up or down inputs on the 360’s dpad and left stick.

Getting realistic momentum with the effect of gravity is a little bit tougher – the solution I’m using is a bit of a hack, but works more or less right. Whenever processing the player’s input, I first determine how far ‘up’ the arc of the chain he is by subtracting the hook’s position on the arc from his position on the arc. This gives me an angle that ranges from -90 degrees to 90 degrees. At this point, I can convert that distance into a multiplier that will cause the player’s momentum to taper off as the chain comes close to becoming horizontal, and likewise decrease the effect of gravity as the chain comes close to becoming vertical. This allows you to easily swing back and forth while the chain is hanging downward but prevents you from gaining absurd momentum and flipping the chain up over the hook.

The finishing touches basically involve playability concerns – making the hook automatically detach if the player tries to jump off a wall or floor, drawing the chain (this is pretty simple – I walk the distance between the player and the hook in increments of a few pixels at a time, blitting a rotated ‘chain link’ sprite repeatedly to render the chain), and making sure logic for things like wall clinging and mantling doesn’t run while the player is hanging from a grappling hook.

And finally a couple side notes:

I spent some time working on an initial implementation of falling damage and player health. It’s functioning, but still needs some polish. Right now falling 1200 pixels will kill you and any fall under 600 pixels will not injure you at all, with a linear scale inbetween – I think I want a more fine-tuned approach to falling damage than that, and I’m still not entirely sure falling damage will be beneficial to the design of the game.

I also spent some time refining the algorithm I use for collision detection, in order to address some edge cases related to sloped surfaces and the edges of surfaces. Previously there was a bug where you could ’slip through’ the edge of a rectangular surface if you were nearly intersecting another surface. This was due to a problem in my ResolveMotion algorithm that caused it to produce velocity vectors that were marked as ‘not intersecting’ when they actually resulted in an intersection between two shapes. Fixing this bug revealed some problems with my implementation of sloped surfaces that caused things to get completely busted on surfaces with steep slopes (slopes where y/x < 1, essentially). I spent about 6 hours testing different approaches to solving the problem before finally arriving at the solution I’m using now, which works correctly for all my test slopes, fixes the problem with slipping through surfaces, and still performs relatively well. As a bonus, the code is much smaller now and is actually faster than the old code in some cases. The downside is that I had to cheat to get a satisfactory result for surfaces with steep slopes.

        public const float StupidCollisionFudgeFactor = 0.1f;

Borne of necessity, I have unleashed a monster upon the waking world. It is arbitrary, hard-coded, and very, very stupid. But at least it works!

Tags: , , , ,

Grappling Hook prototype II

More work on the grappling hook. Still working out the physics and controls. The collision detection still has some issues to be resolved as well…

Tags: , , , ,

Grappling Hook prototype

Not quite done with this one yet, so I don’t have much to say:

I’ve been working on an implementation for the player’s grappling hook. So far I have some of the basics implemented – aiming the hook, throwing it, the hook attaching itself to surfaces, the hook retracting so you can throw it again, and so on. I still have to figure out the majority of the physics for the hook, along with other details like preventing it from going through walls and floors.

Getting it to work has required me to make some significant refactorings to the code I use to handle player movement. So far, the code’s been becoming simpler, but I’ve had to go back and rethink some of the logic I wrote to handle acceleration and deceleration. I’ve been replacing a lot of special cases and nested ifs with expressions that make heavy use of Min, Max, Sign, and Abs to properly clamp and adjust the player’s velocity based on various inputs (is the user pressing the dpad, is the user jumping, etc.)

Tags: , , , ,

luminance is Digg proof thanks to caching by WP Super Cache