It is currently Wed Jul 18, 2018 7:57 am

 All times are UTC - 7 hours

 Page 1 of 3 [ 37 posts ] Go to page 1, 2, 3  Next
 Print view Previous topic | Next topic
Author Message
 Post subject: Learning Action Game BasicsPosted: Fri Aug 11, 2017 8:44 am

Joined: Tue May 28, 2013 5:49 am
Posts: 977
Location: Sweden
Learning Action Game Basics, Acceleration-based Movement, Variable Height Jumping and Collision

Edit: The following code is apparently fine. I figured out the problem elsewhere and it fixed it. My other question is still relevant though.

So I decided to learn how to make an action game.
In order to make physics smooth and flexible like I want them, I understand that I need to make: A fixed-point subpixel system so I can move slower than 1 pixel/frame, acceleration-based movement for both running and jumping, and variable height jumping.

I already made subpixel coordinates using one byte for the fractional part and one for the integer part of the coordinate and they work fine.

Now I'm trying to make acceleration-based movement. I started making the controller handling for rightward movement. I learned to do comparisons with signed numbers from this article.

So to move rightwards, I'm using two bytes for velocity that are added to by a two-byte acceleration value every frame that the RIGHT d-pad button is pressed. Before updating the velocity I first compare (using 16-bit signed comparison from above link) the new velocity value with the max speed value and if it's more than the max speed, I set the velocity to the max speed value, else I proceed to update velocity with the new value.

Velocity is then added to the 16-bit X-coordinate which updates the sprite position.

My code works fine as long as my max speed isn't set to \$00.7F or more. As soon as my sprite comes up to this speed he will start slowing down to zero again, then accelerate again and so on as long as I hold the button. I suspect something about the comparison routine makes it think that the lower velocity byte is signed. Shouldn't the sign only be in the most significant byte so that the range becomes \$80.00 to \$7F.FF?

Code:
;Constants:
MOB_ACCEL_L = 4  ;walking/running acceleration value fractional part
MOB_ACCEL_H = 0  ;walking/running acceleration value integer part
MOB_MSPD_L = 128 ;maximum walking/running speed fractional part
MOB_MSPD_H = 0   ;maximum walking/running speed integer part

;Variables:
temp+0
temp+1      ;temporary storage
p1_x+0      ;horizontal coordinate fractional part
p1_x+1      ;horizontal coordinate integer part
p1_vx+0     ;horizontal velocity fractional part
p1_vx+1     ;horizontal velocity integer part

;Code:
@right1:                  ;controller I input handler for RIGHT button
lda con_state+0
and #CON_RIGHT
beq @not_right1

lda p1_vx+1
bmi @not_right1         ;if velocity negative, not right

lda p1_vx+0
clc
sta temp+0
lda p1_vx+1
sta temp+1              ;store new velocity temporary for comparison

lda temp+0
cmp #MOB_MSPD_L
lda temp+1
sbc #MOB_MSPD_H
bvc @n_xor_v_vr
eor #\$80
@n_xor_v_vr:              ;16-bit signed comparison with max speed
bmi @not_terminal_vr    ;if velocity < max speed, allow acceleration
lda #MOB_MSPD_L
ldx #MOB_MSPD_H
jmp @terminal_vr        ;else, velocity = max speed
@not_terminal_vr:
lda temp+0
ldx temp+1
@terminal_vr:
sta p1_vx+0
stx p1_vx+1             ;set velocity
jmp @a1                 ;exit

@not_right1:              ;if !RIGHT, decelerate

;......

p1_move_x:                ;horizontal movement based on velocity
lda p1_x+0
clc
sta p1_x+0              ;add velocity to position to move, low byte

lda p1_x+1
sta p1_x+1              ;add high byte plus carry

Questions:
1) What's wrong with my code?
Edit: Of course soon after posting this, I found out the problem. It appears the code is fine, the problem was very simple and unrelated to this code. I guess the code can be used by others trying to do the same thing. My other question is still relevant though.

2) What is the Overflow Flag normally used for? From what I understand it indicates overflow/underflow between \$7F and \$80 to indicate that the value didn't fit in the signed number, much like Carry indicates that a number doesn't fit in a byte. But unlike Carry it isn't used in additions and subtractions. The only use I know of it is for signed comparisons, as a simulation of a branch always instruction and used in a special feature of the BIT instruction.

Any help is greatly appreciated!

Last edited by Pokun on Fri Aug 11, 2017 10:55 am, edited 1 time in total.

Top

 Post subject: Re: Learning Action Game BasicsPosted: Fri Aug 11, 2017 10:55 am

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7310
Location: Seattle
Pokun wrote:
Code:
lda p1_vx+1
bmi @not_right1         ;if velocity negative, not right
How is the user supposed to brake if they can't add acceleration against the current velocity vector?

Quote:
Code:
lda temp+0
cmp #MOB_MSPD_L
lda temp+1
sbc #MOB_MSPD_H
bvc @n_xor_v_vr
eor #\$80
@n_xor_v_vr:              ;16-bit signed comparison with max speed
bmi @not_terminal_vr    ;if velocity < max speed, allow acceleration
Because I'm terrible at math in asm, I tried checking against the output from cc65...

I think that's supposed to be bpl.

Quote:
2) What is the Overflow Flag normally used for? [...] The only use I know of it is for signed comparisons, as a simulation of a branch always instruction and used in a special feature of the BIT instruction.
It's for any manual fixup of signed math. And I just found this article.

In practice, it's so rarely used that the bare 6502 has an external SET OVERFLOW pin, and I remember seeing a minimalist 6502 devkit that used it as one of the two dedicated inputs.

Top

 Post subject: Re: Learning Action Game BasicsPosted: Fri Aug 11, 2017 10:59 am

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6402
I thought the BMI was correct. (reference)

As far as the code, it looks okay to me, but as always, it's very helpful to use a debugger and step through it to make sure it's going wrong where you think it is. Problems are often hard to read just on the page.

The overflow flag question... well you listed all the common uses, I think.

Top

 Post subject: Re: Learning Action Game BasicsPosted: Fri Aug 11, 2017 11:08 am

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 414
Location: Central Illinois, USA
Pokun wrote:
Learning Action Game Basics, Acceleration-based Movement, Variable Height Jumping and Collision

_________________
My games: http://www.bitethechili.com

Top

 Post subject: Re: Learning Action Game BasicsPosted: Fri Aug 11, 2017 11:22 am

Joined: Tue May 28, 2013 5:49 am
Posts: 977
Location: Sweden
I edited the post a bit too late. I think maybe there is no problem with the code. It appears an old stray routine was meddling with it. I added a jump over the stray routine and it fixed it. Thanks a lot everyone anyway!

lidnariq wrote:
Pokun wrote:
Code:
lda p1_vx+1
bmi @not_right1         ;if velocity negative, not right
How is the user supposed to brake if they can't add acceleration against the current velocity vector?

You are right. I think I added that line after looking at other people's code to see if it would solve any problem, but it didn't. I left it in anyway because I wasn't sure if it was needed or not. I guess I'll need to remove it so the user can break faster by pressing the opposite direction.

Quote:
Quote:
2) What is the Overflow Flag normally used for? [...] The only use I know of it is for signed comparisons, as a simulation of a branch always instruction and used in a special feature of the BIT instruction.
It's for any manual fixup of signed math. And I just found this article.

In practice, it's so rarely used that the bare 6502 has an external SET OVERFLOW pin, and I remember seeing a minimalist 6502 devkit that used it as one of the two dedicated inputs.

Ah I see, that clears things up. I read that article too, but I thought maybe I misunderstood something and that maybe it was related to my problem messing with signed numbers. This experience has cleared up many question marks about 6502.

Quote:
As far as the code, it looks okay to me, but as always, it's very helpful to use a debugger and step through it to make sure it's going wrong where you think it is. Problems are often hard to read just on the page.

Yeah thanks. I tried to use the debugger but I don't really understand how to use it properly, adding breakpoints and whatnot, so it didn't help me much. I should probably research how to use debuggers more.

Quote:

Thanks for the link. Looks like it may be able to answer lots of questions I will have from now on making an action game.

Now I'm going to add in moving left, then we have jumping and collisions. I might post again if I run into any more problems.

Top

 Post subject: Re: Learning Action Game BasicsPosted: Fri Aug 11, 2017 12:23 pm

Joined: Fri May 08, 2015 7:17 pm
Posts: 2147
Location: DIGDUG
Quote:
debugger but I don't really understand how to use it

If you can get your assembler to output a labels file, with all the addresses of the labels, you can set a breakpoint for execution from that address.

Or, you can insert a 'inc \$ff' (assuming address ff is unused) just before the code you are looking at. Set a breakpoint for writes to \$ff.

For movement code, you need correct input. Either set (with FCEUX) an auto-hold on a button press (I can't remember the default keys to do this)... or open the hex editor, locate your ram address for controller reads (probably also found on that label file)...press the direction on the controller, pause emulation, click on the ram address, and 'freeze' its value. Now set a breakpoint, and step through the code.

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

Top

 Post subject: Re: Learning Action Game BasicsPosted: Sat Aug 12, 2017 2:55 am

Joined: Tue May 28, 2013 5:49 am
Posts: 977
Location: Sweden
Ah yeah Asm6 outputs a listing file with all addresses, I got a break point to work. I guess I could also use the BRK instruction and break on IRQ, I'm just not sure how to return to the game after a BRK though as the return address seems not to be preserved.
Can't find anything about using inputs in the debugger. I know Mesen recently added that feature though.

Yeah I've been using the hex editor in FCEUX a lot. It's very useful for RAM viewing and writing, I wish more emulators had such a thing. One thing FCEUX is missing though is an OAM viewer.

I added left movement by copying the right movement and changing to subtracting acceleration value instead of adding, and limiting velocity to the negated max speed.
The player sprite moves faster to the right than to the left however. It's strange because I can confirm that the velocity values in RAM goes up to the same max speed in both directions, just that the left max speed is negated.

I also started implementing gravity and jumping (basically copied left and right movement with some modifications). I haven't limited jump height or jump button presses so jumping works more like a jet-pack at the moment. Googling for variable height jumping it appears that jump height is often limited by a jump time counter.

Top

 Post subject: Re: Learning Action Game BasicsPosted: Sat Aug 12, 2017 5:14 am

Joined: Sun Feb 07, 2016 6:16 pm
Posts: 451
Pokun wrote:
I guess I could also use the BRK instruction and break on IRQ, I'm just not sure how to return to the game after a BRK though as the return address seems not to be preserved.
Mesen has a "Break on BRK" option you can enable. Once the debugger breaks on a BRK instruction, you should be able to right-click and do "Set Next Statement" on the instruction below the BRK to skip executing the BRK instruction completely (so doing this means the BRK instruction will have no actual impact on the execution). "Set Next Statement" essentially changes the program counter and allows you to change the execution flow as you need (e.g force the code to skip a branch, etc.)

dougeff's idea of using write-specific breakpoints & writing to a specific address that you don't normally is probably simpler to use, though. If you're not using save/work ram, you can just write to the \$6000-\$7FFF range with no side effects and break on those writes.

I need to take a look at ASM6's listing output (couldn't find any example of them online) - might be something I can import into the debugger like I did for CC65's DBG files.
Also, out of sheer curiosity, is there anything FCEUX's hex editor does that you need that Mesen's doesn't?

Top

 Post subject: Re: Learning Action Game BasicsPosted: Sat Aug 12, 2017 9:31 am

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6402
Pokun wrote:
Ah yeah Asm6 outputs a listing file with all addresses, I got a break point to work. I guess I could also use the BRK instruction and break on IRQ, I'm just not sure how to return to the game after a BRK though as the return address seems not to be preserved.

An RTI instruction in your IRQ handler would do the job. The BRK would take you to the IRQ handler, and one "step" should take you back to the instruction after the BRK (by executing the RTI).

BRK does preserve a return value, there's just an extra byte on the stack compared to a JSR (P/flags).

Top

 Post subject: Re: Learning Action Game BasicsPosted: Sat Aug 12, 2017 9:46 am

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20258
Location: NE Indiana, USA (NTSC)
If you're using a breakpoint on an empty IRQ handler (that is, one that immediately executes RTI), make sure your assembler emits two bytes for BRK. Some emit one.

Top

 Post subject: Re: Learning Action Game BasicsPosted: Sat Aug 12, 2017 9:46 am

Joined: Mon Jan 03, 2005 10:36 am
Posts: 3090
Location: Tampere, Finland
rainwarrior wrote:
Pokun wrote:
Ah yeah Asm6 outputs a listing file with all addresses, I got a break point to work. I guess I could also use the BRK instruction and break on IRQ, I'm just not sure how to return to the game after a BRK though as the return address seems not to be preserved.

An RTI instruction in your IRQ handler would do the job. The BRK would take you to the IRQ handler, and one "step" should take you back to the instruction after the BRK (by executing the RTI).

BRK does preserve a return value, there's just an extra byte on the stack compared to a JSR (P/flags).

Also note that the return address pushed by BRK takes you one byte past the byte following the BRK opcode, i.e.:
Code:
brk ; branches to irq
nop
nop ; rti returns here

irq:
rti

(In some assemblers BRK supports "immediate addressing", that is, you can write brk #\$69. In ca65 an operand is not supported.)

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi

Top

 Post subject: Re: Learning Action Game BasicsPosted: Sat Aug 12, 2017 4:12 pm

Joined: Tue May 28, 2013 5:49 am
Posts: 977
Location: Sweden
Ah yes it seems ASM6 assembles "BRK" as \$00, and "BRK #\$DB" becomes \$00 \$DB ("BRK \$DB" also works). Passing a dummy argument like this works as the dummy byte, and now my IRQ handler returns properly on BRK. I could even detect the BRK IRQ by pulling P from the stack and checking the B flag.

Sour wrote:
Pokun wrote:
I need to take a look at ASM6's listing output (couldn't find any example of them online) - might be something I can import into the debugger like I did for CC65's DBG files.
Also, out of sheer curiosity, is there anything FCEUX's hex editor does that you need that Mesen's doesn't?

That would be helpful, asm6 is one of the most popular assemblers after all.
Not anything I can think about, Mesen's hex editor seems to have about everything that the FCEUX one have, plus more. Only thing might be that Mesen has a slow interface on my laptop I'm currently programming on, and things like hex editor and PPU viewers requires first opening the debugger (which also opens slowly). But maybe that can't be helped.
FCEUX opens up lighting fast and has quick menus, which makes it ideal for quick testing.

Top

 Post subject: Re: Learning Action Game BasicsPosted: Mon Aug 14, 2017 3:26 pm

Joined: Tue May 28, 2013 5:49 am
Posts: 977
Location: Sweden
OK next problem.

I have got both moving sideways and jumping to work pretty well if given the right parameters, but there are two bugs that I can't figure out why they happen, and I'm not sure how the debugger could help me here either.

The first is that going left is a lot slower than going right. When going right, I come up to the speed \$01.E0 pixel/frame according to the value in RAM, and when going left, I come up in \$FF.20 pixel/frame. That's the negative of \$01.E0 no? Yet I come up in this speed faster and the player character moves clearly much slower to the left than to the right.

Here is my leftward acceleration code (the rightward one is the same as the one in the first post). It is almost identical to the rightward code, except that acceleration is subtracted instead of added and the max speed is negated:
Code:
Constants:
MOB_ACCEL_L = 8
MOB_ACCEL_H = 0         ;running acceleration value added to velocity each frame
MOB_DECEL_L = 12
MOB_DECEL_H = 0         ;breaking acceleration value subtracted from velocity each frame not pressing RIGHT/LEFT
MOB_MSPD_L  = 224
MOB_MSPD_H  = 1         ;maximum allowed running speed

@left1:
lda con_state+0
and #CON_LEFT
beq @not_left1

;lda p1_ground           ;if in mid-air, skip acceleration (disallows
;beq @right1             ;horizontal acceleration control in mid-air)

