gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > SPRITES - MOVEMENT AND SPEED

#147649 - noodle - Tue Dec 25, 2007 11:16 am

Hi,
I am relatively new to the concept of sprite animation and movement and was wondering if someone could help.

Basically the problem is that I have 10 sprites and I want to be able to move them at different speeds from each other.

how can this be acomplished?

Thanks.

#147651 - Kyoufu Kawa - Tue Dec 25, 2007 12:49 pm

For each sprite, you could have a delta variable. Each game cycle, you increase the sprite's position by that value. That way, one sprite can move one pixel per frame, while another could move three, for example.

That's the simple way.

#147668 - tepples - Tue Dec 25, 2007 9:31 pm

Another tip: Learn fixed-point arithmetic.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#147674 - noodle - Tue Dec 25, 2007 11:52 pm

Kyoufu Kawa wrote:
For each sprite, you could have a delta variable. Each game cycle, you increase the sprite's position by that value. That way, one sprite can move one pixel per frame, while another could move three, for example.

That's the simple way.


Hi,
Thanks for replying to my post,
I am wondering, how is the delta variable calculated and also is it a floating point or an integer?
I have noticed a lot of sprite structs use 'int' to hold sprites x and y coords.

One more thing, are the sprites movement calculated every 1/60th sec video frame or every program loop?

Thanks Again

#147676 - tepples - Tue Dec 25, 2007 11:56 pm

noodle wrote:
I am wondering, how is the delta variable calculated and also is it a floating point or an integer?

The GBA doesn't have floating-point hardware. So to fake floating-point math, a lot of programs use an integer and treat it as having a constant denominator of 256 or something.

Quote:
One more thing, are the sprites movement calculated every 1/60th sec video frame or every program loop?

Sprites are moved every program loop. But this is the same as every video frame because most programs call a "wait for vertical blank" at one point in the loop.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#147677 - noodle - Wed Dec 26, 2007 12:24 am

tepples wrote:

The GBA doesn't have floating-point hardware. So to fake floating-point math, a lot of programs use an integer and treat it as having a constant denominator of 256 or something.

Would this mean that the value stored against say sprite.x is not the actual sprite pixel coordinate on the map or screen?

Quote:

Sprites are moved every program loop. But this is the same as every video frame because most programs call a "wait for vertical blank" at one point in the loop.

wouldn't the waiting for vertical blank halt the program loop until vblank has occured, hence making the whole program really slow?

I tried something before where I updated the sprite movement every vblank, however this meant that the sprite was limited to a movement of 60 pixels every second.

I also considered applying more than one pixel per movement of the sprite, however this messed up the collision detection routine and the sprite was able to travel through other objects in its way.

Thanks again.

#147679 - tepples - Wed Dec 26, 2007 12:54 am

noodle wrote:
tepples wrote:

The GBA doesn't have floating-point hardware. So to fake floating-point math, a lot of programs use an integer and treat it as having a constant denominator of 256 or something.

Would this mean that the value stored against say sprite.x is not the actual sprite pixel coordinate on the map or screen?

The coordinate stored in OAM is the actual coordinate on the screen. But some games use a different coordinate system within the game logic and then translate that to screen coordinates. (See Coordinates (mathematics) and Coordinate system on Wikipedia.) This is especially common in games where objects smoothly speed up, slow down, or turn corners, or in games where the screen scrolls.

Quote:
Quote:
Sprites are moved every program loop. But this is the same as every video frame because most programs call a "wait for vertical blank" at one point in the loop.

wouldn't the waiting for vertical blank halt the program loop until vblank has occured, hence making the whole program really slow?

That's exactly what you want. "Really slow" to a computer is "playably fast" to a human.

Quote:
I also considered applying more than one pixel per movement of the sprite, however this messed up the collision detection routine and the sprite was able to travel through other objects in its way.

That's strange. How wide were the objects?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#147698 - noodle - Wed Dec 26, 2007 1:30 pm

Thanks Tepples,

For the purposes of sprite speed and collision, I am attempting to create a sort of partical/bouncyball program where each ball moves at different speeds from each other and has the ability to bounce off each other also.


Quote:
Quote:
I also considered applying more than one pixel per movement of the sprite, however this messed up the collision detection routine and the sprite was able to travel through other objects in its way.

