• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Mac OSX Collision events giving false positive between ball and brick

Hey everyone,

I'm currently on some of the later stages of a personal project I'm working on - creating a variation of the Brick and Ball "Breakthrough" game. I have 95% of the core mechanics worked out with my sprites and collision masks, but am having some strange behaviour when the ball gets to the corner of these rectangle bricks. I've tried looking at possible solutions with the collision masks, and have debugged as much as I know how to, but the issue seems to be that the collision event between the ball and the brick is being triggered when it shouldn't.

Here's an example image.

Screenshot 2021-10-26 at 7.04.39 PM.png

The ball is moving in the direction of the red arrow, so you would think that the collision events would be triggered between the ball and the dark blue bricks, but for some reason (and only occasionally, if the ball hits the two dark blue brick perfectly in the corner) the collision event between the ball and the SKULL brick is triggered. I am lost as to where to look at why. Any ideas? Would more information help?

Here are a few more screenshots with more info.


Ball sprite

Screenshot 2021-10-26 at 7.17.02 PM.png


SKULL brick sprite
Screenshot 2021-10-26 at 7.17.37 PM.png


Code that is triggered when the ball and skull brick collision is detected. It shouldn't be anything in here, but thought I'd include it.

Screenshot 2021-10-26 at 7.18.11 PM.png


I'm pretty new to GM2. I do software development for work, but don't the large majority of the 'best practices' or useful functions in GM2. I appreciate anyone's suggestions on where to start looking.
 

Attachments

chamaeleon

Member
I am lost as to where to look at why
My understanding is that both instances involved need precise collision masks, otherwise only bounding box collision tests are performed. The skull one may be rectangular but it would probably still need the setting applied. See the Sprite editor section of the manual for the examples under Collision Mask.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
For precise collisions to be properly detected BOTH instances in the collision need to have the collision mask set to precise, otherwise BBox collisions are used. So, in this case, it's more than likely that the bounding boxes are overlapping and causing the collision, especially if your ball sprite uses image_angle to create rotation (as this will change the bbox size and may make it larger). Make the brick shape also use the precise collision mask, and see if that resolves things. :)
 
Thanks a lot for the suggestions Nocturne/Chamaeleon. I changed all of the sprites involved to "Precise", and then "Precise per frame". Neither of them took away the false positive collisions.

One thought I had - In the top picture you can see that the issue arises when the ball is directly next to both the dark blue blocks, so the collision event is being triggered based on the position of the ball in the next frame which hasn't been rendered to the screen yet. So technically, in the image, the ball hasn't collided with anything, but the next frame it will be about 9 pixels along the same line it was traveling, so it will then be colliding with all three of the blocks. Maybe it's just registering the collision event with the skull block first which destroys the ball and makes the collision event with the other 2 dark blue blocks inconsequential.

That idea makes sense, and sounds like something lots of people would encounter. The goal would be to create some kind of collision priority? I feel like I know what would need to happen, but don't know exactly what to implement. Any thoughts?
 

Nidoking

Member
Don't move the thing nine pixels at once if that causes collision problems. Move it one pixel in a loop and check for collisions, nine times.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
One thought I had - In the top picture you can see that the issue arises when the ball is directly next to both the dark blue blocks, so the collision event is being triggered based on the position of the ball in the next frame which hasn't been rendered to the screen yet. So technically, in the image, the ball hasn't collided with anything, but the next frame it will be about 9 pixels along the same line it was traveling, so it will then be colliding with all three of the blocks. Maybe it's just registering the collision event with the skull block first which destroys the ball and makes the collision event with the other 2 dark blue blocks inconsequential.
Don't move the thing nine pixels at once if that causes collision problems. Move it one pixel in a loop and check for collisions, nine times.
Yup, this is correct! Drawing is done AFTER all processing is done... and what @Nidoking says is also correct. You want to check collisions in the STEP event. Now, this can be done in two ways generally:

1) Use a collision_line in front of the instance to check for a collision, and if there is none, then move the instance, and if there is one, then use a FOR loop to move the instance until the collision is triggered.
2) Move the instance first, then check for a collision and if there is one, use a FOR loop to move the instance back along the direction of movement until no collision is detected.

The first method is usually better when you have your own movement system in place and aren't using the built-in variables, and the second version is usually better when you are using the built in speed/direction stuff, but both of them are perfectly valid. See if you can set up such a thing and if you need further help let us know and we'll give you some code snippets to get you started (but it's always better to try yourself first so you learn more! :) ).
 
Yup, this is correct! Drawing is done AFTER all processing is done... and what @Nidoking says is also correct. You want to check collisions in the STEP event. Now, this can be done in two ways generally:

1) Use a collision_line in front of the instance to check for a collision, and if there is none, then move the instance, and if there is one, then use a FOR loop to move the instance until the collision is triggered.
2) Move the instance first, then check for a collision and if there is one, use a FOR loop to move the instance back along the direction of movement until no collision is detected.

The first method is usually better when you have your own movement system in place and aren't using the built-in variables, and the second version is usually better when you are using the built in speed/direction stuff, but both of them are perfectly valid. See if you can set up such a thing and if you need further help let us know and we'll give you some code snippets to get you started (but it's always better to try yourself first so you learn more! :) ).
Awesome, thanks for the pointers. Such a genius way to handle the in-between frames. I'm thinking it may be time for me to implement my own collision detection in the STEP event like you mentioned. Right now I've set up collision events (they're visible in one of the screenshots above) that the GM2 IDE provides on my objBall object and the bricks (eg: objDeathBrick in the picture), so when the ball collides with a brick, that event between the ball and the particular brick object is called. I can't think of how I could work backwards to see which brick is hit first in this scenario since the GM2 framework handles the order of the collision events as singular events between a ball and one brick object, instead of the ball and a group of bricks (which I assume I could do in the STEP event)

Does that sound right? Is there a point at which most people using GM2 will create their own collision detection? This seems like it would be a very common scenario which the GM2 wouldn't work with. I think I'd like to implement my own collision detection and get rid of all the collision events, or have the primary collision detection in the STEP event and then (if this is possible) trigger the object specific collision events myself depending on what kind of brick my ball is hitting. That would help keep code clean by separating the unique behaviour that needs to be performed on collision with different brick types.

Again, thanks for the replies.
 

Nidoking

Member
I find that Game Maker offers a lot of mechanisms to make simple things easy for inexperienced users, but many of them aren't needed. When you discover that something doesn't meet your needs, and you know enough to replace it, it may make sense to do that. I've written my own place_free and place_meeting replacements because I want instances to act solid sometimes and not at other times, and I've moved from collision events to collision checking in step events as well. But I still use alarms, because they usually do what I need them to do and I don't have a particular reason to replace them.

I wouldn't use collision events if you're processing your own collisions. Game Maker will still use them for collisions even if you're calling them manually. You might want to put them into functions instead. One neat thing you can do is make a function with the same name in each object type to describe how it behaves in a particular situation (such as a collision) and then call that function when you detect that situation. Each instance will know how it behaves based on its function, and the colliding instance doesn't even need to know which specific type of thing it collided with. But you can just make them functions in a script somewhere and call them from a switch statement, or use a map to relate object indices to functions.
 
Makes sense. Thanks for being so helpful and open to contributing ideas @Nidoking and @Nocturne. It’s extremely helpful. I’ll put some work into making my own collision detection and post here if I’m not able to figure out my questions from here. I feel like I have a pretty solid idea of where to go from here. Thanks again.
 
Hey, I know those blocks! That's Diamonds! I have the shareware disc right behind me and some old floppy disks with levels I made saved onto them, lol!
Yeah that’s right! My dad and my brothers loved playing the game growing up but it became difficult to keep a version of Mac OS9 on modern hardware so we haven’t played it in probably 15 years. I’m recreating the entire game, as close to 1-to-1 as possible, as a present for my dad. It’s coming along quite nicely. I have levels 1-25 made and functional except for little bugs like I’ve discussed in this thread.
 
Yeah that’s right! My dad and my brothers loved playing the game growing up but it became difficult to keep a version of Mac OS9 on modern hardware so we haven’t played it in probably 15 years. I’m recreating the entire game, as close to 1-to-1 as possible, as a present for my dad. It’s coming along quite nicely. I have levels 1-25 made and functional except for little bugs like I’ve discussed in this thread.
Nice! You should let me test it sometime! =)
 
Nice! You should let me test it sometime! =)
I was low-key hoping you would ask, because the number of people on this earth that have played the game and could give technical feedback is very small. Do you have a Mac? My dad has a Mac, so I'm developing it on and for Mac. If you only have Windows I won't have the developer tier for that platform as well.

I find that Game Maker offers a lot of mechanisms to make simple things easy for inexperienced users, but many of them aren't needed. When you discover that something doesn't meet your needs, and you know enough to replace it, it may make sense to do that. I've written my own place_free and place_meeting replacements because I want instances to act solid sometimes and not at other times, and I've moved from collision events to collision checking in step events as well. But I still use alarms, because they usually do what I need them to do and I don't have a particular reason to replace them.

I wouldn't use collision events if you're processing your own collisions. Game Maker will still use them for collisions even if you're calling them manually. You might want to put them into functions instead. One neat thing you can do is make a function with the same name in each object type to describe how it behaves in a particular situation (such as a collision) and then call that function when you detect that situation. Each instance will know how it behaves based on its function, and the colliding instance doesn't even need to know which specific type of thing it collided with. But you can just make them functions in a script somewhere and call them from a switch statement, or use a map to relate object indices to functions.
This is just where I am now. I've developed most of the collision logic that is the same for all bricks (finding the angle the ball should bounce back at), but have other logic unique to each brick type, so I'll need to put them in a separate place and call them. Your "function" idea sounds much better than a switch statement. What do you mean when you say, "make a function with the same name in each object type"? I wasn't aware of a way to create a generic object function that you could call from the object whenever you want. I've used scripts for stuff like that, but think an object level function is much better for this, and many other scenarios I've come across.
 

Nidoking

Member
The way I used to do this was to put the code in a User Event, but with functions, you can declare the function in the object definition. Say, in the Create event, you do breakme = function(){...} and fill in whatever you want instances of that specific object to do when broken. When you break one, you call its breakme() function to do whatever it does. The same thing works for any type of function you want to define, as long as you define it for every object.

The events are nice because if you want to have a general action and then have the child objects do specific additional things, you could use event_inherited to call the parent event. There's no clean way to do this with functions.
 
Top