Created
June 20, 2023 15:57
-
-
Save pwab/8c13b0f1ec9c0f497ec168bf08d55a41 to your computer and use it in GitHub Desktop.
Godot 4 physics investigation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################################################################# | |
# WHY: | |
# Investigating energy loss in Godot 4 Physics | |
# | |
# WHAT: | |
# A pendulum with no damping is created at 90°. | |
# Energy is lost on the way from maximum to maximum and | |
# the delta is tracked. | |
# | |
# HOW: | |
# Just create a Node2D, add that script to it and press play. | |
# Parameters can be changed in the inspector. | |
# | |
# CONCLUSION: | |
# The faster the pendulum moves, the more energy is lost. | |
# This can be due to rounding errors and the fact that low | |
# physics fps can't replicate such a system. | |
# Maybe this should be documented in the official docs. | |
# | |
# DOCS: | |
# https://docs.godotengine.org/en/stable/tutorials/physics/ | |
# troubleshooting_physics_issues.html | |
# | |
################################################################# | |
extends Node2D | |
# Just global references | |
var ground = StaticBody2D.new() | |
var body = RigidBody2D.new() | |
var textedit = TextEdit.new() | |
# Tracking variables | |
var last_angles = [90, 90, 90] | |
var last_max = 90 | |
# --- PARAMETERS --- | |
@export var BODY_LENGTH = 200 #Try 2000 and 20 | |
@export var BODY_WIDTH = 10 | |
@export var BODY_MASS = 1 | |
@export var BODY_GRAVITY_SCALE = 1 | |
@export var BODY_LINEAR_DAMP = 0 | |
@export var BODY_ANGULAR_DAMP = 0 | |
@export var BODY_FRICTION = 0 | |
@export var PIN_SOFTNESS = 0 | |
@export var PIN_BIAS = 1 | |
@export var PHYSICS_TICKS = 60 #Standard: 60 | |
@export var PHYSICS_STEPS = 8 #Standard: 8 | |
func _init(): | |
# Set global physics parameters | |
Engine.physics_ticks_per_second = PHYSICS_TICKS | |
Engine.max_physics_steps_per_frame = PHYSICS_STEPS | |
func _ready(): | |
# Materials | |
var physics_material = PhysicsMaterial.new() | |
physics_material.friction = BODY_FRICTION | |
# Shapes | |
var body_collision_shape = CollisionShape2D.new() | |
var ground_collision_shape = CollisionShape2D.new() | |
var rectangle_shape = RectangleShape2D.new() | |
rectangle_shape.size = Vector2(BODY_LENGTH, BODY_WIDTH) | |
body_collision_shape.shape = rectangle_shape | |
ground_collision_shape.shape = rectangle_shape | |
# Outline | |
var outline = Line2D.new() | |
outline.points = [Vector2(-BODY_LENGTH/2, 0), Vector2(BODY_LENGTH/2, 0)] | |
outline.width = BODY_WIDTH | |
outline.begin_cap_mode = Line2D.LINE_CAP_ROUND | |
outline.end_cap_mode = Line2D.LINE_CAP_ROUND | |
# Pin | |
var pin = PinJoint2D.new() | |
pin.position = get_viewport_rect().size/2 | |
pin.softness = PIN_SOFTNESS | |
pin.bias = PIN_BIAS | |
pin.name = "pin" | |
pin.node_a = "/root/" + name + "/ground" | |
pin.node_b = "/root/" + name + "/body" | |
# Coordinate System | |
var xaxis = Line2D.new() | |
var yaxis = Line2D.new() | |
xaxis.points = [Vector2(-BODY_LENGTH, 0), Vector2(BODY_LENGTH, 0)] | |
yaxis.points = [Vector2(0, -BODY_LENGTH), Vector2(0, BODY_LENGTH)] | |
xaxis.name = "xaxis" | |
yaxis.name = "yaxis" | |
for axis in [xaxis, yaxis]: | |
axis.width = 2 | |
axis.position = pin.position | |
add_child(axis) | |
# Ground management | |
# There is no need to specify the shape/position of the ground explicitly | |
ground.add_child(ground_collision_shape) | |
ground.physics_material_override = physics_material | |
ground.name = "ground" | |
# Body management | |
body.add_child(outline) | |
body.add_child(body_collision_shape) | |
body.physics_material_override = physics_material | |
body.mass = BODY_MASS | |
body.linear_damp_mode = RigidBody2D.DAMP_MODE_REPLACE | |
body.linear_damp = BODY_LINEAR_DAMP | |
body.angular_damp_mode = RigidBody2D.DAMP_MODE_REPLACE | |
body.angular_damp = BODY_ANGULAR_DAMP | |
body.gravity_scale = BODY_GRAVITY_SCALE | |
body.position = pin.position + Vector2(BODY_LENGTH/2, 0) | |
body.name = "body" | |
# Textedit | |
textedit.size.x = get_viewport_rect().size.x/4 | |
textedit.size.y = get_viewport_rect().size.y | |
textedit.position = Vector2(0, 0) | |
textedit.add_theme_font_size_override("font_size", 11) | |
add_child(textedit) | |
# Scene management | |
add_child(ground) | |
add_child(body) | |
add_child(pin) | |
func _process(delta): | |
# Track angles to display the current maxima | |
last_angles.append(abs(body.rotation_degrees - 90)) | |
if last_angles.size() > 3: | |
last_angles.pop_front() | |
if last_angles[1] > last_angles[0] and last_angles[1] > last_angles[2]: | |
var current_max = last_angles[1] | |
var output = "Max: " + str(current_max) + "\t|\t" + "Dif: " + str(current_max - last_max) | |
print(output) | |
textedit.text += output + "\n" | |
last_max = current_max |
Author
pwab
commented
Jun 20, 2023
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment