Collisions


It's been a while since my last development log. I'm trying to keep the game a little under wraps until I can reveal some actual game play. So for that reason I'm going to talk about some of the code in Doc in a bit more detail instead and save the latest work in progress demonstration until the near future. I'm also going to do these logs less frequently, every 2-3 weeks to ensure that there is actual content to put in them. So without delay let's talk about collisions in Doc Cosmos 2.


Mapping sprites to the map data

In order to get Doc to collide properly with the world I need to know whats in his way in every direction so that he can land on the floor, hit the ceiling when he jumps and stops at walls he runs into.  To achieve this we start by working out where Docs sprite is in relation to the screen co-ordinates. The screen is 40 x 25 and the sprite area that fits that is 320 x 200 as its 8x8 pixels per char. A sprite itself is 24x21 but for the purposes of collision its easier to make that 16x16 and enough of Doc fits inside of that trim:


The trimmed red rectangle represents the box that will collide with the background.  Colliding with the ground needs to be accurate so the base is perfectly aligned with the feet, the left and right need to be less accurate but enough to make sure it doesn't look odd  stood against a wall and so in this example I have the collision box pretty centered on Doc with just the tip of his gun and foot sticking out in front so that Doc will be pretty flush against any walls he runs into, the flipped version of this sprite should have the same two pixels sticking out but on the left instead. The top of this collision box is less important, its only job is to block when going up and so is only used on low ceiling passages, jumping or ladders if they reach the ceiling (they don't currently). So the top edge can be snapped to 16 pixels to make the calculations easier, and it just means Docs head has 4 rows of pixels that don't collide, and that's acceptable and looks fine in every situation so far.

So as Doc moves around we can work out which 16x16 section of sprite space he is in and use that to work out his char position. The picture below shows Doc stood in a part of the world:


With the map using multi-directional scrolling we need to include the fine scroll H and V values into our calculation because sprites do not move only the background. These values range from 0 to 7 and are used along with the offset for the trimmed sprite rectangle to calculate the sprites actual position on screen. Sprite positions are measured from the screen edges not the border so we need to subtract 24 from the horizontal position for the left border and 50 from the vertical position for the top border. Now finally we convert to screen space by dividing both values by 8, we now have an X and Y position for the top left quarter of Docs collision rectangle. Because we need to know everything around the whole of Doc not just inside, we need to move the square back by 1 step in the X and Y. This X and Y is converted to a screen address using a lookup table of row positions ($0000 and $0400), adding the high byte for the current active screen buffer address and then stored in the zero page so that we can use indirect zero page indexed-Y to quickly read all of the spaces around and on Doc:

TABLES: {
    RowPositionsLSB:
        .byte <($28 * 25)
    RowPositionsMSB:
        .byte >($28 * 25)
}
CLC
LDA TABLES.RowPositionsLSB, Y
ADC CalculatedX
STA ZEROPAGE.CollisionVector
LDA TABLES.RowPositionsMSB, Y
ADC ZEROPAGE.ActiveBuffer
STA ZEROPAGE.CollisionVector + 1
LDY #121
LDA (ZEROPAGE.CollisionVector), y
TAX
LDA TABLES.CollisionFlags, x

Using something similar to this code above lets us determine any character around Doc by changing the offset in the LDY instruction. The floor is three rows below the origin point we calculated and 1 char across so we use an offset of 121 (40 x 3 + 1) retrieve the value and that tells us what is directly under the left hand side of Doc. This value can be used to look up a collision flag value for that char from a lookup table


Determining Docs Surroundings

The current collision flags for Doc are as follows:

Bit#     Description
0Solid - Walls, floors, ceilings and anything else that can be bumped into
1Death - If Doc touches these chars he will die
2Climb - Used for ladders and anything Doc needs to climb
3Active1 - Used to signify an active element, for example a power console
4Active2 - Second bit for active elements allowing 4 different combinations of active flags
5Collectable - Used for keys, tech pieces and doors


By reading the values around Doc and using and/or operations we can determine  lots of things:

  • If offsets 121 and 122 have no solid flag, then Doc is not stood on the ground and should fall.
  • However if offsets 121 or 122 have a climb flag, then we are on a ladder and should not fall
  • If offsets 40 or 80 have a solid flag, then Doc cannot move left as he is blocked
  • If offsets 43 or 83 have a solid flag, then Doc cannot move right as he is blocked
  • If offsets 1 or 2 have solid flag and Doc is jumping, then he has hit a ceiling and must start falling
  • If offsets 81 or 82 have a climb flag, then Doc is stood over a ladder and begin a climb animation
  • If offsets 81 or 82 have a collectable flag, then we can pickup an item (this just involves replacing the map tile at that location and redrawing)
  • If offsets 43 or 83 have both solid and collectable flag then Doc is directly left of a door and can open it if he has a key

And so on...

Using this system makes it a very simple task to create pickups using map tiles, in fact the only tricky thing is making sure that when the collectable tile is picked up that it is cleared from the map data and redrawn correctly over both buffers. It also allows combinations of collision types which currently I use to handle doors. Because I want doors to stop bullets and Doc from passing when closed I just create a map tile of the door with the solid flag enabled, then by attaching a collectable flag to it as well I create a unique combination that when detected lets me know Doc is next to a door so I can check if he has the key and can open the door by "collecting" it.

So thats it for this log, it was still relatively short and a bit more technical than usual but I'm going to try and include a little more technical detail in upcoming logs as well as general game progress information.

Leave a comment

Log in with itch.io to leave a comment.