Collisions for Area Change Trigger
This commit is contained in:
parent
721f005ff3
commit
1eeca653f5
5 changed files with 74 additions and 70 deletions
|
|
@ -1,7 +1,3 @@
|
||||||
# Projektübersicht: weltenauge
|
|
||||||
|
|
||||||
Dieses Dokument dient als zentrale Wissensbasis für das Godot-Projekt "weltenauge". Es soll dem KI-Assistenten und den Entwicklern helfen, den Überblick über die Architektur und die Ziele des Projekts zu behalten.
|
|
||||||
|
|
||||||
## 1. Projektziel & Vision
|
## 1. Projektziel & Vision
|
||||||
|
|
||||||
"weltenauge" ist ein von Neverwinter Nights (NWN) inspiriertes Multiplayer-Rollenspiel. Das Kernstück ist eine robuste Client/Server-Architektur, die in einer einzigen Godot-Codebase umgesetzt wird.
|
"weltenauge" ist ein von Neverwinter Nights (NWN) inspiriertes Multiplayer-Rollenspiel. Das Kernstück ist eine robuste Client/Server-Architektur, die in einer einzigen Godot-Codebase umgesetzt wird.
|
||||||
|
|
@ -131,4 +127,3 @@ Die Konfiguration des `MultiplayerSynchronizer` für wiederverwendbare Szenen (w
|
||||||
sync.set_visibility_for(id, true)
|
sync.set_visibility_for(id, true)
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ albedo_color = Color(9.3854965e-07, 0.4579985, 0.32070312, 1)
|
||||||
[sub_resource type="PlaneMesh" id="PlaneMesh_0iae2"]
|
[sub_resource type="PlaneMesh" id="PlaneMesh_0iae2"]
|
||||||
material = SubResource("StandardMaterial3D_30klc")
|
material = SubResource("StandardMaterial3D_30klc")
|
||||||
|
|
||||||
|
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_1yg6u"]
|
||||||
|
data = PackedVector3Array(1, 0, 1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, -1, 1, 0, -1)
|
||||||
|
|
||||||
[node name="keuwin_grasland" type="Node3D" unique_id=1953380732]
|
[node name="keuwin_grasland" type="Node3D" unique_id=1953380732]
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1262732162]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1262732162]
|
||||||
|
|
@ -35,6 +38,11 @@ environment = SubResource("Environment_8r0t0")
|
||||||
transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0)
|
transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0)
|
||||||
mesh = SubResource("PlaneMesh_0iae2")
|
mesh = SubResource("PlaneMesh_0iae2")
|
||||||
|
|
||||||
|
[node name="StaticBody3D" type="StaticBody3D" parent="Architecture/MeshInstance3D" unique_id=209028061]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="Architecture/MeshInstance3D/StaticBody3D" unique_id=1119853048]
|
||||||
|
shape = SubResource("ConcavePolygonShape3D_1yg6u")
|
||||||
|
|
||||||
[node name="Navigation" type="Node3D" parent="." unique_id=1472582667]
|
[node name="Navigation" type="Node3D" parent="." unique_id=1472582667]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.01873529, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.01873529, 0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ albedo_color = Color(0.1800012, 0.004964735, 0.2305904, 1)
|
||||||
[sub_resource type="PlaneMesh" id="PlaneMesh_0iae2"]
|
[sub_resource type="PlaneMesh" id="PlaneMesh_0iae2"]
|
||||||
material = SubResource("StandardMaterial3D_30klc")
|
material = SubResource("StandardMaterial3D_30klc")
|
||||||
|
|
||||||
|
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_0hy1c"]
|
||||||
|
data = PackedVector3Array(1, 0, 1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, -1, 1, 0, -1)
|
||||||
|
|
||||||
[node name="halle_der_ahnen" type="Node3D" unique_id=1953380732]
|
[node name="halle_der_ahnen" type="Node3D" unique_id=1953380732]
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1262732162]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1262732162]
|
||||||
|
|
@ -38,6 +41,11 @@ environment = SubResource("Environment_8r0t0")
|
||||||
transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0)
|
transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0)
|
||||||
mesh = SubResource("PlaneMesh_0iae2")
|
mesh = SubResource("PlaneMesh_0iae2")
|
||||||
|
|
||||||
|
[node name="StaticBody3D" type="StaticBody3D" parent="Architecture/MeshInstance3D" unique_id=1395982254]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="Architecture/MeshInstance3D/StaticBody3D" unique_id=521000493]
|
||||||
|
shape = SubResource("ConcavePolygonShape3D_0hy1c")
|
||||||
|
|
||||||
[node name="Navigation" type="Node3D" parent="." unique_id=1472582667]
|
[node name="Navigation" type="Node3D" parent="." unique_id=1472582667]
|
||||||
|
|
||||||
[node name="SpawnPoints" type="Node3D" parent="." unique_id=438053421]
|
[node name="SpawnPoints" type="Node3D" parent="." unique_id=438053421]
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ func _on_player_disconnected(id):
|
||||||
func change_player_area(id: int, area_key: String):
|
func change_player_area(id: int, area_key: String):
|
||||||
if not multiplayer.is_server(): return
|
if not multiplayer.is_server(): return
|
||||||
print("Server is loading ", area_key)
|
print("Server is loading ", area_key)
|
||||||
var path = area_paths[area_key
|
var path = area_paths[area_key]
|
||||||
|
|
||||||
var container_name = area_key + "_container"
|
var container_name = area_key + "_container"
|
||||||
var viewport_name = area_key + "_viewport"
|
var viewport_name = area_key + "_viewport"
|
||||||
|
|
@ -111,7 +111,7 @@ func client_ready_to_spawn():
|
||||||
if not multiplayer.is_server(): return
|
if not multiplayer.is_server(): return
|
||||||
|
|
||||||
if player_worlds.has(id):
|
if player_worlds.has(id):
|
||||||
var viewport_node = player_worlds[id
|
var viewport_node = player_worlds[id]
|
||||||
print("Client ", id, " ist bereit. Spawne jetzt.")
|
print("Client ", id, " ist bereit. Spawne jetzt.")
|
||||||
_spawn_player_in_world(id, viewport_node)
|
_spawn_player_in_world(id, viewport_node)
|
||||||
|
|
||||||
|
|
@ -152,7 +152,8 @@ func client_load_area(area_key):
|
||||||
|
|
||||||
|
|
||||||
# Kamera-Setup nur beim ersten Laden oder falls sie gelöscht wurde
|
# Kamera-Setup nur beim ersten Laden oder falls sie gelöscht wurde
|
||||||
if not multiplayer.is_server() and local_camera == null:
|
if not multiplayer.is_server() and not is_instance_valid(local_camera):
|
||||||
|
print("Setting up camera")
|
||||||
setup_client_camera(viewport_node)
|
setup_client_camera(viewport_node)
|
||||||
|
|
||||||
# Jetzt können wir dem Server ok fürs Spawnen geben
|
# Jetzt können wir dem Server ok fürs Spawnen geben
|
||||||
|
|
@ -225,11 +226,3 @@ func handle_area_change_request(area_key: String):
|
||||||
|
|
||||||
# 2. Bestehende Funktion für den Wechsel aufrufen
|
# 2. Bestehende Funktion für den Wechsel aufrufen
|
||||||
change_player_area(id, area_key)
|
change_player_area(id, area_key)
|
||||||
|
|
||||||
## The following script is for a Portal node.
|
|
||||||
## File: portal.gd
|
|
||||||
extends Area3D
|
|
||||||
|
|
||||||
## Definiert, wohin dieses Portal führt.
|
|
||||||
## Dieser Wert muss einem Schlüssel in NetworkManager.area_paths entsprechen.
|
|
||||||
@export var target_area_key: String = ""
|
|
||||||
|
|
|
||||||
|
|
@ -76,53 +76,53 @@ func request_move(target: Vector3):
|
||||||
path = [target]
|
path = [target]
|
||||||
target_position = target
|
target_position = target
|
||||||
|
|
||||||
|
# ERSETZT die alte _input(event)-Funktion
|
||||||
func _input(event):
|
func _input(event):
|
||||||
# Ist dieser Avatar nicht der des lokalen Spielers? Wenn ja, sofort abbrechen.
|
# Ist dieser Avatar nicht der des lokalen Spielers? Wenn ja, sofort abbrechen.
|
||||||
if name.to_int() != multiplayer.get_unique_id(): return
|
if name.to_int() != multiplayer.get_unique_id(): return
|
||||||
|
|
||||||
# NUR der Client, der diesen Spieler kontrolliert, darf klicken
|
|
||||||
#if not is_multiplayer_authority(): return
|
|
||||||
if DisplayServer.get_name() == "headless": return # Server ignoriert Klicks
|
if DisplayServer.get_name() == "headless": return # Server ignoriert Klicks
|
||||||
|
|
||||||
if multiplayer.is_server(): return # Kein Input vom Server
|
if multiplayer.is_server(): return # Kein Input vom Server
|
||||||
|
|
||||||
if event is InputEventMouseButton and event.pressed:
|
# Wir reagieren nur auf den Klick der linken Maustaste
|
||||||
print("Click")
|
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
var target = _get_click_floor_pos()
|
var click_result = _get_click_result()
|
||||||
if target != Vector3.INF:
|
|
||||||
print("Request move to target {0} by user {1}".format([target, name]))
|
if click_result: # Prüfen, ob der Strahl überhaupt etwas getroffen hat
|
||||||
# RPC an den Server (ID 1)
|
var collider = click_result.collider
|
||||||
request_move.rpc_id(1, target)
|
|
||||||
|
# PRIORITÄT 1: Haben wir ein Portal angeklickt?
|
||||||
|
# Wir prüfen, ob das getroffene Objekt gültig ist und zur Gruppe "portals" gehört.
|
||||||
|
if is_instance_valid(collider) and collider.is_in_group("portals"):
|
||||||
|
print("Portal angeklickt. Fordere Area-Wechsel an zu: ", collider.target_area_key)
|
||||||
|
# Sende RPC direkt an den NetworkManager mit dem Ziel des Portals
|
||||||
|
NetworkManager.handle_area_change_request.rpc_id(1, collider.target_area_key)
|
||||||
|
return # Wichtig: Aktion ist beendet, nichts weiter tun.
|
||||||
|
|
||||||
|
# PRIORITÄT 2 (Fallback): Wir haben etwas anderes getroffen (vermutlich den Boden), also bewegen.
|
||||||
|
var target_position = click_result.position
|
||||||
|
request_move.rpc_id(1, target_position)
|
||||||
|
|
||||||
|
|
||||||
func _get_click_floor_pos():
|
|
||||||
# Wir brauchen die Kamera, die tatsächlich für diesen Viewport zuständig ist
|
# ERSETZT die alte _get_click_floor_pos()-Funktion
|
||||||
|
func _get_click_result():
|
||||||
var camera = get_viewport().get_camera_3d()
|
var camera = get_viewport().get_camera_3d()
|
||||||
|
if not camera: return null
|
||||||
# DEBUG: Falls keine Kamera gefunden wird, wissen wir woran es liegt
|
|
||||||
if not camera:
|
|
||||||
print("Keine Kamera im Viewport von ", name, " gefunden!")
|
|
||||||
return Vector3.INF
|
|
||||||
|
|
||||||
var mouse_pos = get_viewport().get_mouse_position()
|
var mouse_pos = get_viewport().get_mouse_position()
|
||||||
|
var space_state = get_world_3d().direct_space_state
|
||||||
var ray_origin = camera.project_ray_origin(mouse_pos)
|
var ray_origin = camera.project_ray_origin(mouse_pos)
|
||||||
var ray_dir = camera.project_ray_normal(mouse_pos)
|
var ray_dir = camera.project_ray_normal(mouse_pos)
|
||||||
|
|
||||||
# Da wir in einer isolierten World3D sind, müssen wir den Physics-State
|
|
||||||
# des Viewports direkt abfragen, statt nur gegen eine Plane zu prüfen:
|
|
||||||
var space_state = get_world_3d().direct_space_state
|
|
||||||
var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_dir * 1000)
|
var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_dir * 1000)
|
||||||
|
# WICHTIG: Schließe den eigenen Körper von der Abfrage aus, um nicht auf sich selbst zu klicken.
|
||||||
|
query.exclude = [self]
|
||||||
|
|
||||||
|
# NEU: Sage dem Raycast, dass er auch mit Area3D-Nodes kollidieren soll.
|
||||||
|
query.collide_with_areas = true
|
||||||
|
|
||||||
# Falls du Layer nutzt: query.collision_mask = 1
|
|
||||||
var result = space_state.intersect_ray(query)
|
var result = space_state.intersect_ray(query)
|
||||||
|
return result # Gibt das komplette Ergebnis-Dictionary zurück
|
||||||
if result:
|
|
||||||
return result.position
|
|
||||||
|
|
||||||
# Fallback auf Plane, falls kein Mesh getroffen wurde
|
|
||||||
var world_plane = Plane(Vector3.UP, 0)
|
|
||||||
var intersection = world_plane.intersects_ray(ray_origin, ray_dir)
|
|
||||||
return intersection if intersection else Vector3.INF
|
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta):
|
func _process(_delta):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue