249 lines
6.4 KiB
GDScript
249 lines
6.4 KiB
GDScript
extends Node3D
|
|
class_name Train
|
|
|
|
signal speed_changed(new_speed: float)
|
|
|
|
@export_group("Train")
|
|
@export var base_speed: float = 15.0
|
|
@export var max_speed: float = 50.0
|
|
@export var min_speed: float = 3.0
|
|
@export var acceleration: float = 12.0
|
|
@export var wagon_spacing: float = 5.5
|
|
|
|
var num_wagons: int
|
|
var group_name: String = ""
|
|
|
|
var locomotive_model_resources: Array[String] = [
|
|
"train-diesel-a.obj",
|
|
"train-diesel-b.obj",
|
|
"train-diesel-box-a.obj",
|
|
"train-diesel-c.obj"
|
|
]
|
|
|
|
var wagon_model_resources: Array[String] = [
|
|
"train-carriage-container-blue.obj",
|
|
"train-carriage-flatbed-wood.obj",
|
|
"train-carriage-lumber.obj",
|
|
"train-carriage-tank-large.obj",
|
|
"train-diesel-box-b.obj",
|
|
"train-diesel-box-c.obj"
|
|
]
|
|
|
|
# Riferimento al generatore (settato da main.gd)
|
|
var track_generator: TrackGenerator
|
|
|
|
# Stato
|
|
var current_speed: float = 15.0
|
|
var distance_traveled: float = 0.0
|
|
var locomotive: Node3D
|
|
var wagons: Array = []
|
|
var wagon_distances: Array = []
|
|
var _horn_player: AudioStreamPlayer
|
|
var _horn_playing: bool = false
|
|
|
|
|
|
func _ready() -> void:
|
|
# Aspetta un frame per assicurarsi che track_generator sia settato
|
|
await get_tree().process_frame
|
|
await get_tree().process_frame
|
|
_create_train()
|
|
current_speed = base_speed
|
|
_create_horn()
|
|
|
|
|
|
func _remove_train():
|
|
for node in get_tree().get_nodes_in_group(group_name):
|
|
node.queue_free()
|
|
|
|
|
|
func _create_train() -> void:
|
|
_remove_train()
|
|
_create_locomotive()
|
|
wagons = []
|
|
wagon_distances = []
|
|
num_wagons = randi_range(1, 10)
|
|
_create_wagons()
|
|
|
|
|
|
func _create_locomotive() -> void:
|
|
var locomotive_model = locomotive_model_resources.pick_random()
|
|
locomotive = load_model("res://resources/train kit/" + locomotive_model, "Locomotive")
|
|
locomotive.transform.rotated(Vector3(1, 0, 0), 180.0)
|
|
group_name = "locomotive" + str(Time.get_unix_time_from_system())
|
|
locomotive.add_to_group(group_name)
|
|
|
|
add_child(locomotive)
|
|
|
|
|
|
func _create_wagons() -> void:
|
|
var loco_length = 0
|
|
for child in locomotive.get_children():
|
|
if child is MeshInstance3D:
|
|
var aabb = child.get_aabb()
|
|
loco_length = aabb.size.z
|
|
|
|
for i in range(num_wagons):
|
|
var wagon_model = wagon_model_resources.pick_random()
|
|
var wagon = load_model("res://resources/train kit/" + wagon_model, "Wagon_%d" % i)
|
|
wagon.transform.rotated(Vector3(1, 0, 0), 180.0)
|
|
wagon.add_to_group(group_name)
|
|
add_child(wagon)
|
|
|
|
wagons.append(wagon)
|
|
wagon_distances.append(loco_length if num_wagons == 1 else 0 * 0.5 + (i + 1) * wagon_spacing)
|
|
_position_wagons_initial()
|
|
|
|
|
|
func _position_wagons_initial() -> void:
|
|
# Posiziona i vagoni sul tracciato all'avvio
|
|
if not track_generator:
|
|
return
|
|
|
|
for i in range(wagons.size()):
|
|
var wagon_dist = distance_traveled - wagon_distances[i]
|
|
# All'inizio i vagoni potrebbero essere "prima" della partenza
|
|
# quindi li posizioniamo dove possibile
|
|
if wagon_dist < 0:
|
|
wagon_dist = 0
|
|
|
|
var wagon_data = track_generator.get_position_on_track(wagon_dist)
|
|
if wagon_data["valid"]:
|
|
_apply_transform(wagons[i], wagon_data["position"], wagon_data["direction"])
|
|
|
|
|
|
func load_model(path: String, node_name: String) -> Node3D:
|
|
var mesh = OBJLoader.load_obj(path)
|
|
if mesh == null:
|
|
return null
|
|
|
|
# Crea un nodo contenitore
|
|
var newobj = Node3D.new()
|
|
newobj.name = node_name
|
|
|
|
# Crea il mesh instance come figlio
|
|
var mesh_instance = MeshInstance3D.new()
|
|
mesh_instance.mesh = mesh
|
|
newobj.add_child(mesh_instance)
|
|
|
|
return newobj
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
if not track_generator or not locomotive:
|
|
return
|
|
|
|
_handle_input(delta)
|
|
_update_movement(delta)
|
|
_update_track()
|
|
|
|
|
|
func _handle_input(delta: float) -> void:
|
|
if Input.is_action_pressed("speed_up"):
|
|
current_speed = min(current_speed + acceleration * delta, max_speed)
|
|
speed_changed.emit(current_speed)
|
|
elif Input.is_action_pressed("speed_down"):
|
|
current_speed = max(current_speed - acceleration * delta, min_speed)
|
|
speed_changed.emit(current_speed)
|
|
elif Input.is_action_pressed("change_train"):
|
|
_create_train()
|
|
current_speed = base_speed
|
|
|
|
if Input.is_action_just_pressed("horn") and not _horn_playing:
|
|
_play_horn()
|
|
|
|
func _update_movement(delta: float) -> void:
|
|
distance_traveled += current_speed * delta
|
|
|
|
# Locomotiva
|
|
var loco_data = track_generator.get_position_on_track(distance_traveled)
|
|
if loco_data["valid"]:
|
|
_apply_transform(locomotive, loco_data["position"], loco_data["direction"])
|
|
|
|
# Vagoni
|
|
for i in range(wagons.size()):
|
|
var wagon_dist = distance_traveled - wagon_distances[i]
|
|
if wagon_dist < 0:
|
|
wagon_dist = 0
|
|
|
|
var wagon_data = track_generator.get_position_on_track(wagon_dist)
|
|
if wagon_data["valid"]:
|
|
_apply_transform(wagons[i], wagon_data["position"], wagon_data["direction"])
|
|
|
|
|
|
func _apply_transform(node: Node3D, pos: Vector3, direction: Vector3) -> void:
|
|
node.global_position = pos
|
|
|
|
if direction.length_squared() > 0.001:
|
|
var target = pos - direction
|
|
var up = Vector3.UP
|
|
if abs(direction.dot(up)) > 0.99:
|
|
up = Vector3.RIGHT
|
|
node.look_at(target, up)
|
|
|
|
|
|
func _update_track() -> void:
|
|
track_generator.update_track(distance_traveled)
|
|
|
|
|
|
func get_locomotive_position() -> Vector3:
|
|
if locomotive:
|
|
return locomotive.global_position
|
|
return Vector3.ZERO
|
|
|
|
|
|
func get_speed() -> float:
|
|
return current_speed
|
|
|
|
|
|
func _create_horn() -> void:
|
|
_horn_player = AudioStreamPlayer.new()
|
|
_horn_player.name = "HornPlayer"
|
|
_horn_player.volume_db = -6.0
|
|
_horn_player.bus = "Master"
|
|
add_child(_horn_player)
|
|
|
|
var sample_rate: int = 22050
|
|
var duration: float = 1.8
|
|
var num_samples: int = int(sample_rate * duration)
|
|
var fade_in: float = 0.15
|
|
var fade_out: float = 0.4
|
|
|
|
var freqs: Array[float] = [277.18, 369.99, 311.13]
|
|
|
|
var data := PackedByteArray()
|
|
data.resize(num_samples * 2)
|
|
|
|
for i in range(num_samples):
|
|
var t: float = float(i) / sample_rate
|
|
var sample: float = 0.0
|
|
for freq in freqs:
|
|
sample += sin(TAU * freq * t)
|
|
sample /= freqs.size()
|
|
|
|
var envelope: float = 1.0
|
|
if t < fade_in:
|
|
envelope = t / fade_in
|
|
elif t > duration - fade_out:
|
|
envelope = (duration - t) / fade_out
|
|
sample *= envelope
|
|
|
|
var value: int = clampi(int(sample * 16000), -32768, 32767)
|
|
data[i * 2] = value & 0xFF
|
|
data[i * 2 + 1] = (value >> 8) & 0xFF
|
|
|
|
var stream := AudioStreamWAV.new()
|
|
stream.format = AudioStreamWAV.FORMAT_16_BITS
|
|
stream.mix_rate = sample_rate
|
|
stream.data = data
|
|
_horn_player.stream = stream
|
|
|
|
|
|
func _play_horn() -> void:
|
|
_horn_playing = true
|
|
_horn_player.play()
|
|
_horn_player.finished.connect(_on_horn_finished, CONNECT_ONE_SHOT)
|
|
|
|
|
|
func _on_horn_finished() -> void:
|
|
_horn_playing = false
|