Merge pull request #584 from nekomatata/physics-tests-2d-character-update

Updated 2D character controller physics tests
This commit is contained in:
Aaron Franke
2021-02-26 09:25:58 -05:00
committed by GitHub
16 changed files with 819 additions and 118 deletions

View File

@@ -190,6 +190,7 @@ margin_right = 408.0
margin_bottom = 89.0
text = "Log start"
valign = 2
autowrap = true
max_lines_visible = 5
__meta__ = {
"_edit_use_anchors_": false

View File

@@ -26,6 +26,10 @@ var _tests = [
"id": "Functional Tests/Character - Tilemap",
"path": "res://tests/functional/test_character_tilemap.tscn",
},
{
"id": "Functional Tests/Character - Pixels",
"path": "res://tests/functional/test_character_pixels.tscn",
},
{
"id": "Functional Tests/One Way Collision",
"path": "res://tests/functional/test_one_way_collision.tscn",

View File

@@ -17,13 +17,19 @@ const OPTION_MOVE_KINEMATIC_STOP_ON_SLOPE = "Move Options/Use stop on slope (Kin
export(Vector2) var _initial_velocity = Vector2.ZERO
export(Vector2) var _constant_velocity = Vector2.ZERO
export(float) var _motion_speed = 400.0
export(float) var _gravity_force = 50.0
export(float) var _jump_force = 1000.0
export(float) var _snap_distance = 0.0
export(float) var _floor_max_angle = 45.0
export(E_BodyType) var _body_type = 0
onready var options = $Options
var _use_snap = true
var _use_stop_on_slope = true
var _body_parent = null
var _rigid_body_template = null
var _kinematic_body_template = null
var _kinematic_body_ray_template = null
@@ -31,43 +37,47 @@ var _moving_body = null
func _ready():
$Options.connect("option_selected", self, "_on_option_selected")
$Options.connect("option_changed", self, "_on_option_changed")
options.connect("option_selected", self, "_on_option_selected")
options.connect("option_changed", self, "_on_option_changed")
_rigid_body_template = find_node("RigidBody2D")
if _rigid_body_template:
remove_child(_rigid_body_template)
_body_parent = _rigid_body_template.get_parent()
_body_parent.remove_child(_rigid_body_template)
var enabled = _body_type == E_BodyType.RIGID_BODY
$Options.add_menu_item(OPTION_OBJECT_TYPE_RIGIDBODY, true, enabled, true)
options.add_menu_item(OPTION_OBJECT_TYPE_RIGIDBODY, true, enabled, true)
_kinematic_body_template = find_node("KinematicBody2D")
if _kinematic_body_template:
remove_child(_kinematic_body_template)
_body_parent = _kinematic_body_template.get_parent()
_body_parent.remove_child(_kinematic_body_template)
var enabled = _body_type == E_BodyType.KINEMATIC_BODY
$Options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC, true, enabled, true)
options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC, true, enabled, true)
_kinematic_body_ray_template = find_node("KinematicBodyRay2D")
if _kinematic_body_ray_template:
remove_child(_kinematic_body_ray_template)
_body_parent = _kinematic_body_ray_template.get_parent()
_body_parent.remove_child(_kinematic_body_ray_template)
var enabled = _body_type == E_BodyType.KINEMATIC_BODY_RAY_SHAPE
$Options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC_RAYSHAPE, true, enabled, true)
options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC_RAYSHAPE, true, enabled, true)
$Options.add_menu_item(OPTION_MOVE_KINEMATIC_SNAP, true, _use_snap)
$Options.add_menu_item(OPTION_MOVE_KINEMATIC_STOP_ON_SLOPE, true, _use_stop_on_slope)
options.add_menu_item(OPTION_MOVE_KINEMATIC_SNAP, true, _use_snap)
options.add_menu_item(OPTION_MOVE_KINEMATIC_STOP_ON_SLOPE, true, _use_stop_on_slope)
_start_test()
func _process(_delta):
var label_floor = $LabelFloor
if _moving_body:
if _moving_body.is_on_floor():
$LabelFloor.text = "ON FLOOR"
$LabelFloor.self_modulate = Color.green
label_floor.text = "ON FLOOR"
label_floor.self_modulate = Color.green
else:
$LabelFloor.text = "OFF FLOOR"
$LabelFloor.self_modulate = Color.red
label_floor.text = "OFF FLOOR"
label_floor.self_modulate = Color.red
else:
$LabelFloor.visible = false
label_floor.visible = false
func _input(event):
@@ -118,7 +128,7 @@ func _on_option_changed(option, checked):
func _start_test():
if _moving_body:
remove_child(_moving_body)
_body_parent.remove_child(_moving_body)
_moving_body.queue_free()
_moving_body = null
@@ -135,11 +145,15 @@ func _start_test():
test_label += template.name
_moving_body = template.duplicate()
add_child(_moving_body)
_body_parent.add_child(_moving_body)
_moving_body._initial_velocity = _initial_velocity
_moving_body._constant_velocity = _constant_velocity
_moving_body._motion_speed = _motion_speed
_moving_body._gravity_force = _gravity_force
_moving_body._jump_force = _jump_force
if _moving_body is KinematicBody2D:
if _use_snap:
_moving_body._snap = Vector2(0, _snap_distance)

View File

@@ -0,0 +1,147 @@
extends TestCharacter
const OPTION_TEST_CASE_ALL = "Test Cases/TEST ALL (0)"
const OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP = "Test Cases/Floor detection (Kinematic Body)"
const OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES = "Test Cases/Floor detection with motion changes (Kinematic Body)"
const MOTION_CHANGES_DIR = Vector2(1.0, 1.0)
const MOTION_CHANGES_SPEEDS = [0.5, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0]
var _test_floor_detection = false
var _test_motion_changes = false
var _floor_detected = false
var _floor_lost = false
var _failed_reason = ""
func _ready():
options.add_menu_item(OPTION_TEST_CASE_ALL)
options.add_menu_item(OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP)
options.add_menu_item(OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES)
func _physics_process(_delta):
if _moving_body:
if _moving_body.is_on_floor():
_floor_detected = true
elif _floor_detected:
_floor_lost = true
if _test_motion_changes:
Log.print_log("Floor lost.")
if _test_motion_changes:
var speed_count = MOTION_CHANGES_SPEEDS.size()
var speed_index = randi() % speed_count
var speed = MOTION_CHANGES_SPEEDS[speed_index]
var velocity = speed * MOTION_CHANGES_DIR
_moving_body._constant_velocity = velocity
#Log.print_log("Velocity: %s" % velocity)
func _input(event):
var key_event = event as InputEventKey
if key_event and not key_event.pressed:
if key_event.scancode == KEY_0:
_on_option_selected(OPTION_TEST_CASE_ALL)
func _on_option_selected(option):
match option:
OPTION_TEST_CASE_ALL:
_test_all()
OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP:
_start_test_case(option)
return
OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES:
_start_test_case(option)
return
._on_option_selected(option)
func _start_test_case(option):
Log.print_log("* Starting " + option)
match option:
OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP:
_test_floor_detection = true
_test_motion_changes = false
_use_snap = false
_body_type = E_BodyType.KINEMATIC_BODY
_start_test()
yield(start_timer(1.0), "timeout")
if is_timer_canceled():
return
_set_result(not _floor_lost)
OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES:
_test_floor_detection = true
_test_motion_changes = true
_use_snap = false
_body_type = E_BodyType.KINEMATIC_BODY
_start_test()
yield(start_timer(4.0), "timeout")
if is_timer_canceled():
_test_motion_changes = false
return
_test_motion_changes = false
_moving_body._constant_velocity = Vector2.ZERO
_set_result(not _floor_lost)
_:
Log.print_error("Invalid test case.")
func _test_all():
Log.print_log("* TESTING ALL...")
# Test floor detection with no snapping.
yield(_start_test_case(OPTION_TEST_CASE_DETECT_FLOOR_NO_SNAP), "completed")
# Test floor detection with no snapping.
# In this test case, motion alternates different speeds.
yield(_start_test_case(OPTION_TEST_CASE_DETECT_FLOOR_MOTION_CHANGES), "completed")
Log.print_log("* Done.")
func _set_result(test_passed):
var result = ""
if test_passed:
result = "PASSED"
else:
result = "FAILED"
if not test_passed and not _failed_reason.empty():
result += _failed_reason
else:
result += "."
Log.print_log("Test %s" % result)
func _start_test():
._start_test()
_failed_reason = ""
_floor_detected = false
_floor_lost = false
if _test_floor_detection:
_failed_reason = ": floor was not detected consistently."
if _test_motion_changes:
# Always use the same seed for reproducible results.
rand_seed(123456789)
_moving_body._gravity_force = 0.0
_moving_body._motion_speed = 0.0
_moving_body._jump_force = 0.0
else:
_moving_body._initial_velocity = Vector2(30, 0)
_test_floor_detection = false
else:
_test_motion_changes = false

View File

@@ -0,0 +1,151 @@
[gd_scene load_steps=12 format=2]
[ext_resource path="res://tests/functional/test_character_pixels.gd" type="Script" id=1]
[ext_resource path="res://utils/rigidbody_controller.gd" type="Script" id=2]
[ext_resource path="res://tests/test_options.tscn" type="PackedScene" id=3]
[ext_resource path="res://tests/static_scene_flat.tscn" type="PackedScene" id=4]
[ext_resource path="res://utils/kinematicbody_controller.gd" type="Script" id=7]
[sub_resource type="PhysicsMaterial" id=1]
friction = 0.0
[sub_resource type="RectangleShape2D" id=2]
extents = Vector2( 3, 5 )
[sub_resource type="RectangleShape2D" id=3]
extents = Vector2( 3, 4.9 )
[sub_resource type="RectangleShape2D" id=4]
extents = Vector2( 3, 3 )
[sub_resource type="RayShape2D" id=5]
length = 3.0
[sub_resource type="RectangleShape2D" id=6]
extents = Vector2( 10, 2 )
[node name="Test" type="Node2D"]
script = ExtResource( 1 )
_motion_speed = 30.0
_gravity_force = 2.0
_jump_force = 50.0
_snap_distance = 1.0
[node name="ViewportContainer" type="ViewportContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
margin_right = 1024.0
margin_bottom = 600.0
size_flags_horizontal = 3
size_flags_vertical = 3
stretch = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Viewport" type="Viewport" parent="ViewportContainer"]
size = Vector2( 128, 75 )
handle_input_locally = false
render_target_update_mode = 3
[node name="StaticSceneFlat" parent="ViewportContainer/Viewport" instance=ExtResource( 4 )]
position = Vector2( 0, -450 )
[node name="RigidBody2D" type="RigidBody2D" parent="ViewportContainer/Viewport"]
position = Vector2( 30, 40 )
collision_mask = 2147483649
mode = 2
physics_material_override = SubResource( 1 )
contacts_reported = 4
contact_monitor = true
script = ExtResource( 2 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="ViewportContainer/Viewport/RigidBody2D"]
shape = SubResource( 2 )
[node name="KinematicBody2D" type="KinematicBody2D" parent="ViewportContainer/Viewport"]
position = Vector2( 30, 40 )
collision_mask = 2147483649
script = ExtResource( 7 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="ViewportContainer/Viewport/KinematicBody2D"]
shape = SubResource( 3 )
[node name="KinematicBodyRay2D" type="KinematicBody2D" parent="ViewportContainer/Viewport"]
position = Vector2( 30, 40 )
collision_mask = 2147483649
script = ExtResource( 7 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="ViewportContainer/Viewport/KinematicBodyRay2D"]
position = Vector2( 0, -2 )
shape = SubResource( 4 )
[node name="CollisionShapeRay2D" type="CollisionShape2D" parent="ViewportContainer/Viewport/KinematicBodyRay2D"]
position = Vector2( 0, -1 )
shape = SubResource( 5 )
[node name="Wall1" type="StaticBody2D" parent="ViewportContainer/Viewport"]
position = Vector2( 20, 40 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="ViewportContainer/Viewport/Wall1"]
rotation = 1.5708
shape = SubResource( 6 )
[node name="Wall2" type="StaticBody2D" parent="ViewportContainer/Viewport"]
position = Vector2( 122, 40 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="ViewportContainer/Viewport/Wall2"]
rotation = 1.5708
shape = SubResource( 6 )
[node name="Platform1" type="StaticBody2D" parent="ViewportContainer/Viewport"]
position = Vector2( 50, 44 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="ViewportContainer/Viewport/Platform1"]
shape = SubResource( 6 )
one_way_collision = true
[node name="Platform2" type="StaticBody2D" parent="ViewportContainer/Viewport"]
position = Vector2( 80, 38 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="ViewportContainer/Viewport/Platform2"]
shape = SubResource( 6 )
[node name="Slope" type="StaticBody2D" parent="ViewportContainer/Viewport"]
position = Vector2( 84, 36 )
[node name="CollisionShape2D" type="CollisionPolygon2D" parent="ViewportContainer/Viewport/Slope"]
polygon = PoolVector2Array( 0, 0, 6, 0, 22, 16, 16, 16 )
[node name="LabelTestType" type="Label" parent="."]
margin_left = 14.0
margin_top = 79.0
margin_right = 145.0
margin_bottom = 93.0
text = "Testing: "
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Options" parent="." instance=ExtResource( 3 )]
[node name="LabelFloor" type="Label" parent="."]
margin_left = 14.0
margin_top = 237.929
margin_right = 145.0
margin_bottom = 251.929
text = "ON FLOOR"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="LabelControls" type="Label" parent="."]
margin_left = 14.0
margin_top = 263.291
margin_right = 145.0
margin_bottom = 294.291
text = "LEFT/RIGHT - MOVE
UP - JUMP"
__meta__ = {
"_edit_use_anchors_": false
}

View File

@@ -26,7 +26,7 @@ length = 64.0
[node name="Test" type="Node2D"]
script = ExtResource( 1 )
_snap_distance = 32.0
_floor_max_angle = 60.0
_floor_max_angle = 45.0
[node name="LabelTestType" type="Label" parent="."]
margin_left = 14.0

View File

@@ -1,26 +1,196 @@
extends TestCharacter
const OPTION_TEST_CASE_JUMP_ONE_WAY = "Test Cases/Jump through one-way tiles"
const OPTION_TEST_CASE_ALL = "Test Cases/TEST ALL (0)"
const OPTION_TEST_CASE_JUMP_ONE_WAY_RIGID = "Test Cases/Jump through one-way tiles (Rigid Body)"
const OPTION_TEST_CASE_JUMP_ONE_WAY_KINEMATIC = "Test Cases/Jump through one-way tiles (Kinematic Body)"
const OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_RIGID = "Test Cases/Jump through one-way corner (Rigid Body)"
const OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_KINEMATIC = "Test Cases/Jump through one-way corner (Kinematic Body)"
const OPTION_TEST_CASE_FALL_ONE_WAY_KINEMATIC = "Test Cases/Fall and pushed on one-way tiles (Kinematic Body)"
var _test_jump_one_way = false
var _test_jump_one_way_corner = false
var _test_fall_one_way = false
var _extra_body = null
var _failed_reason = ""
func _ready():
$Options.add_menu_item(OPTION_TEST_CASE_JUMP_ONE_WAY, true, false)
options.add_menu_item(OPTION_TEST_CASE_ALL)
options.add_menu_item(OPTION_TEST_CASE_JUMP_ONE_WAY_RIGID)
options.add_menu_item(OPTION_TEST_CASE_JUMP_ONE_WAY_KINEMATIC)
options.add_menu_item(OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_RIGID)
options.add_menu_item(OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_KINEMATIC)
options.add_menu_item(OPTION_TEST_CASE_FALL_ONE_WAY_KINEMATIC)
func _on_option_changed(option, checked):
func _input(event):
var key_event = event as InputEventKey
if key_event and not key_event.pressed:
if key_event.scancode == KEY_0:
_on_option_selected(OPTION_TEST_CASE_ALL)
func _on_option_selected(option):
match option:
OPTION_TEST_CASE_JUMP_ONE_WAY:
_test_jump_one_way = checked
_start_test()
OPTION_TEST_CASE_ALL:
_test_all()
OPTION_TEST_CASE_JUMP_ONE_WAY_RIGID:
_start_test_case(option)
return
OPTION_TEST_CASE_JUMP_ONE_WAY_KINEMATIC:
_start_test_case(option)
return
OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_RIGID:
_start_test_case(option)
return
OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_KINEMATIC:
_start_test_case(option)
return
OPTION_TEST_CASE_FALL_ONE_WAY_KINEMATIC:
_start_test_case(option)
return
._on_option_changed(option, checked)
._on_option_selected(option)
func _start_test_case(option):
Log.print_log("* Starting " + option)
match option:
OPTION_TEST_CASE_JUMP_ONE_WAY_RIGID:
_body_type = E_BodyType.RIGID_BODY
_test_jump_one_way_corner = false
yield(_start_jump_one_way(), "completed")
OPTION_TEST_CASE_JUMP_ONE_WAY_KINEMATIC:
_body_type = E_BodyType.KINEMATIC_BODY
_test_jump_one_way_corner = false
yield(_start_jump_one_way(), "completed")
OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_RIGID:
_body_type = E_BodyType.RIGID_BODY
_test_jump_one_way_corner = true
yield(_start_jump_one_way(), "completed")
OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_KINEMATIC:
_body_type = E_BodyType.KINEMATIC_BODY
_test_jump_one_way_corner = true
yield(_start_jump_one_way(), "completed")
OPTION_TEST_CASE_FALL_ONE_WAY_KINEMATIC:
_body_type = E_BodyType.KINEMATIC_BODY
yield(_start_fall_one_way(), "completed")
_:
Log.print_error("Invalid test case.")
func _test_all():
Log.print_log("* TESTING ALL...")
# RigidBody tests.
yield(_start_test_case(OPTION_TEST_CASE_JUMP_ONE_WAY_RIGID), "completed")
yield(_start_test_case(OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_RIGID), "completed")
# KinematicBody tests.
yield(_start_test_case(OPTION_TEST_CASE_JUMP_ONE_WAY_KINEMATIC), "completed")
yield(_start_test_case(OPTION_TEST_CASE_JUMP_ONE_WAY_CORNER_KINEMATIC), "completed")
yield(_start_test_case(OPTION_TEST_CASE_FALL_ONE_WAY_KINEMATIC), "completed")
Log.print_log("* Done.")
func _set_result(test_passed):
var result = ""
if test_passed:
result = "PASSED"
else:
result = "FAILED"
if not test_passed and not _failed_reason.empty():
result += _failed_reason
else:
result += "."
Log.print_log("Test %s" % result)
func _start_test():
if _extra_body:
_body_parent.remove_child(_extra_body)
_extra_body.queue_free()
_extra_body = null
._start_test()
if _test_jump_one_way:
_test_jump_one_way = false
_moving_body._initial_velocity = Vector2(600, -1000)
if _test_jump_one_way_corner:
_moving_body.position.x = 147.0
$JumpTargetArea2D.visible = true
$JumpTargetArea2D/CollisionShape2D.disabled = false
if _test_fall_one_way:
_test_fall_one_way = false
_moving_body.position.y = 350.0
_moving_body._gravity_force = 100.0
_moving_body._motion_speed = 0.0
_moving_body._jump_force = 0.0
_extra_body = _moving_body.duplicate()
_extra_body._gravity_force = 100.0
_extra_body._motion_speed = 0.0
_extra_body._jump_force = 0.0
_extra_body.position -= Vector2(0.0, 200.0)
_body_parent.add_child(_extra_body)
$FallTargetArea2D.visible = true
$FallTargetArea2D/CollisionShape2D.disabled = false
func _start_jump_one_way():
_test_jump_one_way = true
_start_test()
yield(start_timer(1.5), "timeout")
if is_timer_canceled():
return
_finalize_jump_one_way()
func _start_fall_one_way():
_test_fall_one_way = true
_start_test()
yield(start_timer(1.0), "timeout")
if is_timer_canceled():
return
_finalize_fall_one_way()
func _finalize_jump_one_way():
var passed = true
if not $JumpTargetArea2D.overlaps_body(_moving_body):
passed = false
_failed_reason = ": the body wasn't able to jump all the way through."
_set_result(passed)
$JumpTargetArea2D.visible = false
$JumpTargetArea2D/CollisionShape2D.disabled = true
func _finalize_fall_one_way():
var passed = true
if $FallTargetArea2D.overlaps_body(_moving_body):
passed = false
_failed_reason = ": the body was pushed through the one-way collision."
_set_result(passed)
$FallTargetArea2D.visible = false
$FallTargetArea2D/CollisionShape2D.disabled = true

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=11 format=2]
[gd_scene load_steps=12 format=2]
[ext_resource path="res://tests/functional/test_character_tilemap.gd" type="Script" id=1]
[ext_resource path="res://tests/test_options.tscn" type="PackedScene" id=3]
@@ -11,7 +11,7 @@
friction = 0.0
[sub_resource type="RectangleShape2D" id=2]
extents = Vector2( 16, 32 )
extents = Vector2( 16, 31.9 )
[sub_resource type="RectangleShape2D" id=3]
extents = Vector2( 16, 24 )
@@ -19,6 +19,9 @@ extents = Vector2( 16, 24 )
[sub_resource type="RayShape2D" id=4]
length = 24.0
[sub_resource type="CircleShape2D" id=5]
radius = 16.0
[node name="Test" type="Node2D"]
script = ExtResource( 1 )
@@ -96,6 +99,22 @@ shape = SubResource( 4 )
position = Vector2( 16, 8 )
shape = SubResource( 4 )
[node name="JumpTargetArea2D" type="Area2D" parent="."]
visible = false
position = Vector2( 810, 390 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="JumpTargetArea2D"]
shape = SubResource( 5 )
disabled = true
[node name="FallTargetArea2D" type="Area2D" parent="."]
visible = false
position = Vector2( 250, 480 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="FallTargetArea2D"]
shape = SubResource( 5 )
disabled = true
[node name="StaticSceneFlat" parent="." instance=ExtResource( 4 )]
position = Vector2( 0, 12 )

View File

@@ -18,6 +18,8 @@ const OFFSET_RANGE = 120.0
export(Vector2) var offset = Vector2.ZERO
onready var options = $Options
var _update_collision = false
var _collision_test_index = 0
var _current_offset = Vector2.ZERO
@@ -27,21 +29,21 @@ var _collision_shapes = []
func _ready():
_initialize_collision_shapes()
$Options.add_menu_item(OPTION_TYPE_RECTANGLE)
$Options.add_menu_item(OPTION_TYPE_SPHERE)
$Options.add_menu_item(OPTION_TYPE_CAPSULE)
$Options.add_menu_item(OPTION_TYPE_CONVEX_POLYGON)
$Options.add_menu_item(OPTION_TYPE_CONCAVE_SEGMENTS)
options.add_menu_item(OPTION_TYPE_RECTANGLE)
options.add_menu_item(OPTION_TYPE_SPHERE)
options.add_menu_item(OPTION_TYPE_CAPSULE)
options.add_menu_item(OPTION_TYPE_CONVEX_POLYGON)
options.add_menu_item(OPTION_TYPE_CONCAVE_SEGMENTS)
$Options.add_menu_item(OPTION_SHAPE_RECTANGLE, true, true)
$Options.add_menu_item(OPTION_SHAPE_SPHERE, true, true)
$Options.add_menu_item(OPTION_SHAPE_CAPSULE, true, true)
$Options.add_menu_item(OPTION_SHAPE_CONVEX_POLYGON, true, true)
$Options.add_menu_item(OPTION_SHAPE_CONCAVE_POLYGON, true, true)
$Options.add_menu_item(OPTION_SHAPE_CONCAVE_SEGMENTS, true, true)
options.add_menu_item(OPTION_SHAPE_RECTANGLE, true, true)
options.add_menu_item(OPTION_SHAPE_SPHERE, true, true)
options.add_menu_item(OPTION_SHAPE_CAPSULE, true, true)
options.add_menu_item(OPTION_SHAPE_CONVEX_POLYGON, true, true)
options.add_menu_item(OPTION_SHAPE_CONCAVE_POLYGON, true, true)
options.add_menu_item(OPTION_SHAPE_CONCAVE_SEGMENTS, true, true)
$Options.connect("option_selected", self, "_on_option_selected")
$Options.connect("option_changed", self, "_on_option_changed")
options.connect("option_selected", self, "_on_option_selected")
options.connect("option_changed", self, "_on_option_changed")
yield(start_timer(0.5), "timeout")
if is_timer_canceled():

View File

@@ -11,6 +11,8 @@ const OPTION_TEST_CASE_CHANGE_POSITIONS = "Test case/Set body positions after ad
const BOX_SIZE = Vector2(64, 64)
onready var options = $Options
var _update_joint = false
var _selected_joint = null
@@ -25,23 +27,24 @@ var _joint_types = {}
func _ready():
for joint_index in $Joints.get_child_count():
var joint_node = $Joints.get_child(joint_index)
var joints = $Joints
for joint_index in joints.get_child_count():
var joint_node = joints.get_child(joint_index)
joint_node.visible = false
var joint_name = joint_node.name
var joint_short = joint_name.substr(0, joint_name.length() - 7)
var option_name = OPTION_JOINT_TYPE % [joint_short, joint_index + 1]
$Options.add_menu_item(option_name)
options.add_menu_item(option_name)
_joint_types[option_name] = joint_node
$Options.add_menu_item(OPTION_TEST_CASE_BODIES_COLLIDE, true, false)
$Options.add_menu_item(OPTION_TEST_CASE_WORLD_ATTACHMENT, true, false)
$Options.add_menu_item(OPTION_TEST_CASE_DYNAMIC_ATTACHMENT, true, false)
$Options.add_menu_item(OPTION_TEST_CASE_DESTROY_BODY, true, false)
$Options.add_menu_item(OPTION_TEST_CASE_CHANGE_POSITIONS, true, false)
options.add_menu_item(OPTION_TEST_CASE_BODIES_COLLIDE, true, false)
options.add_menu_item(OPTION_TEST_CASE_WORLD_ATTACHMENT, true, false)
options.add_menu_item(OPTION_TEST_CASE_DYNAMIC_ATTACHMENT, true, false)
options.add_menu_item(OPTION_TEST_CASE_DESTROY_BODY, true, false)
options.add_menu_item(OPTION_TEST_CASE_CHANGE_POSITIONS, true, false)
$Options.connect("option_selected", self, "_on_option_selected")
$Options.connect("option_changed", self, "_on_option_changed")
options.connect("option_selected", self, "_on_option_selected")
options.connect("option_changed", self, "_on_option_changed")
_selected_joint = _joint_types.values()[0]
_update_joint = true

View File

