It is currently Thu Nov 14, 2019 8:04 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 9 posts ] 
Author Message
PostPosted: Sat May 25, 2019 7:30 pm 
Offline
User avatar

Joined: Sat May 25, 2019 6:49 pm
Posts: 3
Location: home.chr
Sorry if this ends up a little bit confusing, but I have issue with my project.
Before I start, I just wanna tell you that:
- Yes, I'm using c65, and also I'm using NESDOUG's lib (props to him btw)

I want to make a simple animation of a character walking, don't worry, you don't have to explain to me what metapsprites are because I understand how this works, the main problem is, I want to make every single frame to play instead of static, or three frames at the same time playing

(I already have the sprites set in sprites.h, so there are no problems)

Code:
sprid = oam_meta_spr(temp_x, high_byte(Char1.y), sprid, RoundWLK1);
sprid = oam_meta_spr(temp_x, high_byte(Char.1y), sprid, RoundWLK2);
sprid = oam_meta_spr(temp_x, high_byte(Char1.y), sprid, RoundWLK3);


When I executed that code, three frames appeared at THE SAME time! So, I tried usng the delay(); command, but that made the gameplay lag alot and also it didn't fixed the issue.


Top
 Profile  
 
PostPosted: Sat May 25, 2019 7:56 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2594
Location: DIGDUG
You're going to want a variable "hero_walk_count" that counts up as you walk (maybe on pad R and pad L).

I can't remember how many frames you want each step of the animation... let's say 10.

So when hero_walk_count goes >= 10, reset it to 0, and increase the hero_walk_frame. If that is over the max, reset that to zero too.

Lots of options next...
have either an if, else if, else if, else checking each hero_walk_frame... or a switch/case... or an array of const pointers to all the animations using hero_walk_frame as the index. pass that pointer to the oam_meta_spr() function.

so, the if else version would be like...
if (hero_walk_frame == 0)
frame_ptr = RoundWLK1;
else if (hero_walk_frame == 1)
frame_ptr = RoundWLK2;
else frame_ptr = RoundWLK3;

oam_meta_spr(...frame_ptr instead of the sprite array)

*edit, removed &

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


Top
 Profile  
 
PostPosted: Sat May 25, 2019 9:51 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11432
Location: Rio de Janeiro - Brazil
One common mistake people make when first programming games or animations is adding a global delay to control timing... As you realized, that doesn't work because it delays *everything*, while what you want is to control the pace of each aspect of the game independently.

Unlike the typical programming tasks they teach in college/courses, that work linearly by receiving input and processing that input to generate an output, a game is a continuous thing with several things happening at the same time. You can't have your CPU busy taking care of one single task until that task is done, because nothing else in the game will do anything while the CPU is busy with that one task.

The trick is to stop thinking linearly, and start thinking in steps. Games work in steps. Each frame is a step, so every frame, everything in your game must advance exactly one step. If every frame everything takes one step, the player will have the impression that everything is animating in parallel.

What constitutes a step depends on the object/aspect which is being updated. In the case of animations, you can't advance every frame because that'd be too fast, so one step needs to be less than a frame. In modern computers you could use fractional numbers to define a slower frame rate, but on 8-bit platforms fractions aren't as straightforward. One simple solution is to do what
dougeff said, and give everything that needs a delay its own counter. Every frame you update the counter by one, and once the counter is up, you reset it and advance one step.


Top
 Profile  
 
PostPosted: Sun May 26, 2019 4:51 am 
Offline
User avatar

Joined: Sat May 25, 2019 6:49 pm
Posts: 3
Location: home.chr
dougeff wrote:
You're going to want a variable "hero_walk_count" that counts up as you walk (maybe on pad R and pad L).

I can't remember how many frames you want each step of the animation... let's say 10.

So when hero_walk_count goes >= 10, reset it to 0, and increase the hero_walk_frame. If that is over the max, reset that to zero too.

Lots of options next...
have either an if, else if, else if, else checking each hero_walk_frame... or a switch/case... or an array of const pointers to all the animations using hero_walk_frame as the index. pass that pointer to the oam_meta_spr() function.

