[C++] Collision with a Rotating Object
st.tches

I'm coming along pretty far in my game, and hopefully once I get this all figured out I will be able to do a series of tutorials, but until then I need help with this haha.

I made a pendulum object for my game, and a class to hold it. Since it's constantly swinging back and forth, I can't just check for collision normally. To do this easily I decided to create a few points along the blade of the pendulum, and check for collision with those points, but I'm having a bit of a problem.

I'm using this to get the x and y coordinates on the swinging part:
bx = x + cx + r * cos(angle - 90);
by = y + cy + r * sin(angle - 90);
where x and y are the positions of the pendulum in my level, (cx, cy) is the center of rotation for my pendulum object, and angle is the angle of the dangle.

Why angle - 90? Well, and if this is incorrect tell me, but on the unit circle 0 degrees is all the way to the right where cos = 1 and sin = 0, but when my pendulum is at 0 degrees it's at the bottom, so I have to subtract 90 from the pendulum's angle to get the "real" angle.

The problem is that, well for lack of a better term and taking into consideration how frustrated I am, I'll dwindle it down to this:
My shit's fucked.

This is my class:

1class PENDULUM 2{ 3public: 4 float x, y, t, cvar; 5 float cx, cy, a, v, r; //Angle, Velocity, Radius 6 ALLEGRO_BITMAP *img; 7 PENDULUM(); 8 ~PENDULUM() { } 9 void init(const char *fPath); 10 float getVel(float angle); 11 void draw(float dx, float dy); 12 bool regCol(PLAYER &player, float bx, float by, const float p); 13 int regCol(PLAYER &player, const float p) 14}; 15PENDULUM::PENDULUM() 16{ 17 x = 0, y = 0, cx = 70, cy = 12; 18 t = 0, cvar = -1; 19 a = 100, v = 3, r = 100; 20} 21void PENDULUM::init(const char *fPath) 22{ 23 img = al_load_bitmap(fPath); 24} 25float PENDULUM::getVel(float angle) 26{ 27 if (angle < 0) angle *= -1; 28 if (a < 45) return 3; 29 else if (a < 67.5) return 2.5; 30 else if (a < 90) return 1.5; 31 else return 1; 32} 33void PENDULUM::draw(float dx, float dy) 34{ 35 if (timer > 91) 36 { 37 cvar *= -1; 38 v = 3; 39 t = 0; 40 } 41 al_draw_rotated_bitmap(img, cx, cy, 42 dx - (float)scrollX, dy, DEGTORAD(a), 0); 43 v = getVel(a); 44 a += v * cvar; 45 x = dx, y = dy; 46 timer ++; 47} 48bool PENDULUM::regCol(PLAYER &player, float bx, float by, const int p) 49{ 50 float px = player.x + p, pw = player.x + player.width - p; 51 float py = player.y + 10, ph = player.y + player.height - 10; 52 if ((px <= bx - scrollX) && (pw >= bx - scrollX) && 53 (py <= by) && (ph >= by)) return true; 54 else return false; 55} 56int PENDULUM::regCol(PLAYER &player, const float p) 57{ 58 float bx = x + cx + (r * cos(a - 90)); 59 float by = y + cy + (r * sin(a - 90)); 60 61 if (regCol(player, bx, by, p)) return 0; 62 63 return -1; 64}

The function I use to actually check for collision is the regCol that returns an integer. As you can see, right now I'm only testing for one point, but that's because I can't even get one to work right now haha.

I read something about "allegro degrees" and shit like that, but I'm not entirely sure how that works.

Oopse, almost forgot, DEGTORAD(x) is this:

And all members of the PLAYER class being used here are integers, don't know if that matters.

What am I doing wrong? I've double and triple checked the math on paper (Setting the angle to 0, so the difference is - 90) and X comes out perfectly, but for y to come out right I have to multiply (angle - 90) by -1 (Make it positive) when taking the sin of it.

EDIT: I also think I need to mention that I'm not trying to do anything complicated with this, I only need to check for three points on the blade (left end, middle, and right), I don't need it to be pixel perfect, because it's a 2D platformer and with the way it works 3 points on the blade should be fine for collision. The above code is me trying to check for the middle point.

Also, the player never actually collides with this, because it kills you on impact, so I don't need to do anything with the player's location or anything like that. To add to that I have no current death animation for my sprite (I have ideas but they're all going to be tedious so I'm avoiding it ) so I don't even have to initialize that, all that has to happen on collision is the player dies.

ryancoboy

From my knowledge, the cos and sin functions use radians. So when you do cos(angle - 90), the "90" part is degrees so now your mixing degrees with a radian operation.

Edit: Try converting your 90 degrees to radians. Then call your cos and sin functions with that value.

st.tches

I thought your signature was directed towards me, I was gonna say I've spent well over two hours with this son of a bitch

Thanks, I'll try it and post back when I figure out how it goes. For whatever reason I thought I read somewhere that you could use radians or degrees and that the function would magically figure it out, but I guess that sounds stupid now that I repeat it, haha.

Since both a and 90 are degrees I'll set a new variable equal to the difference (in radians) and I'll edit this post (Or create a new one) saying if it worked or not

ryancoboy

HAHA...yeah I should probably edit my sig. Actually I'll do it now.

st.tches

Haha yeah, although your sig has a point

Well I changed the regCol function (That returns an integer) and it's still fucked up, just not as arbitrarily (Is that a word?).

This is the new function:

1int PENDULUM::regCol(PLAYER &player, const float p) 2{ 3 float air = DEGTORAD(a - 90); 4 float bx = x + cx + (r * cos(air)); 5 float by = y + cy + (r * sin(air)); 6 7 if (regCol(player, bx, by, p)) return 0; 8 9 return -1; 10}

As you can see I made a new variable called air (Angle in radians) and set it equal (a - 90) in radians (a is, by default, in degrees, so that needs to be converted as well). I still have absolutely no idea why this isn't working .

ryancoboy

I'm not quite sure what the problem is then.

What exactly is happening in your game. does it crash? does it just simply not work at all (no apparent changes are happening)?

I see no more problems with your math syntactically. And if you say you checked the math several times, I would assume the problem is elsewhere.

st.tches

Every single thing about the pendulum works, except for collision. The problem with it is that - while my character is near it when he dies - he's not at the point I specified. I.E. if the pendulum is near the bottom almost dead center I could die just left of the center of rotation. These are the only things in my code that have anything to do with this class, in other words the only other places something could be going wrong:

1//Declaring the object 2PENDULUM pnone; 3 4//Initializing the object 5pnone.init("C:/stitchd/resources/images/environment/pendulum.png"); 6 7//This is from my function that draws everything for the main game: 8pnone.draw(500, 50); 9 10//If the player is on level one it uses the pendulum. Kind of pointless right now 11//but it'll be something later, also will place drawing here eventually... 12void pendulums() 13{ 14 switch (level) 15 { 16 case 0: 17 if (pnone.regCol(player, 20) == 0) dead = true; 18 break; 19 } 20} 21 22//All these are at different points in my code, they're just all in one code box to save space.

Also, it draws completely fine. Velocity, angle, timing, and all.

You want me to take a video of the problem occurring? Would that could help?

ryancoboy

Making a video of the problem would probably be useful for me/anyone else that may look at this thread.

st.tches

Ugh sorry it took so long, my old recorder apparently stopped working, so I downloaded this one, but then I realized the file was fucking 800 something MB, so I had to turn the quality wayyy down.

In other words, it's laggy, terrible quality, and altogether a shitty video, but it works haha.

EDIT: WHAT THE HELL? Video isn't working... -_-
I'll upload it to mediafire or something real quick if attaching it again doesn't work :/

http://www.mediafire.com/?25e4guoko1na1xl

Also, has anyone used mediafire recently? Totally changed (In a good way).

Oscar Giner
st.tches said:

Also, has anyone used mediafire recently? Totally changed (In a good way).

*Sees an ad with 2 nude men at the left*

st.tches

What?! For real? o.O I just clicked it and there are ads but... no nude men
Try it again haha, I'm leaving my house right now so I won't be able to respond for a little while. When I get back I can take a screen shot of the page if you want.

Oscar Giner

Yeah, well, nothing was actually showing, and the ad was for some kind of diet thing

someone972

I believe the problem is that the al_draw_rotated_bitmap function rotates clockwise, while sin and cos work counterclockwise. The offset of -90 is correct, but you need to do (360-a) first, i.e. cos((360.0f-a)-90.0f). Alternatively you could put the (360-a) into the drawing function instead, depending on how you want your angles to be based.

st.tches

Now my function looks like this:

1int PENDULUM::regCol(PLAYER &player, const float p) 2{ 3 float air = DEGTORAD((360 - a) - 90); 4 float bx = x + cx + (r * cos(air)); 5 float by = y + cy + (r * sin(air)); 6 7 if (regCol(player, bx, by, p)) return 0; 8 9 return -1; 10}

And it still does the exact same thing (Or at least looks like it does) so I don't understand this at all.

I tried swapping in (360 - a) for (a) in my draw function, but it also didn't really change anything :/

What the fuck?!

Raidho36

Why are your two "regCol" functions are having different return value ranges?(why having two of them in the first place?)

Try return regCol(player, bx, by, p)

st.tches

to be honest I have no idea, just a stupid design mistake I guess... I think I fucked up and didn't feel like changing it, then it just kinda stuck.

I don't think that was meant as a solution to my problem, just a suggestion, but regardless if it changes anything I'll edit this post.

EDIT: Actually I do know the reason I had two functions, but yeah the different return types was just stupid. The reason for the overloaded function was that in the second I planned on calling the first a couple more times, for each point I created on the pendulum blade, but since I can't even get the first one to work I don't have more than one yet.

EDIT2: Yeah, nothing changed, but I didn't really expect it to do anything other than remove a stupid mistake haha.

Anyways, here are the updated regCol functions:

1int PENDULUM::regCol(PLAYER &player, float bx, float by, const float p) 2{ 3 float px = player.x + p, pw = player.x + player.width - p; 4 float py = player.y + 10, ph = player.y + player.height - 10; 5 6 if ((px <= bx - scrollX) && (pw >= bx - scrollX) && 7 (py <= by) && (ph >= by)) return 0; 8 else return -1; 9} 10int PENDULUM::regCol(PLAYER &player, const float p) 11{ 12 float air = DEGTORAD((360 - a) - 90); 13 float bx = x + cx + (r * cos(air)); 14 float by = y + cy + (r * sin(air)); 15 16 return (regCol(player, bx, by, p)); 17}

someone972

I think it should probably be minus sin instead of plus for the y value. What is p?

Edit: It's helpful to try and picture what is happening in your mind. Starting with an angle of zero, the pendulum will be displayed strait down. Since cos/sin of 0 degrees points to the right you need to subtract off 90 degrees to get it aligned with your pendulum. After that cos(-90) will be 0, but sin(-90) will be -1. Adding the negative sine value to the y position makes the point face up instead of down, so you must have to subtract sine instead. I used the same sort of thought process to correct the clockwise problem. I hope that kind of makes sense .

Raidho36

I believe the problem is that the al_draw_rotated_bitmap function rotates clockwise, while sin and cos work counterclockwise.

What, really? How comes I didn't knew that yet?

It's helpful to try and picture what is happening in your mind

It's more helpful to render some debug graphics on screen overlay.

st.tches said:

float py = player.y + 10, ph = player.y + player.height - 10;

What's the reason to have 10 instead of p on this particular line?

But now that I studied code more closely, I have found that you have sine-returned value unflipped. Unless your Y-axis points upwards, that result is directly opposite of what you need. In your case it makes your pendulum be vertically inverted.

Though I'm having a very persistent idea that I have seen someone else was pointing that out.

st.tches

I have the +10 there because - for testing purposes - I was making the "bounding box" around the player smaller. It isn't "p" again because p is for something more specific. My sprite sheets house 60x60 slots for each frame, but my character is only about 20 px wide (Not counting his weapon, which shouldn't judge collision) so I subtract p from the x and width values to get the collision for my actual character, and not collide with white space. I'm not fixing that because I plan on having character customization and I want the option to add wider sprites using the exact same format, and all I'd have to do is use a change-able variable to plug in for p when functions are called then change that according to what character is being used.

Anyways, back on topic.

I'm assuming either multiplying the return value of sine by -1 OR multiplying the parameter for sine by -1 would both work?

Believe it or not I actually realized that when I was sketching it out on paper, but that was before I'd figured out some of the stuff in this thread so I was thinking it wasn't making a difference. Huh, I made a stupid .

Well I'll get back with how it works after the change.

EDIT:

Yeah, it's still doing it
I'm going to fucking break something .

I've tried it like this:

1int PENDULUM::regCol(PLAYER &player, const float p) 2{ 3 float air = DEGTORAD((360 - a) - 90); 4 float bx = x + cx + (r * cos(air)); 5 float by = y + cy + (r * sin(air * -1)); 6 7 return (regCol(player, bx, by, p)); 8}

and with the "float by =" looking like this:

1float by = y + cy + (r * sin(air) * -1)

And it still doesn't work. I might just give up on pendulums if I don't solve this fucking thing my Monday... ugh this is annoying.

HOLY TITTIES!

It fucking worked! I am uneblievably fucking happy, I can't even explain it. After all the tweaking I finally decided to draw a debug spike at bx and by so I could see what it was doing...

ALL I NEEDED TO DO WAS STOP ADDING CX AND CY TO MY FUCKING COORDINATES!

I had drawn the debug spike before and it was going all bat shit crazy, so I stopped, but after the adjustment to sine and the info that the draw function was operating clockwise instead of counterclockwise everything is working!

Titties!

HOLY SHIT GUYS, THANK YOU SOOOO MUCH! THANKYOUTHANKYOUTHANKYOUTHANKYOUTHANKYOUTHANKYOUTHANKYOUTHANKYOUTHANKYOUTHANKYOU!

Raidho36
st.tches said:

I'm assuming either multiplying the return value of sine by -1 OR multiplying the parameter for sine by -1 would both work?

No. Only prior would work. Read the math book. Also, no need to multiply. Just put minus sign in front of sin.

And... oh wow, this guy went ape-shit.

st.tches

yeah, now you might get just how long I was consistently working on this fucking thing before (and even after) I asked this question. It's a gigantic relief to finally be done with this, and just to mention something real quick;

This does work:

1vary = y + ((r + ro) * sin(air * -1));

ro is just the offset of the radius because the blade isn't a straight line. Don't worry about it if you don't understand the reasoning behind it haha, the point is that multiplying the parameter for the sine function seams to be working just fine. I've drawn that debug spike again and everything's exactly where I want it, so I guess I'm just leaving it like this .

Oh and just think of vary as by from earlier, I did some changing around to reduce clutter so it just looks a little different.