How to tell if you’ve stepped in something

12/15-2017 to 12/16/2017

This next bit took me two days to write because I struggled a bit to write it as I was trying to code. It is, however, an important subject.

Let’s say you get up in the middle of the night and are wandering about in a room. Let’s also say that you have a young child that loves to play with legos and left some on the same floor you are walking on. Let’s also say that you have bare feet. Other than the intense pain when you step on a lego, do you tell that you have stepped on one.

Now let’s think about that computationally. Kinda hard.

This next part will probably take me a while to write because frankly, I’m not exactly sure what I’m doing with this part. I do have a few ideas, for either good or bad. Collision detection. This part will be collision detection to a static object. The next type of collision detection will be harder (at least in my head), collision detection on a moving object.

We’ll keep moving objects out of our head for right now though.

Back to a static object, our non-moving object.

Our game will have levels, a number of them and on each level, we will have a number of a static object on the screen for each level. The object can do a number of things like purely block you, or perhaps the objects can kill you, perhaps serve as an exit, how about a power-up. There are a number of different possibilities and ways to do this, at least that’s what google said.

The first way I am going to explore is by drawing a box around each object that I want to be an obstacle and will be the focus of this article.

One of the first things I want to place is a global variable for the level.

char level;//which game level are we on

In the section right after the main menu, we want to initialize the variable. This way we start off at the beginning every time we select controls.

level = 0;

Now let’s create a function

void obsticleCollision (char playerX, char playerY, char level)
{
	//these are going to be hard numbers
	//in the shapes of the boxes that surround
	//the obsticle.
	if (level == 0)
	{
		//array is edges of rectangles
	}
}

As you can probably understand in my comments, this is going to be laborious. Also, this is going to only really apply to box shapes (or rectangles).

First thing first, we are going to have to know a target we can draw a box around. Let’s open up tiled and work on our first screen.

In this example so far, I am just reusing the same graphics found in the Dogmole example. The bombs around the edges represent our border. The tombstone represents the first obstacle, which will block us from moving into. The final object which looks like a moon will be our end goal object.

Now the action each of these objects are different, but I’m not going to concentrate on that just yet, I just want to define them.

With the map saved, let’s compile and run the game.

There we go. Our utility, the printtester is showing us our x and y position.

So the coordinates of our box that we are going to work with to show our collision is

43,43
69,43
43,69
69,69

and the logic will be, if the main character’s position falls between 43,43 and 69,43 and 43,69 and 69,69, then an action will happen.

Let’s see if we can put that in code.

First, we’re going to start off with one set of constraints

void obsticleCollision (unsigned char playerX, unsigned char playerY, unsigned char level)
{
	if (level == 0)
	{
		if (playerX == 43)
		{
			zx_border(6);
		}
		printtester2(playerX,playerY);
	}	
}

Now we test and sure enough, if the x position is equal to 43, then our border flashes.

Now, let’s try another set of constraints

if (playerX == 43 && playerY == 43)
{
	zx_border(6);
}

Notice that I placed in a && symbol, this is’ of course’ the logical operator and.
What this really means that only if the 2 constants are met, then the logical outcome would be a flashing border. Now let’s try the next set.

if (playerX == 43 && playerY == 43 || playerX == 69 && playerY == 43)
{
	zx_border(6);
}

OK, something went wrong here. Sure, if you are on 43,43 if flashes, it also does it for 69,43. However, if you are on 44,43, the border does not flash. So, our logic fails in that department, let’s see if we can solve this one.

How about we try

if (playerX > 43 && playerX  43 && playerY < 69)
{
	zx_border(6);
}

That works. The border flashes whenever you are higher than 43 and less than 69 in the X and Y position.

So our entire block of code would look like this

void obsticleCollision (unsigned char playerX, unsigned char playerY, unsigned char level)
{
    //these are going to be hard numbers
    //in the shapes of the boxes that surround
    //the obstacle.
    if (level == 0)
    {		
        if (playerX > 43 && playerX  43 && playerY < 69)
        {
            zx_border(6);
        }
        printtester2(playerX,playerY);
    }
}

However..

Hard coding each of these in each block position is going to be prone to error and make for quite the lengthy block of code for just one level.

Are there other ways? you betcha!

Another way this can be expressed is with using arrays and this is the method I’m going to explore in more detail. Yes, it will take more memory in the short run, but the memory usage will stay rather constant (with exception of defining arrays with each level).

You first would declare your array and a variable for iterating the array as well as your 4 corners.

Why are we using 150 slots? The tiles are 16×16 and the playing screen is 15 tiles wide and 10 tiles high. 15X10 is our 150 slots.

