[[https://github.com/godotengine/godot|Godot]] ist eine kostenlose Open Source [[gameengine|Game Engine]] unter [[https://github.com/godotengine/godot/blob/master/LICENSE.txt|MIT Lizenz]]. [[https://www.youtube.com/watch?v=9i6uxm_ioG0|Clipping]] =====Bodies===== In Godot gibt es verschiedene Arten von Körpern (Bodies), die verwendet werden können, um die physikalischen Eigenschaften von Objekten in einer Spielwelt zu simulieren. Hier sind die wichtigsten Körperarten in Godot: * StaticBody: Ein StaticBody ist ein starrer Körper, der von der Physik-Engine nicht beeinflusst wird. Er bleibt an einem festen Ort und kann nicht bewegt werden. Er wird oft für statische Elemente wie Wände, Böden und Plattformen verwendet. * RigidBody: Ein RigidBody ist ein dynamischer Körper, der von der Physik-Engine beeinflusst wird. Er reagiert auf Kräfte wie Schwerkraft und Kollisionen mit anderen Körpern. RigidBody-Objekte sind in der Lage, sich zu bewegen und können aufgrund von Kräften beschleunigen. * KinematicBody: Wie bereits erwähnt, ist ein KinematicBody ein Körper, der Kollisionen erkennt, aber von der Physik-Engine nicht beeinflusst wird. Er kann mithilfe von Code oder Skripten gesteuert werden und ist ideal für Charaktere oder Objekte, die spezielle Bewegungslogik benötigen. * Area: Ein Area ist ein unsichtbarer Körper, der nur Kollisionen registriert, aber keine physische Interaktion mit anderen Körpern hat. Er wird oft für Trigger-Bereiche oder Bereiche verwendet, die spezielle Ereignisse auslösen sollen, wenn andere Körper sie betreten oder verlassen. * MultiMeshInstance: Dieser Körper dient dazu, mehrere Instanzen eines Meshes zu erstellen, die individuell positioniert, skaliert und rotiert werden können. Er wird häufig in Szenen mit wiederholenden Elementen verwendet. * VehicleBody: Ein spezialisierter Körper, der für die Simulation von Fahrzeugen mit Rädern entwickelt wurde. * Distanz-Gelenk (DistanceJoint2D, DistanceJoint3D): Ermöglicht es, zwei Punkte in einem bestimmten Abstand voneinander zu halten. * Gelenk mit eingeschränktem Abstand (DampedSpringJoint2D, DampedSpringJoint3D): Ermöglicht es, zwei Punkte in einem bestimmten Abstand zu halten, mit der Möglichkeit, Dämpfung hinzuzufügen. * Gelenk mit eingeschränktem Winkel (RevoluteJoint2D, PinJoint, HingeJoint): Ermöglicht die Rotation von zwei Körpern um einen gemeinsamen Punkt (wie ein Türscharnier). * Schiebergelenk (SliderJoint2D, SliderJoint3D): Ermöglicht die lineare Bewegung zwischen zwei Körpern entlang einer bestimmten Achse. * Gelenk mit konstanter Richtung (GrooveJoint2D, GrooveJoint3D): Begrenzt die Bewegung eines Körpers auf eine bestimmte Richtung, ähnlich wie eine Schiene. * Können-Gelenk (ConeTwistJoint): Erlaubt komplexe Einschränkungen von Drehungen, insbesondere in 3D-Szenen. * Gelenk zur Vermeidung von Kollisionen (GearJoint2D, GearJoint3D): Verbindet zwei Gelenke miteinander und ermöglicht komplexe Interaktionen. * PrismaticJoint2D: Ermöglicht eine eingeschränkte lineare Bewegung zwischen zwei Körpern. =====Mesh===== extends Node func _ready(): _ready1() func _ready1(): var myMesh = MeshInstance3D.new() myMesh.mesh = PlaneMesh.new() var size = Vector3(15.0, 1, 15.0) myMesh.scale = size myMesh.create_convex_collision(true, true) add_child(myMesh) var camera = Camera3D.new() camera.position = Vector3(0, 2, 2) add_child(camera) func _ready2(): var myMesh = MeshInstance3D.new() var custom_plane = create_custom_plane(Vector3(15, 1, 15), 2, 2) # Größe 5x5, 2x2 Segmente myMesh.mesh = custom_plane myMesh.scale = Vector3(15, 1, 15) myMesh.rotation_degrees = Vector3(180, 0, 0) add_child(myMesh) var camera = Camera3D.new() camera.position = Vector3(0, 2, 2) add_child(camera) func create_custom_plane(size: Vector3, seg_x: int, seg_y: int) -> ArrayMesh: var array_mesh = ArrayMesh.new() var vertices = PackedVector3Array() var indices = PackedInt32Array() var uvs = PackedVector2Array() var half_width = size.x / 2 var half_height = size.z / 2 # Vertices und UVs erstellen for y in range(seg_y + 1): for x in range(seg_x + 1): var vx = lerp(-half_width, half_width, x / seg_x) var vy = 0 var vz = lerp(-half_height, half_height, y / seg_y) vertices.append(Vector3(vx, vy, vz)) uvs.append(Vector2(x / seg_x, y / seg_y)) # Indices für die Dreiecke erstellen for y in range(seg_y): for x in range(seg_x): var i = y * (seg_x + 1) + x indices.append(i) indices.append(i + seg_x + 1) indices.append(i + seg_x + 2) indices.append(i) indices.append(i + seg_x + 2) indices.append(i + 1) # Mesh-Daten vorbereiten var arrays = [] arrays.resize(Mesh.ARRAY_MAX) arrays[Mesh.ARRAY_VERTEX] = vertices arrays[Mesh.ARRAY_INDEX] = indices arrays[Mesh.ARRAY_TEX_UV] = uvs # ArrayMesh hinzufügen array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) return array_mesh =====Button===== {{godot_button_pressed_event.png}} func _on_button_pressed(extra_arg_0, extra_arg_1): print(extra_arg_0 + extra_arg_1) =====GDScript===== ====Instantiation==== # Lade die Szene var scene = preload("res://capsule.tscn") # Instanziere die Szene var instance = scene.instantiate() # Füge die instanziierte Szene als Kind zum aktuellen Node hinzu add_child(instance) ====Movement==== Move/rotate a Node3D extends Node3D # Geschwindigkeit, mit der das Objekt bewegt wird var speed = 5.0 func _process(delta): # Bewegung auf der X-Achse # position.x += speed * delta # oder translate(Vector3(speed * delta, 0, 0)) # Bewegung auf der Z-Achse # position.z += speed * delta # oder translate(Vector3(0, 0, speed * delta)) # Rotation um die X-Achse # rotation.x += 10 * deltaTime # oder rotate(Vector3(1, 0, 0), deg_to_rad(abs(speed) * 10 * delta)) # Rotation um die Z-Achse #rotation.z += 10 * deltaTime # oder rotate(Vector3(0, 0, 1), deg_to_rad(abs(speed) * 10 * delta)) # Umkehrung der Bewegungsrichtung, wenn die X-Position eine bestimmte Grenze erreicht if position.x > 5.0 or position.x < -5.0: speed = -speed Move/rotate a MeshInstance3D extends MeshInstance3D # Geschwindigkeit, mit der das Objekt bewegt wird var speed = 5.0 func _process(delta): # Bewegung auf der X-Achse # position.x += speed * delta # oder translate(Vector3(speed * delta, 0, 0)) # Bewegung auf der Z-Achse # position.z += speed * delta # oder translate(Vector3(0, 0, speed * delta)) # Rotation um die X-Achse # rotation.x += 10 * deltaTime # oder rotate(Vector3(1, 0, 0), deg_to_rad(abs(speed) * 10 * delta)) # Rotation um die Z-Achse #rotation.z += 10 * deltaTime # oder rotate(Vector3(0, 0, 1), deg_to_rad(abs(speed) * 10 * delta)) # Umkehrung der Bewegungsrichtung, wenn die X-Position eine bestimmte Grenze erreicht if position.x > 5.0 or position.x < -5.0: speed = -speed ====Raycast2D==== extends Raycast2D var raycast_length = 10.0 func _ready(): raycast.target_position = Vector2(0, raycast_length) raycast.enabled = true func _physics_process(delta): if is_colliding(): print("Der Raycast kollidiert.") else: print("Der Raycast kollidiert nicht.") extends CharacterBody2D var raycast_length = 10.0 var raycast: RayCast2D func _ready(): raycast = RayCast2D.new() raycast.target_position = Vector2(0, raycast_length) raycast.enabled = true add_child(raycast) func _process(delta): if raycast.is_colliding(): print("Der Raycast kollidiert.") else: print("Der Raycast kollidiert nicht.") ====Raycast3D==== Der Raycast3D-Node muss zuerst im Scenetree hinzugrfügt werden. Z.B als Child der Camera. extends RayCast3D # Called when the node enters the scene tree for the first time. func _ready(): pass # Replace with function body. # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): # get_collision_point() # get_collider() if is_colliding(): var length = (get_parent().global_position - get_collider().global_position).length() print("colliding with ", get_collider(), " at position ", get_collider().global_position, " at a dist of ", length) func shoot(): var space = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.create( global_position, global_position - global_transform.basis.z * 2 ) var collision = space.intersect_ray(query) if collision: print(collision.collider.name) =====Signals===== func _on_body_entered(body): print("on_body_entered:", body.name) func _on_body_exited(body): print("on_body_exited:", body.name) Um Funktionen im Script mit Signalen aus der Engine zu verknüpfen, Doppelklick auf das entsprechende Signal. {{signals.png}} =====Colission===== 3D Kolissionen func _process(delta): var space_state = get_world_3d().direct_space_state var transform = global_transform var query = PhysicsShapeQueryParameters3D.new() query.shape = collision_shape.shape query.transform = transform var results = space_state.intersect_shape(query) for result in results: if result.collider: print("Kollision mit:", result.collider.name) =====Area===== Signale setzen! extends Area2D # or # extends Area3D # Wenn 2 Areas überlappen func _on_area_entered(area): print("on_area_entered:", area.name) # Wenn 2 Areas nicht mehr überlappen func _on_area_exited(area): print("on_area_exited:", area.name) # Wenn eine Area und ein Node überlappen func _on_body_entered(body): print("on_body_entered:", body.name) # Wenn eine Area und ein Node nicht mehr überlappen func _on_body_exited(body): print("on_body_exited:", body.name) # Teste ob ein Player in der Box ist func is_player_in_box(): var bodies = get_overlapping_bodies() for body in bodies: if body.name == "CharacterBody3D": return true return false func _process(delta): if is_player_in_box(): print("player is in the box") =====CameraController===== extends Camera3D @export var move_speed: float = 10.0 @export var mouse_sensitivity: float = 0.1 var rotation_x: float = 0.0 var rotation_y: float = 0.0 func _ready(): Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) func _process(delta: float): handle_movement(delta) func _input(event: InputEvent): if event is InputEventMouseMotion: handle_mouse_rotation(event.relative) if Input.is_action_just_pressed("Escape"): if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) else: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) func handle_movement(delta: float): var velocity = Vector3.ZERO if Input.is_action_pressed("move_forward"): velocity -= transform.basis.z if Input.is_action_pressed("move_backward"): velocity += transform.basis.z if Input.is_action_pressed("move_left"): velocity -= transform.basis.x if Input.is_action_pressed("move_right"): velocity += transform.basis.x if velocity != Vector3.ZERO: velocity = velocity.normalized() * move_speed * delta global_transform.origin += velocity func handle_mouse_rotation(mouse_motion: Vector2): rotation_y -= mouse_motion.x * mouse_sensitivity rotation_x -= mouse_motion.y * mouse_sensitivity rotation_x = clamp(rotation_x, -90.0, 90.0) rotation_degrees = Vector3(rotation_x, rotation_y, 0.0) extends Node3D @export var move_speed: float = 10.0 @export var mouse_sensitivity: float = 0.1 var rotation_x: float = 0.0 var rotation_y: float = 0.0 var camera: Camera3D = null func _ready(): camera = Camera3D.new() camera.current = true add_child(camera) Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) func _process(delta: float): if camera: handle_movement(delta) func _input(event: InputEvent): if event is InputEventMouseMotion: handle_mouse_rotation(event.relative) if Input.is_action_just_pressed("Escape"): if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) else: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) func handle_movement(delta: float): var velocity = Vector3.ZERO if Input.is_action_pressed("move_forward"): velocity -= camera.transform.basis.z if Input.is_action_pressed("move_backward"): velocity += camera.transform.basis.z if Input.is_action_pressed("move_left"): velocity -= camera.transform.basis.x if Input.is_action_pressed("move_right"): velocity += camera.transform.basis.x if velocity != Vector3.ZERO: velocity = velocity.normalized() * move_speed * delta camera.global_transform.origin += velocity func handle_mouse_rotation(mouse_motion: Vector2): if not camera: return rotation_y -= mouse_motion.x * mouse_sensitivity rotation_x -= mouse_motion.y * mouse_sensitivity rotation_x = clamp(rotation_x, -90.0, 90.0) camera.rotation_degrees = Vector3(rotation_x, rotation_y, 0.0) =====CharacterBody===== ====CharacterBody2D==== extends CharacterBody2D @onready var collision_shape = $CollisionShape2D var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") var speed = 50 func _physics_process(delta): var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") if direction: velocity = direction * speed else: velocity.x = move_toward(velocity.x, 0, speed) velocity.y = move_toward(velocity.y, 0, speed) velocity.y += gravity * delta move_and_slide() #move_and_collide(velocity) #check_collision() var collisions = get_slide_collision_count() if collisions: for collision in collisions: print(get_slide_collision(collision)) if $RayCast2D.is_colliding(): print("[CharacterBody2D:raycast] Raycast colliding"); func check_collision(): var space_state = get_world_2d().direct_space_state var transform = global_transform var query = PhysicsShapeQueryParameters2D.new() query.shape = collision_shape.shape query.transform = transform var results = space_state.intersect_shape(query) for result in results: if result.collider and result.collider.name != "CharacterBody2D": print("[CharacterBody2D:intersect_shape] Kollision mit:", result.collider.name) ====CharacterBody3D==== Player Controller extends CharacterBody3D @onready var head = $SpringArm3D/Head @onready var collision_shape = $CollisionShape3D @onready var camera = $SpringArm3D/Head var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") var speed = 4.0 var jump_speed = 6.0 var mouse_sensitivity = 0.002 var vertical_angle = 0.0 var is_crouching = false var crouch_height = 0.5 var standing_height = 1.8 func _ready(): Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) func shoot(): var space = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.create( head.global_position, head.global_position - head.global_transform.basis.z * 2 ) var collision = space.intersect_ray(query) if collision: print(collision.collider.name) func get_input(): shoot() var input = Input.get_vector("move_left", "move_right", "move_forward", "move_backward") var movement_dir = transform.basis * Vector3(input.x, 0, input.y) velocity.x = movement_dir.x * speed velocity.z = movement_dir.z * speed if Input.is_action_just_pressed("crouch") and is_on_floor(): toggle_crouch() if Input.is_action_just_pressed("Escape"): if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) else: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) if Input.is_action_pressed("jump") and is_on_floor(): velocity.y = jump_speed func _process(delta): pass #check_collision() func check_collision(): var space_state = get_world_3d().direct_space_state var transform = global_transform var query = PhysicsShapeQueryParameters3D.new() query.shape = collision_shape.shape query.transform = transform #query.collide_with_areas = true # Nur Areas berücksichtigen var results = space_state.intersect_shape(query) for result in results: if result.collider: print("Kollision mit:", result.collider.name) func _physics_process(delta): velocity.y += -gravity * delta get_input() move_and_slide() func toggle_crouch(): if is_crouching: is_crouching = false collision_shape.shape.height = standing_height head.position.y = standing_height # Kamera anpassen else: is_crouching = true collision_shape.shape.height = crouch_height head.position.y = crouch_height # Kamera anpassen func _unhandled_input(event): if event is InputEventMouseMotion: # Horizontale Charakterrotation (um die y-Achse) rotate_y(-event.relative.x * mouse_sensitivity) # Vertikale Kamerarotation (um die x-Achse des SpringArm3D) vertical_angle -= event.relative.y * mouse_sensitivity vertical_angle = clamp(vertical_angle, -PI / 2, PI / 2) # Begrenzung der vertikalen Rotation auf +-90° head.rotation.x = vertical_angle Plane Controller extends CharacterBody3D @export var maxSpeed :float = 1.0 @export var turnRate :float = 0.1 @onready var Gravity = ProjectSettings.get_setting("physics/3d/default_gravity") var Speed = 0.0 func _physics_process(delta): _rotate() if Input.is_action_just_pressed("move_forward") and (Speed <= maxSpeed): Speed += 1.0 if Input.is_action_just_pressed("move_backward"): if (Speed >= 1.0): Speed -= 1.0 else: Speed = 0.0 if (Speed > 0.0): global_translate(transform.basis.z) move_and_slide() func _rotate(): var yaw = Input.get_axis("move_right", "move_left") # Y Axis var roll = Input.get_axis("ui_left", "ui_right") # Z Axis var pitch = Input.get_axis("ui_down", "ui_up") # X Axis var direction = Vector3(pitch, yaw, roll) * turnRate if direction: var a = Quaternion(transform.basis.from_euler(rotation)) var b = Quaternion(transform.basis.from_euler(direction)) transform.basis = Basis(a * b) =====VehicleBody3D===== extends VehicleBody3D var steering_smoothness : float = 5.0 # Steuerungsdämpfung func _physics_process(delta: float) -> void: var target_steering = Input.get_axis("move_right", "move_left") * 0.5 var target_engine_force = Input.get_axis("move_backward", "move_forward") * 80 # Steuerung anwenden (sanft interpolieren) steering = lerp(steering, target_steering, steering_smoothness * delta) engine_force = target_engine_force for child in get_children(): if child is VehicleWheel3D: if child.steering: child.set_steering(steering) else: child.engine_force = engine_force =====Networking===== ====RPC==== [[https://www.youtube.com/watch?v=frjU-WC_pwg|Youtube]] func _process(): rpc("rpc_function") # will transfer reliable rpc_unreliable("rpc_function") # will transfer unreliable rpc_function.rpc() rpc_function.rpc_id(peer_id) # call it on a specified peer @rpc(mode, sync, transfer, channel) func rpc_function(): var sender = multiplayer.get_remote_sender_id() if sender == get_multiplayer_authority(): do_stuff() ===Mode=== Von wem kann die Funktion ausgeführt werden. Default ist "authority". Man kann die Multiplayer Authority mit der Funktion Node.set_multiplayer_authority() auf einer per-Peer-Basis konfigurieren. * authority: Funktion kann **nur von dem Peer der die Multiplayer Authority hat** ausgeführt werden, nicht von anderen peers * any_peer: Funktion kann **von jedem peer** ausgeführt werden ===Sync=== Wo wird die Funktion ausgeführt. Nur remote oder auch lokal. * call_remote: Funktion kann **nur Remote** ausgeführt werden * call_local: Funktion wird beim Aufruf **remote und auch lokal** ausgeführt ===Transfer=== * unreliable: UDP * unreliable_ordered: UDP * reliable: TCP ===Channel=== Muss immer das letzte Argument sein. =====Links===== * [[https://www.youtube.com/watch?v=BUjCtwLO0S8|Level/Map Editor]] * [[https://www.youtube.com/watch?v=NlzTmL_eB-U|Learn Godot in 90 Minutes]] * [[https://www.youtube.com/watch?v=sVsn9NqpVhg|Make your first 3D Platformer in Godot 4: Setup, Movement, and Camera Controls]] * [[https://www.youtube.com/watch?v=v4IEPi1c0eE|Simple 3d movement]] * [[https://www.youtube.com/watch?v=Wj1FfilAe2Y|Godot 4 VehicleBody3d Tutorial]] * [[https://www.youtube.com/watch?v=xIKErMgJ1Yk|First Person CharacterController]] * [[https://www.youtube.com/watch?v=h8BmeRRGWxI|First-Person Shooter in Godot 4.0 - Overview]] * [[https://www.youtube.com/watch?v=4nOEVPjVmjc|Bezier Curves und Splines]] * [[https://github.com/TheDuckCow/godot-road-generator|Godot Road Generator AddOn]] * [[https://www.youtube.com/watch?v=n8D3vEx7NAE|Godot Multiplayer FPS Tutorial + project source]] * [[https://www.youtube.com/watch?v=_ItA2r69c-Q|Godot 4 Makes Multiplayer EASY!]] * [[https://www.youtube.com/watch?v=K62jDMLPToA|ENet Multiplayer in 3 Minutes]] * [[https://github.com/mohsenph69/Godot-MTerrain-plugin|MTerrain Plugin]] * [[https://www.youtube.com/watch?v=s1s1zdTGliQ|MTerrain Curve Deformation]]