Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Any Direction in 2d??

This thread is locked; no one can reply to it. rss feed Print
Any Direction in 2d??
dylan sack
Member #6,590
November 2005

So, I have no idea even how to go about this.. So sorry I don't really have any source to put, so this'll mainly be a logic or pseudo-code thread. I've seen this done before, so i'm not just insane, how would I go about making a 2d topdown game (not iso) that you can face and shoot in any direction? The most desirable would probably be, your character would always face your cursor(which would be a gun aim picture thingy reticule) and you could turn around like that, and left mouse click would shoot. W would be forward, S be back, a and d be strafe and you know. Just kinda throwing that out there to get the feels of the controls I guess. Anyway, back to the point. The only way i've thought of for this would be manually handelling the shoot function for every single possible direction you could face.. but that's.. that just can't be the only way, heh.. Would it have to involve some 3d aspects? I don't understand how I could update a bullet and read it to see which direction to shoot in any way in 2d. Am I crazy and the games I saw that did this were not 2d but iso or something??

EDIT: http://www.gamedev.net/community/forums/topic.asp?topic_id=390918 <-- that game does it and it's 2d topdown, so I can't be insane. But I still don't know how to go about it :P
I was kinda thinking about it, and i'd need to maybe somehow assign a "front" to the picture, and then some kind of function where the "front" always points towards the cursor. Any help or advice would be appreciated

CursedTyrant
Member #7,080
April 2006
avatar

If I understand correctly, you need something like this:

void CEnemy::Move(float nX, float nY)
{
    float Angle = atan2(nY-Y, nX-X)*180.0f/M_PI;
    
    X += cosf(Angle*M_PI/180.0f)*Speed;
    Y += sinf(Angle*M_PI/180.0f)*Speed;
}

What it does is make the CEnemy object move towards the (nX; nY) coordinates.

And in the bullet object you just do something like:

CBullet::CBullet(float nAngle)
{
    Angle = nAngle;
    //other stuff here
}
void CBullet::Move()
{
    X += cosf(Angle*M_PI/180.0f)*Speed;
    Y += sinf(Angle*M_PI/180.0f)*Speed;
}

That should work fine.

---------
Signature.
----
[My Website] | [My YouTube Channel]

Mark Oates
Member #1,146
March 2001
avatar

yes, what Mr. Tyrnat said. It all basically comes down to angles, sin, and cosine. I recommend this tutorial to help clear things up:
http://pixwiki.bafsoft.com/mags/5/articles/circle/sincos.htm

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Jonatan Hedborg
Member #4,886
July 2004
avatar

Or you could do it slightly less confusing (and faster) by storing the angle as radians instead...

void CEnemy::Move(float nX, float nY)
{
    float Angle = atan2(nY-Y, nX-X);
    
    X += cosf(Angle)*Speed;
    Y += sinf(Angle)*Speed;
}

you could also use pure vector maths for this problem;

1void CEnemy::Move(float nX, float nY)
2{
3 float tx,ty,length;
4 
5//This gets a vector pointing at the nx,ny
6 tx = x-nx;
7 ty = y-ny;
8 
9//Gets the length of the vector through pyth's
10 length = sqrt(tx*tx+ty*ty);
11 
12//Normalizes the vector, making it's total length 1. You should also make sure length isnt 0
13 tx = tx/length;
14 ty = ty/length;
15 
16//Update the position
17 X += tx*Speed;
18 Y += ty*Speed;
19}

Untested, but that ought to work too.

Not sure which one would be faster, but both has some merits.

And as for the bullet, once again, store the angle in the native form:

CBullet::CBullet(float nAngle)
{
    Angle = nAngle*M_PI/180.0f;
    //other stuff here
}
void CBullet::Move()
{
    X += cos(Angle)*Speed;
    Y += sin(Angle)*Speed;
}

Or use vectors

CBullet::CBullet(float nAngle)
{
    tx = cos(nAngle);
    ty = sin(nAngle);
    //other stuff here
}
void CBullet::Move()
{
    X += tX*Speed;
    Y += tY*Speed;
}

In this case vectors would be faster and simpler (heck, you can even store speed right in the tx/ty pair by multiplying them with it in the start).

