[[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]]