so, the if else version would be like...
if (hero_walk_frame == 0)
frame_ptr = RoundWLK1;
else if (hero_walk_frame == 1)
frame_ptr = RoundWLK2;
else frame_ptr = RoundWLK3;

oam_meta_spr(...frame_ptr instead of the sprite array)

*edit, removed &


Thanks! It really did worked. So, do I have to make a new variable for everything new that I want to have programmed in the game?


Top
 Profile  
 
PostPosted: Sun May 26, 2019 5:33 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2594
Location: DIGDUG
Every Sprite object will need a set of variables.

X position,Y position, type of object, state (which can include animation), health, etc.

6502 isn't very good at allocating memory on the fly, so all these should be in predetermined sized arrays. so you might want another array... is_active[ ] to only process the objects that are currently in use.

So, then the question is, do I need an array of object structs? or ONE object struct of arrays?

While an array of structs might be easier code to read and program, it is slower than ONE structure with arrays. Here's a discussion...

viewtopic.php?f=2&t=17465

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


Last edited by dougeff on Sun May 26, 2019 5:42 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Sun May 26, 2019 5:41 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21688
Location: NE Indiana, USA (NTSC)
All other things being equal, a structure of arrays is the more convenient of the two on 6502. This lets you keep (say) a pool of 8 general-purpose objects and use the X register to pass around the index into the object array.

Code:
NUM_ACTORS = 8

.bss
actor_x: .res NUM_ACTORS
actor_y: .res NUM_ACTORS
actor_frame: .res NUM_ACTORS
actor_frame_sub: .res NUM_ACTORS

.code
some_routine:
  lda actor_y,x
  ; lots of things omitted
  rts


Or in C:
Code:
#define NUM_ACTORS = 8

unsigned char actor_x[NUM_ACTORS];
unsigned char actor_y[NUM_ACTORS];
unsigned char actor_frame[NUM_ACTORS];
unsigned char actor_frame_sub[NUM_ACTORS];

void some_routine(unsigned char self) {
  unsigned char aa = actor_y[self];
  /* lots of things omitted */
}


This is because 6502's indexed mode uses a small (8-bit) variable offset from a large (16-bit) fixed base address. This contrasts with Z80's modes using registers IX and IY, 68000's d(A0) mode, and analogous modes on MIPS and ARM, which use a small fixed offset from a large variable pointer. If you have a large pointer, an array of structures becomes more practical. But because 6502 registers aren't large enough to hold a pointer, code falls back to using a pointer on zero page with (dd),Y indirect indexed mode, which is not quite as fast as aaaa,X or aaaa,Y indexed modes used with SOA.

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Sun May 26, 2019 5:49 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2594
Location: DIGDUG
What do ya know, there's even a wikipedia article...

https://en.m.wikipedia.org/wiki/AOS_and_SOA?wprov=sfla1

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


Top
 Profile  
 
PostPosted: Sun May 26, 2019 3:44 pm 
Offline
User avatar

Joined: Sat May 25, 2019 6:49 pm
Posts: 3
Location: home.chr
So... my bless became a curse! I used NESDOUG's example. It worked, but not in the way that I wanted. The first frame is overlapping other frames. It's working pretty well, the problem is with the overlapping.

I think that I know what's happening, this "oam_meta_spr" command is spawning more sprites in the screen, what should I do to "destroy" these sprites, so there's no overlapping?

edit: I used "Generic.x + 20" to move the sprite a little bit to the right, and it actually worked but the animation was a little bit to the right and the static sprite was still there. hmm


Last edited by Taluigi on Sun May 26, 2019 4:18 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Sun May 26, 2019 4:17 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2594
Location: DIGDUG
The neslib library has 2 options.

oam_clear() at the start of each frame will blank all sprites. Then you call your Sprite functions. That way, only the sprites written this frame will show.

Or.
sprid = 0; at the start of the frame, and then push all active sprites. Then call oam_hide_rest() to blank just the unused part of the Sprite buffer.

I prefer oam_clear() at the start of the frame, because if something goes wrong with the other method (like slowdown) and you don't reach the oam_hide_rest(), you could have extra sprites that don't get blanked, and that could look weird.

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


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: MSN [Bot] and 9 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