Legacy GM GML: Bouncing off Ends of Rectangular Walls

D

deciduous

Guest
Hello there!

I'm looking for some help with an algorithm that allows the player object to bounce off of a rectangular wall.

Some Context: in the game your player object is rotating around a circular "platform". You can tap the screen (or left click) to launch the player at any given time and it will follow its current direction until it interacts will another object, such as a bouncy wall. The bouncy walls are long and thin, and you are meant to bounce off one of the two long sides.

The Problem: Currently the bouncy walls work just fine positioned at any angle as long as you hit one of the long sides, but when you collide with one of the two end caps (shorter sides) of the rectangle it takes on a new direction as though you had hit a long side. (ie: if the wall is positioned at a 45 degree angle and you are travelling upwards at a 90 degree angle you should bounce off the long side and take on a 0 degree angle, and if you were to hit the shorter side you should take on a 180 degree angle. However the shorter side will also give you a new angle of 0. I've drawn an example image and posted it to the bottom of the post.)

Here is the current working code:

if(place_meeting(x, y, obj_wall))
{
if(!global.mute)
audio_play_sound(snd_bounce, 0, false);

the_wall = instance_place(x, y, obj_wall);
launch_dir = (2 * the_wall.image_angle) - launch_dir; // launch_dir is the direction the player is travelling

x += lengthdir_x(move_speed_x, launch_dir);
y += lengthdir_y(move_speed_y, launch_dir);
}

I understand the issue and why it does not work. One possible solution would be to calculate whether or not the player collides with the rectangle and it is in between the coordinates for either long side of the rectangle, but this is challenging because lots of the bouncy walls are positioned at an angle other than 0 or 90 degrees.

My apologies if my explanation is long winded or confusing. Please let me know if I can clear any confusion up, and thank you in advance for any help you may give!

gm_forum_image_01.png
 
S

Snail Man

Guest
Hmm, well what I would likely do in your situation is to program two separate collisions: one for horizontal, and one for vertical. You already have the vertical one working fine, just change your current code so it checks for the block at y-1 (and/or y+1) instead of just y. Then make separate place_meeting calls to check at x+1 and x-1. When this happens, just reverse the horizontal velocity of the ball. (move_speed_x *= -1)
 
D

deciduous

Guest
Snail Man, thanks for your reply! That's a great idea, but the problem is that many of the bouncy walls are positioned at angles other than 0 and 90 degrees. So if I bounce off an end that is at a 45 of 30 degree angle the horizontal or vertical solution wouldn't work quite right.

Edit: Here is another example image that shows a bouncy wall at an angle, and what happens if you collide with it.

gm_forum_image_02.png
 
Last edited by a moderator:
B

bojack29

Guest
I would use place_meeting with a few other checks.

Is it precise collisions for the rotated rectangle? Otherwise your working with bounding boxes and that can give you weird results.

You could check the x and y of both the ball and rectangle and determine the proper collision after, that would seem to work well.
 
D

deciduous

Guest
Bojack29, thanks much for your reply! I'm using place_meeting for the initial check and it's working great. The collision masks are precise and rotate with the rectangles. How would you recommend implementing checking x and y in this case?

One thing I'm trying this morning that I have absolutely no experience with is the drag and drop system. I was hoping maybe I could add a collision event for the bouncy wall then add a bounce action, but my player doesn't bounce at all when I do that. Does anyone know a drag and drop way to solve the problem? That may be easier.
 

TheouAegis

Member
You are not even taking the shape of the wall into consideration. All your code does is make the ball go 0° (in this example) because all it concerns itself with is the wall's position,not its shape. The bouncing in the wall with seemingly random exiting is because by using x+= and y+=, you are shoving the ball into the wall, causing it to bounce between long edges.

If you don't use D&D, i'm thinking you may need to calculate the position of each individual side and check for collisions manually. D&D would be easier.
 
R

Robert

Guest
I just went through all of this recently so here are a few tips:

Im pretty sure your problem is this part:

Code:
launch_dir = (2 * the_wall.image_angle) - launch_dir; // launch_dir is the direction the player is travelling
As here you are specifying a certain angle for only 1 situation, but as you noted in your images your ball can hit at all different angles, not just the one you have set here. The walls image angle is static and has nothing to do at the angle in which the object has collided with the wall, also you are setting launch_dir and using it in your assignment it seems off. But the wall.image_angle Im pretty sure need to be the angle of the side of the wall you are colliding with, as in colliding horizontally OR vertically. Also, and dont quote me on this, but I think that you might also need to do something special for the exact corners, as I had issues where the player could get their object stuck in corers of objects where there was't even a pixel of empty space. Eventually I ended up restricting the bouncing areas in a vary complicated way so my code wont really help you.

Probably the easiest way to get boncies is to just make the wall a solid object and then in your object create a collision with the wall and in the wall collision add move_bounce_solid(false); Gamemaker has built in bounce collision, though it too has problems especially for bouncing off overlapping objects.
 
D

deciduous

Guest
TheouAegis, thanks for your response. I understand what you're saying, but I'm not entirely sure how to fix it. Can you think of a way to distinguish between different sides of the rectangle? Also, how would you suggest I go about doing this in D&D? I'm an absolute noob in that area. I set a wall collision event with a bounce action inside, and the player just stops completely when it hits the wall :p

JML, the rectangles are stationary in game.

Robert, I tried your suggestion and used move_bounce_solid(false); in the collision code with the wall, and the same thing happens as when I use the D&D collision and bounce combo; which is that the player object just completely stops when it collides with the bouncy wall. I'm at a total loss as to why though :(

Thanks everyone for all your suggestions so far! I very much appreciate it.
 
B

bigreddog

Guest
So I have an Answer for you.. I kind of ran into the same problem as you. This MIGHT help you , or it might not.
So Gamemaker is notorious for if you object moves to fast or falls to fast or you keep hammering away at a wall it will give. you might want to look at one of those aspects to your game. for example
in my game i had

if place_meeting(x-10,y,obj_ground_tile1) then {x=x;}
if place_meeting(x-10,y,obj_moving_ground) then {x=x;}
if place_meeting(x-10,y,obj_rev_moving_ground) then {x=x;}

but then had my move code afterwards. Example
x=x+global.move;

what was happening was that the character would jump, hit the wall and keep hammering away at it as it progressed up the wall till it hit the corner and then freeze the game.
what I did was put an exit AFTER the x=x in the place_meeting code. so Example:


if place_meeting(x-10,y,obj_ground_tile1) then {x=x;exit;}

so what that did was when I hit the wall, it didn't allow the right key press to keep going and hammer the wall causing the character to get stuck on the corners. Because I was ALREADY -10 from the wall it would always exit from the move code until it wasnt any more. then it would more the character forward..

so this MAY or MAYNOT help. But it solved all of my problems.

best of luck!

Cheers!
 

dphsw

Member
Your ball is generally travelling partly in a direction perpendicular to the long side of the wall, and partly in a direction perpendicular to the short side of the wall. If you can work out what amounts these are, then bouncing it off one side is as simple as multiplying that component by -1.

Suppose your wall is at an angle of 30 degrees, and your ball is travelling horizontally towards it at a speed of 5. It is then travelling at a speed of 5*cos(30 degrees) perpendicular to the short side of the wall, and 5*sin(-30 degrees) perpendicular to the long side of the wall.



More generally, if the ball is moving with a certain speed at a certain angle, it should automatically have hspeed = speed*dcos(angle) and vspeed=speed*dsin(-angle). (If you're handling speed and direction manually, you'll have to compute that step yourself).

Then:
Code:
platform_short_side_perp_speed = hspeed*dcos(platform_angle) + vspeed*dsin(-platform_angle);
platform_long_side_perp_speed = hspeed*dsin(-platform_angle) + vspeed*dcos(platform_angle);

if(bounce_long_side) platform_long_side_perp_speed*=-1;
if(bounce_short_side) platform_short_side_perp_speed*=-1;

hspeed = platform_short_side_perp_speed*dcos(platform_angle) + platform_long_side_perp_speed*dsin(-platform_angle);
vspeed = platform_short_side_perp_speed*dsin(-platform_angle) + platform_long_side_perp_speed*dcos(platform_angle);
// There could be a few minus signs in the wrong place there - the GameMaker '+Y is down' coordinate system always confuses me
Then it's just a question of how to actually detect whether to bounce off the sides. Firstly I'd check what angle the ball is heading in, what the angle is from the ball to the platform, and what the angle is between those two angles - if the ball is heading away from the platform, then you don't want to bounce even if there is a collision, because it would just mean bouncing back towards it, and probably bouncing every frame after that.

Indeed, you could use angles to know where the ball is hitting. If you know the platform is 80 pixels wide and 10 pixels high, then just getting point_direction(0,0,80,10) will tell you the angle from the centre to the platform to the top right corner. It's about 7 degrees in that case, so you know the other corners are at 173 degrees (180-7), 187 degrees, and so on. So if the platform is rotated by 30 degrees, those points are now at 37 degrees, 203 degrees, 217 degrees, etc. And if you get a collision when the ball is at 214 degrees to the platform, that's between the two points on the left, so it must have hit that little wall on the left.
 
Edit:
This topic is 8 months old...

You could use collision_normal() script from gmlscripts. There's a cool html5 demo on the site too.

Something like this:
Code:
//collision with wall detected

//first move back to before there was a collision
x = xprevious;
y = yprevious;

//move just to the collision
while(!place_meeting(x,y,obj_wall)){
    x += lengthdir_x(1, launch_dir);
    y += lengthdir_y(1, launch_dir);
}

//calculate the normal from the wall
var norm_dir = collision_normal(x,y,obj_wall,32);

//calculate the vector of the normal normalized
var norm_x = lengthdir_x(1, norm_dir);
var norm_y = lengthdir_y(1, norm_dir);

//calculate the vector of the movement
var launch_x = lengthdir_x(move_speed, launch_dir);
var launch_y = lengthdir_y(move_speed, launch_dir);

//calculate the movement vector in the direction of the normal
var dot = dot_product(norm_x, norm_y, launch_x, launch_y);
var perp_x = dot * norm_x;
var perp_y = dot * norm_y;

//now we just reflect that for a bounce
launch_x -= 2 * perp_x;
launch_y -= 2 * perp_y;

launch_dir = point_direction(0,0,launch_x,launch_y);
Notes:
  1. The 32 in the collision_normal script should be the radius of the ball but you might need to add one to it to detect the collision.
  2. The part where we reflect for the bounce, the 2 that we used can be changed depending on how you want the bounce to work. Using a value of 1 will simply cancel out the speed perpendicular to the wall. The object will appear to slide against the edge. A value between 1 and 2 will kill some of the energy. A value greater than 2 will add energy to the bounce.
  3. Untested but it should work.
 
Last edited:
Top