[4.5.X] Player Object Jittering When Scrolling - Anyone else noticed this?

Jonny

Well-known member
This looks a lot worse in the actual game. Capture doesn't do it justice. Character looks a blur when scrolling in mesen.
I noticed this a while back but I found out that at one specific max speed the oscillating / jittering stopped. Unfortunately I don't really want that speed, its too fast for the game.
It's almost as if it were in sync with the scroll update only at 1 set speed, so the jittering stopped.

jittering.gif

Does anyone know whats happening here and how I might combat it? My scripts are not optimized, there's probably a lot of stuff I'm not using. Not sure if that would help with something like this, to get the sprite pos updating better?

If anyone has any suggestions, please let me know becasue I don't know where to start.
 

Attachments

  • jittering.gif
    jittering.gif
    1.1 MB · Views: 8
Last edited:

CluckFox

Active member
The Basic Brawler - Intermediate tutorial explained the jitter as the scrolling input script lagging the player-motion input by a tick.
 

Jonny

Well-known member
The Basic Brawler - Intermediate tutorial explained the jitter as the scrolling input script lagging the player-motion input by a tick.
Thank you, I'll take a look at that tutorial.
It doesn't look that bad on the capture above but in-game it's just a bunny blur. I'm guessing the video probably says something like "it does this but you'll have to fix that yourself". I'll have a watch though, maybe it'll give some clues.
 

Jonny

Well-known member
@CluckFox Just watched the video, it's explained quite well actually. So, if I'm understanding it correctly - I need to do some optimizing whereby I've got less code between where I'm updating the player object position and the camera stuff in doHandlePhysics.asm
Unless Joe means to close the gap in code with all the scripts between drawing the player and updating the camers but I wouldn't know in what order they ran in once compiled. I'll do some experimenting.
 

CluckFox

Active member
@CluckFox Just watched the video, it's explained quite well actually. So, if I'm understanding it correctly - I need to do some optimizing whereby I've got less code between where I'm updating the player object position and the camera stuff in doHandlePhysics.asm
Unless Joe means to close the gap in code with all the scripts between drawing the player and updating the camers but I wouldn't know in what order they ran in once compiled. I'll do some experimenting.
I've not myself tried any sort of scrolling optimization, but I interpret it the same way you do. Good luck!
 

Jonny

Well-known member
I've been experimenting a bit more with this. I noticed that if I limited the total_max_objects in doHandleObjects.asm the jittering goes away completely.
(obv not ideal as I might want 4 monsters in some parts, maybe)

I've started slimming down the parts I don't need in physics script but that didn't make a difference yet.
Not looked at scrolling input script, thats next, but locking the scroll to one way also seemed to make the jittering better.
 

dale_coop

Moderator
Staff member
The fun fact is if you set a player speed of "16", there is no jitter.
(... maybe any multiple of "16"?)
 

Jonny

Well-known member
The fun fact is if you set a player speed of "16", there is no jitter.
(... maybe any multiple of "16"?)

That could be right because I think my non-jittery speed was 32. It was just too fast.
It can sometimes be intermittent so hard to figure out what helps. For example, it only seems to happen when 2 way scrolling is enabled. I haven't seen it, or noticed it at least in any other nesmaker games. Maybe my rabbit needs the toilet :unsure:
 

Jonny

Well-known member
I noticed that jitter on every of my scrolling games with the 4.5. It's really annoying.
I was about to say I hadn't noticed it but all your games that I have tried have not been scrollers.
Well, I'll let you know if I discover anything that 100% fixes it.
I've seen it happen in some commercial NES-era games, like Darkwing Duck but not very often. Almost like it goes out of sync.
I'll just keep working on other parts of my game and making code more efficiant, maybe I'll discover more by accident along the way.
 

9Panzer

Well-known member
All good @Jonny I had Panzer at object speed 23 and changing it to 24 fixed the issue (as you noted above with the interval of 16). I figured that if it was an interval of 8 I would get the same result and I was correct.
 

dale_coop

Moderator
Staff member
In my project it's an interval of 16... else it's jittery.
Could it be related to the size of the player object or bounding box... 🤔
 
I have the Player Jittering too in the Metrovania module, any speed other than 16 have my player jitters.
I have tried combining the "do_simple_scroll" input script with the "move" input script together instead of them being two separate script but it did not resolve the Jittering.
 

dale_coop

Moderator
Staff member
Yep, I can use a multiple of "16" for the player's speed -> no jitter
Not really a limitation I was expecting >_<
 

Knietfeld

Member
I tried to solve this problem when I first saw this thread but failed. However, in working on my game last night I just found a solution. It's not too elegant, but it works.
I'll explain why it works and how to do it and mention a somewhat related issue that becomes more of a problem if you change scrolling the way I explain here.

The reason scrolling makes the sprites jitter is because of the way and order the variable camX is set and used.
I'm going to go into some detail about what's going on, if you don't care about why this is happening or my explanation doesn't make any sense, feel free to skip ahead.

The order it works in each frame is.
0. In the NMI routine camX is used to set the scroll value of the background
1. controller input tells the input routines do_simpleScrollLeft or right to set the scroll flags if player1_object is hitting the scroll pad.
2. Physics routine moves player object to a new location based on speed, etc. and saves new location to xHold_hi
3. after checking xHold_hi against tile collisions and Object collisions, doHandleObjectUpdate set's xHold_hi to Object_x_hi,x
4. The Tile Drawing routine uses Object_x_hi,x and camX to figure out where to draw the sprites, then it draws them
5. Then the Object update loop cycles through and moves and draws every other object the way it did with player object
6. after all objects are updated and drawn, doCamera (in MainGameLoop) uses player1_object's speed which was set way back in Physics routine to update camX
(if the controller input is activating the scroll byte)
0. then it cycles back to the NMI routine, and the background scroll is set.

TLDR, Sprites are being drawn using the camX offset before camX is set for the next frames Background scrolling location. This results in the objects locations lagging behind the background scroll by one frame every frame.

We can't just go to MainGameLoop and move "JSR doCamera" before "JSR doHandleObjects", because you need player1_object's updated speed variable to move camX properly.

So, my solution was to

In MainGameLoop.asm, comment out "JSR doCamera"

In doHandleObjects_withinCamera.asm, immediately after ".include SCR_HANDLE_PHYSICS" insert some stuff so it looks like this.

SwitchBank #$1C
TXA
PHA
.include SCR_HANDLE_PHYSICS

cpx player1_object
bne +
jsr doCamera
+

PLA
TAX
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;DEMO PHYSICS
ReturnBank

That fixes Object Jittering for every object except the player object, which has already been moved and the new location is saved in xHold_hi, waiting to be checked for collisions.
This is the part that I don't feel is very elegant. I keep thinking there must be some sweet spot maybe inside the physics routine, where doCamera could be plugged in and it would work for the player and everything else, but I haven't found it and I'm tired of trying for now.
So for now, I added two little bits to doUpdateCamera.asm to fix player jitter.

Because we moved doCamera to the middle of player1_Object's updating routine, the final x location for the player has not been set by the time camX is updated, so within doCamera we will just force the player's tentative x location to the scroll Pad.

It's easy to find the two places to modify. Just search (ctrl+f) " getCamSeam ". There are two results. The top one is at the end of the Left Scroll Stuff.
Right after getCamSeam add a bit so it looks like this.


STA camX_hi
JSR getCamSeam

lda #LEFT_SCROLL_PAD;kn- if scrolling, force player sprite to stick to scroll Pad
clc
adc camX
sta xHold_hi
LDA camX_hi
adc #$00
STA xHold_screen

JMP noHorizontalCameraUpdate

The next getCamSeam is at the end of the Right Scroll Stuff.

Add to become

STA camX_hi
JSR getCamSeam


lda #RIGHT_SCROLL_PAD ;kn- if scrolling, force player sprite to stick to scroll Pad
clc
adc camX
sta xHold_hi
LDA camX_hi
adc #$00
STA xHold_screen


noHorizontalCameraUpdate:

And That stops player character jitter when scrolling, but we've introduced a new problem.
Because scroll is activated any time you press left or right on the d-pad when your character is at or across one of the scroll pads, if you for example are in a room with a right scroll edge and you cross the scroll pad then stop, then press right again, you activate scroll again and your character is tossed back over to the scroll pad. Joe actually already solved for that problem in your scrolling input scripts, he just commented those lines out. Well, we need them now, so

uncomment the lines at the top of do_simpleScrollLeft.asm and do_simpleScrollRight.asm.

These lines deactivate scrolling if camX is in a room with a scroll edge. Scrolling Right works perfectly after you uncomment the lines, but do_simpleScrollLeft needs a little more work. Since camX represents the left edge of the gamescreen, when you scroll even one pixel to the left into a room that has the left edge bit ticked it stops scrolling. So we have to add a little check to make sure camX is on the far left side of that room before it deactivates scrolling.

It should look something like this:

LDA ScreenFlags00
AND #%00100000
BEQ +doActivateScrollByte

lda camX ;;kn-Added this check to let player actually see the room that is a left scroll edge
cmp #$03 ;; this is #$03 just because it suited me, you'll want to pick a number that is larger than your max speed, but small enough that it's not too noticeable that
bcs +doActivateScrollByte ;; you aren't seeing quite the whole screen

LDA scrollByte
AND #%00111111
STA scrollByte
RTS
+doActivateScrollByte:


However, the way scrolling is triggered causes some problems for us. If you're character has a gradual acceleration you will notice, (and maybe already have) that after scrolling your character slides a little bit but the camera doesn't move with them. Before implementing this scroll pad lock that I just told you to do, you could work your way to the edge of the screen because of the deceleration slide and break the game if you tried hard enough. That's because scrolling is only activated when you are pressing left or right on the controller, not when the player is moving to the left or right. After changing the code as I've shown you here, your character will still slide as it decelerates, but then if you continue in that direction they will pop back to the scroll pad when scroll is activated again.

This is the part where I was going to explain how to solve that problem but I'm out of time for the day and realized that my simple solution didn't work immediately. I modified my physics routine long ago to include activating scrolling by player movement instead of by controller input. I basically copied the input scripts into the end of isMovingRight and isMovingLeft and only triggered scrolls if x=player1_object. My physics code is completely different from the stock NESMaker Platformer physics now so I guess copy pasting doesn't work anymore. I'll see if I can come back and share the solution for that new, more obvious problem soon, but I'm out of time this afternoon.

Also, after coming back to address some issues @9Panzer brought to my attention, I realized that some similar problems will now affect Scroll locks that allow scrolling again after all the monsters on a screen are destroyed. I didn't take the time to make a screen that uses scroll lock in my demo project but I bet if you kill the last monster while the player character is to the active side of the scroll pad it will automatically stick your player to the scroll pad again. So that's another thing to deal with.

Like I said, I don't think this is an elegant solution. I don't like the solution to need a bunch of other solutions to solve new problems the first fix made.


Good Luck!
I hope I made enough sense to help someone! If anyone figures out a better way to implement this kind of fix I'd love to hear it.
 
Last edited:

9Panzer

Well-known member
@Knietfeld I ran through your guide and it looks like there is a problem with the release input. I'm finding that if I release on the edges of a screen that doesn't scroll it toss the player back to where the scrollpad is set. Also, It looks like I randomly just have the player vanish - which might be related.

Having said that - the player doesn't jitter! lol
 

Knietfeld

Member
@9Panzer Woo! I got one thing right! You are totally right. I forgot to test for that. When I want to share something I've coded in my game I always try to recreate it in a basic metroidvania project from a different NESmaker folder because I've got so much custom code in my main projects. I was just so excited to share that I forgot a couple other things that gave me trouble when I originally figured it out. I'll go add some stuff to the original post.

There's a new three lines added to the end of doRightCameraUpdate that fixes the random vanishing (it vanishes, i think, because when you went back into the nametable to the right your character was forced to the offscreen nametable and thus was deactivated, I had already solved that for doLeftCameraUpdate but it wasn't a problem in my original code... or maybe I just never ran to the right? So I thought I could skip it)

The other problem is solved by editing the input scripts that activate scrolling.

I realized it probably made reactivating scrolling after a monster lock become more janky too.

Maybe I should have waited until I had a better complete solution before trying to share. I had kind of accepted the jitter and I was just so excited to have found a fix for it that I had to share!
 
Last edited:
Top Bottom