lda p1_vx+0
sec
sbc #MOB_ACCEL_L
sta temp+0
lda p1_vx+1
sbc #MOB_ACCEL_H        ;subtract acceleration value from velocity
sta temp+1              ;store new velocity temporarily for comparison

lda temp+0
cmp #(256-MOB_MSPD_L)
lda temp+1
sbc #(256-MOB_MSPD_H)
bvc @n_xor_v_vl
eor #\$80
@n_xor_v_vl:              ;16-bit signed comparison with (-max speed)
bpl @not_terminal_vl    ;if velocity < (-max speed), allow acceleration
lda #(256-MOB_MSPD_L)
ldx #(256-MOB_MSPD_H)
jmp @terminal_vl        ;else, velocity = (-max speed)
@not_terminal_vl:
lda temp+0
ldx temp+1
@terminal_vl:
sta p1_vx+0
stx p1_vx+1             ;set velocity
jmp @right1             ;exit

@not_left1:
lda p1_vx+1
bpl @right1             ;if velocity positive, no need to decelerate

;lda p1_ground           ;if in mid-air, skip acceleration (disallows
;beq @right1             ;horizontal acceleration control in mid-air)

lda p1_vx+0
clc
sta temp+0
lda p1_vx+1
sta temp+1              ;store new velocity temporarily for comparison

lda temp+0
cmp #\$00
lda temp+1
sbc #\$00
bvc @n_xor_v_vls
eor #\$80
@n_xor_v_vls:             ;16-bit signed comparison with 0
bpl @positive_vls       ;if velocity >= 0, velocity = 0
lda temp+0
sta p1_vx+0
lda temp+1
sta p1_vx+1             ;else, allow deceleration
jmp @right1             ;exit
@positive_vls:
lda #\$00
sta p1_vx+0
sta p1_vx+1             ;stop

@right1:
;...

There are no meddling routines that run this time from what I can tell. Not anything that affects horizontal velocity or x-position anyway.
I attached the game to demonstrate the problem. That other problem is about jumping, but I'll get to that later.

 Attachments: File comment: Problems: 1) Leftward acceleration is slower than rightward. 2) When jumping, double jumping is possible if done really quickly on the way up. actiongame.zip [3.57 KiB] Downloaded 37 times
Top

 Post subject: Re: Learning Action Game BasicsPosted: Mon Aug 14, 2017 6:12 pm

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20258
Location: NE Indiana, USA (NTSC)
The two's complement of \$01E0 (+480) is \$FE20 (-480). \$FF20 is -224.

Top

 Post subject: Re: Learning Action Game BasicsPosted: Tue Aug 15, 2017 8:31 am

Joined: Mon Jan 03, 2005 10:36 am
Posts: 3090
Location: Tampere, Finland
It's best to replace MOB_MSPD_L and MOB_MSPD_H with MOB_MSPD = \$1E0, and use <MOB_MSPD, >MOB_MSPD, <(-MOB_MSPD) and >(-MOB_MSPD) in the code.

That kind of mistake is very common (at least I seemed to make them all the time when handling two's complement fixed point numbers, nowadays I try to be extra careful with them).

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi

Top

 Display posts from previous: All posts1 day7 days2 weeks1 month3 months6 months1 year Sort by AuthorPost timeSubject AscendingDescending
 Page 1 of 3 [ 37 posts ] Go to page 1, 2, 3  Next

 All times are UTC - 7 hours

#### Who is online

Users browsing this forum: No registered users and 4 guests

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

Search for:
 Jump to:  Select a forum ------------------ NES / Famicom    NESdev    NESemdev    NES Graphics    NES Music    Homebrew Projects       2018 NESdev Competition       2017 NESdev Competition       2016 NESdev Competition       2014 NESdev Competition       2011 NESdev Competition    Newbie Help Center    NES Hardware and Flash Equipment       Reproduction    NESdev International       FCdev       NESdev China       NESdev Middle East Other    General Stuff    Membler Industries    Other Retro Dev       SNESdev       GBDev    Test Forum Site Issues    phpBB Issues    Web Issues    nesdevWiki