Tutorial game: Let’s crash into stuff (part 8)

Note: The source code for this tutorial is available in the tutorials/TutorialGame_Introduction/part8.ras (in the TRSEĀ  framework), together with all resources mentioned in these texts.

The next logical step in our tutorial game is to implement background collisions. The C64 VIC chip has support for hardware sprite collisions with both other sprites and background, but this feature is unfortunately kind of sucky. Firstly, because sprites will only collide with background that is transparent (background color), and secondly because there is no way to get information on what the sprite is colliding with.

We’ll therefore be opting for software collision, which unfortunately is a somewhat costly – but necessary process. Here is the idea:

  • Create a 256 byte array of values that correspond to the background character set. Let “0” represent tiles that are collidable.. collideable? crashable , and “1” those that are not. This is requires a lot of memory, and should ideally be shorted to 32 bytes of bits on/off, but is harder to implement. In addition, this way it is possible to enable other kinds of lookups: entrances and exits, special tiles, hidden values and loot pickup etc. So let’s opt for the easy but costly way.
  • Next, for each frame we simply transform the player sprite coordinates to screen character space.
  • When moving the player sprite, we only have to check if the screen character space tile in the movement direction has “0” or “1”. if “1”, we simply prevent the player from moving.

Start by copying the previous part “part7.ras” to a new file, “part8.ras”. First, let’s add some new variables:

 


var 
    ...
	k,l,blockx, blocky,map_player_direction : byte = 0;

	background_mask: array[256] of byte=(
	1,1,1,1,1,1,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	1,1,1,1,1,1,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0);

Here, we added some new counter variables (k,l) in addition to blockx and blocky, which will ble toggled if x/y movement is blocked. We also have a map_player_direction variable, which basically tells us which direction the player is moving. Finally, we have the large (and somewhat unnecessary) block of bytes which corresponds to the charset: which characters have collisions (1), and which not (0). This will probably be included in the charset color map in a future edition. Or, you can compress it as you wish. For now, this is the easiest way of performing sprite collisions.

Next, let’s add a background collosion test routine


procedure PlayerBackgroundCollide();
var
	tmp:integer;
	
begin
	tmp:=(player_x - 12);
	i:=tmp/8;
	j:=(player_y-38)/8;
	moveto(i-1,j-1, $04);
	blockx:=0;
	blocky:=0;
	
	k:=screenmemory[ map_player_direction +40+1];
	
	if (background_mask[k]=1) then begin
		if (joystickdown=1 or joystickup=1) then blocky:=1;
		if (joystickleft=1 or joystickright=1) then blockx:=1;
	end;
end;

Explanation:

  • Define an integer tmp that corresponds to the charset space location of the sprite position (sprite_x-12)/8. We set byte i to be tmp afterwards.
  • Similarly, let j be the charset space location of the sprite y position: j:=(sprite_y-38)/8. These numbers 12 and 38 are only chosen because they arbitrarily represent the center of our player sprite.
  • We then move the screenmemory pointer with moveto to the sprite position (x-1, y-1), or the upper left corner of the sprite.
  • Obtain the current character value of the screenmemory position shifted with map_player_direction.
    • Note: map_player_direction contains the current screenmemory displacement. The values are 1 character space if you go right, 40 if you go down, -40 if you go up and -1 if you go left
    • Adding 40+1 to this value yields the current screenmemory position of the direction that the joystick is pointing to
  • if background_mask is set to 1 at this position, prevent either blocking in the x or y direction

Finally, here’s the modified MovePlayerSprite procedure:


procedure MovePlayerSprite();
begin
	joystick();

	map_player_direction:=0;
	if (joystickleft=1) then map_player_direction:=map_player_direction-1;
	if (joystickright=1) then map_player_direction:=map_player_direction+1;
	if (joystickdown=1) then map_player_direction:=map_player_direction+40;
	if (joystickup=1) then map_player_direction:=map_player_direction-40;

	PlayerBackgroundCollide();

	if (blockx=0) then
		player_x:=player_x+joystickright-joystickleft;
	if (blocky=0) then
		player_y:=player_y+joystickdown-joystickup;

	spritepos(player_x,player_y,@playerSprite);
end;

Explanation:

  • Register joystick movement
  • Calculate the direction of the player movement (map_player_direction) directly translated to the screen position
  • Call the PlayerBackgroundCollosion that will use this map_player_direction variable to set blocking (blockx, blocky)
  • Check for blocking in x or y direction, move player sprite, ignore if blocked=1

And that’s it! you now should have blocked movement when hitting any of the blocks you have defined in your character set. Remember to update the background_mask to correspond to your own character set!

If this tutorial is starting to look like Rogue Burger One, there is a good reason for it.

Next tutorial.