@@ -2,24 +2,40 @@ extends Test
tool
signal all_tests_done()
signal test_done()
const OPTION_OBJECT_TYPE_RIGIDBODY = "Object type/Rigid body (1)"
const OPTION_OBJECT_TYPE_KINEMATIC = "Object type/Kinematic body (2)"
const OPTION_TEST_CASE_ALL_ANGLES = "Test case/Around the clock (0)"
const OPTION_TEST_CASE_ALL = "Test Cases/TEST ALL (0)"
const OPTION_TEST_CASE_ALL_RIGID = "Test Cases/All Rigid Body tests"
const OPTION_TEST_CASE_ALL_KINEMATIC = "Test Cases/All Kinematic Body tests"
const OPTION_TEST_CASE_ALL_ANGLES_RIGID = "Test Cases/Around the clock (Rigid Body)"
const OPTION_TEST_CASE_ALL_ANGLES_KINEMATIC = "Test Cases/Around the clock (Kinematic Body)"
const OPTION_TEST_CASE_MOVING_PLATFORM_RIGID = "Test Cases/Moving Platform (Rigid Body)"
const OPTION_TEST_CASE_MOVING_PLATFORM_KINEMATIC = "Test Cases/Moving Platform (Kinematic Body)"
const TEST_ALL_ANGLES_STEP = 15.0
const TEST_ALL_ANGLES_MAX = 344.0
export(float, 32, 128, 0.1) var _platform_size = 64.0 setget _set_platform_size
export(float, 0, 360, 0.1) var _platform_angle = 0.0 setget _set_platform_angle
export(float) var _platform_speed = 0.0
export(float, 0, 360, 0.1) var _body_angle = 0.0 setget _set_rigidbody_angle
export(Vector2) var _body_velocity = Vector2(400.0, 0.0)
export(bool) var _use_kinematic_body = false
onready var options = $Options
var _rigid_body_template = null
var _kinematic_body_template = null
var _moving_body = null
var _platform_template = null
var _platform_body = null
var _platform_velocity = Vector2.ZERO
var _contact_detected = false
var _target_entered = false
var _test_passed = false
@@ -31,12 +47,18 @@ var _lock_controls = false
func _ready():
if not Engine.editor_hint:
$Options.add_menu_item(OPTION_OBJECT_TYPE_RIGIDBODY, true, not _use_kinematic_body, true)
$Options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC, true, _use_kinematic_body, true)
options.add_menu_item(OPTION_OBJECT_TYPE_RIGIDBODY, true, not _use_kinematic_body, true)
options.add_menu_item(OPTION_OBJECT_TYPE_KINEMATIC, true, _use_kinematic_body, true)
$Options.add_menu_item(OPTION_TEST_CASE_ALL_ANGLES)
options.add_menu_item(OPTION_TEST_CASE_ALL)
options.add_menu_item(OPTION_TEST_CASE_ALL_RIGID)
options.add_menu_item(OPTION_TEST_CASE_ALL_KINEMATIC)
options.add_menu_item(OPTION_TEST_CASE_ALL_ANGLES_RIGID)
options.add_menu_item(OPTION_TEST_CASE_ALL_ANGLES_KINEMATIC)
options.add_menu_item(OPTION_TEST_CASE_MOVING_PLATFORM_RIGID)
options.add_menu_item(OPTION_TEST_CASE_MOVING_PLATFORM_KINEMATIC)
$Options.connect("option_selected", self, "_on_option_selected")
options.connect("option_selected", self, "_on_option_selected")
$Controls/PlatformSize/HSlider.value = _platform_size
$Controls/PlatformAngle/HSlider.value = _platform_angle
@@ -51,6 +73,9 @@ func _ready():
_kinematic_body_template = $KinematicBody2D
remove_child(_kinematic_body_template)
_platform_template = $OneWayKinematicBody2D
remove_child(_platform_template)
_start_test()
@@ -60,20 +85,25 @@ func _process(_delta):
_reset_test(false)
func _physics_process(_delta):
func _physics_process(delta):
if not Engine.editor_hint:
if _moving_body and _use_kinematic_body:
_moving_body.move_and_slide(_body_velocity)
if _moving_body.get_slide_count() > 0:
var colliding_body = _moving_body.get_slide_collision(0).collider
_on_contact_detected(colliding_body)
if _moving_body and not _contact_detected:
if _use_kinematic_body:
var collision = _moving_body.move_and_collide(_body_velocity * delta, false)
if collision:
var colliding_body = collision.collider
_on_contact_detected(colliding_body)
if _platform_body and _platform_velocity != Vector2.ZERO:
var motion = _platform_velocity * delta
_platform_body.global_position += motion
func _input(event):
var key_event = event as InputEventKey
if key_event and not key_event.pressed:
if key_event.scancode == KEY_0:
_on_option_selected(OPTION_TEST_CASE_ALL_ANGLES)
_on_option_selected(OPTION_TEST_CASE_ALL)
if key_event.scancode == KEY_1:
_on_option_selected(OPTION_OBJECT_TYPE_RIGIDBODY)
elif key_event.scancode == KEY_2:
@@ -84,41 +114,49 @@ func _exit_tree():
if not Engine.editor_hint:
_rigid_body_template.free()
_kinematic_body_template.free()
_platform_template.free()
func _set_platform_size(value):
func _set_platform_size(value, reset = true):
if _lock_controls:
return
if value == _platform_size:
return
_platform_size = value
if is_inside_tree():
$OneWayRigidBody2D/CollisionShape2D.shape.extents.x = value
if not Engine.editor_hint:
# Bug: need to re-add when changing shape.
var platform = $OneWayRigidBody2D
var child_index = platform.get_index()
remove_child(platform)
add_child(platform)
move_child(platform, child_index)
_reset_test()
if Engine.editor_hint:
$OneWayKinematicBody2D/CollisionShape2D.shape.extents.x = value
else:
var platform_collision = _platform_template.get_child(0)
platform_collision.shape.extents.x = value
if _platform_body:
# Bug: need to re-add when changing shape.
var child_index = _platform_body.get_index()
remove_child(_platform_body)
add_child(_platform_body)
move_child(_platform_body, child_index)
if reset:
_reset_test()
func _set_platform_angle(value):
func _set_platform_angle(value, reset = true):
if _lock_controls:
return
if value == _platform_angle:
return
_platform_angle = value
if is_inside_tree():
$OneWayRigidBody2D.rotation = deg2rad(value)
if not Engine.editor_hint:
_reset_test()
if Engine.editor_hint:
$OneWayKinematicBody2D.rotation = deg2rad(value)
else:
if _platform_body:
_platform_body.rotation = deg2rad(value)
_platform_template.rotation = deg2rad(value)
if reset:
_reset_test()
func _set_rigidbody_angle(value):
func _set_rigidbody_angle(value, reset = true):
if _lock_controls:
return
if value == _body_angle:
@@ -133,7 +171,8 @@ func _set_rigidbody_angle(value):
_moving_body.rotation = deg2rad(value)
_rigid_body_template.rotation = deg2rad(value)
_kinematic_body_template.rotation = deg2rad(value)
_reset_test()
if reset:
_reset_test()
func _on_option_selected(option):
@@ -144,14 +183,130 @@ func _on_option_selected(option):
OPTION_OBJECT_TYPE_RIGIDBODY:
_use_kinematic_body = false
_reset_test()
OPTION_TEST_CASE_ALL_ANGLES:
OPTION_TEST_CASE_ALL:
_test_all()
OPTION_TEST_CASE_ALL_RIGID:
_test_all_rigid_body()
OPTION_TEST_CASE_ALL_KINEMATIC:
_test_all_kinematic_body()
OPTION_TEST_CASE_ALL_ANGLES_RIGID:
_use_kinematic_body = false
_test_all_angles = true
_reset_test(false)
OPTION_TEST_CASE_ALL_ANGLES_KINEMATIC:
_use_kinematic_body = true
_test_all_angles = true
_reset_test(false)
OPTION_TEST_CASE_MOVING_PLATFORM_RIGID:
_use_kinematic_body = false
_test_moving_platform()
OPTION_TEST_CASE_MOVING_PLATFORM_KINEMATIC:
_use_kinematic_body = true
_test_moving_platform()
func _start_test_case(option):
Log.print_log("* Starting " + option)
_on_option_selected(option)
yield(self, "all_tests_done")
func _wait_for_test():
_reset_test()
yield(self, "test_done")
func _test_all_rigid_body():
Log.print_log("* All RigidBody test cases...")
_set_platform_size(64.0, false)
_set_rigidbody_angle(0.0, false)
yield(_start_test_case(OPTION_TEST_CASE_ALL_ANGLES_RIGID), "completed")
_set_platform_size(64.0, false)
_set_rigidbody_angle(45.0, false)
yield(_start_test_case(OPTION_TEST_CASE_ALL_ANGLES_RIGID), "completed")
_set_platform_size(32.0, false)
_set_rigidbody_angle(45.0, false)
yield(_start_test_case(OPTION_TEST_CASE_ALL_ANGLES_RIGID), "completed")
yield(_start_test_case(OPTION_TEST_CASE_MOVING_PLATFORM_RIGID), "completed")
func _test_all_kinematic_body():
Log.print_log("* All KinematicBody test cases...")
_set_platform_size(64.0, false)
_set_rigidbody_angle(0.0, false)
yield(_start_test_case(OPTION_TEST_CASE_ALL_ANGLES_KINEMATIC), "completed")
_set_platform_size(64.0, false)
_set_rigidbody_angle(45.0, false)
yield(_start_test_case(OPTION_TEST_CASE_ALL_ANGLES_KINEMATIC), "completed")
_set_platform_size(32.0, false)
_set_rigidbody_angle(45.0, false)
yield(_start_test_case(OPTION_TEST_CASE_ALL_ANGLES_KINEMATIC), "completed")
yield(_start_test_case(OPTION_TEST_CASE_MOVING_PLATFORM_KINEMATIC), "completed")
func _test_moving_platform():
Log.print_log("* Start moving platform tests")
Log.print_log("* Platform moving away from body...")
_set_platform_size(64.0, false)
_set_rigidbody_angle(0.0, false)
_platform_speed = 50.0
_set_platform_angle(90.0, false)
yield(_wait_for_test(), "completed")
_set_platform_angle(-90.0, false)
yield(_wait_for_test(), "completed")
Log.print_log("* Platform moving towards body...")
_set_platform_size(64.0, false)
_set_rigidbody_angle(0.0, false)
_platform_speed = -50.0
_set_platform_angle(90.0, false)
yield(_wait_for_test(), "completed")
_set_platform_angle(-90.0, false)
yield(_wait_for_test(), "completed")
_platform_speed = 0.0
emit_signal("all_tests_done")
func _test_all():
Log.print_log("* TESTING ALL...")
yield(_test_all_rigid_body(), "completed")
yield(_test_all_kinematic_body(), "completed")
Log.print_log("* Done.")
func _start_test():
var test_label = "Testing: "
var platform_angle = _platform_template.rotation
if _platform_body:
platform_angle = _platform_body.rotation
remove_child(_platform_body)
_platform_body.queue_free()
_platform_body = null
_platform_body = _platform_template.duplicate()
_platform_body.rotation = platform_angle
add_child(_platform_body)
if _use_kinematic_body:
test_label += _kinematic_body_template.name
_moving_body = _kinematic_body_template.duplicate()
@@ -162,6 +317,14 @@ func _start_test():
_moving_body.connect("body_entered", self, "_on_contact_detected")
add_child(_moving_body)
if _platform_speed != 0.0:
var platform_pos = _platform_body.global_position
var body_pos = _moving_body.global_position
var dir = (platform_pos - body_pos).normalized()
_platform_velocity = dir * _platform_speed
else:
_platform_velocity = Vector2.ZERO
if _test_all_angles:
test_label += " - All angles"
@@ -187,9 +350,10 @@ func _reset_test(cancel_test = true):
if cancel_test:
Log.print_log("*** Stop around the clock tests")
_test_all_angles = false
emit_signal("all_tests_done")
else:
Log.print_log("*** Start around the clock tests")
$OneWayRigidBody2D.rotation = deg2rad(_platform_angle)
_platform_body.rotation = deg2rad(_platform_angle)
_lock_controls = true
$Controls/PlatformAngle/HSlider.value = _platform_angle
_lock_controls = false
@@ -204,16 +368,17 @@ func _next_test(force_start = false):
_moving_body = null
if _test_all_angles:
var angle = rad2deg($OneWayRigidBody2D.rotation)
var angle = rad2deg(_platform_body.rotation)
if angle >= _platform_angle + TEST_ALL_ANGLES_MAX:
$OneWayRigidBody2D.rotation = deg2rad(_platform_angle)
_platform_body.rotation = deg2rad(_platform_angle)
_lock_controls = true
$Controls/PlatformAngle/HSlider.value = _platform_angle
_lock_controls = false
_test_all_angles = false
Log.print_log("*** Done all angles")
else:
angle = _platform_angle + _test_step * TEST_ALL_ANGLES_STEP
$OneWayRigidBody2D.rotation = deg2rad(angle)
_platform_body.rotation = deg2rad(angle)
_lock_controls = true
$Controls/PlatformAngle/HSlider.value = angle
_lock_controls = false
@@ -243,7 +408,7 @@ func _on_target_entered(_body):
func _should_collide():
var platform_rotation = round(rad2deg($OneWayRigidBody2D.rotation))
var platform_rotation = round(rad2deg(_platform_body.rotation))
var angle = fposmod(platform_rotation, 360)
return angle > 180
@@ -258,8 +423,15 @@ func _on_timeout():
yield(get_tree().create_timer(0.5), "timeout")
var was_all_angles = _test_all_angles
_next_test()
emit_signal("test_done")
if was_all_angles and not _test_all_angles:
emit_signal("all_tests_done")
func _set_result():
var result = ""
@@ -272,7 +444,7 @@ func _set_result():
$LabelResult.text = result
var platform_angle = rad2deg($OneWayRigidBody2D.rotation)
var platform_angle = rad2deg(_platform_body.rotation)
result += ": size=%.1f, angle=%.1f, body angle=%.1f" % [_platform_size, platform_angle, _body_angle]
Log.print_log("Test %s" % result)

View File

@@ -205,11 +205,10 @@ position = Vector2( 724, 300 )
[node name="CollisionShape2D" type="CollisionShape2D" parent="TargetArea2D"]
shape = SubResource( 1 )
[node name="OneWayRigidBody2D" type="RigidBody2D" parent="."]
[node name="OneWayKinematicBody2D" type="KinematicBody2D" parent="."]
position = Vector2( 512, 300 )
mode = 3
[node name="CollisionShape2D" type="CollisionShape2D" parent="OneWayRigidBody2D"]
[node name="CollisionShape2D" type="CollisionShape2D" parent="OneWayKinematicBody2D"]
shape = SubResource( 2 )
one_way_collision = true

View File

@@ -7,11 +7,13 @@ const OPTION_TYPE_SPHERE = "Shape type/Sphere"
const OPTION_TYPE_CAPSULE = "Shape type/Capsule"
const OPTION_TYPE_CONVEX_POLYGON = "Shape type/Convex Polygon"
const OPTION_TYPE_CONCAVE_POLYGON = "Shape type/Concave Polygon"
export(Array) var spawns = Array()
export(Array) var spawns = Array()
export(int) var spawn_count = 100
export(int, 1, 10) var spawn_multiplier = 5
onready var options = $Options
var _object_templates = []
@@ -20,19 +22,20 @@ func _ready():
if is_timer_canceled():
return
while $DynamicShapes.get_child_count():
var type_node = $DynamicShapes.get_child(0)
var dynamic_shapes = $DynamicShapes
while dynamic_shapes.get_child_count():
var type_node = dynamic_shapes.get_child(0)
type_node.position = Vector2.ZERO
_object_templates.push_back(type_node)
$DynamicShapes.remove_child(type_node)
dynamic_shapes.remove_child(type_node)
$Options.add_menu_item(OPTION_TYPE_ALL)
$Options.add_menu_item(OPTION_TYPE_RECTANGLE)
$Options.add_menu_item(OPTION_TYPE_SPHERE)
$Options.add_menu_item(OPTION_TYPE_CAPSULE)
$Options.add_menu_item(OPTION_TYPE_CONVEX_POLYGON)
$Options.add_menu_item(OPTION_TYPE_CONCAVE_POLYGON)
$Options.connect("option_selected", self, "_on_option_selected")
options.add_menu_item(OPTION_TYPE_ALL)
options.add_menu_item(OPTION_TYPE_RECTANGLE)
options.add_menu_item(OPTION_TYPE_SPHERE)
options.add_menu_item(OPTION_TYPE_CAPSULE)
options.add_menu_item(OPTION_TYPE_CONVEX_POLYGON)
options.add_menu_item(OPTION_TYPE_CONCAVE_POLYGON)
options.connect("option_selected", self, "_on_option_selected")
_start_all_types()

View File

@@ -3,6 +3,9 @@ extends KinematicBody2D
var _initial_velocity = Vector2.ZERO
var _constant_velocity = Vector2.ZERO
var _motion_speed = 400.0
var _gravity_force = 50.0
var _jump_force = 1000.0
var _velocity = Vector2.ZERO
var _snap = Vector2.ZERO
var _floor_max_angle = 45.0
@@ -24,12 +27,12 @@ func _physics_process(_delta):
# Handle horizontal controls.
if Input.is_action_pressed("character_left"):
if position.x > 0.0:
_velocity.x = -400.0
_velocity.x = -_motion_speed
_keep_velocity = false
_constant_velocity = Vector2.ZERO
elif Input.is_action_pressed("character_right"):
if position.x < 1024.0:
_velocity.x = 400.0
_velocity.x = _motion_speed
_keep_velocity = false
_constant_velocity = Vector2.ZERO
@@ -38,15 +41,15 @@ func _physics_process(_delta):
if not _jumping and Input.is_action_just_pressed("character_jump"):
# Start jumping.
_jumping = true
_velocity.y = -1000.0
elif not _jumping:
# Apply velocity when standing for floor detection.
_velocity.y = 10.0
else:
# Apply gravity and get jump ready.
_jumping = false
_velocity.y += 50.0
_velocity.y = -_jump_force
# Always apply gravity for floor detection.
_velocity.y += _gravity_force
var snap = _snap if not _jumping else Vector2.ZERO
var max_angle = deg2rad(_floor_max_angle)
move_and_slide_with_snap(_velocity, snap, Vector2.UP, _stop_on_slope, 4, max_angle)
_velocity = move_and_slide_with_snap(_velocity, snap, Vector2.UP, _stop_on_slope, 4, max_angle)
# Get next jump ready.
if _jumping:
_jumping = false

View File

@@ -3,6 +3,9 @@ extends RigidBody2D
var _initial_velocity = Vector2.ZERO
var _constant_velocity = Vector2.ZERO
var _motion_speed = 400.0
var _gravity_force = 50.0
var _jump_force = 1000.0
var _velocity = Vector2.ZERO
var _on_floor = false
var _jumping = false
@@ -22,12 +25,12 @@ func _physics_process(_delta):
# Handle horizontal controls.
if Input.is_action_pressed("character_left"):
if position.x > 0.0:
_velocity.x = -400.0
_velocity.x = -_motion_speed
_keep_velocity = false
_constant_velocity = Vector2.ZERO
elif Input.is_action_pressed("character_right"):
if position.x < 1024.0:
_velocity.x = 400.0
_velocity.x = _motion_speed
_keep_velocity = false
_constant_velocity = Vector2.ZERO
@@ -36,14 +39,14 @@ func _physics_process(_delta):
if not _jumping and Input.is_action_just_pressed("character_jump"):
# Start jumping.
_jumping = true
_velocity.y = -1000.0
_velocity.y = -_jump_force
elif not _jumping:
# Apply velocity when standing for floor detection.
_velocity.y = 10.0
# Reset gravity.
_velocity.y = 0.0
else:
# Apply gravity and get jump ready.
_velocity.y += _gravity_force
_jumping = false
_velocity.y += 50.0
linear_velocity = _velocity

View File

@@ -4,6 +4,11 @@ extends ScrollContainer
export(bool) var auto_scroll = false setget set_auto_scroll
func _ready():
var scrollbar = get_v_scrollbar()
scrollbar.connect("scrolling", self, "_on_scrolling")
func _process(_delta):
if auto_scroll:
var scrollbar = get_v_scrollbar()
@@ -12,3 +17,8 @@ func _process(_delta):
func set_auto_scroll(value):
auto_scroll = value
func _on_scrolling():
auto_scroll = false
$"../CheckBoxScroll".pressed = false