I made interesting observations !
@pmoosi, you gave me an idea: Using the equations of motion I learned at school. So I wrote the following script:
extends CharacterBody2D
var t = 0
var v_0 = Vector2.ZERO
var pos_0 = position
@onready var acceleration = Vector2.RIGHT * get_parent().get_meta("acceleration")
func _physics_process(delta: float) -> void:
t += delta
var new_position : Vector2 = (acceleration * t ** 2) / 2 + v_0 * t + pos_0
velocity = new_position - position
move_and_collide(velocity)
and an equivalent with move_and_slide
:
extends CharacterBody2D
var t = 0
var v_0 = Vector2.ZERO
var pos_0 = position
@onready var acceleration = Vector2.RIGHT * get_parent().get_meta("acceleration")
func _physics_process(delta: float) -> void:
t += delta
var new_position : Vector2 = (acceleration * t ** 2) / 2 + v_0 * t + pos_0
velocity = new_position - position
velocity /= delta
move_and_slide()
Then, I made measures of position (at different frame rates) with the following script (which is an improvement of the one I shared earlier: `delta` : when and when not to use it? - #19 by rom1dev):
extends Node2D
var counter = 0
var previous_try_frames = 0
var sc_scene = preload("res://first/sc.tscn")
var end_scene = preload("res://first/end.tscn")
const END_TIME = 5
var t = 0
const fps_list = [6, 20, 30, 40, 60, 70, 100, 120]
func _ready() -> void:
print("import matplotlib.pyplot as plt\n\n", "fps_list = ", fps_list,
"""
JTB_mvs = []
BNA_mvs = []
PHY_mvs = []
JTB_mvc = []
BNA_mvc = []
PHY_mvc = []
JTB_mvs_position_X = 0
BNA_mvs_position_X = 0
PHY_mvs_position_X = 0
JTB_mvc_position_X = 0
BNA_mvc_position_X = 0
PHY_mvc_position_X = 0
def add_to_lists():
JTB_mvs.append(JTB_mvs_position_X)
BNA_mvs.append(BNA_mvs_position_X)
PHY_mvs.append(PHY_mvs_position_X)
JTB_mvc.append(JTB_mvc_position_X)
BNA_mvc.append(BNA_mvc_position_X)
PHY_mvc.append(PHY_mvc_position_X)
"""
)
print("expected_position_X = ", 0.5 * $Sc/MvCollideOwnew.acceleration.x * END_TIME ** 2)
print("# -- capture positions at ", END_TIME, "s -- ")
print("end_time = ", END_TIME)
Engine.physics_ticks_per_second = fps_list[counter]
func _physics_process(delta: float) -> void:
t += delta
if t >= END_TIME or Engine.get_physics_frames() - previous_try_frames == Engine.physics_ticks_per_second * END_TIME:
get_child(0).name = "Sc"
print("\n\n#time = ", t, "s impressicion: ", t - END_TIME)
print("# ", "at ", Engine.physics_ticks_per_second, " physics fps")
print("\n# ----- with move_and_slide:")
print("JTB_mvs_position_X = ", $Sc/CharacterBody2D.position.x, " # just before")
print("BNA_mvs_position_X = ", $Sc/CharacterBody2D2.position.x, " # before and after")
print("PHY_mvs_position_X = ", $Sc/MvSlideOwnew.position.x, " # physic equation")
print("\n# ----- with move_and_collide:")
print("JTB_mvc_position_X = ", $Sc/MvCollide.position.x, " # just before")
print("BNA_mvc_position_X = ", $Sc/MvCollide2.position.x, " # before and after")
print("PHY_mvc_position_X = ", $Sc/MvCollideOwnew.position.x, " # physic equation")
print("add_to_lists()")
if counter >= fps_list.size() - 1:
print(
"""
plt.xlabel("physics FPS")
plt.ylabel(f"position after {end_time}s")
plt.plot(fps_list, JTB_mvc, "yo", label="just before (move and collide)")
plt.plot(fps_list, BNA_mvc, "go",label="before and after (move and collide)")
plt.plot(fps_list, PHY_mvc, "ro", label="physic equation based (move and collide)")
plt.plot(fps_list, JTB_mvs, "y-", label="just before (move and slide)")
plt.plot(fps_list, BNA_mvs, "g-",label="before and after (move and slide)", linewidth=5.0)
plt.plot(fps_list, PHY_mvs, "r-", label="physic equation based (move and slide)")
plt.plot(fps_list, [expected_position_X] * len(fps_list), "b--")
plt.legend()
plt.show()
"""
)
get_tree().change_scene_to_packed(end_scene)
return
else:
counter += 1
Engine.physics_ticks_per_second = fps_list[counter]
previous_try_frames = Engine.get_physics_frames()
t = 0
$Sc.queue_free()
add_child(sc_scene.instantiate())
This also measures the positions of bodies with different scripts (I have shared earlier: `delta` : when and when not to use it? - #19 by rom1dev).
It prints a python script which I then use to visualize measures:
import matplotlib.pyplot as plt
fps_list = [6, 20, 30, 40, 60, 70, 100, 120]
JTB_mvs = []
BNA_mvs = []
PHY_mvs = []
JTB_mvc = []
BNA_mvc = []
PHY_mvc = []
JTB_mvs_position_X = 0
BNA_mvs_position_X = 0
PHY_mvs_position_X = 0
JTB_mvc_position_X = 0
BNA_mvc_position_X = 0
PHY_mvc_position_X = 0
def add_to_lists():
JTB_mvs.append(JTB_mvs_position_X)
BNA_mvs.append(BNA_mvs_position_X)
PHY_mvs.append(PHY_mvs_position_X)
JTB_mvc.append(JTB_mvc_position_X)
BNA_mvc.append(BNA_mvc_position_X)
PHY_mvc.append(PHY_mvc_position_X)
expected_position_X = 625.0
# -- capture positions at 5s --
end_time = 5
#time = 5.0s impressicion: 0.0
# at 6 physics fps
# ----- with move_and_slide:
JTB_mvs_position_X = 604.166564941406 # just before
BNA_mvs_position_X = 584.027893066406 # before and after
PHY_mvs_position_X = 584.027770996094 # physic equation
# ----- with move_and_collide:
JTB_mvc_position_X = 604.166564941406 # just before
BNA_mvc_position_X = 584.027893066406 # before and after
PHY_mvc_position_X = 584.027770996094 # physic equation
add_to_lists()
#time = 4.99999999999999s impressicion: -0.00000000000001
# at 20 physics fps
# ----- with move_and_slide:
JTB_mvs_position_X = 618.75 # just before
BNA_mvs_position_X = 612.5625 # before and after
PHY_mvs_position_X = 612.5625 # physic equation
# ----- with move_and_collide:
JTB_mvc_position_X = 618.75 # just before
BNA_mvc_position_X = 612.5625 # before and after
PHY_mvc_position_X = 612.5625 # physic equation
add_to_lists()
#time = 4.99999999999999s impressicion: -0.00000000000001
# at 30 physics fps
# ----- with move_and_slide:
JTB_mvs_position_X = 620.833557128906 # just before
BNA_mvs_position_X = 616.693969726562 # before and after
PHY_mvs_position_X = 616.694458007812 # physic equation
# ----- with move_and_collide:
JTB_mvc_position_X = 620.833557128906 # just before
BNA_mvc_position_X = 616.693969726562 # before and after
PHY_mvc_position_X = 616.694458007812 # physic equation
add_to_lists()
#time = 5.0s impressicion: 0.0
# at 40 physics fps
# ----- with move_and_slide:
JTB_mvs_position_X = 621.875 # just before
BNA_mvs_position_X = 618.765625 # before and after
PHY_mvs_position_X = 618.765625 # physic equation
# ----- with move_and_collide:
JTB_mvc_position_X = 621.875 # just before
BNA_mvc_position_X = 618.765625 # before and after
PHY_mvc_position_X = 618.765625 # physic equation
add_to_lists()
#time = 4.99999999999999s impressicion: -0.00000000000001
# at 60 physics fps
# ----- with move_and_slide:
JTB_mvs_position_X = 622.916259765625 # just before
BNA_mvs_position_X = 620.841247558594 # before and after
PHY_mvs_position_X = 620.840270996094 # physic equation
# ----- with move_and_collide:
JTB_mvc_position_X = 622.916259765625 # just before
BNA_mvc_position_X = 620.841247558594 # before and after
PHY_mvc_position_X = 620.840270996094 # physic equation
add_to_lists()
#time = 4.99999999999998s impressicion: -0.00000000000002
# at 70 physics fps
# ----- with move_and_slide:
JTB_mvs_position_X = 623.213500976562 # just before
BNA_mvs_position_X = 621.433898925781 # before and after
PHY_mvs_position_X = 621.433715820312 # physic equation
# ----- with move_and_collide:
JTB_mvc_position_X = 623.213500976562 # just before
BNA_mvc_position_X = 621.433898925781 # before and after
PHY_mvc_position_X = 621.433715820312 # physic equation
add_to_lists()
#time = 4.99999999999994s impressicion: -0.00000000000006
# at 100 physics fps
# ----- with move_and_slide:
JTB_mvs_position_X = 623.749938964844 # just before
BNA_mvs_position_X = 622.502502441406 # before and after
PHY_mvs_position_X = 622.502502441406 # physic equation
# ----- with move_and_collide:
JTB_mvc_position_X = 623.749938964844 # just before
BNA_mvc_position_X = 622.502502441406 # before and after
PHY_mvc_position_X = 622.502502441406 # physic equation
add_to_lists()
#time = 5.00000000000004s impressicion: 0.00000000000004
# at 120 physics fps
# ----- with move_and_slide:
JTB_mvs_position_X = 623.959228515625 # just before
BNA_mvs_position_X = 622.916625976562 # before and after
PHY_mvs_position_X = 622.918395996094 # physic equation
# ----- with move_and_collide:
JTB_mvc_position_X = 623.959228515625 # just before
BNA_mvc_position_X = 622.916625976562 # before and after
PHY_mvc_position_X = 622.918395996094 # physic equation
add_to_lists()
plt.xlabel("physics FPS")
plt.ylabel(f"position after {end_time}s")
plt.plot(fps_list, JTB_mvc, "yo", label="just before (move and collide)")
plt.plot(fps_list, BNA_mvc, "go",label="before and after (move and collide)")
plt.plot(fps_list, PHY_mvc, "ro", label="physic equation based (move and collide)")
plt.plot(fps_list, JTB_mvs, "y-", label="just before (move and slide)")
plt.plot(fps_list, BNA_mvs, "g-",label="before and after (move and slide)", linewidth=5.0)
plt.plot(fps_list, PHY_mvs, "r-", label="physic equation based (move and slide)")
plt.plot(fps_list, [expected_position_X] * len(fps_list), "b--")
plt.legend()
plt.show()
And the resulting graph (after executing this python script):
I made 3 interesting observations:
- my new idea (“physics equation based”) gives the exact same result as “before and after”.
- “just before” is always closer to the expected value.
- “just before” seems to be less frame rate dependent.
Is someone able to explain these observations (particularly 2 and 3 which are the most surprising to me) ?
I hope this post is clear enough… So if it is not, or if there are missing information, don’t hesitate to ask.
EDIT: I just fixed a mistake (the “(move and collide)” and “(move and slide)” labels in on the graph were inverted). I just re-uploaded a correct graph and correct python and GDScript code so what you see now is correct. That said, as move_and_slide
and move_and_collide
doesn’t seem to make any difference in this case, this fix is minor.