It is currently Mon May 20, 2019 7:54 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 13 posts ] 
Author Message
PostPosted: Fri Apr 12, 2019 5:48 am 
Offline

Joined: Sun Jun 30, 2013 7:59 am
Posts: 37
Okay, this is one I feel like I'm going to feel very silly about, but I just can't fathom it out.

I initialise the first sprite in OAM to an x_pos of 8. In main, I just check to see if right is pressed, and if so, increase x_pos by 4.
Why is it (visually, and in memory viewer) that the sprite can take seemingly any position, regardless of whether it is a multiple of 4? If the x_pos starts at 8, and I'm only ever increasing it in increments of 4 per frame, how is it possible that the sprite can move this way? FCEUX's hex editor shows $0203 as holding 0x52, 0x9D, and so on....

I've removed everything I thought could possibly affect it, like NMI flags and whatnot, encase it was due to some kind of misunderstanding regarding something else. Here's the code:

After clearing memory, and waiting twice for vblank to let PPU warm up:
Code:
setup_first_screen:
init_sprite_0:
  LDA #$08
  STA $0200
  STA $0203
  LDA #$00
  STA $0201
  STA $0202

JSR fill_palettes

init_rendering:
  LDA #%00010000      ; enable sprites
  STA PPUMASK
 
  LDA #%10000000      ; enable NMIs
  STA PPUCTRL

main:
  JSR wait_nmi
  JSR read_joy
@if_right:
  LDA buttons
  AND #%00000001
  BEQ +
  LDA $0203
  ADC #$04
  STA $0203
+ JMP main

NMI:
  pushregs

  INC nmi_flag

dma_transfer:
  LDA #$00
  STA OAMADDR
  LDA #$02
  STA OAMDMA
 
  pullregs
  RTI

read_joy is the ring counter from the wiki, and wait_nmi is just:
Code:
wait_nmi:
  LDA nmi_flag
- CMP nmi_flag
  BEQ -
  RTS


Thanks. I really didn't want to post this out of embarrassment but it's just eluding me completely. I was in the middle of trying to code a DW1 style walk, but I can't see that being fruitful until I address this bit of silliness :oops:


Top
 Profile  
 
PostPosted: Fri Apr 12, 2019 5:58 am 
Offline
Formerly WheelInventor
User avatar

Joined: Thu Apr 14, 2016 2:55 am
Posts: 2015
Location: Gothenburg, Sweden
Maybe you can trap it in a breakpoint that checks for a write to $203 that leaves a modulo of 4 to determine where it happens - Like so?

$203 % 4 != 0

I'm completely new to using C-style breakpoint conditions in the debugger, so someone more used to it may want to confirm or correct.

_________________
http://www.frankengraphics.com - personal NES blog


Last edited by FrankenGraphics on Fri Apr 12, 2019 6:01 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Fri Apr 12, 2019 6:01 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11345
Location: Rio de Janeiro - Brazil
Looks like you're not clearing the carry flag before the addition, so you could sometimes be adding 5 instead of 4, if the carry happens to be set.


Top
 Profile  
 
PostPosted: Fri Apr 12, 2019 6:15 am 
Offline

Joined: Sun Jun 30, 2013 7:59 am
Posts: 37
tokumaru wrote:
Looks like you're not clearing the carry flag before the addition, so you could sometimes be adding 5 instead of 4, if the carry happens to be set.
Oh boy, I really do feel silly! That was it, thanks Tokumaru.

I really don't want to post another thread right away, so I hope you guys don't mind me asking for advice on what I was actually wanting to post before having a total idiot moment with the above:

Is this a decent way of doing a Dragon-Warrior-1 style grid-walk? Of course, I'll have to add some kind of timer to slow down the actual movement between tiles but I wanted to see if I have the right idea in mind or if this just isn't a good way to start going about it:
Code:
main:
  JSR wait_nmi
  JSR read_joy
@if_right:
  LDA buttons
  AND #%00000001
  BEQ @if_moving
  LDA #$01
  STA moving?
@if_moving:
  LDA moving?
  BEQ end_main
  INC $0203
  LDA $0203
  AND #%00000111
  BNE end_main
  LDA #$00
  STA moving?
end_main:
  JMP main

I'm still very much getting the hang of figuring out how to sequence things with branches, taking into account what should be done each frame. Surprising just how much a lack of if, else... has had on being able to structure conditionals. I wasn't sure whether to have the moving? check before getting input and skip over getting input if it was set to 1. I'm still trying to get a clear idea of just how much should be handled each frame.

Thanks again, all the help is very, very much appreciated :)


Top
 Profile  
 
PostPosted: Fri Apr 12, 2019 8:08 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11345
Location: Rio de Janeiro - Brazil
That looks OK, I think. Figuring out concise branch paths for the logic you need to implement is one of my favorite things to do in assembly, and I think your attempt is pretty good.

One criticism that needs to be made is that you're directly manipulating OAM entries, which is okay in games with constant or really predictable sprite usage, but the preferred method for anything with more dynamic sprite needs is to maintain object state independent of their visual representation. Manipulate all states in dedicated object slots in RAM, and use that information to build OAM entries dynamically for display.

Another thing to point out is that your code doesn't prevent movement in other directions, so you could end up with diagonal movement or movement in opposing directions cancelling each other, which might not be what you want. Maybe you could have separate counters for the X and Y movements, and only allow movement to start if both are 0. That's just something to think about.


Top
 Profile  
 
PostPosted: Fri Apr 12, 2019 8:43 am 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 1160
Location: Hokkaido, Japan
I don't want to spoil it but, in case you want to see examples I can show you how I did DW-style tile-based movement a few years ago.

To make the actual movement speed be slower than 1 pixel/frame or between 1 and 2 pixel/frame etc, I'd probably just use fixed point position and speed values so that non-integer values can be used. It's mostly for acceleration-based action games, but it's easy to implement and makes very smooth movement possible, better than a timer/counter will.


Top
 Profile  
 
PostPosted: Fri Apr 12, 2019 10:01 am 
Offline

Joined: Sun Jun 30, 2013 7:59 am
Posts: 37
tokumaru wrote:
That looks OK, I think. Figuring out concise branch paths for the logic you need to implement is one of my favorite things to do in assembly, and I think your attempt is pretty good.
Thanks! Just a few words of encouragement like that make a big difference :)
tokumaru wrote:
Another thing to point out is that your code doesn't prevent movement in other directions, so you could end up with diagonal movement or movement in opposing directions cancelling each other, which might not be what you want. Maybe you could have separate counters for the X and Y movements, and only allow movement to start if both are 0. That's just something to think about
I hadn't actually thought of that! Being that I was only focusing on one direction until I worked something out. That's a good idea. The only other way I could think of was checking if moving? was set before checking controllers, and just skipping direction-checking entirely in that case.

Pokun wrote:
I don't want to spoil it but, in case you want to see examples I can show you how I did DW-style tile-based movement a few years ago.
To make the actual movement speed be slower than 1 pixel/frame or between 1 and 2 pixel/frame etc, I'd probably just use fixed point position and speed values so that non-integer values can be used. It's mostly for acceleration-based action games, but it's easy to implement and makes very smooth movement possible, better than a timer/counter will.
Awesome! Hey, if I can learn from the effort you put in to expedite the process, all the better :) Cool that you checked out the FF disassembly too. I'll pore over that code, and hopefully it'll click, meaning I won't have to implement my timer-based version first or anything. To be honest though, I was petrified as soon as you mentioned fixed point numbers as I've never used them in 6502. They're notoriously tricky things to get right even in modern languages, if memory serves me well. Nonetheless, to pour I go!

Thanks everyone, very much appreciated :)


Top
 Profile  
 
PostPosted: Fri Apr 12, 2019 2:27 pm 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 1160
Location: Hokkaido, Japan
Back then there was a lot of things in the FF code that I didn't understand, but it taught me some tricks that I used in my own simpler version you see there.

I was also afraid of fixed point math at first but it turned out to be really simple. Doesn't matter what language you implement it in (I've tried it in C++ as well, easy as pie). Maybe you are mixing it up with floating point?

For fixed point, all you need to do is if you have for example player position in two RAM slots player_x and player_y, you just add two more RAM slots that holds the fraction of the position value (AKA subpixels). Let's call them player_x_sub and player_y_sub. Then if movement speed is for example $80 subpixel/frame, you just add this value to player_x_sub/player_y_sub at the button press, and whenever it overflows from $FF and wraps around, you just increment player_x/player_y. This way one subpixel is a 256th of a pixel so you can move really slow if you want. The whole pixel position (player_x/player_y) is what you write to OAM each frame, so of course the PPU can't draw sprites in between pixels but that doesn't matter, the movement will be smooth like a banana peel unlike if you use a simple frame counter to time movement with.

Of course things like movement speed can also be 16-bit so if you have player_move_speed and player_move_speed_sub you just add them both to the position variables at the button press (or at whatever point the object is moving) as per normal math rules, so you can have any speed you want between 0.1 and 255.255 pixel/frame.


Top
 Profile  
 
PostPosted: Fri Apr 12, 2019 11:32 pm 
Offline

Joined: Sun Jun 30, 2013 7:59 am
Posts: 37
Pokun wrote:
I was also afraid of fixed point math at first but it turned out to be really simple. Doesn't matter what language you implement it in (I've tried it in C++ as well, easy as pie). Maybe you are mixing it up with floating point?
You guessed it, I was... :oops:
Pokum wrote:
For fixed point, all you need to do is...
That explanation was fantastic! I think it may have actually clicked on the first reading, thank you :)
Quote:
Of course things like movement speed can also be 16-bit so if you have player_move_speed and player_move_speed_sub you just add them both to the position variables at the button press (or at whatever point the object is moving) as per normal math rules, so you can have any speed you want between 0.1 and 255.255 pixel/frame.
In this case, if the values were:
player_move_speed = 1
player_move_speed_sub = 0

That would be the same as just incrementing one pixel per frame, correct? And if you had:
player_move_speed = 255
player_move_speed_sub = 255

that would be 99.70% of the horizontal width of the screen per frame?

Thanks so much for your help. You've potentially saved me a whole lot of time showing me this better method, I really appreciate it :)


Top
 Profile  
 
PostPosted: Sat Apr 13, 2019 1:53 am 
Offline

Joined: Sun Mar 27, 2016 7:56 pm
Posts: 214
CrowleyBluegrass wrote:
In this case, if the values were:
player_move_speed = 1
player_move_speed_sub = 0

That would be the same as just incrementing one pixel per frame, correct?
Yep.

CrowleyBluegrass wrote:
And if you had:
player_move_speed = 255
player_move_speed_sub = 255

that would be 99.70% of the horizontal width of the screen per frame?
Well... when they wrote "255.255", they didn't mean the actual decimal number 255.255, they just meant that both of the bytes are 255.

The number is actually 255 255/256, or 255.99609375, so it's 99.998% of the width of the screen.

In short, if:
player_move_speed = X
player_move_speed_sub = Y

then the number represented is X Y/256.


Top
 Profile  
 
PostPosted: Sat Apr 13, 2019 2:00 am 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1287
CrowleyBluegrass wrote:
In this case, if the values were:
player_move_speed = 1
player_move_speed_sub = 0

That would be the same as just incrementing one pixel per frame, correct?

Correct.
Quote:
player_move_speed = 255
player_move_speed_sub = 255

that would be 99.70% of the horizontal width of the screen per frame?

Sort of, but not for the reasons you think. Ignore the subpixel part.

If adding 1 from player_move_speed moves 1 pixel right per frame, then adding 255 from player_move_speed adds 255 pixels per frame.

So if the pixel position is 16bit, you've moved a small amount left, because you've wrapped by adding a large enough right value.

player_move_speed = 0
player_move_speed = 255

Would be slightly less than 1 pixel a frame.


Edit: No, I am dumb. You are right, but I leave the post up to not confuse anyone who may have seen it. I read your post incorrectly.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Sat Apr 13, 2019 2:28 am 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 1160
Location: Hokkaido, Japan
CrowleyBluegrass wrote:
Thanks so much for your help. You've potentially saved me a whole lot of time showing me this better method, I really appreciate it :)
You are welcome! I'm glad my explanation was understandable.

Quote:
You guessed it
It's called fixed point math because the radix point (we can't call it a decimal point as we are not dealing with decimal numbers) is not moving, it's always fixed in between player_x and player_x_sub. A floating point on the other hand can move around and is not as precise but you can have more numbers more easily or something I think. But it's not like we need any more numbers in this case (255.255 is way overkill and probably not a useful value to use) and implementing floating points in software will probably just be slow and isn't really that useful in games. Even some modern game consoles like the Nintendo DS doesn't have floating point hardware, so fixed point math is still a useful thing to implement in modern games as well.

Quote:
In this case, if the values were:
player_move_speed = 1
player_move_speed_sub = 0

That would be the same as just incrementing one pixel per frame, correct? And if you had:
player_move_speed = 255
player_move_speed_sub = 255

that would be 99.70% of the horizontal width of the screen per frame?
Exactly (well almost, as Nicole explained)! You can also limit the subpixels to a smaller value like 15 or 7 or whatever you want, but you would have to handle the wrapping manually in that case, and that would just mean more work for less granularity. But if coding in a high level language for other systems it may be useful to limit the subpixels at 255 or 65535 or something to guarantee the same behaviour at different systems with differently sized integers.

Of course any speed over say 8 pixel/frame is probably too fast to have any real use as it would skip large parts of the screen and maybe even miss collisions, even 2 pixel/frame is quite fast as you may have noticed. The point is (pun intended) to have fine adjustment of movement speed. If you want to make the speeds behave the same on PAL you probably also needs a fixed point system.
If all objects always move at the same speed, the player_move_speed and/or player_move_speed_sub can be constants instead of variables of course.


Top
 Profile  
 
PostPosted: Sun Apr 14, 2019 4:40 am 
Offline
User avatar

Joined: Fri Jan 24, 2014 9:05 am
Posts: 187
Location: Hungary
If you want easy rectangular hitboxes, try to limit the fastest speed anything can go at to be less than the smallest length of any hitbox's side. Otherwise, like mentioned before, objects may pass through other objects if they go too fast (in one frame the object is to the left of something, and in the other frame it's already to the right of it). Since my smallest hitbox size is 8x8, I can't allow anything to go faster than 7 pixels and 0 subpixels / frame, otherwise if everything aligns I could get a passthrough.


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: Nioreh and 7 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