update
This commit is contained in:
@@ -4,12 +4,17 @@ class_name EarthWorld
|
||||
|
||||
var spawn := Vector3(0, 1.4, 12.0)
|
||||
var humans: Array = []
|
||||
var passanten: Array = [] # Statisten ohne Geschichte
|
||||
var spieler: Node3D = null # setzt main, für den Etagenschnitt
|
||||
var nav_map: RID # Navigationsnetz: Straßen, Türen, Möbel, Klötze
|
||||
const HOEHE := 3.0 # Raumhöhe; Klötze sind Vielfache davon
|
||||
const NAV_ZELLE_HOCH := 0.1
|
||||
var _bauten: Array = [] # alles, was der Etagenschnitt aufschneidet
|
||||
|
||||
var _sun: DirectionalLight3D
|
||||
var _sky: ProceduralSkyMaterial
|
||||
var _env: Environment
|
||||
var _we: WorldEnvironment
|
||||
var _nachtlichter: Array = []
|
||||
|
||||
|
||||
@@ -20,12 +25,41 @@ func _ready() -> void:
|
||||
_orte()
|
||||
_park()
|
||||
_saeule()
|
||||
_navigation()
|
||||
_menschen()
|
||||
_himmel_aktualisieren()
|
||||
|
||||
|
||||
## Das Navigationsnetz wird aus allen Kollisionsformen gebacken: Boden,
|
||||
## Straßen, Wände mit Türlücken, Möbel, Klötze. Wer darauf läuft, findet die
|
||||
## Türen von allein.
|
||||
func _navigation() -> void:
|
||||
var region := NavigationRegion3D.new()
|
||||
add_child(region)
|
||||
var nm := NavigationMesh.new()
|
||||
nm.geometry_parsed_geometry_type = NavigationMesh.PARSED_GEOMETRY_STATIC_COLLIDERS
|
||||
nm.agent_radius = 0.4
|
||||
nm.agent_height = 1.6
|
||||
nm.agent_max_climb = 0.35
|
||||
nm.agent_max_slope = 40.0
|
||||
nm.cell_height = NAV_ZELLE_HOCH # fein, sonst schweben die Leute 25 cm über der Straße
|
||||
var quelle := NavigationMeshSourceGeometryData3D.new()
|
||||
NavigationServer3D.parse_source_geometry_data(nm, quelle, self)
|
||||
NavigationServer3D.bake_from_source_geometry_data(nm, quelle)
|
||||
# eigene Karte: die Traumwelt liegt im selben Raum, ihre Polygone dürfen hier nicht auftauchen
|
||||
nav_map = NavigationServer3D.map_create()
|
||||
NavigationServer3D.map_set_active(nav_map, true)
|
||||
NavigationServer3D.map_set_cell_height(nav_map, NAV_ZELLE_HOCH)
|
||||
region.set_navigation_map(nav_map)
|
||||
region.navigation_mesh = nm
|
||||
# synchron: sonst kommen die ersten Pfade leer zurück
|
||||
NavigationServer3D.map_set_use_async_iterations(nav_map, false)
|
||||
NavigationServer3D.map_force_update(nav_map)
|
||||
|
||||
|
||||
func _environment() -> void:
|
||||
var we := WorldEnvironment.new()
|
||||
_we = WorldEnvironment.new()
|
||||
var we := _we
|
||||
_env = Environment.new()
|
||||
_env.background_mode = Environment.BG_SKY
|
||||
var sky := Sky.new()
|
||||
@@ -46,9 +80,22 @@ func _environment() -> void:
|
||||
add_child(_sun)
|
||||
|
||||
|
||||
const STADTRAND := 140.0 # unsichtbare Wand: hinter dem Bezirk endet die Welt
|
||||
|
||||
func _boden() -> void:
|
||||
Prims.box(self, Vector3(0, -0.53, 0), Vector3(300, 1, 300), Color(0.42, 0.48, 0.40))
|
||||
_strassen()
|
||||
# Stadtrand: wer immer weiter läuft, fiele sonst vom Rand der Welt
|
||||
for d: Vector3 in [Vector3.FORWARD, Vector3.BACK, Vector3.LEFT, Vector3.RIGHT]:
|
||||
var wand := StaticBody3D.new()
|
||||
var cs := CollisionShape3D.new()
|
||||
var form := BoxShape3D.new()
|
||||
form.size = Vector3(4.0, 30.0, STADTRAND * 2.0 + 8.0) if absf(d.x) > 0.0 \
|
||||
else Vector3(STADTRAND * 2.0 + 8.0, 30.0, 4.0)
|
||||
cs.shape = form
|
||||
wand.add_child(cs)
|
||||
wand.position = d * (STADTRAND + 2.0) + Vector3(0, 15.0, 0)
|
||||
add_child(wand)
|
||||
|
||||
|
||||
## Arterien breit, roji schmal -- beide als flaches Band mit Belag.
|
||||
@@ -61,7 +108,9 @@ func _strassen() -> void:
|
||||
continue
|
||||
var breit := float(st["breite"])
|
||||
var farbe := Color(0.62, 0.60, 0.58) if breit > 5.0 else Color(0.55, 0.53, 0.52)
|
||||
var band := Prims.box(self, (a + b) * 0.5 + Vector3(0, 0.02, 0),
|
||||
# gestaffelt: Kreuzungen und Gasseneinfahrten flimmern sonst (Z-Fighting)
|
||||
var hoch := 0.02 if breit > 5.0 and absf(d.z) > 0.0 else (0.03 if breit > 5.0 else 0.04)
|
||||
var band := Prims.box(self, (a + b) * 0.5 + Vector3(0, hoch, 0),
|
||||
Vector3(breit, 0.06, d.length() + breit), farbe, false)
|
||||
band.rotation.y = atan2(d.x, d.z)
|
||||
|
||||
@@ -84,7 +133,7 @@ func _bebauung() -> void:
|
||||
if minf(g.x, g.y) < 3.0:
|
||||
continue
|
||||
var n := 1 + rng.randi_range(0, 3)
|
||||
var hoehe := Orte.hoehe(n)
|
||||
var hoehe := HOEHE * float(n)
|
||||
var ton := Color.from_hsv(fmod(0.07 + rng.randf() * 0.12, 1.0),
|
||||
0.10 + rng.randf() * 0.14, 0.62 + rng.randf() * 0.26)
|
||||
var bau := Node3D.new()
|
||||
@@ -93,18 +142,17 @@ func _bebauung() -> void:
|
||||
# Kollision etwas größer: so schließt sich die Fuge zum Nachbarn, niemand keilt sich hinein
|
||||
Prims._add_box_body(klotz, Vector3(g.x + 0.9, hoehe, g.y + 0.9))
|
||||
for f in range(n):
|
||||
_fenster(bau, c + Vector3(0, Orte.hoehe(f), 0), g, ton)
|
||||
_fenster(bau, c + Vector3(0, HOEHE * float(f), 0), g, ton)
|
||||
Prims.box(bau, c + Vector3(0, hoehe + 0.2, 0),
|
||||
Vector3(g.x + 0.5, 0.4, g.y + 0.5), ton.darkened(0.25))
|
||||
if rng.randf() < 0.35:
|
||||
Prims.box(bau, c + Vector3(0, hoehe + 0.9, 0),
|
||||
Vector3(g.x * 0.4, 1.0, g.y * 0.4), ton.darkened(0.1))
|
||||
_bauten.append({"c": c, "size": g, "hoch": hoehe, "etagen": [bau], "dach": [],
|
||||
"waende": [], "voll": true})
|
||||
_bauten.append({"c": c, "size": g, "hoch": hoehe, "teile": [bau], "waende": []})
|
||||
|
||||
|
||||
## Wohnungen und Sonderbauten: geschlossene Häuser mit Stockwerken, Treppenhaus
|
||||
## und Möbeln. Der Geist gleitet hinein, der Etagenschnitt öffnet sie ihm.
|
||||
## Wohnungen und Sonderbauten: geschlossene Häuser, ein Raum mit Tür, Möbeln
|
||||
## und Spuren. Der Geist geht hinein, dann verschwinden Dach und Vorderwände.
|
||||
func _orte() -> void:
|
||||
for key in HumanDB.PLACES:
|
||||
var pl: Dictionary = HumanDB.PLACES[key]
|
||||
@@ -112,42 +160,32 @@ func _orte() -> void:
|
||||
if float(s[0]) <= 0.0:
|
||||
continue
|
||||
var c := Orte.center(HumanDB.PLACES, key)
|
||||
var groesse := Vector3(float(s[0]), Orte.ETAGE, float(s[1]))
|
||||
var groesse := Vector3(float(s[0]), HOEHE, float(s[1]))
|
||||
var farbe: Color = pl["color"]
|
||||
var tuer: float = maxf(1.6, minf(3.0, minf(groesse.x, groesse.z) - 3.0))
|
||||
var n := Orte.etagen(HumanDB.PLACES, key)
|
||||
var etagen: Array = []
|
||||
var waende: Array = []
|
||||
for f in range(n):
|
||||
var eb := Node3D.new()
|
||||
add_child(eb)
|
||||
var cf := c + Vector3(0, Orte.hoehe(f), 0)
|
||||
var ton := farbe.lightened(0.06 * float(f))
|
||||
waende.append(Prims.room(eb, cf, groesse, ton, farbe.darkened(0.35),
|
||||
Orte.DIR_VEC[pl["dir"]] if f == 0 else Vector3.ZERO, tuer, f == 0))
|
||||
if f > 0:
|
||||
_decke(eb, key, cf, groesse, farbe.darkened(0.35))
|
||||
_fenster(eb, cf, Vector2(groesse.x, groesse.z), ton)
|
||||
_einrichtung(eb, cf, groesse, farbe, key if f == 0 or HumanDB.ETAGEN_ARBEIT.has(key)
|
||||
else "heim_", key)
|
||||
if f + 1 < n:
|
||||
_treppe(eb, key, f)
|
||||
var licht := OmniLight3D.new()
|
||||
licht.position = cf + Vector3(0, 2.2, 0)
|
||||
licht.omni_range = 12.0
|
||||
licht.light_color = Color(1.0, 0.85, 0.6)
|
||||
licht.light_energy = 0.0
|
||||
eb.add_child(licht)
|
||||
_nachtlichter.append(licht)
|
||||
etagen.append(eb)
|
||||
var raum := Node3D.new()
|
||||
add_child(raum)
|
||||
var waende: Array = Prims.room(raum, c, groesse, farbe, farbe.darkened(0.35),
|
||||
Orte.DIR_VEC[pl["dir"]], tuer, true)
|
||||
_fenster(raum, c, Vector2(groesse.x, groesse.z), farbe)
|
||||
_einrichtung(raum, c, groesse, farbe, key)
|
||||
var wer := HumanDB.bewohner_von(key)
|
||||
if not wer.is_empty():
|
||||
_spuren(raum, key, c, groesse, wer)
|
||||
var licht := OmniLight3D.new()
|
||||
licht.position = c + Vector3(0, 2.2, 0)
|
||||
licht.omni_range = 12.0
|
||||
licht.light_color = Color(1.0, 0.85, 0.6)
|
||||
licht.light_energy = 0.0
|
||||
raum.add_child(licht)
|
||||
_nachtlichter.append(licht)
|
||||
var dach := Node3D.new()
|
||||
add_child(dach)
|
||||
Prims.box(dach, c + Vector3(0, Orte.hoehe(n) + 0.1, 0),
|
||||
Prims.box(dach, c + Vector3(0, HOEHE + 0.1, 0),
|
||||
Vector3(groesse.x + 0.4, 0.2, groesse.z + 0.4), farbe.darkened(0.3))
|
||||
Prims.label(self, c + Vector3(0, Orte.hoehe(n) + 1.6, 0), str(pl["name"]), 0.55,
|
||||
Color(1, 1, 0.9))
|
||||
_bauten.append({"c": c, "size": Vector2(groesse.x, groesse.z), "hoch": Orte.hoehe(n),
|
||||
"etagen": etagen, "dach": [dach], "waende": waende})
|
||||
Prims.label(self, c + Vector3(0, HOEHE + 1.6, 0), str(pl["name"]), 0.55, Color(1, 1, 0.9))
|
||||
_bauten.append({"c": c, "size": Vector2(groesse.x, groesse.z), "hoch": HOEHE,
|
||||
"teile": [dach], "waende": waende})
|
||||
|
||||
|
||||
## Ein Fensterband je Wand, damit man die Stockwerke von außen zählt.
|
||||
@@ -163,51 +201,21 @@ func _fenster(parent: Node, cf: Vector3, g: Vector2, ton: Color) -> void:
|
||||
glas, false)
|
||||
|
||||
|
||||
## Ein Lauf von Podest zu Podest -- die Treppe, die Bewohner und Geist
|
||||
## hochgehen. Sie trägt, deshalb mit Kollision.
|
||||
func _treppe(parent: Node, key: String, f: int) -> void:
|
||||
var lauf := Orte.treppen_lauf(HumanDB.PLACES, key, f)
|
||||
var von: Vector3 = lauf[0]
|
||||
var bis: Vector3 = lauf[1]
|
||||
var d := bis - von
|
||||
var stufe := Prims.box(parent, (von + bis) * 0.5, Vector3(1.6, 0.18, d.length()),
|
||||
Color(0.55, 0.50, 0.46))
|
||||
stufe.basis = Basis.looking_at(d.normalized(), Vector3.UP)
|
||||
var r := Orte.treppen_rahmen(HumanDB.PLACES, key)
|
||||
var podest := Orte.treppe(HumanDB.PLACES, key, f + 1)
|
||||
var mass := Orte._xz(r["a"], Orte.PODEST * 2.0, Orte.TREPPE_TIEFE - 0.8)
|
||||
Prims.box(parent, podest + Vector3(0, -0.09, 0), Vector3(mass.x, 0.18, mass.y),
|
||||
Color(0.55, 0.50, 0.46))
|
||||
|
||||
|
||||
## Der Boden einer oberen Etage lässt über dem Treppenhaus ein Loch frei --
|
||||
## sonst stieße, wer die Treppe hochkommt, mit dem Kopf an die Decke.
|
||||
func _decke(parent: Node, key: String, cf: Vector3, _groesse: Vector3, farbe: Color) -> void:
|
||||
for platte in Orte.treppen_decke(HumanDB.PLACES, key):
|
||||
var mitte: Vector3 = platte[0]
|
||||
var mass: Vector2 = platte[1]
|
||||
mitte.y = cf.y - 0.1
|
||||
Prims.box(parent, mitte, Vector3(mass.x, 0.2, mass.y), farbe)
|
||||
|
||||
|
||||
## Ein paar Möbel, damit man sieht, wofür ein Raum da ist. Gestellt wird im
|
||||
## Rahmen der Tür (quer / zur Tür hin); was in der Türzone, im Treppenstreifen
|
||||
## oder auf dem Weg von der Tür zur Treppe stünde, bleibt weg.
|
||||
func _einrichtung(parent: Node, c: Vector3, groesse: Vector3, farbe: Color, key: String,
|
||||
ort: String = "") -> void:
|
||||
if ort == "":
|
||||
ort = key
|
||||
## Rahmen der Tür (quer / zur Tür hin); was in der Türzone stünde, bleibt weg.
|
||||
func _einrichtung(parent: Node, c: Vector3, groesse: Vector3, farbe: Color, key: String) -> void:
|
||||
var ort := key
|
||||
var d: Vector3 = Orte.DIR_VEC[HumanDB.PLACES[ort]["dir"]]
|
||||
var hd: float = (groesse.z if absf(d.z) > 0.0 else groesse.x) * 0.5
|
||||
var hq: float = (groesse.x if absf(d.z) > 0.0 else groesse.z) * 0.5
|
||||
var w := maxf(0.5, hq - 2.0)
|
||||
var h := maxf(0.5, hd - 2.0)
|
||||
if key.begins_with("heim_"):
|
||||
# an der Türwand, links und rechts der Tür -- die Rückwand gehört der Treppe
|
||||
# an der Türwand, links und rechts der Tür
|
||||
_moebel(parent, ort, c, hq - 1.35, hd - 1.6, 0.35, Vector3(1.9, 0.7, 2.4),
|
||||
Color(0.85, 0.85, 0.90))
|
||||
if _moebel(parent, ort, c, -(hq - 1.15), hd - 1.6, 0.75, Vector3(1.5, 0.15, 2.2),
|
||||
farbe.darkened(0.5)):
|
||||
farbe.darkened(0.5)) != null:
|
||||
_moebel(parent, ort, c, -(hq - 1.15), hd - 1.6, 1.05, Vector3(0.8, 0.55, 0.1),
|
||||
Color(0.2, 0.25, 0.3))
|
||||
elif key == "cafe":
|
||||
@@ -236,38 +244,64 @@ func _einrichtung(parent: Node, c: Vector3, groesse: Vector3, farbe: Color, key:
|
||||
Color(0.52, 0.48, 0.46))
|
||||
|
||||
|
||||
## Spuren: was von den Ursachen eines Menschen in seiner Wohnung herumliegt.
|
||||
## Je Ursache eine, an den Seitenwänden hinter Bett und Tisch, ansprechbar mit E.
|
||||
func _spuren(parent: Node, key: String, c: Vector3, groesse: Vector3, wer: Dictionary) -> void:
|
||||
var d: Vector3 = Orte.DIR_VEC[HumanDB.PLACES[key]["dir"]]
|
||||
var hd: float = (groesse.z if absf(d.z) > 0.0 else groesse.x) * 0.5
|
||||
var hq: float = (groesse.x if absf(d.z) > 0.0 else groesse.z) * 0.5
|
||||
var plaetze: Array = [[hq - 0.7, hd - 3.4], [-(hq - 0.7), hd - 3.4], [hq - 0.7, hd - 4.4],
|
||||
[-(hq - 0.7), hd - 4.4], [hq - 0.7, hd - 5.4], [-(hq - 0.7), hd - 5.4], [0.0, hd - 3.9]]
|
||||
var formen: Dictionary = {
|
||||
"anfang": [Vector3(0.28, 0.08, 0.5), Color(0.25, 0.28, 0.36)],
|
||||
"zeit": [Vector3(0.08, 0.6, 0.7), Color(0.92, 0.88, 0.78)],
|
||||
"kraft": [Vector3(0.3, 0.28, 0.55), Color(0.75, 0.55, 0.35)],
|
||||
"halt": [Vector3(0.32, 0.18, 0.42), Color(0.9, 0.86, 0.74)],
|
||||
"grund": [Vector3(0.48, 0.28, 0.75), Color(0.45, 0.35, 0.28)],
|
||||
}
|
||||
var naechster := 0
|
||||
for u in wer.get("ursachen", []):
|
||||
var us := str(u)
|
||||
if not formen.has(us):
|
||||
continue
|
||||
var mass: Vector3 = formen[us][0]
|
||||
var farbe: Color = formen[us][1]
|
||||
while naechster < plaetze.size():
|
||||
var pl: Array = plaetze[naechster]
|
||||
naechster += 1
|
||||
var y: float = 1.5 if us == "zeit" else mass.y * 0.5
|
||||
var stueck := _moebel(parent, key, c, float(pl[0]), float(pl[1]), y, mass, farbe, false, 0.9, true)
|
||||
if stueck == null:
|
||||
continue
|
||||
var marke := Node3D.new()
|
||||
stueck.add_child(marke)
|
||||
marke.add_to_group("interactable")
|
||||
marke.set_meta("kind", "spur")
|
||||
marke.set_meta("arg", us)
|
||||
marke.set_meta("human", str(wer["id"]))
|
||||
marke.set_meta("titel", str(Ursachen.LISTE[us]["spur"]))
|
||||
break
|
||||
|
||||
|
||||
## Ein Möbelstück: aq quer zur Tür, ad zur Tür hin (positiv = zur Tür), Maße
|
||||
## als (quer, hoch, tief). Steht es im Weg, wird es nicht gestellt.
|
||||
## als (quer, hoch, tief). Steht es im Weg, wird es nicht gestellt (null).
|
||||
func _moebel(parent: Node, ort: String, c: Vector3, aq: float, ad: float, y: float,
|
||||
mass: Vector3, farbe: Color, rund: bool = false) -> bool:
|
||||
mass: Vector3, farbe: Color, rund: bool = false, glanz: float = 0.0,
|
||||
klein: bool = false) -> Node3D:
|
||||
var d: Vector3 = Orte.DIR_VEC[HumanDB.PLACES[ort]["dir"]]
|
||||
var quer := Vector3(-d.z, 0, d.x)
|
||||
var pos := c + quer * aq + d * ad
|
||||
pos.y = c.y + y
|
||||
var s: Array = HumanDB.PLACES[ort]["size"]
|
||||
var hd: float = (float(s[1]) if absf(d.z) > 0.0 else float(s[0])) * 0.5
|
||||
var halb := maxf(mass.x, mass.z) * 0.5
|
||||
# Türzone: 2,2 tief hinter der Tür, 4 breit
|
||||
if ad + mass.z * 0.5 > hd - 2.2 and absf(aq) - mass.x * 0.5 < 2.0:
|
||||
return false
|
||||
# Treppenstreifen
|
||||
var r := Orte.treppen_rahmen(HumanDB.PLACES, ort)
|
||||
var n: Vector3 = r["n"]
|
||||
var tief_n: float = (mass.z if absf(n.dot(d)) > 0.5 else mass.x) * 0.5
|
||||
if (pos - c).dot(n) + tief_n > float(r["halb_n"]) - Orte.TREPPE_TIEFE - 0.3:
|
||||
return false
|
||||
# Weg von der Tür zur Treppe
|
||||
var von := Orte.innen_tuer(HumanDB.PLACES, ort)
|
||||
var bis := Orte.treppe(HumanDB.PLACES, ort, 0)
|
||||
var flach := Vector3(pos.x, 0, pos.z)
|
||||
if Geometry3D.get_closest_point_to_segment(flach, von, bis).distance_to(flach) < halb + 0.9:
|
||||
return false
|
||||
# Türzone: 2,2 tief hinter der Tür, 4 breit -- Kleinkram darf näher heran
|
||||
var rand := 1.7 if klein else 2.0
|
||||
if ad + mass.z * 0.5 > hd - 2.2 and absf(aq) - mass.x * 0.5 < rand:
|
||||
return null
|
||||
var welt_mass := mass if absf(d.z) > 0.0 else Vector3(mass.z, mass.y, mass.x)
|
||||
if rund:
|
||||
Prims.cyl(parent, pos, mass.x * 0.5, mass.y, farbe)
|
||||
else:
|
||||
Prims.box(parent, pos, welt_mass, farbe)
|
||||
return true
|
||||
return Prims.cyl(parent, pos, mass.x * 0.5, mass.y, farbe, true, glanz)
|
||||
return Prims.box(parent, pos, welt_mass, farbe, true, glanz)
|
||||
|
||||
|
||||
## Der mittlere Block bleibt frei: Rasen, Teich, Bäume, Bänke. Der Park ist
|
||||
@@ -319,8 +353,20 @@ func _menschen() -> void:
|
||||
for i in range(HumanDB.LIST.size()):
|
||||
var m := Bewohner.new()
|
||||
add_child(m)
|
||||
m.setup(HumanDB.LIST[i], i, HumanDB.PLACES, HumanDB.NETZ, HumanDB.ANKER)
|
||||
m.setup(HumanDB.LIST[i], i, HumanDB.PLACES)
|
||||
m.nav_map = nav_map
|
||||
humans.append(m)
|
||||
for i in range(HumanDB.PASSANTEN_LISTE.size()):
|
||||
var pz := Bewohner.new()
|
||||
add_child(pz)
|
||||
pz.setup(HumanDB.PASSANTEN_LISTE[i], i + 7, HumanDB.PLACES)
|
||||
pz.nav_map = nav_map
|
||||
passanten.append(pz)
|
||||
|
||||
|
||||
## Nur die aktive Welt stellt den Himmel; zwei WorldEnvironments vertragen sich nicht.
|
||||
func aktiv_setzen(an: bool) -> void:
|
||||
_we.environment = _env if an else null
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
|
||||
Reference in New Issue
Block a user