biome generator
This commit is contained in:
5
core/biome_generator/biome.gd
Normal file
5
core/biome_generator/biome.gd
Normal file
@@ -0,0 +1,5 @@
|
||||
extends Resource
|
||||
class_name Biome
|
||||
|
||||
@export var name: String = "New Biome"
|
||||
@export var available_chunks: Array[PackedScene]
|
||||
1
core/biome_generator/biome.gd.uid
Normal file
1
core/biome_generator/biome.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://wv6kcqkibium
|
||||
302
core/biome_generator/biome_generator.gd
Normal file
302
core/biome_generator/biome_generator.gd
Normal file
@@ -0,0 +1,302 @@
|
||||
extends Node3D
|
||||
|
||||
@export_group("Rails")
|
||||
@export var rail_path: Path3D
|
||||
|
||||
@export_group("Biomes")
|
||||
@export var biome_list: Array[Biome]
|
||||
|
||||
@export_group("Grid and Area")
|
||||
@export var chunk_size: float = 20.0
|
||||
@export var eye_line: int = 3
|
||||
@export var district_scale: float = 0.05
|
||||
|
||||
var board: Dictionary = {}
|
||||
var last_pos_train: Vector2i = Vector2i(999999, 999999)
|
||||
var noise_generator: FastNoiseLite
|
||||
var altitude_generator: FastNoiseLite
|
||||
|
||||
var manual_biome: Biome = null
|
||||
|
||||
func _ready() -> void:
|
||||
if biome_list.is_empty() or rail_path == null: return
|
||||
|
||||
noise_generator = FastNoiseLite.new()
|
||||
noise_generator.noise_type = FastNoiseLite.TYPE_PERLIN
|
||||
noise_generator.seed = randi()
|
||||
noise_generator.frequency = district_scale
|
||||
|
||||
altitude_generator = FastNoiseLite.new()
|
||||
altitude_generator.noise_type = FastNoiseLite.TYPE_PERLIN
|
||||
altitude_generator.seed = randi()
|
||||
altitude_generator.frequency = district_scale * 0.5
|
||||
|
||||
_update_set_pieces()
|
||||
|
||||
func _update_set_pieces() -> void:
|
||||
var set_pieces = get_tree().get_nodes_in_group("set_pieces")
|
||||
|
||||
print("🔍 Found ", set_pieces.size(), " in 'set_pieces' group.")
|
||||
|
||||
for sp in set_pieces:
|
||||
if not "exclusive_biome" in sp or sp.exclusive_biome == "":
|
||||
print("⚠️ Set Piece '", sp.name, "' not have exclusive biome -> always visible.")
|
||||
continue
|
||||
|
||||
var local_biome = ""
|
||||
if manual_biome != null:
|
||||
local_biome = manual_biome.nome
|
||||
else:
|
||||
var grid_x = roundi(sp.global_position.x / chunk_size)
|
||||
var grid_z = roundi(sp.global_position.z / chunk_size)
|
||||
local_biome = _get_procedural_biome_name(noise_generator.get_noise_2d(grid_x, grid_z))
|
||||
|
||||
print("👉 Analyze: '", sp.name, "' | Need: [", sp.exclusive_biome, "] | Current biome is: [", local_biome, "]")
|
||||
|
||||
if local_biome == sp.exclusive_biome:
|
||||
sp.show()
|
||||
sp.process_mode = Node.PROCESS_MODE_INHERIT
|
||||
print(" ✅ Temple is visible.")
|
||||
else:
|
||||
sp.hide()
|
||||
sp.process_mode = Node.PROCESS_MODE_DISABLED
|
||||
print(" ❌ Temple is a invisible ghost.")
|
||||
|
||||
func _destroy_and_regenrate_world() -> void:
|
||||
#1. Turn off old temples
|
||||
_update_set_pieces()
|
||||
|
||||
#2. Destroy all models
|
||||
for pos in board.keys():
|
||||
var cella = board[pos]
|
||||
if cella["tipo"] == "bioma":
|
||||
if cella.has("nodo") and is_instance_valid(cella["nodo"]):
|
||||
cella["nodo"].queue_free()
|
||||
|
||||
#3. Clean board
|
||||
board.clear()
|
||||
|
||||
#4. Create train
|
||||
last_pos_train = Vector2i(999999, 999999)
|
||||
_train_radar()
|
||||
last_pos_train = Vector2i(999999, 999999)
|
||||
_train_radar()
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
if rail_path == null or rail_path.train_instance == null: return
|
||||
|
||||
var train_pos = rail_path.train_instance.global_position
|
||||
var grid_x = roundi(train_pos.x / chunk_size)
|
||||
var grid_z = roundi(train_pos.z / chunk_size)
|
||||
var current_pos = Vector2i(grid_x, grid_z)
|
||||
|
||||
if current_pos != last_pos_train:
|
||||
last_pos_train = current_pos
|
||||
_generate_pieces_around_train(current_pos)
|
||||
_clean_far_chunks(current_pos)
|
||||
_train_radar()
|
||||
|
||||
func _generate_pieces_around_train(centro: Vector2i) -> void:
|
||||
for x in range(-eye_line, eye_line + 1):
|
||||
for z in range(-eye_line, eye_line + 1):
|
||||
var grid_pos = centro + Vector2i(x, z)
|
||||
|
||||
if not board.has(grid_pos):
|
||||
var ce_obstacle = _register_cell_with_laser(grid_pos)
|
||||
if not ce_obstacle:
|
||||
_add_compatible_biome(grid_pos)
|
||||
|
||||
func _choose_catalogue_by_cell(grid_pos: Vector2i) -> Array[PackedScene]:
|
||||
if manual_biome != null:
|
||||
return manual_biome.available_chunks
|
||||
|
||||
var value = noise_generator.get_noise_2d(grid_pos.x, grid_pos.y)
|
||||
var normalized_value = (value + 1.0) / 2.0
|
||||
var indice = clamp(int(normalized_value * biome_list.size()), 0, biome_list.size() - 1)
|
||||
return biome_list[indice].available_chunks
|
||||
|
||||
func _get_procedural_biome_name(value: float) -> String:
|
||||
if manual_biome != null:
|
||||
return manual_biome.name
|
||||
|
||||
var normalized_value = (value + 1.0) / 2.0
|
||||
var index = clamp(int(normalized_value * biome_list.size()), 0, biome_list.size() - 1)
|
||||
return biome_list[index].name
|
||||
|
||||
func _register_cell_with_laser(grid_pos: Vector2i) -> bool:
|
||||
if board.has(grid_pos): return true
|
||||
|
||||
var world_pos = Vector3(grid_pos.x * chunk_size, 0, grid_pos.y * chunk_size)
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
var ray_origin = Vector3(world_pos.x, 50.0, world_pos.z)
|
||||
var ray_end = Vector3(world_pos.x, -10.0, world_pos.z)
|
||||
|
||||
var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_end)
|
||||
var result = space_state.intersect_ray(query)
|
||||
|
||||
if result.size() > 0:
|
||||
var collider = result["collider"]
|
||||
var current_node = collider
|
||||
var exit_found = {"north": false, "est": false, "south": false, "west": false}
|
||||
var height_found = {"north": 0, "est": 0, "south": 0, "west": 0}
|
||||
|
||||
while current_node != null and current_node != get_tree().root:
|
||||
if current_node.has_method("get_rotated_data"):
|
||||
var rotation_steps = roundi(rad_to_deg(current_node.global_rotation.y) / -90.0)
|
||||
var data = current_node.get_rotated_data(rotation_steps)
|
||||
exit_found = data["connections"]
|
||||
height_found = data["heights"]
|
||||
break
|
||||
current_node = current_node.get_parent()
|
||||
|
||||
board[grid_pos] = {"type": "obstacle", "exit": exit_found, "heights": height_found}
|
||||
return true
|
||||
return false
|
||||
|
||||
func _add_compatible_biome(grid_pos: Vector2i) -> void:
|
||||
var req_conn_north = _needed_connections(grid_pos + Vector2i(0, -1), "south")
|
||||
var req_conn_est = _needed_connections(grid_pos + Vector2i(1, 0), "west")
|
||||
var req_conn_south = _needed_connections(grid_pos + Vector2i(0, 1), "north")
|
||||
var req_conn_west = _needed_connections(grid_pos + Vector2i(-1, 0), "est")
|
||||
|
||||
var req_height_north = _needed_heights(grid_pos + Vector2i(0, -1), "south")
|
||||
var req_height_est = _needed_heights(grid_pos + Vector2i(1, 0), "west")
|
||||
var req_height_south = _needed_heights(grid_pos + Vector2i(0, 1), "north")
|
||||
var req_height_west = _needed_heights(grid_pos + Vector2i(-1, 0), "est")
|
||||
|
||||
var height_noise = altitude_generator.get_noise_2d(grid_pos.x, grid_pos.y)
|
||||
var height_target = clamp(roundi((height_noise + 1.0) / 2.0), 0, 1)
|
||||
|
||||
var zone_catalogue = _choose_catalogue_by_cell(grid_pos)
|
||||
var valid_candidates = []
|
||||
|
||||
for scene in zone_catalogue:
|
||||
var test_chunk = scene.instantiate()
|
||||
|
||||
if test_chunk.has_method("get_rotated_data"):
|
||||
for rot in range(4):
|
||||
var data = test_chunk.get_rotated_data(rot)
|
||||
var u_conn = data["connections"]
|
||||
var u_height = data["heights"]
|
||||
|
||||
var match_conn_n = (req_conn_north == -1) or ((req_conn_north == 1) == u_conn["north"])
|
||||
var match_conn_e = (req_conn_est == -1) or ((req_conn_est == 1) == u_conn["est"])
|
||||
var match_conn_s = (req_conn_south == -1) or ((req_conn_south == 1) == u_conn["south"])
|
||||
var match_conn_o = (req_conn_west == -1) or ((req_conn_west == 1) == u_conn["west"])
|
||||
|
||||
var match_alt_n = (req_height_north == -1) or (req_height_north == u_height["north"])
|
||||
var match_alt_e = (req_height_est == -1) or (req_height_est == u_height["est"])
|
||||
var match_alt_s = (req_height_south == -1) or (req_height_south == u_height["south"])
|
||||
var match_alt_o = (req_height_west == -1) or (req_height_west == u_height["west"])
|
||||
|
||||
var diff_n = abs(u_height["north"] - height_target) if req_height_north == -1 else 0
|
||||
var diff_e = abs(u_height["est"] - height_target) if req_height_est == -1 else 0
|
||||
var diff_s = abs(u_height["south"] - height_target) if req_height_south == -1 else 0
|
||||
var diff_o = abs(u_height["west"] - height_target) if req_height_west == -1 else 0
|
||||
var excessive_jumps = diff_n > 1 or diff_e > 1 or diff_s > 1 or diff_o > 1
|
||||
|
||||
if match_conn_n and match_conn_e and match_conn_s and match_conn_o and match_alt_n and match_alt_e and match_alt_s and match_alt_o and not excessive_jumps:
|
||||
var score = 0
|
||||
if req_height_north == -1 and u_height["north"] == height_target: score += 1
|
||||
if req_height_est == -1 and u_height["est"] == height_target: score += 1
|
||||
if req_height_south == -1 and u_height["south"] == height_target: score += 1
|
||||
if req_height_west == -1 and u_height["west"] == height_target: score += 1
|
||||
|
||||
valid_candidates.append({"scene": scene, "rotation": rot, "data": data, "score": score})
|
||||
|
||||
test_chunk.queue_free()
|
||||
|
||||
if valid_candidates.size() > 0:
|
||||
var max_score = -1
|
||||
for c in valid_candidates:
|
||||
if c.score > max_score: max_score = c.score
|
||||
|
||||
var best_candidate = []
|
||||
for c in valid_candidates:
|
||||
if c.score == max_score: best_candidate.append(c)
|
||||
|
||||
var choise = best_candidate.pick_random()
|
||||
var new_chunk = choise.scene.instantiate()
|
||||
add_child(new_chunk)
|
||||
new_chunk.position = Vector3(grid_pos.x * chunk_size, 0, grid_pos.y * chunk_size)
|
||||
new_chunk.rotation.y = choise.rotation * (-PI / 2.0)
|
||||
|
||||
board[grid_pos] = {"type": "biome", "exit": choise.data["connections"], "heights": choise.data["heights"], "nodo": new_chunk}
|
||||
else:
|
||||
push_warning("⚠️ One piece miss for position " + str(grid_pos) + "!")
|
||||
|
||||
var fmt_alt = func(req): return str(req) if req != -1 else "Free"
|
||||
#var fmt_conn = func(req): return "Yes" if req == 1 else ("No" if req == 0 else "Free")
|
||||
|
||||
print("We need missed piece -> North: ", fmt_alt.call(req_height_north), " | Est: ", fmt_alt.call(req_height_est), " | South: ", fmt_alt.call(req_height_south), " | West: ", fmt_alt.call(req_height_west))
|
||||
|
||||
var backup = zone_catalogue[0].instantiate()
|
||||
add_child(backup)
|
||||
backup.position = Vector3(grid_pos.x * chunk_size, 0, grid_pos.y * chunk_size)
|
||||
|
||||
var safe_heights = {
|
||||
"north": req_height_north if req_height_north != -1 else height_target,
|
||||
"est": req_height_est if req_height_est != -1 else height_target,
|
||||
"south": req_height_south if req_height_south != -1 else height_target,
|
||||
"west": req_height_west if req_height_west != -1 else height_target
|
||||
}
|
||||
board[grid_pos] = {"type": "biome", "exit": {"north":false, "est":false, "south":false, "west":false}, "heights": safe_heights, "node": backup}
|
||||
|
||||
func _needed_connections(near_pos: Vector2i, side_needed: String) -> int:
|
||||
if not board.has(near_pos):
|
||||
_register_cell_with_laser(near_pos)
|
||||
if board.has(near_pos) and board[near_pos].has("exit"):
|
||||
return 1 if board[near_pos]["exit"][side_needed] else 0
|
||||
return -1
|
||||
|
||||
func _needed_heights(near_pos: Vector2i, side_needed: String) -> int:
|
||||
if board.has(near_pos) and board[near_pos].has("heights"):
|
||||
return board[near_pos]["heights"][side_needed]
|
||||
return -1
|
||||
|
||||
func _train_radar() -> void:
|
||||
if rail_path == null or rail_path.curve == null or rail_path.train_instance == null: return
|
||||
|
||||
if manual_biome != null:
|
||||
return
|
||||
|
||||
var train_pos = rail_path.train_instance.global_position
|
||||
var grid_x = roundi(train_pos.x / chunk_size)
|
||||
var grid_z = roundi(train_pos.z / chunk_size)
|
||||
var current_biome = _get_procedural_biome_name(noise_generator.get_noise_2d(grid_x, grid_z))
|
||||
|
||||
var current_progress = rail_path.train_progress
|
||||
var total_length = rail_path.curve.get_baked_length()
|
||||
|
||||
for step in range(1, 31):
|
||||
var next_progress = wrapf(current_progress + (step * chunk_size), 0.0, total_length)
|
||||
var next_local_pos = rail_path.curve.sample_baked(next_progress, true)
|
||||
var next_global_pos = rail_path.to_global(next_local_pos)
|
||||
|
||||
var f_x = roundi(next_global_pos.x / chunk_size)
|
||||
var f_z = roundi(next_global_pos.z / chunk_size)
|
||||
var next_biome = _get_procedural_biome_name(noise_generator.get_noise_2d(f_x, f_z))
|
||||
|
||||
if next_biome != current_biome:
|
||||
#distanza_cambio = step * chunk_size
|
||||
#prossimo_bioma = next_biome
|
||||
break
|
||||
|
||||
func _clean_far_chunks(centro_attuale: Vector2i) -> void:
|
||||
var cells_to_remove = []
|
||||
var safe_margin = 2
|
||||
|
||||
for grid_pos in board.keys():
|
||||
var cell = board[grid_pos]
|
||||
if cell["type"] == "obstacle": continue
|
||||
|
||||
var dist_x = abs(grid_pos.x - centro_attuale.x)
|
||||
var dist_z = abs(grid_pos.y - centro_attuale.y)
|
||||
|
||||
if dist_x > eye_line + safe_margin or dist_z > eye_line + safe_margin:
|
||||
if cell.has("node") and is_instance_valid(cell["node"]):
|
||||
cell["node"].queue_free()
|
||||
cells_to_remove.append(grid_pos)
|
||||
|
||||
for pos in cells_to_remove:
|
||||
board.erase(pos)
|
||||
1
core/biome_generator/biome_generator.gd.uid
Normal file
1
core/biome_generator/biome_generator.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ct2k25kslaxe5
|
||||
6
core/biome_generator/biome_generator.tscn
Normal file
6
core/biome_generator/biome_generator.tscn
Normal file
@@ -0,0 +1,6 @@
|
||||
[gd_scene format=3 uid="uid://ujv2f1l4d2ps"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://ct2k25kslaxe5" path="res://core/biome_generator/biome_generator.gd" id="1_c7ilo"]
|
||||
|
||||
[node name="biome_generator" type="Node3D" unique_id=1861369341]
|
||||
script = ExtResource("1_c7ilo")
|
||||
46
core/biome_generator/chunk_info.gd
Normal file
46
core/biome_generator/chunk_info.gd
Normal file
@@ -0,0 +1,46 @@
|
||||
extends Node3D
|
||||
class_name ChunkInfo
|
||||
|
||||
@export_enum("Biome", "straight_track", "curved_track") var chunk_type: int = 0
|
||||
|
||||
@export_group("Set Piece Rules (Unique pieces)")
|
||||
@export var exclusive_biome: String = "" # Es: "Forest"
|
||||
|
||||
@export_group("Base exit (no roation)")
|
||||
@export var north: bool = false
|
||||
@export var est: bool = false
|
||||
@export var south: bool = false
|
||||
@export var west: bool = false
|
||||
|
||||
@export_group("Maring heights (level 0, 1, 2)")
|
||||
@export var height_north: int = 0
|
||||
@export var height_est: int = 0
|
||||
@export var height_south: int = 0
|
||||
@export var height_west: int = 0
|
||||
|
||||
func _ready() -> void:
|
||||
if exclusive_biome != "":
|
||||
add_to_group("set_pieces")
|
||||
|
||||
func get_rotated_data(steps_90: int) -> Dictionary:
|
||||
var original_exit = [north, est, south, west]
|
||||
var original_height = [height_north, height_est, height_south, height_west]
|
||||
|
||||
var calculated_exit = []
|
||||
var calculated_height = []
|
||||
|
||||
for i in range(4):
|
||||
var index = (i - steps_90 + 4) % 4
|
||||
calculated_exit.append(original_exit[index])
|
||||
calculated_height.append(original_height[index])
|
||||
|
||||
return {
|
||||
"connections": {
|
||||
"north": calculated_exit[0], "est": calculated_exit[1],
|
||||
"south": calculated_exit[2], "west": calculated_exit[3]
|
||||
},
|
||||
"heights": {
|
||||
"north": calculated_height[0], "est": calculated_height[1],
|
||||
"south": calculated_height[2], "west": calculated_height[3]
|
||||
}
|
||||
}
|
||||
1
core/biome_generator/chunk_info.gd.uid
Normal file
1
core/biome_generator/chunk_info.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dg2h4kbqe8j3m
|
||||
43
core/camera.tscn
Normal file
43
core/camera.tscn
Normal file
@@ -0,0 +1,43 @@
|
||||
[gd_scene format=3 uid="uid://cv5xmnow451kl"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b3nmswr1iaexw" path="res://core/camera_move.gd" id="1_6doux"]
|
||||
|
||||
[node name="camera" type="Node3D" unique_id=310254818]
|
||||
|
||||
[node name="camera_front" type="Camera3D" parent="." unique_id=849013180]
|
||||
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 2.55, 7.1)
|
||||
fov = 86.0
|
||||
near = 0.001
|
||||
far = 5000.0
|
||||
|
||||
[node name="camera_top" type="Camera3D" parent="." unique_id=454024645]
|
||||
transform = Transform3D(-0.9998601, 0.0062594875, -0.015508555, -0.0015509288, 0.88861585, 0.45864955, 0.016652059, 0.45860943, -0.8884819, 0, 7.4, 0.53)
|
||||
fov = 82.7
|
||||
near = 0.001
|
||||
far = 5000.0
|
||||
|
||||
[node name="camera_side1" type="Camera3D" parent="." unique_id=1328559378]
|
||||
transform = Transform3D(-0.18395132, 0, 0.98293537, 0, 1, 0, -0.98293537, 0, -0.18395132, -0.57, 2.257, 2.776)
|
||||
fov = 60.0
|
||||
|
||||
[node name="camera_side2" type="Camera3D" parent="." unique_id=888646238]
|
||||
transform = Transform3D(-4.371139e-08, 0.005235964, -0.9999863, 0, 0.9999863, 0.005235964, 1, 2.2887126e-10, -4.3710788e-08, 0.89, 2.207, 2.814)
|
||||
fov = 96.6
|
||||
far = 5000.0
|
||||
|
||||
[node name="camera_iso" type="Camera3D" parent="." unique_id=308091984]
|
||||
transform = Transform3D(0.5778576, -0.5770964, 0.5770964, 0, 0.70710677, 0.70710677, -0.8161376, -0.40860704, 0.40860704, 225, 290, 173.7)
|
||||
projection = 1
|
||||
fov = 96.6
|
||||
size = 35.0
|
||||
near = 0.001
|
||||
far = 500.0
|
||||
|
||||
[node name="pivot" type="Node3D" parent="." unique_id=1852377804 node_paths=PackedStringArray("camera_iso", "pivot", "camera_front", "camera_side1", "camera_side2", "camera_top")]
|
||||
script = ExtResource("1_6doux")
|
||||
camera_iso = NodePath("../camera_iso")
|
||||
pivot = NodePath(".")
|
||||
camera_front = NodePath("../camera_front")
|
||||
camera_side1 = NodePath("../camera_side1")
|
||||
camera_side2 = NodePath("../camera_side2")
|
||||
camera_top = NodePath("../camera_top")
|
||||
150
core/camera_move.gd
Normal file
150
core/camera_move.gd
Normal file
@@ -0,0 +1,150 @@
|
||||
extends Node3D
|
||||
|
||||
@export_group("Camera")
|
||||
@export var camera_iso : Camera3D
|
||||
@export var pivot : Node3D
|
||||
@export var camera_front : Camera3D
|
||||
@export var camera_side1 : Camera3D
|
||||
@export var camera_side2 : Camera3D
|
||||
@export var camera_top : Camera3D
|
||||
|
||||
@export_group("Camera Movememnt")
|
||||
@export var move_speed : float = 20.0
|
||||
@export var rotation_speed : float = 0.2
|
||||
|
||||
@export_group("Cinematic Mode")
|
||||
@export var change_camera_time : float = 10.0
|
||||
|
||||
@export_group("Isometric Zoom")
|
||||
@export var normal_size : float = 35.0
|
||||
@export var zoom_size : float = 60.0
|
||||
@export var zoom_time : float = 1.2
|
||||
var is_zoomed : bool = false
|
||||
var zoom_tween : Tween
|
||||
|
||||
var is_moving_enabled : bool = true
|
||||
|
||||
var is_cinematic_mode : bool = false
|
||||
var cinematic_timer : float = 0.0
|
||||
var cinematic_index : int = 0
|
||||
var array_camera : Array[Camera3D] = []
|
||||
|
||||
var initial_pivot_transformation : Transform3D
|
||||
|
||||
func _ready():
|
||||
if pivot:
|
||||
initial_pivot_transformation = pivot.transform
|
||||
|
||||
if camera_iso: array_camera.append(camera_iso)
|
||||
if camera_front: array_camera.append(camera_front)
|
||||
if camera_side1: array_camera.append(camera_side1)
|
||||
if camera_side2: array_camera.append(camera_side2)
|
||||
if camera_top: array_camera.append(camera_top)
|
||||
|
||||
if camera_iso:
|
||||
camera_iso.make_current()
|
||||
|
||||
func _input(event):
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
|
||||
#R-> Reset isometric camera
|
||||
if event.keycode == KEY_R and is_moving_enabled and pivot:
|
||||
pivot.transform = initial_pivot_transformation
|
||||
print("Isometric camera resetted!")
|
||||
return
|
||||
|
||||
#C-> Toggle cinematic mode
|
||||
if event.keycode == KEY_C:
|
||||
is_cinematic_mode = !is_cinematic_mode
|
||||
if is_cinematic_mode:
|
||||
cinematic_timer = 0.0
|
||||
for i in range(array_camera.size()):
|
||||
if array_camera[i].current:
|
||||
cinematic_index = i
|
||||
break
|
||||
print("Cinematic Mode: enabled")
|
||||
else:
|
||||
print("Cinematic Mode: disabled")
|
||||
return
|
||||
|
||||
#Z-> soft zoom
|
||||
if event.keycode == KEY_Z and camera_iso:
|
||||
is_zoomed = !is_zoomed
|
||||
var target_size = zoom_size if is_zoomed else normal_size
|
||||
|
||||
if zoom_tween and zoom_tween.is_valid():
|
||||
zoom_tween.kill()
|
||||
|
||||
zoom_tween = create_tween()
|
||||
zoom_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
zoom_tween.tween_property(camera_iso, "size", target_size, zoom_time)
|
||||
return
|
||||
|
||||
#manual controls (1-5)
|
||||
var manual_action = false
|
||||
|
||||
if event.keycode == KEY_1 and camera_iso:
|
||||
camera_iso.make_current()
|
||||
is_moving_enabled = true
|
||||
manual_action = true
|
||||
|
||||
elif event.keycode == KEY_2 and camera_front:
|
||||
camera_front.make_current()
|
||||
is_moving_enabled = false
|
||||
manual_action = true
|
||||
|
||||
elif event.keycode == KEY_3 and camera_side1:
|
||||
camera_side1.make_current()
|
||||
is_moving_enabled = false
|
||||
manual_action = true
|
||||
|
||||
elif event.keycode == KEY_4 and camera_side2:
|
||||
camera_side2.make_current()
|
||||
is_moving_enabled = false
|
||||
manual_action = true
|
||||
|
||||
elif event.keycode == KEY_5 and camera_top:
|
||||
camera_top.make_current()
|
||||
is_moving_enabled = false
|
||||
manual_action = true
|
||||
|
||||
if manual_action and is_cinematic_mode:
|
||||
is_cinematic_mode = false
|
||||
print("Cinematic Mode: Disabled")
|
||||
|
||||
if is_moving_enabled and pivot:
|
||||
if event is InputEventMouseMotion and Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
|
||||
pivot.rotate_y(deg_to_rad(-event.relative.x * rotation_speed))
|
||||
|
||||
func _process(delta):
|
||||
if is_cinematic_mode and array_camera.size() > 0:
|
||||
cinematic_timer += delta
|
||||
if cinematic_timer >= change_camera_time:
|
||||
cinematic_timer = 0.0
|
||||
cinematic_index = (cinematic_index + 1) % array_camera.size()
|
||||
var next_cam = array_camera[cinematic_index]
|
||||
next_cam.make_current()
|
||||
is_moving_enabled = (next_cam == camera_iso)
|
||||
|
||||
if not is_moving_enabled or pivot == null:
|
||||
return
|
||||
|
||||
var input_dir = Vector3.ZERO
|
||||
if Input.is_key_pressed(KEY_W): input_dir.z -= 1
|
||||
if Input.is_key_pressed(KEY_S): input_dir.z += 1
|
||||
if Input.is_key_pressed(KEY_A): input_dir.x -= 1
|
||||
if Input.is_key_pressed(KEY_D): input_dir.x += 1
|
||||
|
||||
if input_dir != Vector3.ZERO:
|
||||
input_dir = input_dir.normalized()
|
||||
|
||||
var forward = pivot.transform.basis.z
|
||||
var right = pivot.transform.basis.x
|
||||
forward.y = 0
|
||||
right.y = 0
|
||||
|
||||
forward = forward.normalized()
|
||||
right = right.normalized()
|
||||
|
||||
var motion = (forward * input_dir.z + right * input_dir.x)
|
||||
pivot.position += motion * move_speed * delta
|
||||
1
core/camera_move.gd.uid
Normal file
1
core/camera_move.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b3nmswr1iaexw
|
||||
Binary file not shown.
@@ -75,6 +75,9 @@ func _init(wind: GPUParticles3D, snow: GPUParticles3D,
|
||||
set_lightning()
|
||||
|
||||
create_sound_players()
|
||||
|
||||
toggle_dust(environment_config.enable_dust)
|
||||
toggle_blur(environment_config.enable_blur)
|
||||
|
||||
func _ready() -> void:
|
||||
|
||||
|
||||
@@ -80,8 +80,8 @@ extends Resource
|
||||
|
||||
#Post-process effect toggles (initial values)
|
||||
@export_group("Post Process")
|
||||
@export var enable_dust: bool = true #Floating dust particles overlay
|
||||
@export var enable_blur: bool = true #Screen blur effect
|
||||
@export var enable_dust: bool = false #Floating dust particles overlay
|
||||
@export var enable_blur: bool = false #Screen blur effect
|
||||
@export var enable_environment_shadows: bool = true #Cloud shadow projection on terrain
|
||||
@export var blur_amount: float = 0.6
|
||||
|
||||
|
||||
27
core/fireworks.gd
Normal file
27
core/fireworks.gd
Normal file
@@ -0,0 +1,27 @@
|
||||
extends Node3D
|
||||
@onready var rocket = $"1_StartRocket"
|
||||
@onready var explosion = $"2_MainExplosion"
|
||||
@onready var trails = $"3_FallingTrails"
|
||||
|
||||
func set_color(target_color: Color) -> void:
|
||||
#set colors
|
||||
set_particles_node_color(rocket, Color(1.5, 1.5, 1.5))
|
||||
set_particles_node_color(explosion, Color(1.2, 1.2, 1.2))
|
||||
set_particles_node_color(trails, target_color)
|
||||
|
||||
func set_particles_node_color(node: GPUParticles3D, new_color: Color) -> void:
|
||||
if node and node.draw_pass_1:
|
||||
var mesh = node.draw_pass_1 as QuadMesh
|
||||
if mesh and mesh.material:
|
||||
var unique_mat = mesh.material.duplicate() as StandardMaterial3D
|
||||
unique_mat.albedo_color = new_color
|
||||
unique_mat.blend_mode = BaseMaterial3D.BLEND_MODE_ADD #Light effect (glow)
|
||||
unique_mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
mesh.material = unique_mat
|
||||
|
||||
func turnon() -> void:
|
||||
if rocket:
|
||||
rocket.emitting = true
|
||||
|
||||
await get_tree().create_timer(6.0).timeout
|
||||
queue_free()
|
||||
1
core/fireworks.gd.uid
Normal file
1
core/fireworks.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://crxch6o5ottg7
|
||||
104
core/fireworks.tscn
Normal file
104
core/fireworks.tscn
Normal file
@@ -0,0 +1,104 @@
|
||||
[gd_scene format=3 uid="uid://dn1btv0e0ses7"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://crxch6o5ottg7" path="res://core/fireworks.gd" id="1_cjxyo"]
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_dp1bx"]
|
||||
direction = Vector3(0, 1, 0)
|
||||
spread = 0.0
|
||||
initial_velocity_min = 13.0
|
||||
initial_velocity_max = 14.0
|
||||
gravity = Vector3(0, 0, 0)
|
||||
sub_emitter_mode = 2
|
||||
sub_emitter_amount_at_end = 1
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_8o1ow"]
|
||||
blend_mode = 1
|
||||
shading_mode = 0
|
||||
albedo_color = Color(1, 0.8901961, 0.24313726, 1)
|
||||
billboard_mode = 3
|
||||
particles_anim_h_frames = 1
|
||||
particles_anim_v_frames = 1
|
||||
particles_anim_loop = false
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_p8l00"]
|
||||
material = SubResource("StandardMaterial3D_8o1ow")
|
||||
size = Vector2(0.2, 0.2)
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_kbf61"]
|
||||
direction = Vector3(0, 1, 0)
|
||||
spread = 180.0
|
||||
initial_velocity_min = 10.0
|
||||
initial_velocity_max = 10.0
|
||||
gravity = Vector3(0, 0, 0)
|
||||
damping_min = 6.0000005
|
||||
damping_max = 6.0000005
|
||||
sub_emitter_mode = 1
|
||||
sub_emitter_frequency = 30.0
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_is2bb"]
|
||||
blend_mode = 1
|
||||
shading_mode = 0
|
||||
billboard_mode = 3
|
||||
particles_anim_h_frames = 1
|
||||
particles_anim_v_frames = 1
|
||||
particles_anim_loop = false
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_f60hc"]
|
||||
material = SubResource("StandardMaterial3D_is2bb")
|
||||
size = Vector2(0.2, 0.2)
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_cjxyo"]
|
||||
colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 0)
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_dp1bx"]
|
||||
gradient = SubResource("Gradient_cjxyo")
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_tt4r5"]
|
||||
gravity = Vector3(0, -2, 0)
|
||||
scale_min = 0.5
|
||||
scale_max = 0.5
|
||||
color_ramp = SubResource("GradientTexture1D_dp1bx")
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xt2tl"]
|
||||
blend_mode = 1
|
||||
shading_mode = 0
|
||||
albedo_color = Color(0.99999994, 0.89069206, 0.2446233, 1)
|
||||
billboard_mode = 3
|
||||
particles_anim_h_frames = 1
|
||||
particles_anim_v_frames = 1
|
||||
particles_anim_loop = false
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_0p1ln"]
|
||||
material = SubResource("StandardMaterial3D_xt2tl")
|
||||
size = Vector2(0.05, 0.05)
|
||||
|
||||
[node name="FuocoD\'artificioRoot" type="Node3D" unique_id=669752765]
|
||||
script = ExtResource("1_cjxyo")
|
||||
|
||||
[node name="1_StartRocket" type="GPUParticles3D" parent="." unique_id=1569398174]
|
||||
emitting = false
|
||||
amount = 15
|
||||
sub_emitter = NodePath("../2_MainExplosion")
|
||||
lifetime = 0.5
|
||||
one_shot = true
|
||||
explosiveness = 1.0
|
||||
trail_enabled = true
|
||||
process_material = SubResource("ParticleProcessMaterial_dp1bx")
|
||||
draw_pass_1 = SubResource("QuadMesh_p8l00")
|
||||
|
||||
[node name="2_MainExplosion" type="GPUParticles3D" parent="." unique_id=863445356]
|
||||
emitting = false
|
||||
amount = 150
|
||||
sub_emitter = NodePath("../3_FallingTrails")
|
||||
lifetime = 1.5
|
||||
one_shot = true
|
||||
explosiveness = 1.0
|
||||
process_material = SubResource("ParticleProcessMaterial_kbf61")
|
||||
draw_pass_1 = SubResource("QuadMesh_f60hc")
|
||||
|
||||
[node name="3_FallingTrails" type="GPUParticles3D" parent="." unique_id=1969872503]
|
||||
emitting = false
|
||||
amount = 500
|
||||
lifetime = 0.5
|
||||
process_material = SubResource("ParticleProcessMaterial_tt4r5")
|
||||
draw_pass_1 = SubResource("QuadMesh_0p1ln")
|
||||
Reference in New Issue
Block a user