Souls/RPG-Hybrid Bewegungssystem, Kamera, Animationen & Dokumentation
Bewegung: - Souls-Modus (kein Ziel): Charakter dreht sich zur Laufrichtung relativ zu camera_pivot.world_yaw; bei RMB gehalten → Strafe statt Drehung - Walk-Toggle (NumLock): RPG-Strafe-Modus mit langsamerer Geschwindigkeit - Lock-On (Ziel markiert): Spieler dreht sich smooth zum Gegner, WASD = Strafe - Ausweichrolle (Shift): rollt in Eingaberichtung (world_yaw-relativ im Souls-Modus) - Sofort-180°-Snap statt animierter Drehung bei >150° Winkelunterschied Kamera (camera_pivot.gd): - world_yaw: absolute Weltausrichtung, unabhängig von Spielerrotation (kein Feedback-Loop) - LMB gehalten: Kamera orbitet, Spieler dreht sich nicht - RMB gehalten: Spieler + Kamera drehen sich gemeinsam - Soft Lock-On: camera_pivot dreht Spieler smooth zum Ziel Animationen: - Neue FBX-Animationen: Quick Roll, Running Jump, Walking Jump, Running Strafe L/R, Running Turn 180 - Animationen im Souls-Modus: immer "run" vorwärts; S = walk_back - Root-Motion-Strip: XZ-Bewegung auf Knochen-Tracks wird genullt Welt: - Boden-Shader: Schachbrettmuster in World-Space (INV_VIEW_MATRIX) - ProceduralSkyMaterial + WorldEnvironment per Code - Alte assets/animations und assets/models durch Warrior+Animation ersetzt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cfmx5li2h2gsb"
|
||||
path="res://.godot/imported/Autoattack.fbx-3670ec5144a5943b8d9ee95f7186efd3.scn"
|
||||
uid="uid://vo540cksibhf"
|
||||
path="res://.godot/imported/Autoattack.fbx-53cd4a5b8eaf2a74c3bff45c47238010.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Autoattack.fbx"
|
||||
dest_files=["res://.godot/imported/Autoattack.fbx-3670ec5144a5943b8d9ee95f7186efd3.scn"]
|
||||
source_file="res://assets/Warrior+Animation/Autoattack.fbx"
|
||||
dest_files=["res://.godot/imported/Autoattack.fbx-53cd4a5b8eaf2a74c3bff45c47238010.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://dpm8k57akvg3k"
|
||||
path="res://.godot/imported/Dying Backwards.fbx-77c7ef392c2c96235d9f4f9f7c331de8.scn"
|
||||
uid="uid://b3n7056tyskib"
|
||||
path="res://.godot/imported/Dying Backwards.fbx-f2b9c7b8a945c1b32b59e6174df75635.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Dying Backwards.fbx"
|
||||
dest_files=["res://.godot/imported/Dying Backwards.fbx-77c7ef392c2c96235d9f4f9f7c331de8.scn"]
|
||||
source_file="res://assets/Warrior+Animation/Dying Backwards.fbx"
|
||||
dest_files=["res://.godot/imported/Dying Backwards.fbx-f2b9c7b8a945c1b32b59e6174df75635.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cmpv0gty3rcm6"
|
||||
path="res://.godot/imported/Heavy Strike.fbx-5d5103d3203a6a38edd177bdd35ccb16.scn"
|
||||
uid="uid://r25qslfmbg4s"
|
||||
path="res://.godot/imported/Heavy Strike.fbx-f9eed104269d2005bd3440c7e91ac7ea.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Heavy Strike.fbx"
|
||||
dest_files=["res://.godot/imported/Heavy Strike.fbx-5d5103d3203a6a38edd177bdd35ccb16.scn"]
|
||||
source_file="res://assets/Warrior+Animation/Heavy Strike.fbx"
|
||||
dest_files=["res://.godot/imported/Heavy Strike.fbx-f9eed104269d2005bd3440c7e91ac7ea.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
BIN
assets/Warrior+Animation/Quick Roll To Run.fbx
Normal file
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://wurcf4qd6sn1"
|
||||
path="res://.godot/imported/Left Strafe Walking.fbx-918d1a60e568dd43e53b31199b3f57d3.scn"
|
||||
uid="uid://ctup6ycp8fuqk"
|
||||
path="res://.godot/imported/Quick Roll To Run.fbx-b02b0c01f60ef724d35b77bd3136b689.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Left Strafe Walking.fbx"
|
||||
dest_files=["res://.godot/imported/Left Strafe Walking.fbx-918d1a60e568dd43e53b31199b3f57d3.scn"]
|
||||
source_file="res://assets/Warrior+Animation/Quick Roll To Run.fbx"
|
||||
dest_files=["res://.godot/imported/Quick Roll To Run.fbx-b02b0c01f60ef724d35b77bd3136b689.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
BIN
assets/Warrior+Animation/Running Jump.fbx
Normal file
44
assets/Warrior+Animation/Running Jump.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bhte2kawlr8bv"
|
||||
path="res://.godot/imported/Running Jump.fbx-02348a505e41bceab5ba1604094098b3.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/Running Jump.fbx"
|
||||
dest_files=["res://.godot/imported/Running Jump.fbx-02348a505e41bceab5ba1604094098b3.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
BIN
assets/Warrior+Animation/Running Left Strafe.fbx
Normal file
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bhv5ig8xnw4v0"
|
||||
path="res://.godot/imported/Right Strafe Walking.fbx-f1318b812bb12a00a11ec8112c1f6855.scn"
|
||||
uid="uid://gkl3ij5dvghc"
|
||||
path="res://.godot/imported/Running Left Strafe.fbx-f8ffe3f38c4c7e18db7a5cb37788743e.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Right Strafe Walking.fbx"
|
||||
dest_files=["res://.godot/imported/Right Strafe Walking.fbx-f1318b812bb12a00a11ec8112c1f6855.scn"]
|
||||
source_file="res://assets/Warrior+Animation/Running Left Strafe.fbx"
|
||||
dest_files=["res://.godot/imported/Running Left Strafe.fbx-f8ffe3f38c4c7e18db7a5cb37788743e.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
BIN
assets/Warrior+Animation/Running Right Strafe run.fbx
Normal file
44
assets/Warrior+Animation/Running Right Strafe run.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://lm2ofk411i4"
|
||||
path="res://.godot/imported/Running Right Strafe run.fbx-31b53a15a609d5d0f6711845d5941119.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/Running Right Strafe run.fbx"
|
||||
dest_files=["res://.godot/imported/Running Right Strafe run.fbx-31b53a15a609d5d0f6711845d5941119.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
BIN
assets/Warrior+Animation/Running Turn 180.fbx
Normal file
44
assets/Warrior+Animation/Running Turn 180.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://7j8e63c4gtt8"
|
||||
path="res://.godot/imported/Running Turn 180.fbx-863af9daa2d6c9284445a0bd16da3f45.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/Running Turn 180.fbx"
|
||||
dest_files=["res://.godot/imported/Running Turn 180.fbx-863af9daa2d6c9284445a0bd16da3f45.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bck2nreqc2gb3"
|
||||
path="res://.godot/imported/Walking Backwards.fbx-485a35ec4e48207190c0e10717297ab7.scn"
|
||||
uid="uid://1unm2efm7s3f"
|
||||
path="res://.godot/imported/Walking Backwards.fbx-cf1183868a4d9d213ebca12d53b3ae81.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Walking Backwards.fbx"
|
||||
dest_files=["res://.godot/imported/Walking Backwards.fbx-485a35ec4e48207190c0e10717297ab7.scn"]
|
||||
source_file="res://assets/Warrior+Animation/Walking Backwards.fbx"
|
||||
dest_files=["res://.godot/imported/Walking Backwards.fbx-cf1183868a4d9d213ebca12d53b3ae81.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
BIN
assets/Warrior+Animation/Walking Jump.fbx
Normal file
44
assets/Warrior+Animation/Walking Jump.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cs35mwwqnitrp"
|
||||
path="res://.godot/imported/Walking Jump.fbx-327d0240715eb7b17d5a21fdf85a64e6.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/Walking Jump.fbx"
|
||||
dest_files=["res://.godot/imported/Walking Jump.fbx-327d0240715eb7b17d5a21fdf85a64e6.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
44
assets/Warrior+Animation/castle_guard_01.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://daeym1tdcnhhd"
|
||||
path="res://.godot/imported/castle_guard_01.fbx-799eb31d8eba8e9d75bc4ca0ebabde43.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/castle_guard_01.fbx"
|
||||
dest_files=["res://.godot/imported/castle_guard_01.fbx-799eb31d8eba8e9d75bc4ca0ebabde43.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://clc13cbh72eov"
|
||||
path.s3tc="res://.godot/imported/Walking_0.png-04bee5fb7233febb0f01a436248762b5.s3tc.ctex"
|
||||
uid="uid://d07eqssia5uce"
|
||||
path.s3tc="res://.godot/imported/castle_guard_01_0.png-64069e01a3d6f24be61f8b235530fefe.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
|
|
@ -14,8 +14,8 @@ generator_parameters={
|
|||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Walking_0.png"
|
||||
dest_files=["res://.godot/imported/Walking_0.png-04bee5fb7233febb0f01a436248762b5.s3tc.ctex"]
|
||||
source_file="res://assets/Warrior+Animation/castle_guard_01_0.png"
|
||||
dest_files=["res://.godot/imported/castle_guard_01_0.png-64069e01a3d6f24be61f8b235530fefe.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dphpf7qa3hkvh"
|
||||
path="res://.godot/imported/Walking_1.png-ba1e8e03f6c24586962fc1b62dccdfe9.ctex"
|
||||
uid="uid://b3ofnfnpmjbv4"
|
||||
path="res://.godot/imported/castle_guard_01_1.png-cc0fe514ff22e36663b36dadc87f514d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
|
@ -13,8 +13,8 @@ generator_parameters={
|
|||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Walking_1.png"
|
||||
dest_files=["res://.godot/imported/Walking_1.png-ba1e8e03f6c24586962fc1b62dccdfe9.ctex"]
|
||||
source_file="res://assets/Warrior+Animation/castle_guard_01_1.png"
|
||||
dest_files=["res://.godot/imported/castle_guard_01_1.png-cc0fe514ff22e36663b36dadc87f514d.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://coqdhbh5uwkjf"
|
||||
path.s3tc="res://.godot/imported/warrior_2.png-0164e54eb7a56547f2b489b55a55ca25.s3tc.ctex"
|
||||
uid="uid://cgqa0kap7nde7"
|
||||
path.s3tc="res://.godot/imported/castle_guard_01_2.png-7d061f5d8fcddd99f52285a75a1f6982.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
|
|
@ -14,8 +14,8 @@ generator_parameters={
|
|||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/warrior_2.png"
|
||||
dest_files=["res://.godot/imported/warrior_2.png-0164e54eb7a56547f2b489b55a55ca25.s3tc.ctex"]
|
||||
source_file="res://assets/Warrior+Animation/castle_guard_01_2.png"
|
||||
dest_files=["res://.godot/imported/castle_guard_01_2.png-7d061f5d8fcddd99f52285a75a1f6982.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ compress/channel_pack=0
|
|||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=1
|
||||
roughness/src_normal="res://assets/models/warrior_2.png"
|
||||
roughness/src_normal="res://assets/Warrior+Animation/castle_guard_01_2.png"
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://da1w523lg7i2b"
|
||||
path="res://.godot/imported/warrior.fbx-b4acdad06881ff9e3d6bb32ec03761c9.scn"
|
||||
uid="uid://bykl7cq8qx0i"
|
||||
path="res://.godot/imported/idle.fbx-6a27ea4d436753d86355f51c515589cc.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/warrior.fbx"
|
||||
dest_files=["res://.godot/imported/warrior.fbx-b4acdad06881ff9e3d6bb32ec03761c9.scn"]
|
||||
source_file="res://assets/Warrior+Animation/idle.fbx"
|
||||
dest_files=["res://.godot/imported/idle.fbx-6a27ea4d436753d86355f51c515589cc.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cxr7xlsq3vvhi"
|
||||
path="res://.godot/imported/Idle.fbx-ecf8c7ec9efbd10accf3a620e1992827.scn"
|
||||
uid="uid://kmypwcf75mm5"
|
||||
path="res://.godot/imported/jump.fbx-4653cae9b7a0a7fea2fdbf8b3432de58.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Idle.fbx"
|
||||
dest_files=["res://.godot/imported/Idle.fbx-ecf8c7ec9efbd10accf3a620e1992827.scn"]
|
||||
source_file="res://assets/Warrior+Animation/jump.fbx"
|
||||
dest_files=["res://.godot/imported/jump.fbx-4653cae9b7a0a7fea2fdbf8b3432de58.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
44
assets/Warrior+Animation/left strafe walking.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://r3rpi8fcmkne"
|
||||
path="res://.godot/imported/left strafe walking.fbx-5f9622a45ca2758ee53f10a63deb0dde.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/left strafe walking.fbx"
|
||||
dest_files=["res://.godot/imported/left strafe walking.fbx-5f9622a45ca2758ee53f10a63deb0dde.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bjwb78njpjo2d"
|
||||
path="res://.godot/imported/Start Walking.fbx-c54b281554787bf73bcae8159e84a930.scn"
|
||||
uid="uid://x3sux8evka5y"
|
||||
path="res://.godot/imported/left strafe.fbx-123d2901134abff2b2af22577a6143ca.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Start Walking.fbx"
|
||||
dest_files=["res://.godot/imported/Start Walking.fbx-c54b281554787bf73bcae8159e84a930.scn"]
|
||||
source_file="res://assets/Warrior+Animation/left strafe.fbx"
|
||||
dest_files=["res://.godot/imported/left strafe.fbx-123d2901134abff2b2af22577a6143ca.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
44
assets/Warrior+Animation/left turn 90.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bfn8s86o81t86"
|
||||
path="res://.godot/imported/left turn 90.fbx-7ede25625141e45b963a6f806ea7a4b6.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/left turn 90.fbx"
|
||||
dest_files=["res://.godot/imported/left turn 90.fbx-7ede25625141e45b963a6f806ea7a4b6.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
44
assets/Warrior+Animation/left turn.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cjs5o45dby6wq"
|
||||
path="res://.godot/imported/left turn.fbx-c8dee54f4dff7f292b4eb1804655f5cb.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/left turn.fbx"
|
||||
dest_files=["res://.godot/imported/left turn.fbx-c8dee54f4dff7f292b4eb1804655f5cb.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
44
assets/Warrior+Animation/right strafe walking.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://drr52cg7ns8ak"
|
||||
path="res://.godot/imported/right strafe walking.fbx-cc75d3596d7e2bd63ef1120c7d273ed9.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/right strafe walking.fbx"
|
||||
dest_files=["res://.godot/imported/right strafe walking.fbx-cc75d3596d7e2bd63ef1120c7d273ed9.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
44
assets/Warrior+Animation/right strafe.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://ctfgb4y033qak"
|
||||
path="res://.godot/imported/right strafe.fbx-a432556f9ff1799cf9608c02c0ad131a.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/right strafe.fbx"
|
||||
dest_files=["res://.godot/imported/right strafe.fbx-a432556f9ff1799cf9608c02c0ad131a.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
44
assets/Warrior+Animation/right turn 90.fbx.import
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bfg20q58h3ifm"
|
||||
path="res://.godot/imported/right turn 90.fbx-1510f429e9c72d07d6b8f5bd0c243b9d.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/Warrior+Animation/right turn 90.fbx"
|
||||
dest_files=["res://.godot/imported/right turn 90.fbx-1510f429e9c72d07d6b8f5bd0c243b9d.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://dusjhgyamhmgt"
|
||||
path="res://.godot/imported/Stop Walking.fbx-3d24a0466a0c2e2a6357ddcab01c8e7a.scn"
|
||||
uid="uid://clk4yl4dln5dn"
|
||||
path="res://.godot/imported/right turn.fbx-b101bc8730403d08b841e8c3a0b41769.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Stop Walking.fbx"
|
||||
dest_files=["res://.godot/imported/Stop Walking.fbx-3d24a0466a0c2e2a6357ddcab01c8e7a.scn"]
|
||||
source_file="res://assets/Warrior+Animation/right turn.fbx"
|
||||
dest_files=["res://.godot/imported/right turn.fbx-b101bc8730403d08b841e8c3a0b41769.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bv1wgnjxtwty1"
|
||||
path="res://.godot/imported/Walking.fbx-a17a5e9807a8458d066589326a10c768.scn"
|
||||
uid="uid://cjbmdhkq1mvwf"
|
||||
path="res://.godot/imported/running.fbx-ca8e5a7b9cad519a1f30381a5dfeccdf.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Walking.fbx"
|
||||
dest_files=["res://.godot/imported/Walking.fbx-a17a5e9807a8458d066589326a10c768.scn"]
|
||||
source_file="res://assets/Warrior+Animation/running.fbx"
|
||||
dest_files=["res://.godot/imported/running.fbx-ca8e5a7b9cad519a1f30381a5dfeccdf.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://kaq2rgjrxnm4"
|
||||
path="res://.godot/imported/Jumping.fbx-785df5cde6fd6a2d61091c404e44b339.scn"
|
||||
uid="uid://c547ty6nvf11c"
|
||||
path="res://.godot/imported/walking.fbx-574a3fce7d862b76db3ed1e8c594f5b6.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Jumping.fbx"
|
||||
dest_files=["res://.godot/imported/Jumping.fbx-785df5cde6fd6a2d61091c404e44b339.scn"]
|
||||
source_file="res://assets/Warrior+Animation/walking.fbx"
|
||||
dest_files=["res://.godot/imported/walking.fbx-574a3fce7d862b76db3ed1e8c594f5b6.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dm2h4hkvqyoph"
|
||||
path.s3tc="res://.godot/imported/Walking_2.png-ce8a6c3320aac98a8634c6e5cf2b9141.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "16d2ed20714fd916290f48d68a233060"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/animations/Walking_2.png"
|
||||
dest_files=["res://.godot/imported/Walking_2.png-ce8a6c3320aac98a8634c6e5cf2b9141.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=1
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=1
|
||||
roughness/src_normal="res://assets/animations/Walking_2.png"
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
|
Before Width: | Height: | Size: 1.9 MiB |
|
|
@ -1,44 +0,0 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://kb2tcclrpsgp"
|
||||
path.s3tc="res://.godot/imported/warrior_0.png-922555626e5523f977897d33a0262592.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "a1dc5c32f83d0182b6af19d596014cce"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/warrior_0.png"
|
||||
dest_files=["res://.godot/imported/warrior_0.png-922555626e5523f977897d33a0262592.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
|
Before Width: | Height: | Size: 1.2 MiB |
|
|
@ -1,43 +0,0 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dnlkuf4pjgbht"
|
||||
path="res://.godot/imported/warrior_1.png-aafdc73fc8d6c19365ff4d442a16edcf.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4201468097ebb1a15961ee25c2d64310"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/warrior_1.png"
|
||||
dest_files=["res://.godot/imported/warrior_1.png-aafdc73fc8d6c19365ff4d442a16edcf.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
|
Before Width: | Height: | Size: 1.5 MiB |
|
|
@ -1,32 +1,76 @@
|
|||
# CameraPivot.gd
|
||||
# Steuert die Third-Person-Kamera: Maussteuerung (RMB), Zoom per Mausrad
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Third-Person Kamerasystem – Kind-Node des Spielers
|
||||
#
|
||||
# Kamera-Modi:
|
||||
# • Kein Ziel, LMB gehalten → Kamera orbitet um den Spieler (world_yaw ändert sich,
|
||||
# Spielerrotation bleibt)
|
||||
# • Kein Ziel, RMB gehalten → Spieler + Kamera drehen sich gemeinsam
|
||||
# • Ziel markiert → Soft Lock-On: Spieler dreht sich smooth zum Ziel,
|
||||
# Kamera bleibt direkt dahinter
|
||||
# • Mausrad → Zoom (min_zoom … max_zoom)
|
||||
#
|
||||
# world_yaw: absolute Weltausrichtung der Kamera in Grad (Y-Achse).
|
||||
# Unabhängig von player.rotation.y → verhindert Feedback-Loop bei Souls-Rotation.
|
||||
# camera_pivot.rotation.y wird in _process() immer als (world_yaw - player.rotation.y)
|
||||
# gesetzt, sodass die Kamera in World-Space stabil bleibt.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
extends Node3D
|
||||
|
||||
@export var sensitivity = 0.3 # Mausempfindlichkeit
|
||||
@export var min_pitch = -40.0 # Maximale Neigung nach unten
|
||||
@export var max_pitch = 20.0 # Maximale Neigung nach oben
|
||||
@export var min_zoom = 5.0 # Minimale Kameraentfernung
|
||||
@export var max_zoom = 20.0 # Maximale Kameraentfernung
|
||||
@export var zoom_speed = 1.0 # Zoom-Geschwindigkeit pro Mausrad-Schritt
|
||||
@export var sensitivity = 0.3
|
||||
@export var min_pitch = -40.0
|
||||
@export var max_pitch = 20.0
|
||||
@export var min_zoom = 5.0
|
||||
@export var max_zoom = 20.0
|
||||
@export var zoom_speed = 1.0
|
||||
@export var lock_on_speed = 5.0
|
||||
|
||||
var pitch = 0.0
|
||||
var pitch: float = 0.0
|
||||
var world_yaw: float = 0.0 # Absolute Weltausrichtung der Kamera (unabhängig von Spielerrotation)
|
||||
|
||||
@onready var camera = $Camera3D
|
||||
|
||||
func _ready():
|
||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||
world_yaw = get_parent().rotation.y
|
||||
|
||||
func _input(event):
|
||||
# RMB gehalten: Kamera drehen
|
||||
if event is InputEventMouseMotion and Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
|
||||
get_parent().rotate_y(deg_to_rad(-event.relative.x * sensitivity))
|
||||
var player = get_parent()
|
||||
var has_target = player.target != null and is_instance_valid(player.target)
|
||||
|
||||
if event is InputEventMouseMotion:
|
||||
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and not has_target:
|
||||
# LMB: nur Kamera dreht sich, Spieler bleibt
|
||||
world_yaw -= deg_to_rad(event.relative.x * sensitivity)
|
||||
pitch -= event.relative.y * sensitivity
|
||||
pitch = clamp(pitch, min_pitch, max_pitch)
|
||||
rotation_degrees.x = pitch
|
||||
elif Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT) and not has_target:
|
||||
# RMB: Spieler + Kamera drehen sich gemeinsam
|
||||
var delta_yaw = deg_to_rad(-event.relative.x * sensitivity)
|
||||
world_yaw += delta_yaw
|
||||
player.rotation.y += delta_yaw
|
||||
pitch -= event.relative.y * sensitivity
|
||||
pitch = clamp(pitch, min_pitch, max_pitch)
|
||||
rotation_degrees.x = pitch
|
||||
|
||||
# Mausrad: Zoom
|
||||
if event is InputEventMouseButton:
|
||||
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
||||
camera.position.z = clamp(camera.position.z + zoom_speed, min_zoom, max_zoom)
|
||||
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||||
camera.position.z = clamp(camera.position.z - zoom_speed, min_zoom, max_zoom)
|
||||
|
||||
func _process(delta):
|
||||
var player = get_parent()
|
||||
|
||||
if player.target != null and is_instance_valid(player.target) and not player.is_rolling:
|
||||
# Soft Lock-On: Spieler dreht sich zum Ziel, Kamera folgt direkt dahinter
|
||||
var to_target = player.target.global_position - player.global_position
|
||||
to_target.y = 0
|
||||
if to_target.length() > 0.1:
|
||||
var target_angle = atan2(-to_target.x, -to_target.z)
|
||||
player.rotation.y = lerp_angle(player.rotation.y, target_angle, delta * lock_on_speed)
|
||||
world_yaw = player.rotation.y
|
||||
|
||||
# Lokale Rotation so setzen dass Kamera immer auf world_yaw zeigt
|
||||
rotation.y = world_yaw - player.rotation.y
|
||||
|
|
|
|||
470
enemy.gd
|
|
@ -1,319 +1,205 @@
|
|||
# Enemy.gd
|
||||
# Steuert den Gegner: KI-Bewegung zum Spieler, Angriff, HP, Zielanzeige
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Gegner-KI – State Machine mit NavigationAgent3D
|
||||
#
|
||||
# Zustände:
|
||||
# IDLE → wartet bis Spieler in detection_range kommt
|
||||
# CHASING → läuft via NavMesh zum Spieler
|
||||
# ATTACKING → steht, dreht sich zum Spieler, greift in attack_speed-Intervallen an
|
||||
# DEAD → Kollision deaktiviert, Node wird nach kurzer Verzögerung entfernt
|
||||
#
|
||||
# Signale:
|
||||
# enemy_died(spawn_position, xp_reward) → World.gd → Respawn + XP
|
||||
# enemy_dropped_loot(loot, world_pos) → World.gd → Spieler → LootWindow
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
extends CharacterBody3D
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# SIGNALE
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
signal enemy_died(spawn_position: Vector3, xp_reward: int)
|
||||
signal enemy_dropped_loot(loot: Dictionary, world_position: Vector3)
|
||||
signal enemy_dropped_loot(loot: Dictionary, world_pos: Vector3)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# STATS
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
@export var max_hp: int = 50
|
||||
@export var min_damage: int = 3
|
||||
@export var max_damage: int = 7
|
||||
@export var attack_range: float = 2.0
|
||||
@export var attack_speed: float = 2.0 # Sekunden zwischen Angriffen
|
||||
@export var move_speed: float = 3.0
|
||||
@export var xp_reward: int = 20
|
||||
@export var detection_range: float = 15.0
|
||||
@export var loot_table: LootTable = null
|
||||
|
||||
var current_hp: int
|
||||
var target = null # Spieler
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ZUSTAND
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
enum State { IDLE, CHASING, ATTACKING, DEAD }
|
||||
var state: State = State.IDLE
|
||||
|
||||
var attack_cooldown: float = 0.0
|
||||
var is_dead: bool = false
|
||||
|
||||
const SPEED = 3.0
|
||||
const PATROL_SPEED = 1.5
|
||||
const GRAVITY = 9.8
|
||||
const ATTACK_RANGE = 1.5
|
||||
const ATTACK_COOLDOWN = 2.0
|
||||
const AGGRO_RANGE = 8.0 # Entfernung ab der der Gegner angreift
|
||||
const PATROL_RADIUS = 5.0 # Radius um Spawn-Position für Patrol
|
||||
const PATROL_WAIT_TIME = 2.0 # Wartezeit am Patrol-Punkt
|
||||
|
||||
# Level-Differenz Konstanten
|
||||
const LEVEL_DIFF_DAMAGE_MOD = 0.1 # 10% mehr/weniger Schaden pro Level-Differenz
|
||||
const MAX_LEVEL_DIFF_MOD = 0.5 # Maximal 50% Modifikation
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# NODE-REFERENZEN
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
enum State { PATROL, CHASE, ATTACK }
|
||||
@onready var nav_agent: NavigationAgent3D = $NavigationAgent3D
|
||||
@onready var health_label: Label3D = $HealthDisplay/Label3D
|
||||
|
||||
# Stats-System
|
||||
@export var level: int = 1
|
||||
@export var base_strength: int = 8
|
||||
@export var base_stamina: int = 10
|
||||
@export var base_armor: int = 5 # Rüstung reduziert Nahkampfschaden
|
||||
|
||||
# Berechnete Stats
|
||||
var strength: int = 8
|
||||
var stamina: int = 10
|
||||
var armor: int = 5
|
||||
var max_hp: int = 100
|
||||
var current_hp: int = 100
|
||||
var attack_damage: int = 5
|
||||
|
||||
# XP-Belohnung (skaliert mit Level)
|
||||
var xp_reward: int = 25
|
||||
|
||||
# Loot-System
|
||||
@export var loot_table: LootTable
|
||||
|
||||
var target = null # Spieler-Referenz (wird von World gesetzt)
|
||||
var can_attack = true
|
||||
var spawn_position: Vector3 # Ursprüngliche Spawn-Position
|
||||
var current_state = State.PATROL
|
||||
var patrol_target: Vector3 # Aktuelles Patrol-Ziel
|
||||
var is_waiting = false # Ob Gegner am Patrol-Punkt wartet
|
||||
|
||||
# Animation System
|
||||
const ANIMATION_FILES = {
|
||||
"walk": "res://assets/animations/Walking.fbx",
|
||||
"autoattack": "res://assets/animations/Autoattack.fbx",
|
||||
"die": "res://assets/animations/Dying Backwards.fbx",
|
||||
"idle": "res://assets/animations/Idle.fbx",
|
||||
}
|
||||
const LOOP_ANIMATIONS = ["walk", "idle"]
|
||||
var anim_player: AnimationPlayer = null
|
||||
var current_anim: String = ""
|
||||
|
||||
@onready var health_label = $HealthLabel
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# READY
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
func _ready():
|
||||
_calculate_stats()
|
||||
current_hp = max_hp
|
||||
health_label.visible = false
|
||||
_update_label()
|
||||
spawn_position = global_position
|
||||
_pick_new_patrol_target()
|
||||
_setup_animations()
|
||||
|
||||
# Animationen laden
|
||||
func _setup_animations():
|
||||
var model = get_node_or_null("EnemyModel")
|
||||
if model == null:
|
||||
return
|
||||
anim_player = _find_node_by_class(model, "AnimationPlayer")
|
||||
if anim_player == null:
|
||||
anim_player = AnimationPlayer.new()
|
||||
anim_player.name = "AnimationPlayer"
|
||||
model.add_child(anim_player)
|
||||
var lib: AnimationLibrary
|
||||
if anim_player.has_animation_library(""):
|
||||
lib = anim_player.get_animation_library("")
|
||||
else:
|
||||
lib = AnimationLibrary.new()
|
||||
anim_player.add_animation_library("", lib)
|
||||
for anim_id in ANIMATION_FILES:
|
||||
var scene = load(ANIMATION_FILES[anim_id]) as PackedScene
|
||||
if scene == null:
|
||||
continue
|
||||
var instance = scene.instantiate()
|
||||
var source_ap = _find_node_by_class(instance, "AnimationPlayer")
|
||||
if source_ap:
|
||||
var names = source_ap.get_animation_list()
|
||||
if names.size() > 0:
|
||||
var anim = source_ap.get_animation(names[0])
|
||||
if anim_id in ["walk", "idle"]:
|
||||
anim.loop_mode = Animation.LOOP_LINEAR
|
||||
if lib.has_animation(anim_id):
|
||||
lib.remove_animation(anim_id)
|
||||
lib.add_animation(anim_id, anim)
|
||||
instance.queue_free()
|
||||
|
||||
func _find_node_by_class(node: Node, class_name_str: String) -> Node:
|
||||
for child in node.get_children():
|
||||
if child.get_class() == class_name_str:
|
||||
return child
|
||||
var result = _find_node_by_class(child, class_name_str)
|
||||
if result:
|
||||
return result
|
||||
return null
|
||||
|
||||
func _play_anim(anim_name: String):
|
||||
if anim_player == null:
|
||||
return
|
||||
# Wenn Animation ausgelaufen ist, zurücksetzen
|
||||
if not anim_player.is_playing():
|
||||
current_anim = ""
|
||||
if anim_name != current_anim:
|
||||
current_anim = anim_name
|
||||
if anim_player.has_animation(anim_name):
|
||||
anim_player.play(anim_name)
|
||||
else:
|
||||
anim_player.stop()
|
||||
|
||||
# Stats basierend auf Level berechnen
|
||||
func _calculate_stats():
|
||||
var levels_gained = level - 1
|
||||
strength = base_strength + levels_gained * 2
|
||||
stamina = base_stamina + levels_gained * 3
|
||||
armor = base_armor + levels_gained * 2
|
||||
|
||||
# HP = Stamina * 10
|
||||
max_hp = stamina * 10
|
||||
# Schaden = Stärke / 2
|
||||
attack_damage = int(strength * 0.5) + 2
|
||||
# XP = 25 * Level
|
||||
xp_reward = 25 * level
|
||||
|
||||
print("Enemy Stats (Lv", level, ") - STR:", strength, " STA:", stamina, " ARM:", armor, " HP:", max_hp, " DMG:", attack_damage)
|
||||
|
||||
# Schaden mit Rüstung und Level-Differenz berechnen
|
||||
func calculate_incoming_damage(raw_damage: int, attacker_level: int, is_melee: bool) -> int:
|
||||
var damage = float(raw_damage)
|
||||
|
||||
# Rüstung reduziert nur Nahkampfschaden
|
||||
if is_melee:
|
||||
# Rüstungsreduktion: armor / (armor + 50) = Prozent Reduktion
|
||||
# Bei 5 Rüstung: 5/55 = ~9% Reduktion
|
||||
# Bei 20 Rüstung: 20/70 = ~29% Reduktion
|
||||
var armor_reduction = float(armor) / (float(armor) + 50.0)
|
||||
damage = damage * (1.0 - armor_reduction)
|
||||
|
||||
# Level-Differenz Modifikator
|
||||
var level_diff = attacker_level - level
|
||||
var level_mod = clamp(level_diff * LEVEL_DIFF_DAMAGE_MOD, -MAX_LEVEL_DIFF_MOD, MAX_LEVEL_DIFF_MOD)
|
||||
damage = damage * (1.0 + level_mod)
|
||||
|
||||
return maxi(1, int(damage)) # Mindestens 1 Schaden
|
||||
|
||||
# HP-Label Text aktualisieren
|
||||
func _update_label():
|
||||
health_label.text = "Lv" + str(level) + " " + str(current_hp) + "/" + str(max_hp)
|
||||
|
||||
# HP-Label anzeigen (wenn Gegner markiert wird)
|
||||
func show_health():
|
||||
health_label.visible = true
|
||||
|
||||
# HP-Label verstecken (wenn Markierung aufgehoben wird)
|
||||
func hide_health():
|
||||
_update_health_display()
|
||||
health_label.visible = false
|
||||
|
||||
# Schaden nehmen und Label aktualisieren
|
||||
func take_damage(amount):
|
||||
current_hp -= amount
|
||||
_update_label()
|
||||
# Aggro bei Schaden — sofort angreifen
|
||||
if current_state == State.PATROL:
|
||||
current_state = State.CHASE
|
||||
is_waiting = false
|
||||
print("Gegner wurde angegriffen und verfolgt den Spieler!")
|
||||
if current_hp <= 0:
|
||||
die()
|
||||
# NavigationAgent konfigurieren
|
||||
nav_agent.path_desired_distance = 0.5
|
||||
nav_agent.target_desired_distance = attack_range * 0.9
|
||||
|
||||
# Schaden mit vollem Schadenssystem (Rüstung, Level-Differenz)
|
||||
func take_damage_from(raw_damage: int, attacker_level: int, is_melee: bool = true):
|
||||
var final_damage = calculate_incoming_damage(raw_damage, attacker_level, is_melee)
|
||||
print("Eingehender Schaden: ", raw_damage, " -> ", final_damage, " (nach Rüstung/Level)")
|
||||
take_damage(final_damage)
|
||||
|
||||
# Gegner aus der Szene entfernen
|
||||
func die():
|
||||
print("Gegner besiegt! +", xp_reward, " XP")
|
||||
# XP an Spieler geben
|
||||
if target and target.has_method("gain_xp"):
|
||||
target.gain_xp(xp_reward)
|
||||
# Loot generieren und droppen
|
||||
_drop_loot()
|
||||
enemy_died.emit(spawn_position, xp_reward)
|
||||
# Death-Animation abspielen, dann entfernen
|
||||
if anim_player and anim_player.has_animation("die"):
|
||||
_play_anim("die")
|
||||
# Kollision deaktivieren damit der Gegner nicht mehr im Weg ist
|
||||
set_physics_process(false)
|
||||
$CollisionShape3D.set_deferred("disabled", true)
|
||||
await anim_player.animation_finished
|
||||
queue_free()
|
||||
|
||||
# Loot generieren basierend auf LootTable
|
||||
func _drop_loot():
|
||||
if loot_table == null:
|
||||
# Standard-Gold-Drop wenn keine LootTable zugewiesen
|
||||
var gold = randi_range(1, 3) * level
|
||||
var loot = {"gold": gold, "items": []}
|
||||
enemy_dropped_loot.emit(loot, global_position)
|
||||
return
|
||||
|
||||
var loot = loot_table.generate_loot()
|
||||
# Gold mit Level skalieren
|
||||
loot["gold"] = loot["gold"] * level
|
||||
enemy_dropped_loot.emit(loot, global_position)
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# PHYSICS PROCESS
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
func _physics_process(delta):
|
||||
if is_dead:
|
||||
return
|
||||
|
||||
# Schwerkraft
|
||||
if not is_on_floor():
|
||||
velocity.y -= GRAVITY * delta
|
||||
|
||||
# Cooldown herunterzählen
|
||||
if attack_cooldown > 0:
|
||||
attack_cooldown -= delta
|
||||
|
||||
# Kein Ziel → Idle
|
||||
if target == null or not is_instance_valid(target):
|
||||
state = State.IDLE
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
move_and_slide()
|
||||
return
|
||||
|
||||
var distance = global_position.distance_to(target.global_position)
|
||||
|
||||
match state:
|
||||
State.IDLE:
|
||||
if distance <= detection_range:
|
||||
state = State.CHASING
|
||||
State.CHASING:
|
||||
if distance <= attack_range:
|
||||
state = State.ATTACKING
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
else:
|
||||
_move_toward_target()
|
||||
State.ATTACKING:
|
||||
if distance > attack_range * 1.5:
|
||||
state = State.CHASING
|
||||
else:
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
_face_target()
|
||||
if attack_cooldown <= 0:
|
||||
_perform_attack()
|
||||
|
||||
move_and_slide()
|
||||
|
||||
func _move_toward_target():
|
||||
if target == null:
|
||||
# Ohne Spieler-Referenz nur patrouillieren
|
||||
_do_patrol()
|
||||
move_and_slide()
|
||||
return
|
||||
|
||||
# Prüfe Distanz zum Spieler für Aggro
|
||||
var distance_to_player = global_position.distance_to(target.global_position)
|
||||
|
||||
# State-Wechsel basierend auf Distanz
|
||||
match current_state:
|
||||
State.PATROL:
|
||||
if distance_to_player <= AGGRO_RANGE:
|
||||
current_state = State.CHASE
|
||||
print("Gegner hat Spieler entdeckt!")
|
||||
else:
|
||||
_do_patrol()
|
||||
State.CHASE:
|
||||
if distance_to_player <= ATTACK_RANGE:
|
||||
current_state = State.ATTACK
|
||||
else:
|
||||
_chase_player()
|
||||
State.ATTACK:
|
||||
if distance_to_player > ATTACK_RANGE:
|
||||
current_state = State.CHASE
|
||||
else:
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
_play_anim("idle") # Idle beim Angriff
|
||||
if can_attack:
|
||||
_attack()
|
||||
|
||||
move_and_slide()
|
||||
|
||||
# Neues Patrol-Ziel in der Nähe der Spawn-Position wählen
|
||||
func _pick_new_patrol_target():
|
||||
var angle = randf() * TAU # Zufälliger Winkel
|
||||
var distance = randf_range(2.0, PATROL_RADIUS)
|
||||
patrol_target = spawn_position + Vector3(cos(angle) * distance, 0, sin(angle) * distance)
|
||||
|
||||
# Patrol-Verhalten: Zufällig herumlaufen
|
||||
func _do_patrol():
|
||||
if is_waiting:
|
||||
nav_agent.target_position = target.global_position
|
||||
if nav_agent.is_navigation_finished():
|
||||
return
|
||||
|
||||
var distance_to_patrol = global_position.distance_to(patrol_target)
|
||||
|
||||
if distance_to_patrol <= 0.5:
|
||||
# Am Ziel angekommen, warten und neues Ziel wählen
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
_play_anim("idle")
|
||||
_wait_at_patrol_point()
|
||||
else:
|
||||
# Zum Patrol-Ziel laufen
|
||||
var direction = (patrol_target - global_position)
|
||||
var next_pos = nav_agent.get_next_path_position()
|
||||
var direction = (next_pos - global_position).normalized()
|
||||
direction.y = 0
|
||||
direction = direction.normalized()
|
||||
velocity.x = direction.x * PATROL_SPEED
|
||||
velocity.z = direction.z * PATROL_SPEED
|
||||
_play_anim("walk")
|
||||
look_at(Vector3(patrol_target.x, global_position.y, patrol_target.z))
|
||||
velocity.x = direction.x * move_speed
|
||||
velocity.z = direction.z * move_speed
|
||||
_face_direction(direction)
|
||||
|
||||
# Am Patrol-Punkt warten
|
||||
func _wait_at_patrol_point():
|
||||
is_waiting = true
|
||||
await get_tree().create_timer(PATROL_WAIT_TIME).timeout
|
||||
is_waiting = false
|
||||
_pick_new_patrol_target()
|
||||
func _face_target():
|
||||
if target == null:
|
||||
return
|
||||
var dir = (target.global_position - global_position)
|
||||
dir.y = 0
|
||||
if dir.length() > 0.01:
|
||||
_face_direction(dir.normalized())
|
||||
|
||||
# Spieler verfolgen
|
||||
func _chase_player():
|
||||
_play_anim("walk")
|
||||
var direction = (target.global_position - global_position)
|
||||
direction.y = 0
|
||||
direction = direction.normalized()
|
||||
velocity.x = direction.x * SPEED
|
||||
velocity.z = direction.z * SPEED
|
||||
look_at(Vector3(target.global_position.x, global_position.y, target.global_position.z))
|
||||
func _face_direction(dir: Vector3):
|
||||
if dir.length() > 0.01:
|
||||
rotation.y = atan2(dir.x, dir.z)
|
||||
|
||||
# Angriff mit Cooldown
|
||||
func _attack():
|
||||
can_attack = false
|
||||
_play_anim("autoattack")
|
||||
# Gegner verwendet auch das Schadenssystem mit Level-Differenz
|
||||
if target.has_method("take_damage_from"):
|
||||
target.take_damage_from(attack_damage, level, true)
|
||||
else:
|
||||
target.take_damage(attack_damage)
|
||||
print("Gegner (Lv", level, ") greift an: ", attack_damage, " Schaden")
|
||||
await get_tree().create_timer(ATTACK_COOLDOWN).timeout
|
||||
can_attack = true
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# KAMPF
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
func _perform_attack():
|
||||
if target == null or not is_instance_valid(target):
|
||||
return
|
||||
var damage = randi_range(min_damage, max_damage)
|
||||
target.take_damage(damage)
|
||||
attack_cooldown = attack_speed
|
||||
print(name + " greift an: " + str(damage) + " Schaden")
|
||||
|
||||
func take_damage(amount: int):
|
||||
if is_dead:
|
||||
return
|
||||
current_hp = clamp(current_hp - amount, 0, max_hp)
|
||||
_update_health_display()
|
||||
if current_hp <= 0:
|
||||
_die()
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# HP-ANZEIGE
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
func show_health():
|
||||
if health_label:
|
||||
health_label.visible = true
|
||||
|
||||
func hide_health():
|
||||
if health_label:
|
||||
health_label.visible = false
|
||||
|
||||
func _update_health_display():
|
||||
if health_label:
|
||||
health_label.text = str(current_hp) + " / " + str(max_hp)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# TOD & LOOT
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
func _die():
|
||||
is_dead = true
|
||||
state = State.DEAD
|
||||
velocity = Vector3.ZERO
|
||||
print(name + " gestorben!")
|
||||
|
||||
# Loot generieren
|
||||
if loot_table != null:
|
||||
var loot = loot_table.generate_loot()
|
||||
enemy_dropped_loot.emit(loot, global_position)
|
||||
|
||||
# XP und Respawn-Signal
|
||||
enemy_died.emit(global_position, xp_reward)
|
||||
|
||||
# Kollision deaktivieren und Node entfernen
|
||||
set_deferred("collision_layer", 0)
|
||||
set_deferred("collision_mask", 0)
|
||||
await get_tree().create_timer(1.5).timeout
|
||||
queue_free()
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://bg5qs3pcfp7p7
|
||||
uid://gaqwoakxyhet
|
||||
|
|
|
|||
42
enemy.tscn
|
|
@ -1,36 +1,26 @@
|
|||
[gd_scene format=3 uid="uid://cvojaeanxugfj"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://da1w523lg7i2b" path="res://assets/models/warrior.fbx" id="1_7k104"]
|
||||
[ext_resource type="Script" uid="uid://bg5qs3pcfp7p7" path="res://enemy.gd" id="2_enemy"]
|
||||
[ext_resource type="Script" path="res://enemy.gd" id="1_enemy"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4gyqm"]
|
||||
radius = 0.6
|
||||
height = 3.0
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1"]
|
||||
radius = 0.4
|
||||
height = 1.8
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_7k104"]
|
||||
radius = 0.6
|
||||
height = 3.0
|
||||
[node name="Enemy" type="CharacterBody3D"]
|
||||
script = ExtResource("1_enemy")
|
||||
|
||||
[node name="Enemy" type="CharacterBody3D" unique_id=332011146]
|
||||
script = ExtResource("2_enemy")
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0)
|
||||
shape = SubResource("CapsuleShape3D_1")
|
||||
|
||||
[node name="EnemyModel" parent="." unique_id=846574684 instance=ExtResource("1_7k104")]
|
||||
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0, 0)
|
||||
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1323028920]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.35, 0)
|
||||
shape = SubResource("CapsuleShape3D_4gyqm")
|
||||
[node name="HealthDisplay" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.2, 0)
|
||||
|
||||
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="." unique_id=457987844]
|
||||
|
||||
[node name="Area3D" type="Area3D" parent="." unique_id=1689838821]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Area3D" unique_id=116643275]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.35, 0)
|
||||
shape = SubResource("CapsuleShape3D_7k104")
|
||||
|
||||
[node name="HealthLabel" type="Label3D" parent="." unique_id=1251847350]
|
||||
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 3.5, 0)
|
||||
visible = false
|
||||
[node name="Label3D" type="Label3D" parent="HealthDisplay"]
|
||||
pixel_size = 0.01
|
||||
billboard = 3
|
||||
text = "50 / 50"
|
||||
font_size = 64
|
||||
outline_size = 8
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
uid://b6n25e5fh82ra
|
||||
uid://cwe8o6mk0hsi4
|
||||
|
|
|
|||
52
player.tscn
|
|
@ -1,41 +1,31 @@
|
|||
[gd_scene format=3 uid="uid://dniyuebl8yhtv"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b6n25e5fh82ra" path="res://player.gd" id="1_4flbx"]
|
||||
[ext_resource type="PackedScene" uid="uid://da1w523lg7i2b" path="res://assets/models/warrior.fbx" id="2_hqtel"]
|
||||
[ext_resource type="Script" uid="uid://bwtwon54po4w3" path="res://camera_pivot.gd" id="2_onrkg"]
|
||||
[ext_resource type="PackedScene" uid="uid://bej3excyoxrdh" path="res://hud.tscn" id="4_hqtel"]
|
||||
[ext_resource type="PackedScene" uid="uid://character_panel" path="res://character_panel.tscn" id="5_char_panel"]
|
||||
[ext_resource type="PackedScene" uid="uid://inventory_panel" path="res://inventory_panel.tscn" id="6_inv_panel"]
|
||||
[ext_resource type="PackedScene" uid="uid://loot_window" path="res://loot_window.tscn" id="7_loot_win"]
|
||||
[ext_resource type="PackedScene" uid="uid://skill_panel" path="res://skill_panel.tscn" id="8_skill_panel"]
|
||||
[ext_resource type="Script" uid="uid://cwe8o6mk0hsi4" path="res://player.gd" id="1_player"]
|
||||
[ext_resource type="Script" uid="uid://bwtwon54po4w3" path="res://camera_pivot.gd" id="2_campivot"]
|
||||
[ext_resource type="PackedScene" uid="uid://bej3excyoxrdh" path="res://hud.tscn" id="3_hud"]
|
||||
[ext_resource type="PackedScene" uid="uid://daeym1tdcnhhd" path="res://assets/Warrior+Animation/castle_guard_01.fbx" id="4_model"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4flbx"]
|
||||
radius = 0.6
|
||||
height = 3.0
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1"]
|
||||
radius = 0.4
|
||||
height = 1.8
|
||||
|
||||
[node name="CharacterBody3D" type="CharacterBody3D" unique_id=937297102]
|
||||
transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0)
|
||||
script = ExtResource("1_4flbx")
|
||||
[node name="Player" type="CharacterBody3D" unique_id=1565111917]
|
||||
script = ExtResource("1_player")
|
||||
|
||||
[node name="PlayerModel" parent="." unique_id=926968795 instance=ExtResource("2_hqtel")]
|
||||
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0, 0)
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=481888033]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9, 0)
|
||||
shape = SubResource("CapsuleShape3D_1")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1359412306]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.35, 0)
|
||||
shape = SubResource("CapsuleShape3D_4flbx")
|
||||
[node name="Model" type="Node3D" parent="." unique_id=297754421]
|
||||
|
||||
[node name="CameraPivot" type="Node3D" parent="." unique_id=638440275]
|
||||
script = ExtResource("2_onrkg")
|
||||
[node name="castle_guard_01" parent="Model" unique_id=1352499997 instance=ExtResource("4_model")]
|
||||
transform = Transform3D(-1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0)
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="CameraPivot" unique_id=1625345908]
|
||||
transform = Transform3D(2, 0, 0, 0, 1.8126155, 0.84523654, 0, -0.84523654, 1.8126155, 0, 5, 5)
|
||||
[node name="CameraPivot" type="Node3D" parent="." unique_id=2063743808]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
|
||||
script = ExtResource("2_campivot")
|
||||
|
||||
[node name="HUD" parent="." unique_id=1901284390 instance=ExtResource("4_hqtel")]
|
||||
[node name="Camera3D" type="Camera3D" parent="CameraPivot" unique_id=1026091049]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 7)
|
||||
|
||||
[node name="CharacterPanel" parent="." instance=ExtResource("5_char_panel")]
|
||||
|
||||
[node name="InventoryPanel" parent="." instance=ExtResource("6_inv_panel")]
|
||||
|
||||
[node name="LootWindow" parent="." instance=ExtResource("7_loot_win")]
|
||||
|
||||
[node name="SkillPanel" parent="." instance=ExtResource("8_skill_panel")]
|
||||
[node name="HUD" parent="." unique_id=904219504 instance=ExtResource("3_hud")]
|
||||
|
|
|
|||
|
|
@ -112,6 +112,16 @@ toggle_skills={
|
|||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":80,"key_label":0,"unicode":112,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
roll={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":1,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
walk_toggle={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194330,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[physics]
|
||||
|
||||
|
|
|
|||
58
world.gd
|
|
@ -1,5 +1,14 @@
|
|||
# World.gd
|
||||
# Initialisiert die Spielwelt: weist dem Gegner den Spieler als Ziel zu
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Spielwelt-Controller
|
||||
#
|
||||
# Verantwortlichkeiten:
|
||||
# • Prozeduraler Boden-Shader (Schachbrettmuster, world-space stabil)
|
||||
# • Prozeduraler Himmel (ProceduralSkyMaterial + WorldEnvironment)
|
||||
# • Hauptmenü → Klassenauswahl → Spielinitialisierung
|
||||
# • Startausrüstung je nach Klasse
|
||||
# • Gegner-Setup, Loot-Weiterleitung, Respawn nach Tod
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
extends Node3D
|
||||
|
||||
const ENEMY_SCENE = preload("res://enemy.tscn")
|
||||
|
|
@ -16,13 +25,60 @@ const STARTER_CHEST = preload("res://equipment/leather_chest.tres")
|
|||
const GOBLIN_LOOT = preload("res://loot_tables/goblin_loot.tres")
|
||||
|
||||
@onready var player = $Player
|
||||
@onready var floor_mesh = $Boden/MeshInstance3D
|
||||
|
||||
func _ready():
|
||||
_setup_floor_material()
|
||||
_setup_sky()
|
||||
# Hauptmenü anzeigen
|
||||
var main_menu = MAIN_MENU.instantiate()
|
||||
add_child(main_menu)
|
||||
main_menu.start_game.connect(_on_start_game)
|
||||
|
||||
func _setup_sky():
|
||||
var sky_mat = ProceduralSkyMaterial.new()
|
||||
sky_mat.sky_top_color = Color(0.15, 0.35, 0.75)
|
||||
sky_mat.sky_horizon_color = Color(0.55, 0.75, 1.0)
|
||||
sky_mat.ground_horizon_color = Color(0.35, 0.30, 0.25)
|
||||
sky_mat.ground_bottom_color = Color(0.1, 0.1, 0.1)
|
||||
sky_mat.sun_angle_max = 30.0
|
||||
sky_mat.sun_curve = 0.15
|
||||
|
||||
var sky = Sky.new()
|
||||
sky.sky_material = sky_mat
|
||||
|
||||
var env = Environment.new()
|
||||
env.background_mode = Environment.BG_SKY
|
||||
env.sky = sky
|
||||
env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
||||
env.ambient_light_energy = 0.6
|
||||
env.tonemap_mode = Environment.TONE_MAPPER_FILMIC
|
||||
|
||||
var world_env = WorldEnvironment.new()
|
||||
world_env.environment = env
|
||||
add_child(world_env)
|
||||
|
||||
func _setup_floor_material():
|
||||
var shader = Shader.new()
|
||||
shader.code = """
|
||||
shader_type spatial;
|
||||
uniform float grid_size : hint_range(0.5, 10.0) = 2.0;
|
||||
uniform vec4 color_a : source_color = vec4(0.22, 0.22, 0.22, 1.0);
|
||||
uniform vec4 color_b : source_color = vec4(0.38, 0.38, 0.38, 1.0);
|
||||
|
||||
void fragment() {
|
||||
vec3 world_pos = (INV_VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
vec2 cell = floor(world_pos.xz / grid_size);
|
||||
float checker = mod(cell.x + cell.y, 2.0);
|
||||
ALBEDO = mix(color_a.rgb, color_b.rgb, checker);
|
||||
ROUGHNESS = 0.85;
|
||||
METALLIC = 0.0;
|
||||
}
|
||||
"""
|
||||
var mat = ShaderMaterial.new()
|
||||
mat.shader = shader
|
||||
floor_mesh.material_override = mat
|
||||
|
||||
# Nach Hauptmenü: Klassenauswahl anzeigen
|
||||
func _on_start_game():
|
||||
var menu = CLASS_SELECTION_MENU.instantiate()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
[ext_resource type="PackedScene" uid="uid://dniyuebl8yhtv" path="res://player.tscn" id="1_f3sb7"]
|
||||
[ext_resource type="Script" uid="uid://cx56h588mfsk0" path="res://world.gd" id="1_tlwt5"]
|
||||
[ext_resource type="PackedScene" uid="uid://cvojaeanxugfj" path="res://enemy.tscn" id="2_fj7yv"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_fj7yv"]
|
||||
size = Vector3(200, 0.5, 200)
|
||||
|
|
@ -21,16 +20,14 @@ script = ExtResource("1_tlwt5")
|
|||
shape = SubResource("BoxShape3D_fj7yv")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Boden" unique_id=1214783061]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2)
|
||||
mesh = SubResource("BoxMesh_tlwt5")
|
||||
|
||||
[node name="Player" parent="." unique_id=937297102 instance=ExtResource("1_f3sb7")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.3, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1394887598]
|
||||
transform = Transform3D(-45, 0, 0, 0, -45, 0, 0, 0, -45, 0, 0, 0)
|
||||
|
||||
[node name="Enemy" parent="." unique_id=332011146 instance=ExtResource("2_fj7yv")]
|
||||
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0.3, -7.038)
|
||||
|
||||
[node name="NavigationRegion3D" type="NavigationRegion3D" parent="." unique_id=827244005]
|
||||
navigation_mesh = SubResource("NavigationMesh_fj7yv")
|
||||
|
|
|
|||