dylan sack
Member #6,590
November 2005

Wow, three answers, talk about service ^_^ Thanks for the help guys i'm going to go try to work that into my game right now.

Johan Halmén
Member #1,550
September 2001

There are only a few basic facts when talking about 2D coordinates and directions. The rest derives from the basics. In Allegro graphics you have one coordinate growing from left to right, usually called x, and another growing from top to down, usually called y. The rest, especially the direction stuff, is up to you, as I see it. You can use radians or Allegro angle units for the direction. You can decide that zero be to the right, to the left, up or down. And you can choose that growing direction is like rotating clockwise, or counter clockwise. I guess there's a standard used in games, but I've never bothered to figure out that. I might use the stuff differently in different games, because I'm a lousy coder what comes to reusing stuff. Whatever you choose, it affects the trigonometric formulae, which should be clear for you.

Jonathan's code said:

void CBullet::Move()
{
    X += cos(Angle)*Speed;
    Y += sin(Angle)*Speed;
}

It works, but it computes the cos(Angle)*Speed each time the bullet moves. It should compute it only when either Angle or Speed is altered.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Years of thorough research have revealed that the red "x" that closes a window, really isn't red, but white on red background.

Years of thorough research have revealed that what people find beautiful about the Mandelbrot set is not the set itself, but all the rest.

dylan sack
Member #6,590
November 2005

Ok tried a few of the methods out, and i'm having weird errors about converting to fixed or whatever. Can anyone explain this?

1class Player
2{
3public:
4 int x, y;
5 int p_facing;
6 int p_hp;
7 void move();
8};
9Player player;
10 
11 
12void move(float nX, float nY)
13{
14 int Speed;
15 float Angle = atan2(nY-player.y, nX-player.x); // conversion from `float' to non-scalar type `fix' requested
16
17 player.x += cos(Angle)*Speed;//conversion from `float' to non-scalar type `fix' requested
18 player.y += sin(Angle)*Speed;//conversion from `float' to non-scalar type `fix' requested
19}

I'm pretty new to using any kind of maths in games and just coding games in general.. (i'm only a freshman in algebra 2 >.<) So if I did something realllly dumb try to forgive me ;)

imaxcs
Member #4,036
November 2003

Include cmath in your source-file:

#include <cmath>

I hope that helps...

CursedTyrant
Member #7,080
April 2006
avatar

BTW, If you're using floats use cosf and sinf. Like this:

float Angle = atan2(nY-player.y, nX-player.x);

player.x += cosf(Angle)*Speed;
player.y += sinf(Angle)*Speed;

You could also use atan2f if you really want to.

---------
Signature.
----
[My Website] | [My YouTube Channel]

Johan Halmén
Member #1,550
September 2001

dylan said:

So if I did something realllly dumb try to forgive me

We forgive you, but I don't see the point in it ::)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Years of thorough research have revealed that the red "x" that closes a window, really isn't red, but white on red background.

Years of thorough research have revealed that what people find beautiful about the Mandelbrot set is not the set itself, but all the rest.

dylan sack
Member #6,590
November 2005

Ok so all these variables are kinda confusing me, what exactly needs to be added to if say I wanted to make a keypress have you rotate to the right? I experimented around with adding values to some of the variables and nothing happened, given I never really took the time to fully understand the code you guys gave me, i'm just kinda using it :P But I donno, I think i'm just generally confused about it still.

1class Player
2{
3public:
4 int x, y;
5 int p_facing;
6 int p_hp;
7 void move();
8};
9Player player;
10 
11 
12void move(float nX, float nY)
13{
14 float X,Y;
15 float Speed;
16 float Angle = atan2f(nY-player.y, nX-player.x);
17
18 X += cosf(Angle)*Speed;
19 Y += sinf(Angle)*Speed;
20 
21
22}

Johan Halmén
Member #1,550
September 2001

There's no use in trying to get our examples work if you don't understand them. If it is that hard, you simply have to step back some steps and do something simpler. And try to understand every step.

I could point out that your class has members x and y but the member function move()declares the variables X and Y, which you use in the moving. Well, it doesn't work. And if you don't get it, don't waste time in trying to get it work. Spend that valuable time instead in studying more basic things.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Years of thorough research have revealed that the red "x" that closes a window, really isn't red, but white on red background.

Years of thorough research have revealed that what people find beautiful about the Mandelbrot set is not the set itself, but all the rest.

Mark Oates
Member #1,146
March 2001
avatar

Quote:

And if you don't get it, don't waste time in trying to get it work. Spend that valuable time instead in studying more basic things.

Johan speaks the truth. But I believe that you can figure this part out. Let me try and help.

If you change your player class to contain these variables it should make things much more clear:

1class player_class
2{
3public:
4 float x;
5 float y;
6 float angle; // the player's angle, or heading
7 float speed; // the player's current speed
8 
9 float turn_speed; // a constant
10 float walk_speed; // a constant
11 
12 player_class()
13 {
14 x = 0.0f;
15 y = 0.0f;
16 angle = 0.0f;
17 speed = 0.0f;
18 
19 turn_speed = 0.001f; // don't know about this, you might want to try a larger or smaller number
20 walk_speed = 5.0f; // expirement with this one too
21 }
22 
23 void turn_right() { angle += turn_speed; }
24 void turn_left() { angle -= turn_speed; }
25 void walk() { speed = walk_speed; }
26 void stop_walking() { speed = 0.0f; }
27 
28 void update()
29 {
30 x += cosf(angle) * speed;
31 y += sinf(angle) * speed;
32 }
33};
34 
35player_class player;

so in your logic loop do something like

if (key[KEY_RIGHT]) player.turn_right();
if (key[KEY_LEFT]) player.turn_left();
if (key[KEY_UP]) player.walk();
if (!key[KEY_UP]) player.stop_walking();

player.update();

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

dylan sack
Member #6,590
November 2005

Thanks for the help, and I know I should go back and really figure it out and understand, which i'm about to do. I was just really excited and kinda rushed because I wanted to see some real progress in my game. Feels like i'm getting somewhere (even though I got a lot of help on this part of it :P ) The thing is, if I declare x and y as floats, then my compiler says when I try to draw the sprite, you can't draw the sprite at a float location. So maybe I could do another x and y that are ints and make them equal to the float x and y's and draw them there? Thanks for all the help btw guys.

Mark Oates
Member #1,146
March 2001
avatar

float f;
int i = (int)f;

you have to 'cast the float as an int'

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

dylan sack
Member #6,590
November 2005

Thanks for all the help, it works perfectly. I'm pretty sure I can figure out rotating the sprite as it turns by myself. It works perfectly besides that. I'm going to sit out a while and just read the code over and over untill I viualize how it perfectly works in my head.

Mark Oates
Member #1,146
March 2001
avatar

haha, ah yes. I know the feeling. It's like taking a dump after being constipated for a long time. "I just want to take a break for a while" :P

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

dylan sack
Member #6,590
November 2005

Yea, but then you get constipated again coughrotatespriteisdumbcough and you must learn from your past and get through it on your own :O

Mark Oates
Member #1,146
March 2001
avatar

otatespriteisdumb?... Oates' Sprite is Dumb!? :o >:(>:( YOU DON'T LIKE MY AVATAR!!!?!? >:(>:(>:(

...

:'(

just kidding.
;D

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

dylan sack
Member #6,590
November 2005

hey I have one last question :P, how come this rotate sprite thingy isn't working. It compiles perfectly, and I have no idea why it's not rotating with the turning function. By all means it should, I know the problem isn't it getting drawn cause I tried it with the x and y a little offset and it showed up, it just doesn't rotate...

1class player_class
2{
3public:
4 float x, y;
5 float speed; // players current speed
6 float angle; // players angle or direction facing
7
8 float turn_speed; // a constant
9 float walk_speed; // a constant
10 BITMAP *robot;
11 bool boost;
12
13 player_class()
14 {
15 x = 0.0f;
16 y = 0.0f;
17 angle = 0.0f;
18 speed = 0.0f;
19
20 turn_speed = .07f;
21 walk_speed = 1.0f;
22 }
23 
24void turn_right() { angle += turn_speed; }
25void turn_left() { angle -= turn_speed; }
26void walk() { speed = walk_speed; }
27void stop_walking() { speed = 0.0f; }
28 
29void update()
30 {
31 x += cosf(angle) * speed;
32 y += sinf(angle) * speed;
33 }
34
35};
36player_class player;
37//rotate stuff
38void rotate() {
39 int x = int(player.x);
40 int y = int(player.y);
41 fixed angle = fixed(player.angle);
42}
43
44 
45void logic() {
46 //more rotate stuff
47
48 if (key[KEY_D]) {player.turn_right(); rotate();}
49 if (key[KEY_A]) {player.turn_left(); rotate();}
50 if (key[KEY_W]) {player.walk();}
51 if (!key[KEY_W]) {player.stop_walking();}
52
53 player.update();
54
55 if (key[KEY_SPACE]) {player.boost = true;}
56 if(player.boost == true) {player.walk_speed = 2;}
57 if (!key[KEY_SPACE]) {player.boost = false;}
58 if(player.boost == false) {player.walk_speed = 1;}
59}

I have no idea why coding/life hates me..

Mark Oates
Member #1,146
March 2001
avatar

what does your rotate sprite function look like?

--
Visit CLUBCATT.com for cat shirts, cat mugs, puzzles, art and more <-- coupon code ALLEGRO4LIFE at checkout and get $3 off any order of 3 or more items!

AllegroFlareAllegroFlare DocsAllegroFlare GitHub

Johan Halmén
Member #1,550
September 2001

Your code said:

//rotate stuff
1: void rotate() {
2:     int x = int(player.x);
3:     int y = int(player.y);
4:     fixed angle = fixed(player.angle);
}

This function seems to be just a separate function, not a member of the class, right? In this function you declare the variables x, y and angle. These variables live only inside the function. In line 2, x is declared and gets its value from player.x, which implies that player is a global object. player.x itself is not changed, not truncated or rounded or anything. After line 4 all three variables, x, y, and angle come to a peaceful death and won't affect anything else in your program.

In my former post I tried to point out a very similar thing. It looks like you're not clear on how functions work and where local variables and global variables live (the scope of the variables).

What comes to casting, I have a bad habbit of not casting floats to ints before drawing. I get lots of compiler warnings, but they are only warnings. The program still works.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Years of thorough research have revealed that the red "x" that closes a window, really isn't red, but white on red background.

Years of thorough research have revealed that what people find beautiful about the Mandelbrot set is not the set itself, but all the rest.

Tobias Dammers
Member #2,604
August 2002
avatar

Always treat warnings as errors. Best case, they are a sign that you are debugging (and have commented out some code). Worst case, they are causes for real errors or bugs. Semi-bad case, they show bad style.
That said, a plain cast, IIRC, just cuts off the fractional part of the float. This is not a problem in this case, but when it comes to tile-based collision detection, for example, it can produce nasty bugs. Both -0.8f and 0.8f evalutate to 0. I prefer throwing in an extra floor():

float f = some_number_you_choose;
int i = (int)floor(f);

@dylan: You need to understand the concept of 'scope'. The scope of a variable is the part of the code during which it is valid. There are, basically, 4 kinds of variables in C++:
- Global variables. These are declared outside of all {} braces, that is, outside of all functions, classes, structs, etc. They are valid everywhere in the program. You can mark them as "static", making them available in the current .cpp only.
- Local variables. Declared inside a block of {} braces, typically a function body, but occasionally, a loop body. Loop variables declared on-the-fly, e.g. in a for() loop, are also valid inside the loop only. On some rare occasions, a {} pair is inserted into a block of code for the sole purpose of creating a scope for a few variables. Usually though, such practise is a sign of bad style, and a dedicated function would be more appropriate.
- Member variables. Variables declared inside a class or struct. Each instance of the class will produce its own instance of the variable, and by default, the variable will only be valid in member functions of the class. Public member variables can be accessed through the . operator (e.g. foo.bar) and the shortcut -> operator (e.g. foo->bar which is equivalent to (*foo).bar).
- Function parameters. A named function parameter works like a variable and is valid for the entire function body.

Any two variables may have the same name, provided their scopes don't overlap. It is even possible to have variables with identical names whose scopes overlap (but are not identical), but this causes a whole bunch of problems and is generally strongly advised against.

Example:

1int i_am_global;
2 
3void some_function(int param1) {
4 int a;
5
6 for (int i = 0; i < 10; ++i) {
7 if (rand())
8 a += param1;
9 } // scope of int i ends here.
10 i_am_global = do_something_with(a);
11} // scope of int a and int param1 ends here.
12 
13void some_other_func(int param1) {
14 /* note that param1 in this function and param1 in some_function() are completely
15 unrelated. */
16 i_am_global = do_something_with(param1);
17 // ...i_am_global, however, refers to the same variable throughout the entire program.
18} // scope of param1 ends here

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

dylan sack
Member #6,590
November 2005

Ok considered what you guys said a bit, and have revised my code.. now it er.. doesn't work still, but i'm pretty sure I stay inside my scopes hah. Anyway, i'm going to post my code again, how about this time you could all just like.. hint towards what i'm supposed to do so I can figure out something on my own for once heh. Anyway, at least i've learned a lot from this about coding in general, right?

1int boostenergy = 100;
2 
3class player_class
4{
5public:
6 float x, y;
7 float speed; // players current speed
8 float angle; // players angle or direction facing
9
10 float turn_speed; // a constant
11 float walk_speed; // a constant
12 BITMAP *robot;
13 bool boost;
14
15 player_class()
16 {
17 x = 0.0f;
18 y = 0.0f;
19 angle = 0.0f;
20 speed = 0.0f;
21
22 turn_speed = .06f;
23 walk_speed = 1.0f;
24 }
25 
26void turn_right() { angle += turn_speed; }
27void turn_left() { angle -= turn_speed; }
28void walk() { speed = walk_speed; }
29void stop_walking() { speed = 0.0f; }
30 //ROTATE junk.
31void rotate_right() {
32 int x = int(x);
33 int y = int(y);
34 fixed angle = fixed(angle);
35 int turn_speed = int(turn_speed);
36 angle += turn_speed;
37 rotate_sprite(buffer, robot, y, x, angle);
38}
39void rotate_left() {
40 int x = int(x);
41 int y = int(y);
42 fixed angle = fixed(angle);
43 int turn_speed = int(turn_speed);
44 angle -= turn_speed;
45 rotate_sprite(buffer, robot, x, y, angle);
46}
47 
48void update()
49 {
50 x += cosf(angle) * speed;
51 y += sinf(angle) * speed;
52 }
53
54};
55player_class player;
56 
57
58 
59void logic() {
60 //rotate logic
61 if (key[KEY_D]) {player.turn_right(); player.rotate_right();}
62 if (key[KEY_A]) {player.turn_left(); player.rotate_left();}
63 if (key[KEY_W]) {player.walk();}
64 if (!key[KEY_W]) {player.stop_walking();}
65
66 if (key[KEY_SPACE]) {player.boost = true;}
67 if(player.boost == true && boostenergy > 0) {player.walk_speed = 2; boostenergy -=1;}
68 if (!key[KEY_SPACE] || boostenergy <= 0) {player.boost = false;}
69 if(player.boost == false) {player.walk_speed = 1; boostenergy+=1;}
70
71 player.update();
72}
73 
74 
75void draw() {
76 int x = int(player.x);
77 int y = int(player.y);
78 acquire_screen();
79 draw_sprite(screen, buffer, 0,0);
80 draw_sprite(buffer, bg, 0, 0);
81 draw_sprite(buffer, player.robot, x, y);
82 release_screen();
83}
84//in main loop
85 clear_keybuf();
86 draw();
87 logic();
88 player.update();

Onewing
Member #6,152
August 2005
avatar

Quote:

doesn't work still

What "doesn't work still"? The rotation? What is it doing?

Anyways, where did you learn to do classes? I'm not implying anything, other than I get the feeling you don't completely understand them.

BTW, weren't you working on a side-scoller? How did that turn out?

------------
Solo-Games.org | My Tech Blog: The Digital Helm

Go to: