Start Character Rooster
This commit is contained in:
parent
9b457d6fb4
commit
6352347c4d
9 changed files with 155 additions and 15 deletions
|
|
@ -2,14 +2,6 @@
|
|||
|
||||
"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.
|
||||
|
||||
Dies ist ein "Ewigkeits-Projekt", das nichtkommerziell ist, und
|
||||
vom Entwickler immer wieder für längere Zeit weggelegt wird. Fokus liegt auf 3D Welten und Weltensimulation.
|
||||
Dadurch ist es notwendig, dass das gesamte System so modular und
|
||||
selbsterklärend aufgebaut wird, dass eine Rückkehr zum Projekt nach
|
||||
langer Pause immer noch machbar ist. Features sind daher soweit einzuschränken,
|
||||
dass ein absolut Minimaler Prototyp in einem vernünftigen Zeitraum erstellbar wird.
|
||||
Ausserdem ist Modularität und einfache Testbarkeit Pflicht.
|
||||
|
||||
## 2. Kernkonzepte der Architektur
|
||||
|
||||
### Client/Server-Modell
|
||||
|
|
@ -63,11 +55,11 @@ Alle Skripte und die dazugehörige Logik sollen thematisch in Unterordnern unter
|
|||
## 5. Implementierungsdetails & Konventionen
|
||||
|
||||
- **Click-to-Move:** Die Spielerbewegung soll aus Performancegründen über eine AStar-Implementierung (`systems/navigation/`) gesteuert werden.
|
||||
- **GDScript-Konvention: `async`-Schlüsselwort vermeiden:** In GDScript 4 wird jede Funktion, die `await` enthält, automatisch von der Engine als asynchron behandelt. Das `async`-Schlüsselwort in der Funktionssignatur (`async func...`) ist optional und hat in diesem Projekt wiederholt zu schwer diagnostizierbaren Syntaxfehlern geführt.
|
||||
- **Regel:** Das `async`-Schlüsselwort **darf nicht** verwendet werden.
|
||||
- **Korrekt:** `func my_function(): await get_tree().process_frame`
|
||||
- **Falsch:** `async func my_function(): await get_tree().process_frame`
|
||||
|
||||
- **Version:** Basis ist **Godot 4.6**. Nutze aktuelle APIs (besonders für Navigation und Multiplayer).
|
||||
- **GDScript-Regel (No Async Keyword):** Das Schlüsselwort `async` darf **nicht** verwendet werden. Funktionen mit `await` sind automatisch asynchron.
|
||||
- - *Korrekt:* `func move(): await get_tree().process_frame`
|
||||
- **Statische Typisierung:** Nutze konsequent Typ-Hints (z.B. `var health: int = 100`), um Fehlerquellen zu minimieren.
|
||||
- **Node-Referenzen:** Nutze `@onready var` und relative Pfade. Vermeide `/root/`-Pfade wegen der SubViewport-Struktur.
|
||||
|
||||
## 6. Globale Skripte (Autoloads)
|
||||
|
||||
|
|
@ -114,9 +106,17 @@ Dieser Abschnitt beschreibt die Verantwortlichkeiten der zentralen Skripte im Pr
|
|||
|
||||
## 8. Wichtige Erkenntnisse & Best Practices
|
||||
|
||||
Dieser Abschnitt dokumentiert wichtige technische Lektionen, die während der Entwicklung gelernt wurden.
|
||||
* **MultiplayerSynchronizer:** Konfiguration **nur per Code** in `_ready()`.
|
||||
* **Property-Syntax:** Pfade in der `SceneReplicationConfig` müssen mit Doppelpunkt starten, wenn sie sich auf den Root-Pfad beziehen (z.B. `config.add_property(":sync_position")`).
|
||||
* **MultiplayerSpawner:** Nutze einen generischen Spawner in der Basis-Level-Szene für automatische Synchronisation bei Late-Joinern.
|
||||
|
||||
### Technische Detail
|
||||
## 7. Anweisungen für die KI (Refactoring & Generierung)
|
||||
|
||||
* **Thinking-Phase:** Analysiere vor der Code-Ausgabe, ob Änderungen die SubViewport-Isolation oder die Server-Autorität beeinflussen könnten.
|
||||
* **Minimaler Eingriff:** Verändere nur angeforderte Logik. Behalte Kommentare und Signal-Strukturen bei.
|
||||
* **Stil:** Schreibe sauberen, performanten GDScript 2.0 Code. Bevorzuge Komposition gegenüber Vererbung.
|
||||
|
||||
### Technische Details
|
||||
|
||||
Es gibt kein async Keyword, aber await funktioniert trotzdem. => await get_tree().process_frame
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
|
||||
[ext_resource type="Script" uid="uid://162cjpb4lac4" path="res://game_root.gd" id="1_xj5yv"]
|
||||
[ext_resource type="PackedScene" uid="uid://bhywhrlwjox6t" path="res://ui/menus/main_menu.tscn" id="2_pjpa4"]
|
||||
[ext_resource type="PackedScene" uid="uid://ca2bhph2k3xpy" path="res://ui/character_select/character_select.tscn" id="3_bxsma"]
|
||||
|
||||
[node name="Game_root" type="Node2D" unique_id=1336425327]
|
||||
script = ExtResource("1_xj5yv")
|
||||
|
||||
[node name="MainMenu" parent="." unique_id=1731390780 instance=ExtResource("2_pjpa4")]
|
||||
visible = false
|
||||
|
||||
[node name="CharacterSelect" parent="." unique_id=2079351175 instance=ExtResource("3_bxsma")]
|
||||
|
|
|
|||
9
weltenauge/systems/data/character_data.gd
Normal file
9
weltenauge/systems/data/character_data.gd
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class_name CharacterData
|
||||
|
||||
var id: int = -1 # Eindeutige ID (wird später wichtig)
|
||||
var name: String = "New Hero"
|
||||
var level: int = 1
|
||||
var area: String = "keuwin_grasland" # Start-Area
|
||||
var position: Vector3 = Vector3.ZERO
|
||||
var testvariable: String = "test"
|
||||
# Hier kommen später noch Inventar, Ausrüstung, etc. hinzu...
|
||||
1
weltenauge/systems/data/character_data.gd.uid
Normal file
1
weltenauge/systems/data/character_data.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dsylf8nx1e2wc
|
||||
47
weltenauge/systems/data/save_load.gd
Normal file
47
weltenauge/systems/data/save_load.gd
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
class_name SaveLoad
|
||||
|
||||
const SAVE_PATH = "user://player_data.json"
|
||||
|
||||
static func save_character(data: CharacterData):
|
||||
var save_dict = {
|
||||
"id": data.id,
|
||||
"name": data.name,
|
||||
"level": data.level,
|
||||
"area": data.area,
|
||||
"position": { # Vector3 als Dictionary speichern
|
||||
"x": data.position.x,
|
||||
"y": data.position.y,
|
||||
"z": data.position.z
|
||||
}
|
||||
}
|
||||
|
||||
var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
|
||||
if file:
|
||||
file.store_string(JSON.stringify(save_dict, "\t"))
|
||||
file.close()
|
||||
print("Saved player data to ", SAVE_PATH)
|
||||
else:
|
||||
printerr("Error saving to ", SAVE_PATH)
|
||||
|
||||
static func load_character() -> CharacterData:
|
||||
var file = FileAccess.open(SAVE_PATH, FileAccess.READ)
|
||||
if file:
|
||||
var json_string = file.get_as_text()
|
||||
file.close()
|
||||
|
||||
var data = JSON.parse_string(json_string)
|
||||
if data == null:
|
||||
printerr("JSON Parse Error")
|
||||
return null
|
||||
|
||||
var char_data = CharacterData.new()
|
||||
char_data.id = data["id"]
|
||||
char_data.name = data["name"]
|
||||
char_data.level = data["level"]
|
||||
char_data.area = data["area"]
|
||||
char_data.position = Vector3(data["position"]["x"],data["position"]["y"],data["position"]["z"])
|
||||
print("Loaded player data from ", SAVE_PATH)
|
||||
return char_data
|
||||
else:
|
||||
print("No savefile found, returning default CharacterData.")
|
||||
return null
|
||||
1
weltenauge/systems/data/save_load.gd.uid
Normal file
1
weltenauge/systems/data/save_load.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dhettikhnfdt4
|
||||
47
weltenauge/ui/character_select/character_select.gd
Normal file
47
weltenauge/ui/character_select/character_select.gd
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
extends Control
|
||||
|
||||
@onready var character_list = $ItemList # Oder Tree, je nachdem wie die Liste aussieht
|
||||
|
||||
var characters: Array[CharacterData] = []
|
||||
|
||||
func _ready():
|
||||
# Vorhandene Charaktere laden
|
||||
load_characters()
|
||||
update_ui()
|
||||
|
||||
func load_characters():
|
||||
# (Hier kommt die Logik zum Durchsuchen des "user://" Ordners nach JSON-Dateien hin)
|
||||
# (Für den Anfang können wir einfach einen Standard-Charakter erstellen)
|
||||
var data = SaveLoad.load_character()
|
||||
if data == null:
|
||||
data = CharacterData.new()
|
||||
data.id = 1 # Eindeutige ID generieren (später)
|
||||
characters = [data]
|
||||
SaveLoad.save_character(data)
|
||||
else:
|
||||
characters = [data]
|
||||
|
||||
|
||||
func update_ui():
|
||||
character_list.clear()
|
||||
for char in characters:
|
||||
character_list.add_item(char.name) # Hier später: Icons, Stats etc.
|
||||
|
||||
func _on_character_selected(index):
|
||||
var selected_character = characters[index]
|
||||
# (Hier Code zum Anzeigen der Details und Bearbeiten des Charakters)
|
||||
# (Am Ende: StartGame.emit(selected_character))
|
||||
pass
|
||||
|
||||
signal StartGame(character: CharacterData) # Ab hier wirds interessant.
|
||||
func _on_start_game_pressed():
|
||||
if characters.size() > 0:
|
||||
StartGame.emit(characters[0]) # Einfachheitshalber: Ersten Charakter nehmen
|
||||
# Aufräumen und die Spielwelt laden (später)
|
||||
|
||||
func _on_create_character_pressed():
|
||||
var new_character = CharacterData.new()
|
||||
new_character.id = characters.size() + 1 # Provisorische ID
|
||||
characters.append(new_character)
|
||||
SaveLoad.save_character(new_character)
|
||||
update_ui()
|
||||
1
weltenauge/ui/character_select/character_select.gd.uid
Normal file
1
weltenauge/ui/character_select/character_select.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b5htxbkq23mo6
|
||||
30
weltenauge/ui/character_select/character_select.tscn
Normal file
30
weltenauge/ui/character_select/character_select.tscn
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
[gd_scene format=3 uid="uid://ca2bhph2k3xpy"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b5htxbkq23mo6" path="res://ui/character_select/character_select.gd" id="1_o7qwi"]
|
||||
|
||||
[node name="CharacterSelect" type="Control" unique_id=2056198803]
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
script = ExtResource("1_o7qwi")
|
||||
|
||||
[node name="ItemList" type="ItemList" parent="." unique_id=1442954519]
|
||||
layout_mode = 0
|
||||
offset_top = 32.0
|
||||
offset_right = 368.0
|
||||
offset_bottom = 571.0
|
||||
|
||||
[node name="Label" type="Label" parent="." unique_id=1413425912]
|
||||
layout_mode = 0
|
||||
offset_left = 111.0
|
||||
offset_top = 2.0
|
||||
offset_right = 217.0
|
||||
offset_bottom = 31.0
|
||||
text = "Charaktere:"
|
||||
|
||||
[node name="Button" type="Button" parent="." unique_id=50672893]
|
||||
layout_mode = 0
|
||||
offset_left = 113.0
|
||||
offset_top = 585.0
|
||||
offset_right = 224.0
|
||||
offset_bottom = 621.0
|
||||
text = "Start"
|
||||
Loading…
Add table
Add a link
Reference in a new issue