Godot 4, flip bullet/sprite 2d/Gun Flash is in the wrong position only when shooting to the left!

Godot Version

Godot v4,3

Question

Hi, i need help with this problem,
I fixed the shoot to the right. the position of the FireFlash/Gunflash and the Sprite2D bullet is correct. But shooting to the left is wrong. The position is wrong for both FireFlash / Sprite2D bullet.

Do I need to make a script to make the the sprite2d position flip to the left with a corrected position.x or offset = something?
what should the script be like?`

This is difficult to fully assist without being able to see more details of your scene for the position of the FireFlash/GunFlash and the code for how you’re generating your bullets. It looks like you’re already toggling flip_h correctly on the FireFlash/GunFlash. How are you designating the correct location when shooting to the right?

Let me make some assumptions:

  • Player position: Vector2(0, 0)
  • FireFlash/GunFlash position: Vector2(100, 0) < this is relative to Player/Muzzle position.
  • Bullet is created at the position of FireFlash/GunFlash.

A simply way to make this work is to invert the FireFlash/GunFlash position when the player changes direction.

  • Player is facing right: FireFlash/GunFlash position: Vector2(100, 0)
  • Player is facing left: FireFlash/GunFlash position: Vector2(-100, 0)
1 Like

I tried to set the FireFlash and Sprite2D bullet in the x offset to -1900 and it looked right in-game!.

But all that I needed was a code to make this happen automatically if the player is flipped /or facing left.

But where I must add this code to the Sprite2D bullet, or the player script

If the change you need to make is on the Sprite2D within the Bullet scene, then I would put the code to toggle direction within the Bullet scene and call down

I don’t know exactly how you’re firing the bullet but consider this psuedo code:

Player Scene

var direction: float = 1.0 # this will either be 1 (facing right), or -1 (facing left)


def _input(event: InputEvent) -> void:
    if event.is_action_pressed("move_right"):
        direction = 1.0
        # other player movement code
    elif event.is_action_pressed("move_left"):
        direction = -1.0
        # other player movement code
        
def shoot():
    var bullet: Bullet = (preload("res://bullet_scene.tscn") as PackedScene).instantiate()
    bullet.set_direction(direction)
    # other shoot code

Bullet Scene

def set_direction(direction: float):
    $Sprite2D.offset = $Sprite2D.offset.abs() * direction # this effectively inverts the current offset - note that if you use a Y value in offset other than zero you may need to change this to compensate.

This way the Bullet scene has all the implementation for what to do when the direction is set, and all the Player (or anything else you make that shoots a bullet) needs to do is tell the Bullet which direction it is supposed to face.

1 Like

I did this in the bullet script did not work

extends Area2D

@onready var sprite_2d: Sprite2D = $Sprite2d

@export var speed = 750
var direction: int = 1

func _ready():
	update_flip()

func _physics_process(delta):
	position += transform.x * speed * delta * direction
	
	if position.x < -1900 and direction > 0:
		direction = -1
		update_flip()

func update_flip():
	$Sprite2d.flip_h = direction < 0

func _on_body_entered(body: Node2D) -> void:
	if body.has_method("enemy_hit"):
		body.enemy_hit()
		queue_free()

func _on_despawntimer_timeout() -> void:
	queue_free()

And
This is in the player script

extends CharacterBody2D

var health = 31
var direction: int = 1


@onready var bullet: Area2D = $Bullet
@onready var player: CharacterBody2D = $Player
@onready var sprite_2d: AnimatedSprite2D = $Sprite2d

@export var bullet2 : PackedScene

@onready var fire_flash: AnimatedSprite2D = $Muzzle/FireFlash
	
var is_shooting := false
	
const SPEED = 500
const JUMP_VELOCITY = -750.0
	
func _physics_process(delta: float) -> void:
	HP()
	handle_hp_regen(delta)
	
	# Add the gravity.
	if not is_on_floor():
		velocity += get_gravity() * delta
	
	if not is_shooting:
		# Get the input direction and handle the movement/deceleration.
		var direction := Input.get_axis("left", "right")
		if direction != 0:
			$Sprite2d.flip_h = direction < 0
			$Muzzle/FireFlash.flip_h = direction < 0
			velocity.x = direction * SPEED
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED)
		
		move_and_slide()
	
	
	if Input.is_action_just_pressed("shoot"):
		shoot()
		sprite_2d.animation = "shoot"
		is_shooting = true
	if Input.is_action_just_released("shoot"):
		sprite_2d.animation = "idle"
		is_shooting = false	
	
		
func shoot():
	var b = preload ("res://bullet.tscn").instantiate()
	b.direction = -1 if sprite_2d.flip_h else 1
	get_tree().root.add_child(b)
	b.transform = $Muzzle.global_transform
	
	
	
	
func shoot2():
	var c = preload ("res://bullet2.tscn").instantiate()
	c.direction = -1 if sprite_2d.flip_h else 1
	get_tree().root.add_child(c)
	c.transform = $Muzzle.global_transform
	
	
	
func shoot3():
	var a = preload ("res://bullet_3.tscn").instantiate()
	a.direction = -1 if sprite_2d.flip_h else 1
	get_tree().root.add_child(a)
	a.transform = $Muzzle.global_transform
	

Hey there, not really caught up to speed with all the replies but i had a similar issue when i was making an enemy for my game. Heres the code that works for that enemy. Hope it helps!

Arrow/bullet script:

extends Area2D

var direction = null
var on_screen = true
@onready var player = get_tree().get_first_node_in_group("Player_body")
@onready var timer: Timer = $VisibleOnScreenNotifier2D/Timer


func _ready(): # gets direction and sprite flip when arrow is instantiated
	direction = (player.position - self.global_position).normalized()
	if direction.x > 0:
		$Sprite2D.flip_h = false
		direction.x = ceil(direction.x) # ceiling() auto snaps the direction to the nearest whole value that is not less than the current value while floor does the opposite
	else:
		$Sprite2D.flip_h = true
		direction.x = floor(direction.x)

func _physics_process(delta): # uses direction to flip
	position.x += direction.x * 200 * delta

script for the bullet / arrow position and instantiation:


func on_enter():
	Flee_state.attack_next = false
	keep_attacking = true  # Allow continuous shooting
	attack()


func attack():
	if keep_attacking and timer.is_stopped():  # Only attack if the player is in range and the timer is ready
		playback.travel(Attack_animation)
		timer.start()


func _process(_delta): 
	if not timer.is_stopped():
		character.velocity.x = 0

	var distance_to_player = snapped(character.player.position.x - character.global_position.x, 1)
	if -100 < distance_to_player and distance_to_player < 100 and Flee_state.timer_cooldown.is_stopped():
		keep_attacking = false
		Flee_state.attack_next = true

	if snapped(timer.time_left, 0.01) == 0.45:
		const Arrow = preload("res://Enemy_archer/Arrow.tscn")
		var New_Arrow = Arrow.instantiate()
		var Arrow_offset = Vector2i(0,-10)
		New_Arrow.global_position = character.global_position + Vector2(Arrow_offset)
		character.add_sibling(New_Arrow)

	if $"../../damageable_archer".Archer_health == 0:
		next_state = Archer_death_state

this is part of an attack state in a finite state machine
This works for me and can hopefully be modified in a way that works for you :slight_smile:

1 Like

Thanks, i will try this.

In this part of your Player script you are adjusting the flip_h of the FireFlash but not flipping the Muzzle or FireFlash position/offset. In your scene does the FireFlash AnimatedSprite2D have an offset configured?.. or is the position of the FireFlash determined by the Muzzle Marker2D position? This is the part of the code you need to be inverting based on the direction of your player so it moves to the opposite side of the Player. That would either be one or both of the following:

	if not is_shooting:
		# Get the input direction and handle the movement/deceleration.
		var direction := Input.get_axis("left", "right")
		if direction != 0:
			$Sprite2d.flip_h = direction < 0
			$Muzzle/FireFlash.flip_h = direction < 0
			# add this:
			$Muzzle/FireFlash.offset.x = abs($Muzzle/FireFlash.offset.x) * direction
			# and/or this:
			$Muzzle.position.x = abs($Muzzle.position.x) * direction
			velocity.x = direction * SPEED
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED)
1 Like

Thanks, this solved the problem.

# Get the input direction and handle the movement/deceleration.
	if not is_shooting:
		var direction := Input.get_axis("left", "right")
		if direction != 0:
			# Flip the sprite and adjust muzzle position
			$Sprite2d.flip_h = direction < 0
			$Muzzle/FireFlash.flip_h = direction < 0
			
			# Adjust muzzle position based on facing direction
			if direction < 0:  # Facing left
				muzzle.position.x = -110  # Your specified offset
			else:  # Facing right
				muzzle.position.x = -22 # Reset to positive value (original position)
			
			velocity.x = direction * SPEED
		else:
			velocity.x = move_toward(velocity.x, 0, SPEED)
			
	
		move_and_slide()
		```