Up and down slope

charliee

New member
I couldn't find anything that specifically addressed non-orthogonic movement in a simple way, so I made this little snippet. Thought it might be interesting to some.
(See attached)
 

Attachments

  • Mesen - game 2021-06-21 18-27-23.zip
    925.9 KB · Views: 41

Bucket Mouse

Active member
Care to share with us how you did that? There's been a debate in the NESMaker Discord about how to make slopes happen.
 

charliee

New member
Here's the pertinent snippet:



Code:
;;;Slope.asm;;;
;This routine allows for player to walk up or down a slope by walking "through" a tile.
;This is set up for specific tile locations = NOT generic

;These are the "bounding box" corners for the slope rectangle
LeftX equ $1D
LeftY equ $30
RightX equ $6D
RightY equ $80

;Note #1: Need user variable "floatXDelta"

;Check if player
cpx player1_object
beq DoSlope
jmp NoSlope

;==============INITIALIZE==============
DoSlope:
;Save Registers
pha

;Set gravity to zero
lda #$00
sta Object_v_speed_lo,x
sta Object_v_speed_hi,x

;==========MAIN MATH===========
;This routine calculates the vertical position of the player
;depending upon where he moves horizontally along the slope = auto up/down
;==============================
;Do the math to find new Y based on X
sec
;Get the player H position
lda #Object_x_hi
;Calculate H distance from start of slope
sbc #LeftX
;Save the distance
sta floatXDelta
clc
;Get the V position of the slope
lda #LeftY
;Calculate the new V position of the player
adc floatXDelta

;Have we gone past the block boundary?
cmp #$81; this value hardcoded to match screen location
;No = save new V position
bne SavePlayerYPos
;Yes = reset V position
lda #$80; this value hardcoded to match screen location
;Save the updated position
SavePlayerYPos:
sta yHold_hi       

;Restore registers
DoneSlope:
pla

NoSlope:
rts
 

Jonny

Well-known member
Looks amazing. So, did you set this up as a tile type or is this a routine which is called from somewhere else? Trying to understand how this works from the code particularly what happens after the slope, for example if there's a solid after an incline.
 

charliee

New member
Yes, there can be a problem in that area.

Short answer: That is handled by the code block starting with ";Have we gone past the block boundary?". It looks at the final Y position to check if it is illegal ($81). If so, reset the position to legal ($80) before making it active.

Long answer: Because the player is animated, and you are transitioning from a "gravity disabled" block (the slope) to a "gravity enabled" block (the solid), it is possible for the player to be positioned inside the solid block, and get stuck there = you can try to walk, but although the player animates, he won't actually change position. To get out of the solid block and get on top of it (as it should be for normal walking), you can either perform a jump (which is somewhat un-realistic, but could make for a brain-teaser type game), or make sure you don't fall into the block in the first place. That's what the block-boundary code does. You'll note the hardcoded values for this ($80, $81). Those values depend upon where that solid block is positioned in relation to the slope. As you can probably guess, my solid block has its' top at $80...so I hardcode for that. You'll note that the solid block at the right end of the slope has (like many other solid, walkable blocks), a two-pixel tall stripe on the top. This helps disguise the transition "oopsie".

As a practical matter, you must be aware that the two transition areas between the solids and slopes cause problems because it is not possible to guarantee that the players' automatic animation vs his manual movement won't result in a double hit of both the solid/gravity block and the slope/no-gravity block. If this occurs, the player could hit the solid block first, thus turning on the gravity, but be located in the slope block that is NOT solid, and thus gravity will pull the player down.

To handle this type of situation, I had to create a section I called "Transition Math", which is not shown in this snippet.
 

Basty

Active member
I don't think character's animation should affect things at all especially considering that NES maker deals with hitboxes, not precise character's animation like Game Maker.
 

Bucket Mouse

Active member
It is definitely a tile, because I just tried it as a tile and it worked. A note about the jerky movement you're getting when you go down the slope in the video...it's happening because the sprite is crossing out of the slope tile at points. If you double up the slope tile horizontally, like this, that won't happen:

