Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Rotation problem

Credits go to Don Freeman, Edgar Reynaldo, and jclarkrichards for helping out!
This thread is locked; no one can reply to it. rss feed Print
Rotation problem
Simsimius
Member #6,017
July 2005

I'm trying to get back into programming, but I'm still as useless as I've always been.

So, starting from scratch with a certain template, I decided to start off with some basics - such as rotating a sprite. But that's... not as easy as I thought.
All I want to do is to rotate the sprite, but when I push the keys, nothing happens.
I've tryed using the code all over the place, in the class, out of the class, etc.
So, any hints/tips?

1 void GetControls()
2 {
3 if(key[KEY_UP]) { if(ypos - speed < 0) ypos = 0; else ypos -= speed; }
4 if(key[KEY_DOWN]) { if(ypos + speed + sprite->h > SCREEN_H) ypos = SCREEN_H - sprite->h; else ypos += speed; }
5 if(key[KEY_LEFT]) { if(xpos - speed < 0) xpos = 0; else xpos -= speed; }
6 if(key[KEY_RIGHT]) { if(xpos + speed + sprite->w > SCREEN_W) xpos = SCREEN_W - sprite->w; else xpos += speed; }
7 if(key[KEY_A]) { rotate_sprite(b, sprite, xpos, ypos, itofix(-100)); }
8 if(key[KEY_D]) { rotate_sprite(b, sprite, xpos, ypos, itofix(100)); }
9 
10 }
11 
12 void Draw(BITMAP* b)
13 {
14 draw_sprite(b, sprite, xpos, ypos);
15 }

____
There's no place like 127.0.0.1

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Store the rotation in a variable, alter the variable when the turning keys are pressed, and display it using the rotation variable when you draw your buffer.

The problem currently is that you draw the sprite into b when you press the rotation key, but when you call your Draw function, it just draws the sprite normally.

Simsimius
Member #6,017
July 2005

So, something like this?

      void GetControls()
      {
         if(key[KEY_UP])    { if(ypos - speed < 0)                    ypos = 0;                    else ypos -= speed; }
         if(key[KEY_DOWN])  { if(ypos + speed + sprite->h > SCREEN_H) ypos = SCREEN_H - sprite->h; else ypos += speed; }
         if(key[KEY_LEFT])  { if(xpos - speed < 0)                    xpos = 0;                    else xpos -= speed; }
         if(key[KEY_RIGHT]) { if(xpos + speed + sprite->w > SCREEN_W) xpos = SCREEN_W - sprite->w; else xpos += speed; }
         if(key[KEY_A]) { rot -= 40 }
         if(key[KEY_D]) { rot += 40}
      }

      void Draw(BITMAP* b)
      {
         //draw_sprite(b, sprite, xpos, ypos);
         rotate_sprite(b, sprite, xpos, ypos, itofix(rot)); }

Although, I'm using the variable, and the intofix, wrongly, as it still isn't rotating. How should I be using it?

____
There's no place like 127.0.0.1

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Remember that rotate_sprite takes allegro degrees [0 to 256), so adjusting by 40 every time the key is down is a large turn. Are you calling Draw(buffer);, and then displaying your buffer onto the screen? Show more code.

Simsimius
Member #6,017
July 2005

Well, here's the code that draws to the buffer then screen.

clear(buffer);
MyPlayer.Draw(buffer);
blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);

It's not rotating at all.
Here's the entire class

1class CPlayer
2{
3 public:
4 int xpos, ypos, speed, rot;
5 BITMAP *sprite;
6 CPlayer(): xpos(0), ypos(0), speed(5), rot(0), { sprite = load_bitmap("test.bmp", NULL); }
7 ~CPlayer() { destroy_bitmap(sprite); }
8 void GetControls()
9 {
10 if(key[KEY_UP]) { if(ypos - speed < 0) ypos = 0; else ypos -= speed; }
11 if(key[KEY_DOWN]) { if(ypos + speed + sprite->h > SCREEN_H) ypos = SCREEN_H - sprite->h; else ypos += speed; }
12 if(key[KEY_LEFT]) { if(xpos - speed < 0) xpos = 0; else xpos -= speed; }
13 if(key[KEY_RIGHT]) { if(xpos + speed + sprite->w > SCREEN_W) xpos = SCREEN_W - sprite->w; else xpos += speed; }
14 if(key[KEY_A]) { rot -= 10 }
15 if(key[KEY_D]) { rot += 10}
16 }
17 
18 void Draw(BITMAP* b)
19 {
20 //draw_sprite(b, sprite, xpos, ypos);
21 rotate_sprite(b, sprite, xpos, ypos, itofix(rot));
22 
23 }
24};

____
There's no place like 127.0.0.1

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

You're still missing semicolon statement endings on these two lines :

         if(key[KEY_A]) { rot -= 10 }
         if(key[KEY_D]) { rot += 10}

Are you calling MyPlayer.GetControls(); in your logic loop? If that's not it, just attach the whole source file.

Simsimius
Member #6,017
July 2005

It's being called.

I'll paste the source code - it's basically just a template I got from AGDN that I always use as a base for any Allegro work.

1class CPlayer
2{
3 public:
4 int xpos, ypos, speed, rot;
5 BITMAP *sprite;
6 
7 CPlayer(): xpos(0), ypos(0), speed(5), rot(0), { sprite = load_bitmap("test.bmp", NULL); }
8 
9 
10 ~CPlayer() { destroy_bitmap(sprite); }
11 
12 
13 void GetControls()
14 {
15 if(key[KEY_UP]) { if(ypos - speed < 0) ypos = 0; else ypos -= speed; }
16 if(key[KEY_DOWN]) { if(ypos + speed + sprite->h > SCREEN_H) ypos = SCREEN_H - sprite->h; else ypos += speed; }
17 if(key[KEY_LEFT]) { if(xpos - speed < 0) xpos = 0; else xpos -= speed; }
18 if(key[KEY_RIGHT]) { if(xpos + speed + sprite->w > SCREEN_W) xpos = SCREEN_W - sprite->w; else xpos += speed; }
19 if(key[KEY_A]) { rot -= 10; }
20 if(key[KEY_D]) { rot += 10; }
21 }
22 
23 void Draw(BITMAP* b)
24 {
25 //draw_sprite(b, sprite, xpos, ypos);
26 rotate_sprite(b, sprite, xpos, ypos, itofix(rot));
27 
28 }
29};
30 
31volatile int game_time = 0;
32 
33void Timer(void)
34{
35 game_time++;
36}
37END_OF_FUNCTION(Timer); // end Timer()
38 
39int main()
40{
41 
42 
43 allegro_init();
44 install_keyboard();
45 set_color_depth(16);
46 set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0);
47 
48 
49 LOCK_VARIABLE(game_time);
50 LOCK_FUNCTION((void*)Timer);
51 install_int_ex(Timer, BPS_TO_TIMER(60));
52 
53 
54 BITMAP* buffer = create_bitmap(640, 480);
55 
56 
57 CPlayer MyPlayer;
58 
59 
60 do{
61 
62 
63 while(game_time > 0)
64 {
65 
66 
67 MyPlayer.GetControls();
68 
69 
70 game_time--;
71 }
72 
73 
74 clear(buffer);
75 
76 MyPlayer.Draw(buffer);
77 
78 
79 blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
80 
81 while(game_time <= 0){}
82 
83 }
84 while(!key[KEY_ESC]);
85 
86 destroy_bitmap(buffer);
87 
88 
89 return 0;
90}
91END_OF_MAIN();

____
There's no place like 127.0.0.1

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Quote:

LOCK_FUNCTION((void*)Timer);

The cast doesn't belong there.

END_OF_MAIN() doesn't use a semi-colon at the end.

There shouldn't be a comma after rot(0) in your constructor initialization list :

