It is currently Wed Dec 13, 2017 8:17 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: Sprite Animation Example
PostPosted: Sun Oct 22, 2017 10:14 am 
Offline

Joined: Tue Oct 17, 2017 12:13 am
Posts: 7
Hey All,

I apologize for a newbie question. I'm working through the Nerdy Nights tutorials, using NESASM (I know people like ca65 or whatever but for now using NESASM). I can understand how to build meta-sprites, move them around, all of that.

I'm having a bit of trouble understanding how to animate sprites.

I've gone through the forums, I've found topics here (http://forums.nesdev.com/viewtopic.php?f=2&t=14852) and elsewhere, like here (http://nintendoage.com/forum/messagevie ... adid=33378)

It's just a bit hard going through posts or pastebin files without an example to also work through. I enjoy the Nerdy Nights tutorial because I can take the example ASM files and fiddle around with them until I completely understand them. It's more difficult to create new ones from scratch.

Does anyone have an example file that I could use to take a look at a simple animation in action? I'm sure that'd be a huge help.

Thanks for any help, this seems like an awesome community.


Top
 Profile  
 
PostPosted: Sun Oct 22, 2017 11:45 am 
Offline
User avatar

Joined: Thu Mar 31, 2016 11:15 am
Posts: 219
I'm pretty sure tepples's nrom example contains animation code: https://pineight.com/nes/nrom-template-0.05.zip


Top
 Profile  
 
PostPosted: Sun Oct 22, 2017 12:34 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1867
Location: DIGDUG
Each animation frame should be an array of tile numbers and relative position of each tile.

Then you have an array of pointers to each frame.

Then, use a bunch of define statements, to enumerate the array. Like

HeroWalk1= 0
HeroWalk2= 1
HeroWalk3= 2

Etc, through every animation. (You could use null = 0, if you want that to = no animation)

Then you can reference HeroWalk1 in your code. Ie. HeroAnimation = HeroWalk1.
LDA #HeroWalk1
STA HeroAnimation


Your code (at the end of each frame's code) should build the OAM buffer, based on the current animation definitions of each object.

Try using NES Screen Tool's metasprite tool. And find the bits in Shiru's neslib for drawing a metasprite to the OAM buffer.


Edit, sorry for the 'should' statements. You are free to plan your own game your own way.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Last edited by dougeff on Mon Oct 23, 2017 7:21 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Mon Oct 23, 2017 12:15 am 
Offline

Joined: Tue Oct 17, 2017 12:13 am
Posts: 7
dougeff wrote:
Edit, sorry for the 'should' statements. You are free to plan your own game your own way.

Don't apologize! I'm an assembly n00b and I'm posting a basic question. I really appreciate the advice on the best way to structure my programs.

Also, pubby, thank you so much for that link! That's basically what I'm looking for. I should probably learn ca65 anyways, seems that's the most popular assembler.

This will still take some work, but I'm going to crank on it tonight, will post when I run into trouble. Thank you guys so much.

EDIT: After a few hours of tinkering, I think I'm finally starting to get it! I'm not there yet but I understand what needs to be done.


Top
 Profile  
 
PostPosted: Mon Oct 23, 2017 6:20 am 
Offline
User avatar

Joined: Thu Sep 15, 2016 6:29 am
Posts: 460
Location: Denmark (PAL)
It's very difficult to tell you "how to animate", as there is no end to the different ways you can do it. Your imagination is really the limit.

dougeff's example is a traditional way to do it, and a good way to start. But other approaches could be simply using algorithms to change out sprite references, etc.

When you make your first animation, I would suggest you just hard code the data you'll be changing.

Here's a very simple example - let's say you have your player character which has an X position and an Y position somewhere in RAM. You'll want to add a timer that counts down every frame so you can time your animation (eg. dec AnimationTimer), and then every frame you'll write your character's sprite to your frame buffer, simply write it the way you'd normally add a sprite (ie. first byte is the Y position, second is the sprite index, third is the attributes, and fourth is the X position), but when you're writing the sprite index, make a branch based on the value of the animation timer (eg. and #$4 and then bne/beq) - on one condition you write one frame of animation, on the other condition you write the other frame.

Once you have that working, you can expand your code to read which sprites to write from a data array similar to the ones dougeff mentioned, and you'll probably want to have multiple animations, with multiple frames of animations in each. On top of that, each frame of animation will probably have to include multiple sprites, too, unless none of your objects are bigger than 8x16 pixels. :P With stuff like this, I'd think you just need to add these features one at a time, to make sure everything works as it should. If you just start out making a complete animation framework for your game, it'll be much harder figuring out where things break (because they definitely will)


Top
 Profile  
 
PostPosted: Mon Oct 23, 2017 7:05 am 
Offline

Joined: Tue Oct 17, 2017 12:13 am
Posts: 7
Sumez wrote:
When you make your first animation, I would suggest you just hard code the data you'll be changing.

Here's a very simple example - let's say you have your player character which has an X position and an Y position somewhere in RAM. You'll want to add a timer that counts down every frame so you can time your animation (eg. dec AnimationTimer), and then every frame you'll write your character's sprite to your frame buffer, simply write it the way you'd normally add a sprite (ie. first byte is the Y position, second is the sprite index, third is the attributes, and fourth is the X position), but when you're writing the sprite index, make a branch based on the value of the animation timer (eg. and #$4 and then bne/beq) - on one condition you write one frame of animation, on the other condition you write the other frame.

Once you have that working, you can expand your code to read which sprites to write from a data array similar to the ones dougeff mentioned, and you'll probably want to have multiple animations, with multiple frames of animations in each. On top of that, each frame of animation will probably have to include multiple sprites, too, unless none of your objects are bigger than 8x16 pixels. :P With stuff like this, I'd think you just need to add these features one at a time, to make sure everything works as it should. If you just start out making a complete animation framework for your game, it'll be much harder figuring out where things break (because they definitely will)


Thank you for your response!

You're right, I wasn't making it easy on people with such a general question.

And I think your explanation is one of the best ones I've actually seen, thanks for using less technical language.

You say to hardcode the data I'll be changing.. that's exactly what I did last night (stayed up all night hah)! I feel like I've got a really good handle on what I need to be changing, how to index metasprite arrays properly, what variables I need to implement. I'm currently using 8x8 sprites, but am starting to realize 8x16 may have been a better idea. No matter!

I think I've got it from here, hopefully. At least for this subject.

Really appreciate everyone here. Crazy that I got three great responses in less than a day. Thanks guys.


Top
 Profile  
 
PostPosted: Mon Oct 23, 2017 9:27 am 
Offline
User avatar

Joined: Sun May 27, 2012 8:43 pm
Posts: 1311
If you can tolerate my shitty newbie code, here's the animation scheme I used for a project I got started on.

I drew a bunch of my animations separately, first being kind of greedy with space used up. Then I realized I can shift over some parts of animations to better pack into tiles. Finally, I made note of tile re-use, and removed even more tiles. I ended up with this CHR layout, which clearly isn't complete, but has a lot of the animation in almost half the space it originally had:

Image

For the animation system, this code snippet describes individual animation frame layouts, the "metasprites". Each line more or less maps to a single sprite's attributes, but the positions are relative to a position specified during drawing. This is for the player's first standing frame:

Code:
pl_mapping_stand1:
   .byte   <-32, $00, %00000000, <-8
   .byte   <-32, $01, %00000000, 0
   .byte   <-24, $10, %00000000, <-8
   .byte   <-24, $11, %00000000, 0
   .byte   <-16, $20, %00000000, <-8
   .byte   <-16, $21, %00000000, 0
   .byte   <-8,  $30, %00000000, <-8
   .byte   <-8,  $31, %00000000, 0
   .byte   MAP_END


MAP_END is a "magic number" which I use to indicate the end of a mapping, as there is no predefined size for the mapping. I think I use $80 for it.

Following that, the different mappings are sequenced as animations:

Code:
pl_anim_stand:
   ; Animation total frame count
   .byte    2
   ; Animation repeat frame #
   .byte    0
   ; Pointer to first mapping
   .addr   pl_mapping_stand1
   ; Number of frames to display this mapping, and pad byte
   .byte    32, 0
   ; Pointer to second mapping
   .addr   pl_mapping_stand2
   ; Number of frames to display this mapping, and pad byte
   .byte   28, 0


And finally, a lookup table for animations, so I may refer to them by number:
Code:
pl_anim_num_map:
   .addr   pl_anim_dummy
   .addr   pl_anim_stand
   .addr   pl_anim_run
   .addr   pl_anim_jump
   .addr   pl_anim_fall


metasprite.asm takes a pointer argument to a metasprite mapping (the first table I defined) and will draw it at the specified X and Y coordinates. I won't paste the whole thing here since that will make the post rather large.

If you follow player_render.asm you can see how animations are sequenced, and how calls to metasprite.asm are made based on it (several levels of indirection used for the animations).

I hope maybe this is a helpful reference, though there will certainly be room for improvement (as there always is).


Top
 Profile  
 
PostPosted: Mon Oct 23, 2017 10:07 pm 
Offline
Site Admin
User avatar

Joined: Mon Sep 20, 2004 6:04 am
Posts: 3487
Location: Indianapolis
I had shared my animation code, I just noticed the link was broken though so now it's an attachment. It's not a complete example that you can just build, though.
http://forums.nesdev.com/viewtopic.php?f=10&t=7331


Top
 Profile  
 
PostPosted: Wed Nov 22, 2017 6:25 am 
Offline

Joined: Tue Oct 17, 2017 12:13 am
Posts: 7
Hey guys!!

I want to thank you all for your help again.

I've had this working for a little while, wanted to show what I've got so far. I loved seeing all of the examples you shared, but I ended developing my own, undoubtedly inferior method, because I'll learn more from my own code even if it's not as good.

Anyhoo, here's what I've got!

Image
The method I use animated both the player and an NPC (although theres an error and the NPC standing frame isn't correct), using the same code. It changes the sprite based on whether it is moving or not, and depending on the direction. It can also change the palette depending on the frame, but that isn't shown here. I'm fairly proud of this, had to put it into a jump table because my branch loop got too long.


Top
 Profile  
 
PostPosted: Wed Nov 22, 2017 7:49 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1867
Location: DIGDUG
how did you get those variables to show up on the bottom right? is that a lua script?

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Nov 22, 2017 8:34 am 
Offline

Joined: Tue Oct 17, 2017 12:13 am
Posts: 7
dougeff wrote:
how did you get those variables to show up on the bottom right? is that a lua script?

I'm ashamed to say I've never used lua before! The only languages I know are Basic derivatives (QBasic as a kid, VBA for work), GML (Game Maker Language), some Java, and R. I don't even know C! Once I finish an NROM game 6502 will be added to that list :)

To actually answer your question, it's just some hacky assembly right before my RTI. In the CHR file I put 0-F in the last row for sprites, put them in a db directive to look up from. That's why you actually see a character sprite appear for temp_z, cause it goes higher than 16.

Code:
  LDA temp_x
  LDA sprNumbers, x
  STA $0281
  LDX temp_y
  LDA sprNumbers, x
  STA $0285
  LDX temp_z
  LDA sprNumbers, x
  STA $0289
  LDX player_moving
  LDA sprNumbers, x
  STA $028D

;PPU clean up code goes here

  RTI

;;;;;;;;;;;;;;;;;;;;;;;;
sprNumbers:
  .db $F0,$F1,$F2,$F3,$F4,$F5,$F6,$F7,$F8,$F9,$FA,$FB,$FC,$FD,$FE,$FF


Again, I'm a bad programmer, sure there's a way better way to do this. Just my simplest solution.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: Google [Bot] and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group