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)