Gw Temp

Menu

Tutorial - 'Hit Detection Using Pictures and Events' by laughy

An item about RPGMaker 2000 posted on

Blurb

Laughy gives a VERY thorough explanation of how to detect picture-picture and picture-event hitting. A superb read!

Body

DEMO FOR THIS TUTORIAL:
www.laughy.net/Ark2k.zip

(download and play ^_^)

WHY?

This is probably the biggest need in rm2k mini-game creation, since it is possible to make such a large amount of games with this knowledge. Having programmed an NES emulator, I know for a fact that the NES picture unit works a lot like this, and in theory all NES games could “function” on rm2k.

It is also possible to make an NES emulator in rm2k. Your imagination is your limit here.

In order to do these things such as creating an NES game in rm2k, you need to realize that the NES works by having sprites on the screen (these sprites are 8x8 pixels in size) and having tiles as well. Sound familiar? Rm2k works in the same way. Sprites, or pictures, and tiles, or the background.

So the question is, what is holding everyone back? It may be because there is no firm understanding on how picture/event hit detection should work. In Mario Brothers, when Mario jumps up and hits his head on a block, the NES is doing nothing different than what you can program in rm2k to detect this. Meaning, it is basically possible to refine hit detection between “tiles” in the background and pictures enough to make whatever you feel you need to make. On the other hand, speed may become a factor, since programming NES hit detection code is much faster than programming it in rm2k.

I should note Rm2k is rather fast. The programmer did a very, very good job.

IMAGE/IMAGE HIT DETECTION

Before I dive into LPDS and complicated event/picture detection, allow me to explain simple hit detection between pictures. This is for those who will not use such measures like event/image detection in their games, and just want a quick detection algorithm but can’t find one.

To detect if two pictures have “touched” each other, you need 8 variables:

P1X = the x coordinate of picture 1
P2X = the x coordinate of picture 2
P1Y = the y coordinate of picture 1
P2Y = the y coordinate of picture 2
P1W = the width of picture 1 divided by 2
P2W = the width of picture 2 divided by 2
P1H = the height of picture 1 divided by 2
P2H = the height of picture 2 divided by 2

IF

(P1W + P2W) - ABS(P1X – P2X) is greater or equal to 0

AND

(P1H + P2H) – ABS(P1Y – P2Y) is greater or equal to 0

than the images have “hit”

What is ABS? ABS means absolute value. That means take the value in the parenthesis and make sure it’s positive.

For instance ABS(-5) = 5
ABS(5) = 5

So in rm2k, you would calculate P1X – P2X, and if the value is negative, then you multiply by -1. Same for P1Y – P2Y.

Here it is in pseudo RM2k code, with things in {} as comments

{P1W + P2W}
SET VARIABLE “RANGEX” to “P1W”
CHANGE VARIABLE “RANGEX” + “P2W”

{P1X – P2X}
SET VARIABLE “ABSX” to “P1X”
CHANGE VARIABLE “ABSX” – “P2X”


{take absolute value here}
IF “ABSX” < 0 THEN
[
CHANGE VARIABLE “ABSX” * -1
]


{subtract the two parts}
CHANGE VARIABLE “RANGEX” – “ABSX”


{If it is greater or equal to zero, then continue}
IF “RANGEX” >= 0 THEN
[
{P1H + P2H}
SET VARIABLE “RANGEY” to “P1H”
CHANGE VARIABLE “RANGEY” + “P2H”

{P1Y - P2Y}
SET VARIABLE “ABSY” to “P1Y”
CHANGE VARIABLE “ABSY” – “P2Y”

{take absolute value here}
IF “ABSY” < 0 THEN
[
CHANGE VARIABLE “ABSX” * -1
]

{subtract the two parts}
CHANGE VARIABLE “RANGEY” – “ABSY”


{If it is greater or equal to zero, then we have hit}
IF “RANGEY” >= 0 THEN
[
SET SWITCH “PICTURES HIT” ON
]

]
ELSE
[
SET SWITCH “PICTURES HIT” OFF
]

This is done in my Tetris pong game if you want to see it in action, in the event at the top right of the screen on the pong page (you’ll know it when you see it).

EVENT/IMAGE HIT DETECTION