slopetile.jpg
This works for some uses, like Castlevania stairs, but I wouldn't consider the slope issue solved. The fact that it has to be in a specific spot isn't good. The fact that you need gravity turned off to walk up the slope is another problem, as well as not being able to jump. I tried to fix it. It's easy to program the jump input to recognize the slope tile, but it doesn't know WHERE exactly to touch down and you just fall through. To continue further would involve math -- my weakness.

I'm still hoping Nietfeld shows up again and explains how he made his slope routine.
 

Knietfeld

Member
Ah jeeze, I've been called out.
I was following this thread to see what you guys came up with so I wouldn't have to work as hard if I ever wanted to make slopes again.

In my project I was working on I didn't have to turn gravity off or anything. You could jump off slopes and I even had objects bouncing and rolling off slopes though that was still pretty janky. It was all very complicated. My player did still flip between running and falling animations too. I figured it was because his horizontal movement was faster than gravity was pulling him to the slope. So it was like he was skipping steps going downhill and I was fine with that.

I did all the slope stuff in 4.1.5 and it spanned across a bunch of different scripts so it would be hard to compile and show what I did. It got pretty messy I think. I also haven't spent a lot of time learning how 4.5.9's tile collisions differ from the old one, but I'll see if I can explain some of the stuff I did.

First, the references that got me pointed in the right direction.



I modified the collisions routine to include a point at the center bottom of every object. When an object was colliding with a solid floor tile the collision routine would eject the object up to the top of that tile. My tile script was just

lda #%01011000 ;say it's on this kind of angle
sta tile_solidity

but in the eject routine I made a "special eject"

lda temp2 ;check if bottom center collided with special tile (tile_solidity of tile colliding with bottom center point is in temp 2 at this point)
and #%00001000 ;could use #%00001000 to indicate whether to use a special eject table and the top four bits 00 to f0 to determine which table to use
bne SpecialEject




SpecialEject:

;get the special eject table pointer, store it to Y register
lda temp2 ;(Still tile_solidity)
and #%01110000
sta temp
++
lda Object_x_hi,x ;xHold_hi
clc
adc mtemp3 ;horizontal center of object
and #%00001111
ora temp
tay

;look up the special eject point for this pixel from the tables
;compare y location and eject point to see if you need to eject (So if you are just falling into that tile you don't immediately pop to standing on the slope)
lda tileY
and #%00001111
sta temp1
cmp Special_Tile_Eject_Tables,y
bcs DoSpecialEject
DontSpecialEject:;(or eject at all)
rts

My table looked like this.


Special_Tile_Eject_Tables:;7 possible types numbered from #$00 to #$70
;00 _iii_ #%0000 1000

.db #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08

;10 _0o_ #%0001 1000
.db #$00, #$01, #$02, #$03, #$04, #$05, #$05, #$07, #$08, #$09, #$0a, #$0b, #$0c, #$0d, #$0d, #$0d

;20 _0i_ #%0010 1000
.db #$00, #$00, #$01, #$01, #$02, #$02, #$03, #$03, #$04, #$04, #$05, #$05, #$05, #$07, #$07, #$07

;30 _io_ #$0011 1000
.db #$08, #$08, #$09, #$09, #$0a, #$0a, #$0a, #$0b, #$0b, #$0b, #$0c, #$0c, #$0d, #$0d, #$0d, #$0d

;40 _iii_ ;not really in use yet but need something to take up the space
.db #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08, #$08

;50 _o0_ #%0101 1000
.db #$0f, #$0e, #$0d, #$0c, #$0b, #$0a, #$09, #$08, #$07, #$05, #$05, #$04, #$03, #$02, #$01, #$00

;60 _i0_ #%0110 1000
.db #$08, #$07, #$07, #$05, #$05, #$05, #$04, #$04, #$03, #$03, #$02, #$02, #$01, #$01, #$00, #$00

;70 _oi_ #%0111 1000
.db #$0f, #$0f, #$0e, #$0e, #$0d, #$0c, #$0b, #$0b, #$0b, #$0a, #$0a, #$0a, #$09, #$09, #$08, #$08



So for example in the image, the tile solidity was #%01011000, and the center point I marked with a yellow pixel is #$01 pixels in, that means, according to the table that instead of ejecting to the top of that tile, it ejects #$0f down from that.

In that code base there was also a bit to flip if your object was supposed to be on the ground, so that was flipped when the object was ejected onto a slope.

There were obviously drawbacks to all my changes. I think I ended up having to make more collision points below the object bounding boxes adding up to maybe 7 points. All of those checks slowed things down even more when a few objects were onscreen. I had to add more checks and conditions if my player was ascending a slope and bumped into a wall or ceiling forcing him to duck. (that's why the table above skips #$06, that's where he got stuck). Also, I don't remember exactly why, but you always had to have a solid or jump through tile below every slope tile for things to work right. I think for just a pixel you have to go into a blank tile so one foot had to be standing on a solid jump through tile or else the object would bounce or something. Because of those solid tiles next to/below all of the slope tiles that means the bottom corners of the object's bounding box would always be colliding with a solid, so you have to tell it to ignore that but the top corners still need to check for solids or else you'll run yourself inside a wall if the slope goes right up to it.

That's kind of how it always works though, isn't it. You get the big idea mostly working and then spend a lot of time patching up all the little things so everything works just the way you want it. Or design around the limitations you've made for yourself.

Anyway, I hope this gives you guys some ideas. I don't know how much modification would have to be done to get something like this working in 4.9.5. I know the tile_solidity variable isn't even used, at least not with that name. Good luck!
 

Attachments

  • Jim on the slope.png
    Jim on the slope.png
    1.3 KB · Views: 11

Jonny

Well-known member
@Knietfeld sorry to go off topic a bit here but is the game itself still being developed? I thought it was looking really good. In fact, there's quite a few projects from byte-off etc that I wonder if they're still on-going. I recently noticed that rumblefest is now being developed on unity, not NM and its looking awesome.

But anyway, thank you for very much for explaining your version of slopes. Interesting to see two different approaches. I expect this thread will be getting bookmarked by a lot of people for future projects.
 

charliee

New member
1. By "animations", I mean the movement of the character as controlled by the keypad, not the "automatic" action of the walking steps. As I indicated, the problem with transitions is that the bounding box hits two tile with differing characteristics, and you can't control which gets hit first.
2. If you "double up the slope tile horizontally", you end up with a standard full block (as is shown in your graphic), you get a stair-step, not a slope. I wanted as-near-as-possible a smooth line as the slope so it would look as natural as possible when is a grass-covered hill.
3. "The fact that it has to be in a specific spot isn't good". The "specific spot" concept only means that you have to indicate the start and end of this particular slope, which I do in the "bounding box" section. Move the slope as a whole to the right, left, up, or down, the code doesn't change, just the equates.
4. If I don't turn off gravity, the player will sink down to the next solid tile (which in my screen is all the way down at the bottom). Keeping gravity off means I can stop on the slope and remain there.
5. I don't understand the concept of the "eject" thingy, that's WAY too much code for a simple "up or down at a relatively smooth angle". Nesmaker certainly allows the tiletop-to-tiletop operations, which, as I mentioned previously, is too much of a staircase and not so much a slope. I would think that using the stairstep while having the player slide down an icy hill (for example), would NOT be the desired effect. This approach smooths that out considerably.
6. Yes, this is a WIP. That's why I posted in this section. And there are other items to fix. For example, when the player goes up/down the stairs, he doesn't necessarily have to be centered on the stairs; he could "hang off" the right or left side. I thought this was a really bad effect to have to live with, especially since it is so simple to fix. My next peeve is that, when on a ladder and you stop up/down, the player halts but the animation keeps him climbing while he doesn't go anyplace. Also, upon review, I think the player is moving too fast, and it makes the actions too jerky...I'm going to have to experiment with the speed.

The goal here was to present a simple, easy to replicate general purpose slope.
 

charliee

New member
So I finally got around to cleaning up the slope stuff. I was able to remove the very kludgy TransitionMath and include it inside the slope code...made things a lot simpler, You'll note that I repositioned the slope over to the left edge of the screen, and lowered the upper floor to I could make the slope longer. Here's the new code:

Code:
SlopeDown:
;;;SlopeDown.asm;;;
;This routine allows for player to walk up or down a slope by
;walking "through" a tile.
;The "bounding box" of the slope must be given in the EQUATES

;These are the "bounding box" corners for the slope rectangle
LeftX equ $00 ;first "left-most" pixel, horizontal
LeftY equ $40 ;first "top-most" pixel, vertical
RightX equ $6F ;last "right-most" pixel, horizontal
RightY equ $AF ;last "bottom-most" pixel, vertical

;These are the locations in the 16x16 player tile where the V and H centers are located
TileOffsetV equ $08 ;Middle of 16 pixel tile vertical
TileOffsetH equ $08    ;Middle of 16 pixel tile horizontal

;This is a "Fudge-factor" because NES puts Y off by 1 one pixel + 1 for "clean" looking player movement
BottomTransitionH equ $02 

;Note #1: Need user variable "floatXDelta"
;Note #2: Object bounding box must go to one pixel up from bottom of object
;Note $3: For the display to look "cleanest", the top-most/left-most tile of the slope
;    is of type "Slope", but the tile image should be that of a flat walkable tile

;Check if player
cpx player1_object
beq DoSlopeDown
jmp NoSlopeDown

;==============INITIALIZE==============
DoSlopeDown:
;Save Registers
pha

;Set gravity to zero
lda #$00
sta Object_v_speed_lo,x
sta Object_v_speed_hi,x

;==========MAIN MATH===========
;This routine calculates the vertical position of the player
;depending upon where he moves horizontally along the slope = auto up/down
;==============================
;Do the math to find new Y based on X...
;Get the player H position
lda #Object_x_hi   

;Are we too much left (= collision with left edge)?
cmp #(#LeftX + #TileOffsetH)
;Get out if yes
bcc DoneSlopeDown

;Are we more than right edge (= collision with non-slope tile)?
cmp #((#RightX - #TileOffsetH) + #BottomTransitionH)
;Get out if yes
bcs DoneSlopeDown

;On slope, so calculate H distance from start of slope
sbc #LeftX
;Save the distance
sta floatXDelta
;Setup for delta between start of slopeY and center of player
clc
;Get the V position of the slope
lda #(#LeftY - #TileOffsetV)

;Calculate the new V position of the player
adc floatXDelta

;Save the Y position
SavePlayerYPosDown:
sta yHold_hi
sta Object_y_hi

;Restore registers
DoneSlopeDown:
pla

NoSlopeDown:
rts

The example vid shows both a full speed run down the slope, as well as a few-steps-at-a-time movement.
 

Attachments

  • Mesen - game 2021-07-15 15-17-05.zip
    1 MB · Views: 14

laurentpb

New member
@Knietfeld, I really like the way you did your slope.
Could you explain or reference me to understand how to use the table afterward to substract the right amount of vertical ejection in the DoSpecialEject ?
I'm guessing you put DoSpecialEject in TileCollision ? And could you tell me where is the more elegant location to place the table?

It is my first time that I directly modify Tables, so the concept is pretty new to me, but it seems really useful!!
 

Attachments

  • capture.zip
    2 MB · Views: 5
Last edited:

Knietfeld

Member
@laurentpb , It looks like you got slopes working in your game, good job!

Based on the method I described in this thread I developed a good way to do slopes in 4.5.9 and started a new thread about it here.

That link has my code and instructions on how to use it.
It was a while ago so I don't remember the answers to all your questions, but I left lots of comments in the code to explain what everything was doing so some of your questions could be answered by looking at the code and reading the comments. If you are trying to modify the tables, just try putting in any numbers 00-0F and see what happens. The numbers change how many pixels below the top of the tile the character is placed.
 
Top Bottom