Jump to content
IGNORED

Simulating gravity (or, how do you do "timing"?)


Recommended Posts

Some unfocused, wishy-washy questions for you!

 

I want to have a "jumping" sprite, and I'm confused on the best way to do it. For other languages/machines I've always done gravity kind of like this:

 

 YVelocity = 10	 ; Set initial jumping velocity

UpdatePlayerPos:   ; Called every frame
 PlayerY = PlayerY - YVelocity
 YVelocity = YVelocity - 1

 

So that for the first frame after jumping the player moves up 10 pixels, then 9,8,7,6,5,4,3,2,1,0 then -1,-2,-3,-4,-5,-6....

Giving a nice looking effect of gravity as a player jumps, then lands.

 

Implementing this for the 2600 is harder then I imagined - I can't move a player 10 pixels a frame, that'd look crazy. The way I imagine would look correct would be that I move the player up one pixel after 1 frame, then up one pixel again after 2 frames, then 3,4,5,6,7,8,9,10 then 9,8,7,6,5,4,3,2,1 frames (maybe not 10 frames... that'd be quite a pause mid air)

 

I've been trying to implement something like this, but it's looking verrry messy (codewise), and not very impressive. Its easy when everything on screen moves 1 pixel per frame, but I can't figure out how timing can be done nicely.

 

Are there any "standard" ways to do timing for games? How would you do it if some sprites need to move quickly (say, 3 pixels per frame) and others need to move slowly (say, 1 pixel every 3 frames)? How do you handle these timing issues?!

Link to comment
Share on other sites

If you're using Batari BASIC, you can use fixed point math to give sub-pixel accuracy. That would allow your character to jump, say, with 3 pixels of force against a gravity of 0.2 pixels per frame. This is more or less what I did in Deimos Lander. (I posted the code in the homebrew thread.)

 

If you're not using bBASIC, then it's still fairly easy to do. Use one variable to represent the number of pixels, and another variable to represent the sub-pixels. For example, you can use 10 subpixels for every pixel. Every frame you add or subtract the gravity number from the subpixel counter. If the subpixel counter goes over 10 (or whatever number you use to represent a full pixel) you add to the pixel counter and subtract 10 from the subpixel counter. If the subpixel counter goes below -10, you subtract one from the pixel counter and add 10 to the subpixel counter.

 

Does that help? :)

Edited by jbanes
Link to comment
Share on other sites

Yup, subpixel is the way to go. Then you just have to define a constant gravity value and add that to your vertical speed every frame.

 

BTW: Depending on what you want to do, it is often also good to use friction as an opposite power to gravity (or other forces like e.g. thrust). Just divide the speed by some power of 2 factor and subtract that from the current speed. That way you will also limit the maximum speed (when gravity becomes equal to subtracted value) which might become useful (e.g. for collision detection).

 

I did that in Thrust and my three SWOOPS! minigames and IMO the result "feels" very good.

Edited by Thomas Jentzsch
Link to comment
Share on other sites

Use one variable to represent the number of pixels, and another variable to represent the sub-pixels. For example, you can use 10 subpixels for every pixel. Every frame you add or subtract the gravity number from the subpixel counter. If the subpixel counter goes over 10 (or whatever number you use to represent a full pixel) you add to the pixel counter and subtract 10 from the subpixel counter.

 

Hi, I just want to make sure I understand. I've been working on something similar and came up with a frame counter to decrement before moving my sprite. So, subpixel is the number of frames to skip before you change the pixel, or scanline, position. Correct?

 

Jim

Link to comment
Share on other sites

Use one variable to represent the number of pixels, and another variable to represent the sub-pixels. For example, you can use 10 subpixels for every pixel. Every frame you add or subtract the gravity number from the subpixel counter. If the subpixel counter goes over 10 (or whatever number you use to represent a full pixel) you add to the pixel counter and subtract 10 from the subpixel counter.

 

Hi, I just want to make sure I understand. I've been working on something similar and came up with a frame counter to decrement before moving my sprite. So, subpixel is the number of frames to skip before you change the pixel, or scanline, position. Correct?

 

Jim

Not necessarily. A frame counter will only delay an integer number of frames. A subpixel counter can move a non-integer number, and also a value greater than 1. For example, 0.4375 pixels per frame, or around 2.29 frames per pixel would appear as usually moving every second frame but sometimes it would take 3 frames. 1.0675 pixels per frame would appear as moving one pixel per frame except every 16th frame it would move two.

 

Using subpixel movement gives more flexibility and makes the games appear smoother.

Link to comment
Share on other sites

Hi, I just want to make sure I understand. I've been working on something similar and came up with a frame counter to decrement before moving my sprite. So, subpixel is the number of frames to skip before you change the pixel, or scanline, position. Correct?

