Trouble with Projectiles?

Godot Version

4.3

Question

The problem I am having revolves around the usage of projectiles it’s not something I’ve dealt with so far although many of the topics around that I have dealt with before but after having a 3 month break I am a little rusty.

So, what I have is a tank of the playfield - doesn’t move yet haven’t got that far but what I want to test out really badly is getting the projectile (bullet) to appear through the use of a PackedScene - I am just trying to get the instance of the bullet to appear onscreen when the spacebar is pressed - nothing more. To test this I have this code and setup in the inspector:

And this is the bullet scene

All very barebones, at the minute, however, when I press the spacebar the shoot_bullet() function is called yet the instantiation does not occur onscreen. And I cannot see what it is that I am doing wrong.

Any help is greatly appreciated.

Regards.

First, please paste code in using ``` on the top and bottom of your code.

Second, you didn’t supply us with the code of your bullet scene.

Third, you didn’t show us where bullet_spawn_point is being declared.

Likely either it is spawning offscreen, or when it is spawned, it is traveling so quickly that it’s moving offscreen before you see it.

Sorry, was in a hurry had to run an errand - and they weren’t happy waiting around for me.

Ok this is a screenshot showing spawnpoint of the bullet in the tank scene:

As for the code it was the screenshot that I was more worried about showing the PackedScene rather than the code. So people could see it was being setup properly.

The code for the bullet and to have it move is quite complex which is why I first just want it to be instantiated when the spacebar is pressed. I believe that is it setup so it won’t move just be displayed for 5 seconds before disappearing as the sped is never set for the bullet, the variable is there but never set when instantiated.

The entire code for the bullet is this (please forgive the formatting it was just gone all over the place - don’t know why):

extends CharacterBody2D

##the collisionshape is smaller than the area 2D collision shape! Why, don’t know

#var set export values in the inspector for all instances of the “object”
#OR use default value: we ha@expor@exporte here
@export var inital_speed: float @exportexport= 400;
@export var max_bounc@exports: in@export = 3;
@export var life_time : float = 5.0;

#Access the follow@onreadyng importa@onreadyt properties
@onready var bull@onreadyt_sprite: Sprite2@onready = $Sprite2D
@o@onreadyready var area_2d: Are@onready2D = $Area2D
@onready var life_timer: Timer = $LifeTimer

var current_bounces: int = 0; #should always start at zero :slight_smile:
#what is this for?
const MAX_COLLISION_ITERATIONS = 4; #Ready

#Ready function
func _ready() → void:
#for when the timer timesout: 5 seconds?!? or one
##life_timer.timeout.connect(_on_life_timer_timeout); #connected below
life_timer.wait_time = life_time; #5 seconds
life_timer.one_shot = true; #once per instance not multiple
#start the timer already :slight_smile:
life_timer.start();
#end

func _physics_process(delta: float) → void:
#if movement is occurring beyond a certain threshold
if(velocity.length() < 0.1):
#set the rotation of an object to match its direction of movement.
rotation = velocity.angle();
#end if

var motion = velocity * delta;  #what is velocity equal to?***
var interations = 0;			#what is this for?

#not getting this at all
while interations < MAX_COLLISION_ITERATIONS:
	#which it will always will at the start 0 < 4
	
	#The collision variable will hold an object that contains details 
	#about what was collided with.
	var collision = move_and_collide(motion);
	#move_and_collide() is used to check for collisions whilst 
	#moving the bullet object with the variable: motion. 
	#Then stored in the variable: collision
	
	#if collision did just occur with the movement
	if(collision): #if collision is not null there is some date there
		#get the spefic date we need from the collision variable
		var collider = collision.get_collider();

		# now we test that specific data against some requirement
		#like "Bouncy Ball" (wtf) or static body (ie the tileset with 
		#physics: TileMapLayer2 
		#if( collider.is_in_group("bouncy_ball") or (collider is StaticBody2D) ):
		#author had set wall that the bullet could/couldn't bounce off
		#if going to bounce off all walls this will suffice
		if( collider is StaticBody2D ):
			#current_bounces is less than max
			if(current_bounces < max_bounces):
				#wtf
				velocity = velocity.bounce(collision.get_normal() );
				motion = velocity * collision.get_remainder().length() / velocity.length();
				current_bounces += 1;	#as least that makes sens
			else:
				#if current bounce equal 3 kill the bullet object - can't bounce again
				queue_free();
				return;		#only needs to happen once stop the rest of 
							#code completeing
				#end if
		elif (collider.is_in_group("player") || collider.is_in_group("enemy_tank")):
			#if the collider is attached to enemy tank or player
			#again destroy the bullet and again return never to happen again
			queue_free();
			return;
		else:
			#really don't understand this but
			#if none of those conditions are met like destroy the bullet also
			#with a return
			queue_free();
			return;
		#end collider if
	else:
		break;	
	#end collider if
	
	#for the while loop
	interations += 1;
pass
#end

#-------------------------------------------------------------------------------
#connect the timer properly to a function
func _on_life_timer_timeout():
#probably clear out/destroy this instance of bullet
queue_free();
pass

#we are essentially doubling up on the checking first with the bullet colliders
#then here with the Area2D - is this really necessary
func _on_area_2d_area_entered(area: Area2D) → void:
#if area his another bullet
if(area.is_in_group(“bullet”) ):
#find the parent and delete what it hit.
area.get_parent().queue_free();
#then destroy the current bullet instance - think about it
#first we destroy the bullet it hits “then” destroy this bullet
queue_free();
return;
#end if

#if area hits player (us) OR enemytank
if(area.is_in_group("player") || area.is_in_group("enemy_tank") ):
	#find the parent and delete what it hit.
	##area.get_parent().queue_free(); #not needed not exactly sure why;
	#then destroy the player/tank bullet instance - think about it
	#first we destroy the player/tank it hits "then" destroy this bullet
	queue_free();
	return;
#end if

#end

As it is the bullet does not appear and code used for the timeout() never runs destroying the instance.

As always, any help is greatly appreciated

Working!

1 Like

So for future reference, the next thing I would have recommended is getting rid of the movement code on the bullet by commenting it out. If it shows up, you know that spawning is not the issue.

Also, the biggest problem I see here is you’re using a CharacterBody2D for a projectile. Your code would be a LOT simpler using either a Raycast2D (no visible projectile - instantaneous hit) or an Area2D (visible projectile). A CharacterBody2D is a heavy object to use for a projectile and may cause you performance issues if you have too may on the screen at a time.

Duly noted on all counts.

Thankyou.

1 Like