That's strange. How wide were the objects?


The objects are 8x8 each in size, I tried adding +10 to the pixel coord to make it move more quickly, but it took the ball to the opposite side of the other side of the ball which I wanted to test collision, so effectively the ball passed straight through the other one as the collision test was not applied. the only way for the collision test to work is to apply a maximum one pixel at a time to the sprite coord, which defeats the purpose of adding +10 for speed reasons.

Regarding the waiting for vertical blank, this would mean that the pixels will move one pixel every 1/60th second which means it would take 4 seconds to move from left to right of screen. I wanted some balls to move a lot faster than this but at the same time work with the collision detection.

Thanks again, really appreciate the response.

#147699 - Peter - Wed Dec 26, 2007 1:49 pm

noodle wrote:
so effectively the ball passed straight through the other one as the collision test was not applied.

You don't only have to test the target position, but the entire way from point A to B, where A is the current position and B being the destination.

Also a common mistake is to quit the collision detection routine once a hit has been detected, which is wrong. You need to test all relevant objects and return the one with the nearest distance.
_________________
Kind Regards,
Peter

#147701 - noodle - Wed Dec 26, 2007 3:16 pm

Peter wrote:
noodle wrote:
so effectively the ball passed straight through the other one as the collision test was not applied.

You don't only have to test the target position, but the entire way from point A to B, where A is the current position and B being the destination.

Also a common mistake is to quit the collision detection routine once a hit has been detected, which is wrong. You need to test all relevant objects and return the one with the nearest distance.


This is one way, although I only require to test for collisions till ball hits the next, at which point it will deflect. Also not sure about the 'target position' how does this work?

Till now I have seen code which looks like below:
Sprite.x += xdirection (which seemed to relate directly to the pixel coords on screen).

However the creates some issues:
If I update per program loop/cycle, the sprite will be too fast, If I update per vblank, the sprite will be too slow. If I add more than one pixel at a time then collision detection doesnt work).

#147703 - Peter - Wed Dec 26, 2007 4:58 pm

noodle wrote:
If I add more than one pixel at a time then collision detection doesnt work).

And this is exactly what I meant. If you move an object from point A to B, you must test for collision along the way from A to B and not only B.
_________________
Kind Regards,
Peter

#147704 - Dwedit - Wed Dec 26, 2007 5:08 pm

If you're using equality with a single value to test collisions, you're doing it wrong. Normally collision detection is done by testing if two bounding boxes overlap.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."

#147722 - noodle - Wed Dec 26, 2007 11:42 pm

Dwedit wrote:
If you're using equality with a single value to test collisions, you're doing it wrong. Normally collision detection is done by testing if two bounding boxes overlap.

nope, not using that.
I am testing using the bounding box overlap.

#147724 - noodle - Wed Dec 26, 2007 11:49 pm

Peter wrote:
noodle wrote:
If I add more than one pixel at a time then collision detection doesnt work).

And this is exactly what I meant. If you move an object from point A to B, you must test for collision along the way from A to B and not only B.

Thanks Peter,

I think that some kind of sub pixel coordinates are required here, not sure if this is the most common way to apply sprite movment (i.e is this same method used in all games/applications by professionals).
Tepples' fixed point math suggestion seems to be the most tempting as it would make sub-pixel movement likely. I would have to store the coordinates as some other value instead of pixel/map coords.

#147727 - tepples - Thu Dec 27, 2007 12:02 am

Forza for Xbox runs the game engine four times per frame and then updates the screen. If you want to have wicked-fast bouncing balls, that might be the best option.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#147730 - Peter - Thu Dec 27, 2007 1:01 am

noodle wrote:
I think that some kind of sub pixel coordinates are required here

Hm?

I thought the problem is this:
A = start position
B = target position
c = collidable object (a wall, whatever)
O = your object

1st Frame:
Code:

A  c   B
O

... advance position of object...

next Frame:
Code:

A  c   B
.......O

O moved to B without colliding with c. This was your problem, wasn't it?
_________________
Kind Regards,
Peter

#147782 - noodle - Thu Dec 27, 2007 10:06 pm

noodle wrote:
Hi,
I am relatively new to the concept of sprite animation and movement and was wondering if someone could help.

