0 votes

Hi everyone! Today I have a problem that I can’t solve: I have a 2D player that shoots projectiles in one direction. However, when I change the horizontal direction of the player (with set flip h (true)), the bullets continue in the same direction. How can I solve it? Thank you!

asked Jan 13, 2019 in Engine by Rob1980 (70 points)

Hi Rob ! There are many different ways to make projectiles. Can you tell us how you implemented the movement of the bullet ? It would be easier to help you then

Hi, idk if i understood, but the problem is that when you flip the character, the bullet change its direction to where the character is looking? If so, it's probably because you added bullet as child of character instead of as child of root node

Hi! This is the projectile script:

extends Area2D
var vel = Vector2()
export var speed = 1000

func _ready():
set_physics_process(true)

func start_at(dir, pos):
set_rotation(dir)
set_position(pos)
vel = Vector2(speed, 0).rotated(dir)

func _physics_process(delta):
set_position(get_position() + vel * delta)

Hi, just for the record, try to format your code like this:

extends Area2D

var vel = Vector2()
export var speed = 1000

func ready():
    set_physics_process(true)

func startat(dir, pos):
    set_rotation(dir)
    set_position(pos)
    vel = Vector2(speed, 0).rotated(dir)

func _physics_process(delta):
    set_position(getposition() + vel * delta)

You must indent with 4 spaces so the code formats correclty.

Anyways, in first place, i must ask if my assumption to what the problem was is correct. And if it so, what we need to help you is not the bullet script, but the script where you instance it. I mean, the script where the bullet is created and everything.

Hi! My problem is that, unfortunately, when I flip the character horizontally, the bullet doesn’t flip.

The bullet is created in the character script, this:

extends Area2D


export (int) var SPEED 
var velocity = Vector2()
var screensize
onready var bullet = preload("res://bullet.tscn")
onready var bullet_container = get_node("bullet_container")
onready var gun_timer = get_node("gun_timer")

export var max_health = 10
var health = max_health

func _ready():
 print(get_owner())
 screensize = get_viewport_rect().size
 set_process(true)
onready var node = get_node("/root/bullet")

func _process(delta):
    velocity = Vector2()
    if Input.is_action_pressed("ui_right"):
        $AnimatedSprite.set_flip_h(false)
        velocity.x += 1
    if Input.is_action_pressed("ui_left"):
        $AnimatedSprite.set_flip_h(true)
        velocity.x -= 1 
    if Input.is_action_pressed("ui_down"):
        velocity.y += 1.5
    if Input.is_action_pressed("ui_up"):
        velocity.y -= 1.5
    if Input.is_action_pressed("ui_select"):
        if gun_timer.get_time_left() == 0:
            $AnimatedSprite.set_animation("Cammy2")
            $AudioStreamPlayer.play()
            shoot()
    if velocity.length() > 0:
        velocity = velocity * SPEED

    position += velocity * delta
    position.y = clamp(position.y, 50, 560)
    position.x = clamp(position.x, 50, 1000)

func start(pos):
    position = pos
    show()
    monitoring = true

func shoot():
    gun_timer.start()
    var b = bullet.instance()
    bullet_container.add_child(b)
    b.start_at(get_rotation(), get_position())

func _on_gun_timer_timeout():
    $AnimatedSprite.set_animation("Cammy")

And what is the structure of your bullet? i know that it extends Area2D... but then it has an Sprite as child?

Yes, yes, this is the structure:

  • Area2D
    --Sprite
    --CollisionShape2D

3 Answers

+1 vote
Best answer

Maybe the problem is that you are setting bullet's rotation equal to your player's rotation with this:

b.start_at(get_rotation(), get_position())

But i don't see you changed the rotation of the player, only flipped the sprite. So, your player rotation is still 0, am i right? What you can do is to add PI to the rotation if the $AnimatedSprite.flip_h is true. Something like this:

func shoot():
    gun_timer.start()
    var b = bullet.instance()
    var rotation = get_rotation()
    if $AnimatedSprite.flip_h:
        rotation += PI
    bullet_container.add_child(b)
    b.start_at(rotation, get_position())

Please, try it and tell me if it works.

answered Jan 14, 2019 by p7f (2,666 points)
selected Jan 15, 2019 by Rob1980

Also, if the player is not going to rotate in other angles, you coulds just use PI as rotation if flip_h is true, or 0 else case.

IT WORKS! Thank you so much! ;-)

You are welcome!

0 votes

Try the vector2.reflect(normal) method or If you want change only horizontal dir then multiply, vector2.x *= -1

answered Jan 13, 2019 by LordViperion (190 points)

Thanks, but my problem is not to know how to flip the direction of the projectile; in fact, I could also use the variable vel = Vector2 (speed, 0).rotated (dir + PI).
My problem is that I don’t know how to implement this command correctly in an "if".

This is the projectile script:

extends Area2D

var vel = Vector2()
export var speed = 1000

func _ready():
    set_physics_process(true)

func start_at(dir, pos):
    set_rotation(dir)
    set_position(pos)
    vel = Vector2(speed, 0).rotated(dir)


func _physics_process(delta):
    set_position(get_position() + vel * delta)

And this is the character's script:

extends Area2D

export (int) var SPEED 
var velocity = Vector2()
var screensize
onready var bullet = preload("res://bullet.tscn")
onready var bullet_container = get_node("bullet_container")
onready var gun_timer = get_node("gun_timer")

export var max_health = 10
var health = max_health

func _ready():
 print(get_owner())
 screensize = get_viewport_rect().size
 set_process(true)
onready var node = get_node("/root/bullet")

func _process(delta):
    velocity = Vector2()
    if Input.is_action_pressed("ui_right"):
        $AnimatedSprite.set_flip_h(false)
        velocity.x += 1
    if Input.is_action_pressed("ui_left"):
        $AnimatedSprite.set_flip_h(true)
        velocity.x -= 1 
    if Input.is_action_pressed("ui_down"):
        velocity.y += 1.5
    if Input.is_action_pressed("ui_up"):
        velocity.y -= 1.5
    if Input.is_action_pressed("ui_select"):
        if gun_timer.get_time_left() == 0:
            $AnimatedSprite.set_animation("Cammy2")
            $AudioStreamPlayer.play()
            shoot()
    if velocity.length() > 0:
        velocity = velocity * SPEED

    position += velocity * delta
    position.y = clamp(position.y, 50, 560)
    position.x = clamp(position.x, 50, 1000)

func start(pos):
    position = pos
    show()
    monitoring = true

func shoot():
    gun_timer.start()
    var b = bullet.instance()
    bullet_container.add_child(b)
    b.start_at(get_rotation(), get_position())

func _on_gun_timer_timeout():
    $AnimatedSprite.set_animation("Cammy")
0 votes

Assuming you're only concerned with horizontal movement, the simplest solution would be to have the player script launch a function in the bullet's script when the player instances it, and have the player's code plug in a direction variable as an argument in that function in the bullet's code:

So the bullet's script can have a variable called direction
var dir = 1

in the bullet's physics process, just set it to automatically travel upon being created:

func physicsprocess(delta: float) -> void:
motion.x = Speed * delta * dir
translate(motion)

translate( ) or moveandslide or moveandcollide, depending on what kind of node your bullet is. Mine is an Area2D, so I used translate(motion)

With that direction variable, you'll be able to use it to tell the bullet whether to flip_h or not:

if dir == -1:
$Sprite.flip_h = true

and if direction is a -1 it will make it travel left, but we'll need the player who creates the bullet to tell our bullet's script which value direction will be. So let's create a function here in the bullet script that the player will access when he creates a bullet.
Maybe something like:

setbulletdirection(d):
dir = d

that little "d" inside the parentheses is like a little teleporter that can receive variables from other scripts that call this function. I called it "d" but you can call it anything you like. Just know that it is a container that will receive a piece of info from the player.

Now in the player's script, figure out a way to tell your player's direction. Could be something like this:

var direction = 1
if Input.isactionjustpressed("uileft"):
direction = -1
elif Input.isactionjustpressed("uiright"):
direction = 1

Or you could do something similar with motion.x being greater than or less than 0 to set the direction variable. However you want to set direction to either 1 or -1, based on your player's movement
Now we create a variable to represent the bullet that we instance. I called mine bullet whereas the preloaded bullet was a constant, so I called that BULLET

const BULLET = preload("res://Objects/Bullet.tscn")
var bullet = BULLET.instance( )

then, before we add the child to the scene, we'll use our bullet variable to access the function in the bullet's script and plug in our own direction variable:

bullet.setbulletdirection(direction)

notice how I put the player's variable "direction" inside the parentheses where in the player script we have a "d"? Basically that makes the bullet's script plug in "direction" in place of that "d" and sets that value of either 1 or -1!

And then, finally we spawn the bullet:

getparent().addchild(Bullet)

After that you can set the bullet's position, but I'll not get into that now, assuming you already know how to do that. Hope this was helpful.

answered May 6 by pattyorigami (16 points)
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.