Skip to content

Commit fa83ee0

Browse files
Updated 2D character controller physics tests
Added new test for 2D character controller: Character - Pixels Functional test for pixel art related issues around KinematicBody and RigidBody character controllers. Adjusted existing tests and added more test cases to cover most use cases from recent fixed issues and regressions for KinematicBody. Added a more automated way to run all tests with checks to see which ones failed in character controller tests. Also fixed some minor issues with the log scrollbar.
1 parent 0b3e046 commit fa83ee0

16 files changed

+824
-123
lines changed

2d/physics_tests/main.tscn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ margin_right = 408.0
190190
margin_bottom = 89.0
191191
text = "Log start"
192192
valign = 2
193+
autowrap = true
193194
max_lines_visible = 5
194195
__meta__ = {
195196
"_edit_use_anchors_": false

2d/physics_tests/tests.gd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ var _tests = [
2626
"id": "Functional Tests/Character - Tilemap",
2727
"path": "res://tests/functional/test_character_tilemap.tscn",
2828
},
29+
{
30+
"id": "Functional Tests/Character - Pixels",
31+
"path": "res://tests/functional/test_character_pixels.tscn",
32+
},
2933
{
3034
"id": "Functional Tests/One Way Collision",
3135
"path": "res://tests/functional/test_one_way_collision.tscn",

2d/physics_tests/tests/functional/test_character.gd

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,57 +17,67 @@ const OPTION_MOVE_KINEMATIC_STOP_ON_SLOPE = "Move Options/Use stop on slope (Kin
1717

1818
export(Vector2) var _initial_velocity = Vector2.ZERO
1919
export(Vector2) var _constant_velocity = Vector2.ZERO
20+
export(float) var _motion_speed = 400.0
21+
export(float) var _gravity_force = 50.0
22+
export(float) var _jump_force = 1000.0
2023
export(float) var _snap_distance = 0.0
2124
export(float) var _floor_max_angle = 45.0
2225
export(E_BodyType) var _body_type = 0
2326

27+
onready var options = $Options
28+
2429
var _use_snap = true
2530
var _use_stop_on_slope = true
2631

32+
var _body_parent = null
2733
var _rigid_body_template = null
2834
var _kinematic_body_template = null
2935
var _kinematic_body_ray_template = null
3036
var _moving_body = null
3137

3238

3339
func _ready():
34-
$Options.connect("option_selected", self, "_on_option_selected")
35-
$Options.connect("option_changed", self, "_on_option_changed")
40+
options.connect("option_selected", self, "_on_option_selected")
41+
options.connect("option_changed", self, "_on_option_changed")
3642

3743
_rigid_body_template = find_node("RigidBody2D")
3844
if _rigid_body_template:
39-
remove_child(_rigid_body_template)
45+
_body_parent = _rigid_body_template.get_parent()
46+
_body_parent.remove_child(_rigid_body_template)
4047
var enabled = _body_type == E_BodyType.RIGID_BODY
41-
$Options.add_menu_item(OPTION_OBJECT_TYPE_RIGIDBODY, true, enabled, true)
48+
options.add_menu_item(OPTION_OBJECT_TYPE_RIGIDBODY, true, enabled, true)
4249

4350
_kinematic_body_template = find_node("KinematicBody2D")
4451
if _kinematic_body_template:
45-
remove_child(_kinematic_body_template)
52+
_body_parent = _kinematic_body_template.get_parent()
53+
_body_parent.remove_child(_kinematic_body_template)
4654
var enabled = _body_type == E_BodyType.KINEMATIC_BODY
47-
$Options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC, true, enabled, true)
55+
options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC, true, enabled, true)
4856

4957
_kinematic_body_ray_template = find_node("KinematicBodyRay2D")
5058
if _kinematic_body_ray_template:
51-
remove_child(_kinematic_body_ray_template)
59+
_body_parent = _kinematic_body_ray_template.get_parent()
60+
_body_parent.remove_child(_kinematic_body_ray_template)
5261
var enabled = _body_type == E_BodyType.KINEMATIC_BODY_RAY_SHAPE
53-
$Options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC_RAYSHAPE, true, enabled, true)
62+
options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC_RAYSHAPE, true, enabled, true)
5463

55-
$Options.add_menu_item(OPTION_MOVE_KINEMATIC_SNAP, true, _use_snap)
56-
$Options.add_menu_item(OPTION_MOVE_KINEMATIC_STOP_ON_SLOPE, true, _use_stop_on_slope)
64+
options.add_menu_item(OPTION_MOVE_KINEMATIC_SNAP, true, _use_snap)
65+
options.add_menu_item(OPTION_MOVE_KINEMATIC_STOP_ON_SLOPE, true, _use_stop_on_slope)
5766

5867
_start_test()
5968

6069

6170
func _process(_delta):
71+
var label_floor = $LabelFloor
6272
if _moving_body:
6373
if _moving_body.is_on_floor():
64-
$LabelFloor.text = "ON FLOOR"
65-
$LabelFloor.self_modulate = Color.green
74+
label_floor.text = "ON FLOOR"
75+
label_floor.self_modulate = Color.green
6676
else:
67-
$LabelFloor.text = "OFF FLOOR"
68-
$LabelFloor.self_modulate = Color.red
77+
label_floor.text = "OFF FLOOR"
78+
label_floor.self_modulate = Color.red
6979
else:
70-
$LabelFloor.visible = false
80+
label_floor.visible = false
7181

7282

7383
func _input(event):
@@ -118,7 +128,7 @@ func _on_option_changed(option, checked):
118128

