jak1: fix balance-plats at high-fps (#4069)

Looking at the balance-plats, they showed an immediate visual clue,
which was the stuttering of Jak's feet on their surface.

This is precisely what happens when you don't include sticky on a
collision; it doesn't "pull" Jak along with it, and he repetitively
"falls" onto it's surface over and over as it moves downwards.

This is a mystery, since this actor does have sticky, in fact, it's not
even possible to detect riders without sticky, which these certainly do.
This same appears on PS2, so I knew fixing it was not an option.

I had an immediate theory of what was happening: since Jak is
continuously falling onto it as it moves downwards, he's only riding
some frames, and skipping others. A higher frame rate = more moments
he's touching.

This means that basically, the balance-plat's entire speed is determined
by when his feet happen to micro-stutter. This is the hardcoded scalar
for it's entire speed.

This means I needed to emulate the 60 FPS "falling" at different
framerates. I tried adding an fps counter to the actor so that the rider
check only ran once every 60th of a second, but I knew adding a variable
to the actor just for fps wasn't a good idea.

However, I was able to confirm that would work. Luckily, this
[comment](https://github.com/open-goal/jak-project/blob/0fa93ce7b83d1621f3fd040625bfa865eadaeef0/goal_src/jak1/engine/game/game-h.gc#L116)
gave me an idea, which meant I could use a variable of process drawable,
avoiding adding a variable.

This unique problem needs a unique solution, I gate rider checks so they
only pass if one sixtieth of a second `(seconds 0.01666666)` has passed
since the last ride. This makes it act like his feet are stuttering like
they do at 60 FPS, avoiding the extra frames where his feet are touching
at high framerates that add more speed.

I also added the same for the send-to messages, since at high frame
rates the passive return to center that runs every frame was stomping
the send-to-next velocities which run less often.

Then, I managed to refactor it down to a tiny edit, a guard in one place
that gates on 1/60th of a second, framerate already 60, or, self grow
(since you don't want to double-gate these).

```lisp
(when (or (= (-> *pc-settings* target-fps) 60)
          (time-elapsed? (-> self state-time) (seconds 0.01666666))
          (-> self got-grow))
```

It's just: this guard, `(set-state-time)` on ride, and
`(set-state-time)` on grow (limits increased-rate return to center
stomping grow).

Lastly, there was an issue with the existing multiplies by FPS RATIO,
which was a clear double scale of the acceleration (once when setting
accelerate, then again when adding the accelerate). I'm guessing since
the extra riding frames added so much speed, this double scalar helped
slow it down, however when corrected the double scalar made it too slow
since it was slowing down for framerate twice.
This commit is contained in:
Grateful Forest
2025-12-29 07:13:34 +10:30
committed by GitHub
parent f4512a41dd
commit 739a2ffeb6
+35 -30
View File
@@ -340,36 +340,41 @@
;; og:preserve-this modified for high fps
(let ((f30-0 (* -0.025 (+ (-> self y-offset) (-> self y-travel))))
(f28-0 (* -0.025 (- (-> self y-offset) (-> self y-travel)))))
(cond
((and (-> self root riders) (nonzero? (-> self root riders num-riders)))
;; has rider, accelerate downwards
(send-event *target* 'no-look-around (seconds 0.25))
(set! (-> self y-accel) (fmin 4.096 (fmax -4.096 (+ (* DISPLAY_FPS_RATIO -0.2048) (-> self y-accel)))))
(set! (-> self y-vel) (fmin f28-0 (fmax f30-0 (+ (-> self y-vel) (* DISPLAY_FPS_RATIO (-> self y-accel))))))
;; tell linked platforms to go up
(send-to-all-after (-> self link) 'grow)
(send-to-all-before (-> self link) 'grow))
((-> self got-grow)
;; got 'grow' signal, accelerate upwards
(set! (-> self got-grow) #f)
(set! (-> self y-accel) (fmin 4.096 (fmax -4.096 (+ (* DISPLAY_FPS_RATIO 0.2048) (-> self y-accel)))))
(set! (-> self y-vel) (fmin f28-0 (fmax f30-0 (+ (-> self y-vel) (* DISPLAY_FPS_RATIO (-> self y-accel)))))))
((< (-> self y-offset) 0.0)
;; accelerate towards neutral position (from below)
(set! (-> self y-accel)
(fmin (fmin 4.096 (fmax -4.096 (+ (* DISPLAY_FPS_RATIO 0.1024) (-> self y-accel)))) (* -0.0001 (-> self y-offset))))
(set! (-> self y-vel) (fmin f28-0 (fmax f30-0 (+ (-> self y-vel) (* DISPLAY_FPS_RATIO (-> self y-accel))))))
;; slow down
(set! (-> self y-vel) (* (- 1.0 (* DISPLAY_FPS_RATIO (- 1.0 0.99))) (-> self y-vel))))
(else
;; accelerate towards neutral position (from above)
(set! (-> self y-accel)
(fmin 4.096 (fmax (fmax -4.096 (+ (* DISPLAY_FPS_RATIO -0.1024) (-> self y-accel))) (* -0.0001 (-> self y-offset)))))
(set! (-> self y-vel) (fmin f28-0 (fmax f30-0 (+ (-> self y-vel) (* DISPLAY_FPS_RATIO (-> self y-accel))))))
;; slow down
(set! (-> self y-vel) (* (- 1.0 (* DISPLAY_FPS_RATIO (- 1.0 0.99))) (-> self y-vel))))))
(+! (-> self y-offset) (* DISPLAY_FPS_RATIO (-> self y-vel)))
(set! (-> self root trans y) (+ (-> self y-init) (-> self y-offset)))
(when (or (= (-> *pc-settings* target-fps) 60) ;; always run untouched if 60 FPS
(time-elapsed? (-> self state-time) (seconds 0.01666666)) ;; added for high-fps, gate riding like 60 fps
(-> self got-grow)) ;; don't gate grow recieve since grow send is gated
(cond
((and (-> self root riders) (nonzero? (-> self root riders num-riders)))
(set-state-time) ;; added for high-fps, record last ride to gate to 60 FPS
;; has rider, accelerate downwards
(send-event *target* 'no-look-around (seconds 0.25))
(set! (-> self y-accel) (fmin 4.096 (fmax -4.096 (+ -0.2048 (-> self y-accel)))))
(set! (-> self y-vel) (fmin f28-0 (fmax f30-0 (+ (-> self y-vel) (* DISPLAY_FPS_RATIO (-> self y-accel))))))
;; tell linked platforms to go up
(send-to-all-after (-> self link) 'grow)
(send-to-all-before (-> self link) 'grow))
((-> self got-grow)
(set-state-time) ;; added for high-fps, gate after grow recieve to stop passive return stomping it at high fps
;; got 'grow' signal, accelerate upwards
(set! (-> self got-grow) #f)
(set! (-> self y-accel) (fmin 4.096 (fmax -4.096 (+ 0.2048 (-> self y-accel)))))
(set! (-> self y-vel) (fmin f28-0 (fmax f30-0 (+ (-> self y-vel) (* DISPLAY_FPS_RATIO (-> self y-accel)))))))
((< (-> self y-offset) 0.0)
;; accelerate towards neutral position (from below)
(set! (-> self y-accel)
(fmin (fmin 4.096 (fmax -4.096 (+ 0.1024 (-> self y-accel)))) (* -0.0001 (-> self y-offset))))
(set! (-> self y-vel) (fmin f28-0 (fmax f30-0 (+ (-> self y-vel) (* DISPLAY_FPS_RATIO (-> self y-accel))))))
;; slow down
(set! (-> self y-vel) (* (- 1.0 (* DISPLAY_FPS_RATIO (- 1.0 0.99))) (-> self y-vel))))
(else
;; accelerate towards neutral position (from above)
(set! (-> self y-accel)
(fmin 4.096 (fmax (fmax -4.096 (+ -0.1024 (-> self y-accel))) (* -0.0001 (-> self y-offset)))))
(set! (-> self y-vel) (fmin f28-0 (fmax f30-0 (+ (-> self y-vel) (* DISPLAY_FPS_RATIO (-> self y-accel))))))
;; slow down
(set! (-> self y-vel) (* (- 1.0 (* DISPLAY_FPS_RATIO (- 1.0 0.99))) (-> self y-vel))))))
(+! (-> self y-offset) (* DISPLAY_FPS_RATIO (-> self y-vel)))
(set! (-> self root trans y) (+ (-> self y-init) (-> self y-offset))))
(suspend)))
:post rider-post)