short x1[150];
short x2[150];
short y1[150];
short y2[150];
short xx1;
short xx2;
short yy1;
short yy2;
short iterator;

In your main loop, you will need to initialize the array to 0.

//clear array on startup
for (iterator = 0; iterator < 150; iterator++)
{
    x1[iterator] = 0;
    x2[iterator] = 0;
    y1[iterator] = 0;
    y2[iterator] = 0;
}
//initialize variables

Finally, our method changes around a bit.

void obsticleCollision (unsigned char playerX, unsigned char playerY, unsigned char level)
{
    //these are going to be hard numbers
    //in the shapes of the boxes that surround
    //the obstacle.	
    if (level == 0)
    {
        //array is edges of rectangles	
        x1[0] = 43;
        x2[0] = 69;
        y1[0] = 43;
        y2[0] = 69;
    }
    for (iterator = 0; iterator  xx1 && playerX  yy1 && playerY  xx1)
        if (playerX  yy1)
            if (playerY < yy2)
            {
                zx_border(6);
            }
            xx1 = xx2 = yy1 = yy2 = 255;
        }
    printtester2(playerX,playerY);	
}

Note, I am stacking up nested if, but we don’t have to do that. We can also express it like:

if (playerX > xx1 && playerX  yy1 && playerY < yy2)
{
    zx_border(6);
}

Looks familiar…

So let’s try to add another tile location. To see if the array and checks are holding.

void obsticleCollision (unsigned char playerX, unsigned char playerY, unsigned char level)
{
    //these are going to be hard numbers
    //in the shapes of the boxes that surround
    //the obstacle.	
    if (level == 0)
    {
        //array is edges of rectangles	
        x1[0] = 43;
        x2[0] = 69;
        y1[0] = 43;
        y2[0] = 69;

        x1[1] = 105;
        x2[1] = 135;
        y1[1] = 56;
        y2[1] = 87;
    }
        for (iterator = 0; iterator  xx1 && playerX  yy1 && playerY < yy2)
        {
            zx_border(6);
        }
    }	
    printtester2(playerX,playerY);	
}

And the answer is yes, it holds true.

Now, of course, the problems with this entire setup are, it runs much slower than the first technique as it is iterating through an array 150 times, reading 4 arrays and is holding up a bit of memory.

So, let’s try another idea.

We know that each tile is 16×16, a nice square and as such are equal on all sides. If we are to work with square tiles, then x1 should be the first coordinate in the X plane and x1 + 16 would be the second parameter. The same holds for y.

Let’s change our routine around a bit.

We will drop off 2 arrays, so the code looks like:

short x1[150];
short y1[150];
short xx1;
short xx2;
short yy1;
short yy2;
short iterator;

Change our array initialize

//clear array on startup
    for (iterator = 0; iterator < 150; iterator++)
    {
        x1[iterator] = 0;
        y1[iterator] = 0;
    }
//initialize variables

Finally, our collision detection, note that xx2 and yy2 are both +32 instead of +16. Don’t know why that is happening, but none the less it works that way.

for (iterator = 0; iterator  xx1 && playerX  yy1 && playerY < yy2)
	{
		zx_border(6);
	}
}

It can also be expressed like

for (iterator = 0; iterator  xx1 && playerX  yy1 && playerY < yy2)
	{
		zx_border(6);
	}
}

So then our function in completeness looks like

void obsticleCollision (unsigned char playerX, unsigned char playerY, unsigned char level)
{	
	//these are going to be hard numbers
	//in the shapes of the boxes that surround
	//the obstacle.	
	if (level == 0)
	{
		//array is edges of rectangles	
		x1[0] = 43;
		y1[0] = 43;
		
		x1[1] = 105;
		y1[1] = 56;
	}	
	for (iterator = 0; iterator  xx1 && playerX  yy1 && playerY < yy2)
		{
			zx_border(6);
		}
	}	
	printtester2(playerX,playerY);	
}

Yet there is another technique bouncing in my head that might make things work a little easier, using a math formula to determine the position, not based on a specific pixel range 0 to 240 and 0 to 160 based, but using character positions 0 to 32 and 0 to 21.

The function would look like this

void obsticleCollision (unsigned char playerX, unsigned char playerY, unsigned char level)
{	
	//these are going to be hard numbers
	//in the shapes of the boxes that surround
	//the obstacle.	
	if (level == 0)
	{
		//array is edges of rectangles
		//x=4,y=4
		//starting at 1
		//so 4*16=64
		//based on character position 16x16
		x1[0] = 4;
		y1[0] = 4;
		
		x1[1] = 8;
		y1[1] = 5;
	}	
	for (iterator = 0; iterator  xx1 && playerX  yy1 && playerY < yy2)
		{
			zx_border(6);
		}
	}	
	printtester2(playerX,playerY);	
}

Nifty little formula. For example, the 4th block over would be multiplied by 16 a subtract 24 to get to the block to detect the object.

The same can be expressed by

xx1 = (x1[iterator]<<4) - 24; 
xx2 = (x1[iterator]<<4) + 8;
yy1 = (y1[iterator]<<4) - 24;
yy2 = (y1[iterator]<<4) + 8;

Next, let’s look at our iterators. Our array is 150 slots, if there are not 150 obstacles, can we do something about it, like terminating the loop early. Yes we can. It will speed up the screen if there are less than 150 objects.

Let’s give our loop a break, take it easy for a microsecond.

First, let’s introduce a high number in slot 25 of the array.

x1[25] = 99;

Then in our loop, we look for that number assignment and break out of the loop

if (x1[iterator] == 99)
	break;

Goodness, it runs much faster!

So now our complete method looks like.

void obsticleCollision (unsigned char playerX, unsigned char playerY, unsigned char level)
{	
	//these are going to be hard numbers
	//in the shapes of the boxes that surround
	//the obstacle.	
	if (level == 0)
	{
		//array is edges of rectangles
		//x=4,y=4
		//starting at 1
		//so 4*16=64
		//based on character position 16x16
		x1[0] = 4;
		y1[0] = 4;
		
		x1[1] = 8;
		y1[1] = 5;
		
		x1[25] = 99;
	}	
	for (iterator = 0; iterator < 150; iterator++)
	{

		xx1 = (x1[iterator]<<4) -24;// xx1 = (x1[iterator]*16) - 24;
		xx2 = (x1[iterator]<<4) + 8;// xx2 = (x1[iterator]*16) + 8;
		yy1 = (y1[iterator]<<4) -24;// yy1 = (y1[iterator]*16) - 24;
		yy2 = (y1[iterator]< xx1 && playerX  yy1 && playerY < yy2)
		{
			zx_border(6);
		}
		
		if (x1[iterator] == 99)
			break;
	}	
	printtester2(playerX,playerY);	
}

We really only need to check x1 for the 99 code before we break out of the loop.

Again, let’s look at our Array, it’s 150 slots, do we really need that many slot. 150 objects and the entire screen fills up. I think that at any time during this game, the most we would want to cover is about 1/3 of the entire screen. That should be more than enough objects and will save on some memory.

Also, since our screen there are only 3 objects in total, let’s make the break out of loop, slot 5 in the array.

Then our complete function (using this technique is)

void obsticleCollision (unsigned char playerX, unsigned char playerY, unsigned char level)
{	
	//these are going to be hard numbers
	//in the shapes of the boxes that surround
	//the obstacle.	
	if (level == 0)
	{
		//array is edges of rectangles
		//x=4,y=4
		//starting at 1
		//so 4*16=64
		//based on character position 16x16
		x1[0] = 4;
		y1[0] = 4;
		
		x1[1] = 8;
		y1[1] = 5;
		
		x1[2] = 13;
		y1[2] = 8;
		
		x1[5] = 99;
	}	
	for (iterator = 0; iterator < 50; iterator++)
	{

		xx1 = (x1[iterator]<<4) -24;// xx1 = (x1[iterator]*16) - 24;
		xx2 = (x1[iterator]<<4) + 8;// xx2 = (x1[iterator]*16) + 8;
		yy1 = (y1[iterator]<<4) -24;// yy1 = (y1[iterator]*16) - 24;
		yy2 = (y1[iterator]< xx1 && playerX  yy1 && playerY < yy2)
		{
			zx_border(6);
		}
		
		if (x1[iterator] == 99)
			break;
	}	
	printtester2(playerX,playerY);	
}

Is this how other people perform collision detection? Yes, some do, I’m sure there are much more optimized routines. In later articles, I’m going to try to post other solutions. This technique though seems to run fairly fast and is fairly easy to understand.

I know this was a long article, but kinda important to a game, thanks for bearing with me.

Please feel free to share your particular technique along with details and I can see if I can somehow muster coding it in C to share with everyone.

Sharing is Caring.

Advertisements

Author: andydansby

I'm a hobbyist coder working with the ZX Spectrum. Living in New York state near the Syracuse area. I grew up in Virgina. The first computer my parents bought for me was a Timex Sinclair 2068.

One thought on “How to tell if you’ve stepped in something”

  1. Hi Andrew

    For collision detection with tiles you have the tiles array. It’s updated every time the screen changed.

    tiles[y*scrw+x]

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s