Basically the problem is that I have 10 sprites and I want to be able to move them at different speeds from each other.

how can this be acomplished?

Thanks.


My problem is as it has always been, to be able to move the sprites at different speeds from each other, while still being able to allow collision detection to work correctly, which would require that the sprite moves no more than a maximum of 1 pixel per cycle.

I have noticed in your interesting 'text diagram' that you have used the terms 'first frame' and 'next frame'. Wouldnt the collision detection be occuring every program cycle, regardless of how much the sprite has travelled in between frames?

Always open to new suggestions
Thanks

#147790 - tepples - Thu Dec 27, 2007 11:35 pm

Why would collision detection require that objects move no more than 1 pixel per cycle, unless the objects are only 1 pixel in size? Say I have a 16-pixel ball moving at 8 pixels per game cycle toward a 16-pixel ball sitting still. Even if the two balls penetrate by 8 pixels, it's still possible to "rewind" the objects by part of a game cycle and compute their trajectories.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#147801 - keldon - Fri Dec 28, 2007 4:15 am

I did write a post in this topic about it but the iron set off the RCD circuit breaker before I managed to post (destroying it without a decent firefox restore). But in short you can calculate the collision point between two moving circles with a number of calculations where the code will not allow one object to pass through another!

See Circle-Circle Collision and Reaction (Frame-Independent) for more info on how it can be achieved - the Jar files contain demo's however I'm in the process of switching over servers!

#147809 - phonymike - Fri Dec 28, 2007 8:47 am

I would just run the detection loop for every one pixel a sprite moves. so if you jump from position 1 to position 10 on one frame, then run the routine 10 times then update the sprite.

frame 1: increase position variable by 1, detect collision. increase position by 1, detect etc. say a collision happens at position 7, break the loop, and you're left with 7. now call any routine such as sound.

frame 2: update the on screen sprite to position 7.

at least that's a quick and dirty way of doing it. I figure a maximum speed of 10 pixels per frame per sprite would take no more time to calculate than it would to copy an 8x8 sprite to memory, it may sound slow but would pry take 0.05% of the time you have per frame.

#147813 - Peter - Fri Dec 28, 2007 11:14 am

phonymike wrote:
I would just run the detection loop for every one pixel a sprite moves. so if you jump from position 1 to position 10 on one frame, then run the routine 10 times then update the sprite.

I would create an area of the tavelled distance. For example if your object moves from x=10 to x=20 and its height=5, you can represent the movement as a rectangle and thus perform basic rectangle overlapping tests. This becomes a bit tricker if the movement is diagonal.

Personally I prefer using lines, as those are very easy to test against all kind of shapes.
_________________
Kind Regards,
Peter

#147816 - noodle - Fri Dec 28, 2007 12:46 pm

tepples wrote:
Why would collision detection require that objects move no more than 1 pixel per cycle, unless the objects are only 1 pixel in size? Say I have a 16-pixel ball moving at 8 pixels per game cycle toward a 16-pixel ball sitting still. Even if the two balls penetrate by 8 pixels, it's still possible to "rewind" the objects by part of a game cycle and compute their trajectories.

Thanks for response guys.

One pixel per cycle is too fast, adding more than this per cycle would be really wierd, not to mention the fact that collision detection would miss items from pixel coordinates in between (see previous posts for explanation on this).

Probably something I have missed, however cannot figure out what is missing if I didn't know it existed, kind of a catch 22 situation.
I have looked for some kind of tutorials on sprite speed and have had no luck.

Thanks again

#147817 - noodle - Fri Dec 28, 2007 12:49 pm

I think that my problem might be relating to the difference between the 'frame rate' and program cycle and how often to call the collision detection.

Will have one last shot at it here.

Also wondering what 'equality' and 'delta' are (mentioned in previous posts).

Thanks

#147819 - keldon - Fri Dec 28, 2007 1:40 pm

Calculating it using the method I described is [probably] quicker than doing ten checks per frame; but nonetheless is still not so error prone and restricting.

One thing I should point out is that geometric shapes have various [simple] methods for collision detection. The code I have written is in Java for detecting moving circle/circle collisions (and also giving their position).

Also when looking for your solution you should search for search terms based on what you are trying to detect a collision for. For example if you are searching for moving circle/circle then google for "moving circle collision detection" or something - but like I said if that's what you're after I have code for it! Mine also returns the point (or distance) of collision, which allows you to calculate multiple bounces within a single frame (correctly).

#147826 - bpoint - Fri Dec 28, 2007 7:55 pm

Just my two cents...

The proper method of doing frame-independant collision detection is to create a line (or ray) from the previous frame's position to the current frame's position. You can then check collision with an object as being a line, circle, or box doing simple 2D math calculations.

For example, if you built a wall as a line in 2D space, then you can check to see if a player collided with the wall by doing a line-to-line intersection calculation. Typically the intersection check also returns the point of intersection so you can determine how far the player should advance in order to keep him from passing through the wall. A box collision check would just test each of the four sides of the box, each as a line by itself.

Most of these calculations are simple algebra. A very good site describing pretty much all of the algorithms you'll need can be found at Paul Bourke's Geometry Page, specifically "The intersection of a line with another line (2D)".

#147829 - keldon - Fri Dec 28, 2007 8:47 pm

I don't see moving circle/circle!

#147832 - bpoint - Fri Dec 28, 2007 9:06 pm

That's because you don't actually do a circle-to-circle collision test, you do a line-to-circle test, where the line is the midpoint of the circle from the previous frame to the current frame.

When you find the intersection point, you simply offset your original circle from the intersection by it's radius to obtain a position that does not collide with the circle you are testing.

#147848 - keldon - Sat Dec 29, 2007 1:28 am

Well I just sketched it out and it's essentially the exact same calculations I use to find the point of collision too, I just think of it as a circle/circle as that's how I draw it.

And that is a good link so I think I'll bookmark that site too.

#147902 - Ruben - Sun Dec 30, 2007 4:06 am

I personally think that the problem would be with your collision detection script, not the amount of pixels moved. You said that if you move 10 pixels p/f, collision doesn't work, right? I think that this is what you are doing:
Code:
//DON'T do this...
void CheckCollision(int dir) {
 int x=0, y=0;
 if(!(PixelsMoved++ & 7)) {
  if(dir == 0) x++;
  else y++;
 }
 sprite.x += x, sprite.y += y;
}

//Better to check against co-ordinates, not pixels moved
u8 CheckCollision(sprite* spr) {
 int x, y;
 u8* map_col = (u8*)map_col_table;
 
 //assuming that X and Y are pixel coordinates...
 x = spr->x>>3, y = spr->y>>3;
 
 //... return the nearest tile coordinate
 return map_col[y*mapsize_x+x];
}


OK, maybe a bit rushed but I think you get the idea. Hope it helped.

BTW: edited 4 times or so. I do too many mistakes in a rush...

#147929 - noodle - Sun Dec 30, 2007 6:16 pm

Hi,
After chewing through most of the posts and looking for the correct answer, I have found that there are many different ways of dealing with the issue of sprite speed.

Regarding the adding more than one pixel per cycle/frame, Do I take this as a given and that it must be done?

Assuming yes then this would mean that I would have to adjust and modify the basic collision bounding box technique (or introduce some support code to allow proper action to be performed after collision detected).

For Example, If the increment was 10 then this might take the sprite inside a solid brick wall, so some sort of back-tracking code might be needed to compliment the standard boundary box testing.

Is the above quite a common method or is it something rarely used?

Thanks

#148134 - Miked0801 - Wed Jan 02, 2008 8:00 pm

We pretty much lamed out on most of the games I worked on. Just did a current position box/box or circle/circle check and if colided solid, backup to previous tic's position (or side along the surface if requested.) We've rarely had issues with missed collisions or such. We just try to make sure that our characters never relatively travel faster that the size of their collisions boxes. Cheesy, but cheap and effective.

#148168 - Ruben - Thu Jan 03, 2008 7:47 am

noodle wrote:
Is the above quite a common method or is it something rarely used?

Thanks

I'm quite sure that this method is used a lot in tile-collision based games, but since you didn't specify whether your planning to check collision against the the circle or the tile, I kinda just figured it would be easier to explain tile collision testing.

For example (assuming you are using pixel co-ordinates):
Code:
//I supose this is your current script
u8 CheckCollision(sprite* spr) {
 return map_col_table[spr->map_y * map_size_x + spr->map_x];
}

void UpdateObjects() {
 spr* Obj0, Obj1;
 if(IS_PRESSED(KEY_UP)) { //Move sprite up
  spr->screen_pos_y--;
  spr->pixels_moved_y++;
 }
 if(IS_PRESSED(KEY_DOWN)) { //Move sprite down
  spr->screen_pos_y++;
  spr->pixels_moved_y++;
 }
 if(IS_PRESSED(KEY_LEFT)) { //Move sprite left
  spr->screen_pos_x--;
  spr->pixels_moved_x++;
 }
 if(IS_PRESSED(KEY_RIGHT)) { //Move sprite right
  spr->screen_pos_x++;
  spr->pixels_moved_x++;
 }
 
 if(spr->pixels_moved_x & 7) spr->map_x++;
 if(spr->pixels_moved_y & 7) spr->map_y++;
}

Although this would work for movements that are power of 2's, it wouldn't work for other numbers. This would be the same script re-written 'correctly':
Code:
//I supose this is your current script
u8 CheckCollision(sprite* spr) {
 return map_col_table[spr->map_y * map_size_x + spr->map_x];
}

void UpdateObjects() {
 spr* Obj0, Obj1;
 if(IS_PRESSED(KEY_UP)) { //Move sprite up
  spr->screen_pos_y--;
  spr->pixels_moved++; //Do this just incase you want to change p/f
 }
 if(IS_PRESSED(KEY_DOWN)) { //Move sprite down
  spr->screen_pos_y++;
  spr->pixels_moved++;
 }
 if(IS_PRESSED(KEY_LEFT)) { //Move sprite left
  spr->screen_pos_x--;
  spr->pixels_moved++;
 }
 if(IS_PRESSED(KEY_RIGHT)) { //Move sprite right
  spr->screen_pos_x++;
  spr->pixels_moved++;
 }
 spr->map_x = spr->screen_pos_x >> 3; //>> 3 = /8
 spr->map_y = spr->screen_pos_y >> 3;
}


That would be the way to detect tile-based collisions. I'm sure there's tutorials on how to be able to detect round/circle collisions... actually, I think someone already mentioned one but i can't be f*cked to read it lol...

#148170 - Ruben - Thu Jan 03, 2008 8:00 am

Oops... forgot a part:

To detect if your sprite went outside a collision area, you'd just retract it's current X/Y position by doing this:

Code:
void CorrectCollision(sprite* spr) {
 int amount;
 amount = 8 - (spr->x & 7);
 spr->x -= amount;
 amount = 8 - (spr->y & 7);
 spr->y -= amount;
}

void UpdateObjects() {
 spr* Obj0, Obj1;
 if(IS_PRESSED(KEY_UP)) {
  spr->screen_pos_y -= spr->speed;
  spr->pixels_moved++;
 }
 if(IS_PRESSED(KEY_DOWN)) {
  spr->screen_pos_y += spr->speed;
  spr->pixels_moved++;
 }
 if(IS_PRESSED(KEY_LEFT)) {
  spr->screen_pos_x -= spr->speed;
  spr->pixels_moved++;
 }
 if(IS_PRESSED(KEY_RIGHT)) {
  spr->screen_pos_x += spr->speed;
  spr->pixels_moved++;
 }
 spr->map_x = spr->screen_pos_x >> 3; //>> 3 = /8
 spr->map_y = spr->screen_pos_y >> 3;
 
 if(CheckCollision(spr)) CorrectCollision(spr);
}


OK. Lame way to do it but I REALLY can't be f*cked typing up the other one 'cause I already typed up a formula and it's hot as hell here...

#148174 - Peter - Thu Jan 03, 2008 9:54 am

And this does work? I don't see code to handle movements from right to left, or down to up. To perform collision resolving correctly, you also need more than the current position, you need a vector that describes the travelled way.

Also for a player object for example, usually it's not enough to just test one point of the object for collision. If you test only the feet, the player might ran into a wall with his head.

Plus, what most people don't think about is when an object gets pushed out through the collision resolving process, it might be pushed into another collision.

BTW, personally I would also throw away the screenpos concept, it overcomplicates the whole thing imo. Just use worldcoordinates for everything and right before rendering convert them to screencoords. But that's probably just a personal taste.
_________________
Kind Regards,
Peter