It is currently Sat Oct 21, 2017 10:02 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 28 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Thu Feb 26, 2015 2:12 pm 
Offline

Joined: Thu Feb 26, 2015 1:58 pm
Posts: 1
I've started programming fairly recently and I've found I learn best by reading other code. I don't know if it's a taboo to ask to see someone else's code (like I'm gonna steal it or something I guess?) but if so I'd be fine with just an explanation of one or some of the ways you might make a character jump. I can make a character go up, of course, and I have a working gravity engine to make them come back down, but what I need to learn to do is make it go up, hit a certain point, then come back down. Y'know. Just a normal jump like in any game. I've been trying a couple different ways and the only time I got it to work, I don't understand why it works. At first it was more of a teleport followed by a fall due to the gravity I'd written, then I put the vblank wait in the middle of the code for the jump and that made it happen correctly for some reason. It looks like this:

Code:
Jump:
  LDX #$08
Moving:
  LDA Sprite1VerticalPosition
  SEC
  SBC #$02
  STA Sprite1VerticalPosition
  LDA Sprite2VerticalPosition
  SEC
  SBC #$02
  STA Sprite2VerticalPosition
  LDA Sprite3VerticalPosition
  SEC
  SBC #$02
  STA Sprite3VerticalPosition
  LDA Sprite4VerticalPosition
  SEC
  SBC #$02
  STA Sprite4VerticalPosition
  DEX

  CPX #$00
  BEQ JumpDone
vblankwaitforjump
readyyy
  BIT $2002
  BPL vblankwaitforjump
  JMP Moving
JumpDone:


Without the Vblank wait there, all eight "move up by 2"s happen at the same time and the character just shoots up to the right point. With the wait it lets each one happen frame by frame, but I don't know why that all works in the context of the rest of the code.

Sorry if this has been posted before. I searched a few different phrases but "jump" being an instruction gets in the way a lot.


Top
 Profile  
 
PostPosted: Thu Feb 26, 2015 2:43 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19115
Location: NE Indiana, USA (NTSC)
When you jump, set a velocity variable to upward. Each frame, add a constant downward acceleration value to the velocity, and add the velocity to the current Y position. Eventually, the accumulated downward accelerations will cause the velocity to cross from upward to downward, which produces the peak of the jump, and as you repeat the process, the Y position will start moving downward.


Top
 Profile  
 
PostPosted: Thu Feb 26, 2015 2:58 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
I've changed the Subject line to reflect the request more clearly. "Can somebody show me a working code for a jump?" to me meant "can someone show me how to use jmp in some weird/quirky way, or how can I do this (e.g. branch tables, etc.)", not actual animation/velocity/movement.


Top
 Profile  
 
PostPosted: Thu Feb 26, 2015 3:50 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5730
Location: Canada
I'm not going to attempt an example in assembly, because it would probably get bogged down in details like how to do 16 bit signed math on the 6502 and so on, but here's sort of a skeleton of how a player that can move and jump might look in a higher level language like C.
Code:
void update_player()
{
   if (on_ground)
   {
      if (button_a) velocity_y = -500; // apply sudden upward velocity for jump
      if (dpad_left) velocity_x -= 5; // accelerate left
      if (dpad_right) velocity_x += 5; // accelerate right
   }
   
   position_x += velocity_x;
   resolve_horizontal_collision();
   
   position_y += velocity_y;
   resolve_vertical_collision();

   velocity_y += 10; // apply gravity
}

void main_loop()
{
   setup_level();
   draw_frame();
   rendering_on();
   
   while (!quit)
   {
      poll_gamepad();
      update_player();
      update_enemies();
      draw_frame();
      wait_vblank();
   }
}


Top
 Profile  
 
PostPosted: Thu Feb 26, 2015 6:34 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Huge thanks to rainwarrior for this -- for almost my entire life I've wondered how to do that (cleanly/clearly). Wow. Awesome.


Top
 Profile  
 
PostPosted: Thu Feb 26, 2015 6:46 pm 
Offline
User avatar

Joined: Mon Sep 15, 2014 4:35 pm
Posts: 3074
Location: Nacogdoches, Texas
One problem I always found is that even if you have the gravitational acceleration at even just 1 pixel, jumping is going to rise and fall very fast. Even though there isn't a half value, you could always make the value jump between 0 and 1 every frame and it seems to work fairly well. You just have to make sure the value starts the same every time you jump. Otherwise, the jumping height will fluctuate a little. You know, could you actually have a table for jump heights that you add to the players y position? you would increment a pointer every frame that offsets the table. You could check to see if you are at the last value of the table and stay there so you don't run into data from other things and have it freak out. The values in the table would be relative to the objects current position, so it would be like: 8,4,2,1,0,-1,-2,-4,-8 (can't you do negative numbers?).


Last edited by Espozo on Thu Feb 26, 2015 6:50 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Thu Feb 26, 2015 6:47 pm 
Offline

Joined: Sun Mar 19, 2006 9:44 pm
Posts: 915
Location: Japan
Also, you will want to have more bits/bytes dedicated to velocity than are to position, otherwise the peak of your jump will be severely angled.

_________________
http://www.chrismcovell.com


Top
 Profile  
 
PostPosted: Thu Feb 26, 2015 7:00 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10066
Location: Rio de Janeiro - Brazil
rainwarrior's method is what I have always done, and it has worked perfectly. I remember someone suggesting "turning gravity off" for a few frames when a jump starts, but I never considered that necessary.


Top
 Profile  
 
PostPosted: Thu Feb 26, 2015 7:39 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5730
Location: Canada
If you subdivide the pixel, the granularity of your velocity isn't really a problem anymore.

There are many ways to subdivide, but the way I usually go with on the NES is 8.8 fixed point. Simply use two bytes each to represent your position and velocity as 16 bit numbers. The high byte is now the pixel position, the low byte is subpixel precision. Other than where you add the velocity to the position, you can generally ignore the low byte most of the time, and just work with the high byte.


Top
 Profile  
 
PostPosted: Fri Feb 27, 2015 4:28 pm 
Offline
User avatar

Joined: Sat Jul 12, 2014 3:04 pm
Posts: 936
(One presumes that resolve_vertical_collision() sets/clears on_ground on landing/walking off)

However, gravity should only be applied if(!on_ground)
Code:
   if (on_ground) {
      if (button_a) {
         velocity_y = -500; // apply sudden upward velocity for jump
         on_ground = false;
      }
      if (dpad_left) velocity_x -= 5; // accelerate left
      if (dpad_right) velocity_x += 5; // accelerate right
   }
   else
      velocity_y += 10; // apply gravity

Part of the point of an on_ground flag is to reduce calculation; you don't need to check "did I bonk into something vertically", only "is there still ground".

Similarly, the order of
Code:
   position_x += velocity_x;
   resolve_horizontal_collision();
   position_y += velocity_y;
   resolve_vertical_collision();
is important. Doing it like that will produce some discrepancies: it will collide in cases A and C when it shouldn't, but not case B nor D when it should. (Indeed, a more in-depth collision than "before and after" is required to get all of these ironed out.)
Attachment:
CollisionProblem.png
CollisionProblem.png [ 606 Bytes | Viewed 2527 times ]

(Caveat: often, objects move slowly enough that this is not a problem; bullets, however, are likely to.)


Top
 Profile  
 
PostPosted: Fri Feb 27, 2015 7:38 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5730
Location: Canada
Well, the actual character controller routine for the game I am working on is many hundreds of lines long. I kept this example simple for illustration purposes.

There are many ways to determine "on_ground". It depends on how you implement collision, and other things. Whether it's a variable, when to update it, how to update it, this requires a specific solution, and this is only a generic question.

Yes, you might like to apply gravity only if not on the ground. It depends on how you're handling it in your game. (In some cases having less code is more important than saving cycles, too, especially if using NROM.)

Yes, you might want vertical motion before horizontal motion. Again, depends on the specifics of your game. You might even want to handle movement and collision in a different way. For bullets, you might want to move both at once and do point tests for collision, for example.

There are all sorts of different ways to do this stuff. I didn't think it would help the example. This is also why I didn't use assembly; I wanted to show the high level concepts involved, the details are for the implementer.


Top
 Profile  
 
PostPosted: Sat Feb 28, 2015 2:11 am 
Offline
User avatar

Joined: Sun Sep 21, 2014 10:18 pm
Posts: 32
He made it clear coding is fairly new to him,supplying anything besides asm isn't helping him


I'll Quote him:
(I can make a character go up, of course, and I have a working gravity engine to make them come back down, but what I need to learn to do is make it go up, hit a certain point, then come back down. Y'know. Just a normal jump like in any game)
.....................
Obviously looking at his code he's lost,the last thing he needs is a OnGroundFlag seriously.He's inquiring about a simple object jumping.
It's also laughable(you guys assumed he was talking about Collision Detection with his comment hit a certain point, then come back down) besides tepples and myself did anyone else even look at his code?
rainwarrior wrote:
I'm not going to attempt an example in assembly

Why bother answering then,did he request assistance in C code?,Which has logic errors as pointed out.



I'll keep this simple 9258 for clarity

Code:
    lda _JumpFlag
    bne ObjectJumpCont
    sta _YPlayerSubPixel
    inc _jumpFlag
    lda #28
    sta _YPlayerSubPixelConst
    lda #4
    sta _YPlayerVelocity
ObjectJumpCont:
   lda _YPlayerSubPixel
   sec
   sbc _YPlayerSubPixelConst
   sta  _YPlayerSubPixel
   lda _YPlayerVelocity
   sbc #0
   sta _YPlayerVelocity
   lda _YPlayer
   sec
   sbc _YPlayerVelocity
   sta _YPlayer
   rts



Top
 Profile  
 
PostPosted: Sat Feb 28, 2015 8:08 am 
Offline
Formerly ~J-@D!~
User avatar

Joined: Sun Mar 12, 2006 12:36 am
Posts: 445
Location: Rive nord de Montréal
Prime wrote:
rainwarrior wrote:
I'm not going to attempt an example in assembly

Why bother answering then,did he request assistance in C code?,Which has logic errors as pointed out.

I don't think the C example is complicated, in fact because it's more like pseudocode, it's not 100% correct C but it's to the point and I think about anyone can understand the code, even if not knowing C. It's also very concise (that's why koitsu loved it afterall), much more concise than asm, and I believe it conveys much more easily the basic idea of an algorithm, without being bogged with implementation details at the machine level. The C example has flaws, of course, because it's too simple, but it was a basic, "high level" algorithm, and the flaws themselves don't impede with the understanding of the code.

That being said, looking at the code, the OP needs help with assembly (and NES architecture in general) because that code would certainly benefit of indexed addressing, and also it spins on $2002, which is not 100% reliable (should use NMI), I don't see the sprite DMA so nothing will ever move (I assume this code is responsible of rendering, it could be done in NMI but the spin on $2002 will make it miss some NMIs). Other flaws are already covered by suggestions by others.

If someone talks about the useless CPX and other such optimisations, you're not helping!


Top
 Profile  
 
PostPosted: Sat Feb 28, 2015 12:13 pm 
Offline

Joined: Thu Aug 28, 2008 1:17 am
Posts: 591
9258 wrote:
I've started programming fairly recently and I've found I learn best by reading other code. I don't know if it's a taboo to ask to see someone else's code (like I'm gonna steal it or something I guess?) but if so I'd be fine with just an explanation of one or some of the ways you might make a character jump. I can make a character go up, of course, and I have a working gravity engine to make them come back down, but what I need to learn to do is make it go up, hit a certain point, then come back down. Y'know. Just a normal jump like in any game. I've been trying a couple different ways and the only time I got it to work, I don't understand why it works. At first it was more of a teleport followed by a fall due to the gravity I'd written, then I put the vblank wait in the middle of the code for the jump and that made it happen correctly for some reason. It looks like this:


