• 2D
  • How do I make a projectile? [SOLVED]

I am trying to make a projectile that shoots when the player clicks the left mouse button. It is not working. when the player clicks the shoot button, the projectile just sits there. Here is my code:

Player:

extends KinematicBody2D


# Declare member variables here. Examples:
# var a = 2
# var b = "text"
var screen_size

export (int) var speed = 1000
export(PackedScene) var bullet
var velocity = Vector2()



# Called when the node enters the scene tree for the first time.
func _ready():
	screen_size = get_viewport_rect().size
	
	
func start(pos):
	position = pos
	
# Change the target whenever a touch event happens.

func get_input():
	look_at(get_global_mouse_position())
	velocity = Vector2()
	if Input.is_mouse_button_pressed(BUTTON_LEFT):
		shoot()
	if Input.is_action_pressed("ui_select"):
		velocity = Vector2(speed, 0).rotated(rotation)

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(_delta):
	get_input()
	velocity = move_and_slide(velocity)
	
	position.x = clamp(position.x, 0, screen_size.x)
	position.y = clamp(position.y, 0, screen_size.y)
	
func shoot():
	var b = bullet.instance()
	self.add_child(b)
	b.transform = $Muzzle.transform

Bullet:

extends Area2D


# Declare member variables here. Examples:
export var speed = 750





# Called every frame. 'delta' is the elapsed time since the previous frame.
func physics_process(delta):
	position += transform.x * speed * delta
	#this is to make the bullet fly to the right when fired

Any help is appreciated. Thanks!

Welcome to the forums @epshteinmatthew!

A few things that could be causing the issue is that you are using local position variables for the bullet's movement, which could be causing it to not move, especially if the bullet's relative transform is small. If you want the bullet to move in the direction of its rotation, instead of using transform.x, you instead want to convert the rotation to a direction vector. Here's the code I would suggest using for the bullet:

extends Area2D
export (float) var speed = 750
func _physics_process(delta):
	global_position += Vector2(cos(rotation), sin(rotation)) * speed * delta

Then the bullet should move as expected! However, you will likely run into another issue, but thankfully it is easy to fix. The issue you may encounter is that the bullets will move when the player moves! This is because they are children nodes to the player, meaning they will inherit any offsets to the player's transform (position, rotation, and scale).

To fix this issue, in the shoot function, just change self.add_child(b) to get_parent().add_child(b) and the bullets will no longer be offset when the player moves.

Ok so it kinda works. The bullets move, but when i press the button, a bunch of them come out, and if i do get_parent() the bullets dont fire. Do i need to make a Game scene or something?

@epshteinmatthew said: Ok so it kinda works. The bullets move, but when i press the button, a bunch of them come out, and if i do get_parent() the bullets dont fire. Do i need to make a Game scene or something?

Well, the first issue is with how you are calling the shoot function. Input.is_mouse_button_pressed(BUTTON_LEFT) returns true when the mouse has been clicked or is being held down, which is why it is shooting a bunch. You probably need to add a small delay/timer so bullets shoot out at a set interval. Something like this:

extends KinematicBody2D
# other code here

var shoot_bullet_timer = 0
const SHOOT_BULLET_WAIT_TIME = 0.2

# other code here

func get_input(delta):
	look_at(get_global_mouse_position())
	velocity = Vector2()
	if (shoot_bullet_timer <= 0):
		if Input.is_mouse_button_pressed(BUTTON_LEFT):
			shoot()
			shoot_bullet_timer = SHOOT_BULLET_WAIT_TIME
	else:
		shoot_bullet_timer -= delta
	if Input.is_action_pressed("ui_select"):
		velocity = Vector2(speed, 0).rotated(rotation)

func _physics_process(delta):
	get_input(delta)
	# other code here

With the code above, a bullet should fire every 1/5 of a second. You can make the number in SHOOT_BULLET_WAIT_TIME larger or smaller to fine tune how fast the player can fire.

For the second issue, I am not sure why it is occurring right off, but I have a hunch. In the shoot function, try changing the line that sets the transform to the following:

b.global_transform = $Muzzle.global_transform

The issue is probably because the code was setting the local transform (the offset relative to its parent) rather than the global transform (the offset relative to the scene origin).

So, I don't want to keep bothering you, but i have one last question. Everything else works fine, but the rotation of the bullet doesn't match the muzzles rotation. When I turn the player and shoot, the bullet goes backward, relative to the players rotation. Can you please help me with this? I promise I wont bother you anymore. Thanks!

No worries, it does not bother me. If the bullet is going backwards instead of forwards, then it should just be a matter of adding PI to the rotation to direction vector used in the bullet script:

global_position += Vector2(cos(rotation + PI), sin(rotation + PI)) * speed * delta

Or, a slightly more performant option would be to rotate the bullet by PI when it is created in the shoot function by adding this right after setting b.global_transform:

b.rotate(PI)

Just make sure to use one of the solutions, but not both, as otherwise nothing will have seemed to happen.

If the rotation is off by 90 degrees instead of 180, then instead of adding PI, you want to add (or subtract, depending on the direction) PI * 0.5 instead of PI.

Well, my problem is that, say, my character is rotated to the left. In this situation, its fine, because of the changes you described. But, now I have the inverse problem. When The character is facing right, the bullets go left. Is there a way for the bullets rotation to just match the players rotation?

Oh, I fixed it with this code:

in player file: b.rotation = $Muzzle.global_rotation in shoot()

in bullet file: func _physics_process(delta): if global_rotation < 180: global_position += Vector2(cos(rotation), sin(rotation)) speed delta else: global_position -= Vector2(cos(rotation), sin(rotation)) speed delta

Thank you, and have a great day!

Awesome! I am glad you were able to figure what was going on and fix if :)

5 days later

anyone know how to do this with adforce?