In this guide, you will learn how to replace Sonic CD's linear moving camera with the smooth one.
WARNING: This guide only works if you have succeeded to have a extended camera from Sonic CD, but you don't like it.
If you already have added extended camera, this tutorial may be simple for you. Go to your _incobj/01 Sonic.asm file and find the Sonic_PanCamera: label. It has a speed limit of how it will extend.
Replace this entire routine of the label with this
Code:
Sonic_PanCamera:
move.w (v_camera_pan).w,d1 ; d1 = Current camera pan value
move.w #320/2,d3 ; d3 = Default center position (160)
move.w obInertia(a0),d0 ; Get Sonic's ground speed
btst #1,obStatus(a0) ; Is Sonic in the air?
beq.s .IsGrounded ; If not, branch
move.w obVelX(a0),d0 ; If in the air, use X velocity
.IsGrounded:
; d0 stores the speed (positive/right or negative/left)
; Maps the speed to the pan offset
ext.l d0 ; Sign-extend d0 to 32-bit for multiplication
asl.l #6,d0 ; Multiply speed by 64 (6-bit Logical Shift Left)
divs.w #$0600,d0 ; Divide by $0600 (Normal maximum speed)
; Subtract from center to invert logic (look ahead of Sonic)
sub.w d0,d3 ; d3 = Center - Offset
; Ensure the pan target does not exceed boundaries (+-64 pixels)
cmpi.w #(320/2)-64,d3 ; Left boundary limit
bge.s .CheckMaxCap
move.w #(320/2)-64,d3
.CheckMaxCap:
cmpi.w #(320/2)+64,d3 ; Right boundary limit
ble.s .ApplyTweening
move.w #(320/2)+64,d3
.ApplyTweening:
; d3 = Perfect target position
; d1 = Current camera position
cmp.w d3,d1 ; Is the camera already at the target position?
beq.s .SetPanVal ; If yes, terminate
blo.s .TweenRight ; If current position < target, move right
.TweenLeft:
; Camera needs to move left (decrease d1 towards d3)
move.w d1,d2 ; d2 = Current position
sub.w d3,d2 ; d2 = Calculate distance to target (current - target)
lsr.w #3,d2 ; Divide distance by 8 (adjust smoothness here)
addq.w #1,d2 ; Guarantee minimum speed of 1px/frame
sub.w d2,d1 ; Apply movement to camera (move left)
cmp.w d3,d1 ; Did it overshoot the target?
bge.s .SetPanVal ; If not, continue
move.w d3,d1 ; If it overshot, snap to exact target
bra.s .SetPanVal
.TweenRight:
; Camera needs to move right (increase d1 towards d3)
move.w d3,d2 ; d2 = Target
sub.w d1,d2 ; d2 = Calculate distance to target (target - current)
lsr.w #3,d2 ; Divide distance by 8 (adjust smoothness here)
addq.w #1,d2 ; Guarantee minimum speed of 1px/frame
add.w d2,d1 ; Apply movement to camera (move right)
cmp.w d3,d1 ; Did it overshoot the target?
ble.s .SetPanVal ; If not, continue
move.w d3,d1 ; If it overshot, snap to exact target
.SetPanVal:
move.w d1,(v_camera_pan).w ; Save the new smoothed pan value into RAM
rts
Please replace your previous version of the 'Pan' Camera code with this newer, fresher one.
Code:
Sonic_PanCamera:
move.w (v_camera_pan).w,d1 ; d1 = Current camera pan value (Actual position)
move.w #320/2,d3 ; d3 = Default target: Screen center (160)
move.w #2,d4 ; d4 = DEFAULT SPEED (lsr.w #2 = Divisor 4 for normal running)
; --- ACTIVE STATE CHECK (SPINDASH / PEEL-OUT) ---
tst.b spindash_flag(a0) ; Is Sonic charging Spindash or Peel-out?
bne.s .ForcedPanDirection ; If yes, force full pan to the current direction
; --- MOVEMENT CHECK ---
move.w obInertia(a0),d0 ; Get Sonic's ground speed
btst #1,obStatus(a0) ; Is Sonic in the air?
beq.s .CheckGroundVel ; If not, check ground speed
move.w obVelX(a0),d0 ; If in the air, use X velocity
.CheckGroundVel:
and.w d0,d0 ; Is Sonic completely stopped?
beq.s .ApplyTweening ; IF STOPPED: Do not modify d3 (Keep default center = 160)
; --- DYNAMIC CALCULATION BY SPEED (Moving) ---
ext.l d0 ; Sign-extend to 32-bit
asl.l #6,d0 ; Multiply speed by 64
divs.w #$0600,d0 ; Divide by the game's maximum speed
sub.w d0,d3 ; d3 = Center (160) - Dynamic offset
bra.s .ClampCameraLimits
.ForcedPanDirection:
move.w #3,d4 ; OMIT ADDITIONAL CHANGES: PEEL-OUT SPEED CHANGE HERE
; (lsr.w #3 = Divisor 8. Makes Peel-Out panning slower and smoother)
; If charging Spindash/Peel-out while stationary, shift the target completely
btst #0,obStatus(a0) ; Check Sonic's direction (Bit 0: 0=Right, 1=Left)
beq.s .ForcedRight
; Looking left: push camera ahead of Sonic (Add to target d3)
add.w #64,d3 ; d3 = 160 + 64 = 224
bra.s .ApplyTweening
.ForcedRight:
; Looking right: push camera ahead of Sonic (Subtract from target d3)
sub.w #64,d3 ; d3 = 160 - 64 = 96
bra.s .ApplyTweening
.ClampCameraLimits:
; Ensures calculation by speed does not break safe boundaries of +-64 pixels
cmpi.w #(320/2)-64,d3 ; Left limit (96)
bge.s .CheckMaxCap
move.w #(320/2)-64,d3
.CheckMaxCap:
cmpi.w #(320/2)+64,d3 ; Right limit (224)
ble.s .ApplyTweening
move.w #(320/2)+64,d3
.ApplyTweening:
; d3 = Final validated target
; d1 = Current camera position
cmp.w d3,d1 ; Is the camera already at the target position?
beq.s .SaveAndExit ; If yes, exit without wasting processing time
bgt.s .TweenLeft ; If current (d1) > target (d3), move left
.TweenRight:
; --- IMPROVED TWEENING TO THE RIGHT ---
move.w d3,d2 ; d2 = Target
sub.w d1,d2 ; d2 = Remaining distance (Target - Current)
lsr.w d4,d2 ; Apply the speed stored in d4
addq.w #1,d2 ; Adjusted minimum speed to 1px for maximum smoothness
add.w d2,d1 ; Apply movement to the right
cmp.w d3,d1 ; Did it overshoot the target?
blt.s .SaveAndExit ; If not, save
move.w d3,d1 ; If it overshot, snap to exact target
bra.s .SaveAndExit
.TweenLeft:
; --- IMPROVED TWEENING TO THE LEFT ---
move.w d1,d2 ; d2 = Current
sub.w d3,d2 ; d2 = Remaining distance (Current - Target)
lsr.w d4,d2 ; Apply the speed stored in d4
addq.w #1,d2 ; Adjusted minimum speed to 1px for maximum smoothness
sub.w d2,d1 ; Apply movement to the left
cmp.w d3,d1 ; Did it overshoot the target?
bgt.s .SaveAndExit ; If not, save
move.w d3,d1 ; If it overshot, snap to exact target
.SaveAndExit:
move.w d1,(v_camera_pan).w ; Save the new corrected pan value into RAM
rts
Happy hacking!

Last edited:
WARNING: This guide only works if you have succeeded to have a extended camera from Sonic CD, but you don't like it.