So, I had some time to mull over this conundrum. What makes projectile implementation so tricky is all the moving parts.
I have to manage/take into account:
The projectile root node + their children.
The player scene in relation to the projectile. (Node interactions)
A multiplayer implementation that works with the system I’ve built so far and that is modifiable enough for new multiplayer system iterations that are GUARANTEED to happen.
So, considering everything I’ve tried so far, I think I’ll try using a RigidBody3D next.
I could subvert the multiplayer physics problem by having the server be the only one who handles projectile physics, while the other clients see either a replicated projectile position OR a predicted position of the projectile they themselves shot.
The multiplayer implementation is another step entirely, but first I need to remake properly working local shooting code in my player script. This is the reason my Area3D implementation attempt failed. I got it working with a CharacterBody3D, but nothing else.
These are the only things that change between each version of my projectile. However, both of them interact with the player’s shoot function the same, with a key difference.
The CharacterBody3D works, and shoots the projectiles properly.
The Area3D version doesn’t work, and only spawns a projectile at 0.0.0 for some reason. (The issue is with the Area3D line that moves the projectile after spawn)
Your Area3D movement code should be changed to this, otherwise your projectile just stays in place, you’re currently not modifying its position anywhere.
I know that in the previous post about projectile movement I asked you to define the velocity, not add/subtract from it, but that was meant for CharacterBody3D, because there is a separate function move_and_slide() that is doing the actual movement. With Area3D you need to do the movement yourself, hence this +=
As for the collision detection - can you maybe use the body_entered signal to detect the collision?
Double check your collision layers and masks as well, those are very easy to setup incorrectly and don’t notice it.
Great news: The projectile movement now WORKS with an Area3D!!!
I completely forgot about the += sign. I just haven’t used it in a while.
I’m now working on getting collisions working. If I can get that done, I’ll be able to implement local projectiles. Then after local projectiles are implemented, I’ll devise a system for multiplayer projectiles.
I’ll keep you updated.
Update 1:
I connected the _body_entered() signal to the projectile script.
However, it still only works with RigidBody3D. I also checked the collision layers and masks; they’re correct as far as I know.
I’m not sure where the exact issue with collision is. But maybe it’s due to me using CSGshapes for my test map. Or maybe it’s something weirder I’m not seeing.
I’ll keep running more tests.
Update 2:
So, in theory, I could use Area3Ds for all geometry, like so:
But I feel this is REALLY impractical, as I have to add a bunch of exception for player and projectile areas. Plus, building maps seems like a royal pain.
I got shooting the projectiles working, and I can even switch the projectile to a RigidBody3D with the same code. (I tested it)
Does anyone have any solutions to the Area3D body collision issue? If I don’t have a solution in a day, I’ll switch to RigidBody3Ds and configure them from there.
All my 3D levels are built from StaticBody3Ds with a MeshInstance3D and a CollisionShape3D hanging off them. And they’re all imported .glb models, so all that setup is done in the Advanced Import dialog.. I’ve never had a problem detecting collisions.
The link I sent you yesterday was the code for a whole project that has it. Look up the cryot_ice_shard.
extends Area3D
@export var ice_shard_scene: PackedScene
@export var activation_sound: AudioStream
@onready var collision_shape = $CollisionShape3D
var targets: Array[Node]
var num_targets = 0
func create_shards(level: int) -> void:
targets = get_tree().get_nodes_in_group("destructible")
num_targets = level
Sounds.play_sound_effect(activation_sound)
var num_targets_found = targets.size()
if targets.is_empty():
return
var current_target = 0
for i in level:
var ice_shard = ice_shard_scene.instantiate()
ice_shard.set_damage(level)
if targets[current_target] == null: # If the target is no longer valid
print_rich("Target Removed: %s" % [targets[current_target]])
targets.remove_at(current_target) # Remove it from our list
ice_shard.set_target(targets[current_target])
current_target += 1 # We want to iterate through the targets
if current_target >= num_targets_found: # But if we have more ice shards than targets
current_target = 0 # start over targeting at the beginning
add_child(ice_shard)
ice_shard.global_position = global_position
# Used by Volfrae
func get_number_of_targets():
targets = get_tree().get_nodes_in_group("destructible")
return targets.size()
So sorry, I was in a meeting when I posted that. That was actually the code for the thing that creates the shards. ice_shard_small is the actual projectile.
Specifically, it only hits boxes/barrels and other breakables. It flies through walls. You’ll notice it has a self-destruct. If its target is destroyed, it deletes itself. The launcher also puts a self-destruct timer on it and if it doesn’t hit something by a certain time, it deletes itself.
The code itself is a year old and if I were refactoring there are a number of things I would fix.
Yeah I didn’t think to ask that question. This is good to know for the future when someone says that physics isn’t working for them and they are saying stuff I know works isn’t working.