Let’s start by a bit of background. A long time ago I made a thing which I dubbed LPDS, or Laughy Picture Detection System (cheesy I know but I’m not good with names). LPDS used a formula wheras if you have a picture on screen then by using the x and y coordinates of that picture, one can decide which event it is over.

However this formula required a large amount of multiplications and subtractions and I felt the need to create something better. In this article I will discuss the creation/implementation/use of LPDS 2+, which I feel is adequate enough for most games out there.

Most likely there is a better way to do this, however I have not delved that deep into it, and it seems no one else has even looked at it. Hopefully there will be more algorithms and way of detecting hits between pictures and events in the future.

THEORY

First let’s take a look at LPDS in action. If you have a pseudo-fast (550 mhz +) computer, you can try my arkanoid demo:

www.laughy.net/Ark2k.zip

Take a look at that and examine how the ball, which is a picture, has rather refined hit detection capabilities.

The way LPDS works is rather simple to say but difficult to explain. When you have a “picture” in rm2k you can define certain “hit points” on the picture. For a ball, the hit points should probably be the 4 corners. It is the location of these hit points that will be used to detect if the ball is “hitting” an event. If one of the hit points of the ball is over an event, then we can safely say the “ball” is hitting it. Thus, we need 4 x and y locations to hold the 4 hit points. The more hit points you have, the more likely you will be accurate in your approximation. However 4 hit points requires a 500mhz or higher computer, and 8 probably requires a ghz or more. More optimization in the future by someone could reduce this, or another way of detection all together.


The yellow shows where the hit points would be located…how do we know if the edge of an image (the hit points) are over/touching an event?


If you edit the Arkanoid demo in Rm2k, you will notice that the entire playing field is covered in events, and that the events count up in order as you go from left to right, and up to down. Thus one starts with event 1 in the top left in the playing field, and then event 2 to the right of it, and so on.

If your “playing field” is an 11 x 6 background square, then event number 12 will be directly below event 1:


An 11 x 6 playing field. Event 1 starts at the top left and it increments from left to right. This MUST be done this way, however the size of the playing field does not matter, and events NOT NEED exist. For example, event 1 could be gone, but event2 still has to be the second square in the playing field. When the ball goes over square1, nothing will happen since an event does not exist there.


Why is this done? This allows one to “call” a certain event based on the location of the hit points, using an algorithm that was described before. If you lay the events as told, then the event to call based on the x and y of a hit point is the following:

EVENT TO CALL =
(X – X MOD 16) / 16 + (WIDTH OF GRID) * (Y – Y MOD 16) / 16 + 1

WHERE

X is the x coordinate of your “point” - the amount of events the “playing field” is from the left edge of the screen * 16

Y is the y coordinate of your “point” - the amount of events the “playing field” is from the top edge of the screen * 16

“WIDTH OF GRID” is the number of events that make up the width of our playing field. In my Arkanoid demo, the playing field is an 11 x 6 grid of events, so the width is 11.

Let me explain the X and the Y thing. The point that is used to calculate which event you are over needs to be the X and Y distance from where your “playing field” starts, NOT where the screen starts. Let this image explain:


As far as Rm2k is concerned, the ball is at 80, 56. However you cannot put 80 = X and 56 = Y in the above function, since the function needs the x and y value of where you are OVER THE PLAYING FIELD. Since the playing field starts two events from the left, you would subtract 32 (16 pixels in width per event) from the x of the ball, giving you 48. Since the playing field is 1 event from the top, you would subtract (16 pixels in height per event), giving you 40. Thus the X and Y that would be put into the function would be 48 and 40…which are the two values corresponding to how far the ball is from the edges of the playing field.



The MOD of a number is how much is left over after you divide it by the number you mod by. For example, 13 mod 2 means divide 2 into 13, and give me the remainder. In this case, 13/2 = 6 with a remainder of 1. The remainder is the number we want, so 13 mod 2 = 1. If we did 28 mod 3, then 28 / 3= 9 with a remainder of 1, so the result is 1. If we did 32 mod 16, then 16 goes evenly into 32 and the remainder is 0. Thus 32 mod 16 = 0.

