Răsfoiți Sursa

Add One Way Collision for 2D Physics tests

PouleyKetchoupp 4 ani în urmă
părinte
comite
6a738c1ede

+ 4 - 0
2d/physics_tests/tests.gd

@@ -18,6 +18,10 @@ var _tests = [
 		"id": "Functional Tests/Collision Pairs",
 		"path": "res://tests/functional/test_collision_pairs.tscn",
 	},
+	{
+		"id": "Functional Tests/One Way Collision",
+		"path": "res://tests/functional/test_one_way_collision.tscn",
+	},
 	{
 		"id": "Functional Tests/Joints",
 		"path": "res://tests/functional/test_joints.tscn",

+ 277 - 0
2d/physics_tests/tests/functional/test_one_way_collision.gd

@@ -0,0 +1,277 @@
+extends Test
+tool
+
+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 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, 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
+
+var _rigid_body_template = null
+var _kinematic_body_template = null
+var _moving_body = null
+
+var _contact_detected = false
+var _target_entered = false
+var _test_passed = false
+var _test_step = 0
+
+var _test_all_angles = false
+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_TEST_CASE_ALL_ANGLES)
+
+		$Options.connect("option_selected", self, "_on_option_selected")
+
+		$Controls/PlatformSize/HSlider.value = _platform_size
+		$Controls/PlatformAngle/HSlider.value = _platform_angle
+		$Controls/BodyAngle/HSlider.value = _body_angle
+
+		$TargetArea2D.connect("body_entered", self, "_on_target_entered")
+		$Timer.connect("timeout", self, "_on_timeout")
+
+		_rigid_body_template = $RigidBody2D
+		remove_child(_rigid_body_template)
+
+		_kinematic_body_template = $KinematicBody2D
+		remove_child(_kinematic_body_template)
+
+		_start_test()
+
+
+func _process(_delta):
+	if not Engine.editor_hint:
+		if Input.is_action_just_pressed("ui_accept"):
+			_reset_test(false)
+
+
+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)
+
+
+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)
+		if key_event.scancode == KEY_1:
+			_on_option_selected(OPTION_OBJECT_TYPE_RIGIDBODY)
+		elif key_event.scancode == KEY_2:
+			_on_option_selected(OPTION_OBJECT_TYPE_KINEMATIC)
+
+
+func _exit_tree():
+	if not Engine.editor_hint:
+		_rigid_body_template.free()
+		_kinematic_body_template.free()
+
+
+func _set_platform_size(value):
+	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()
+
+
+func _set_platform_angle(value):
+	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()
+
+
+func _set_rigidbody_angle(value):
+	if _lock_controls:
+		return
+	if value == _body_angle:
+		return
+	_body_angle = value
+	if is_inside_tree():
+		if Engine.editor_hint:
+			$RigidBody2D.rotation = deg2rad(value)
+			$KinematicBody2D.rotation = deg2rad(value)
+		else:
+			if _moving_body:
+				_moving_body.rotation = deg2rad(value)
+			_rigid_body_template.rotation = deg2rad(value)
+			_kinematic_body_template.rotation = deg2rad(value)
+			_reset_test()
+
+
+func _on_option_selected(option):
+	match option:
+		OPTION_OBJECT_TYPE_KINEMATIC:
+			_use_kinematic_body = true
+			_reset_test()
+		OPTION_OBJECT_TYPE_RIGIDBODY:
+			_use_kinematic_body = false
+			_reset_test()
+		OPTION_TEST_CASE_ALL_ANGLES:
+			_test_all_angles = true
+			_reset_test(false)
+
+
+func _start_test():
+	var test_label = "Testing: "
+
+	if _use_kinematic_body:
+		test_label += _kinematic_body_template.name
+		_moving_body = _kinematic_body_template.duplicate()
+	else:
+		test_label += _rigid_body_template.name
+		_moving_body = _rigid_body_template.duplicate()
+		_moving_body.linear_velocity = _body_velocity
+		_moving_body.connect("body_entered", self, "_on_contact_detected")
+	add_child(_moving_body)
+
+	if _test_all_angles:
+		test_label += " - All angles"
+
+	$LabelTestType.text = test_label
+
+	_contact_detected = false
+	_target_entered = false
+	_test_passed = false
+	_test_step += 1
+
+	$Timer.start()
+
+	$LabelResult.text = "..."
+	$LabelResult.self_modulate = Color.white
+
+
+func _reset_test(cancel_test = true):
+	$Timer.stop()
+
+	_test_step = 0
+
+	if _test_all_angles:
+		if cancel_test:
+			Log.print_log("*** Stop around the clock tests")
+			_test_all_angles = false
+		else:
+			Log.print_log("*** Start around the clock tests")
+		$OneWayRigidBody2D.rotation = deg2rad(_platform_angle)
+		_lock_controls = true
+		$Controls/PlatformAngle/HSlider.value = _platform_angle
+		_lock_controls = false
+
+	_next_test(true)
+
+
+func _next_test(force_start = false):
+	if _moving_body:
+		remove_child(_moving_body)
+		_moving_body.queue_free()
+		_moving_body = null
+
+	if _test_all_angles:
+		var angle = rad2deg($OneWayRigidBody2D.rotation)
+		if angle >= _platform_angle + TEST_ALL_ANGLES_MAX:
+			$OneWayRigidBody2D.rotation = deg2rad(_platform_angle)
+			_lock_controls = true
+			$Controls/PlatformAngle/HSlider.value = _platform_angle
+			_lock_controls = false
+			Log.print_log("*** Done all angles")
+		else:
+			angle = _platform_angle + _test_step * TEST_ALL_ANGLES_STEP
+			$OneWayRigidBody2D.rotation = deg2rad(angle)
+			_lock_controls = true
+			$Controls/PlatformAngle/HSlider.value = angle
+			_lock_controls = false
+			_start_test()
+	elif force_start:
+		_start_test()
+
+
+func _on_contact_detected(_body):
+	if _contact_detected or _target_entered:
+		return
+
+	_contact_detected = true
+	_test_passed = _should_collide()
+	_set_result()
+	_on_timeout()
+
+
+func _on_target_entered(_body):
+	if _contact_detected or _target_entered:
+		return
+
+	_target_entered = true
+	_test_passed = not _should_collide()
+	_set_result()
+	_on_timeout()
+
+
+func _should_collide():
+	var platform_rotation = round(rad2deg($OneWayRigidBody2D.rotation))
+
+	var angle = fposmod(platform_rotation, 360)
+	return angle > 180
+
+
+func _on_timeout():
+	if not _contact_detected and not _target_entered:
+		Log.print_log("Test TIMEOUT")
+		_set_result()
+
+	$Timer.stop()
+
+	yield(get_tree().create_timer(0.5), "timeout")
+
+	_next_test()
+
+
+func _set_result():
+	var result = ""
+	if _test_passed:
+		result = "PASSED"
+		$LabelResult.self_modulate = Color.green
+	else:
+		result = "FAILED"
+		$LabelResult.self_modulate = Color.red
+
+	$LabelResult.text = result
+
+	var platform_angle = rad2deg($OneWayRigidBody2D.rotation)
+
+	result += ": size=%.1f, angle=%.1f, body angle=%.1f" % [_platform_size, platform_angle, _body_angle]
+	Log.print_log("Test %s" % result)