Quote:
Without the Vblank wait there, all eight "move up by 2"s happen at the same time and the character just shoots up to the right point. With the wait it lets each one happen frame by frame, but I don't know why that all works in the context of the rest of the code.


I having a problem understanding.. what you're not understanding. That is to say, you don't understand why you need vblanks? I think you might have some serious gaps in the general fundamentals of game logic (which is strange, if you have the capability of coding in ASM then surely something simple in logic like this should be obvious or simply worked out). Did no one else feel similar about this post??? Maybe I'm just misunderstanding you, but I'm going to assume you really don't understand some very basic/simplistic ideas. I mean, I don't know how else to interpret your post, considering what you've said. This should be in the newbie help section. So I'm going to assume this is ALL relatively new to you.


So, 'jumping' is obviously a state that exists for a period of frames. The object moves up, hits a point where the acceleration in that direction stops, then object falls back 'down'. However you implement this, linear or non-linear, it's all irrelevant if you don't understand why 'vblanks' in between each step made it not 'teleport', or why you would even need them. If you don't understand that, then I doubt you're gonna understand any of the examples/code given here. This isn't about ASM coding, this is about some pretty basic logic.

So OK, 'jumping' is an action that happens across a series of frames; a it's temporal thing (a series of events). You should have a main game engine loop that occurs, typically, every vblank. If the player pushes a button, then the main game loop executes the jump action. Each time, the game engine calls the jump routine, and each time that routine either adds height or decreases height - until either the object/player dies or hits solid ground, or some other event that game logic determines should end the jump routine. It needs to happen across a series of frames because otherwise the game logic will not execute correctly and you get that teleporting effect. To be honest, this shouldn't really need an explanation. Maybe you can give some history/background on what experience you have (programming, etc)?

So your code here...
Code:
Jump:
  LDX #$08
Moving:
  LDA Sprite1VerticalPosition
  SEC
  SBC #$02
  STA Sprite1VerticalPosition
  LDA Sprite2VerticalPosition
  SEC
  SBC #$02
  STA Sprite2VerticalPosition
  LDA Sprite3VerticalPosition
  SEC
  SBC #$02
  STA Sprite3VerticalPosition
  LDA Sprite4VerticalPosition
  SEC
  SBC #$02
  STA Sprite4VerticalPosition
  DEX

  CPX #$00
  BEQ JumpDone
