Need help with camera shaking

I’m a new godot developer and i need help with the player’s camera shaking when they touch the hitbox, the hit is registered inside the object that hits, because it was easier for me(for some reason)

here’s the player code:

extends CharacterBody2D

var pitch_cooldown := 0.0
const GRAVITY = 200.0
const WALK_SPEED = 200
@export var SonicPitch = int(0)
var sonic_sound_playing = false
var coyote_time := 0.15  # Time allowed after leaving ground to still jump
var coyote_timer := 0.0
@export var Health = float(4.0)

func _physics_process(delta):
	pitch_cooldown -= delta
	velocity.y += delta * GRAVITY
	if Health > 0.0:
		if is_on_floor():
			coyote_timer = coyote_time
		else:
			coyote_timer -= delta	
		# Sonic Pitch Controls
		if pitch_cooldown <= 0.0:
			if Input.is_key_pressed(KEY_D) and not Input.is_key_pressed(KEY_X):
				pitch_cooldown = 1.0
				for i in 2:
					for m in 32:
						$Camera2D/CanvasLayer/SonicUI.rotation_degrees += 10
						await get_tree().create_timer(0.000001).timeout
				$Camera2D/CanvasLayer/SonicUI.rotation = 0
				if SonicPitch < 0:
					$Sprite2D/Sonicclickup.play()
				elif SonicPitch > 0:
					$Sprite2D/Sonicclickdown.play()
				SonicPitch = 0
						
			
			elif Input.is_physical_key_pressed(KEY_A) and SonicPitch > -5 and not Input.is_key_pressed(KEY_X):
				pitch_cooldown = 1.0
				for i in 32:
					$Camera2D/CanvasLayer/SonicUI.rotation_degrees += 1
					await get_tree().create_timer(0.0001).timeout
				SonicPitch -= 1
				$Sprite2D/Sonicclickup.play()
				
			elif Input.is_physical_key_pressed(KEY_S) and SonicPitch < 5 and not Input.is_key_pressed(KEY_X):
				pitch_cooldown = 1.0
				for i in 32:
					$Camera2D/CanvasLayer/SonicUI.rotation_degrees -= 1
					await get_tree().create_timer(0.0001).timeout
				SonicPitch += 1
				$Sprite2D/Sonicclickup.play()
		
		# Sonic Screwdriver Sound Handling
		if Input.is_key_pressed(KEY_X) and coyote_timer > 0.0:
			if SonicPitch == 0.0:
				if not $Sprite2D/sonicsound0.is_playing():
					$Sprite2D/sonicsound0.play()
					sonic_sound_playing = true
			elif SonicPitch == 1.0:
				if not $Sprite2D/sonicsound1.is_playing():
					$Sprite2D/sonicsound1.play()
					sonic_sound_playing = true
			elif SonicPitch == 2.0:
				if not $Sprite2D/sonicsound2.is_playing():	
					$Sprite2D/sonicsound2.play()
					sonic_sound_playing = true
			elif SonicPitch == 3.0:
				if not $Sprite2D/sonicsound3.is_playing():
					$Sprite2D/sonicsound3.play()
					sonic_sound_playing = true
			elif SonicPitch == 4.0:
				if not $Sprite2D/sonicsound4.is_playing():
					$Sprite2D/sonicsound4.play()
					sonic_sound_playing = true
			elif SonicPitch == 5.0:
				if not $Sprite2D/sonicsound5.is_playing():
					$Sprite2D/sonicsound5.play()
					sonic_sound_playing = true
			elif SonicPitch == -1.0:
				if not $Sprite2D/"sonicsound-1".is_playing():
					$"Sprite2D/sonicsound-1".play()
					sonic_sound_playing = true
			elif SonicPitch == -2:
				if not $"Sprite2D/sonicsound-2".is_playing():
					$"Sprite2D/sonicsound-2".play()
					sonic_sound_playing = true
			elif SonicPitch == -3:
				if not $"Sprite2D/sonicsound-3".is_playing():
					$"Sprite2D/sonicsound-3".play()
					sonic_sound_playing = true
			elif SonicPitch == -4:
				if not $"Sprite2D/sonicsound-4".is_playing():
					$"Sprite2D/sonicsound-4".play()
					sonic_sound_playing = true
			elif SonicPitch == -5:
				if not $"Sprite2D/sonicsound-5".is_playing():
					$"Sprite2D/sonicsound-5".play()
					sonic_sound_playing = true
	
			# Stop movement when sonic sound is active
			velocity.x = 0
			$Sprite2D/AnimationPlayer.play("ScrewDriverhold")
		
		# Stop Sonic Sound when X is released
		elif not Input.is_key_pressed(KEY_X) and sonic_sound_playing:
			if SonicPitch == 0:
				$Sprite2D/sonicsound0.stop()
			elif SonicPitch == 1:	
				$Sprite2D/sonicsound1.stop()
			elif SonicPitch == 2:	
				$Sprite2D/sonicsound2.stop()
			elif SonicPitch == 3:	
				$Sprite2D/sonicsound3.stop()
			elif SonicPitch == 4:	
				$Sprite2D/sonicsound4.stop()
			elif SonicPitch == 5:	
				$Sprite2D/sonicsound5.stop()
			elif SonicPitch == -1:
				$"Sprite2D/sonicsound-1".stop()
			elif SonicPitch == -2:
				$"Sprite2D/sonicsound-2".stop()
			elif SonicPitch == -3:
				$"Sprite2D/sonicsound-3".stop()
			elif SonicPitch == -4:
				$"Sprite2D/sonicsound-4".stop()
			elif SonicPitch == -5:	
				$"Sprite2D/sonicsound-5".stop()
			sonic_sound_playing = false
		
		# Player Movement and Animation
		elif Input.is_action_pressed("ui_left") and Input.is_physical_key_pressed(KEY_Z) and is_on_floor():
			if coyote_timer > 0.0:
				velocity.x = -WALK_SPEED
				if $Sprite2D/AnimationPlayer.current_animation != "Jump_Animation":
					$Sprite2D/AnimationPlayer.play("Jump_Animation")
				if is_on_floor():
					velocity.y = -GRAVITY
			else:
				pass
	
		elif Input.is_action_pressed("ui_right") and Input.is_physical_key_pressed(KEY_Z) and is_on_floor():
			if coyote_timer > 0.0:
				velocity.x = WALK_SPEED
				if $Sprite2D/AnimationPlayer.current_animation != "Jump_Animation":
					$Sprite2D/AnimationPlayer.play("Jump_Animation")
				if is_on_floor():
					velocity.y = -GRAVITY
			else:
				pass
	
		elif Input.is_action_pressed("ui_right") and Input.is_action_pressed("ui_left"):
			velocity.x = 0
			$Sprite2D/AnimationPlayer.play("Idle_Animation")
	
		elif Input.is_action_pressed("ui_left"):
			velocity.x = -WALK_SPEED
			$Sprite2D/AnimationPlayer.play("Walk_Animation")
		elif Input.is_action_pressed("ui_right"):
			velocity.x =  WALK_SPEED
			$Sprite2D/AnimationPlayer.play("Walk_Animation")
		elif Input.is_key_pressed(KEY_Z):
			if coyote_timer > 0.0:
				if $Sprite2D/AnimationPlayer.current_animation != "Jump_Animation":
					$Sprite2D/AnimationPlayer.play("Jump_Animation")
				if is_on_floor():
					velocity.y = -GRAVITY
			else:
				pass
		elif not Input.is_key_pressed(KEY_Z) and $Sprite2D/AnimationPlayer.current_animation == "Jump_Animation":
			if coyote_timer > 0.0:
				if $Sprite2D/AnimationPlayer.current_animation != "Jump_Animation":
					$Sprite2D/AnimationPlayer.play("Jump_Animation")
			else:
				pass
		
		# Default Idle State
		else:
			velocity.x = 0
			$Sprite2D/AnimationPlayer.play("Idle_Animation")
	
		# Flip sprite direction based on velocity
		if velocity.x:
			$Sprite2D.flip_h = velocity.x < 0
			$Sprite2D/SonicHitbox/CollisionShape2D.position.x = absf($Sprite2D/SonicHitbox/CollisionShape2D.position.x) * signf(velocity.x)

		if not is_on_floor() and velocity.y > 0: # in air, AND dropping
			if $Sprite2D/AnimationPlayer.current_animation != "Fall_Animation":
				$Sprite2D/AnimationPlayer.play("Fall_Animation")
	
		# Move player
		move_and_slide()

	
	if Health <= 4.0 and not Health <=3.0 or Health > 4.0 and not Health <= 3.0:
		$"Camera2D/CanvasLayer/Full Health".show()
		$"Camera2D/CanvasLayer/slightly damaged".hide()
		$"Camera2D/CanvasLayer/Managable".hide()
		$"Camera2D/CanvasLayer/Close to regeneration".hide()
	if Health <= 3.0 and not Health <=2.0:
		$"Camera2D/CanvasLayer/Full Health".hide()
		$"Camera2D/CanvasLayer/slightly damaged".show() 
		$"Camera2D/CanvasLayer/Managable".hide()
		$"Camera2D/CanvasLayer/Close to regeneration".hide()
	if Health <= 2.0 and not Health <=1.0:
		$"Camera2D/CanvasLayer/Full Health".hide()
		$"Camera2D/CanvasLayer/slightly damaged".hide()
		$"Camera2D/CanvasLayer/Managable".show()
		$"Camera2D/CanvasLayer/Close to regeneration".hide()
	if Health <= 1.0:
		$"Camera2D/CanvasLayer/Full Health".hide()
		$"Camera2D/CanvasLayer/slightly damaged".hide()
		$"Camera2D/CanvasLayer/Managable".hide()
		$"Camera2D/CanvasLayer/Close to regeneration".show()


func _on_area_2d_body_entered(body: StaticBody2D) -> void:
	if body.is_in_group("Mechanic"):
		if body.SonicPitch_req == SonicPitch:
			if body.has_method("Breaking"):
				body.Breaking()
			else:
				pass
	else:
		pass

and here’s the stationary hit object code:

extends Area2D

@onready var player = get_node("/root/test/CharacterBody2D")
@onready var camera = get_node("/root/test/CharacterBody2D/Camera2D")
	
func _on_body_entered(body) -> void:
	if body.is_in_group("Hurtbox"):
		player.Health -= 1.0
		$Timer.start()

func _on_body_exited(body) -> void:
	if body.is_in_group("Hurtbox"):
		$Timer.stop()

func _on_timer_timeout() -> void:
	player.Health -= 1.0

I’d be really glad if someone can help with making the camera shake, i tried with camera.Offset.x = {number} but it doesn’t work, and also {number} is just an example that i added instead of adding float, because it was unnecessary to the question to not add any confusion. And sorry if there’s bad grammar, english is not my first language.

If your player is or has the group “Hurtbox” it’s much better to get the player reference that way, rather than by a root path. I’m assuming you want this camera shake as part of taking damage, as the player has Health and also has the Camera as a child you should make a function on the player to take damage, applying both the health loss and camera shake.