+ 243 - 0
2d/physics_tests/tests/functional/test_one_way_collision.tscn

@@ -0,0 +1,243 @@
+[gd_scene load_steps=9 format=2]
+
+[ext_resource path="res://tests/functional/test_one_way_collision.gd" type="Script" id=1]
+[ext_resource path="res://icon.png" type="Texture" id=2]
+[ext_resource path="res://tests/test_options.tscn" type="PackedScene" id=3]
+[ext_resource path="res://utils/label_slider_value.gd" type="Script" id=4]
+[ext_resource path="res://utils/slider.gd" type="Script" id=5]
+
+[sub_resource type="RectangleShape2D" id=1]
+extents = Vector2( 32, 32 )
+
+[sub_resource type="RectangleShape2D" id=2]
+extents = Vector2( 64, 32 )
+
+[sub_resource type="RectangleShape2D" id=3]
+extents = Vector2( 32, 32 )
+
+[node name="Test" type="Node2D"]
+script = ExtResource( 1 )
+
+[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="Controls" type="VBoxContainer" parent="."]
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_left = 25.3619
+margin_top = 416.765
+margin_right = 265.362
+margin_bottom = 484.765
+custom_constants/separation = 10
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="PlatformSize" type="HBoxContainer" parent="Controls"]
+margin_right = 432.0
+margin_bottom = 16.0
+custom_constants/separation = 20
+alignment = 2
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="Label" type="Label" parent="Controls/PlatformSize"]
+margin_left = 8.0
+margin_top = 1.0
+margin_right = 92.0
+margin_bottom = 15.0
+text = "Platform size"
+
+[node name="HSlider" type="HSlider" parent="Controls/PlatformSize"]
+margin_left = 112.0
+margin_right = 312.0
+margin_bottom = 16.0
+rect_min_size = Vector2( 200, 0 )
+min_value = 32.0
+max_value = 128.0
+value = 64.0
+script = ExtResource( 5 )
+
+[node name="LabelValue" type="Label" parent="Controls/PlatformSize"]
+margin_left = 332.0
+margin_top = 1.0
+margin_right = 432.0
+margin_bottom = 15.0
+rect_min_size = Vector2( 100, 0 )
+text = "64.0"
+script = ExtResource( 4 )
+
+[node name="PlatformAngle" type="HBoxContainer" parent="Controls"]
+margin_top = 26.0
+margin_right = 432.0
+margin_bottom = 42.0
+custom_constants/separation = 20
+alignment = 2
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="Label" type="Label" parent="Controls/PlatformAngle"]
+margin_top = 1.0
+margin_right = 92.0
+margin_bottom = 15.0
+text = "Platform angle"
+
+[node name="HSlider" type="HSlider" parent="Controls/PlatformAngle"]
+margin_left = 112.0
+margin_right = 312.0
+margin_bottom = 16.0
+rect_min_size = Vector2( 200, 0 )
+max_value = 360.0
+script = ExtResource( 5 )
+snap_step = 5.0
+
+[node name="LabelValue" type="Label" parent="Controls/PlatformAngle"]
+margin_left = 332.0
+margin_top = 1.0
+margin_right = 432.0
+margin_bottom = 15.0
+rect_min_size = Vector2( 100, 0 )
+text = "0.0"
+script = ExtResource( 4 )
+
+[node name="BodyAngle" type="HBoxContainer" parent="Controls"]
+margin_top = 52.0
+margin_right = 432.0
+margin_bottom = 68.0
+custom_constants/separation = 20
+alignment = 2
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="Label" type="Label" parent="Controls/BodyAngle"]
+margin_left = 22.0
+margin_top = 1.0
+margin_right = 92.0
+margin_bottom = 15.0
+text = "Body angle"
+
+[node name="HSlider" type="HSlider" parent="Controls/BodyAngle"]
+margin_left = 112.0
+margin_right = 312.0
+margin_bottom = 16.0
+rect_min_size = Vector2( 200, 0 )
+max_value = 360.0
+script = ExtResource( 5 )
+snap_step = 5.0
+
+[node name="LabelValue" type="Label" parent="Controls/BodyAngle"]
+margin_left = 332.0
+margin_top = 1.0
+margin_right = 432.0
+margin_bottom = 15.0
+rect_min_size = Vector2( 100, 0 )
+text = "0.0"
+script = ExtResource( 4 )
+
+[node name="LabelResultTitle" type="Label" parent="."]
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+margin_left = 34.1273
+margin_top = 251.131
+margin_right = 88.1273
+margin_bottom = 265.131
+text = "RESULT: "
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="LabelResult" type="Label" parent="."]
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+margin_left = 34.1273
+margin_top = 266.131
+margin_right = 88.1273
+margin_bottom = 280.131
+text = "..."
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="LabelRestart" type="Label" parent="."]
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+margin_left = 34.1273
+margin_top = 304.841
+margin_right = 139.127
+margin_bottom = 318.841
+text = "SPACE - RESTART"
+align = 1
+valign = 1
+__meta__ = {
+"_edit_use_anchors_": false
+}
+
+[node name="Timer" type="Timer" parent="."]
+wait_time = 5.0
+one_shot = true
+
+[node name="TargetArea2D" type="Area2D" parent="."]
+position = Vector2( 724, 300 )
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="TargetArea2D"]
+shape = SubResource( 1 )
+
+[node name="OneWayRigidBody2D" type="RigidBody2D" parent="."]
+position = Vector2( 512, 300 )
+mode = 3
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="OneWayRigidBody2D"]
+shape = SubResource( 2 )
+one_way_collision = true
+
+[node name="RigidBody2D" type="RigidBody2D" parent="."]
+position = Vector2( 300, 300 )
+collision_mask = 2147483649
+gravity_scale = 0.0
+contacts_reported = 1
+contact_monitor = true
+
+[node name="Sprite" type="Sprite" parent="RigidBody2D"]
+self_modulate = Color( 1, 1, 1, 0.501961 )
+scale = Vector2( 0.5, 0.5 )
+texture = ExtResource( 2 )
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="RigidBody2D"]
+shape = SubResource( 3 )
+
+[node name="KinematicBody2D" type="KinematicBody2D" parent="."]
+position = Vector2( 300, 300 )
+collision_mask = 2147483649
+
+[node name="Sprite" type="Sprite" parent="KinematicBody2D"]
+self_modulate = Color( 1, 1, 1, 0.501961 )
+scale = Vector2( 0.5, 0.5 )
+texture = ExtResource( 2 )
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="KinematicBody2D"]
+shape = SubResource( 3 )
+[connection signal="value_changed" from="Controls/PlatformSize/HSlider" to="." method="_set_platform_size"]
+[connection signal="value_changed" from="Controls/PlatformAngle/HSlider" to="." method="_set_platform_angle"]
+[connection signal="value_changed" from="Controls/BodyAngle/HSlider" to="." method="_set_rigidbody_angle"]

+ 7 - 0
2d/physics_tests/utils/label_slider_value.gd

@@ -0,0 +1,7 @@
+extends Label
+tool
+
+
+func _process(_delta):
+	var slider = get_node("../HSlider")
+	text = "%.1f" % slider.value

+ 14 - 3
2d/physics_tests/utils/option_menu.gd

@@ -6,7 +6,7 @@ signal option_selected(item_path)
 signal option_changed(item_path, checked)
 
 
-func add_menu_item(item_path, checkbox = false, checked = false):
+func add_menu_item(item_path, checkbox = false, checked = false, radio = false):
 	var path_elements = item_path.split("/", false)
 	var path_element_count = path_elements.size()
 	assert(path_element_count > 0)
@@ -19,7 +19,10 @@ func add_menu_item(item_path, checkbox = false, checked = false):
 		popup = _add_popup(popup, path, popup_label)
 
 	var label = path_elements[path_element_count - 1]
-	if checkbox:
+	if radio:
+		popup.add_radio_check_item(label)
+		popup.set_item_checked(popup.get_item_count() - 1, checked)
+	elif checkbox:
 		popup.add_check_item(label)
 		popup.set_item_checked(popup.get_item_count() - 1, checked)
 	else:
@@ -52,7 +55,15 @@ func _add_popup(parent_popup, path, label):
 func _on_item_pressed(item_index, popup_menu, path):
 	var item_path = path + popup_menu.get_item_text(item_index)
 
-	if popup_menu.is_item_checkable(item_index):
+	if popup_menu.is_item_radio_checkable(item_index):
+		var checked = popup_menu.is_item_checked(item_index)
+		if not checked:
+			popup_menu.set_item_checked(item_index, true)
+			for other_index in popup_menu.get_item_count():
+				if other_index != item_index:
+					popup_menu.set_item_checked(other_index, false)
+			emit_signal("option_selected", item_path)
+	elif popup_menu.is_item_checkable(item_index):
 		var checked = not popup_menu.is_item_checked(item_index)
 		popup_menu.set_item_checked(item_index, checked)
 		emit_signal("option_changed", item_path, checked)

+ 11 - 0
2d/physics_tests/utils/slider.gd

@@ -0,0 +1,11 @@
+extends Slider
+
+
+export(float) var snap_step = 1.0
+
+
+func _process(_delta):
+	if Input.is_key_pressed(KEY_SHIFT):
+		step = 0.1
+	else:
+		step = snap_step