Luckily rm2k supports all of these operations. Here it is in pseudo rm2k code. It assumes that “PLAYING_FIELD_LEFT_DISTANCE” and “PLAYING_FIELD_TOP_DISTANCE” contain the distance, in “tiles,” the playing field is from the top and left of the screen (see image 3 above).

CHANGE VARIABLE “PLAYING_FIELD_LEFT_DISTANCE” * 16
CHANGE VARIABLE “PLAYING_FIELD_TOP_DISTANCE” * 16
SET VARIABLE “X” to “HITPOINTX”
CHANGE VARIABLE “X” – “PLAYING_FIELD_LEFT_DISTANCE”
SET VARIABLE “XMOD16” to “X”
CHANGE VARIABLE “XMOD16” MOD 16
CHANGE VARIABLE “X” – “XMOD16”
CHANGE VARIABLE “X” / 16
SET VARIABLE “Y” to “HITPOINTY”
CHANGE VARIABLE “Y” – “PLAYING_FIELD_TOP_DISTANCE”
SET VARIABLE “YMOD16” to “Y”
CHANGE VARIABLE “YMOD16” MOD 16
CHANGE VARIABLE “Y”- “YMOD16”
CHANGE VARIABLE “Y” / 16
CHANGE VARIABLE “Y” * WIDTH_OF_GRID
SET VARIABLE “EVENT TO CALL” to “X”
CHANGE VARIABLE “EVENT TO CALL” + Y
CHANGE VARIABLE “EVENT TO CALL” + 1
CALL EVENT “EVENT TO CALL”

One mods by 16 and subtracts it from it’s corresponding variable because that’s the same thing as rounding down. Let me show you why:

a number like 55 mod 16 is:

55 / 16 = 4 with a remainder of 7. Thus 55 mod 16 = 7.

Now subtract this 7 from 55 and we get

48.

48 is evenly divisible by 16 and gives us 4!

As you can see, any number – the number mod (mod number), and then that divided by (mod number), will give us not the remainder, but the whole number of the number divided by (mod number). This is the same thing as dividing and rounding down.

So if we did 88 / 16, we know that this is 5 with a remainder of 8. I only want the 5, so to get the 5 (round down) I have to subtract off the remainder. How do I get the remainder? Mod 88 it by 16!

Rm2k rounds to the nearest number, but in order for this to work correctly one needs to round down ALL the time. If you think about it, you’ll understand: If a hit point on a picture is at 14, 13, then if we just divided each by 16 (that is, the width and height of an event).


The two left yellow hit points are over event1…the two right are over event two. If the point 1 (the top left) was at 14, 5, then 14 / 16 = .875, and 5 / 16 = .3125. Rm2k would round that to 1 and 0. Adding our extra +1 yields event number 2 for this coordinate, which is false. That’s why 14 / 16 needs to be 0, not 1; we have to round down. Hence, the mod. If the top right hit point was 19, 5, then 19/16 = 1.1875 and 5 / 16 = .3125. Rm2k rounds this down to 1 and 0, and with the extra +1 this means event number 2, which IS correct. The calculation will only be incorrect if either the x or y coordinate is equal to or above 8, since rm2k rounds to the NEAREST value, not down. When it is above 8, it rounds up, giving us one event too many.


Then rm2k would spit out 1 and 1, meaning our event is at 1 + 1 * the width of our grid + 1 (which will be at least 2). This is incorrect, since 14,13 SHOULD put our hit point on event number one. 14 – 14 mod 16 is 0, and 13 – 13 mod 16 is also 0, thus we would get 0 + 0 * width of our grid + 1, which is one and the correct event number to call. We add 1 at the end since our events start with the number 1, not the number 0.

USE

By “calling” events, we can simply put code inside the event and it will automatically run when the picture goes over it. Let’s take, for example, the Mario Brothers question mark box. When you hit it, then it turns into a different block and the creates a mushroom out of the top of it. It wouldn’t be hard to put code in that does this. It can set a switch, say “EVENTHIT”, so that if the event ever was called again it wouldn’t execute its code, but rather do something else (in the case of Super Mario Brothers, make a “THUNK!” sound)

Also, what is great about this is that it’s really easy to put another question mark somewhere. If you wanted to make event #39 on the field a question mark as well, then delete the current event #39 and copy your question mark event to where it was. Boom! It’s ready to go, provided you change the “EVENTHIT” variable to something else, so that it doesn’t share the same variable as with its clone. No other coding needs to be done. No forks, nothing. The event #39 will be called when a hit point goes over it, and will spit out a mushroom over its head, provided you set the mushroom image x and y variables to be in relation to “THISEVENT.”

BUT….

If you made a ball move, and every time it moved you ran all of its hit points through that algorithm and called all the events that needed to be called, you would have a problem…that is, it would be rather slow. You COULD program it so that it only calls an event once, and it does not call it again unless it actually leaves the event first, however this still would be slow, since every time it moves it has to go through that algorithm to see which event it SHOULD be calling.

WHAT WE DO LAUGHY WHAT WE DO?

Thus I invented LPDS2+. I wanted to make a game like Arkanoid above, so I knew something faster was needed. LPDS2+ doesn’t calculate which event you are on, it uses where you were before and then calculates the above ONCE, then depending on where the hitpoints are, it calculates which events should be called. This means adding more hit points won’t lag it as much at all, and full hit detection is possible.

LPDS2+ basically does the same thing above, except with the X and Y coordinates of the ball, instead of the hit points.

First calculate the X and Y of the ball (not the hitpoints of it) and run it through the algorithm described above. What you will get is which event the location of the center of the ball is. Now we need to determine where its hit points are located.

To do so, get the distance the ball x and y is from the EDGE OF THE EVENT IT IS ON…that is, the MOD value:

If the ball is at 50, 40 (from the edge of the playing field), 50 mod 16 = 2, and 40 mod 16 = 8. That means the center coordinates of the ball are 2 pixels to the left of the event it is on, and 8 pixels from the top.

If the ball is at 70, 30 (from the edge of the playing field), 70 mod 16 = 6, and 30 mod 16 = 14. That means the center coordinates of the ball are 6 pixels to the left of the event it is on, and 14 pixels from the top.


The ball is 6 pixels from the left of the event, and 14 pixels from the top. We can use that to determine which events the hit points are over.


Now for each hit point, simply add it’s distance from the center of the ball. If your ball was 9 by 9 pixels, and the hit points are on the edges of the ball, then:

hitpoint1 would be -4X and -4Y from the center of the ball
hitpoint2 would be -4X and +4Y from the center of the ball
hitpoint3 would be +4X and -4Y from the center of the ball
hitpoint4 would be +4X and +4Y from the center of the ball


Hitpoint3 is 4 pixels X more and 4 pixels Y less than the center of the ball, since the ball is 9 x 9 pixels. Depending on the size of your image, the hitpoints would be at different locations from the “center” of the ball. It doesn’t have to be the center…just the point that you ran through the algorithm above (the center is usually the best choice).


Now, add these distances to your “center” x and y values mod 16. Let’s take our 70,30 value. After running through the algorithm, we take the mod again of 70 giving us 6, and mod of 30 gives us 14.

For hitpoint1:
6 – 4 = 2
14 – 4 = 10

You see we “added” the distance the hitpoint is from the center of the ball. Since hitpoint1 is -4x and -4y from the center, we got a lower result. What this says is that hitpoint 1 is 2 pixels from the left of our currentevent the center of the ball is on, and 10 pixels from the top.

For hitpoint2:
6 – 4 = 2
14 + 4 = 18

This is where we stop for a second. This tells us that hitpoint2 is 2 pixels from the left of our current event, and 18 pixels from the top. 18 pixels from the top??? That means it’s in the EVENT BELOW THIS ONE!

In other words, we can see if hitpoints are in different events other than this one rather quickly, depending on where the center is located. If you don’t understand yet, just keep reading:

For hitpoint3:
6 + 4 = 10
14 - 4 = 10

This hit point is 10 pixels from the left and 10 pixels from the top of our current event, thus it is in the same event as our current one.

For hitpoint4:
6 + 4 = 10
14 + 4 = 18

This hitpoint is 10 pixels from the left of our event, but 18 pixels from the top, meaning that it is in the event below this one.

Here is an image to clear things up:


Two hit points of our ball are located in the event the “center” of the ball is located, but two are located on the event below that. We know this because the ball’s center coordinates are 6 and 14 from the edge of the event, and when you add how far the hit point is from the center it spills over 16 and into the next event. We have to call both events to be accurate, since the ball is really touching both of them.


If we subtract and it is less than 0, than our hitpoint is either to the left of the event the center coordinate of the ball is on (subtract x and it is less than 0) , or above it (subtract y and it is less than 0).

If the Y value flows over, then we call the the event number the middle of our ball is on + the width of the grid. On a 11 x 6 playing field, If the middle of our ball was over event 1, and one of our hit points went over in the Y, we would call event 1 AND event number 12. If one of our hit points went over 16 in the x coordinate, then we would call event 1 AND event 2. It’s possible to call every event at once, depending on how big your picture is and where your hit points are.

Here is LPDS2 in RM2k code. If you have questions ask!

Normal lpds algorithm, using the ball center values instead.

CHANGE VARIABLE “PLAYING_FIELD_LEFT_DISTANCE” * 16
CHANGE VARIABLE “PLAYING_FIELD_TOP_DISTANCE” * 16
SET VARIABLE “X” to “BALLX”
CHANGE VARIABLE “X” – “PLAYING_FIELD_LEFT_DISTANCE”
SET VARIABLE “XMOD16” to “X”
CHANGE VARIABLE “XMOD16” MOD 16
CHANGE VARIABLE “X” – “XMOD16”
CHANGE VARIABLE “X” / 16
SET VARIABLE “Y” to “BALLY”
CHANGE VARIABLE “Y” – “PLAYING_FIELD_TOP_DISTANCE”
SET VARIABLE “YMOD16” to “Y”
CHANGE VARIABLE “YMOD16” MOD 16
CHANGE VARIABLE “Y”- “YMOD16”
CHANGE VARIABLE “Y” / 16
CHANGE VARIABLE “Y” * WIDTH_OF_GRID
SET VARIABLE “EVENT TO CALL” to “X”
CHANGE VARIABLE “EVENT TO CALL” + Y
CHANGE VARIABLE “EVENT TO CALL” + 1

Here we begin to detect hit points. Note that Y mod 16 still exists as the YMOD16 variable and x mod 16 still exists as the XMOD16 variable. The variable “HP_DISTANCE_FROM_CENTER” would be the 4 for our 9 x 9 ball. It is how far the hitpoint is from the center of the ball, in pixels.

SET VARIABLE “HIT_POINT_DISTANCE_FROM_CENTER” to 4

CHANGE VARIABLE “HITPOINT1X” to “XMOD16”
CHANGE VARIABLE “HITPOINT1X” – “HP_DISTANCE_FROM_CENTER”
CHANGE VARIABLE “HITPOINT1Y” to “YMOD16”
CHANGE VARIABLE “HITPOINT1Y” – “HP_DISTANCE_FROM_CENTER”

See if it is on a different event or the same one

SET VARIABLE “HITPOINT1CALL” to “EVENT TO CALL”
IF “HITPOINT1X” < 0 then
CHANGE VARIABLE “HITPOINT1CALL” - 1
IF “HITPOINT1X” >=16 then
CHANGE VARIABLE “HITPOINT1CALL” + 1
IF “HITPOINT1Y” >=16 then
CHANGE VARIABLE “HITPOINT1CALL” + GRIDWIDTH
IF “HITPOINT1Y” < 0 then
CHANGE VARIABLE “HITPOINT1CALL” – GRIDWIDTH

If “HITPOINT1CALL” DOES ONT EQUAL “EVENT TO CALL” then
CALL EVENT “HITPOINT1CALL”

Detect hit point 2

CHANGE VARIABLE “HITPOINT2X” to “XMOD16”
CHANGE VARIABLE “HITPOINT2X” – “HP_DISTANCE_FROM_CENTER”
CHANGE VARIABLE “HITPOINT2Y” to “YMOD16”
CHANGE VARIABLE “HITPOINT2Y” + “HP_DISTANCE_FROM_CENTER”

SET VARIABLE “HITPOINT2CALL” to “EVENT TO CALL”
IF “HITPOINT2X” < 0 then
CHANGE VARIABLE “HITPOINT2CALL” - 1
IF “HITPOINT2X” >=16 then
CHANGE VARIABLE “HITPOINT2CALL” + 1
IF “HITPOINT2Y” >=16 then
CHANGE VARIABLE “HITPOINT2CALL” + GRIDWIDTH
IF “HITPOINT2Y” < 0 then
CHANGE VARIABLE “HITPOINT2CALL” – GRIDWIDTH

If “HITPOINT2CALL” DOES ONT EQUAL “EVENT TO CALL” then
CALL EVENT “HITPOINT2CALL”

Detect hit point 3

CHANGE VARIABLE “HITPOINT3X” to “XMOD16”
CHANGE VARIABLE “HITPOINT3X” + “HP_DISTANCE_FROM_CENTER”
CHANGE VARIABLE “HITPOINT3Y” to “YMOD16”
CHANGE VARIABLE “HITPOINT3Y” - “HP_DISTANCE_FROM_CENTER”

SET VARIABLE “HITPOINT3CALL” to “EVENT TO CALL”
IF “HITPOINT3X” < 0 then
CHANGE VARIABLE “HITPOINT3CALL” - 1
IF “HITPOINT3X” >=16 then
CHANGE VARIABLE “HITPOINT3CALL” + 1
IF “HITPOINT3Y” >=16 then
CHANGE VARIABLE “HITPOINT3CALL” + GRIDWIDTH
IF “HITPOINT3Y” < 0 then
CHANGE VARIABLE “HITPOINT3CALL” – GRIDWIDTH

If “HITPOINT3CALL” DOES ONT EQUAL “EVENT TO CALL” then
CALL EVENT “HITPOINT3CALL”

Detect hit point 4

CHANGE VARIABLE “HITPOINT4X” to “XMOD16”
CHANGE VARIABLE “HITPOINT4X” + “HP_DISTANCE_FROM_CENTER”
CHANGE VARIABLE “HITPOINT4Y” to “YMOD16”
CHANGE VARIABLE “HITPOINT4Y” + “HP_DISTANCE_FROM_CENTER”


SET VARIABLE “HITPOINT4CALL” to “EVENT TO CALL”
IF “HITPOINT4X” < 0 then
CHANGE VARIABLE “HITPOINT4CALL” - 1
IF “HITPOINT4X” >=16 then
CHANGE VARIABLE “HITPOINT4CALL” + 1
IF “HITPOINT4Y” >=16 then
CHANGE VARIABLE “HITPOINT4CALL” + GRIDWIDTH
IF “HITPOINT4Y” < 0 then
CHANGE VARIABLE “HITPOINT4CALL” – GRIDWIDTH

If “HITPOINT4CALL” DOES ONT EQUAL “EVENT TO CALL” then
CALL EVENT “HITPOINT4CALL”

Make sure if you actually use that that you only call an event once. Keep track of events called and make sure a hitpoint leaves that event before it can call it again.

I should note that on second look I see how this way of doing things could be greatly optimized. In fact one could simply store the event one started at the beginning and record each time the change in events and go off that, further increasing speed. If you are going to make a mini-game with this detection, use this only as a guide in your thinking, and if you have any questions don’t be afraid to e-mail Laugh10704@hotmail.com.

If you open up my Arkanoid demo and edit the event in the bottom left of the screen, page 2, you see the algorithm in action. I had to greatly modify it so that it would detect differences between two different “blocks” (upper and lower) in the Arkanoid game, and take away/detect the correct one. Most likely you won’t need to customize it that much for your mini-game, whatever it happened to be.

LAUGHY Q/A TIME

Q: ME NO GET
A: ME GIVE ANSWER: Email Laugh10704@hotmail.com!

Q: Hello. I represent the Kirby organization. I ask that you please refrain from doing Kirby dances as it violates our copyright. OK?
A:
<(^_^<)
(>^_^)>
<(^_^)>
<( )>
<[o_O<]
[>o_O]>
<[O_O]>

Q: I could do this in my sleep. I knew this from day 1, the minute I came out of my mother’s womb.
A: Cool. Now write tutorials on what you know so I can get more information.

Q: OMG LAUGHY I LOVE YOU AND I’M FEMALE AND SINGLE
A: YOU LIE! LIE LIE LIE! IF YOU LOVED ME YOU WOULDN’T BOSS ME AROUND!

Q: GO TO SLEEP JORDAN!
A: Sorry mom :(.

EOF (earthlings or frakenstein)