No exactly. Your solution doesn't allow e.g. movements every 1.5 frames or smooth accelerations.

 

Fractional postioning/movement uses two variables (Lo/Hi) for each direction (X/Y). The Hi variables describe the position on the screen. The Lo variables describe the subpixel position.

 

Each frame you add a constant 16bit value to the 16bit positions (e.g. For moving every 1.5th frame, you add 256/1.5 = ~171).

 

For accelerated movement (e.g. due to gravity), you don't use speed constants, but add acceleration constants to speed variables (which will finally be added to the position variables).

 

Hope that makes sense.

Link to comment
Share on other sites

Hi, I just want to make sure I understand. I've been working on something similar and came up with a frame counter to decrement before moving my sprite. So, subpixel is the number of frames to skip before you change the pixel, or scanline, position. Correct?

Hi Jim! If I understand you correctly, the answer is 'no'. You're describing a similar, but altogether different concept. What you're doing is effectively reducing the framerate by skipping frames between updates.

 

What I described is a form of fixed point math, but with an arbitrary amount of overflow. If you define the maximum to be a number divisible by 10, then you pretty much have fixed-point math. For example, if the second byte overflows at 100, then:

 

Byte1: 3

Byte2: 20

 

Is equivalent to 3.20. After 3.99 would come 4.00 or 4 and 0 for bytes 1 and 2.

 

The advantage to the fixed-point method is that your accurately computing the exact position at the full framerate of 60FPS. This makes high speeds work a lot better as you have more updates each second.

 

That way you will also limit the maximum speed (when gravity becomes equal to subtracted value) which might become useful (e.g. for collision detection).

I usually use a preset terminal velocity myself, but I imagine that including air friction would make it feel more accurate. :)

Link to comment
Share on other sites

Ahh.. Thank you all for your responses. This is great stuff.

 

I did a search and found Kirk's demo for calculating the subpixel. It is also a great example of how to do exact horizontal positioning.

 

http://alienbill.com/2600/cookbook/subpixel.html

 

Question, would you ever want to use a subpixel for moving horizontally?

 

 

 

Jim

Link to comment
Share on other sites

Question, would you ever want to use a subpixel for moving horizontally?

Sure!

 

If you're going to make the character move at a fixed rate, then there isn't much point to it. However, if you want the character to accelerate, it works great. An example of this sort of acceleration is 7800 Centipede. The longer you hold the contoller, the faster your "gnome" moves. In shoot-em' ups, using acceleration (usually with some sort of cap on the maximum velocity) can give the controls a much better feel than using a fixed rate of motion. In platformers it can make the character seem to move much more realistically. (Think: Sonic the Hedgehog) In car games, it can make a car seem more like a car. ;)

Link to comment
Share on other sites

Yup, subpixel is the way to go. Then you just have to define a constant gravity value and add that to your vertical speed every frame.

 

BTW: Depending on what you want to do, it is often also good to use friction as an opposite power to gravity (or other forces like e.g. thrust). Just divide the speed by some power of 2 factor and subtract that from the current speed. That way you will also limit the maximum speed (when gravity becomes equal to subtracted value) which might become useful (e.g. for collision detection).

 

I did that in Thrust and my three SWOOPS! minigames and IMO the result "feels" very good.

 

Very interesting and quite illuminating I might add. I've never used friction in my 'space' games because I never thought it was needed, but I can see how it does add a nice touch to the control. It's good to have some damping in the system even on these 8 bit physics systems. I will always remember this and the next time I'm working on a simple physics system I'm going to experiment with this!! If you don't do this you will get that very watery feel but maybe you really want a little mud. ;-)

Link to comment
Share on other sites

Very interesting and quite illuminating I might add. I've never used friction in my 'space' games because I never thought it was needed, but I can see how it does add a nice touch to the control.

Sometime it is good and sometimes it is essential.

 

E.g. my Cave1k minigame wouldn't work without damping. If you assemble it without FRICTION enabled, the helicopter control becomes almost impossible.

Link to comment
Share on other sites

I think I got it. In the attached example, I am using subpixels to handle Y positioning, and rotation.

Ok, the code is messy, and I haven't figured out all the switch-draw variations out there, the subpixeling I understand. :cool:

And now I can see how using a subpixel to control the X position would be useful.

 

 

Left and Right rotate, Up moves forward. Down does nothing.

 

player.zip

 

Thanks!

 

Jim

Link to comment
Share on other sites

That subpixel movement is awesome!

 

I don't know if this is related, but is there a sneaky clever way to do animation timing? I am using two variables PlayerFrame and FrameCounter - I set FrameCounter to some value, decrement it each frame, and when it is zero increment the PlayerFrame and reset the counter. Then in a horribly complex and ugly looking "switch" statement I point the SpritePointer to the correct frame (depending if the player is walking or jumping etc)

 

Is this the best way to handle animation timing? The 16 bit math cleaned up my movement code nicely, and now my animation code looks really ugly :)

 

Any animation tips?!

 

Thanks!

Link to comment
Share on other sites

I don't know if this is related, but is there a sneaky clever way to do animation timing? I am using two variables PlayerFrame and FrameCounter - I set FrameCounter to some value, decrement it each frame, and when it is zero increment the PlayerFrame and reset the counter. Then in a horribly complex and ugly looking "switch" statement I point the SpritePointer to the correct frame (depending if the player is walking or jumping etc)

With the 2600's constraints, I don't think there's any "right" way to do most things. However, it sounds like what you want is tables. i.e. You'd have a list of pointers to the animation frames somewhere in memory. When you increment the animation frame, you simply do a lookup in the table to get the address of the sprite. Since I assume you'll want to use 2 bytes instead of 1 for each address (unless ALL your sprites fit within a single page of memory) you'll want to shift the counter value left by one bit. This will multiply the value by 2. Store the value in X, use a LDA Absolute,X to get the first byte, INX to point to the second byte, then a LDA Absolute,X again to transfer the second byte. Bam. All done.

 

Of course, this won't work in bBASIC. Switch-style statements are your only option unless you're willing to embed a bit of assembler.

 

Is that what you had in mind?

Link to comment
Share on other sites

That's a cool idea - could you give me an example of what the lookup table would look like? How do you do a table of addresses for dasm?

 

I'm learning assembler, so I haven't had a look at batari basic - it kind of seems like cheating ;)

 

In regards to the actually "counters" for animation, is that how you do your timing for animation frames - Store a timer counter variable and decrement each loop, when it's zero increment the animation frame?

 

I can see what you mean about there not being a "right" way. I've only got about 3/4 of game logic working in the vertical blank area and I've run out of cycles! That's why I'm looking for more efficient methods of doing things like timing. My first efforts are working, but aren't very elegant :)

Link to comment
Share on other sites

That's a cool idea - could you give me an example of what the lookup table would look like? How do you do a table of addresses for dasm?

No assembler handy to test, but it would look something like this:

 

Frame		  = $80
SpriteAddress1 = $81; Low byte of the address!
SpriteAddress2 = $82; High byte of the address!

[...]

; Load the frame of animation. This would normally be done by incrementing or decrementing "Frame"
 LDA #1 ; Load the second frame - remember that this is zero based! 0 is 1, 1 is 2, and 2 is 3!
 STA Frame

[...]

; Load the address
 LDA Frame
 ROL; When you shift left in binary, you effectively multiply by 2
 TAX
 LDA MySpriteFrames,X
 STA SpriteAddress1
 INX
 LDA MySpriteFrames,X
 STA SpriteAddress2

[...]

; Somewhere in the kernal...
; Someone set us up the sprite!
kernal
 LDA (SpriteAddress1),X; Load the current line stored in X
 STA GRP0
 STA WSYNC
 DEX
 BNE kernal; As long as we've got lines to render, keep going!


[...]

; Lookup table for the different frames. The code below stores the address with the low-byte first, high-byte second.
MySpriteFrames
.byte #<MySpriteFrame1, #>MySpriteFrame1
.byte #<MySpriteFrame2, #>MySpriteFrame2
.byte #<MySpriteFrame3, #>MySpriteFrame3

[...]

org $FF00

MySpriteFrame1
; sprite data
MySpriteFrame2
; sprite data
MySpriteFrame3
; sprite data

 

Presumably you're doing this because (unlike the example) your frames are either out-of-order, of an unpredictable size, cross pages, or are otherwise too weird to compute the offset. Also, that's pretty generic up above. If you know certain things about your data, there are probably a billion and one ways to optimize it.

 

In regards to the actually "counters" for animation, is that how you do your timing for animation frames - Store a timer counter variable and decrement each loop, when it's zero increment the animation frame?

Without knowing a lot more about what you're doing than I want to know, I can't really give you any good advice. All I can say is that if you animate sprites at less than 60fps, then what you're doing sounds fine. My only suggestion would be to make sure you're using a unified counter rather than having a different counter for each sprite. Of course, your game may not allow for a unified counter, so take that with a grain of salt. :)

Link to comment
Share on other sites

I had my "walk cycles" using an offset to the sprite data, but special other frames (like jumping, and ducking) will be much nicer using the lookup table approach I think.

 

And no, I wasn't using a unified counter to control any of the animation - thats a good idea!

 

Thanks for your help

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...