Godot 4.6 exploration game with player movement, step-rotation camera rig, and POI interaction system (explore/rest/close menu). main.tscn is a test scene with placeholder props; debug/ holds unwired terrain prototypes (voxel river, procedural island mesh, cross-shaped hub
135 lines
4.9 KiB
GDScript
135 lines
4.9 KiB
GDScript
extends Node3D
|
|
|
|
const W := 10 # Breite X
|
|
const D := 10 # Tiefe Z
|
|
const H := 3 # Höhe Y
|
|
|
|
# Fluss läuft entlang Z, mittig auf X
|
|
const FLUSS_X := 5 # Welche X-Spalte (0-basiert, Mitte = 4 oder 5)
|
|
const FLUSS_TIEFE := 2 # Wie viele Blöcke von oben rausgenommen
|
|
const WASSER_Y := 0 # Y-Index wo Wasser sitzt (unterster Block des Grabens)
|
|
|
|
# Materialien — einmal erstellen, mehrfach nutzen
|
|
var mat_erde : StandardMaterial3D
|
|
var mat_gras : StandardMaterial3D
|
|
var mat_wasser : ShaderMaterial
|
|
|
|
func _ready() -> void:
|
|
_make_materials()
|
|
_build()
|
|
|
|
# ═══════════════════════════════════════════════
|
|
# MATERIALIEN
|
|
# ═══════════════════════════════════════════════
|
|
func _make_materials() -> void:
|
|
mat_erde = StandardMaterial3D.new()
|
|
mat_erde.albedo_color = Color(0.38, 0.26, 0.16)
|
|
mat_erde.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
|
|
mat_gras = StandardMaterial3D.new()
|
|
mat_gras.albedo_color = Color(0.35, 0.60, 0.22)
|
|
mat_gras.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
|
|
# Wasser-Shader
|
|
var shader := Shader.new()
|
|
shader.code = """
|
|
shader_type spatial;
|
|
render_mode unshaded, cull_disabled, blend_mix;
|
|
|
|
uniform vec4 color_shallow : source_color = vec4(0.2, 0.55, 0.85, 0.75);
|
|
uniform vec4 color_deep : source_color = vec4(0.1, 0.35, 0.70, 0.90);
|
|
uniform float speed : hint_range(0.0, 2.0) = 0.6;
|
|
uniform float wave_scale : hint_range(0.1, 5.0) = 1.5;
|
|
|
|
void fragment() {
|
|
vec2 uv = UV * wave_scale + vec2(TIME * speed, TIME * speed * 0.4);
|
|
float wave = sin(uv.x * 6.28) * 0.5 + 0.5;
|
|
wave += sin(uv.y * 6.28 * 1.3 + TIME * speed) * 0.3;
|
|
wave = clamp(wave * 0.5, 0.0, 1.0);
|
|
ALBEDO = mix(color_shallow.rgb, color_deep.rgb, wave);
|
|
ALPHA = mix(color_shallow.a, color_deep.a, wave);
|
|
}
|
|
"""
|
|
mat_wasser = ShaderMaterial.new()
|
|
mat_wasser.shader = shader
|
|
|
|
# ═══════════════════════════════════════════════
|
|
# WELT BAUEN
|
|
# ═══════════════════════════════════════════════
|
|
func _build() -> void:
|
|
for x in range(W):
|
|
for z in range(D):
|
|
_build_spalte(x, z)
|
|
|
|
func _build_spalte(x: int, z: int) -> void:
|
|
var ist_fluss := (x == FLUSS_X)
|
|
|
|
for y in range(H):
|
|
# Fluss-Graben: oberste FLUSS_TIEFE Blöcke fehlen
|
|
if ist_fluss and y >= (H - FLUSS_TIEFE):
|
|
continue
|
|
|
|
var mat : StandardMaterial3D
|
|
if y == H - 1 and not ist_fluss:
|
|
mat = mat_gras # Oberste Schicht = Gras
|
|
else:
|
|
mat = mat_erde # Alles darunter = Erde
|
|
|
|
_place_block(Vector3(x, y, z), mat)
|
|
|
|
# Wasser in den Graben
|
|
if ist_fluss:
|
|
var wasser_y := H - FLUSS_TIEFE # Wasser sitzt auf dem Boden des Grabens
|
|
_place_wasser(Vector3(x, wasser_y, z))
|
|
|
|
# Wasserfall: wo Fluss auf Z-Rand trifft
|
|
if ist_fluss and (z == 0 or z == D - 1):
|
|
_spawn_wasserfall(x, z)
|
|
|
|
# ═══════════════════════════════════════════════
|
|
# BLÖCKE PLATZIEREN
|
|
# ═══════════════════════════════════════════════
|
|
func _place_block(pos: Vector3, mat: StandardMaterial3D) -> void:
|
|
var mi := MeshInstance3D.new()
|
|
var box := BoxMesh.new()
|
|
box.size = Vector3(1, 1, 1)
|
|
box.surface_set_material(0, mat)
|
|
mi.mesh = box
|
|
mi.position = pos + Vector3(0.5, 0.5, 0.5) # Pivot unten-links
|
|
add_child(mi)
|
|
|
|
func _place_wasser(pos: Vector3) -> void:
|
|
var mi := MeshInstance3D.new()
|
|
var box := BoxMesh.new()
|
|
# Wasser etwas flacher als 1m damit man Tiefe sieht
|
|
box.size = Vector3(1.0, 0.65, 1.0)
|
|
box.surface_set_material(0, mat_wasser)
|
|
mi.mesh = box
|
|
mi.position = pos + Vector3(0.5, 0.33, 0.5)
|
|
mi.transparency = 0.3
|
|
add_child(mi)
|
|
|
|
# ═══════════════════════════════════════════════
|
|
# WASSERFALL
|
|
# ═══════════════════════════════════════════════
|
|
func _spawn_wasserfall(x: int, z: int) -> void:
|
|
# Vertikale Wasser-Plane von Graben-Unterkante bis Boden
|
|
var fall_h := float(H - FLUSS_TIEFE) # Höhe des Falls
|
|
var mi := MeshInstance3D.new()
|
|
var quad := QuadMesh.new()
|
|
quad.size = Vector2(1.0, fall_h)
|
|
quad.surface_set_material(0, mat_wasser)
|
|
mi.mesh = quad
|
|
|
|
# Positionierung: Außenkante der Platte
|
|
var dir_z := -1.0 if z == 0 else 1.0
|
|
mi.position = Vector3(
|
|
x + 0.5,
|
|
fall_h * 0.5, # Vertikal zentriert
|
|
z + 0.5 * dir_z # Außenkante
|
|
)
|
|
# Plane zeigt nach außen
|
|
mi.rotation.y = 0.0 if z == D - 1 else PI
|
|
|
|
add_child(mi)
|