119129
func _start_test():
120130
if _moving_body:
121-
remove_child(_moving_body)
131+
_body_parent.remove_child(_moving_body)
122132
_moving_body.queue_free()
123133
_moving_body = null
124134

@@ -135,11 +145,15 @@ func _start_test():
135145

136146
test_label += template.name
137147
_moving_body = template.duplicate()
138-
add_child(_moving_body)
148+
_body_parent.add_child(_moving_body)
139149

140150
_moving_body._initial_velocity = _initial_velocity
141151
_moving_body._constant_velocity = _constant_velocity
142152

153+
_moving_body._motion_speed = _motion_speed
154+
_moving_body._gravity_force = _gravity_force
155+
_moving_body._jump_force = _jump_force
156+
143157
if _moving_body is KinematicBody2D:
144158
if _use_snap:
145159
_moving_body._snap = Vector2(0, _snap_distance)
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
extends TestCharacter
2+
3+
4+
const OPTION_TEST_CASE_ALL = "Test Cases/TEST ALL (0)"
5+
const OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP = "Test Cases/Floor detection (Kinematic Body)"
6+
const OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES = "Test Cases/Floor detection with motion changes (Kinematic Body)"
7+
8+
const MOTION_CHANGES_DIR = Vector2(1.0, 1.0)
9+
const MOTION_CHANGES_SPEEDS = [0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0]
10+
11+
var _test_floor_detection = false
12+
var _test_motion_changes = false
13+
var _floor_detected = false
14+
var _floor_lost = false
15+
16+
var _failed_reason = ""
17+
18+
19+
func _ready():
20+
options.add_menu_item(OPTION_TEST_CASE_ALL)
21+
options.add_menu_item(OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP)
22+
options.add_menu_item(OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES)
23+
24+
func _physics_process(_delta):
25+
if _moving_body:
26+
if _moving_body.is_on_floor():
27+
_floor_detected = true
28+
elif _floor_detected:
29+
_floor_lost = true
30+
if _test_motion_changes:
31+
Log.print_log("Floor lost.")
32+
33+
if _test_motion_changes:
34+
var speed_count = MOTION_CHANGES_SPEEDS.size()
35+
var speed_index = randi() % speed_count
36+
var speed = MOTION_CHANGES_SPEEDS[speed_index]
37+
var velocity = speed * MOTION_CHANGES_DIR
38+
_moving_body._constant_velocity = velocity
39+
#Log.print_log("Velocity: %s" % velocity)
40+
41+
42+
func _input(event):
43+
var key_event = event as InputEventKey
44+
if key_event and not key_event.pressed:
45+
if key_event.scancode == KEY_0:
46+
_on_option_selected(OPTION_TEST_CASE_ALL)
47+
48+
49+
func _on_option_selected(option):
50+
match option:
51+
OPTION_TEST_CASE_ALL:
52+
_test_all()
53+
OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP:
54+
_start_test_case(option)
55+
return
56+
OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES:
57+
_start_test_case(option)
58+
return
59+
60+
._on_option_selected(option)
61+
62+
63+
func _start_test_case(option):
64+
Log.print_log("* Starting " + option)
65+
66+
match option:
67+
OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP:
68+
_test_floor_detection = true
69+
_test_motion_changes = false
70+
_use_snap = false
71+
_body_type = E_BodyType.KINEMATIC_BODY
72+
_start_test()
73+
74+
yield(start_timer(1.0), "timeout")
75+
if is_timer_canceled():
76+
return
77+
78+
_set_result(not _floor_lost)
79+
OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES:
80+
_test_floor_detection = true
81+
_test_motion_changes = true
82+
_use_snap = false
83+
_body_type = E_BodyType.KINEMATIC_BODY
84+
_start_test()
85+
86+
yield(start_timer(4.0), "timeout")
87+
if is_timer_canceled():
88+
_test_motion_changes = false
89+
return
90+
91+
_test_motion_changes = false
92+
_moving_body._constant_velocity = Vector2.ZERO
93+
94+
_set_result(not _floor_lost)
95+
_:
96+
Log.print_error("Invalid test case.")
97+
98+
99+
func _test_all():
100+
Log.print_log("* TESTING ALL...")
101+
102+
# Test floor detection with no snapping.
103+
yield(_start_test_case(OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP), "completed")
104+
105+
# Test floor detection with no snapping.
106+
# In this test case, motion alternates different speeds.
107+
yield(_start_test_case(OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES), "completed")
108+
109+
Log.print_log("* Done.")
110+
111+
112+
func _set_result(test_passed):
113+
var result = ""
114+
if test_passed:
115+
result = "PASSED"
116+
else:
117+
result = "FAILED"
118+
119+
if not test_passed and not _failed_reason.empty():
120+
result += _failed_reason
121+
else:
122+
result += "."
123+
124+
Log.print_log("Test %s" % result)
125+
126+
127+
func _start_test():
128+
._start_test()
129+
130+
_failed_reason = ""
131+
132+
_floor_detected = false
133+
_floor_lost = false
134+
135+
if _test_floor_detection:
136+
_failed_reason = ": floor was not detected consistently."
137+
if _test_motion_changes:
138+
# Always use the same seed for reproducible results.
139+
rand_seed(123456789)
140+
_moving_body._gravity_force = 0.0
141+
_moving_body._motion_speed = 0.0
142+
_moving_body._jump_force = 0.0
143+
else:
144+
_moving_body._initial_velocity = Vector2(30, 0)
145+
_test_floor_detection = false
146+
else:
147+
_test_motion_changes = false

0 commit comments

Comments
 (0)