vblankwaitforjump
readyyy
  BIT $2002
  BPL vblankwaitforjump
  JMP Moving
JumpDone:


... screams something seriously wrong with your understanding of how even simple events work (disregarding the linear movement). So I'm gonna re-write this code in a context that you can hopefully understand or at your level. Whatever.

Here's my version of your code (kept as simple as possible):
Code:
;..........................................
;This is the main game engine loop. All
;actions happen inside this loop.
;

GameEngineLoop:
    BIT $2002
    BPL GameEngineLoop
    JSR ReadGamepad
    JSR DoSomeOtherStuffs
    JSR DoSomeMoreGameLogic
    JSR DoJump
  jmp GameEngineLoop 
   
   
;..........................................
;Jump subroutine 
;
DoJump:
    LDA SpriteJumpState
    CMP #$00
    BEQ .out
    CMP #$09
    BCC .fall

.rise
    LDA Sprite1VerticalPosition
    SEC
    SBC #$02
    STA Sprite1VerticalPosition
    DEC SpriteJumpState
  RTS
 
.fall
    LDA Sprite1VerticalPosition
    CLC
    ADC #$02
    STA Sprite1VerticalPosition
    DEC SpriteJumpState
  RTS
   
.out
  RTS
 


;..........................................
;Set the jump routine in motion
;
ActivateJump:
    LDA #$10
    STA SpriteJumpState
  RTS
 


And here's the explanation: The game engine loop handle all action, including reading the gamepad. Notice how it's aligned to happen every vblank. At some point, after reading the gamepad, the "game logic" decides whether if the jump button is pressed - should receive that action (it passes the requirements, whatever they may be). It then calls ActivateJump which enables the jump subroutine to actually play that out. The jump routine is always called inside the main game engine loop, but the required state of the counter "SpriteJumpState" dictates whether this will happen or not (I could have used a flag variable instead). If the counter isn't 0, then it's decremented every time by the routine. If the counter is between $10 and $09, then it will subtract from the vertical position (jumps "up"), when the value is between $08 and $01, then it will add to the vertical position (fall down). Since the counter is always decremented, it moves from one states (going up) to eventually the opposite state (going down), until it reaches a third state (counter=0) which nothing happens. Again, nothing is optimal here; it's just to help show how you would handle an event such as 'jumping'.

If I'm wrong, and I completely misunderstood your post - then I apologize (I don't mean to insult)... and I just wasted my time. If I'm right though, I hope this helps.

_________________
__________________________
http://pcedev.wordpress.com


Top
 Profile  
 
PostPosted: Sat Feb 28, 2015 5:34 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10066
Location: Rio de Janeiro - Brazil
Prime wrote:
He made it clear coding is fairly new to him,supplying anything besides asm isn't helping him

I fail to see how supplying ASM code someone can copy and paste is of any actual help. Pseudo-code helps a person understand a concept, while providing ready-to-use code hardly teaches anything. A person will grow much more as a programmer if they understand WHAT they have to do before actually doing it. Then, if he does have trouble implementing the idea in assembly, we can go about solving that problem too, but just understanding the concept is a great first step, much better than just copying/pasting and ending with something that appears to work but he doesn't understand why.

Quote:
Why bother answering then,did he request assistance in C code?,Which has logic errors as pointed out.

ASM is a terrible language to convey ideas. ASM requires much more effort to understand than pseudo-code, and rainwarrior was trying to convey an idea, not a final product. A good teacher doesn't give you the answer, but instead helps you find it on your own.

Now back to the topic: What the OP is doing is a common mistake for people who are getting started with game programming, often coming from more linear types of programming. He has a loop that controls only one thing. This is what you'd do when you processing database records or something like that, because the goal there is to just do whatever you have to do with all those records and be done with them, but a game is different.

In a game, all the changes happen over time. You don't want to get it all done as fast as possible, otherwise players wouldn't be able to keep up. This is where VBlanks come in. Vertical blanks occur consistently 60 times per second, so they're perfect to set the pace of your game: every time a VBlank happens, you advance everything in the game 1 step. I said everything, so you can't focus on a single object/character. This will result in smooth 60fps animations, which is the goal of every game.

Programmers who don't have any experience in game programming often have trouble switching from the "get it all done at once" mentality to the "advance everything by 1 step each time" mentality, which is the basis of every game. That's normal, but I would suggest getting more acquainted with this concept before experimenting with more specific things such as physics. You should probably focus on getting your game objects to do something on their own (like moving left and right) with smooth 60fps animations (to make sure you have a working game loop), before giving them more complex behaviors.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 28 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

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