Home > Game Development Walkthroughs > 2D Platform Games Part 5: Surface Dynamics (slippery and sticky platforms)

## 2D Platform Games Part 5: Surface Dynamics (slippery and sticky platforms)

IMPORTANT! All of the articles in this series require you to have either Visual Studio 2010 or the Visual C++ 2010 Redistributable Package (4.8MB) installed in order to be able to try the pre-compiled EXEs provided with the examples. The Redistributable Package can be freely bundled with your own applications.

This article builds upon the demo project created in 2D Platform Games Part 4: Moving Platforms and Crush Detection. Start with 2D Platform Games Part 1: Collision Detection for Dummies if you just stumbled upon this page at random!

So far we’ve covered static platforms and moving platforms and provided accurate collision detection for each. We’ve also started to create a framework which lets us configure the behavioral properties of individual platforms. This puts us in a good position to introduce new types of platforms, like the classic ‘slippery ice’ and ‘sticky’ platforms which are the examples I shall show below. These types of platforms which merely change the movement physics of the player don’t require any changes to the collision detection system, whereas ladders and pass-through platforms (where you can jump onto them from below but are solid on top) do, so we defer these to subsequent articles.

Let us begin by creating a list of possible platform types. We’ll start with three – normal platforms, frictionless platforms (“ice”) and platforms with high friction (“sticky”):

```// Possible platform types
typedef enum {
Normal = 0,
Frictionless = 1,
Sticky = 2
} SurfaceType;
```

We will then create a new `struct` which describes the properties of each type of surface:

```// Properties of each platform type
struct Surface {

// The image to use as a tile
ImageBrush tile;

// X-acceleration when accelerating in the current direction of movement
float accXf;

// X-acceleration when accelerating away from the current direction of movement (turning around)
float accXb;

// X-deceleration to be applied when player does not attempt to move left or right
float decX;

// The maximum allowed X and Y movement speeds
float maxSpeedX;
float maxSpeedY;
};
```

First we allow each platform type to have its own sprite (image brush). This lets each platform type have a distinct visual appearance to make it obvious to the player what kind of platform we are standing on. Next, we define how the player should move when on the surface. These replace the global defaults we have used until now. Note that I have de-composed `accX` into two new variables `accXf` and `accXb`. While completely optional, this gives greater flexibility over the movement of the player, enabling control of how difficult it is for him or her to stop and begin moving in the opposite direction.

In the definition of each individual `Platform`, we add a member indicating the surface type of the platform:

```// An individual platform or surface
struct Platform {

// The surface type
SurfaceType surface;
...
};
```

In the main game class definition, we add an array defining each surface and change the definition of `accX` to use `accXf` and `accXb` instead. Note that we still keep these variables (along with `maxSpeedX` and `maxSpeedY`) and they will now refer to the physics model currently in use. The reason we need this is because we won’t always be on a platform; sometimes we’ll be in mid-air, but we want to keep using the same physics until the player lands on a different platform, however we can’t check which settings to use when we’re not standing on a platform, so we must keep a copy of them for when the player leaves the platform. If we don’t do this, players can cheat on ice and sticky platforms by jumping up to slow themselves down or speed themselves up.

We also get rid of the global image to use for platforms since we will now select a different image for each one:

```/* Remove this */
// The bitmap brush to use to draw the platforms
ImageBrush tile;
/* */
...
// Platform type definitions
Surface surfaces[3];
...
// The amount of X acceleration to apply when the player moves forward (current direction of travel) or backwards (opposite direction)
// The amount of X deceleration to apply when the player does not attempt to move left or right
// These are updated depending on the type of the last surface stood on
float accXf, accXb, decX;
...
}
```

In the class constructor, we replace the assignment of `tile` with loaders for images for each platform type:

```surfaces[Normal].tile       = MakeBrush(MakeImage(L"tile.png"), Custom, D2D1_EXTEND_MODE_WRAP);
surfaces[Frictionless].tile = MakeBrush(MakeImage(L"ice-tile.png"), Custom, D2D1_EXTEND_MODE_WRAP);
surfaces[Sticky].tile       = MakeBrush(MakeImage(L"glue-tile.png"), Custom, D2D1_EXTEND_MODE_WRAP);
```

We remove the assignments to `accX`, `decX`, `maxSpeedX` and `maxSpeedY` as these will be set to defaults whenever the player re-spawns (in case the player dies on an ice or sticky platform, we need to reset the physics model when the player re-spawns).

Next we define the properties of each surface type:

```// Define surface types
surfaces[Normal].accXf = 12.f * mScale;
surfaces[Normal].accXb = 12.f * mScale;
surfaces[Normal].decX = 15.f * mScale;
surfaces[Normal].maxSpeedX = 5.0f * mScale;
surfaces[Normal].maxSpeedY = 10.0f * mScale;

surfaces[Frictionless].accXf = 24.f * mScale;
surfaces[Frictionless].accXb = 24.f * mScale;
surfaces[Frictionless].decX = 0.f * mScale;
surfaces[Frictionless].maxSpeedX = 12.0f * mScale;
surfaces[Frictionless].maxSpeedY = 30.0f * mScale;

surfaces[Sticky].accXf = 1.f * mScale;
surfaces[Sticky].accXb = 15.f * mScale;
surfaces[Sticky].decX = 20.f * mScale;
surfaces[Sticky].maxSpeedX = 2.0f * mScale;
surfaces[Sticky].maxSpeedY = 10.0f * mScale;
```

These figures can take a bit of tweaking to get them to behave as you want; I have found the above values are fairly representative of most video games. The Normal surface uses the same settings we have used globally until now, and are the settings used by default.

As you can see, FrictionLess platforms cause acceleration twice as fast (24.f vs 12.f), have no deceleration at all in `decX` which removes all friction, and your maximum speed is considerably faster, to allow the player to shoot back and forth on the ice.

In sharp contrast, Sticky platforms decelerate the player very fast when they are not trying to move; they also allow the player to stop and turn around very quickly, but acceleration and top speed are very slow, making it difficult to walk on the platform.

In `SetupResources()` we add a line in the object creation loop to set the default surface type of each platform:

```for (int o = 0; o < worldObjectCount; o++)
{
...
p.surface = Normal;
...
}
```

and then at the end of the function, we configure a few platforms to use the new surface types:

```// Slippery ice platform
worldObjects[15].surface = Frictionless;
worldObjects[16].surface = Frictionless;
worldObjects[17].surface = Frictionless;

// Glue tiles
worldObjects[19].surface = Sticky;
worldObjects[20].surface = Sticky;
```

In `spawnPlayer()`, add code to reset the physics when the player spawns:

```// Set defaults before we are standing on any platform
accXf = surfaces[Normal].accXf;
accXb = surfaces[Normal].accXb;
decX = surfaces[Normal].decX;
maxSpeedX = surfaces[Normal].maxSpeedX;
maxSpeedY = surfaces[Normal].maxSpeedY;
```

Finally, in the rendering code, change the line which uses the old global tile to select an image depending on the object’s surface type:

```SetBrush(surfaces[it->surface].tile);
```

So far, so good. This is all pretty standard stuff and there shouldn’t really be any surprises so far. If you run the program now you should see the visual appearance of each platform type (the ice and sticky platforms are on the right of the first screen in our example game world), but the player’s behaviour remains the same as before.

#### Altering Player Physics

The physics applied to the player should depend on the type of the platform we are standing on. So as soon as we land or walk onto a new platform, we update the current physics settings with the settings from that platform’s surface type as follows (this code goes immediately after the player’s position and platform currently standing on are updated, and right before checking the keyboard input):

```// The physics applied to the player depend on the type of surface we are standing on
if (standingPlatform)
{
accXf = surfaces[standingPlatform->surface].accXf;
accXb = surfaces[standingPlatform->surface].accXb;
decX = surfaces[standingPlatform->surface].decX;
maxSpeedX = surfaces[standingPlatform->surface].maxSpeedX;
maxSpeedY = surfaces[standingPlatform->surface].maxSpeedY;
}
```

Note that if we are not standing on a platform (in mid-air), the settings are not updated or set to the normal defaults, but the current settings are retained instead. This prevents the ‘jumping cheat’ I mentioned earlier. This behaviour is optional and if you don’t want the player to be able to go whizzing off ice platforms at high speed, you may wish to reset the physics settings when the player enters flight.

We also need to change the processing of moving left and right with the correct amount of acceleration or deceleration:

```// Move (accelerate) leftwards
if (GetAsyncKeyState(VK_LEFT))
{
/* Remove this line */
speedX -= LinearMovement(accX, updateTick);

// How much acceleration to apply depends on whether we are trying to move
// in the same direction as now, or the opposite way
if (speedX <= 0)
speedX -= LinearMovement(accXf, updateTick);
else
speedX -= LinearMovement(accXb, updateTick);

moveRequest = true;
}

// Move (accelerate) rightwards
if (GetAsyncKeyState(VK_RIGHT))
{
/* Remove this line */
speedX += LinearMovement(accX, updateTick);

if (speedX >= 0)
speedX += LinearMovement(accXf, updateTick);
else
speedX += LinearMovement(accXb, updateTick);

moveRequest = true;
}
```

This code simply ensures that if we try moving in the current direction of travel, `accXf` is applied, otherwise `accXb` is applied.

#### That’s All She Wrote

The demo code linked at the top of the page includes all of these changes so you can see the surface types in action.

In Part 6 we will examine a true nightmare of collision detection and player physics handling as we introduce ladders to our platform game. Until next time!