# in player script
func take_damage(amount: float) -> void:
	Health -= amount
	# If you have a camera shake AnimationPlayer
	$AnimationPlayer.play("camera_shake")

# in damage area script
extends Area2D
var target
	
func _on_body_entered(body) -> void:
	if body.is_in_group("Hurtbox"):
		target = body
		target.take_damage(1.0)
		$Timer.start()

func _on_body_exited(body) -> void:
	if body == target:
		target = null
		$Timer.stop()

func _on_timer_timeout() -> void:
	target.Health -= 1.0

Now offset works for me, so I’d recommend using that in your take_damage function. You don’t have any code to alter the camera posted so I’d recommend using an AnimationPlayer

You can add a script to the camera itself. You could do that, and then something like:

# NOTE: Untested!
extends Camera2D

@onready shake_intensity: float = 0.0
@onready shake_attack:    float = 0.0
@onready shake_sustain:   float = 0.0
@onready shake_release:   float = 0.0
@onready shake_time:      float = 0.0

func _process(delta: float) -> void:
    var intensity = 0.0
    shake_time += delta

    if !is_zero_approx(shake_attack):
        intensity = lerp(0.0, shake_intensity, shake_time / shake_attack)
        if shake_time >= shake_attack: # Done this phase.
            shake_time   = 0.0
            shake_attack = 0.0
    elif !is_zero_approx(shake_sustain):
        intensity = shake_intensity
        if shake_time >= shake_sustain: # Done this phase.
            shake_time    = 0.0
            shake_sustain = 0.0
    elif !is_zero_approx(shake_release):
        intensity = lerp(shake_intensity, 0.0, shake_time / shake_release)
        if shake_time >= shake_release: # Done everything.
            shake_time    = 0.0
            shake_release = 0.0

    if is_zero_approx(shake_intensity):
        offset = Vector2.ZERO
        zoom   = Vector2(1.0, 1.0)
    else:
        offset = Vector2(randf_range(-intensity, intensity), randf_range(-intensity, intensity))
        var rzoom = randf_range(-0.1 * intensity, 0.1 * intensity)
        zoom = Vector2(rzoom, rzoom)

# Call this from elsewhere to start (or restart) camera shake.
#    intensity - pixels to shake by at peak
#    attack    - seconds to ramp from no shake to full shake
#    sustain - seconds to shake at full intensity
#    release - seconds to ramp from full shake to complete

func ShakeItBaby(float intensity, float attack, float sustain, float release) -> void:
    shake_intensity = intensity
    shake_attack    = attack
    shake_sustain   = sustain
    shake_release   = release
    shake_time      = 0.0

Then in your hit code you could do:

$Level/Camera.ShakeItBaby(10.0, 0.2, 0.8, 0.2)

You may or may not want the zoom shake; comment it out if you don’t want it.