#61383 - thegamefreak0134 - Sat Nov 19, 2005 4:25 pm
I'm putting this here because it's really more of a math issue than a graphics problem. I'm working on that port of bejeweled still on the main page. If you haven't noticed yet, you can rotate the entire board using L and R. This is for a freestyle version of play later down the road. Unfortunately, the board rotate is kinda instant, and I want an animation. I want to get the entire board (composed of sprites) to rotate as one object, only without actually rotating the sprites. (that way the sprites stay right-side up) What I don't know is how I would plot the coordnates for such a move. Does anybody have a suggestion?
I am familiar with the rotating of sprites, but I understand that the rotation itself is done with hardware, and therefore I don't know how to actually use that for this purpose.
If I need to elaborate more, I can. I'm even willing to draw pictures.
Thanks in Advance!
_________________
What if the hokey-pokey really is what it's all about?
[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]
#61689 - Miked0801 - Tue Nov 22, 2005 6:56 pm
Do you understand any sort of matrix math?
#61710 - thegamefreak0134 - Tue Nov 22, 2005 9:15 pm
Probably. It depends on what kind. If the language can do it, can pick up on concepts easily enough.
_________________
What if the hokey-pokey really is what it's all about?
[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]
#61713 - keldon - Tue Nov 22, 2005 9:18 pm
#61719 - DekuTree64 - Tue Nov 22, 2005 9:31 pm
Basically you need to rotate the center point of each sprite around the center of the screen.
The good old formula for rotation around the origin is:
Code: |
Vector2 Rotate(Vector2 point, int angle)
{
Vector2 rotated;
rotated.x = point.x * cos(angle) + point.y * sin(angle);
rotated.y = point.x * -sin(angle) + point.y * cos(angle);
return rotated;
} |
To rotate around any point, just translate to the origin, rotate, and translate back:
Code: |
spriteCenter = Rotate(spriteCenter - screenCenter, angle) + screenCenter; |
You'll also need to adjust for the fact that sprites are positioned by their top left corner instead of center. Basically just add 1/2 the dimensions to the top left position to get the center, do the rotation, and then subtract 1/2 dimensions to get the new top left.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#61721 - thegamefreak0134 - Tue Nov 22, 2005 9:36 pm
You people are so WONDERFUL. ... ...... I don't get it ... ..... Oh well. I'll figure it ot in a secnod or two. This sounds absolutely perfect, thank you SOO much. I should try it and have it down in a week or so. I guess my biggest problem will be speed, as I generally do things the slow way at first. Not to mention that I have to apply this function to 64 different sprites every frame. But hey, it's worth it!
_________________
What if the hokey-pokey really is what it's all about?
[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]
#61728 - thegamefreak0134 - Tue Nov 22, 2005 10:10 pm
OK, let me know if I get this right. Using this function, I would first need a type declaration that had variable.x and variable.y. I would then put the screen positions in one declaration of that type, the sprite positions in another variable of that type, pick a nice happy angle to rotate by, plug all that into the function, and take the .x and .y positions from the result as the new position for the sprite?
If this is correct (and i'll know in a few moments) I will be so happy it won't even be funny. If it's not, please let me know. I'm still pretty rusty when it comes to C++ functions and type declarations, but I think I can do it.
I'm assuming that the cos/sin functions here are standard C and will be pretty damn slow. I'll deal with that for now, and write a lookup table later.
I was going to ask something else, but it completely escapes me at the moment. (Brain Fart.) I'll prolly remember it later. Thanks again sooo much. Awaiting your post!
_________________
What if the hokey-pokey really is what it's all about?
[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]
#61734 - DekuTree64 - Tue Nov 22, 2005 11:07 pm
Yep, that's the idea. Vector addition just adds each component individually (x+x, y+y, etc).
The sin/cos functions can be standard library, or your own. Whatever you want, as long as they're consistent with your angle units.
If your angle is in degrees, you can make a lookup table of 360 entries and index into that for the sine/cosine.
Or what I usually do is make a lookup table of 256 entries, and do all my angles on a 0-255 scale instead of normal degrees. It's handy because you can use angle & 255 to wrap to one rotation, shift down 6 bits to find what quadrant you're in, stuff like that. But only use it if you're comfortable with non-standard units :)
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#62225 - thegamefreak0134 - Mon Nov 28, 2005 2:46 pm
Yay! Yay! Happy and Joy! <dancing> SO HAPPY!!! </dancing>
Um, it should be obvious now that I finally got the thing to work. However, it was not without a price. It took me about a full week of coding and error checking to get all of the kinks out of my pitifull code, but it works, and quite beautifully at that. BTW, I didn't have to adjust each sprite to account for the top-left thing, is was much easier just to adjust where my screen was centered since evry sprite has the same dimentions.
I learned many things from this endevor.
(1) The GBA doesn't exactly seem to like floating point math. At all. I was slightly prepared for this with a lookup table of all the needed sine and cosine values multiplied by 100, but my undoing code attemted to undo this too soon. I had:
Code: |
rotated.x = point.x * (cosine1[angle]/100) + point.y * (sine1[angle]/100); |
and I should've had
Code: |
rotated.x = (point.x * cosine1[angle])/100 + (point.y * (sine1[angle])/100; |
The first, apparently because of the whole floating point thing, was reducing all of my points to the center. My only guess is that is was rounding every cos/100 and sin/100 to 0.
(2) Re-using points is not highly recommended. (unless you REEELY want your sprites to go flying off in random directions.) I discovered very quickly that by re-performing the rotation by 3 degrees to already rotated points resulted in a garbled mess at the end. This was because (I can only guess) the calculated points were rounded to the display points, making the rotation more and more in-accurate every frame. I countered this by copying my sprite data, and then performing the rotations on the copy. This final step produced very close to the results I wanted.
(3) Signed integers. Not unsigned. Unsigned bad...
That was an early mistake as well. It was causing half my board (well, more actually) to be translated off in wierd spots because the sign was never allowed to be negative. Not happy. Easily fixed, however.
I was so happy at this point that it took me a couple of tries to realize how badly my music was skipping. (degrees was set to 1 at this point.) Nah, my code was slow? couldn't be... But it was. I actually have a couple things in mind that might clear it up. (for now, I set the degrees to 3 and threw in a WaitForVsync() every so often.)
For some wierd reason, when I try to copy sprites to sprites2 (both OAMEntry types, back from DevKitAdv, which I'm still using, so get over it) it gave me a type initialize error. A similar error occured when I tried to copy after a initialization. I was doing it like this:
OAMEntry sprites2[128] = sprites; //Wherein sprites is an OAMEntry type with 128 set as the length also...
What am I doing wring here? Since my code makes pretty heavy use of this (almost every frame) it would really be useful to use this type of code rather than the loop I used to get around it. It seems like I'm just missing a little piece of C++ knowledge here, but help is appreciated. With degree set to 3, it's a little choppier than I would like, although not noticable to the untrained eye.
Um, question: Is 100 a little too simplified for my sin/cos calculations? I can't really tell, but would 1000 make the animation look better? The points really don't ever get more than about 32 pixels away from the origin during the rotation. Never having actually used rotation in this manner before, I don't know what the level of accuracy needed would be.
Thanks again for all of your help. This is I think the most difficult piece of my code so far, thanks to that little formula you gave me. If you want to see all of the sections of code involved in this happy little rotation for review and comment, tell me and I'll post. I actually have a very specific question to ask on that note, but I'll do it then.
Still, very happy. Maybe I'll go have some cheese...
PS: If you should ever know, I got my TI-83 Plus SE to do the rotation looong before I got the coding to work on the GBA, although I will say the calc was only doing one point and the GBA was still much faster.
-----
The above was typed whilst away from the internet. As such, I have a few additions. (questions.)
I did some calculations and discovered that I'm requiring a divide operation 260 times per frame. I figrue this would be a valid slow-downing operation, so I have a possible solution. Would it be possible to, rather than multiplying by 100, multiply my lookup tables by say 1024 and then bitshift instead of dividing? (1024 because, after analysis, I need a little more precision than my first attempt got, although it still looks alright to me.)
Sorry for the length of the post, but this is kind of the most impressive part of the code so far and I want it to work as efficiently as possible, you use it a LOT in gameplay.
_________________
What if the hokey-pokey really is what it's all about?
[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]
#62249 - Miked0801 - Mon Nov 28, 2005 6:25 pm
rotated.x = (point.x * cosine1[angle])/100 + (point.y * (sine1[angle])/100;
becomes
rotated.x = (point.x * cosine1[angle] + point.y * sine1[angle]) / 100;
and saves 1/2 your divides.
BTW, why 100? 256 or 65536 are much more common results. Just multiply your sin/cos float values by 65536 and when done, shift right 16. Way accurate. 256 would work as well with >> 8.
#62251 - thegamefreak0134 - Mon Nov 28, 2005 6:37 pm
100 because it seemed right to me. I'm still way used to my old calc which was happy about just about any scale you wanted, and 100 is easy to work with (plus makes more sense to me) so...
To accomodate for the bitshifting, my code should now become :
Code: |
rotated.x = ((point.x * cosine1[angle] + point.y * sine1[angle])>>8) |
Yes? (assuming I've re-defined my tables as cos/sin of value * 256)
And this should be much faster?
_________________
What if the hokey-pokey really is what it's all about?
[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]
#62281 - DekuTree64 - Tue Nov 29, 2005 1:30 am
thegamefreak0134 wrote: |
100 because it seemed right to me. I'm still way used to my old calc which was happy about just about any scale you wanted, and 100 is easy to work with (plus makes more sense to me) so... |
What you've come up with here is called fixed-point math, and is very useful. The rules stay the same with any base, but normally powers of two are used for speed.
Quote: |
To accomodate for the bitshifting, my code should now become :
Code: | rotated.x = ((point.x * cosine1[angle] + point.y * sine1[angle])>>8) |
Yes? (assuming I've re-defined my tables as cos/sin of value * 256)
And this should be much faster? |
Yes and yes. You'll probably get smoother movement too, because the addition is done while you still have the fractional portion of the values.
Another optimization you could make is to only do the sin/cos lookups once, since the angle is the same for all the sprites anyway. Table lookups are pretty quick, but as said in my sig, doing nothing is always faster :)
And for your array copying question... use a for loop or memcpy, compilers aren't smart enough to copy whole arrays.
Sometimes the size of an array isn't known at all (eg. declared like extern int array[];), and if the sizes of the arrays aren't equal, then there's not much of a logical thing for it to do. I guess the language designers decided it wasn't worth the trouble to do it for the one case where it would work.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#62290 - tepples - Tue Nov 29, 2005 2:30 am
DekuTree64 wrote: |
use a for loop or memcpy, compilers aren't smart enough to copy whole arrays. |
In cases where sizeof() actually works, would this macro work?
Code: |
#define arraycpy(dst, src) memcpy(dst, src, sizeof(src)) |
Is there a way to give it better error handling for cases where you pass a pointer (that is, when sizeof(src) is the same as sizeof(void *) )?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#62398 - thegamefreak0134 - Wed Nov 30, 2005 5:44 pm
Yay. It works. First off, apparently 256 was as accurate as it needed to be, as it looks muck less... <searches for word> ... jittery. I am able to pull my extra Vsyncs out and use 1 as the angle and it still goes fastenough. Yay!
I noticed that changing from a /256 to a >>8 had no visible effect, when I expected it to go just as slow the first time. Perhaps my compiler automatically converts /256 to >>8 in favor of speed for me?
Anywho, the whole thing works very well now and I'm done asking for help. Thanks again sooo much! I'll try to have a new release of the game out with all of the extra features I've added. (proper rotating, a hint system (well kindof) sound, music, and such...) Keep an eye out.
_________________
What if the hokey-pokey really is what it's all about?
[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]
#62416 - poslundc - Wed Nov 30, 2005 7:47 pm
thegamefreak0134 wrote: |
Yay. It works. First off, apparently 256 was as accurate as it needed to be, as it looks muck less... <searches for word> ... jittery. I am able to pull my extra Vsyncs out and use 1 as the angle and it still goes fastenough. Yay!
I noticed that changing from a /256 to a >>8 had no visible effect, when I expected it to go just as slow the first time. Perhaps my compiler automatically converts /256 to >>8 in favor of speed for me? |
For signed variables, /256 is compiled to slightly more complicated code than >>8, because shifting of negative numbers converges to -1 instead of 0. But there would be no noticeable speed penalty from this in all but the most tightly optimized code - usually at the point where you're writing at the assembly level anyway.
In general, division by constants that aren't on even powers-of-two boundaries are also optimized by GCC into reciprocal multiplication.
These are peephole optimizations, and are enabled when you specify an optimization level of -O1 or greater. If you turn off optimizations (-O0), then /256 will be way more expensive than >>8, but it's generally adviseable to use the more understandable /256 and just turn the necessary optimizations on.
Dan.
#62431 - tepples - Wed Nov 30, 2005 9:17 pm
One thing that might speed up your code is to look up cosine and sine once, before the loop, and then keep them in variables through the inner loop. It would also help you refactor your code to use a more generic matrix in the future.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#62516 - thegamefreak0134 - Thu Dec 01, 2005 7:35 pm
I do that already. I looked up the values myself, using microsoft excel, suprisingly, and then manually copied those values into a text file and rearranged to define them as my cosine1, sine1, etc. lists.
_________________
What if the hokey-pokey really is what it's all about?
[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]