save code
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
@tool
|
||||
extends Node3D
|
||||
|
||||
@export var play_particles: bool = false:
|
||||
set(value):
|
||||
if value:
|
||||
_find_particles() # Always scan for new particles
|
||||
_cleanup_delays() # Remove deleted particles from the list
|
||||
_start_delays() # Ensure delays exist for new particles
|
||||
_play_particles()
|
||||
play_particles = false # Reset the checkbox immediately
|
||||
|
||||
var particle_systems: Array
|
||||
@export var delays: Dictionary
|
||||
|
||||
@export var cleanDelays: bool = false:
|
||||
set(value):
|
||||
if value:
|
||||
delays.clear()
|
||||
cleanDelays = false
|
||||
|
||||
func _find_particles():
|
||||
particle_systems.clear() # Always clear and re-detect
|
||||
var existing_delays = delays.duplicate() # Preserve existing delays
|
||||
|
||||
for child in get_children():
|
||||
if child is GPUParticles3D:
|
||||
particle_systems.append(child)
|
||||
if not existing_delays.has(child.name):
|
||||
existing_delays[child.name] = 0.0 # Assign default delay
|
||||
|
||||
delays = existing_delays # Restore old delays + new ones
|
||||
|
||||
func _cleanup_delays(): # Remove delays for particles that no longer exist
|
||||
var valid_particle_names = particle_systems.map(func(p): return p.name)
|
||||
for key in delays.keys():
|
||||
if key not in valid_particle_names:
|
||||
delays.erase(key) # Remove old particle system references
|
||||
|
||||
func _start_delays():
|
||||
for particle in particle_systems:
|
||||
if not delays.has(particle.name):
|
||||
delays[particle.name] = 0.0 # Keep existing delays, add new ones
|
||||
|
||||
func _play_particles():
|
||||
for particle in particle_systems:
|
||||
var delay = delays.get(particle.name, 0.0)
|
||||
_playDelay(particle, delay)
|
||||
|
||||
func _playDelay(ps: GPUParticles3D, delay: float):
|
||||
ps.emitting = false # Reset first (prevents issues)
|
||||
await get_tree().create_timer(delay).timeout
|
||||
ps.restart() # Works in play mode
|
||||
ps.emitting = true # Start emitting
|
||||
@@ -0,0 +1 @@
|
||||
uid://dxhfn5g0spoim
|
||||
@@ -0,0 +1,68 @@
|
||||
extends RigidBody3D
|
||||
|
||||
@export var speed: float = 10.0
|
||||
@export var muzzle_prefab: PackedScene
|
||||
@export var impact_prefab: PackedScene
|
||||
@export var delay_trails_start: float = 0.1
|
||||
@export var trails: Array[GPUTrail3D]
|
||||
|
||||
var direction: Vector3 = Vector3.FORWARD
|
||||
var fire_point_position: Vector3
|
||||
|
||||
func set_fire_point(fp_position: Vector3):
|
||||
fire_point_position = fp_position
|
||||
|
||||
func _ready():
|
||||
delay_trails()
|
||||
spawn_muzzle()
|
||||
contact_monitor = true
|
||||
max_contacts_reported = 1
|
||||
|
||||
func _integrate_forces(state):
|
||||
linear_velocity = direction * speed
|
||||
|
||||
var collision = state.get_contact_count()
|
||||
if collision > 0:
|
||||
var contact = state.get_contact_collider_position(0)
|
||||
var normal = state.get_contact_local_normal(0)
|
||||
spawn_impact(contact, normal)
|
||||
queue_free()
|
||||
|
||||
func spawn_muzzle():
|
||||
if muzzle_prefab:
|
||||
var muzzle_instance = muzzle_prefab.instantiate()
|
||||
call_deferred("add_child", muzzle_instance)
|
||||
await get_tree().create_timer(0.001).timeout
|
||||
muzzle_instance.global_transform.origin = fire_point_position
|
||||
muzzle_instance.play_particles = true
|
||||
|
||||
await get_tree().create_timer(3.0).timeout
|
||||
if is_instance_valid(muzzle_instance):
|
||||
muzzle_instance.queue_free()
|
||||
|
||||
func spawn_impact(contact_point: Vector3 = Vector3.ZERO, contact_normal: Vector3 = Vector3.ZERO):
|
||||
if impact_prefab:
|
||||
var impact_instance = impact_prefab.instantiate()
|
||||
get_tree().current_scene.add_child(impact_instance)
|
||||
impact_instance.play_particles = true
|
||||
|
||||
impact_instance.global_transform.origin = contact_point
|
||||
impact_instance.look_at(contact_point + contact_normal, Vector3.UP)
|
||||
|
||||
await get_tree().create_timer(3.0).timeout
|
||||
if is_instance_valid(impact_instance):
|
||||
impact_instance.queue_free()
|
||||
|
||||
func delay_trails():
|
||||
if trails.is_empty():
|
||||
return
|
||||
|
||||
for trail in trails:
|
||||
if is_instance_valid(trail):
|
||||
trail.speed_scale = 0 # Stop movement initially, avoids glitches
|
||||
|
||||
await get_tree().create_timer(delay_trails_start).timeout
|
||||
|
||||
for trail in trails:
|
||||
if is_instance_valid(trail):
|
||||
trail.speed_scale = 1 # Resume movement
|
||||
@@ -0,0 +1 @@
|
||||
uid://srxbm3tbdhyu
|
||||
@@ -0,0 +1,19 @@
|
||||
extends Node3D
|
||||
|
||||
@export var camera: Camera3D = null
|
||||
@export var rotation_speed: float = 5.0
|
||||
|
||||
func _process(delta):
|
||||
if camera:
|
||||
var mouse_pos = get_viewport().get_mouse_position()
|
||||
var ray_origin = camera.project_ray_origin(mouse_pos)
|
||||
var ray_dir = camera.project_ray_normal(mouse_pos)
|
||||
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_dir * 1000.0)
|
||||
var result = space_state.intersect_ray(query)
|
||||
|
||||
var target_position = result["position"] if result else ray_origin + ray_dir * 10.0
|
||||
var direction = (target_position - global_transform.origin).normalized()
|
||||
var target_rotation = Quaternion(Vector3.FORWARD, direction)
|
||||
transform.basis = Basis(transform.basis.get_rotation_quaternion().slerp(target_rotation, delta * rotation_speed))
|
||||
@@ -0,0 +1 @@
|
||||
uid://b86hxdolpatb8
|
||||
@@ -0,0 +1,37 @@
|
||||
extends Node3D
|
||||
|
||||
@export var camera: Camera3D = null
|
||||
@export var fire_rate: float = 0.5
|
||||
@export var aoe_lifetime: float = 7.0
|
||||
@export var aoe_prefab: PackedScene
|
||||
|
||||
var can_fire: bool = true
|
||||
|
||||
func _process(_delta):
|
||||
if Input.is_action_pressed("fire") and can_fire:
|
||||
fire_aoe()
|
||||
can_fire = false
|
||||
await get_tree().create_timer(fire_rate).timeout
|
||||
can_fire = true
|
||||
|
||||
func fire_aoe():
|
||||
if not (aoe_prefab and camera):
|
||||
return
|
||||
|
||||
var mouse_position = get_viewport().get_mouse_position()
|
||||
var ray_origin = camera.project_ray_origin(mouse_position)
|
||||
var ray_direction = camera.project_ray_normal(mouse_position) * 1000 # Long ray
|
||||
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_direction)
|
||||
var result = space_state.intersect_ray(query)
|
||||
|
||||
if result.has("position"):
|
||||
var aoe_instance = aoe_prefab.instantiate()
|
||||
get_parent().add_child(aoe_instance)
|
||||
aoe_instance.global_position = result["position"] # Spawn at hit position
|
||||
aoe_instance.play_particles = true # Start particle effect
|
||||
|
||||
await get_tree().create_timer(aoe_lifetime).timeout
|
||||
if is_instance_valid(aoe_instance):
|
||||
aoe_instance.queue_free()
|
||||
@@ -0,0 +1 @@
|
||||
uid://bkdqbagbitktv
|
||||
@@ -0,0 +1,31 @@
|
||||
extends Node3D
|
||||
|
||||
@export var fire_rate: float = 0.5
|
||||
@export var projectile_lifetime: float = 4.0
|
||||
@export var projectile_prefab: PackedScene
|
||||
@export var rotate_to_mouse: Node3D
|
||||
@export var fire_point: Node3D
|
||||
|
||||
var can_fire: bool = true
|
||||
|
||||
func _process(_delta):
|
||||
if Input.is_action_pressed("fire") and can_fire:
|
||||
fire_projectile()
|
||||
can_fire = false
|
||||
await get_tree().create_timer(fire_rate).timeout
|
||||
can_fire = true
|
||||
|
||||
func fire_projectile():
|
||||
if projectile_prefab and fire_point and rotate_to_mouse:
|
||||
var projectile_instance = projectile_prefab.instantiate()
|
||||
get_parent().add_child(projectile_instance)
|
||||
projectile_instance.global_position = fire_point.global_position
|
||||
projectile_instance.set_fire_point(fire_point.global_transform.origin)
|
||||
|
||||
var direction = -rotate_to_mouse.global_transform.basis.z.normalized()
|
||||
projectile_instance.direction = direction
|
||||
projectile_instance.look_at(fire_point.global_position + -direction)
|
||||
|
||||
await get_tree().create_timer(projectile_lifetime).timeout
|
||||
if projectile_instance:
|
||||
projectile_instance.queue_free()
|
||||
@@ -0,0 +1 @@
|
||||
uid://gs00msx4i83u
|
||||
@@ -0,0 +1,116 @@
|
||||
class_name FreeLookCamera extends Camera3D
|
||||
|
||||
# Modifier keys' speed multiplier
|
||||
const SHIFT_MULTIPLIER = 2.5
|
||||
const ALT_MULTIPLIER = 1.0 / SHIFT_MULTIPLIER
|
||||
|
||||
|
||||
@export_range(0.0, 1.0) var sensitivity: float = 0.25
|
||||
|
||||
# Mouse state
|
||||
var _mouse_position = Vector2(0.0, 0.0)
|
||||
var _total_pitch = 0.0
|
||||
|
||||
# Movement state
|
||||
var _direction = Vector3(0.0, 0.0, 0.0)
|
||||
var _velocity = Vector3(0.0, 0.0, 0.0)
|
||||
var _acceleration = 30
|
||||
var _deceleration = -10
|
||||
var _vel_multiplier = 4
|
||||
|
||||
# Keyboard state
|
||||
var _w = false
|
||||
var _s = false
|
||||
var _a = false
|
||||
var _d = false
|
||||
var _q = false
|
||||
var _e = false
|
||||
var _shift = false
|
||||
var _alt = false
|
||||
|
||||
func _input(event):
|
||||
# Receives mouse motion
|
||||
if event is InputEventMouseMotion:
|
||||
_mouse_position = event.relative
|
||||
|
||||
# Receives mouse button input
|
||||
if event is InputEventMouseButton:
|
||||
match event.button_index:
|
||||
MOUSE_BUTTON_RIGHT: # Only allows rotation if right click down
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED if event.pressed else Input.MOUSE_MODE_VISIBLE)
|
||||
MOUSE_BUTTON_WHEEL_UP: # Increases max velocity
|
||||
_vel_multiplier = clamp(_vel_multiplier * 1.1, 0.2, 20)
|
||||
MOUSE_BUTTON_WHEEL_DOWN: # Decereases max velocity
|
||||
_vel_multiplier = clamp(_vel_multiplier / 1.1, 0.2, 20)
|
||||
|
||||
# Receives key input
|
||||
if event is InputEventKey:
|
||||
match event.keycode:
|
||||
KEY_W:
|
||||
_w = event.pressed
|
||||
KEY_S:
|
||||
_s = event.pressed
|
||||
KEY_A:
|
||||
_a = event.pressed
|
||||
KEY_D:
|
||||
_d = event.pressed
|
||||
KEY_Q:
|
||||
_q = event.pressed
|
||||
KEY_E:
|
||||
_e = event.pressed
|
||||
KEY_SHIFT:
|
||||
_shift = event.pressed
|
||||
KEY_ALT:
|
||||
_alt = event.pressed
|
||||
|
||||
# Updates mouselook and movement every frame
|
||||
func _process(delta):
|
||||
_update_mouselook()
|
||||
_update_movement(delta)
|
||||
|
||||
# Updates camera movement
|
||||
func _update_movement(delta):
|
||||
# Computes desired direction from key states
|
||||
_direction = Vector3(
|
||||
(_d as float) - (_a as float),
|
||||
(_e as float) - (_q as float),
|
||||
(_s as float) - (_w as float)
|
||||
)
|
||||
|
||||
# Computes the change in velocity due to desired direction and "drag"
|
||||
# The "drag" is a constant acceleration on the camera to bring it's velocity to 0
|
||||
var offset = _direction.normalized() * _acceleration * _vel_multiplier * delta \
|
||||
+ _velocity.normalized() * _deceleration * _vel_multiplier * delta
|
||||
|
||||
# Compute modifiers' speed multiplier
|
||||
var speed_multi = 1
|
||||
if _shift: speed_multi *= SHIFT_MULTIPLIER
|
||||
if _alt: speed_multi *= ALT_MULTIPLIER
|
||||
|
||||
# Checks if we should bother translating the camera
|
||||
if _direction == Vector3.ZERO and offset.length_squared() > _velocity.length_squared():
|
||||
# Sets the velocity to 0 to prevent jittering due to imperfect deceleration
|
||||
_velocity = Vector3.ZERO
|
||||
else:
|
||||
# Clamps speed to stay within maximum value (_vel_multiplier)
|
||||
_velocity.x = clamp(_velocity.x + offset.x, -_vel_multiplier, _vel_multiplier)
|
||||
_velocity.y = clamp(_velocity.y + offset.y, -_vel_multiplier, _vel_multiplier)
|
||||
_velocity.z = clamp(_velocity.z + offset.z, -_vel_multiplier, _vel_multiplier)
|
||||
|
||||
translate(_velocity * delta * speed_multi)
|
||||
|
||||
# Updates mouse look
|
||||
func _update_mouselook():
|
||||
# Only rotates mouse if the mouse is captured
|
||||
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
|
||||
_mouse_position *= sensitivity
|
||||
var yaw = _mouse_position.x
|
||||
var pitch = _mouse_position.y
|
||||
_mouse_position = Vector2(0, 0)
|
||||
|
||||
# Prevents looking up/down too far
|
||||
pitch = clamp(pitch, -90 - _total_pitch, 90 - _total_pitch)
|
||||
_total_pitch += pitch
|
||||
|
||||
rotate_y(deg_to_rad(-yaw))
|
||||
rotate_object_local(Vector3(1,0,0), deg_to_rad(-pitch))
|
||||
@@ -0,0 +1 @@
|
||||
uid://dq7h3nectp4hy
|
||||
Reference in New Issue
Block a user