//
      CPlayer(): xpos(0), ypos(0), speed(5), rot(0), { sprite = load_bitmap("test.bmp", NULL); }
//

It's also not a good idea to load bitmaps in your constructor or destroy bitmaps in your destructor because the objects can't be global, and you have to make another function call to see if they loaded properly anyway. Which brings us to - check the return values of functions that can fail like allegro_init() , set_gfx_mode(), load_bitmap(), etc...

After fixing those things and replacing load_bitmap with create_bitmap, it ran fine for me. It also shouldn't have been compiling for you, as it came up with an error when the extra comma was there. Use -Wall to turn on most all of the compiler warnings.

Simsimius
Member #6,017
July 2005

Thanks for helping out.

Quote:

Edgar Reynaldo:
Quote:
LOCK_FUNCTION((void*)Timer);
The cast doesn't belong there.

So, what do I do?

Quote:

It's also not a good idea to load bitmaps in your constructor or destroy bitmaps in your destructor because the objects can't be global, and you have to make another function call to see if they loaded properly anyway. Which brings us to - check the return values of functions that can fail like allegro_init() , set_gfx_mode(), load_bitmap(), etc...

What should I do with the bitmaps instead?

Quote:

After fixing those things and replacing load_bitmap with create_bitmap, it ran fine for me. It also shouldn't have been compiling for you, as it came up with an error when the extra comma was there. Use -Wall to turn on most all of the compiler warnings.

Turned it on.

____
There's no place like 127.0.0.1

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Quote:

So, what do I do?

Just get rid of the cast.

LOCK_FUNCTION(Timer);

Quote:

What should I do with the bitmaps instead?

Just make some load/unload member functions. That way you can detect and return an error if anything is missing or doesn't load properly. Or you can separate out all your loading and resource freeing calls into global load_resources() and free_resources() functions. If the loading is successful, just assign the appropriate BITMAP*s to the objects that need them. Another option is to pass the BITMAP* as a parameter to your constructor after it has been loaded and verified. In that case, make sure not to destroy it in the destructor, as it is only using it as a reference and doesn't really own the resource, but rather the free_resources() function does.

Simsimius
Member #6,017
July 2005

I'm still somewhat confused.
So, I should create a function,

public:
      int sload( sprite = create_bitmap("test.bmp", NULL););

right?

And then call the function elsewhere in the class - where exactly should that be?

I apologize for asking such questions, but it's been a very long time since I touched this stuff. I thought this would a simple thing to do [rotating a sprite], that would be a good step back and get back with everything. It appears I was mistaken :P.

____
There's no place like 127.0.0.1

Don Freeman
Member #5,110
October 2004
avatar

Don't give up so easily. You are nearly there! You can call it anywhere after you create your object and you have initiated allegro and setup the video mode. Just make sure that the function returns at least a bool if the function did indeed succeed in loading the bitmap.

1class SomeClass
2{
3public:
4 SomeClass()
5 {
6 m_Image = NULL;
7 }
8 ~SomeClass()
9 {
10 this->Cleanup();
11 }
12 bool LoadImage( const char *fileName )
13 {
14 // make sure we cleanup any previous images...
15 if ( m_Image )
16 destroy_bitmap(m_Image);
17 // load the image
18 m_Image = load_bitmap(fileName,NULL);
19 if ( m_Image )
20 return true;
21 // didn't get the image loaded... :(
22 return false;
23 }
24protected:
25 void Cleanup()
26 {
27 if ( m_Image )
28 destroy_bitmap(m_Image);
29 m_Image = NULL;
30 }
31 BITMAP *m_Image;
32};

Then you use it like:

1int main( void )
2{
3 allegro_init();
4 set_color_depth(32);
5 set_gfx_mode(GFX_AUTODETECT_WINDOWED,800,600,0,0);
6 set_color_conversion(COLORCONV_TOTAL);
7 install_timer();
8 install_keyboard();
9 /////////////////////
10 // Since you are calling allegro functions inside the class, you
11 // should make this a dynamic variable. If not, your class might try
12 // to cleanup after allegro has exited.
13 SomeClass *pSomeClass = new SomeClass;
14 if ( !pSomeClass )
15 return 1;
16 if ( pSomeClass->LoadImage("data/image.bmp") )
17 {
18 // error loading image
19 }
20 // do other stuff...
21 delete pSomeClass;
22 pSomeClass = NULL;
23 return 0;
24}END_OF_MAIN()

--
"Everyone tells me I should forget about you, you don’t deserve me. They’re right, you don’t deserve me, but I deserve you."
"It’s so simple to be wise. Just think of something stupid to say and then don’t say it."

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Quote:

I'm still somewhat confused.
So, I should create a function,

Sure, or you can leave the loading out of your class, and just set the sprite in your constructor call after it's been loaded and verified.

1//
2class CPlayer
3{
4//...
5 CPlayer(BITMAP* spr): xpos(0), ypos(0), speed(5), rot(0), sprite(spr) {}
6//...
7};
8 
9int main()
10{
11// ... setup and stuff
12 
13 BITMAP* buffer = create_bitmap(640, 480);
14 BITMAP* player = load_bitmap("test.bmp" , NULL);
15 
16 if (!buffer || !player) {
17 // problem loading resources
18 if (buffer) {destroy_bitmap(buffer);}
19 if (player) {destroy_bitmap(player);}
20 return 0;
21 }
22 
23 CPlayer MyPlayer(player);
24 
25// .... rest of code
26//

jclarkrichards
Member #10,404
November 2008

Hey, I'm not a C++ programmer, but here's a simple program in C that I wrote that might help with your problem. This basically just takes an image and rotates it. You rotate the image by passing different angles to the itofix() function depending on the key you pressed. For the image to be facing to the left, the angle has to be 0, facing right, the angle is 128, facing up, angle is 64, and facing down, angle is 192. Hope this helps. I should also mention that the initial direction of the sprite in this example is facing left. So, if your sprite initially faces another direction, you may just need to rearrange then angles in the rotateImage() function.

1#include <allegro.h>
2 
3int x;
4int y;
5int angle = 0;
6 
7void rotateImage(void); //function prototype
8 
9BITMAP *buffer;
10BITMAP *image;
11 
12int main()
13{
14 allegro_init();
15 install_keyboard();
16 set_color_depth(16);
17 set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640,480,0,0);
18 image = load_bitmap("myimage.bmp", NULL);
19 //place the image in the middle of screen
20 x = SCREEN_W / 2;
21 y = SCREEN_H / 2;
22 buffer = create_bitmap(SCREEN_W, SCREEN_H);
23 draw_sprite(buffer, image, x, y); //draw image in initial position
24
25 while(!key[KEY_ESC]) //main loop
26 {
27 rotateImage();
28 //clear the image frame
29 clear_to_color(buffer, makecol(0,0,0));
30
31 //draw the image frame
32 rotate_sprite(buffer, image, x, y, itofix(angle));
33
34 blit(buffer, screen, 0,0,0,0, buffer->w, buffer->h);
35 }
36
37 destroy_bitmap(buffer);
38 destroy_bitmap(image);
39 return 0;
40}
41END_OF_MAIN()
42 
43void rotateImage()
44{
45 if(key[KEY_LEFT])
46 {
47 angle = 0;
48 }
49
50 if(key[KEY_RIGHT])
51 {
52 angle = 128;
53 }
54
55 if(key[KEY_UP])
56 {
57 angle = 64;
58 }
59
60 if(key[KEY_DOWN])
61 {
62 angle = 192;
63 }
64}

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Use code tags please. You can edit the post with the pen and paper icon that appears in your post, and in the post editing page there is a help button that explains the rest of the forum markup tags.

<code>
Code goes here...
</code>

Simsimius
Member #6,017
July 2005

Alright, I'll try it now.

I'll mark this thread as "solved".

____
There's no place like 127.0.0.1

Go to: