Rename to final folder name
This commit is contained in:
+222
@@ -0,0 +1,222 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5959760
|
||||
# Test Case Title : Check that force region (capsule) exerts point force
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
box_entity_found = ("Box was found in game", "Box COULD NOT be found in game")
|
||||
capsule_entity_found = ("Capsule was found in game", "Capsule COULD NOT be found in game")
|
||||
box_pos_found = ("Box position found", "Box position not found")
|
||||
capsule_pos_found = ("Capsule position found", "Capsule position not found")
|
||||
force_region_entered = ("Force region entered", "Force region never entered")
|
||||
force_exertion_predicted = ("Force exerted was predictable", "The force exerted WAS NOT predicted")
|
||||
box_fell = ("Box fell", "The box did not fall")
|
||||
box_was_pushed_x_z = ("Box moved positive X, Z", "Box DID NOT move in positive X, Z direction")
|
||||
box_no_y_movement = ("Box had no substantial Y movement", "Box HAD substantial Y movement")
|
||||
capsule_no_move = ("Capsule did not move", "Capsule DID move")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
time_out = ("Test did not time out", "Test DID time out")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_CapsuleShapedForce():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure point force from a capsule force region is exerted on rigid body objects.
|
||||
|
||||
Level Description:
|
||||
A cube (entity: Box) set above a capsule force region (entity: Capsule). The Capsule was assigned point force
|
||||
with magnitude set to 1000. The Box has been set for "gravity enabled"
|
||||
|
||||
Expected behavior:
|
||||
The Box will fall (due to gravity) into the Capsule's force region. The force region should exert the point
|
||||
force on the Box, applying a positive X and Z force of substantial magnitude.
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level / Enters game mode
|
||||
2) Retrieve entities
|
||||
3) Ensures that the test objects (Box and Capsule) are located
|
||||
3.5) set up variables and handlers for monitoring results
|
||||
4) Waits for the box to fall into the force region
|
||||
or for time out if something unexpected happens
|
||||
5) Logs results
|
||||
6) Closes the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Global constants
|
||||
CLOSE_ENOUGH = 0.001
|
||||
TIME_OUT = 1.5
|
||||
|
||||
# Base class
|
||||
class EntityBase:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = None
|
||||
self.initial_pos = None
|
||||
self.current_pos = None
|
||||
|
||||
# Box child class of EntityBase
|
||||
class Box(EntityBase):
|
||||
def __init__(self, name):
|
||||
EntityBase.__init__(self, name)
|
||||
self.triggered_pos = None
|
||||
self.fell = False
|
||||
self.force_observed = False
|
||||
|
||||
def check_for_fall(self):
|
||||
FALL_BUFFER = 0.2
|
||||
if not self.fell:
|
||||
self.fell = (
|
||||
self.initial_pos.z > self.current_pos.z + FALL_BUFFER
|
||||
and abs(self.initial_pos.x - self.current_pos.x) < CLOSE_ENOUGH
|
||||
and abs(self.initial_pos.y - self.current_pos.y) < CLOSE_ENOUGH
|
||||
)
|
||||
return self.fell
|
||||
|
||||
def check_for_force(self):
|
||||
FORCE_BUFFER = 0.2
|
||||
if not self.force_observed:
|
||||
self.force_observed = (
|
||||
self.current_pos.z > self.triggered_pos.z + FORCE_BUFFER
|
||||
and self.current_pos.x > self.triggered_pos.x
|
||||
and abs(self.triggered_pos.y - self.current_pos.y) < CLOSE_ENOUGH
|
||||
)
|
||||
return self.force_observed
|
||||
|
||||
# Force Region child class of EntityBase
|
||||
class ForceRegion(EntityBase):
|
||||
def __init__(self, name):
|
||||
EntityBase.__init__(self, name)
|
||||
self.expected_force_magnitude = None
|
||||
self.actual_force_vector = None
|
||||
self.actual_force_magnitude = None
|
||||
self.forced_entity = None
|
||||
self.triggered = False
|
||||
|
||||
# 1) Open level / Enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "ForceRegion_CapsuleShapedForce")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve entities
|
||||
box = Box("Box")
|
||||
box.id = general.find_game_entity(box.name)
|
||||
capsule = ForceRegion("Capsule")
|
||||
capsule.id = general.find_game_entity(capsule.name)
|
||||
|
||||
Report.critical_result(Tests.box_entity_found, box.id.IsValid())
|
||||
Report.critical_result(Tests.capsule_entity_found, capsule.id.IsValid())
|
||||
|
||||
# 3) Log positions for Box and Capsule
|
||||
box.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", box.id)
|
||||
capsule.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", capsule.id)
|
||||
box.current_pos = box.initial_pos
|
||||
capsule.current_pos = capsule.initial_pos
|
||||
|
||||
# validate and print positions to confirm objects were found
|
||||
Report.critical_result(Tests.box_pos_found, box.initial_pos is not None and not box.initial_pos.IsZero())
|
||||
Report.critical_result(
|
||||
Tests.capsule_pos_found, capsule.initial_pos is not None and not capsule.initial_pos.IsZero()
|
||||
)
|
||||
capsule.expected_force_magnitude = azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "GetMagnitude", capsule.id)
|
||||
|
||||
# 3.5) set up handler
|
||||
|
||||
# Force Region Event Handler
|
||||
def on_force_calculated(args):
|
||||
|
||||
# Only store data for first force region calculation
|
||||
if not capsule.triggered and capsule.id.Equal(args[0]):
|
||||
capsule.triggered = True
|
||||
capsule.forced_entity = args[1]
|
||||
capsule.actual_force_vector = args[2]
|
||||
capsule.actual_force_magnitude = args[3]
|
||||
if capsule.forced_entity.Equal(box.id):
|
||||
box.triggered_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", box.id)
|
||||
Report.info("Force Region exerted force on {}".format(box.name))
|
||||
|
||||
# Assign the handler
|
||||
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
handler.connect(None)
|
||||
handler.add_callback("OnCalculateNetForce", on_force_calculated)
|
||||
|
||||
def done_collecting_results():
|
||||
# Update entity positions
|
||||
capsule.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", capsule.id)
|
||||
box.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", box.id)
|
||||
# Check for three "test complete" conditions
|
||||
# ! Careful ordering for logic short circuiting. DO NOT SWAP ORDER !
|
||||
return box.check_for_fall() and capsule.triggered and box.check_for_force()
|
||||
|
||||
# 4) wait for force region entry or time out
|
||||
test_completed = helper.wait_for_condition(done_collecting_results, TIME_OUT)
|
||||
Report.critical_result(Tests.time_out, test_completed)
|
||||
|
||||
# 5) Report findings
|
||||
Report.result(Tests.box_fell, box.fell)
|
||||
Report.result(Tests.force_region_entered, capsule.triggered)
|
||||
Report.result(
|
||||
Tests.force_exertion_predicted,
|
||||
abs(capsule.expected_force_magnitude - capsule.actual_force_magnitude) < CLOSE_ENOUGH,
|
||||
)
|
||||
Report.result(Tests.box_was_pushed_x_z, box.force_observed)
|
||||
Report.result(Tests.box_no_y_movement, abs(box.initial_pos.y - box.current_pos.y) < CLOSE_ENOUGH)
|
||||
Report.result(Tests.capsule_no_move, capsule.initial_pos.IsClose(capsule.current_pos))
|
||||
|
||||
# Collected Data Dump
|
||||
Report.info("******* Collected Data *******")
|
||||
Report.info("Entity: {}".format(box.name))
|
||||
Report.info_vector3(box.initial_pos, " Initial Position:")
|
||||
Report.info_vector3(box.triggered_pos, " Trigger Position:")
|
||||
Report.info_vector3(box.current_pos, " Final Position:")
|
||||
Report.info(" Fell: {}".format(box.fell))
|
||||
Report.info(" Force Observed: {}".format(box.force_observed))
|
||||
Report.info("******************************")
|
||||
Report.info("Entity: {}".format(capsule.name))
|
||||
Report.info_vector3(capsule.initial_pos, " Initial Position:")
|
||||
Report.info_vector3(capsule.current_pos, " Final Position:")
|
||||
Report.info(" Expected Force Magnitude: {:.2f}".format(capsule.expected_force_magnitude))
|
||||
Report.info_vector3(capsule.actual_force_vector, " Actual Force Vector:", capsule.actual_force_magnitude)
|
||||
Report.info(" Triggered: {}".format(capsule.triggered))
|
||||
Report.info(
|
||||
" Triggered Entity: {}".format(
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", capsule.forced_entity)
|
||||
)
|
||||
)
|
||||
|
||||
Report.info("******************************")
|
||||
|
||||
# 6) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
Report.info("*** FINISHED TEST ***")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_CapsuleShapedForce)
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C12868578
|
||||
# Test Case Title : Check that World space and local space force direction doesn't affect magnitude of force exerted
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
entity_position = ("All entities in good relative position", "Not all entities in correct position")
|
||||
sphere_collisions = ("All spheres collided with Force Regions", "Not All spheres collided")
|
||||
initial_velocity = ("Spheres started moving correctly", "Spheres not moving correctly")
|
||||
velocity_updated = ("Sphere velocities updated", "Sphere velocities didn't update")
|
||||
|
||||
# Z Direction
|
||||
sphere_0_found = ("sphere_0 is found", "sphere_0 is not found")
|
||||
sphere_1_found = ("sphere_1 is found", "sphere_1 is not found")
|
||||
force_region_0_found = ("force_region_0 is found", "force_region_0 is not found")
|
||||
force_region_1_found = ("force_region_1 is found", "force_region_1 is not found")
|
||||
local_force_mag_z = ("z-axis Local Space force magnitude valid", "z-axis Local Space force magnitude invalid")
|
||||
local_force_dir_z = ("z-axis Local Space force direction valid", "z-axis Local Space force direction invalid")
|
||||
world_force_mag_z = ("z-axis World Space force magnitude valid", "z-axis World Space force magnitude invalid")
|
||||
world_force_dir_z = ("z-axis World Space force direction valid", "z-axis World Space force direction invalid")
|
||||
|
||||
# X Direction
|
||||
sphere_2_found = ("sphere_2 is found", "sphere_2 is not found")
|
||||
sphere_3_found = ("sphere_3 is found", "sphere_3 is not found")
|
||||
force_region_2_found = ("force_region_2 is found", "force_region_2 is not found")
|
||||
force_region_3_found = ("force_region_3 is found", "force_region_3 is not found")
|
||||
local_force_mag_x = ("x-axis Local Space force magnitude valid", "x-axis Local Space force magnitude invalid")
|
||||
local_force_dir_x = ("x-axis Local Space force direction valid", "x-axis Local Space force direction invalid")
|
||||
world_force_mag_x = ("x-axis World Space force magnitude valid", "x-axis World Space force magnitude invalid")
|
||||
world_force_dir_x = ("x-axis World Space force direction valid", "x-axis World Space force direction invalid")
|
||||
|
||||
# Y Direction
|
||||
sphere_4_found = ("sphere_4 is found", "sphere_4 is not found")
|
||||
sphere_5_found = ("sphere_5 is found", "sphere_5 is not found")
|
||||
force_region_4_found = ("force_region_4 is found", "force_region_4 is not found")
|
||||
force_region_5_found = ("force_region_5 is found", "force_region_5 is not found")
|
||||
local_force_mag_y = ("y-axis Local Space force magnitude valid", "y-axis Local Space force magnitude invalid")
|
||||
local_force_dir_y = ("y-axis Local Space force direction valid", "y-axis Local Space force direction invalid")
|
||||
world_force_mag_y = ("y-axis World Space force magnitude valid", "y-axis World Space force magnitude invalid")
|
||||
world_force_dir_y = ("y-axis World Space force direction valid", "y-axis World Space force direction invalid")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_DirectionHasNoAffectOnTotalForce():
|
||||
"""
|
||||
Summary: Check that world and local space force direction should not affect magnitude of force exerted on entity.
|
||||
|
||||
Level Description:
|
||||
sphere_0 - Directly above force_region_0 with velocity of 10.0 in the negative z direction; has sphere shape
|
||||
collider, rigid body, and sphere shape
|
||||
sphere_1 - Directly above force_region_1 with velocity of 10.0 in the negative z direction; has sphere shape
|
||||
collider, rigid body, and sphere shape
|
||||
force_region_0 - Directly below sphere_0 with world space force of magnitude 100.0 and direction vector of
|
||||
<0.0,0.0,999.0>; has box shape collider and force region
|
||||
force_region_1 - Directly below sphere_1 with local space force of magnitude 100.0 and direction vector of
|
||||
<0.0,0.0,999.0>; has box shape collider and force region
|
||||
|
||||
Expected Behavior: Both spheres bounce off of there respective force regions with a force of magnitude that is close
|
||||
to 100.0 in positive z direction. The direction is normalized from the manual entered direction input.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Set up and validate entities
|
||||
4) Wait for collision
|
||||
5) Wait for velocities to become positive
|
||||
6) Log and validate results
|
||||
7) Exit Game Mode
|
||||
8) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = sys.float_info.epsilon
|
||||
TIMEOUT = 1
|
||||
MAGNITUDE_THRESHOLD = 0.1
|
||||
FORCE_VECTOR_THRESHOLD = 0.001
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name):
|
||||
# type (str, hex) -> None
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.collision_happened = False
|
||||
# ID validation
|
||||
self.found = Tests.__dict__[self.name + "_found"]
|
||||
Report.critical_result(self.found, self.id.isValid())
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
# type () -> Vector3
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Sphere(Entity):
|
||||
def __init__(self, name, axis, force_region):
|
||||
Entity.__init__(self, name)
|
||||
self.paired_force_region = force_region
|
||||
self.axis = axis
|
||||
self.force_vector = None
|
||||
self.force_magnitude = None
|
||||
# Set Handler
|
||||
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
self.handler.connect(None)
|
||||
self.handler.add_callback("OnCalculateNetForce", self.on_calculate_net_force)
|
||||
# Report initial values
|
||||
Report.info_vector3(self.position, "{} initial position: ".format(self.name))
|
||||
Report.info_vector3(self.velocity, "{} initial velocity: ".format(self.name))
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
# type () -> Vector3
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
@property
|
||||
def is_moving_in_positive_direction(self):
|
||||
# type () -> bool
|
||||
# A List of the attribute names for the velocity (Vector3)
|
||||
axis = ["x", "y", "z"]
|
||||
# Finds the index in the list of the attribute in which the sphere is moving in
|
||||
index = axis.index(self.axis)
|
||||
# Checking that we are moving along that axis
|
||||
moving_component = getattr(self.velocity, axis[index]) > 0.0
|
||||
# Getting rid of moving axis from list
|
||||
axis.pop(index)
|
||||
# Checking that the sphere is not moving along either of the remaining two axis.
|
||||
stationary_components = (
|
||||
abs(getattr(self.velocity, axis[0])) < FLOAT_THRESHOLD
|
||||
and abs(getattr(self.velocity, axis[1])) < FLOAT_THRESHOLD
|
||||
)
|
||||
return moving_component and stationary_components
|
||||
|
||||
def report_values(self):
|
||||
# type () -> None
|
||||
# Reports final position and velocity information
|
||||
Report.info_vector3(self.position, "{} final position: ".format(self.name))
|
||||
Report.info_vector3(self.velocity, "{} final velocity: ".format(self.name))
|
||||
|
||||
def on_calculate_net_force(self, args):
|
||||
# type (list) -> None
|
||||
# Flips the collision happened boolean for the sphere object and prints the force values.
|
||||
if self.paired_force_region.id.Equal(args[0]) and self.id.equal(args[1]) and not self.collision_happened:
|
||||
self.collision_happened = True
|
||||
self.force_vector = args[2]
|
||||
self.force_magnitude = args[3]
|
||||
# Report force vector information
|
||||
Report.info_vector3(self.force_vector, "{} had following force vector applied".format(self.name))
|
||||
Report.info("{} is the applied force magnitude".format(self.force_magnitude))
|
||||
|
||||
def validate_local_force_results(sphere):
|
||||
# type (Sphere) -> None
|
||||
local_force_direction = Tests.__dict__["local_force_dir_{}".format(sphere.axis)]
|
||||
local_force_magnitude = Tests.__dict__["local_force_mag_{}".format(sphere.axis)]
|
||||
|
||||
Report.result(local_force_direction, check_applied_force_vector(sphere.force_vector))
|
||||
force_region_magnitude = azlmbr.physics.ForceLocalSpaceRequestBus(azlmbr.bus.Event,"GetMagnitude", sphere.paired_force_region.id)
|
||||
print(force_region_magnitude)
|
||||
print("LOOKKKK ABOVE!")
|
||||
Report.result(local_force_magnitude, abs(sphere.force_magnitude - force_region_magnitude) < MAGNITUDE_THRESHOLD)
|
||||
|
||||
def validate_world_force_results(sphere):
|
||||
# type (Sphere) -> None
|
||||
world_force_direction = Tests.__dict__["world_force_dir_{}".format(sphere.axis)]
|
||||
world_force_magnitude = Tests.__dict__["world_force_mag_{}".format(sphere.axis)]
|
||||
|
||||
Report.result(world_force_direction, check_applied_force_vector(sphere.force_vector))
|
||||
force_region_magnitude = azlmbr.physics.ForceWorldSpaceRequestBus(azlmbr.bus.Event,"GetMagnitude", sphere.paired_force_region.id)
|
||||
Report.result(world_force_magnitude, abs(sphere.force_magnitude - force_region_magnitude) < MAGNITUDE_THRESHOLD)
|
||||
|
||||
def check_pair_position(sphere):
|
||||
# type (Sphere) -> bool
|
||||
# Ensures sphere lines up with its associated force region
|
||||
force_region_position = sphere.paired_force_region.position
|
||||
axis = ["x", "y", "z"]
|
||||
index = axis.index(sphere.axis)
|
||||
offset_component = getattr(force_region_position, axis[index]) < getattr(sphere.position, axis[index])
|
||||
axis.pop(index)
|
||||
zero_components = (
|
||||
abs(getattr(force_region_position, axis[0]) - getattr(sphere.position, axis[0])) < FLOAT_THRESHOLD
|
||||
and abs(getattr(force_region_position, axis[1]) - getattr(sphere.position, axis[1])) < FLOAT_THRESHOLD
|
||||
)
|
||||
return offset_component and zero_components
|
||||
|
||||
def check_applied_force_vector(vector):
|
||||
# type (Sphere) -> bool
|
||||
# Ensures the force vector is within expected threshold. The components of the vector can either be 0 or 1
|
||||
axis = ["x", "y", "z"]
|
||||
return all(
|
||||
[
|
||||
True
|
||||
for component in axis
|
||||
if abs(getattr(vector, component) - 1.00) < FORCE_VECTOR_THRESHOLD
|
||||
or abs(getattr(vector, component)) < FORCE_VECTOR_THRESHOLD
|
||||
]
|
||||
)
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "ForceRegion_DirectionHasNoAffectOnTotalForce")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Set up and validate entities
|
||||
force_region_0 = Entity("force_region_0")
|
||||
force_region_1 = Entity("force_region_1")
|
||||
force_region_2 = Entity("force_region_2")
|
||||
force_region_3 = Entity("force_region_3")
|
||||
force_region_4 = Entity("force_region_4")
|
||||
force_region_5 = Entity("force_region_5")
|
||||
|
||||
sphere_0 = Sphere("sphere_0", "z", force_region_0)
|
||||
sphere_1 = Sphere("sphere_1", "z", force_region_1)
|
||||
sphere_2 = Sphere("sphere_2", "x", force_region_2)
|
||||
sphere_3 = Sphere("sphere_3", "x", force_region_3)
|
||||
sphere_4 = Sphere("sphere_4", "y", force_region_4)
|
||||
sphere_5 = Sphere("sphere_5", "y", force_region_5)
|
||||
sphere_list = [sphere_0, sphere_1, sphere_2, sphere_3, sphere_4, sphere_5]
|
||||
local_force_list = [sphere_1, sphere_3, sphere_5]
|
||||
world_force_list = [sphere_0, sphere_2, sphere_4]
|
||||
|
||||
Report.critical_result(
|
||||
Tests.entity_position, all([check_pair_position(sphere) for sphere in sphere_list])
|
||||
)
|
||||
|
||||
Report.critical_result(
|
||||
Tests.initial_velocity,
|
||||
all([not sphere.is_moving_in_positive_direction for sphere in sphere_list]),
|
||||
)
|
||||
|
||||
# 4) Wait for collision
|
||||
Report.critical_result(
|
||||
Tests.sphere_collisions,
|
||||
helper.wait_for_condition(
|
||||
lambda: all([sphere.collision_happened for sphere in sphere_list]), TIMEOUT
|
||||
),
|
||||
)
|
||||
|
||||
# 5) Wait for velocities to become positive
|
||||
Report.critical_result(
|
||||
Tests.velocity_updated,
|
||||
helper.wait_for_condition(
|
||||
lambda: all([sphere.is_moving_in_positive_direction for sphere in sphere_list]), TIMEOUT
|
||||
),
|
||||
)
|
||||
|
||||
# 6) Log and validate results
|
||||
[validate_local_force_results(sphere) for sphere in local_force_list]
|
||||
[validate_world_force_results(sphere) for sphere in world_force_list]
|
||||
|
||||
[sphere.report_values() for sphere in sphere_list]
|
||||
|
||||
# 7) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_DirectionHasNoAffectOnTotalForce)
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C6321601
|
||||
# Test Case Title : Check that very high values of direction axes of forces do not throw error
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_terrain = ("Terrain found", "Terrain not found")
|
||||
find_sphere_world_space = ("Sphere above world force region found", "Sphere above world force region not found")
|
||||
find_sphere_local_space = ("Sphere above local force region found", "Sphere above local force region not found")
|
||||
find_sphere_point = ("Sphere above point force region found", "Sphere above point force region not found")
|
||||
find_sphere_simple_drag = ("Sphere above simple drag force region found", "Sphere above simple drag force region not found")
|
||||
find_sphere_linear_damping = ("Sphere above linear damping force region found", "Sphere above linear damping force region not found")
|
||||
find_forcevol_world_space = ("World force region found", "World force region not found")
|
||||
find_forcevol_local_space = ("Local force region found", "Local force region not found")
|
||||
find_forcevol_point = ("Point force region found", "Point force region not found")
|
||||
find_forcevol_simple_drag = ("Simple drag force region found", "Simple drag force region not found")
|
||||
find_forcevol_linear_damping = ("Linear damping force region found", "Linear damping force region not found")
|
||||
world_force_magnitude = ("World force magnitude equal to expected magnitude", "World force magnitude not equal to expected magnitude")
|
||||
world_force_direction = ("World force direction equal to expected direction", "World force direction not equal to expected direction")
|
||||
local_force_magnitude = ("Local force magnitude equal to expected magnitude", "Local force magnitude not equal to expected magnitude")
|
||||
local_force_direction = ("Local force direction equal to expected direction", "Local force direction not equal to expected direction")
|
||||
point_force_magnitude = ("Point force magnitude equal to expected magnitude", "Point force magnitude not equal to expected magnitude")
|
||||
simp_drag_density = ("Simple Drag force density equal to expected value", "Simple Drag force density not equal to expected value")
|
||||
lin_damp_damping = ("Linear Damping force damping equal to expected value", "Linear Damping force damping not equal to expected value")
|
||||
error_not_found = ("Error not found", "Error found")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_HighValuesDirectionAxesWorkWithNoError():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Check that very high values of direction axes of forces do not throw error.
|
||||
|
||||
Level Description:
|
||||
Sphere_World_Space, Sphere_Local_Space, Sphere_Point, Sphere_Simple_Drag, Sphere_Linear_Damping
|
||||
(Entities) Entities with components:
|
||||
- Physx Collider (Sphere shaped with radius 1.0)
|
||||
- Mesh(Prmitive sphere mesh)
|
||||
- PhysX Rigid Body Physics
|
||||
|
||||
Below are the entities with common components
|
||||
- PhysX Collider (Trigger enabled)
|
||||
- PhysX Force Region(Visible and Debug Forces enabled)
|
||||
They differ in Force Region Force Type with the following properties:
|
||||
1) ForceVol_World_Space
|
||||
Type - World Space - Direction(0.0, 0.0, 999999.0) - Magnitude(999999.0)
|
||||
2) ForceVol_Local_Space
|
||||
Type - Local Space - Direction(0.0, 0.0, 999999.0) - Magnitude(999999.0)
|
||||
3) ForceVol_Point
|
||||
Type - Point - Magnitude(999999.0)
|
||||
4) ForceVol_Simple_Drag
|
||||
Type - Simple Drag - Region Density(999.0)
|
||||
5) ForceVol_Linear_Damping
|
||||
Type - Linear Damping - Damping(99.0)
|
||||
Each sphere is placed above its corresponding force regions.
|
||||
Each of the force regions are seperated by some distance
|
||||
|
||||
Expected Behavior:
|
||||
The given force should be applied as it is without any error on the Sphere.
|
||||
We are verifying if the force being applied on each sphere is equal to the expected value
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Add force region handler and validate the forces
|
||||
5) Exit game mode
|
||||
6) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Aed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
from editor_python_test_tools.utils import Tracer
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
# Constants
|
||||
TOLERANCE_PERCENT = 0.001
|
||||
EXPECTED_DIRECTION = lymath.Vector3(0.0, 0.0, 1.0)
|
||||
EXPECTED_DAMPING = 99.0
|
||||
EXPECTED_DENSITY = 400.0
|
||||
CLOSE_THRESHOLD = 0.0001
|
||||
|
||||
class SphereForceRegion:
|
||||
def __init__(self, force_name):
|
||||
self.force_name = force_name
|
||||
self.sphere_name = "Sphere_{}".format(force_name)
|
||||
self.force_region_name = "ForceVol_{}".format(force_name)
|
||||
self.sphere_id = general.find_game_entity(self.sphere_name)
|
||||
self.force_region_id = general.find_game_entity(self.force_region_name)
|
||||
self.in_force_region = False
|
||||
self.validate_entities()
|
||||
|
||||
def validate_entities(self):
|
||||
Report.result(Tests.__dict__["find_{}".format(self.sphere_name.lower())], self.sphere_id.IsValid())
|
||||
Report.result(
|
||||
Tests.__dict__["find_{}".format(self.force_region_name.lower())], self.force_region_id.IsValid()
|
||||
)
|
||||
|
||||
def validate_world_space_force(args):
|
||||
Report.info("Validating world space force...")
|
||||
world_expected_magnitude = azlmbr.physics.ForceWorldSpaceRequestBus(
|
||||
azlmbr.bus.Event, "GetMagnitude", regions[0].force_region_id
|
||||
)
|
||||
world_actual_magnitude = args[3]
|
||||
Report.info(
|
||||
"Actual Magnitude: {}\t Expected Magnitude: {}".format(world_actual_magnitude, world_expected_magnitude)
|
||||
)
|
||||
Report.result(
|
||||
Tests.world_force_magnitude,
|
||||
abs(world_actual_magnitude - world_expected_magnitude) < TOLERANCE_PERCENT * world_expected_magnitude,
|
||||
)
|
||||
world_actual_direction = args[2]
|
||||
Report.result(Tests.world_force_direction, world_actual_direction.IsClose(EXPECTED_DIRECTION, CLOSE_THRESHOLD))
|
||||
|
||||
def validate_local_space_force(args):
|
||||
Report.info("Validating local space force...")
|
||||
local_expected_magnitude = azlmbr.physics.ForceLocalSpaceRequestBus(
|
||||
azlmbr.bus.Event, "GetMagnitude", regions[1].force_region_id
|
||||
)
|
||||
local_actual_magnitude = args[3]
|
||||
Report.info(
|
||||
"Actual Magnitude: {}\t Expected Magnitude: {}".format(local_actual_magnitude, local_expected_magnitude)
|
||||
)
|
||||
Report.result(
|
||||
Tests.local_force_magnitude,
|
||||
abs(local_actual_magnitude - local_expected_magnitude) < TOLERANCE_PERCENT * local_expected_magnitude,
|
||||
)
|
||||
local_actual_direction = args[2]
|
||||
Report.result(Tests.local_force_direction, local_actual_direction.IsClose(EXPECTED_DIRECTION, CLOSE_THRESHOLD))
|
||||
|
||||
def validate_point_force(args):
|
||||
Report.info("Validating Point space force...")
|
||||
point_expected_magnitude = azlmbr.physics.ForcePointRequestBus(
|
||||
azlmbr.bus.Event, "GetMagnitude", regions[2].force_region_id
|
||||
)
|
||||
point_actual_magnitude = args[3]
|
||||
Report.info(
|
||||
"Actual Magnitude: {}\t Expected Magnitude: {}".format(point_actual_magnitude, point_expected_magnitude)
|
||||
)
|
||||
Report.result(
|
||||
Tests.point_force_magnitude,
|
||||
abs(point_actual_magnitude - point_expected_magnitude) < TOLERANCE_PERCENT * point_expected_magnitude,
|
||||
)
|
||||
|
||||
def validate_simple_drag_force(args):
|
||||
Report.info("Validating Simple Drag force...")
|
||||
simp_drag_density = azlmbr.physics.ForceSimpleDragRequestBus(
|
||||
azlmbr.bus.Event, "GetDensity", regions[3].force_region_id
|
||||
)
|
||||
Report.info("Density: {}\t Expected Density: {}".format(simp_drag_density, EXPECTED_DENSITY))
|
||||
Report.result(Tests.simp_drag_density, simp_drag_density == EXPECTED_DENSITY)
|
||||
|
||||
def validate_linear_damping_force(args):
|
||||
Report.info("Validating Linear Damping force...")
|
||||
lin_damp_damping = azlmbr.physics.ForceLinearDampingRequestBus(
|
||||
azlmbr.bus.Event, "GetDamping", regions[4].force_region_id
|
||||
)
|
||||
Report.info("Damping: {}\t Expected Damping: {}".format(lin_damp_damping, EXPECTED_DAMPING))
|
||||
Report.result(Tests.lin_damp_damping, lin_damp_damping == EXPECTED_DAMPING)
|
||||
|
||||
def on_calc_net_force(args):
|
||||
"""
|
||||
Args:
|
||||
args[0] - force region entity
|
||||
args[1] - entity entering
|
||||
args[2] - vector
|
||||
args[3] - magnitude
|
||||
"""
|
||||
for index, region in enumerate(regions):
|
||||
if args[0].Equal(region.force_region_id) and args[1].Equal(region.sphere_id) and not region.in_force_region:
|
||||
region.in_force_region = True
|
||||
force_validations[index][1](args)
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
with Tracer() as entity_error_tracer:
|
||||
|
||||
def has_physx_error():
|
||||
return entity_error_tracer.has_errors
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_HighValuesDirectionAxesWorkWithNoError")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
# Terrain
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.result(Tests.find_terrain, terrain_id.IsValid())
|
||||
force_validations = (
|
||||
("World_Space", validate_world_space_force),
|
||||
("Local_Space", validate_local_space_force),
|
||||
("Point", validate_point_force),
|
||||
("Simple_Drag", validate_simple_drag_force),
|
||||
("Linear_Damping", validate_linear_damping_force),
|
||||
)
|
||||
regions = []
|
||||
for item in force_validations:
|
||||
regions.append(SphereForceRegion(item[0]))
|
||||
|
||||
# 4) Add force region handler and validate the forces
|
||||
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
force_notification_handler.connect(None)
|
||||
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
|
||||
|
||||
# Wait for 3 secs, because there is a known bug identified and filed in
|
||||
# JIRA LY-107677
|
||||
# The error "[Error] Huge object being added to a COctreeNode, name: 'MeshComponentRenderNode', objBox:"
|
||||
# will show (if occured) in about 3 sec into the game mode.
|
||||
helper.wait_for_condition(has_physx_error, 3.0)
|
||||
|
||||
# 5) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
Report.result(Tests.error_not_found, not has_physx_error())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_HighValuesDirectionAxesWorkWithNoError)
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5959764
|
||||
# Test Case Title : Check that rigid body (Cube) gets impulse from force region
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_cube = ("Entity Cube found", "Cube not found")
|
||||
find_force_region = ("Force Region found", "Force Region not found")
|
||||
cube_gained_height = ("Cube went up", "Cube didn't go up")
|
||||
force_region_success = ("Force Region impulsed Cube", "Force Region didn't impulse Cube")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
tests_completed = ("Tests completed", "Tests did not complete")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ImpulsesBoxShapedRigidBody():
|
||||
"""
|
||||
This run() function will open a a level and validate that a Cube gets impulsed by a force region.
|
||||
It does this by:
|
||||
1) Open level
|
||||
2) Enters Game mode
|
||||
3) Finds the entities in the scene
|
||||
4) Gets the position of the Cube
|
||||
5) Listens for Cube to enter the force region
|
||||
6) Gets the vector and magnitude when Cube is in force region
|
||||
7) Lets the Cube travel up
|
||||
8) Gets new position of Cube
|
||||
9) Validate the results
|
||||
10) Exits game mode and editor
|
||||
"""
|
||||
# Setup path
|
||||
import os, sys
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
class Cube:
|
||||
id = None
|
||||
gained_height = False # Did the Cube gain height
|
||||
z_end_position = 0
|
||||
z_start_position = 0
|
||||
|
||||
# Listen for Force Region events
|
||||
class RegionData:
|
||||
def __init__(self, ID):
|
||||
self.force_region_id = ID
|
||||
self.force_region_entered = False
|
||||
self.force_vector = None
|
||||
self.force_magnitude_on_cube = 0 # Magnitude applied on cube
|
||||
self.force_region_magnitude = 0 # Magnitude value for the force region set in the editor
|
||||
self.force_region_magnitude_range = (
|
||||
0.01
|
||||
) # Delta value allowed between magnitude force_magnitude_on_cube and force_region_magnitude
|
||||
|
||||
def force_region_in_range(self):
|
||||
return abs(self.force_magnitude_on_cube - self.force_region_magnitude) < 0.01 # 0.01 for buffer room
|
||||
|
||||
def ifVectorClose(vec1, vec2):
|
||||
return abs(vec1 - vec2) < 0.01 # 0.01 For buffer room for dif in the two vectors.
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# *****Variables*****
|
||||
TIME_OUT = 4.0 # Seconds
|
||||
UPWARD_Z_VECTOR = 1.0
|
||||
|
||||
# Open level
|
||||
helper.open_level("Physics", "ForceRegion_ImpulsesBoxShapedRigidBody")
|
||||
|
||||
# Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# Get Entities
|
||||
Cube.id = general.find_game_entity("Cube")
|
||||
Report.critical_result(Tests.find_cube, Cube.id.IsValid())
|
||||
|
||||
RegionObject = RegionData(general.find_game_entity("Force Region"))
|
||||
Report.critical_result(Tests.find_force_region, RegionObject.force_region_id.IsValid())
|
||||
|
||||
# Set values for cube and force region
|
||||
Cube.z_start_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Cube.id)
|
||||
RegionObject.force_region_magnitude = azlmbr.physics.ForcePointRequestBus(
|
||||
azlmbr.bus.Event, "GetMagnitude", RegionObject.force_region_id
|
||||
)
|
||||
Report.info("Cube start z position = {}".format(Cube.z_start_position))
|
||||
|
||||
# Force Region Event Handler
|
||||
def on_calculate_net_force(args):
|
||||
"""
|
||||
Called when there is a collision in the level
|
||||
Args:
|
||||
args[0] - force region entity
|
||||
args[1] - entity entering
|
||||
args[2] - vector
|
||||
args[3] - magnitude
|
||||
"""
|
||||
assert RegionObject.force_region_id.Equal(args[0])
|
||||
|
||||
vect = args[2]
|
||||
mag = args[3]
|
||||
if not RegionObject.force_region_entered:
|
||||
RegionObject.force_region_entered = True
|
||||
RegionObject.force_vector = vect
|
||||
RegionObject.force_magnitude_on_cube = mag
|
||||
Report.info("Force Region entered")
|
||||
|
||||
# Assign the handler
|
||||
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
handler.connect(None)
|
||||
handler.add_callback("OnCalculateNetForce", on_calculate_net_force)
|
||||
|
||||
# Give cube time to travel. Exit when cube is done moving or time runs out.
|
||||
def test_completed():
|
||||
Cube.z_end_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Cube.id)
|
||||
Cube.gained_height = Cube.z_end_position > (Cube.z_start_position + 0.5) # 0.5 for buffer
|
||||
|
||||
# Validate if cube gained height and entered force region
|
||||
if Cube.gained_height and RegionObject.force_region_entered:
|
||||
Report.success(Tests.cube_gained_height)
|
||||
return True
|
||||
return False
|
||||
|
||||
# Wait for test to complete
|
||||
test_is_completed = helper.wait_for_condition(test_completed, TIME_OUT)
|
||||
|
||||
if not test_is_completed:
|
||||
Report.info("The test timed out, check log for possible solutions or adjust the time out time")
|
||||
Report.failure(Tests.tests_completed)
|
||||
|
||||
else:
|
||||
# Did Force Region succeed. True if vector z is close to 1 and force region magnitude is close to magnitude applied on cube
|
||||
force_region_result = (
|
||||
ifVectorClose(RegionObject.force_vector.z, UPWARD_Z_VECTOR) and RegionObject.force_region_in_range()
|
||||
)
|
||||
Report.result(Tests.force_region_success, force_region_result)
|
||||
Report.success(Tests.tests_completed)
|
||||
|
||||
# Report test info to log
|
||||
Report.info("******* FINAL ENTITY INFORMATION *********")
|
||||
Report.info("Cube Entered force region = {}".format(RegionObject.force_region_entered))
|
||||
Report.info("Start Z Position = {} End Z Position = {}".format(Cube.z_start_position, Cube.z_end_position))
|
||||
Report.info("Cube Gained height = {}".format(Cube.gained_height))
|
||||
Report.info("Vector = {} Magnitude = {}".format(RegionObject.force_vector.z, RegionObject.force_magnitude_on_cube))
|
||||
|
||||
# Exit game mode and close the editor
|
||||
Report.result(Tests.tests_completed, test_is_completed)
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ImpulsesBoxShapedRigidBody)
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5959764
|
||||
# Test Case Title : Check that rigid body (Capsule) gets impulse from force region
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_capsule = ("Entity Capsule found", "Capsule not found")
|
||||
find_force_region = ("Force Region found", "Force Region not found")
|
||||
capsule_gained_height = ("Capsule went up", "Capsule didn't go up")
|
||||
force_region_success = ("Force Region impulsed Capsule", "Force Region didn't impulse Capsule")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
tests_completed = ("Tests completed", "Tests did not complete")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ImpulsesCapsuleShapedRigidBody():
|
||||
"""
|
||||
This run() function will open a a level and validate that a Capsule gets impulsed by a force region.
|
||||
It does this by:
|
||||
1) Open level
|
||||
2) Enters Game mode
|
||||
3) Finds the entities in the scene
|
||||
4) Gets the position of the Capsule
|
||||
5) Listens for Capsule to enter the force region
|
||||
6) Gets the vector and magnitude when Capsule is in force region
|
||||
7) Lets the Capsule travel up
|
||||
8) Gets new position of Capsule
|
||||
9) Validate the results
|
||||
10) Exits game mode and editor
|
||||
"""
|
||||
# Setup path
|
||||
import os, sys
|
||||
|
||||
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
class Capsule:
|
||||
id = None
|
||||
gained_height = False # Did the Capsule gain height
|
||||
z_end_position = 0
|
||||
z_start_position = 0
|
||||
|
||||
# Listen for Force Region events
|
||||
class RegionData:
|
||||
def __init__(self, ID):
|
||||
self.force_region_id = ID
|
||||
self.force_region_entered = False
|
||||
self.force_vector = None
|
||||
self.force_magnitude_on_capsule = 0 # Magnitude applied on Capsule
|
||||
self.force_region_magnitude = 0 # Magnitude value for the force region set in the editor
|
||||
self.force_region_magnitude_range = (
|
||||
0.01
|
||||
) # Delta value allowed between magnitude force_magnitude_on_capsule and force_region_magnitude
|
||||
|
||||
def force_region_in_range(self):
|
||||
return abs(self.force_magnitude_on_capsule - self.force_region_magnitude) < 0.01 # 0.01 for buffer room
|
||||
|
||||
def ifVectorClose(vec1, vec2):
|
||||
return abs(vec1 - vec2) < 0.01 # 0.01 For buffer room for dif in the two vectors.
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# *****Variables*****
|
||||
TIME_OUT = 4.0 # Seconds
|
||||
UPWARD_Z_VECTOR = 1.0
|
||||
|
||||
# Open level
|
||||
helper.open_level("Physics", "ForceRegion_ImpulsesCapsuleShapedRigidBody")
|
||||
|
||||
# Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# Get Entities
|
||||
Capsule.id = general.find_game_entity("Capsule")
|
||||
Report.critical_result(Tests.find_capsule, Capsule.id.IsValid())
|
||||
|
||||
RegionObject = RegionData(general.find_game_entity("Force Region"))
|
||||
Report.critical_result(Tests.find_force_region, RegionObject.force_region_id.IsValid())
|
||||
|
||||
# Set values for Capsule and force region
|
||||
Capsule.z_start_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Capsule.id)
|
||||
RegionObject.force_region_magnitude = azlmbr.physics.ForcePointRequestBus(
|
||||
azlmbr.bus.Event, "GetMagnitude", RegionObject.force_region_id
|
||||
)
|
||||
Report.info("Capsule start z position = {}".format(Capsule.z_start_position))
|
||||
|
||||
# Force Region Event Handler
|
||||
def on_calculate_net_force(args):
|
||||
"""
|
||||
Called when there is a collision in the level
|
||||
Args:
|
||||
args[0] - force region entity
|
||||
args[1] - entity entering
|
||||
args[2] - vector
|
||||
args[3] - magnitude
|
||||
"""
|
||||
assert RegionObject.force_region_id.Equal(args[0])
|
||||
|
||||
vect = args[2]
|
||||
mag = args[3]
|
||||
if not RegionObject.force_region_entered:
|
||||
RegionObject.force_region_entered = True
|
||||
RegionObject.force_vector = vect
|
||||
RegionObject.force_magnitude_on_capsule = mag
|
||||
Report.info("Force Region entered")
|
||||
|
||||
# Assign the handler
|
||||
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
handler.connect(None)
|
||||
handler.add_callback("OnCalculateNetForce", on_calculate_net_force)
|
||||
|
||||
# Give Capsule time to travel. Exit when Capsule is done moving or time runs out.
|
||||
def test_completed():
|
||||
Capsule.z_end_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Capsule.id)
|
||||
Capsule.gained_height = Capsule.z_end_position > (Capsule.z_start_position + 0.5) # 0.5 for buffer
|
||||
|
||||
# Validate if Capsule gained height and entered force region
|
||||
if Capsule.gained_height and RegionObject.force_region_entered:
|
||||
Report.success(Tests.capsule_gained_height)
|
||||
return True
|
||||
return False
|
||||
|
||||
# Wait for test to complete
|
||||
test_is_completed = helper.wait_for_condition(test_completed, TIME_OUT)
|
||||
|
||||
if not test_is_completed:
|
||||
Report.info("The test timed out, check log for possible solutions or adjust the time out time")
|
||||
Report.failure(Tests.tests_completed)
|
||||
|
||||
else:
|
||||
# Did Force Region succeed. True if vector z is close to 1 and force region magnitude is close to magnitude applied on Capsule
|
||||
force_region_result = (
|
||||
ifVectorClose(RegionObject.force_vector.z, UPWARD_Z_VECTOR) and RegionObject.force_region_in_range()
|
||||
)
|
||||
Report.result(Tests.force_region_success, force_region_result)
|
||||
Report.success(Tests.tests_completed)
|
||||
|
||||
# Report test info to logSS
|
||||
Report.info("******* FINAL ENTITY INFORMATION *********")
|
||||
Report.info("Capsule Entered force region = {}".format(RegionObject.force_region_entered))
|
||||
Report.info("Start Z Position = {} End Z Position = {}".format(Capsule.z_start_position, Capsule.z_end_position))
|
||||
Report.info("Capsule Gained height = {}".format(Capsule.gained_height))
|
||||
Report.info(
|
||||
"Vector = {} Magnitude = {}".format(RegionObject.force_vector.z, RegionObject.force_magnitude_on_capsule)
|
||||
)
|
||||
|
||||
# Exit game mode and close the editor
|
||||
Report.result(Tests.tests_completed, test_is_completed)
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ImpulsesCapsuleShapedRigidBody)
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5959765
|
||||
# Test Case Title : Check that rigid body (asset) gets impulse from force region
|
||||
|
||||
|
||||
# fmt: off
|
||||
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_asset = ("Entity asset found", "Asset not found")
|
||||
find_force_region = ("Force Region found", "Force Region not found")
|
||||
asset_gained_height = ("Asset went up", "Asset didn't go up")
|
||||
force_region_success = ("Force Region impulsed asset", "Force Region didn't impulse asset")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
tests_completed = ("Tests completed", "Tests did not complete")
|
||||
# fmt: on
|
||||
|
||||
def ForceRegion_ImpulsesPxMeshShapedRigidBody():
|
||||
"""
|
||||
# This run() function will open a a level and validate that a asset gets impulsed by a force region.
|
||||
# It does this by:
|
||||
# 1) Open level
|
||||
# 2) Enters Game mode
|
||||
# 3) Finds the entities in the scene
|
||||
# 4) Set values for Asset and force region
|
||||
# 5) Listens for asset to enter the force region
|
||||
# 6) Gets the vector and magnitude when asset is in force region
|
||||
# 7) Lets the asset travel up
|
||||
# 8) Validate if Asset gained height and entered force region
|
||||
# 9) Checks if test completed
|
||||
# 10) Exits game mode and editor
|
||||
|
||||
# Level setup: Sedan asset above force region
|
||||
# First Asset: Name = "Sedan" This entity should drop vertically, collide with force region, and be shot up
|
||||
# First force region: Name = "Force Region" Should shoot Sedan entity up upon entry
|
||||
"""
|
||||
# Setup path
|
||||
import os, sys
|
||||
|
||||
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
class Asset:
|
||||
id = None
|
||||
gained_height = False # Did the Asset gain height
|
||||
z_end_position = 0
|
||||
z_start_position = 0
|
||||
|
||||
# Listen for Force Region events
|
||||
class RegionData:
|
||||
def __init__(self, ID):
|
||||
self.force_region_id = ID
|
||||
self.force_region_entered = False
|
||||
self.force_vector = None
|
||||
self.force_magnitude_on_asset = 0 # Magnitude applied on Asset
|
||||
self.force_region_magnitude = 0 # Magnitude value for the force region set in the editor
|
||||
self.force_region_magnitude_range = (
|
||||
0.01
|
||||
) # Delta value allowed between magnitude force_magnitude_on_asset and force_region_magnitude
|
||||
|
||||
def force_region_in_range(self):
|
||||
return abs(self.force_magnitude_on_asset - self.force_region_magnitude) < 0.01 # 0.01 for buffer room
|
||||
|
||||
def ifVectorAxisClose(vec1, vec2):
|
||||
return abs(vec1 - vec2) < 0.01 # 0.01 For buffer room for dif in the two vectors.
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# *****Variables*****
|
||||
TIME_OUT = 4.0 # Seconds
|
||||
UPWARD_Z_VECTOR = 1.0
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_ImpulsesPxMeshShapedRigidBody")
|
||||
|
||||
# 2) Enters Game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Finds the entities in the scene
|
||||
Asset.id = general.find_game_entity("Sedan")
|
||||
Report.critical_result(Tests.find_asset, Asset.id.IsValid())
|
||||
|
||||
RegionObject = RegionData(general.find_game_entity("Force Region"))
|
||||
Report.critical_result(Tests.find_force_region, RegionObject.force_region_id.IsValid())
|
||||
|
||||
# 4) Set values for Asset and force region
|
||||
Asset.z_start_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Asset.id)
|
||||
RegionObject.force_region_magnitude = azlmbr.physics.ForcePointRequestBus(
|
||||
azlmbr.bus.Event, "GetMagnitude", RegionObject.force_region_id
|
||||
)
|
||||
Report.info("Asset start z position = {}".format(Asset.z_start_position))
|
||||
|
||||
# 5) Listens for asset to enter the force region
|
||||
def on_calculate_net_force(args):
|
||||
"""
|
||||
Called when there is a collision in the level
|
||||
Args:
|
||||
args[0] - force region entity
|
||||
args[1] - entity entering
|
||||
args[2] - vector
|
||||
args[3] - magnitude
|
||||
"""
|
||||
|
||||
# 6) Gets the vector and magnitude when asset is in force region
|
||||
vect = args[2]
|
||||
mag = args[3]
|
||||
if RegionObject.force_region_id.Equal(args[0]) and not RegionObject.force_region_entered:
|
||||
RegionObject.force_region_entered = True
|
||||
RegionObject.force_vector = vect
|
||||
RegionObject.force_magnitude_on_asset = mag
|
||||
Report.info("Force Region entered")
|
||||
|
||||
# Assign the handler
|
||||
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
handler.connect(None)
|
||||
handler.add_callback("OnCalculateNetForce", on_calculate_net_force)
|
||||
|
||||
def test_completed():
|
||||
# test_completed() will return a bool saying if all the Necessary actions in the test have been completed.
|
||||
# Necessary Actions: 1) Asset entered Force Region 2) Asset end_height > start_height
|
||||
Asset.z_end_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Asset.id)
|
||||
Asset.gained_height = Asset.z_end_position > (Asset.z_start_position + 0.5) # 0.5 for buffer
|
||||
|
||||
# 8) Validate if Asset gained height and entered force region
|
||||
if Asset.gained_height and RegionObject.force_region_entered:
|
||||
Report.success(Tests.asset_gained_height)
|
||||
return True
|
||||
return False
|
||||
|
||||
# 7) Lets the asset travel up
|
||||
test_is_completed = helper.wait_for_condition(test_completed, TIME_OUT)
|
||||
|
||||
# 9) Checks if test completed
|
||||
if not test_is_completed:
|
||||
Report.info("The test timed out, check log for possible solutions or adjust the time out time")
|
||||
Report.failure(Tests.tests_completed)
|
||||
|
||||
else:
|
||||
# Did Force Region succeed. True if vector z is close to 1 and force region magnitude is close to magnitude applied on Asset
|
||||
force_region_result = (
|
||||
ifVectorAxisClose(RegionObject.force_vector.z, UPWARD_Z_VECTOR) and RegionObject.force_region_in_range()
|
||||
)
|
||||
Report.result(Tests.force_region_success, force_region_result)
|
||||
Report.success(Tests.tests_completed)
|
||||
|
||||
# Report test info to log
|
||||
Report.info("******* FINAL ENTITY INFORMATION *********")
|
||||
Report.info("Asset Entered force region = {}".format(RegionObject.force_region_entered))
|
||||
Report.info("Start Z Position = {} End Z Position = {}".format(Asset.z_start_position, Asset.z_end_position))
|
||||
Report.info("Asset Gained height = {}".format(Asset.gained_height))
|
||||
Report.info("Vector = {} Magnitude = {}".format(RegionObject.force_vector.z, RegionObject.force_magnitude_on_asset))
|
||||
|
||||
# 10) Exit game mode and close the editor
|
||||
Report.result(Tests.tests_completed, test_is_completed)
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ImpulsesPxMeshShapedRigidBody)
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5932042
|
||||
# Test Case Title : Check that force region exerts linear damping force on rigid bodies
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
sphere_validated = ("Sphere entity validated", "Sphere entity NOT validated")
|
||||
force_region_validated = ("Force Region validated", "Force Region NOT validated")
|
||||
trigger_validated = ("Trigger entity validated", "Trigger entity NOT validated")
|
||||
sphere_pos_found = ("Sphere position found", "Sphere position NOT found")
|
||||
force_region_pos_found = ("Force Region position found", "Force Region position NOT found")
|
||||
trigger_pos_found = ("Trigger position found", "Trigger position NOT found")
|
||||
level_setup = ("Level looks set up right", "Level NOT set up right")
|
||||
damping_force_entered = ("Sphere entered linear damping region", "Linear damping region never entered")
|
||||
damping_force_expected = ("Damping force was as expected", "Damping force differed from expected")
|
||||
sphere_slowed = ("Sphere slowed down", "Sphere DID NOT slow down")
|
||||
sphere_stopped = ("Sphere entity stopped", "Sphere entity DID NOT stop")
|
||||
force_region_no_move = ("Force Region did not move", "Fore Region DID move")
|
||||
trigger_no_move = ("Trigger did not move", "Trigger DID move")
|
||||
timed_out = ("The test did not time out", "The test TIMED OUT")
|
||||
trigger_not_triggered = ("The Trigger was not triggered", "The Trigger WAS triggered")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_LinearDampingForceOnRigidBodies():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure linear damping is exerted on rigid body objects from force regions.
|
||||
|
||||
Level Description:
|
||||
A sphere (entity: Sphere) in positioned above a large cube force region (entity: force_region_entity) who is
|
||||
assigned a linear damping force with damping of 10.0. The Sphere has gravity enabled, and is positioned high
|
||||
enough for gravity to accelerate it faster than the maximum velocity inside the linear damping force region.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, gravity should accelerate the Sphere downward. The velocity of the Sphere should peak
|
||||
right before it enters force_region_entity. Upon entering the force region, the Sphere should noticeably slow
|
||||
down. The slower velocity should have a substantially larger (less negative) velocity.
|
||||
|
||||
Test Steps:
|
||||
0) Define useful classes and constants
|
||||
1) Loads the level / Enters game mode
|
||||
2) Retrieve and validate entities
|
||||
3) Ensures that the test object (Sphere) is located
|
||||
3.5) Set up event handlers
|
||||
4) Execute test until exit condition met
|
||||
5) Logs results
|
||||
5.5) Dump all collected data to log
|
||||
6) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as azmath
|
||||
|
||||
# Entity base class: Handles basic entity data
|
||||
class EntityBase:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = None
|
||||
self.initial_pos = None
|
||||
self.current_pos = None
|
||||
|
||||
# Specific Sphere class
|
||||
class Sphere(EntityBase):
|
||||
def __init__(self, name):
|
||||
EntityBase.__init__(self, name)
|
||||
self.initial_velocity = None
|
||||
self.initial_velocity_magnitude = None
|
||||
self.current_velocity = None
|
||||
self.slowed = False
|
||||
self.stopped = False
|
||||
|
||||
def check_for_stop(self):
|
||||
if not self.stopped:
|
||||
self.current_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
self.slowed = self.initial_velocity_magnitude > self.current_velocity.GetLength() + (
|
||||
0.5 * self.initial_velocity_magnitude
|
||||
)
|
||||
self.stopped = self.current_velocity.IsZero()
|
||||
return self.stopped
|
||||
|
||||
# Specific Force Region class
|
||||
class ForceRegion(EntityBase):
|
||||
def __init__(self, name):
|
||||
EntityBase.__init__(self, name)
|
||||
self.entered = False
|
||||
self.object_entered = None
|
||||
self.expected_force_direction = None
|
||||
self.actual_force_vector = None
|
||||
self.actual_force_magnitude = None
|
||||
self.handler = None
|
||||
|
||||
# Specific Trigger class
|
||||
class Trigger(EntityBase):
|
||||
def __init__(self, name):
|
||||
EntityBase.__init__(self, name)
|
||||
self.triggered = False
|
||||
self.triggering_obj = None
|
||||
self.handler = None
|
||||
|
||||
# Constants
|
||||
CLOSE_ENOUGH = 0.001
|
||||
TIME_OUT = 3.0
|
||||
INITIAL_VELOCITY = azmath.Vector3(0.0, 0.0, -10.0)
|
||||
|
||||
# 1) Open level / Enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "ForceRegion_LinearDampingForceOnRigidBodies")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve and validate entities
|
||||
sphere = Sphere("Sphere")
|
||||
sphere.id = general.find_game_entity(sphere.name)
|
||||
force_region = ForceRegion("ForceRegion")
|
||||
force_region.id = general.find_game_entity(force_region.name)
|
||||
trigger = Trigger("Trigger")
|
||||
trigger.id = general.find_game_entity(trigger.name)
|
||||
|
||||
Report.critical_result(Tests.sphere_validated, sphere.id.IsValid())
|
||||
Report.critical_result(Tests.force_region_validated, force_region.id.IsValid())
|
||||
Report.critical_result(Tests.trigger_validated, trigger.id.IsValid())
|
||||
|
||||
# 3) Log Entities' positions and initial data
|
||||
sphere.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere.id)
|
||||
force_region.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", force_region.id)
|
||||
trigger.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", trigger.id)
|
||||
|
||||
Report.critical_result(Tests.sphere_pos_found, sphere.initial_pos is not None and not sphere.initial_pos.IsZero())
|
||||
Report.critical_result(
|
||||
Tests.force_region_pos_found, force_region.initial_pos is not None and not force_region.initial_pos.IsZero()
|
||||
)
|
||||
Report.critical_result(
|
||||
Tests.trigger_pos_found, trigger.initial_pos is not None and not trigger.initial_pos.IsZero()
|
||||
)
|
||||
|
||||
sphere.initial_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere.id)
|
||||
|
||||
level_correct = (
|
||||
(abs(sphere.initial_pos.y - force_region.initial_pos.y) < CLOSE_ENOUGH)
|
||||
and (abs(sphere.initial_pos.y - trigger.initial_pos.y) < CLOSE_ENOUGH)
|
||||
and (abs(sphere.initial_pos.x - force_region.initial_pos.x) < CLOSE_ENOUGH)
|
||||
and (abs(sphere.initial_pos.x - trigger.initial_pos.x) < CLOSE_ENOUGH)
|
||||
and (sphere.initial_pos.z > force_region.initial_pos.z > trigger.initial_pos.z)
|
||||
and sphere.initial_velocity.IsClose(INITIAL_VELOCITY, CLOSE_ENOUGH)
|
||||
)
|
||||
|
||||
Report.critical_result(Tests.level_setup, level_correct)
|
||||
|
||||
sphere.current_pos = sphere.initial_pos
|
||||
force_region.current_pos = force_region.initial_pos
|
||||
trigger.current_pos = trigger.initial_pos
|
||||
force_region.expected_force_direction = sphere.initial_velocity.MultiplyFloat(-1.0)
|
||||
force_region.expected_force_direction.Normalize()
|
||||
sphere.current_velocity = sphere.initial_velocity
|
||||
sphere.initial_velocity_magnitude = sphere.initial_velocity.GetLength()
|
||||
|
||||
# 3.5) Set up variables and handler for observing force region interaction
|
||||
|
||||
def done_collecting_results():
|
||||
|
||||
# Update current positions
|
||||
sphere.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere.id)
|
||||
force_region.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", force_region.id)
|
||||
trigger.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", trigger.id)
|
||||
|
||||
return force_region.entered and sphere.check_for_stop()
|
||||
|
||||
# Force Region Event Handler
|
||||
def on_calc_net_force(args):
|
||||
if args[0].Equal(force_region.id):
|
||||
if args[1].Equal(sphere.id):
|
||||
if not force_region.entered:
|
||||
force_region.entered = True
|
||||
force_region.object_entered = sphere
|
||||
force_region.actual_force_vector = args[2]
|
||||
force_region.actual_force_magnitude = args[3]
|
||||
Report.info("Entity: {} entered entity: {}'s volume".format(sphere.name, force_region.name))
|
||||
|
||||
def on_trigger_entered(args):
|
||||
if args[0].Equal(sphere.id):
|
||||
trigger.triggered = True
|
||||
trigger.triggering_obj = sphere
|
||||
|
||||
# Assign event handlers
|
||||
force_region.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
force_region.handler.connect(None)
|
||||
force_region.handler.add_callback("OnCalculateNetForce", on_calc_net_force)
|
||||
|
||||
trigger.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
trigger.handler.connect(trigger.id)
|
||||
trigger.handler.add_callback("OnTriggerEnter", on_trigger_entered)
|
||||
|
||||
# 4) Execute test until exit condition is met
|
||||
Report.critical_result(Tests.timed_out, helper.wait_for_condition(done_collecting_results, TIME_OUT))
|
||||
|
||||
# 5) Log results
|
||||
Report.result(Tests.damping_force_entered, force_region.entered)
|
||||
Report.result(
|
||||
Tests.damping_force_expected,
|
||||
force_region.actual_force_vector.IsClose(force_region.expected_force_direction, CLOSE_ENOUGH),
|
||||
)
|
||||
Report.result(Tests.sphere_slowed, sphere.slowed)
|
||||
Report.result(Tests.sphere_stopped, sphere.stopped)
|
||||
Report.result(Tests.trigger_not_triggered, not trigger.triggered)
|
||||
Report.result(Tests.force_region_no_move, force_region.initial_pos.IsClose(force_region.current_pos, CLOSE_ENOUGH))
|
||||
Report.result(Tests.trigger_no_move, trigger.initial_pos.IsClose(trigger.current_pos, CLOSE_ENOUGH))
|
||||
|
||||
# 5.5) Collected Data Dump
|
||||
Report.info(" ********** Collected Data ***************")
|
||||
Report.info("{}:".format(sphere.name))
|
||||
Report.info_vector3(sphere.initial_pos, " Initial position:")
|
||||
Report.info_vector3(sphere.current_pos, " Final position:")
|
||||
Report.info_vector3(sphere.initial_velocity, " Initial velocity:")
|
||||
Report.info_vector3(sphere.current_velocity, " Final velocity:")
|
||||
Report.info(" Slowed: {}".format(sphere.slowed))
|
||||
Report.info(" Stopped: {}".format(sphere.stopped))
|
||||
Report.info("***********************************")
|
||||
Report.info("{}:".format(force_region.name))
|
||||
Report.info_vector3(force_region.initial_pos, " Initial position:")
|
||||
Report.info_vector3(force_region.current_pos, " Final position:")
|
||||
Report.info_vector3(force_region.expected_force_direction, " Expected Force Direction:")
|
||||
Report.info_vector3(
|
||||
force_region.actual_force_vector, " Actual Force Direction:", force_region.actual_force_magnitude
|
||||
)
|
||||
Report.info(" Entered: {}".format(force_region.entered))
|
||||
Report.info(" Object Entered: {}".format(force_region.object_entered.name))
|
||||
Report.info("***********************************")
|
||||
Report.info("{}:".format(trigger.name))
|
||||
Report.info_vector3(trigger.initial_pos, " Initial position:")
|
||||
Report.info_vector3(trigger.current_pos, " Final position:")
|
||||
Report.info(" Triggered: {}".format(trigger.triggered))
|
||||
Report.info(" Triggering Object: {}".format(trigger.triggering_obj))
|
||||
Report.info("***********************************")
|
||||
|
||||
# 6) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
Report.info("*** FINISHED TEST ***")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_LinearDampingForceOnRigidBodies)
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5932041
|
||||
# Test Case Title : Check that force region exerts local space force on rigid bodies
|
||||
|
||||
# Sphere drops and is acted upon in an upward and positive x-ward direction by a force
|
||||
# with a magnitude close to the assigned force region magnitude when it reaches the force region.
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("Sphere entity found", "Sphere entity not found")
|
||||
find_box = ("Box entity found", "Box entity not found")
|
||||
sphere_pos_found = ("Sphere position found", "Sphere position not found")
|
||||
sphere_velocity_found = ("Sphere has downward velocity", "Sphere does not have downward velocity")
|
||||
box_pos_found = ("Box position found", "Box position not found")
|
||||
force_region_entered = ("Force region entered", "Force region never entered")
|
||||
force_x_component_detected = ("Force x-component detected on the Sphere", "Force x-component was not detected on the Sphere")
|
||||
force_z_component_detected = ("Force z-component detected on the Sphere", "Force z-component was not detected on the Sphere")
|
||||
force_y_component_not_detected = ("Force y-component not detected on the Sphere", "Force y-component was detected on the Sphere")
|
||||
force_magnitude_detected = ("Force magnitude detected on the Sphere", "Force magnitude was not detected on the Sphere")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_LocalSpaceForceOnRigidBodies():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that when a rigid body enters a force region a local space force is exerted.
|
||||
|
||||
Level Description:
|
||||
Box (entity) - suspended above terrain at 45 degree angle with Direction Z = 1.0, magnitude = 1000,
|
||||
and gravity disabled; contains box mesh, PhysX Collider, and PhysX Force Region
|
||||
Sphere (entity) - suspended above Box with slight x-axis offset, initial velocity in negative z direction,
|
||||
gravity disabled; contains sphere mesh, PhysX Rigid Body, PhysX Collider
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the Sphere entity will travel torward the terrain.
|
||||
It will reach the force region of the Box entity and be imbued with a net force in the x and z directions.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve entities
|
||||
4) Log Sphere velocity and positions for Sphere and Box
|
||||
5) Set up handler and variables
|
||||
6) Wait for force region entry or time out
|
||||
7) Look for positive x, zero y, positive z force with a valid magnitude, and report findings
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 2.0
|
||||
TOLERANCE = 1
|
||||
MAGNITUDE = 1000 # Magnitude assigned to the force region
|
||||
FORCE_Y_TOLERANCE = sys.float_info.epsilon
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_LocalSpaceForceOnRigidBodies")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
box_id = general.find_game_entity("Box")
|
||||
|
||||
Report.critical_result(Tests.find_sphere, sphere_id.isValid())
|
||||
Report.critical_result(Tests.find_box, box_id.isValid())
|
||||
|
||||
# 4) Log Sphere velocity and positions for Sphere and Box
|
||||
sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
|
||||
box_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
|
||||
# Validate and print positions and sphere velocity
|
||||
sphere_pos_found = sphere_pos is not None and sphere_pos.x != 0 and sphere_pos.y != 0 and sphere_pos.z != 0
|
||||
Report.critical_result(Tests.sphere_pos_found, sphere_pos_found)
|
||||
Report.info_vector3(sphere_pos, "Sphere Position:")
|
||||
|
||||
sphere_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere_id)
|
||||
Report.critical_result(Tests.sphere_velocity_found, sphere_velocity.z < 0)
|
||||
Report.info_vector3(sphere_velocity, "Sphere Initial Velocity:")
|
||||
|
||||
box_pos_found = box_pos is not None and box_pos.x != 0 and box_pos.y != 0 and box_pos.z != 0
|
||||
Report.critical_result(Tests.box_pos_found, box_pos_found)
|
||||
Report.info_vector3(box_pos, "Box Position:")
|
||||
|
||||
# 5) Set up handler and variables
|
||||
class RegionData:
|
||||
force_region_entered = False
|
||||
force_vector = None
|
||||
force_magnitude = 0
|
||||
|
||||
# Force Region Event Handler
|
||||
def on_force_region_entered(args):
|
||||
region_id = args[0]
|
||||
object_id = args[1]
|
||||
force_vector = args[2]
|
||||
force_magnitude = args[3]
|
||||
if region_id.Equal(box_id) and object_id.Equal(sphere_id):
|
||||
if not RegionData.force_region_entered:
|
||||
RegionData.force_region_entered = True
|
||||
RegionData.force_vector = force_vector
|
||||
RegionData.force_magnitude = force_magnitude
|
||||
Report.info("Force Region entered")
|
||||
|
||||
# Assign the handler
|
||||
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
handler.connect(None)
|
||||
handler.add_callback("OnCalculateNetForce", on_force_region_entered)
|
||||
|
||||
# 6) Wait for force region entry or time out
|
||||
helper.wait_for_condition(lambda: RegionData.force_region_entered, TIMEOUT)
|
||||
|
||||
# 7) Look for positive x, positive z force with a valid magnitude, and report findings
|
||||
force_x_component_detected = RegionData.force_vector.x > 0
|
||||
force_z_component_detected = RegionData.force_vector.z > 0
|
||||
force_y_component_detected = abs(RegionData.force_vector.y) > FORCE_Y_TOLERANCE
|
||||
force_magnitude_detected = abs(RegionData.force_magnitude - MAGNITUDE) < TOLERANCE
|
||||
|
||||
Report.result(Tests.force_region_entered, RegionData.force_region_entered)
|
||||
Report.info_vector3(RegionData.force_vector, "Force vector detected", RegionData.force_magnitude)
|
||||
Report.result(Tests.force_x_component_detected, force_x_component_detected)
|
||||
Report.result(Tests.force_z_component_detected, force_z_component_detected)
|
||||
Report.result(Tests.force_y_component_not_detected, not force_y_component_detected)
|
||||
Report.result(Tests.force_magnitude_detected, force_magnitude_detected)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_LocalSpaceForceOnRigidBodies)
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5968760
|
||||
# Test Case Title : Check moving force region changes net force
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("SphereRigidBody found", "SphereRigidBody not found")
|
||||
find_force_region = ("ForceRegionBox is found", "ForceRegionBox is not found")
|
||||
sphere_dropped = ("Sphere dropped down", "Sphere did not drop down")
|
||||
sphere_bounced = ("Sphere bounced to its left", "Sphere did not bounce to its left")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_MovingForceRegionChangesNetForce():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Check moving force region changes net force.
|
||||
|
||||
Level Description:
|
||||
The SphereRigidBody entity is placed above the ForceRegionBox entity.
|
||||
ForceRegionBox (entity) - Entity with PhysX Force Region, Mesh, PhysX Collider
|
||||
SphereRigidBody (entity) - Entity with PhysX Rigid body, Mesh and collider components
|
||||
|
||||
Expected Behavior:
|
||||
We are checking if the ball falls down initially and then moving the force region to the right to verify if the
|
||||
ball bounces off to the left when it collides with the force region.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Get the initial position of the Sphere (rigid body)
|
||||
5) Move the object to right (X - direction) and rotate in Y - direction
|
||||
6) Check if the ball is falling down
|
||||
7) Add force region notification handler
|
||||
8) Wait till the ball enters the force region
|
||||
9) Check if the ball has bounced and moved left
|
||||
10) Exit game mode
|
||||
11) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 3.0 # wait a maximum of 3 seconds
|
||||
SPHERE_RADIUS = 0.5
|
||||
TRANSLATION_OFFSET = 0.2
|
||||
ROTATION_OFFSET = -0.005
|
||||
|
||||
class Sphere:
|
||||
id = None
|
||||
initial_position = None
|
||||
current_position = None
|
||||
z_at_collision = None
|
||||
in_force_region = False
|
||||
bounced = False
|
||||
|
||||
class ForceRegion:
|
||||
id = None
|
||||
translation_position = None
|
||||
rotation_position = None
|
||||
|
||||
def sphere_bounced():
|
||||
Sphere.current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
|
||||
bounced_up = Sphere.current_position.z > Sphere.z_at_collision + SPHERE_RADIUS
|
||||
bounced_left = Sphere.current_position.x < Sphere.initial_position.x
|
||||
Sphere.bounced = bounced_left and bounced_up
|
||||
return Sphere.bounced
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_MovingForceRegionChangesNetForce")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
Sphere.id = general.find_game_entity("SphereRigidBody")
|
||||
Report.critical_result(Tests.find_sphere, Sphere.id.IsValid())
|
||||
ForceRegion.id = general.find_game_entity("ForceRegionBox")
|
||||
Report.critical_result(Tests.find_force_region, ForceRegion.id.IsValid())
|
||||
|
||||
# 4) Get the initial position of the Sphere (rigid body)
|
||||
Sphere.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
|
||||
|
||||
# 5) Move the object to right (X - direction) and rotate in Y - direction
|
||||
ForceRegion.translation_position = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldTranslation", ForceRegion.id
|
||||
)
|
||||
azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "SetWorldX", ForceRegion.id, (ForceRegion.translation_position.x + TRANSLATION_OFFSET)
|
||||
)
|
||||
# Rotation in y direction anti clockwise
|
||||
ForceRegion.rotation_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", ForceRegion.id)
|
||||
azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "RotateAroundLocalY", ForceRegion.id, (ForceRegion.rotation_position.y + ROTATION_OFFSET)
|
||||
)
|
||||
Report.info("The force region has been repositioned")
|
||||
|
||||
# 6) Check if the ball is falling down
|
||||
Sphere.current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
|
||||
sphere_dropped = Sphere.current_position.z < (Sphere.initial_position.z + SPHERE_RADIUS)
|
||||
Report.critical_result(Tests.sphere_dropped, sphere_dropped)
|
||||
|
||||
# 7) Add force region notification handler
|
||||
def on_force_region_entered(args):
|
||||
region_id = args[0]
|
||||
object_id = args[1]
|
||||
if region_id.Equal(ForceRegion.id) and object_id.Equal(Sphere.id):
|
||||
if not Sphere.in_force_region:
|
||||
Sphere.in_force_region = True
|
||||
Report.info("Force Region entered")
|
||||
|
||||
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
force_notification_handler.connect(None)
|
||||
force_notification_handler.add_callback("OnCalculateNetForce", on_force_region_entered)
|
||||
|
||||
# 8) Wait till the ball enters the force region
|
||||
helper.wait_for_condition(lambda: Sphere.in_force_region, TIMEOUT)
|
||||
# sphere z position when it entered force region
|
||||
Sphere.z_at_collision = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id).z
|
||||
|
||||
# 9) Check if the ball has bounced and moved left
|
||||
# wait frames till the ball bounces
|
||||
helper.wait_for_condition(sphere_bounced, TIMEOUT)
|
||||
Report.result(Tests.sphere_bounced, Sphere.bounced)
|
||||
|
||||
# 10) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_MovingForceRegionChangesNetForce)
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5968759
|
||||
# Test Case Title : Check nested force regions exert forces simultaneously on rigid body
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_vertical_sphere = ("Vertical sphere found", "Vertical sphere not found")
|
||||
find_angled_sphere = ("Angled sphere found", "Angled sphere not found")
|
||||
find_point_force_region = ("Point force region found", "Force Region not found")
|
||||
find_angled_force_region = ("Angled force region found", "Angled force region not found")
|
||||
vertical_entered_force_region = ("Vertical Sphere actions completed", "Vertical Sphere actions not completed")
|
||||
timed_out = ("Test did not time out", "Test TIMED OUT")
|
||||
angled_sphere_enter_force_region = ("Angled Sphere Entered Force Region", "Angled Sphere didn't enter Force Region")
|
||||
vertical_sphere_fell_vertically = ("Vertical Sphere fell vertically", "Vertical Sphere didn't fall vertically")
|
||||
angled_sphere_fell_at_angle = ("Angled Sphere fell at an angle", "Angled Sphere didn't fall at an angle")
|
||||
vertical_sphere_slowed = ("Vertical Sphere slowed", "Vertical Sphere not slowed")
|
||||
angled_sphere_slowed = ("Angled Sphere slowed", "Angled Sphere not slowed")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
|
||||
# fmt: on
|
||||
|
||||
import os, sys
|
||||
|
||||
|
||||
def ForceRegion_MultipleComponentsCombineForces():
|
||||
"""
|
||||
Run() will open a a level and validate that the spheres are affected by the force regions as expected.
|
||||
|
||||
Expected Results: Both spheres fall into the force regions and are slowed. One of the spheres also falls at an angle
|
||||
|
||||
It does this by:
|
||||
--> Opens level and enter game mode
|
||||
--> Finds the entities in the scene
|
||||
--> Listens for spheres to enter the force regions
|
||||
--> Set Spheres start position and velocity
|
||||
--> Listen for spheres to exit force regions
|
||||
--> Set Spheres end position and velocity
|
||||
--> Validate the results
|
||||
--> Exits game mode and editor
|
||||
|
||||
Level Description: Two spheres floating above 2 force regions.
|
||||
Sphere: 1 Name = "Sphere_vertical_drop" This sphere should fall vertically
|
||||
Sphere: 2 Name = "Sphere_angled_drop" This sphere should fall at an angle
|
||||
First force region: Name = "Force Region Point" Applies point force along the X axis to only the second sphere
|
||||
Second force region: Name = "Force Region Simple Drag" Applies a drag force on both spheres
|
||||
Setup path
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.physics
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 6.0 # Second to wait before timing out
|
||||
POSITION_TOLERANCE = 0.1
|
||||
|
||||
def is_close_XY_position(vec1, vec2):
|
||||
return abs(vec1.x - vec2.x) < POSITION_TOLERANCE and abs(vec1.y - vec2.y) < POSITION_TOLERANCE
|
||||
|
||||
# Holds details about the sphere
|
||||
class Sphere:
|
||||
def __init__(self, sphere_id, sphere_name):
|
||||
self.name = sphere_name
|
||||
self.id = sphere_id
|
||||
self.start_position = None
|
||||
self.end_position = None
|
||||
self.start_velocity = None
|
||||
self.end_velocity = None
|
||||
self.entered_force_region = False
|
||||
self.exited_force_region = False
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def get_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
|
||||
# 1) Opens level with spheres above a force region
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "ForceRegion_MultipleComponentsCombineForces")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Finds the entities in the scene
|
||||
sphere_vertical = Sphere(general.find_game_entity("Sphere_vertical_drop"), "Sphere Vertical")
|
||||
Report.critical_result(Tests.find_vertical_sphere, sphere_vertical.id.IsValid())
|
||||
|
||||
sphere_angled = Sphere(general.find_game_entity("Sphere_angled_drop"), "Sphere Angled")
|
||||
Report.critical_result(Tests.find_angled_sphere, sphere_angled.id.IsValid())
|
||||
|
||||
point_force_region_id = general.find_game_entity("Force Region Point")
|
||||
Report.critical_result(Tests.find_point_force_region, point_force_region_id.IsValid())
|
||||
|
||||
simple_drag_force_region_id = general.find_game_entity("Force Region Simple Drag")
|
||||
Report.critical_result(Tests.find_angled_force_region, simple_drag_force_region_id.IsValid())
|
||||
|
||||
# ******** Handler Functions ********
|
||||
|
||||
# Called if Sphere enters force region
|
||||
def on_trigger_begin(args):
|
||||
other_id = args[0]
|
||||
# 4) Gets start position and velocity of spheres
|
||||
if other_id.Equal(sphere_vertical.id) and sphere_vertical.entered_force_region is False:
|
||||
Report.info("Trigger Entered")
|
||||
sphere_vertical.entered_force_region = True
|
||||
sphere_vertical.start_position = sphere_vertical.get_position()
|
||||
sphere_vertical.start_velocity = sphere_vertical.get_velocity()
|
||||
Report.result(Tests.vertical_entered_force_region, sphere_vertical.entered_force_region)
|
||||
elif other_id.Equal(sphere_angled.id) and sphere_angled.entered_force_region is False:
|
||||
sphere_angled.entered_force_region = True
|
||||
sphere_angled.start_position = sphere_angled.get_position()
|
||||
sphere_angled.start_velocity = sphere_angled.get_velocity()
|
||||
Report.result(Tests.angled_sphere_enter_force_region, sphere_angled.entered_force_region)
|
||||
|
||||
def on_trigger_exit(args):
|
||||
# 4) Gets end position and velocity of spheres
|
||||
other_id = args[0]
|
||||
if other_id.Equal(sphere_vertical.id):
|
||||
Report.info("Trigger exited")
|
||||
sphere_vertical.end_position = sphere_vertical.get_position()
|
||||
sphere_vertical.end_velocity = sphere_vertical.get_velocity()
|
||||
sphere_vertical.exited_force_region = True
|
||||
|
||||
elif other_id.Equal(sphere_angled.id):
|
||||
Report.info("Trigger exited")
|
||||
sphere_angled.end_position = sphere_angled.get_position()
|
||||
sphere_angled.end_velocity = sphere_angled.get_velocity()
|
||||
|
||||
sphere_angled.exited_force_region = True
|
||||
|
||||
# 3) Listens for spheres to enter the force regions
|
||||
# Create a handler for each force region
|
||||
point_force_region_handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
point_force_region_handler.connect(point_force_region_id)
|
||||
point_force_region_handler.add_callback("OnTriggerEnter", on_trigger_begin)
|
||||
point_force_region_handler.add_callback("OnTriggerExit", on_trigger_exit)
|
||||
|
||||
simple_drag_force_region_handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
simple_drag_force_region_handler.connect(simple_drag_force_region_id)
|
||||
simple_drag_force_region_handler.add_callback("OnTriggerEnter", on_trigger_begin)
|
||||
simple_drag_force_region_handler.add_callback("OnTriggerExit", on_trigger_exit)
|
||||
|
||||
Report.result(Tests.timed_out, helper.wait_for_condition(lambda: sphere_angled.exited_force_region and
|
||||
sphere_vertical.exited_force_region, TIME_OUT))
|
||||
|
||||
sphere_vertical_slowed_by_force_region = sphere_vertical.end_velocity.z > sphere_vertical.start_velocity.z
|
||||
sphere_angled_slowed_by_force_region = sphere_angled.end_velocity.z > sphere_angled.start_velocity.z
|
||||
|
||||
sphere_angled_fell_at_expected_angle = sphere_angled.end_position.x > sphere_angled.start_position.x + POSITION_TOLERANCE
|
||||
sphere_vertical_fell_at_expected_angle = is_close_XY_position(sphere_vertical.end_position, sphere_vertical.start_position)
|
||||
|
||||
Report.result(Tests.angled_sphere_fell_at_angle, sphere_angled_fell_at_expected_angle)
|
||||
Report.result(Tests.vertical_sphere_fell_vertically, sphere_vertical_fell_at_expected_angle)
|
||||
Report.result(Tests.angled_sphere_slowed, sphere_angled_slowed_by_force_region)
|
||||
Report.result(Tests.vertical_sphere_slowed, sphere_vertical_slowed_by_force_region)
|
||||
|
||||
# 8) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_MultipleComponentsCombineForces)
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5959810
|
||||
# Test Case Title : Check that multiple forces in single force region create correct net force
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
sphere_exists = ("Sphere has been found", "Sphere has not been found")
|
||||
force_region_exists = ("Force Region has been found", "Force Region has not been found")
|
||||
sphere_position_found = ("Sphere position found", "Sphere position not found")
|
||||
force_region_position_found = ("Force Region position found", "Force Region position not found")
|
||||
orientation_before_collision = ("Sphere is above Force Region", "Sphere is not above Force Region")
|
||||
sphere_velocity_found = ("Sphere velocity found", "Sphere velocity not found")
|
||||
sphere_velocity_before_collision = ("Sphere has valid initial velocity", "Sphere initial velocity not valid")
|
||||
collision = ("Collision has occurred", "No collision has occurred")
|
||||
force_applied = ("Forces were combined correctly", "Forces were not applied correctly")
|
||||
new_velocity_applied = ("Sphere velocity has been updated", "Sphere velocity was never updated")
|
||||
orientation_after_collision = ("Sphere is above and to the left", "Entity orientation is not valid")
|
||||
sphere_velocity_post_collision = ("Sphere velocity valid post-collision", "Sphere velocity no longer valid")
|
||||
force_region_has_not_moved = ("Force Region has not moved", "Force Region has somehow moved")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_MultipleForcesInSameComponentCombineForces():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary: Runs an automated test to ensure that separate forces in a force region add correctly
|
||||
|
||||
Level Description:
|
||||
Sphere - Placed directly above the force region with an initial velocity in the -z direction;
|
||||
has PhysX Rigid Body, sphere shaped PhysX Collider, Sphere Shape
|
||||
Force Region - Placed directly under the sphere, has a point, and world space force in the
|
||||
z and negative x direction respectively; has PhysX Force Region, box shaped PhysX Collider
|
||||
|
||||
Expected Behavior: Sphere collides with force region and is sent in the negative x and positive z direction
|
||||
|
||||
Test Steps:
|
||||
1) Load Level
|
||||
2) Enter Game Mode
|
||||
3) Find Entities
|
||||
4) Validate initial positions and velocity
|
||||
5) Set up handler
|
||||
6) Wait for Sphere collision with Force Region
|
||||
7) Validate and Log Results
|
||||
8) Exit Game Mode
|
||||
9) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 1
|
||||
FLOAT_THRESHOLD = sys.float_info.epsilon
|
||||
|
||||
# Helper Functions
|
||||
class Collision:
|
||||
happened = False
|
||||
force_vector = None
|
||||
force_magnitude = None
|
||||
velocity_now_updated = False
|
||||
|
||||
class Entity:
|
||||
def __init__(self, name):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.initial_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
self.final_velocity = None
|
||||
self.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
self.final_position = None
|
||||
|
||||
def get_final_position_and_velocity(self):
|
||||
self.final_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
self.final_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def report_sphere_values(self):
|
||||
Report.info_vector3(self.initial_position, "{} initial position: ".format(self.name))
|
||||
Report.info_vector3(self.initial_velocity, "{} initial velocity: ".format(self.name))
|
||||
|
||||
Report.info_vector3(self.final_position, "{} final position: ".format(self.name))
|
||||
Report.info_vector3(self.final_velocity, "{} final velocity: ".format(self.name))
|
||||
|
||||
def report_force_region_values(self):
|
||||
Report.info_vector3(self.initial_position, "{} initial position: ".format(self.name))
|
||||
Report.info_vector3(self.final_position, "{} final position: ".format(self.name))
|
||||
|
||||
def validate_positions(collision_happened, sphere_position, force_region_position):
|
||||
if collision_happened:
|
||||
result = sphere_position.x < force_region_position.x
|
||||
else:
|
||||
result = abs(sphere_position.x - force_region_position.x) < FLOAT_THRESHOLD
|
||||
|
||||
return (
|
||||
result
|
||||
and sphere_position.z > force_region_position.z
|
||||
and abs(sphere_position.y - force_region_position.y) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
def validate_sphere_velocity(collision_happened, sphere_velocity_vector):
|
||||
if collision_happened:
|
||||
x_result = sphere_velocity_vector.x < 0
|
||||
z_result = sphere_velocity_vector.z > 0
|
||||
else:
|
||||
x_result = abs(sphere_velocity_vector.x) < FLOAT_THRESHOLD
|
||||
z_result = sphere_velocity_vector.z < 0
|
||||
|
||||
return x_result and z_result and abs(sphere_velocity_vector.y) < FLOAT_THRESHOLD
|
||||
|
||||
def vector_valid(vector, can_be_zero):
|
||||
if can_be_zero:
|
||||
return vector != None
|
||||
else:
|
||||
return vector != None and not vector.IsZero()
|
||||
|
||||
def on_collision_begin(args):
|
||||
assert force_region.id.Equal(args[0])
|
||||
|
||||
if sphere.id.equal(args[1]):
|
||||
Collision.happened = True
|
||||
Report.info("Collision has begun")
|
||||
if vector_valid(args[2], False):
|
||||
Collision.force_vector = args[2]
|
||||
Collision.force_magnitude = args[3]
|
||||
|
||||
def force_valid(vector, magnitude):
|
||||
return magnitude > 0 and vector.x < 0 and vector.z > 0 and abs(vector.y) < FLOAT_THRESHOLD
|
||||
|
||||
def velocity_update_check():
|
||||
current_velocity_vector = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere.id)
|
||||
velocity_updated = (
|
||||
current_velocity_vector.x < sphere.initial_velocity.x
|
||||
and current_velocity_vector.z > sphere.initial_velocity.z
|
||||
)
|
||||
if velocity_updated:
|
||||
Collision.velocity_now_updated = True
|
||||
|
||||
return velocity_updated
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load Level
|
||||
helper.open_level("physics", "ForceRegion_MultipleForcesInSameComponentCombineForces")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Find Entities
|
||||
sphere = Entity("Sphere")
|
||||
force_region = Entity("Force_Region")
|
||||
|
||||
Report.critical_result(Tests.sphere_exists, sphere.id.isValid())
|
||||
Report.critical_result(Tests.force_region_exists, force_region.id.isValid())
|
||||
|
||||
# 4) Validate initial positions and velocity
|
||||
# Position validation
|
||||
Report.critical_result(Tests.sphere_position_found, vector_valid(sphere.initial_position, False))
|
||||
Report.critical_result(Tests.force_region_position_found, vector_valid(force_region.initial_position, False))
|
||||
# Velocity validation
|
||||
Report.critical_result(Tests.sphere_velocity_found, vector_valid(sphere.initial_velocity, False))
|
||||
|
||||
# Value validation
|
||||
Report.critical_result(
|
||||
Tests.orientation_before_collision,
|
||||
validate_positions(Collision.happened, sphere.initial_position, force_region.initial_position),
|
||||
)
|
||||
Report.critical_result(
|
||||
Tests.sphere_velocity_before_collision, validate_sphere_velocity(Collision.happened, sphere.initial_velocity)
|
||||
)
|
||||
|
||||
# 5) Set up handler
|
||||
handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
handler.connect(None)
|
||||
handler.add_callback("OnCalculateNetForce", on_collision_begin)
|
||||
|
||||
# 6) Wait for Sphere collision with Force Region and Velocity application
|
||||
helper.wait_for_condition(lambda: Collision.happened, TIMEOUT)
|
||||
helper.wait_for_condition(velocity_update_check, TIMEOUT)
|
||||
|
||||
# 7) Validate and Log Results
|
||||
sphere.get_final_position_and_velocity()
|
||||
force_region.get_final_position_and_velocity()
|
||||
|
||||
# Value validation
|
||||
Report.result(Tests.new_velocity_applied, Collision.velocity_now_updated)
|
||||
Report.result(Tests.collision, Collision.happened)
|
||||
Report.result(Tests.force_applied, force_valid(Collision.force_vector, Collision.force_magnitude))
|
||||
Report.result(
|
||||
Tests.orientation_after_collision,
|
||||
validate_positions(Collision.happened, sphere.final_position, force_region.final_position),
|
||||
)
|
||||
Report.result(
|
||||
Tests.sphere_velocity_post_collision, validate_sphere_velocity(Collision.happened, sphere.final_velocity)
|
||||
)
|
||||
Report.result(
|
||||
Tests.force_region_has_not_moved,
|
||||
force_region.final_position.Subtract(force_region.initial_position).IsZero(FLOAT_THRESHOLD),
|
||||
)
|
||||
# Value logging
|
||||
sphere.report_sphere_values()
|
||||
force_region.report_force_region_values()
|
||||
Report.info_vector3(Collision.force_vector, "Applied Force: ", Collision.force_magnitude)
|
||||
|
||||
# 8) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_MultipleForcesInSameComponentCombineForces)
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C15845879
|
||||
# Test Case Title : Check that linear damping with high values do not make the object to quiver
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
sphere_found = ("Found sphere", "Did not find sphere")
|
||||
force_region_found = ("Found force region", "Did not find force region")
|
||||
check_relative_position = ("Sphere is above force region", "Sphere isn't above force region")
|
||||
sphere_moving_down = ("Sphere heading to force region", "Sphere has invalid initial velocity")
|
||||
sphere_entered_force_region = ("Sphere has entered force region", "Sphere never entered force region")
|
||||
sphere_stopped_moving = ("Sphere final velocity is zero", "Sphere final velocity invalid")
|
||||
sphere_still_above_force_region = ("Sphere still above force region", "Sphere not above force region")
|
||||
no_quiver = ("Sphere is not quivering", "Sphere quivering in force region")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_NoQuiverOnHighLinearDampingForce():
|
||||
"""
|
||||
Summary: Check that linear damping with high values do not make the object to quiver
|
||||
|
||||
Level Description:
|
||||
sphere - Starts above the force_region entity with initial velocity in the negative z direction and
|
||||
gravity disabbled; has physx collider in sphere shape, physx rigid body, and sphere shape
|
||||
force_region - Sits below sphere entity, has linear damping force set at 100 and region has scaling
|
||||
(5,5,5); has physx collider in box shape and physx force region
|
||||
|
||||
Expected Behavior: Sphere falls into force region and is stuck by the damping force. It specifically should
|
||||
not quiver up and down.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Setup handler and wait for sphere to enter force region
|
||||
5) Validate the Sphere remains in Force Region
|
||||
6) Check to see if the sphere is quivering
|
||||
7) Exit Game Mode
|
||||
8) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = sys.float_info.epsilon
|
||||
TIMEOUT = 1
|
||||
VELOCITY_THRESHOLD = 0.01
|
||||
QUIVER_THRESHOLD = 0.01
|
||||
SLOWDOWN_FRAMES = 30
|
||||
SPHERE_STOP_OFFSET = 3.5
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.force_region_id = None
|
||||
self.entered_force_region = False
|
||||
self.quiver_reference = None
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
# type () -> Vector3
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
# type () -> Vector3
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
@property
|
||||
def is_moving_up(self):
|
||||
# type () -> bool
|
||||
return (
|
||||
abs(self.velocity.x) < FLOAT_THRESHOLD
|
||||
and abs(self.velocity.y) < FLOAT_THRESHOLD
|
||||
and self.velocity.z > 0.0
|
||||
)
|
||||
|
||||
def set_handler(self):
|
||||
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
self.handler.connect(None)
|
||||
self.handler.add_callback("OnCalculateNetForce", self.on_calculate_net_force)
|
||||
|
||||
def on_calculate_net_force(self, args):
|
||||
# type (list) -> None
|
||||
# Flips the collision happened boolean for the sphere object and prints the force values.
|
||||
if self.force_region_id.Equal(args[0]) and self.id.Equal(args[1]) and not self.entered_force_region:
|
||||
self.entered_force_region = True
|
||||
|
||||
def sphere_not_quivering():
|
||||
# type () -> bool
|
||||
# Returns False if sphere "quivers" from its initial position, True if it stays close to it's original position
|
||||
return abs(sphere.position.z - sphere.quiver_reference) > QUIVER_THRESHOLD
|
||||
|
||||
def sphere_above_force_region(sphere_position, force_region_position):
|
||||
# type () -> bool
|
||||
return (
|
||||
abs(sphere_position.x - force_region_position.x) < FLOAT_THRESHOLD
|
||||
and abs(sphere_position.y - force_region_position.y) < FLOAT_THRESHOLD
|
||||
and sphere_position.z > force_region_position.z
|
||||
)
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "ForceRegion_NoQuiverOnHighLinearDampingForce")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
sphere = Entity("sphere")
|
||||
force_region = Entity("force_region")
|
||||
|
||||
sphere.force_region_id = force_region.id
|
||||
Report.critical_result(Tests.sphere_moving_down, not sphere.is_moving_up)
|
||||
Report.critical_result(
|
||||
Tests.check_relative_position, sphere_above_force_region(sphere.position, force_region.position)
|
||||
)
|
||||
|
||||
# 4) Setup handler and wait for sphere to enter force region
|
||||
sphere.set_handler()
|
||||
Report.critical_result(
|
||||
Tests.sphere_entered_force_region, helper.wait_for_condition(lambda: sphere.entered_force_region, TIMEOUT)
|
||||
)
|
||||
|
||||
# 5) Validate the Sphere remains in Force Region
|
||||
# Must wait for the sphere to slow down
|
||||
Report.result(Tests.sphere_stopped_moving, helper.wait_for_condition(lambda: sphere.velocity.IsZero(VELOCITY_THRESHOLD), TIMEOUT))
|
||||
# Force region has scaling (5,5,5). Thus the upper edge of the force region is 2.5m above the transform. With proper offset we can
|
||||
# see that sphere is stuck on top of the force region and did not bounce off.
|
||||
Report.result(Tests.sphere_still_above_force_region, (sphere.position.z - force_region.position.z) < SPHERE_STOP_OFFSET)
|
||||
|
||||
# 6) Check to see if the sphere is quivering
|
||||
sphere.quiver_reference = sphere.position.z
|
||||
Report.result(Tests.no_quiver, not helper.wait_for_condition(sphere_not_quivering, TIMEOUT))
|
||||
|
||||
# 7) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_NoQuiverOnHighLinearDampingForce)
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C6090547
|
||||
# Test Case Title : Check that force regions in parent and child entities work together.
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("Sphere found", "Sphere not found")
|
||||
find_parent_force_region = ("Parent Force Region found", "Parent Force Region not found")
|
||||
find_child_force_region = ("Child Force Region found", "Child Force Region not found")
|
||||
find_trigger_box = ("Trigger Box found", "Trigger Box not found")
|
||||
sphere_gravity_disabled = ("Sphere gravity disabled", "Sphere gravity not disabled")
|
||||
parent_force_region_direction = ("Parent Force Region is in positive x direction", "Parent Force Region is not in positive x direction")
|
||||
child_force_region_direction = ("Child Force Region is in positive y direction", "Child Force Region is not in positive y direction")
|
||||
parent_force_on_sphere = ("Parent Force Region applied total force on sphere", "Parent Force Region did not apply total force on sphere")
|
||||
child_force_on_sphere = ("Child Force Region applied total force on sphere", "Child Force Region did not apply total force on sphere")
|
||||
sphere_enters_trigger = ("Sphere entered Trigger", "Sphere did not enter Trigger before Timeout")
|
||||
sphere_exits_trigger = ("Sphere exited Trigger", "Sphere did not exit Trigger before Timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ParentChildForcesCombineForces():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that force regions in parent and child entities work together.
|
||||
|
||||
Level Description:
|
||||
Parent Force Region (entity) - contains PhysX Force Region with world space force which has positive X force
|
||||
Magnitude with Direction (1, 0, 0) and PhysX Collider (box shape).
|
||||
Child Force Region (entity) - contains PhysX Force Region with world space force which has positive Y force
|
||||
Magnitude with Direction (0, 1, 0) and PhysX Collider (box shape).
|
||||
Sphere (entity) - contains a Sphere mesh, PhysX Collider (sphere shape) and PhysX Rigid Body.
|
||||
Sphere located at the low (x, y) corner of where the force regions overlap.
|
||||
Trigger Box (entity) - contains PhysX Collider (box shape)
|
||||
trigger box placed in the (1, 1, 0) direction from the sphere at the opposite end of
|
||||
the force region overlap.
|
||||
Parent and Child force regions are placed above the terrain as two overlapping sheets.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the Sphere should accelerate evenly in the positive (x, y) direction and it should move
|
||||
as much in x as it does in y. Sphere should pass through Trigger Box.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Make sure gravity is off from the start
|
||||
5) Make sure the parent entity is set as the parent of the child entity in level
|
||||
6) Make sure parent and child force regions are in correct directions
|
||||
7) Check parent and child force regions each exert its force on sphere
|
||||
8) Verify sphere passes through trigger box
|
||||
9) Exit game mode
|
||||
10) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 3.0
|
||||
X_DIRECTION = lymath.Vector3(1.0, 0.0, 0.0)
|
||||
Y_DIRECTION = lymath.Vector3(0.0, 1.0, 0.0)
|
||||
EXPECTED_MAGNITUDE = 100.0
|
||||
TOLERANCE = 0.1
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_ParentChildForcesCombineForces")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
Report.critical_result(Tests.find_sphere, sphere_id.IsValid())
|
||||
|
||||
parent_id = general.find_game_entity("Parent Force Region")
|
||||
Report.critical_result(Tests.find_parent_force_region, parent_id.IsValid())
|
||||
|
||||
child_id = general.find_game_entity("Child Force Region")
|
||||
Report.critical_result(Tests.find_child_force_region, child_id.IsValid())
|
||||
|
||||
trigger_box_id = general.find_game_entity("Trigger Box")
|
||||
Report.critical_result(Tests.find_trigger_box, trigger_box_id.IsValid())
|
||||
|
||||
# 4) Make sure gravity is off from the start
|
||||
is_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", sphere_id)
|
||||
Report.critical_result(Tests.sphere_gravity_disabled, not is_gravity_enabled)
|
||||
|
||||
# 5) Make sure the parent entity is set as the parent of the child entity in level
|
||||
id = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetParentId", child_id)
|
||||
if id.Equal(parent_id):
|
||||
Report.info("parent and child force regions are in correct position")
|
||||
|
||||
# 6) Make sure parent and child force regions are in correct directions
|
||||
dir_parent = azlmbr.physics.ForceWorldSpaceRequestBus(azlmbr.bus.Event, "GetDirection", parent_id)
|
||||
dir_child = azlmbr.physics.ForceWorldSpaceRequestBus(azlmbr.bus.Event, "GetDirection", child_id)
|
||||
Report.info_vector3(dir_parent, "Parent force region direction : ")
|
||||
Report.info_vector3(dir_child, "Child force region direction : ")
|
||||
Report.critical_result(Tests.parent_force_region_direction, dir_parent.IsClose(X_DIRECTION, TOLERANCE))
|
||||
Report.critical_result(Tests.child_force_region_direction, dir_child.IsClose(Y_DIRECTION, TOLERANCE))
|
||||
|
||||
# 7) Check parent and child force regions each exert its force on sphere
|
||||
class NetForceMagnitude:
|
||||
parent_force_region_magnitude = 0
|
||||
child_force_region_magnitude = 0
|
||||
|
||||
def on_calc_net_force(args):
|
||||
"""
|
||||
args[0] - force region entity
|
||||
args[1] - entity entering
|
||||
args[2] - vector
|
||||
args[3] - magnitude
|
||||
"""
|
||||
force_region_id = args[0]
|
||||
entering_entity = args[1]
|
||||
if entering_entity.Equal(sphere_id):
|
||||
if force_region_id.Equal(parent_id):
|
||||
NetForceMagnitude.parent_force_region_magnitude = args[3]
|
||||
elif force_region_id.Equal(child_id):
|
||||
NetForceMagnitude.child_force_region_magnitude = args[3]
|
||||
|
||||
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
force_notification_handler.connect(None)
|
||||
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
|
||||
helper.wait_for_condition(lambda : NetForceMagnitude.parent_force_region_magnitude != 0 and NetForceMagnitude.child_force_region_magnitude != 0, 1.0)
|
||||
|
||||
Report.info("Parent Force Region Magnitude on Sphere : {}".format(NetForceMagnitude.parent_force_region_magnitude))
|
||||
Report.info("Child Force Region Magnitude on Sphere : {}".format(NetForceMagnitude.child_force_region_magnitude))
|
||||
Report.critical_result(
|
||||
Tests.parent_force_on_sphere,
|
||||
abs(EXPECTED_MAGNITUDE - NetForceMagnitude.parent_force_region_magnitude) < TOLERANCE,
|
||||
)
|
||||
Report.critical_result(
|
||||
Tests.child_force_on_sphere,
|
||||
abs(EXPECTED_MAGNITUDE - NetForceMagnitude.child_force_region_magnitude) < TOLERANCE,
|
||||
)
|
||||
|
||||
# 8) Verify sphere passes through trigger box
|
||||
class Trigger:
|
||||
on_entered = False
|
||||
on_exited = False
|
||||
|
||||
def on_trigger_enter(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(sphere_id):
|
||||
Trigger.on_entered = True
|
||||
|
||||
def on_trigger_exit(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(sphere_id):
|
||||
Trigger.on_exited = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(trigger_box_id)
|
||||
handler.add_callback("OnTriggerEnter", on_trigger_enter)
|
||||
handler.add_callback("OnTriggerExit", on_trigger_exit)
|
||||
|
||||
# Check sphere enters trigger box
|
||||
helper.wait_for_condition(lambda: Trigger.on_entered, TIMEOUT)
|
||||
Report.critical_result(Tests.sphere_enters_trigger, Trigger.on_entered)
|
||||
|
||||
# Check sphere exits trigger box
|
||||
helper.wait_for_condition(lambda: Trigger.on_exited, TIMEOUT)
|
||||
Report.result(Tests.sphere_exits_trigger, Trigger.on_exited)
|
||||
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ParentChildForcesCombineForces)
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5932044
|
||||
# Test Case Title : Check that force region exerts point force on rigid bodies
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode" )
|
||||
find_ball = ("Ball entity found", "Ball entity not found" )
|
||||
find_box = ("Box entity found", "Box entity not found" )
|
||||
gravity_works = ("Ball fell", "Ball did not fall" )
|
||||
ball_entered_force_region = ("Ball entered force region", "Ball did not enter force region before timeout" )
|
||||
ball_exited_force_region = ("Ball exited force region", "Ball did not exit force region before timeout" )
|
||||
net_force_magnitude = ("The net force magnitude on ball is close to expected value", "The net force magnitude on ball is not close to expected value")
|
||||
ball_moved_up = ("Ball moved up", "Ball did not move up" )
|
||||
ball_moved_right = ("Ball moved right", "Ball did not move right" )
|
||||
ball_not_moved_y = ("Ball did not move in the y direction", "Ball moved in the y direction" )
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode" )
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_PointForceOnRigidBodies():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that that a force region exerts point force on rigid bodies
|
||||
|
||||
Level Description:
|
||||
RigidBody (entity) - a sphere suspended above and to the right the a force region with gravity enabled;
|
||||
contains a sphere mesh, PhysX Collider (sphere shape), and PhysX RigidBody
|
||||
ForceRegion (entity) - contains box mesh, PhysX Collider (Box shape), and PhysX RigidBody
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the ball entity will experience gravity and fall toward the upper right edge (+z, +x) of
|
||||
the force region. The force region applies a point force to the ball, sending it upwards (+z) and to the right (+x)
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve entities
|
||||
4) Get the starting x & z position of the ball
|
||||
5) Check that the ball falls (gravity check)
|
||||
6) Check that the ball enters the trigger area
|
||||
7) Get the magnitude of the collision
|
||||
8) Check that the ball exits the trigger area
|
||||
9) Verify that the magnitude of the collision is as expected
|
||||
10) Check that the ball is moving up and to the right
|
||||
11) Exit game mode
|
||||
12) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
class Ball:
|
||||
start_position_x = None
|
||||
start_position_z = None
|
||||
fell = False
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 2.0
|
||||
MAGNITUDE_TOLERANCE = 0.2 # Magnitudes must be within this amount in order to be valid
|
||||
NO_MOTION_Y_TOLERANCE = sys.float_info.epsilon # Motion in the y axis must be below this in order to be valid
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_PointForceOnRigidBodies")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
ball_id = general.find_game_entity("RigidBody")
|
||||
Report.critical_result(Tests.find_ball, ball_id.IsValid())
|
||||
|
||||
box_id = general.find_game_entity("ForceRegion")
|
||||
Report.critical_result(Tests.find_box, box_id.IsValid())
|
||||
|
||||
# 4) Get the starting x & z position of the ball
|
||||
Ball.start_position_x = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldX", ball_id)
|
||||
Ball.start_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
Report.info("Ball start X: {}".format(Ball.start_position_x))
|
||||
Report.info("Ball start Z: {}".format(Ball.start_position_z))
|
||||
|
||||
# 5) Check that the ball falls (gravity check)
|
||||
def ball_falls():
|
||||
if not Ball.fell:
|
||||
ball_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
if (ball_position_z - Ball.start_position_z) < 0.0:
|
||||
Report.info("Ball position is now lower than the starting position")
|
||||
Ball.fell = True
|
||||
return Ball.fell
|
||||
|
||||
helper.wait_for_condition(ball_falls, TIMEOUT)
|
||||
Report.result(Tests.gravity_works, Ball.fell)
|
||||
|
||||
# 6) Check that the ball enters the trigger area
|
||||
class ForceRegionTrigger:
|
||||
entered = False
|
||||
exited = False
|
||||
|
||||
def on_enter(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(ball_id):
|
||||
Report.info("Trigger entered")
|
||||
ForceRegionTrigger.entered = True
|
||||
|
||||
def on_exit(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(ball_id):
|
||||
Report.info("Trigger exited")
|
||||
ForceRegionTrigger.exited = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(box_id)
|
||||
handler.add_callback("OnTriggerEnter", on_enter)
|
||||
handler.add_callback("OnTriggerExit", on_exit)
|
||||
|
||||
helper.wait_for_condition(lambda: ForceRegionTrigger.entered, TIMEOUT)
|
||||
Report.result(Tests.ball_entered_force_region, ForceRegionTrigger.entered)
|
||||
|
||||
# 7) Get the magnitude of the collision
|
||||
class NetForceMagnitude:
|
||||
value = 0
|
||||
|
||||
def on_calc_net_force(args):
|
||||
"""
|
||||
args[0] - force region entity
|
||||
args[1] - entity entering
|
||||
args[2] - vector
|
||||
args[3] - magnitude
|
||||
"""
|
||||
force_magnitude = args[3]
|
||||
NetForceMagnitude.value = force_magnitude
|
||||
|
||||
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
force_notification_handler.connect(None)
|
||||
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
|
||||
|
||||
# 8) Check that the ball exits the trigger area
|
||||
helper.wait_for_condition(lambda: ForceRegionTrigger.exited, TIMEOUT)
|
||||
Report.result(Tests.ball_exited_force_region, ForceRegionTrigger.exited)
|
||||
|
||||
# 9) Verify that the magnitude of the collision is as expected
|
||||
def is_close_float(a, b, tolerance):
|
||||
return abs(b - a) < tolerance
|
||||
|
||||
force_region_magnitude = azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "GetMagnitude", box_id)
|
||||
Report.info(
|
||||
"NetForce magnitude is {}, Force Region magnitude is {}".format(NetForceMagnitude.value, force_region_magnitude)
|
||||
)
|
||||
Report.result(
|
||||
Tests.net_force_magnitude, is_close_float(NetForceMagnitude.value, force_region_magnitude, MAGNITUDE_TOLERANCE)
|
||||
)
|
||||
|
||||
# 10) Check that the ball is moving up and to the right
|
||||
linear_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", ball_id)
|
||||
Report.info_vector3(linear_velocity, "Ball linear velocity")
|
||||
Report.result(Tests.ball_moved_up, linear_velocity.z > 0)
|
||||
Report.result(Tests.ball_moved_right, linear_velocity.x > 0)
|
||||
Report.result(Tests.ball_not_moved_y, abs(linear_velocity.y) < NO_MOTION_Y_TOLERANCE)
|
||||
|
||||
# 11) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_PointForceOnRigidBodies)
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C5959808
|
||||
# Test Case Title : Verify Force Region Position Offset
|
||||
|
||||
|
||||
|
||||
# fmt:off
|
||||
class Tests:
|
||||
# General tests
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
test_completed = ("The test successfully completed", "The test timed out")
|
||||
# ***** Entities found *****
|
||||
# Force Regions
|
||||
force_region_x_found = ("Force Region for X axis test was found", "Force Region for X axis test was NOT found")
|
||||
force_region_y_found = ("Force Region for Y axis test was found", "Force Region for Y axis test was NOT found")
|
||||
force_region_z_found = ("Force Region for Z axis test was found", "Force Region for Z axis test was NOT found")
|
||||
# Force Region Pass Boxes
|
||||
force_region_pass_box_x_found = ("Force Region Pass Box for X axis test was found", "Force Region Pass Box for X axis test was NOT found")
|
||||
force_region_pass_box_y_found = ("Force Region Pass Box for Y axis test was found", "Force Region Pass Box for Y axis test was NOT found")
|
||||
force_region_pass_box_z_found = ("Force Region Pass Box for Z axis test was found", "Force Region Pass Box for Z axis test was NOT found")
|
||||
# External Pass Boxes
|
||||
external_pass_box_x_found = ("External Pass Box for X axis test was found", "External Pass Box for X axis test was NOT found")
|
||||
external_pass_box_y_found = ("External Pass Box for Y axis test was found", "External Pass Box for Y axis test was NOT found")
|
||||
external_pass_box_z_found = ("External Pass Box for Z axis test was found", "External Pass Box for Z axis test was NOT found")
|
||||
# Force Region Fail Boxes
|
||||
force_region_fail_box_x_found = ("Force Region Fail Box for X axis test was found", "Force Region Fail Box for X axis test was NOT found")
|
||||
force_region_fail_box_y_found = ("Force Region Fail Box for Y axis test was found", "Force Region Fail Box for Y axis test was NOT found")
|
||||
force_region_fail_box_z_found = ("Force Region Fail Box for Z axis test was found", "Force Region Fail Box for Z axis test was NOT found")
|
||||
# External Fail Boxes
|
||||
external_fail_box_x_found = ("External Fail Box for X axis test was found", "External Fail Box for X axis test was NOT found")
|
||||
external_fail_box_y_found = ("External Fail Box for Y axis test was found", "External Fail Box for Y axis test was NOT found")
|
||||
external_fail_box_z_found = ("External Fail Box for Z axis test was found", "External Fail Box for Z axis test was NOT found")
|
||||
# Pass spheres
|
||||
sphere_pass_x_found = ("Pass Sphere for X axis test was found", "Pass Sphere for X axis test was NOT found")
|
||||
sphere_pass_y_found = ("Pass Sphere for Y axis test was found", "Pass Sphere for Y axis test was NOT found")
|
||||
sphere_pass_z_found = ("Pass Sphere for Z axis test was found", "Pass Sphere for Z axis test was NOT found")
|
||||
# Bounce Spheres
|
||||
sphere_bounce_x_found = ("Bounce Sphere for X axis test was found", "Bounce Sphere for X axis test was NOT found")
|
||||
sphere_bounce_y_found = ("Bounce Sphere for Y axis test was found", "Bounce Sphere for Y axis test was NOT found")
|
||||
sphere_bounce_z_found = ("Bounce Sphere for Z axis test was found", "Bounce Sphere for Z axis test was NOT found")
|
||||
|
||||
# ****** Entities' results ******
|
||||
# Force Regions
|
||||
force_region_x_mag_result = ("Force Region for X axis magnitude exerted was as expected", "Force Region for X axis magnitude exerted was NOT as expected")
|
||||
force_region_y_mag_result = ("Force Region for Y axis magnitude exerted was as expected", "Force Region for Y axis magnitude exerted was NOT as expected")
|
||||
force_region_z_mag_result = ("Force Region for Z axis magnitude exerted was as expected", "Force Region for Z axis magnitude exerted was NOT as expected")
|
||||
force_region_x_norm_result = ("Force Region for X axis normal exerted was as expected", "Force Region for X axis normal exerted was NOT as expected")
|
||||
force_region_y_norm_result = ("Force Region for Y axis normal exerted was as expected", "Force Region for Y axis normal exerted was NOT as expected")
|
||||
force_region_z_norm_result = ("Force Region for Z axis normal exerted was as expected", "Force Region for Z axis normal exerted was NOT as expected")
|
||||
# Force Region Pass Boxes
|
||||
force_region_pass_box_x_result = ("Force Region Pass Box for X axis collided with exactly one sphere", "Force Region Pass Box for X axis DID NOT collide with exactly one sphere")
|
||||
force_region_pass_box_y_result = ("Force Region Pass Box for Y axis collided with exactly one sphere", "Force Region Pass Box for Y axis DID NOT collide with exactly one sphere")
|
||||
force_region_pass_box_z_result = ("Force Region Pass Box for Z axis collided with exactly one sphere", "Force Region Pass Box for Z axis DID NOT collide with exactly one sphere")
|
||||
# External Pass Boxes
|
||||
external_pass_box_x_result = ("External Pass Box for X axis collided with exactly one sphere", "External Pass Box for X axis DID NOT collide with exactly one sphere")
|
||||
external_pass_box_y_result = ("External Pass Box for Y axis collided with exactly one sphere", "External Pass Box for Y axis DID NOT collide with exactly one sphere")
|
||||
external_pass_box_z_result = ("External Pass Box for Z axis collided with exactly one sphere", "External Pass Box for Z axis DID NOT collide with exactly one sphere")
|
||||
# Force Region Fail Boxes
|
||||
force_region_fail_box_x_result = ("Force Region Fail Box for X axis collided with no spheres", "Force Region Fail Box for X axis DID collide with a sphere")
|
||||
force_region_fail_box_y_result = ("Force Region Fail Box for Y axis collided with no spheres", "Force Region Fail Box for Y axis DID collide with a sphere")
|
||||
force_region_fail_box_z_result = ("Force Region Fail Box for Z axis collided with no spheres", "Force Region Fail Box for Z axis DID collide with a sphere")
|
||||
# External Fail Boxes
|
||||
external_fail_box_x_result = ("External Fail Box for X axis collided with no spheres", "External Fail Box for X axis DID collide with a sphere")
|
||||
external_fail_box_y_result = ("External Fail Box for Y axis collided with no spheres", "External Fail Box for Y axis DID collide with a sphere")
|
||||
external_fail_box_z_result = ("External Fail Box for Z axis collided with no spheres", "External Fail Box for Z axis DID collide with a sphere")
|
||||
# Pass spheres
|
||||
sphere_pass_x_result = ("Pass Sphere for X axis collided with expected Box", "Pass Sphere for X axis DID NOT collide with expected Box")
|
||||
sphere_pass_y_result = ("Pass Sphere for Y axis collided with expected Box", "Pass Sphere for Y axis DID NOT collide with expected Box")
|
||||
sphere_pass_z_result = ("Pass Sphere for Z axis collided with expected Box", "Pass Sphere for Z axis DID NOT collide with expected Box")
|
||||
# Bounce Spheres
|
||||
sphere_bounce_x_result = ("Bounce Sphere for X axis collided with expected Box", "Bounce Sphere for X axis DID NOT collide with expected Box")
|
||||
sphere_bounce_y_result = ("Bounce Sphere for Y axis collided with expected Box", "Bounce Sphere for Y axis DID NOT collide with expected Box")
|
||||
sphere_bounce_z_result = ("Bounce Sphere for Z axis collided with expected Box", "Bounce Sphere for Z axis DID NOT collide with expected Box")
|
||||
# fmt:on
|
||||
|
||||
@staticmethod
|
||||
# Test tuple accessor via string
|
||||
def get_test(test_name):
|
||||
if test_name in Tests.__dict__:
|
||||
return Tests.__dict__[test_name]
|
||||
else:
|
||||
return None
|
||||
|
||||
def ForceRegion_PositionOffset():
|
||||
"""
|
||||
Summary:
|
||||
Force Region positional offset is tested for each of the 3 axises (X, Y, and Z). Each axis's test has one
|
||||
ForceRegion, two spheres and four boxes. By monitoring which box each sphere collides with we can validate the
|
||||
integrity of the ForceRegions positional offset.
|
||||
|
||||
Level Description:
|
||||
Each axis's test has the following entities:
|
||||
one force region - set for point force and with it's collider set offset (on the axis in test).
|
||||
two spheres - one positioned near the transform of the force region, one positioned near the [offset] collider for
|
||||
the force region
|
||||
four boxes - One box is positioned inside the force region's transform, one inside the force region's [offset]
|
||||
collider. The other two boxes are positioned behind the two spheres (relative to the direction they will be
|
||||
initially traveling)
|
||||
|
||||
Expected Behavior:
|
||||
All three axises' tests run in parallel. when the tests begin, the spheres should move toward their expected
|
||||
force regions. The spheres positioned to collide with their region's [offset] collider should be forced backwards
|
||||
before entering the force region and collide with the box behind it. The spheres positioned by their force region's
|
||||
transforms should pass straight into the transform and collide with the box inside the transform.
|
||||
The boxes inside the Force Regions' [offset] colliders and the boxes behind the spheres set to move into the Force
|
||||
Regions' transforms should not register any collisions.
|
||||
|
||||
Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Set up tests and variables
|
||||
3) Wait for test results (or time out)
|
||||
(Report results)
|
||||
4) Exit game mode and close the editor
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as azmath
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
|
||||
# Constants
|
||||
CLOSE_ENOUGH = 0.01 # Close enough threshold for comparing floats
|
||||
TIME_OUT = 2.0 # Time out (in seconds) until test is aborted
|
||||
FORCE_MAGNITUDE = 1000.0 # Point force magnitude for Force Regions
|
||||
SPEED = 3.0 # Initial speed (in m/s) of the moving spheres.
|
||||
|
||||
# Full list for all spheres. Used for EntityId look up in event handlers
|
||||
all_spheres = []
|
||||
|
||||
# Entity base class handles very general entity initialization
|
||||
# Should be treated as a "virtual" class and all implementing child
|
||||
# classes should implement a "self.result()" function referenced in EntityBase::report(self)
|
||||
class EntityBase:
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
self.name = name
|
||||
self.print_list = []
|
||||
self.id = general.find_game_entity(name)
|
||||
found_test = Tests.get_test(name + "_found")
|
||||
Report.critical_result(found_test, self.id.IsValid())
|
||||
|
||||
# Reports this entity's result. Implicitly calls "get" on result.
|
||||
# Subclasses implement their own definition of a successful result
|
||||
def report(self):
|
||||
# type: () -> None
|
||||
result_test = Tests.get_test(self.name + "_result")
|
||||
Report.result(result_test, self.result())
|
||||
|
||||
# Prints the print queue (with decorated header) if not empty
|
||||
def print_log(self):
|
||||
# type: () -> None
|
||||
if self.print_list:
|
||||
Report.info("*********** {} **********".format(self))
|
||||
for line in self.print_list:
|
||||
Report.info(line)
|
||||
Report.info("")
|
||||
|
||||
# Quick string cast, returns entity name
|
||||
def __str__(self):
|
||||
# type: () -> str
|
||||
return self.name
|
||||
|
||||
# ForceRegion handles all the data and behavior associated with a ForceRegion (for this test)
|
||||
# They simply wait for a Sphere to collide with them. On collision they store the calculated force
|
||||
# magnitude for verification.
|
||||
class ForceRegion(EntityBase):
|
||||
def __init__(self, name, magnitude):
|
||||
# type: (str, float) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.expected_magnitude = magnitude
|
||||
self.actual_magnitude = None
|
||||
self.expected_normal = None
|
||||
self.actual_normal = None
|
||||
# Set point force Magnitude
|
||||
azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "SetMagnitude", self.id, magnitude)
|
||||
# Set up handler
|
||||
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
self.handler.connect(None)
|
||||
self.handler.add_callback("OnCalculateNetForce", self.on_calc_force)
|
||||
|
||||
# Callback function for OnCalculateNetForce event
|
||||
def on_calc_force(self, args):
|
||||
# type: ([EntityId, EntityId, azmath.Vector3, float]) -> None
|
||||
if self.id.Equal(args[0]) and self.actual_magnitude is None:
|
||||
for sphere in all_spheres:
|
||||
if sphere.id.Equal(args[1]):
|
||||
# Log event in print queue (for me and for the sphere)
|
||||
self.print_list.append("Exerting force on {}:".format(sphere))
|
||||
sphere.print_list.append("Force exerted by {}".format(self))
|
||||
# Save calculated data to be compared later
|
||||
self.actual_normal = args[2]
|
||||
self.actual_magnitude = args[3]
|
||||
self.expected_normal = sphere.initial_velocity.GetNormalizedSafe().Unary()
|
||||
# Add expected/actual to print queue
|
||||
self.print_list.append("Force Vector: ")
|
||||
self.print_list.append(
|
||||
" Expected: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
self.expected_normal.x, self.expected_normal.y, self.expected_normal.z
|
||||
)
|
||||
)
|
||||
self.print_list.append(
|
||||
" Actual: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
self.actual_normal.x, self.actual_normal.y, self.actual_normal.z
|
||||
)
|
||||
)
|
||||
self.print_list.append("Force Magnitude: ")
|
||||
self.print_list.append(" Expected: {}".format(self.expected_magnitude))
|
||||
self.print_list.append(" Actual: {:.2f}".format(self.actual_magnitude))
|
||||
|
||||
# EntityBase::report() overload.
|
||||
# Force regions have 2 test tuples to report on
|
||||
def report(self):
|
||||
magnitude_test = Tests.get_test(self.name + "_mag_result")
|
||||
normal_test = Tests.get_test(self.name + "_norm_result")
|
||||
Report.result(magnitude_test, self.magnitude_result())
|
||||
Report.result(normal_test, self.normal_result())
|
||||
|
||||
# Test result calculations
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
# type: () -> bool
|
||||
return self.magnitude_result() and self.normal_result()
|
||||
|
||||
def magnitude_result(self):
|
||||
# type: () -> bool
|
||||
return (
|
||||
self.actual_magnitude is not None
|
||||
and abs(self.actual_magnitude - self.expected_magnitude) < CLOSE_ENOUGH
|
||||
)
|
||||
|
||||
def normal_result(self):
|
||||
# type: () -> bool
|
||||
return (
|
||||
self.actual_normal is not None
|
||||
and self.expected_normal is not None
|
||||
and self.expected_normal.IsClose(self.actual_normal, CLOSE_ENOUGH)
|
||||
)
|
||||
|
||||
# Spheres are the objects that test the force regions. They store an expected collision entity and an
|
||||
# actual collision entity
|
||||
class Sphere(EntityBase):
|
||||
def __init__(self, name, initial_velocity, expected_collision):
|
||||
# type: (str, azmath.Vector3, EntityBase, bool) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.initial_velocity = initial_velocity
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, initial_velocity)
|
||||
self.print_list.append(
|
||||
"Initial velocity: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
initial_velocity.x, initial_velocity.y, initial_velocity.z
|
||||
)
|
||||
)
|
||||
self.expected_collision = expected_collision
|
||||
self.print_list.append("Expected Collision: {}".format(expected_collision))
|
||||
self.actual_collision = None
|
||||
self.active = True
|
||||
self.force_normal = None
|
||||
|
||||
# Registers a collision with this sphere. Saves a reference to the colliding entity for processing later.
|
||||
# Deactivate self after collision is registered.
|
||||
def collide(self, collision_entity):
|
||||
# type: (EntityBase) -> None
|
||||
# Log the event
|
||||
self.print_list.append("Collided with {}".format(collision_entity))
|
||||
self.actual_collision = collision_entity
|
||||
# Deactivate self
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", self.id)
|
||||
self.active = False
|
||||
|
||||
# Calculates result
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
if self.actual_collision is None:
|
||||
return False
|
||||
else:
|
||||
return self.expected_collision.id.Equal(self.actual_collision.id)
|
||||
|
||||
# Box entities wait for a collision with a sphere as a means of validation the force region's offset
|
||||
# worked according to plan.
|
||||
class Box(EntityBase):
|
||||
def __init__(self, name, expected_sphere_collisions):
|
||||
# type: (str, int) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.spheres_collided = 0
|
||||
self.expected_sphere_collisions = expected_sphere_collisions
|
||||
# Set up handler
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
# Callback function for OnCollisionBegin event
|
||||
def on_collision_begin(self, args):
|
||||
for sphere in all_spheres:
|
||||
if sphere.id.Equal(args[0]):
|
||||
# Log event
|
||||
self.print_list.append("Collided with {}".format(sphere))
|
||||
# Register collision with sphere
|
||||
sphere.collide(self)
|
||||
self.spheres_collided += 1 # Count collisions for validation later
|
||||
break
|
||||
|
||||
# Calculates test result
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
return self.spheres_collided == self.expected_sphere_collisions
|
||||
|
||||
# Manages the entities required to run the test for one axis (X, Y, or Z)
|
||||
class AxisTest:
|
||||
def __init__(self, axis, init_velocity):
|
||||
# type: (str, azmath.Vector3) -> None
|
||||
self.name = axis + " axis test"
|
||||
self.force_region = ForceRegion("force_region_" + axis, FORCE_MAGNITUDE)
|
||||
self.spheres = [
|
||||
Sphere("sphere_pass_" + axis, init_velocity, Box("force_region_pass_box_" + axis, 1)),
|
||||
Sphere("sphere_bounce_" + axis, init_velocity, Box("external_pass_box_" + axis, 1)),
|
||||
]
|
||||
self.boxes = [
|
||||
Box("external_fail_box_" + axis, 0),
|
||||
Box("force_region_fail_box_" + axis, 0)
|
||||
] + [
|
||||
# Gets the Boxes passed to spheres on init
|
||||
sphere.expected_collision for sphere in self.spheres
|
||||
]
|
||||
# Full list for all entities this test is responsible for
|
||||
self.all_entities = self.boxes + self.spheres + [self.force_region]
|
||||
# Add spheres to global "lookup" list
|
||||
all_spheres.extend(self.spheres)
|
||||
|
||||
# Checks for all entities' test passing conditions
|
||||
def passed(self):
|
||||
return all([e.result() for e in self.all_entities])
|
||||
|
||||
# Returns true when this test has completed (i.e. when the spheres have collided and are deactivated)
|
||||
def completed(self):
|
||||
return all([not sphere.active for sphere in self.spheres])
|
||||
|
||||
# Reports results for all entities in this test
|
||||
def report(self):
|
||||
Report.info("::::::::::::::::::::::::::::: {} Results :::::::::::::::::::::::::::::".format(self.name))
|
||||
for entity in self.all_entities:
|
||||
entity.report()
|
||||
|
||||
# Prints the logs for all entities in this test
|
||||
def print_log(self):
|
||||
Report.info("::::::::::::::::::::::::::::: {} Log :::::::::::::::::::::::::::::".format(self.name))
|
||||
for entity in self.all_entities:
|
||||
entity.print_log()
|
||||
|
||||
# *********** Execution Code ***********
|
||||
|
||||
# 1) Open level
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "ForceRegion_PositionOffset")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Variable set up
|
||||
# Initial velocities for the three different directions spheres will be moving
|
||||
x_vel = azmath.Vector3(SPEED, 0.0, 0.0)
|
||||
y_vel = azmath.Vector3(0.0, SPEED, 0.0)
|
||||
z_vel = azmath.Vector3(0.0, 0.0, SPEED)
|
||||
|
||||
# The three tests, one for each axis
|
||||
axis_tests = [
|
||||
AxisTest("x", z_vel), # Spheres move in Z direction when testing X axis offset
|
||||
AxisTest("y", x_vel), # Spheres move in X direction when testing Y axis offset
|
||||
AxisTest("z", y_vel), # Spheres move in Y direction when testing Z axis offset
|
||||
]
|
||||
|
||||
# 3) Wait for test results or time out
|
||||
Report.result(
|
||||
Tests.test_completed, helper.wait_for_condition(
|
||||
lambda: all([test.completed() for test in axis_tests]), TIME_OUT
|
||||
)
|
||||
)
|
||||
|
||||
# Report results
|
||||
for test in axis_tests:
|
||||
test.report()
|
||||
|
||||
# Print entity print queues for each failed test
|
||||
for test in axis_tests:
|
||||
if not test.passed():
|
||||
test.print_log()
|
||||
|
||||
# 4) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_PositionOffset)
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5959761
|
||||
# Test Case Title : Check that force region (physics asset) exerts point force
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ball = ("Ball entity found", "Ball entity not found")
|
||||
find_sedan = ("Sedan entity found", "Sedan entity not found")
|
||||
ball_fell = ("The ball fell", "The ball did not fall")
|
||||
ball_enters_force_region = ("Ball entered force region", "Ball did not enter force region")
|
||||
ball_exits_force_region = ("Ball exited force region", "Ball did not exit force region")
|
||||
ball_moved_up = ("Ball moved up", "Ball did not move up")
|
||||
ball_moved_forward = ("Ball moved forward", "Ball did not move forward")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_PxMeshShapedForce():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that PhysX force regions with physics assets exert point force on rigid bodies
|
||||
|
||||
Level Description:
|
||||
A ball is suspended over a force region with a physics asset mesh (sedan). The ball is offset 1 unit towards the
|
||||
hood of the sedan (In the Y direction)
|
||||
|
||||
Ball (entity) - Sphere shaped PhysX Collider; PhysX Rigid body with gravity enabled
|
||||
Sedan (entity) - Sedan shaped PhysX Collider; PhysX Force Region with a point force (magnitude 1000.0)
|
||||
|
||||
Expected Behavior:
|
||||
The ball should fall once game mode is entered and bounce off the force region down and to the right.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Validate entities
|
||||
4) Wait for ball to fall
|
||||
5) Wait for ball to enter force region
|
||||
6) Wait for ball to exit force region
|
||||
7) Validate velocity vector
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
TIMEOUT = 2.0
|
||||
|
||||
class Ball:
|
||||
def __init__(self, name):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.entered_force_region = False
|
||||
self.exited_force_region = False
|
||||
|
||||
def get_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
def is_falling(self):
|
||||
return self.get_velocity().z < 0.0
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_PxMeshShapedForce")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Validate entities
|
||||
ball = Ball("Ball")
|
||||
Report.critical_result(Tests.find_ball, ball.id.IsValid())
|
||||
|
||||
sedan_id = general.find_game_entity("Sedan")
|
||||
Report.critical_result(Tests.find_sedan, sedan_id.IsValid())
|
||||
|
||||
# 4) Wait for ball to fall
|
||||
def on_trigger_enter(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(ball.id):
|
||||
ball.entered_force_region = True
|
||||
|
||||
def on_trigger_exit(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(ball.id):
|
||||
ball.exited_force_region = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(sedan_id)
|
||||
handler.add_callback("OnTriggerEnter", on_trigger_enter)
|
||||
handler.add_callback("OnTriggerExit", on_trigger_exit)
|
||||
|
||||
Report.critical_result(Tests.ball_fell, helper.wait_for_condition(ball.is_falling, TIMEOUT))
|
||||
|
||||
# 5) Wait for ball to enter force region
|
||||
helper.wait_for_condition(lambda: ball.entered_force_region, TIMEOUT)
|
||||
Report.critical_result(Tests.ball_enters_force_region, ball.entered_force_region)
|
||||
|
||||
# 6) Wait for ball to exit force region
|
||||
helper.wait_for_condition(lambda: ball.exited_force_region, TIMEOUT)
|
||||
Report.critical_result(Tests.ball_exits_force_region, ball.exited_force_region)
|
||||
|
||||
# 7) Validate velocity vector
|
||||
velocity = ball.get_velocity()
|
||||
Report.result(Tests.ball_moved_up, velocity.z > 0.0)
|
||||
Report.result(Tests.ball_moved_forward, velocity.y > 0.0)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_PxMeshShapedForce)
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C5959809
|
||||
# Test Case Title : Verify Force Region Rotational Offset
|
||||
|
||||
|
||||
|
||||
# fmt:off
|
||||
class Tests:
|
||||
# General tests
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
test_completed = ("The test successfully completed", "The test timed out")
|
||||
# ***** Entities found *****
|
||||
# Force Regions
|
||||
force_region_x_found = ("Force Region for X axis test was found", "Force Region for X axis test was NOT found")
|
||||
force_region_y_found = ("Force Region for Y axis test was found", "Force Region for Y axis test was NOT found")
|
||||
force_region_z_found = ("Force Region for Z axis test was found", "Force Region for Z axis test was NOT found")
|
||||
# Force Region Pass Boxes
|
||||
force_region_pass_box_x_found = ("Force Region Pass Box for X axis test was found", "Force Region Pass Box for X axis test was NOT found")
|
||||
force_region_pass_box_y_found = ("Force Region Pass Box for Y axis test was found", "Force Region Pass Box for Y axis test was NOT found")
|
||||
force_region_pass_box_z_found = ("Force Region Pass Box for Z axis test was found", "Force Region Pass Box for Z axis test was NOT found")
|
||||
# External Pass Boxes
|
||||
external_pass_box_x_found = ("External Pass Box for X axis test was found", "External Pass Box for X axis test was NOT found")
|
||||
external_pass_box_y_found = ("External Pass Box for Y axis test was found", "External Pass Box for Y axis test was NOT found")
|
||||
external_pass_box_z_found = ("External Pass Box for Z axis test was found", "External Pass Box for Z axis test was NOT found")
|
||||
# Force Region Fail Boxes
|
||||
force_region_fail_box_x_found = ("Force Region Fail Box for X axis test was found", "Force Region Fail Box for X axis test was NOT found")
|
||||
force_region_fail_box_y_found = ("Force Region Fail Box for Y axis test was found", "Force Region Fail Box for Y axis test was NOT found")
|
||||
force_region_fail_box_z_found = ("Force Region Fail Box for Z axis test was found", "Force Region Fail Box for Z axis test was NOT found")
|
||||
# External Fail Boxes
|
||||
external_fail_box_x_found = ("External Fail Box for X axis test was found", "External Fail Box for X axis test was NOT found")
|
||||
external_fail_box_y_found = ("External Fail Box for Y axis test was found", "External Fail Box for Y axis test was NOT found")
|
||||
external_fail_box_z_found = ("External Fail Box for Z axis test was found", "External Fail Box for Z axis test was NOT found")
|
||||
# Pass spheres
|
||||
sphere_pass_x_found = ("Pass Sphere for X axis test was found", "Pass Sphere for X axis test was NOT found")
|
||||
sphere_pass_y_found = ("Pass Sphere for Y axis test was found", "Pass Sphere for Y axis test was NOT found")
|
||||
sphere_pass_z_found = ("Pass Sphere for Z axis test was found", "Pass Sphere for Z axis test was NOT found")
|
||||
# Bounce Spheres
|
||||
sphere_bounce_x_found = ("Bounce Sphere for X axis test was found", "Bounce Sphere for X axis test was NOT found")
|
||||
sphere_bounce_y_found = ("Bounce Sphere for Y axis test was found", "Bounce Sphere for Y axis test was NOT found")
|
||||
sphere_bounce_z_found = ("Bounce Sphere for Z axis test was found", "Bounce Sphere for Z axis test was NOT found")
|
||||
|
||||
# ****** Entities' results ******
|
||||
# Force Regions
|
||||
force_region_x_mag_result = ("Force Region for X axis magnitude exerted was as expected", "Force Region for X axis magnitude exerted was NOT as expected")
|
||||
force_region_y_mag_result = ("Force Region for Y axis magnitude exerted was as expected", "Force Region for Y axis magnitude exerted was NOT as expected")
|
||||
force_region_z_mag_result = ("Force Region for Z axis magnitude exerted was as expected", "Force Region for Z axis magnitude exerted was NOT as expected")
|
||||
force_region_x_norm_result = ("Force Region for X axis normal exerted was as expected", "Force Region for X axis normal exerted was NOT as expected")
|
||||
force_region_y_norm_result = ("Force Region for Y axis normal exerted was as expected", "Force Region for Y axis normal exerted was NOT as expected")
|
||||
force_region_z_norm_result = ("Force Region for Z axis normal exerted was as expected", "Force Region for Z axis normal exerted was NOT as expected")
|
||||
# Force Region Pass Boxes
|
||||
force_region_pass_box_x_result = ("Force Region Pass Box for X axis collided with exactly one sphere", "Force Region Pass Box for X axis DID NOT collide with exactly one sphere")
|
||||
force_region_pass_box_y_result = ("Force Region Pass Box for Y axis collided with exactly one sphere", "Force Region Pass Box for Y axis DID NOT collide with exactly one sphere")
|
||||
force_region_pass_box_z_result = ("Force Region Pass Box for Z axis collided with exactly one sphere", "Force Region Pass Box for Z axis DID NOT collide with exactly one sphere")
|
||||
# External Pass Boxes
|
||||
external_pass_box_x_result = ("External Pass Box for X axis collided with exactly one sphere", "External Pass Box for X axis DID NOT collide with exactly one sphere")
|
||||
external_pass_box_y_result = ("External Pass Box for Y axis collided with exactly one sphere", "External Pass Box for Y axis DID NOT collide with exactly one sphere")
|
||||
external_pass_box_z_result = ("External Pass Box for Z axis collided with exactly one sphere", "External Pass Box for Z axis DID NOT collide with exactly one sphere")
|
||||
# Force Region Fail Boxes
|
||||
force_region_fail_box_x_result = ("Force Region Fail Box for X axis collided with no spheres", "Force Region Fail Box for X axis DID collide with a sphere")
|
||||
force_region_fail_box_y_result = ("Force Region Fail Box for Y axis collided with no spheres", "Force Region Fail Box for Y axis DID collide with a sphere")
|
||||
force_region_fail_box_z_result = ("Force Region Fail Box for Z axis collided with no spheres", "Force Region Fail Box for Z axis DID collide with a sphere")
|
||||
# External Fail Boxes
|
||||
external_fail_box_x_result = ("External Fail Box for X axis collided with no spheres", "External Fail Box for X axis DID collide with a sphere")
|
||||
external_fail_box_y_result = ("External Fail Box for Y axis collided with no spheres", "External Fail Box for Y axis DID collide with a sphere")
|
||||
external_fail_box_z_result = ("External Fail Box for Z axis collided with no spheres", "External Fail Box for Z axis DID collide with a sphere")
|
||||
# Pass spheres
|
||||
sphere_pass_x_result = ("Pass Sphere for X axis collided with expected Box", "Pass Sphere for X axis DID NOT collide with expected Box")
|
||||
sphere_pass_y_result = ("Pass Sphere for Y axis collided with expected Box", "Pass Sphere for Y axis DID NOT collide with expected Box")
|
||||
sphere_pass_z_result = ("Pass Sphere for Z axis collided with expected Box", "Pass Sphere for Z axis DID NOT collide with expected Box")
|
||||
# Bounce Spheres
|
||||
sphere_bounce_x_result = ("Bounce Sphere for X axis collided with expected Box", "Bounce Sphere for X axis DID NOT collide with expected Box")
|
||||
sphere_bounce_y_result = ("Bounce Sphere for Y axis collided with expected Box", "Bounce Sphere for Y axis DID NOT collide with expected Box")
|
||||
sphere_bounce_z_result = ("Bounce Sphere for Z axis collided with expected Box", "Bounce Sphere for Z axis DID NOT collide with expected Box")
|
||||
# fmt:on
|
||||
|
||||
@staticmethod
|
||||
# Test tuple accessor via string
|
||||
def get_test(test_name):
|
||||
if test_name in Tests.__dict__:
|
||||
return Tests.__dict__[test_name]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def ForceRegion_RotationalOffset():
|
||||
"""
|
||||
Summary:
|
||||
Force Region rotational offset is tested for each of the 3 axises (X, Y, and Z). Each axis's test has one
|
||||
ForceRegion, two spheres and four boxes. By monitoring which box each sphere collides with we can validate the
|
||||
integrity of the ForceRegions rotational offset.
|
||||
|
||||
Level Description:
|
||||
Each axis's test has the following entities:
|
||||
one force region - set for point force and with it's collider rotationally offset (on the axis in test).
|
||||
two spheres - one positioned near the transform of the force region, one positioned near the [offset] collider for
|
||||
the force region
|
||||
four boxes - One box is positioned inside the force region's transform, one inside the force region's [offset]
|
||||
collider. The other two boxes are positioned behind the two spheres (relative to the direction they will be
|
||||
initially traveling)
|
||||
|
||||
Expected Behavior:
|
||||
All three axises' tests run in parallel. when the tests begin, the spheres should move toward their expected
|
||||
force regions. The spheres positioned to collide with their region's [offset] collider should be forced backwards
|
||||
before entering the force region and collide with the box behind it. The spheres positioned by their force region's
|
||||
transforms should pass straight into the transform and collide with the box inside the transform.
|
||||
The boxes inside the Force Regions' [offset] colliders and the boxes behind the spheres set to move into the Force
|
||||
Regions' transforms should not register any collisions.
|
||||
|
||||
Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Set up tests and variables
|
||||
3) Wait for test results (or time out)
|
||||
(Report results)
|
||||
4) Exit game mode and close the editor
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as azmath
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
|
||||
# Constants
|
||||
CLOSE_ENOUGH = 0.01 # Close enough threshold for comparing floats
|
||||
TIME_OUT = 2.0 # Time out (in seconds) until test is aborted
|
||||
FORCE_MAGNITUDE = 1000.0 # Point force magnitude for Force Regions
|
||||
SPEED = 3.0 # Initial speed (in m/s) of the moving spheres.
|
||||
|
||||
# Full list for all spheres. Used for EntityId look up in event handlers
|
||||
all_spheres = []
|
||||
|
||||
# Entity base class handles very general entity initialization
|
||||
# Should be treated as a "virtual" class and all implementing child
|
||||
# classes should implement a "self.result()" function referenced in EntityBase::report(self)
|
||||
class EntityBase:
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
self.name = name
|
||||
self.print_list = []
|
||||
self.id = general.find_game_entity(name)
|
||||
found_test = Tests.get_test(name + "_found")
|
||||
Report.critical_result(found_test, self.id.IsValid())
|
||||
|
||||
# Reports this entity's result. Implicitly calls "get" on result.
|
||||
# Subclasses implement their own definition of a successful result
|
||||
def report(self):
|
||||
# type: () -> None
|
||||
result_test = Tests.get_test(self.name + "_result")
|
||||
Report.result(result_test, self.result())
|
||||
|
||||
# Prints the print queue (with decorated header) if not empty
|
||||
def print_log(self):
|
||||
# type: () -> None
|
||||
if self.print_list:
|
||||
Report.info("*********** {} **********".format(self))
|
||||
for line in self.print_list:
|
||||
Report.info(line)
|
||||
Report.info("")
|
||||
|
||||
# Quick string cast, returns entity name
|
||||
def __str__(self):
|
||||
# type: () -> str
|
||||
return self.name
|
||||
|
||||
# ForceRegion handles all the data and behavior associated with a ForceRegion (for this test)
|
||||
# They simply wait for a Sphere to collide with them. On collision they store the calculated force
|
||||
# magnitude for verification.
|
||||
class ForceRegion(EntityBase):
|
||||
def __init__(self, name, magnitude):
|
||||
# type: (str, float) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.expected_magnitude = magnitude
|
||||
self.actual_magnitude = None
|
||||
self.expected_normal = None
|
||||
self.actual_normal = None
|
||||
# Set point force Magnitude
|
||||
azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "SetMagnitude", self.id, magnitude)
|
||||
# Set up handler
|
||||
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
self.handler.connect(None)
|
||||
self.handler.add_callback("OnCalculateNetForce", self.on_calc_force)
|
||||
|
||||
# Callback function for OnCalculateNetForce event
|
||||
def on_calc_force(self, args):
|
||||
# type: ([EntityId, EntityId, azmath.Vector3, float]) -> None
|
||||
if self.id.Equal(args[0]) and self.actual_magnitude is None:
|
||||
for sphere in all_spheres:
|
||||
if sphere.id.Equal(args[1]):
|
||||
# Log event in print queue (for me and for the sphere)
|
||||
self.print_list.append("Exerting force on {}:".format(sphere))
|
||||
sphere.print_list.append("Force exerted by {}".format(self))
|
||||
# Save calculated data to be compared later
|
||||
self.actual_normal = args[2]
|
||||
self.actual_magnitude = args[3]
|
||||
pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere.id)
|
||||
self.expected_normal = sphere_pos.Subtract(pos).GetNormalizedSafe()
|
||||
# Add expected/actual to print queue
|
||||
self.print_list.append("Force Vector: ")
|
||||
self.print_list.append(
|
||||
" Expected: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
self.expected_normal.x, self.expected_normal.y, self.expected_normal.z
|
||||
)
|
||||
)
|
||||
self.print_list.append(
|
||||
" Actual: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
self.actual_normal.x, self.actual_normal.y, self.actual_normal.z
|
||||
)
|
||||
)
|
||||
self.print_list.append("Force Magnitude: ")
|
||||
self.print_list.append(" Expected: {}".format(self.expected_magnitude))
|
||||
self.print_list.append(" Actual: {:.2f}".format(self.actual_magnitude))
|
||||
|
||||
# EntityBase::report() overload.
|
||||
# Force regions have 2 test tuples to report on
|
||||
def report(self):
|
||||
magnitude_test = Tests.get_test(self.name + "_mag_result")
|
||||
normal_test = Tests.get_test(self.name + "_norm_result")
|
||||
Report.result(magnitude_test, self.magnitude_result())
|
||||
Report.result(normal_test, self.normal_result())
|
||||
|
||||
# Test result calculations
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
# type: () -> bool
|
||||
return self.magnitude_result() and self.normal_result()
|
||||
|
||||
def magnitude_result(self):
|
||||
# type: () -> bool
|
||||
return (
|
||||
self.actual_magnitude is not None
|
||||
and abs(self.actual_magnitude - self.expected_magnitude) < CLOSE_ENOUGH
|
||||
)
|
||||
|
||||
def normal_result(self):
|
||||
# type: () -> bool
|
||||
return (
|
||||
self.actual_normal is not None
|
||||
and self.expected_normal is not None
|
||||
and self.expected_normal.IsClose(self.actual_normal, CLOSE_ENOUGH)
|
||||
)
|
||||
|
||||
# Spheres are the objects that test the force regions. They store an expected collision entity and an
|
||||
# actual collision entity
|
||||
class Sphere(EntityBase):
|
||||
def __init__(self, name, initial_velocity, expected_collision):
|
||||
# type: (str, azmath.Vector3, EntityBase) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.initial_velocity = initial_velocity
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, initial_velocity)
|
||||
self.print_list.append(
|
||||
"Initial velocity: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
initial_velocity.x, initial_velocity.y, initial_velocity.z
|
||||
)
|
||||
)
|
||||
self.expected_collision = expected_collision
|
||||
self.print_list.append("Expected Collision: {}".format(expected_collision))
|
||||
self.actual_collision = None
|
||||
self.active = True
|
||||
self.force_normal = None
|
||||
|
||||
# Registers a collision with this sphere. Saves a reference to the colliding entity for processing later.
|
||||
# Deactivate self after collision is registered.
|
||||
def collide(self, collision_entity):
|
||||
# type: (EntityBase) -> None
|
||||
# Log the event
|
||||
self.print_list.append("Collided with {}".format(collision_entity))
|
||||
self.actual_collision = collision_entity
|
||||
# Deactivate self
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity",
|
||||
self.id)
|
||||
self.active = False
|
||||
|
||||
# Calculates result
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
if self.actual_collision is None:
|
||||
return False
|
||||
else:
|
||||
return self.expected_collision.id.Equal(self.actual_collision.id)
|
||||
|
||||
# Box entities wait for a collision with a sphere as a means of validation the force region's offset
|
||||
# worked according to plan.
|
||||
class Box(EntityBase):
|
||||
def __init__(self, name, expected_sphere_collisions):
|
||||
# type: (str, int) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.spheres_collided = 0
|
||||
self.expected_sphere_collisions = expected_sphere_collisions
|
||||
# Set up handler
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
# Callback function for OnCollisionBegin event
|
||||
def on_collision_begin(self, args):
|
||||
for sphere in all_spheres:
|
||||
if sphere.id.Equal(args[0]):
|
||||
# Log event
|
||||
self.print_list.append("Collided with {}".format(sphere))
|
||||
# Register collision with sphere
|
||||
sphere.collide(self)
|
||||
self.spheres_collided += 1 # Count collisions for validation later
|
||||
break
|
||||
|
||||
# Calculates test result
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
return self.spheres_collided == self.expected_sphere_collisions
|
||||
|
||||
# Manages the entities required to run the test for one axis (X, Y, or Z)
|
||||
class AxisTest:
|
||||
def __init__(self, axis, init_velocity):
|
||||
# type: (str, azmath.Vector3) -> None
|
||||
self.name = axis + " axis test"
|
||||
self.force_region = ForceRegion("force_region_" + axis, FORCE_MAGNITUDE)
|
||||
self.spheres = [
|
||||
Sphere("sphere_pass_" + axis, init_velocity, Box("force_region_pass_box_" + axis, 1)),
|
||||
Sphere("sphere_bounce_" + axis, init_velocity, Box("external_pass_box_" + axis, 1)),
|
||||
]
|
||||
self.boxes = [
|
||||
Box("external_fail_box_" + axis, 0),
|
||||
Box("force_region_fail_box_" + axis, 0)
|
||||
] + [
|
||||
sphere.expected_collision for sphere in self.spheres
|
||||
# Gets the Boxes passed to spheres on init
|
||||
]
|
||||
# Full list for all entities this test is responsible for
|
||||
self.all_entities = self.boxes + self.spheres + [self.force_region]
|
||||
# Add spheres to global "lookup" list
|
||||
all_spheres.extend(self.spheres)
|
||||
|
||||
# Checks for all entities' test passing conditions
|
||||
def passed(self):
|
||||
return all([e.result() for e in self.all_entities])
|
||||
|
||||
# Returns true when this test has completed (i.e. when the spheres have collided and are deactivated)
|
||||
def completed(self):
|
||||
return all([not sphere.active for sphere in self.spheres])
|
||||
|
||||
# Reports results for all entities in this test
|
||||
def report(self):
|
||||
Report.info("::::::::::::::::::::::::::::: {} Results :::::::::::::::::::::::::::::".format(self.name))
|
||||
for entity in self.all_entities:
|
||||
entity.report()
|
||||
|
||||
# Prints the logs for all entities in this test
|
||||
def print_log(self):
|
||||
Report.info("::::::::::::::::::::::::::::: {} Log :::::::::::::::::::::::::::::".format(self.name))
|
||||
for entity in self.all_entities:
|
||||
entity.print_log()
|
||||
|
||||
# *********** Execution Code ***********
|
||||
|
||||
# 1) Open level
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "ForceRegion_RotationalOffset")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Variable set up
|
||||
# Initial velocities for the three different directions spheres will be moving
|
||||
x_vel = azmath.Vector3(SPEED, 0.0, 0.0)
|
||||
y_vel = azmath.Vector3(0.0, SPEED, 0.0)
|
||||
z_vel = azmath.Vector3(0.0, 0.0, SPEED)
|
||||
|
||||
# The three tests, one for each axis
|
||||
axis_tests = [AxisTest("x", x_vel), AxisTest("y", y_vel), AxisTest("z", z_vel)]
|
||||
|
||||
# 3) Wait for test results or time out
|
||||
Report.result(
|
||||
Tests.test_completed, helper.wait_for_condition(
|
||||
lambda: all([test.completed() for test in axis_tests]), TIME_OUT
|
||||
)
|
||||
)
|
||||
|
||||
# Report results
|
||||
for test in axis_tests:
|
||||
test.report()
|
||||
|
||||
# Print entity print queues for each failed test
|
||||
for test in axis_tests:
|
||||
if not test.passed():
|
||||
test.print_log()
|
||||
|
||||
# 4) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_RotationalOffset)
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
# coding=utf-8
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5932043
|
||||
# Test Case Title : Check that force region exerts simple drag force on rigid bodies
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("Sphere found", "Sphere not found")
|
||||
find_force_region = ("Force Region found", "Force Region not found")
|
||||
|
||||
# entity_actions_success refers to if the entity completed all expected actions in the test. For this test, this
|
||||
# will be, did the Sphere enter and exit the force region
|
||||
entity_actions_success = ("Entity actions completed", "Entity actions not completed")
|
||||
sphere_lost_height = ("Sphere went down", "Sphere didn't go down")
|
||||
force_region_slows = ("Force Region slowed Sphere", "Force Region didn't slow Sphere")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_SimpleDragForceOnRigidBodies():
|
||||
# This run() function will open a a level and validate that the force region slows down a spheres fall.
|
||||
# It does this by:
|
||||
# 1) Opens level with sphere above a force region
|
||||
# 2) Enters Game mode
|
||||
# 3) Finds the entities in the scene
|
||||
# 4) Listens for sphere to enter the force region
|
||||
# 5) Gets z velocity and position of sphere
|
||||
# 6) Listens for sphere to exit force region
|
||||
# 7) Gets new velocity and position of sphere
|
||||
# 8) Validate the results
|
||||
# 9) Exits game mode and editor
|
||||
|
||||
# Setup path
|
||||
import os, sys
|
||||
|
||||
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Holds details about the sphere
|
||||
class Sphere:
|
||||
id = None
|
||||
start_velocity_z = 0.0
|
||||
end_velocity_z = 0.0
|
||||
sphere_start_z_position = 0.0
|
||||
sphere_end_z_position = 0.0
|
||||
entered_force_region = False
|
||||
exited_force_region = False
|
||||
|
||||
TIME_OUT = 4.0 # Time given to test to complete.
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_SimpleDragForceOnRigidBodies")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Get Entities
|
||||
Sphere.id = general.find_game_entity("Sphere")
|
||||
Report.result(Tests.find_sphere, Sphere.id.IsValid())
|
||||
|
||||
force_region_id = general.find_game_entity("Force Region")
|
||||
Report.result(Tests.find_force_region, force_region_id.IsValid())
|
||||
|
||||
# Called if Sphere enters force region
|
||||
def on_trigger_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(Sphere.id):
|
||||
Report.info("Entered force region")
|
||||
Sphere.entered_force_region = True
|
||||
# 5) Gets z velocity and position of sphere
|
||||
Sphere.start_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Sphere.id).z
|
||||
Sphere.sphere_start_z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Sphere.id)
|
||||
Report.info(
|
||||
"Sphere Start Z position = {} Z Start Velocity = {}".format(
|
||||
Sphere.sphere_start_z_position, Sphere.start_velocity_z
|
||||
)
|
||||
)
|
||||
|
||||
# Called when sphere exits force region
|
||||
def on_trigger_end(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(Sphere.id):
|
||||
Report.info("Exited force region")
|
||||
Sphere.exited_force_region = True
|
||||
# 7) Gets new velocity and position of sphere
|
||||
Sphere.end_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Sphere.id).z
|
||||
Sphere.sphere_end_z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Sphere.id)
|
||||
Report.info(
|
||||
"Sphere End Z position = {} Sphere End Z Velocity = {}".format(
|
||||
Sphere.sphere_end_z_position, Sphere.end_velocity_z
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(force_region_id)
|
||||
# 4) Listens for sphere to enter the force region
|
||||
handler.add_callback("OnTriggerEnter", on_trigger_begin)
|
||||
# 6) Listens for sphere to exit force region
|
||||
handler.add_callback("OnTriggerExit", on_trigger_end)
|
||||
|
||||
# Wait until all entities in scene are done performing their actions or test times out.
|
||||
def done_with_entity_actions():
|
||||
return Sphere.entered_force_region and Sphere.exited_force_region
|
||||
|
||||
test_completed = helper.wait_for_condition(done_with_entity_actions, TIME_OUT)
|
||||
Report.result(Tests.entity_actions_success, test_completed)
|
||||
|
||||
# 8) Validate the results
|
||||
if test_completed:
|
||||
# Did Sphere fall
|
||||
sphere_descended = Sphere.sphere_end_z_position + 0.5 < Sphere.sphere_start_z_position # 0.5 for buffer
|
||||
Report.result(Tests.sphere_lost_height, sphere_descended)
|
||||
|
||||
# Did Force Region slow down sphere's falling
|
||||
# Note: The faster a sphere falls, the greater its negative/downward velocity will be. Adding 1.0 for buffer.
|
||||
force_region_result = round(Sphere.end_velocity_z, 2) > round(Sphere.start_velocity_z, 2) + 1.0
|
||||
Report.result(Tests.force_region_slows, force_region_result)
|
||||
|
||||
# 9) Exits game mode and editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_SimpleDragForceOnRigidBodies)
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C6090546
|
||||
# Test Case Title : Check that a force region slice can be saved and instantiated
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("SphereRigidBody found", "SphereRigidBody not found")
|
||||
find_force_region = ("ForceRegionSliceEntity found", "ForceRegionSliceEntity not found")
|
||||
sphere_dropped = ("Sphere dropped down", "Sphere did not drop down")
|
||||
sphere_bounced = ("Sphere bounced up vertically", "Sphere did not bounce up vertically")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_SliceFileInstantiates():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Check that a force region slice can be saved and instantiated
|
||||
|
||||
Level Description:
|
||||
The SphereRigidBody entity is placed above the ForceRegionBox entity
|
||||
ForceRegionSliceEntity (entity) - Slice Asset which is imported from .slice file of another level which has
|
||||
an entity with force region component.
|
||||
SphereRigidBody (entity) - Entity with PhysX Rigid body, Mesh and collider components.
|
||||
The SphereRigidBody is placed above the ForceRegionEntity.
|
||||
|
||||
Expected Behavior:
|
||||
Sphere drops and bounces vertically up from force region.
|
||||
We are checking if the ball started falling down from its initial position and then verifying if it has bounced up
|
||||
its initial position after entering into the force region.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Get the initial position of the Sphere (rigid body)
|
||||
5) Check if the ball is falling down
|
||||
6) Add trigger notification handler
|
||||
7) Wait till the ball enters the force region
|
||||
8) Check if the ball has bounced vertically up
|
||||
9) Exit game mode
|
||||
10) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 3.0 # wait a maximum of 3 seconds
|
||||
SPHERE_RADIUS = 0.5
|
||||
|
||||
class Sphere:
|
||||
id = None
|
||||
initial_position = None
|
||||
current_position = None
|
||||
in_force_region = False
|
||||
bounced = False
|
||||
|
||||
class ForceRegion:
|
||||
id = None
|
||||
|
||||
def sphere_bounced():
|
||||
Sphere.current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
|
||||
Sphere.bounced = Sphere.current_position.z > (Sphere.initial_position.z + SPHERE_RADIUS)
|
||||
return Sphere.bounced
|
||||
|
||||
def on_trigger_enter(args):
|
||||
if args[0].Equal(Sphere.id):
|
||||
Sphere.in_force_region = True
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_SliceFileInstantiates")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
Sphere.id = general.find_game_entity("SphereRigidBody")
|
||||
Report.critical_result(Tests.find_sphere, Sphere.id.IsValid())
|
||||
ForceRegion.id = general.find_game_entity("ForceRegionSliceEntity")
|
||||
Report.critical_result(Tests.find_force_region, ForceRegion.id.IsValid())
|
||||
|
||||
# 4) Get the initial position of the Sphere (rigid body)
|
||||
Sphere.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
|
||||
|
||||
# 5) Check if the ball is falling down
|
||||
Sphere.current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
|
||||
sphere_dropped = Sphere.current_position.z < (Sphere.initial_position.z + SPHERE_RADIUS)
|
||||
Report.critical_result(Tests.sphere_dropped, sphere_dropped)
|
||||
|
||||
# 6) Add trigger notification handler
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(ForceRegion.id)
|
||||
handler.add_callback("OnTriggerEnter", on_trigger_enter)
|
||||
|
||||
# 7) Wait till the ball enters the force region
|
||||
helper.wait_for_condition(lambda: Sphere.in_force_region, TIMEOUT)
|
||||
|
||||
# 8) Check if the ball has bounced vertically up
|
||||
# wait till the ball bounces
|
||||
helper.wait_for_condition(sphere_bounced, TIMEOUT)
|
||||
Report.critical_result(Tests.sphere_bounced, Sphere.bounced)
|
||||
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_SliceFileInstantiates)
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C12905527
|
||||
# Test Case Title : Check that deviation occurring in Force Magnitude due to Values in Force direction is not large
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_force_region = ("Force region was found", "Force region was not found")
|
||||
find_sphere = ("Sphere was found", "Sphere was not found")
|
||||
sphere_entered_region = ("Sphere entered force region", "Sphere did not enter force region")
|
||||
sphere_exited_region = ("Sphere exited force region", "Sphere did not exit force region")
|
||||
force_magnitude_close = ("The net force magnitude was close to the expected value", "The net force magnitude was not close to the expected value")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_SmallMagnitudeDeviationOnLargeForces():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that the calculated net force magnitude is close to the configured value
|
||||
|
||||
Level Description:
|
||||
A sphere (Sphere) is positioned above a force region (ForceRegion)
|
||||
Sphere has a sphere PhysX collider and PhysX Rigid Body. Gravity is disabled, and it has an initial velocity of
|
||||
2 m/s in the Z direction.
|
||||
|
||||
ForceRegion has a box PhysX collider and PhysX Force Region. Magnitude on the force region is set to 1,000,000.0
|
||||
|
||||
Expected Behavior:
|
||||
The sphere enters and exits the force region. on_calc_net_force returns a value close to 1,000,000.0
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Validate entities
|
||||
4) Wait for the sphere to enter and exit the force region
|
||||
5) Check the calculated net force against what we expect
|
||||
6) Exit game mode
|
||||
7) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
EXPECTED_MAGNITUDE = 1000000.0
|
||||
PERMISSIBLE_ERROR = 0.001 # +/- 0.1%
|
||||
TIMEOUT = 1.0
|
||||
|
||||
class Sphere:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.entered_force_region = False
|
||||
self.exited_force_region = False
|
||||
self.net_force_magnitude = 0
|
||||
|
||||
def on_trigger_enter(args):
|
||||
Report.info("triggered")
|
||||
other_id = args[0]
|
||||
if other_id.Equal(sphere.id):
|
||||
sphere.entered_force_region = True
|
||||
|
||||
def on_trigger_exit(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(sphere.id):
|
||||
sphere.exited_force_region = True
|
||||
|
||||
def on_calc_net_force(args):
|
||||
other_id = args[1]
|
||||
force_magnitude = args[3]
|
||||
|
||||
if other_id.Equal(sphere.id):
|
||||
sphere.net_force_magnitude = force_magnitude
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
Report.info(general.get_current_level_name())
|
||||
helper.open_level("Physics", "ForceRegion_SmallMagnitudeDeviationOnLargeForces")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Validate entities
|
||||
sphere = Sphere("Sphere")
|
||||
force_region_id = general.find_game_entity("ForceRegion")
|
||||
|
||||
Report.critical_result(Tests.find_sphere, sphere.id.IsValid())
|
||||
Report.critical_result(Tests.find_force_region, force_region_id.IsValid())
|
||||
|
||||
# Create handlers
|
||||
trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
trigger_handler.connect(force_region_id)
|
||||
trigger_handler.add_callback("OnTriggerEnter", on_trigger_enter)
|
||||
trigger_handler.add_callback("OnTriggerExit", on_trigger_exit)
|
||||
|
||||
net_force_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
net_force_handler.connect(None)
|
||||
net_force_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
|
||||
|
||||
# 4) Wait for the sphere to enter and exit the force region
|
||||
Report.result(Tests.sphere_entered_region, helper.wait_for_condition(lambda: sphere.entered_force_region, TIMEOUT))
|
||||
Report.result(Tests.sphere_exited_region, helper.wait_for_condition(lambda: sphere.exited_force_region, TIMEOUT))
|
||||
|
||||
# 5) Check the calculated net force against what we expect
|
||||
absolute_difference = abs(sphere.net_force_magnitude - EXPECTED_MAGNITUDE)
|
||||
error = absolute_difference / EXPECTED_MAGNITUDE
|
||||
net_force_was_close = error < PERMISSIBLE_ERROR
|
||||
|
||||
Report.result(Tests.force_magnitude_close, net_force_was_close)
|
||||
if not net_force_was_close:
|
||||
Report.info(
|
||||
"\nExpected Magnitude: {}"
|
||||
"\nActual Magnitude: {}"
|
||||
"\nPermissible Error: {}"
|
||||
"\nMeasured Error: {}".format(EXPECTED_MAGNITUDE, sphere.net_force_magnitude, PERMISSIBLE_ERROR, error)
|
||||
)
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_SmallMagnitudeDeviationOnLargeForces)
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C5959759
|
||||
# Test Case Title : Check that force region (sphere) exerts point force
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_cube = ("Entity Cube found", "Cube not found")
|
||||
find_force_region = ("Entity force region found", "Force region not found")
|
||||
gravity_works = ("Cube falls", "Cube did not fall")
|
||||
sphere_gravity_enabled = ("Gravity is enabled on the cube", "Gravity is not enabled on the cube")
|
||||
force_region_trigger = ("Cube entered and exited force region", "Cube did not enter adn exit force region")
|
||||
force_calculated = ("OnCalculateNetForce calculated", "OnCalculateNetForce did not get calculated")
|
||||
force_x_vector = ("Force x vector is positive", "Force x vector is not positive")
|
||||
force_y_vector = ("Force y vector is positive", "Force y vector is not positive")
|
||||
point_force_magnitude_value = ("Magnitude is set to 1000", "Magnitude is not set to 1000")
|
||||
point_force_magnitude = ("Calculated magnitude is greater than set magnitude", "Calculated magnitude is not greater than set magnitude")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_SphereShapedForce():
|
||||
"""
|
||||
Level Setup
|
||||
The level consists of a sherical force region with a point force of 1000.
|
||||
A RigidBody cube with mass 1kg is positioned above the force region at an offset.
|
||||
On entering game mode the cube will fall into the spherical point force region.
|
||||
This should cause the cube to bounce off at considerable velocity.
|
||||
We validate the point force observed is as expected
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
EXPECTED_MAGNITUDE = 1000.0
|
||||
NEGATIVE_VELOCITY = -0.001
|
||||
TOLERANCE = 0.001
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "ForceRegion_SphereShapedForce")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve entities
|
||||
cube_id = general.find_game_entity("CubeRigidBody")
|
||||
Report.critical_result(Tests.find_cube, cube_id.IsValid())
|
||||
|
||||
sphere_force_region = general.find_game_entity("SphereForceRegion")
|
||||
Report.critical_result(Tests.find_force_region, sphere_force_region.IsValid())
|
||||
|
||||
# 3) Gravity works
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", cube_id)
|
||||
Report.result(Tests.sphere_gravity_enabled, gravity_enabled)
|
||||
|
||||
def is_going_down():
|
||||
vel = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", cube_id)
|
||||
return vel.z < NEGATIVE_VELOCITY
|
||||
|
||||
helper.wait_for_condition(is_going_down, 1.0)
|
||||
cube_linear_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", cube_id)
|
||||
Report.info("Cube z velocity from gravity: {}".format(cube_linear_velocity.z))
|
||||
Report.result(Tests.gravity_works, cube_linear_velocity.z < NEGATIVE_VELOCITY)
|
||||
|
||||
# 4) Listen to trigger events and OnCalculateNetForce notification
|
||||
class SphereForceRegion:
|
||||
enter = False
|
||||
exit = False
|
||||
|
||||
def on_enter(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(cube_id):
|
||||
Report.info("Cube touched spherical force region")
|
||||
SphereForceRegion.enter = True
|
||||
|
||||
def on_exit(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(cube_id):
|
||||
Report.info("Cube touched spherical force region")
|
||||
SphereForceRegion.exit = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(sphere_force_region)
|
||||
handler.add_callback("OnTriggerEnter", on_enter)
|
||||
handler.add_callback("OnTriggerExit", on_exit)
|
||||
|
||||
class NetForce:
|
||||
vector = None
|
||||
magnitude = 0
|
||||
|
||||
def on_calc_net_force(args):
|
||||
"""
|
||||
args[0] - force region entity
|
||||
args[1] - entity entering
|
||||
args[2] - vector
|
||||
args[3] - magnitude
|
||||
"""
|
||||
entering_entity = args[1]
|
||||
if entering_entity.Equal(cube_id):
|
||||
vector = args[2]
|
||||
magnitude = args[3]
|
||||
Report.info_vector3(vector, "Net Force vector", magnitude)
|
||||
NetForce.vector = vector
|
||||
NetForce.magnitude = magnitude
|
||||
|
||||
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
force_notification_handler.connect(None)
|
||||
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
|
||||
|
||||
def force_region_enter_and_exit():
|
||||
return SphereForceRegion.enter and SphereForceRegion.exit
|
||||
|
||||
helper.wait_for_condition(force_region_enter_and_exit, 3.0)
|
||||
|
||||
# 5) Report results
|
||||
Report.result(Tests.force_region_trigger, force_region_enter_and_exit())
|
||||
force_region_magnitude = azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "GetMagnitude", sphere_force_region)
|
||||
Report.info("Force region magnitude = {}".format(force_region_magnitude))
|
||||
# explicit check that the value is as expected
|
||||
# set at level creation
|
||||
Report.result(Tests.point_force_magnitude_value, force_region_magnitude == EXPECTED_MAGNITUDE)
|
||||
# prevent the test from hanging if the force vector is not set
|
||||
if NetForce.vector:
|
||||
Report.success(Tests.force_calculated)
|
||||
Report.result(Tests.force_x_vector, NetForce.vector.x > 0)
|
||||
Report.result(Tests.force_y_vector, NetForce.vector.z > 0)
|
||||
else:
|
||||
Report.failure(Tests.force_calculated)
|
||||
outcome = abs(NetForce.magnitude - force_region_magnitude) < TOLERANCE
|
||||
Report.result(Tests.point_force_magnitude, outcome)
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_SphereShapedForce)
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C5932045
|
||||
# Test Case Title : Check that force region exerts spline follow force on rigid bodies
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("Sphere entity found", "Sphere entity not found")
|
||||
find_force_region = ("Force region found", "Force region not found")
|
||||
find_trigger_0 = ("Trigger0 entity found", "Trigger0 entity not found")
|
||||
find_trigger_1 = ("Trigger1 entity found", "Trigger1 entity not found")
|
||||
find_trigger_2 = ("Trigger2 entity found", "Trigger2 entity not found")
|
||||
find_trigger_3 = ("Trigger3 entity found", "Trigger3 entity not found")
|
||||
triggers_positioned_apart = ("All triggers were positioned apart", "All triggers were not positioned apart")
|
||||
sphere_fell = ("The sphere fell", "The sphere did not fall")
|
||||
sphere_entered_force_region = ("The sphere entered the force region", "The sphere did not enter the force region before timeout")
|
||||
sphere_reached_trigger0 = ("The sphere reached Trigger0", "The sphere did not reach Trigger0 before timeout")
|
||||
sphere_reached_trigger1 = ("The sphere reached Trigger1", "The sphere did not reach Trigger1 before timeout")
|
||||
sphere_reached_trigger2 = ("The sphere reached Trigger2", "The sphere did not reach Trigger2 before timeout")
|
||||
sphere_reached_trigger3 = ("The sphere reached Trigger3", "The sphere did not reach Trigger3 before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_SplineForceOnRigidBodies():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that a PhysX force region exerts spline follow force on rigid bodies
|
||||
|
||||
Level Description:
|
||||
A sphere entity is positioned above a force region entity
|
||||
The sphere has a PhysX collider (sphere) and rigidbody component with default values
|
||||
|
||||
The force region entity has a PhysX collider (Box) and force region with a 'spline follow' force.
|
||||
It also has a spline component with 4 nodes. Each node is connected linearly in the following pattern:
|
||||
[0]___
|
||||
___[1]
|
||||
[2]___
|
||||
[3]
|
||||
|
||||
There are 4 trigger entities, each with a PhysX collider (sphere). They are positioned at each node.
|
||||
|
||||
Expected Behavior:
|
||||
The sphere will fall into the force region and begin to follow the spline. It will visit each node in order.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find entities
|
||||
4) Verify triggers are apart
|
||||
5) Drop the sphere
|
||||
6) Wait for sphere to complete path
|
||||
7) Exit game mode
|
||||
8) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import itertools
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
|
||||
TIMEOUT = 5
|
||||
MIN_TRIGGER_DISTANCE = 2
|
||||
|
||||
class Sphere:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
|
||||
def get_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
def is_falling(self):
|
||||
return self.get_velocity().z < 0.0
|
||||
|
||||
class Trigger:
|
||||
def __init__(self, name, valid_test, triggered_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.valid_test = valid_test
|
||||
self.triggered_test = triggered_test
|
||||
self.triggered = False
|
||||
self.create_handler()
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
self.triggered = True
|
||||
|
||||
def create_handler(self):
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
def are_apart(position1, position2, distance):
|
||||
return position1.GetDistance(position2) >= distance
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_SplineForceOnRigidBodies")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Find entities
|
||||
sphere = Sphere("Sphere")
|
||||
Report.critical_result(Tests.find_sphere, sphere.id.IsValid())
|
||||
|
||||
force_region = Trigger("ForceRegion", Tests.find_force_region, Tests.sphere_entered_force_region)
|
||||
trigger0 = Trigger("Trigger0", Tests.find_trigger_0, Tests.sphere_reached_trigger0)
|
||||
trigger1 = Trigger("Trigger1", Tests.find_trigger_1, Tests.sphere_reached_trigger1)
|
||||
trigger2 = Trigger("Trigger2", Tests.find_trigger_2, Tests.sphere_reached_trigger2)
|
||||
trigger3 = Trigger("Trigger3", Tests.find_trigger_3, Tests.sphere_reached_trigger3)
|
||||
all_triggers = (force_region, trigger0, trigger1, trigger2, trigger3)
|
||||
|
||||
for trigger in all_triggers:
|
||||
Report.critical_result(trigger.valid_test, trigger.id.IsValid())
|
||||
|
||||
# 4) Verify triggers are apart
|
||||
all_triggers_apart = True
|
||||
for combination in itertools.combinations(all_triggers, 2):
|
||||
if not are_apart(combination[0].get_position(), combination[1].get_position(), MIN_TRIGGER_DISTANCE):
|
||||
all_triggers_apart = False
|
||||
|
||||
Report.critical_result(Tests.triggers_positioned_apart, all_triggers_apart)
|
||||
|
||||
# 5) Drop the sphere
|
||||
Report.result(Tests.sphere_fell, helper.wait_for_condition(sphere.is_falling, TIMEOUT))
|
||||
|
||||
# 6) Wait for sphere to complete path
|
||||
for trigger in all_triggers:
|
||||
Report.result(trigger.triggered_test, helper.wait_for_condition(lambda: trigger.triggered, TIMEOUT))
|
||||
|
||||
# 7) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_SplineForceOnRigidBodies)
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C12868580
|
||||
# Test Case Title : Check that spline follow force works if transform components of entity are altered
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("Sphere entity found", "Sphere entity not found")
|
||||
find_force_region = ("Force region found", "Force region not found")
|
||||
find_trigger_0 = ("Trigger0 entity found", "Trigger0 entity not found")
|
||||
find_trigger_1 = ("Trigger1 entity found", "Trigger1 entity not found")
|
||||
find_trigger_2 = ("Trigger2 entity found", "Trigger2 entity not found")
|
||||
find_trigger_3 = ("Trigger3 entity found", "Trigger3 entity not found")
|
||||
triggers_positioned_apart = ("All triggers were positioned apart", "All triggers were not positioned apart")
|
||||
sphere_entered_force_region = ("The sphere entered the force region", "The sphere did not enter the force region before timeout")
|
||||
sphere_reached_trigger0 = ("The sphere reached Trigger0", "The sphere did not reach Trigger0 before timeout")
|
||||
sphere_reached_trigger1 = ("The sphere reached Trigger1", "The sphere did not reach Trigger1 before timeout")
|
||||
sphere_reached_trigger2 = ("The sphere reached Trigger2", "The sphere did not reach Trigger2 before timeout")
|
||||
sphere_reached_trigger3 = ("The sphere reached Trigger3", "The sphere did not reach Trigger3 before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_SplineRegionWithModifiedTransform():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that a PhysX force region correctly exerts spline follow force on rigid bodies when
|
||||
its transform component has been modified
|
||||
|
||||
Level Description:
|
||||
A sphere entity is positioned outside (in the +x direction) a force region entity.
|
||||
The sphere has a PhysX collider (sphere) and rigidbody component with gravity disabled, and an initial velocity of
|
||||
-3 m/s (in the x direction)
|
||||
|
||||
The force region entity has a PhysX collider (Box) and force region with a 'spline follow' force.
|
||||
It also has a bezier spline component with 4 nodes. Each node is connected in a meandering path through the region.
|
||||
|
||||
[3]~~~[2]
|
||||
)
|
||||
O -> [0]~~~[1]
|
||||
(sphere)
|
||||
|
||||
The force region is transformed 45 degrees around the Z axis, and scaled by 2 units in the X, Y, and Z directions.
|
||||
|
||||
There are also 4 trigger entities, each with a PhysX collider (sphere). They are positioned at each node.
|
||||
|
||||
Expected Behavior:
|
||||
The sphere will enter into the force region and begin to follow the spline. It will visit each node in order.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find entities
|
||||
4) Verify triggers are apart
|
||||
5) Wait for sphere to complete path
|
||||
6) Exit game mode
|
||||
7) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import itertools
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
|
||||
# region Constants
|
||||
TIMEOUT = 5.0
|
||||
MIN_TRIGGER_DISTANCE = 2.0
|
||||
# endregion
|
||||
|
||||
# region Entity Classes
|
||||
class Sphere:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
|
||||
def get_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
class Trigger:
|
||||
def __init__(self, name, valid_test, triggered_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.valid_test = valid_test
|
||||
self.triggered_test = triggered_test
|
||||
self.triggered = False
|
||||
self.create_handler()
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
self.triggered = True
|
||||
|
||||
def create_handler(self):
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
# endregion
|
||||
|
||||
# region Helper Functions
|
||||
def are_apart(position1, position2, distance):
|
||||
return position1.GetDistance(position2) >= distance
|
||||
|
||||
# endregion
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_SplineRegionWithModifiedTransform")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Find Entities
|
||||
sphere = Sphere("Sphere")
|
||||
Report.critical_result(Tests.find_sphere, sphere.id.IsValid())
|
||||
|
||||
force_region = Trigger("ForceRegion", Tests.find_force_region, Tests.sphere_entered_force_region)
|
||||
trigger0 = Trigger("Trigger0", Tests.find_trigger_0, Tests.sphere_reached_trigger0)
|
||||
trigger1 = Trigger("Trigger1", Tests.find_trigger_1, Tests.sphere_reached_trigger1)
|
||||
trigger2 = Trigger("Trigger2", Tests.find_trigger_2, Tests.sphere_reached_trigger2)
|
||||
trigger3 = Trigger("Trigger3", Tests.find_trigger_3, Tests.sphere_reached_trigger3)
|
||||
all_triggers = (force_region, trigger0, trigger1, trigger2, trigger3)
|
||||
|
||||
for trigger in all_triggers:
|
||||
Report.critical_result(trigger.valid_test, trigger.id.IsValid())
|
||||
|
||||
# 4) Verify triggers are apart
|
||||
all_triggers_apart = True
|
||||
for trigger_a, trigger_b in itertools.combinations(all_triggers, 2):
|
||||
if not are_apart(trigger_a.get_position(), trigger_b.get_position(), MIN_TRIGGER_DISTANCE):
|
||||
Report.info("{} was not far enough away from {}".format(trigger_a.name, trigger_b.name))
|
||||
all_triggers_apart = False
|
||||
|
||||
Report.critical_result(Tests.triggers_positioned_apart, all_triggers_apart)
|
||||
|
||||
# 5) Wait for sphere to complete path
|
||||
for trigger in all_triggers:
|
||||
Report.result(trigger.triggered_test, helper.wait_for_condition(lambda: trigger.triggered, TIMEOUT))
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_SplineRegionWithModifiedTransform)
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
|
||||
Test case ID : C12905528
|
||||
Test Case Title : Check that user is warned if non-trigger collider component is used with force region
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_test_entity = ("Entity created successfully", "Failed to create Entity")
|
||||
add_physx_force_region = ("PhysX Force Region component added", "Failed to add PhysX Force Region component")
|
||||
add_physx_collider = ("PhysX Collider component added", "Failed to add PhysX Collider component")
|
||||
warnings_found = ("Warnings found in logs", "No warnings found in logs")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_WithNonTriggerColliderWarning():
|
||||
"""
|
||||
Summary:
|
||||
Create entity with PhysX Force Region component. Check that user is warned if new PhysX Collider component is
|
||||
added to Entity.
|
||||
|
||||
Expected Behavior:
|
||||
User is warned by message in the console that the PhysX Collider component was not marked as a trigger
|
||||
|
||||
Test Steps:
|
||||
1) Load the empty level
|
||||
2) Create test entity
|
||||
3) Add PhysX Force Region component
|
||||
4) Start the Tracer to catch any errors and warnings
|
||||
5) Add PhysX Collider component to the Entity
|
||||
6) Verify there is warning in the logs
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import azlmbr.legacy.general as general
|
||||
from editor_python_test_tools.editor_entity_utils import EditorEntity
|
||||
from editor_python_test_tools.utils import Report
|
||||
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
from editor_python_test_tools.utils import Tracer
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the empty level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create test entity
|
||||
test_entity = EditorEntity.create_editor_entity("TestEntity")
|
||||
Report.result(Tests.create_test_entity, test_entity.id.IsValid())
|
||||
|
||||
# 3) Add PhysX Force Region component
|
||||
test_entity.add_component("PhysX Force Region")
|
||||
Report.result(Tests.add_physx_force_region, test_entity.has_component("PhysX Force Region"))
|
||||
|
||||
# 4) Start the Tracer to catch any errors and warnings
|
||||
Report.info("Starting warning monitoring")
|
||||
with Tracer() as section_tracer:
|
||||
# 5) Add the PhysX Collider component
|
||||
test_entity.add_component("PhysX Collider")
|
||||
Report.result(Tests.add_physx_collider, test_entity.has_component("PhysX Collider"))
|
||||
general.idle_wait_frames(1)
|
||||
Report.info("Ending warning monitoring")
|
||||
|
||||
# ) Verify there is warning in the logs
|
||||
success_condition = section_tracer.has_warnings
|
||||
# Checking if warning exist and the exact warning is caught in the expected lines in Test file
|
||||
Report.result(Tests.warnings_found, success_condition)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_WithNonTriggerColliderWarning)
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C5932040
|
||||
# Test Case Title : Check that force region exerts world space force on rigid bodies
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ball = ("Ball entity found", "Ball entity not found")
|
||||
find_box = ("Box entity found", "Box entity not found")
|
||||
gravity_works = ("Ball fell", "Ball did not fall")
|
||||
ball_triggers_force_region = ("Ball triggered force region", "Ball did not trigger force region")
|
||||
net_force_magnitude = ("The net force magnitude on the ball is close to expected value", "The net force magnitude on the ball is not close to expected value")
|
||||
ball_moved_up = ("Ball moved up", "Ball did not move up before timeout occurred")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def is_close_float(a, b, factor):
|
||||
return abs(b - a) < factor
|
||||
|
||||
|
||||
def ForceRegion_WorldSpaceForceOnRigidBodies():
|
||||
"""
|
||||
Summary:
|
||||
A ball is suspended over a box shaped force region.
|
||||
The ball drops and is forced upward by the world space force of the force region
|
||||
|
||||
Level Description:
|
||||
Ball (entity) - Sphere shaped Mesh; Sphere shaped PhysX Collider; PhysX Rigid Body
|
||||
ForceRegion (entity) - Cube shaped Mesh; Cube shaped PhysX Collider; PhysX Force Region with world space force
|
||||
|
||||
Expected Behavior:
|
||||
The level opens and enters game mode. At this time the ball will fall towards the force region.
|
||||
When it collides, it will be launched upwards. Then the game mode will exit and the editor will close.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find the entities
|
||||
4) Get starting position of the ball
|
||||
5) Check that gravity works and ball falls
|
||||
6) Check that the ball enters the trigger area of force region
|
||||
7) Get the magnitude of the collision
|
||||
8) Check that the ball moved up
|
||||
9) Verify that the magnitude of the collision is as expected
|
||||
10) Exit game mode
|
||||
11) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
class Ball:
|
||||
start_position_z = None
|
||||
fell = False
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 2.0
|
||||
BALL_MIN_MOVED_UP = 5 # Minimum amount to indicate Z movement
|
||||
MAGNITUDE_TOLERANCE = 0.26 # Force region magnitude tolerance
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_WorldSpaceForceOnRigidBodies")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
ball_id = general.find_game_entity("Ball")
|
||||
Report.critical_result(Tests.find_ball, ball_id.IsValid())
|
||||
|
||||
box_id = general.find_game_entity("ForceRegion")
|
||||
Report.critical_result(Tests.find_box, box_id.IsValid())
|
||||
|
||||
# 4) Get the starting z position of the ball
|
||||
Ball.start_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
Report.info("Starting Height of the ball: {}".format(Ball.start_position_z))
|
||||
|
||||
# 5) Check that gravity works and the ball falls
|
||||
def ball_falls():
|
||||
if not Ball.fell:
|
||||
ball_after_frame_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
if (ball_after_frame_z - Ball.start_position_z) < 0.0:
|
||||
Report.info("Ball position is now lower than the starting position")
|
||||
Ball.fell = True
|
||||
return Ball.fell
|
||||
|
||||
helper.wait_for_condition(ball_falls, TIMEOUT)
|
||||
Report.result(Tests.gravity_works, Ball.fell)
|
||||
|
||||
# 6) Check that the ball enters the trigger area
|
||||
class ForceRegionTrigger:
|
||||
entered = False
|
||||
exited = False
|
||||
|
||||
def on_enter(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(ball_id):
|
||||
Report.info("Trigger entered")
|
||||
ForceRegionTrigger.entered = True
|
||||
|
||||
def on_exit(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(ball_id):
|
||||
Report.info("Trigger exited")
|
||||
ForceRegionTrigger.exited = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(box_id)
|
||||
handler.add_callback("OnTriggerEnter", on_enter)
|
||||
handler.add_callback("OnTriggerExit", on_exit)
|
||||
|
||||
helper.wait_for_condition(lambda: ForceRegionTrigger.entered, TIMEOUT)
|
||||
Report.result(Tests.ball_triggers_force_region, ForceRegionTrigger.entered)
|
||||
|
||||
# 7) Get the magnitude of the collision
|
||||
class NetForceMagnitude:
|
||||
value = 0
|
||||
|
||||
def on_calc_net_force(args):
|
||||
"""
|
||||
args[0] - force region entity
|
||||
args[1] - entity entering
|
||||
args[2] - vector
|
||||
args[3] - magnitude
|
||||
"""
|
||||
force_magnitude = args[3]
|
||||
NetForceMagnitude.value = force_magnitude
|
||||
|
||||
force_notification_handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
force_notification_handler.connect(None)
|
||||
force_notification_handler.add_callback("OnCalculateNetForce", on_calc_net_force)
|
||||
|
||||
def ball_moved_up():
|
||||
ball_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
return ball_z > Ball.start_position_z + BALL_MIN_MOVED_UP
|
||||
|
||||
# 8) Check that the ball moved up
|
||||
if helper.wait_for_condition(lambda: ball_moved_up and ForceRegionTrigger.exited, TIMEOUT):
|
||||
Report.success(Tests.ball_moved_up)
|
||||
else:
|
||||
Report.failure(Tests.ball_moved_up)
|
||||
|
||||
# 9) Verify that the magnitude of the collision is as expected
|
||||
force_region_magnitude = azlmbr.physics.ForceWorldSpaceRequestBus(azlmbr.bus.Event, "GetMagnitude", box_id)
|
||||
Report.info(
|
||||
"NetForce magnitude is {}, Force Region magnitude is {}".format(NetForceMagnitude.value, force_region_magnitude)
|
||||
)
|
||||
Report.result(
|
||||
Tests.net_force_magnitude, is_close_float(NetForceMagnitude.value, force_region_magnitude, MAGNITUDE_TOLERANCE)
|
||||
)
|
||||
|
||||
# 10) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_WorldSpaceForceOnRigidBodies)
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
"""
|
||||
Test case ID : C6090552
|
||||
Test Case Title : Check that force region exerts linear damping force on rigid bodies (negative test)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
ball_exists = ("The Ball entity was found", "The Ball entity was not found")
|
||||
forceregion_exists = ("The ForceRegion entity was found", "The ForceRegion entity was not found")
|
||||
terrain_exists = ("The Terrain entity was found", "The Terrain entity was not found")
|
||||
ball_over_force_region = ("The ball is over the force region", "The ball is not over the force region")
|
||||
gravity_enabled_on_ball = ("The ball has gravity enabled", "The ball gravity failed to enable")
|
||||
ball_entered_force_region = ("The ball entered the force region", "The ball did not enter the force region before timeout")
|
||||
ball_fell_through_region = ("The ball fell through the force region", "The ball did not exit force region below enter location")
|
||||
enter_exit_distance_close = ("The ball has passed through the entire force region", "Ball did not move the height of the force region")
|
||||
ball_hit_ground = ("The ball collided with the PhysX Terrain", "The ball did not collide with the PhysX Terrain before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ZeroLinearDampingDoesNothing():
|
||||
"""
|
||||
Summary:
|
||||
A ball is suspended over a force region with a linear damping value of zero and a PhysX Terrain.
|
||||
|
||||
Level Description:
|
||||
Ball - (entity): Mesh: Sphere shaped
|
||||
PhysX Rigid Body: Gravity is enabled; Default settings
|
||||
PhysX Collider: Sphere shape; offset (0.0, 0.0, 0.5); draw collider checked
|
||||
|
||||
ForceRegion - (entity): Box Shape: Game view checked; Shape dimensions (3.0, 3.0, 3.0)
|
||||
PhysX Force Region: Linear damping force: 0.0
|
||||
PhysX Collider: Box shape; Box dimensions (3.0, 3.0, 3.0); Trigger checked;
|
||||
Draw collider checked
|
||||
|
||||
Terrain - (entity): PhysX Terrain: Default settings
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the ball will fall down toward the force region. It will enter the region and fall
|
||||
straight through as if the region did not exist because the linear damping is set to zero. It will then
|
||||
collide with the PhysX Terrain.
|
||||
|
||||
Test Steps
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find/setup entities and handlers
|
||||
4) Check that the ball is over the force region
|
||||
5) Wait for ball to hit the ground
|
||||
6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
|
||||
7) Exit game mode and close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
TIMEOUT_SECONDS = 3.0
|
||||
X_Y_Z_TOLERANCE = 1.5
|
||||
REGION_HEIGHT = 3.0
|
||||
TERRAIN_HEIGHT = 32.0
|
||||
|
||||
def is_close(value1, value2, tolerance):
|
||||
"""
|
||||
() -> bool
|
||||
Absolute value of difference of values being less than or equal to the tolerance
|
||||
"""
|
||||
return abs(value1 - value2) <= tolerance
|
||||
|
||||
def is_at_least(value1, value2, minimum):
|
||||
"""
|
||||
() -> bool
|
||||
Subtraction of value2 from value1 being greater than or equal to the minimum
|
||||
"""
|
||||
return (value1 - value2) >= minimum
|
||||
|
||||
class Entity(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(Tests.__dict__[self.name.lower() + "_exists"], self.id.IsValid())
|
||||
|
||||
def get_location(self):
|
||||
# () -> Vector3
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Ball(Entity):
|
||||
def __init__(self):
|
||||
super(Ball, self).__init__("Ball")
|
||||
self.location = self.get_location()
|
||||
self.enter_region_location = None
|
||||
self.exit_region_location = None
|
||||
self.enable_gravity()
|
||||
self.check_gravity_is_on()
|
||||
|
||||
def enable_gravity(self):
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id)
|
||||
|
||||
def check_gravity_is_on(self):
|
||||
gravity_status = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
Report.critical_result(Tests.__dict__["gravity_enabled_on_" + self.name.lower()], gravity_status)
|
||||
|
||||
def check_alignment(self, region):
|
||||
# () -> bool
|
||||
Report.info_vector3(self.location, "Location of Ball: ")
|
||||
Report.info_vector3(region.location, "Location of ForceRegion: ")
|
||||
|
||||
aligned = True
|
||||
if not is_close(self.location.x, region.location.x, X_Y_Z_TOLERANCE):
|
||||
aligned = False
|
||||
Report.info("The ball is not aligned in the X axis.")
|
||||
if not is_close(self.location.y, region.location.y, X_Y_Z_TOLERANCE):
|
||||
aligned = False
|
||||
Report.info("The ball is not aligned in the Y axis.")
|
||||
if (self.location.z - region.location.z) < X_Y_Z_TOLERANCE:
|
||||
aligned = False
|
||||
Report.info("The ball is not high enough to enter the force region.")
|
||||
if self.location.z < TERRAIN_HEIGHT:
|
||||
aligned = False
|
||||
Report.info("The ball is below the terrain.")
|
||||
return aligned
|
||||
|
||||
def did_ball_fall_in_region_as_expected(self):
|
||||
# () -> bool
|
||||
if not self.exit_region_location:
|
||||
Report.info("The ball did not exit the force region in time.")
|
||||
return False
|
||||
return (self.enter_region_location.z - self.exit_region_location.z) > X_Y_Z_TOLERANCE
|
||||
|
||||
def check_enter_vs_exit_locations(self):
|
||||
"""
|
||||
Check that the ball enter location is above the exit location (ball successfully fell through the force
|
||||
region) and check that the distance between the enter/exit locations is at least the height of the region
|
||||
|
||||
NOTE: The ball will "enter" the force region when the bottom edge of its collision box enters the region and
|
||||
will "exit" when it is completely outside the force region (top edge leaves region)
|
||||
"""
|
||||
Report.info_vector3(self.enter_region_location, "Ball entering location: ")
|
||||
Report.info_vector3(self.exit_region_location, "Ball exiting location: ")
|
||||
|
||||
Report.result(Tests.ball_fell_through_region, self.did_ball_fall_in_region_as_expected())
|
||||
Report.result(
|
||||
Tests.enter_exit_distance_close,
|
||||
is_at_least(self.enter_region_location.z, self.exit_region_location.z, REGION_HEIGHT),
|
||||
)
|
||||
|
||||
class ForceRegion(Entity):
|
||||
def __init__(self, ball):
|
||||
super(ForceRegion, self).__init__("ForceRegion")
|
||||
self.ball = ball # This creates a reference point to access the ball without looking in global scope
|
||||
self.location = self.get_location()
|
||||
self.ball_entered_trigger_area = False
|
||||
self.trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.connect_trigger_handler()
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball has entered the force region trigger area.")
|
||||
self.ball_entered_trigger_area = True
|
||||
self.ball.enter_region_location = self.ball.get_location()
|
||||
|
||||
def on_trigger_exit(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball has exited the force region trigger area.")
|
||||
self.ball.exit_region_location = self.ball.get_location()
|
||||
|
||||
def connect_trigger_handler(self):
|
||||
self.trigger_handler.connect(self.id)
|
||||
self.trigger_handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
self.trigger_handler.add_callback("OnTriggerExit", self.on_trigger_exit)
|
||||
|
||||
class Terrain(Entity):
|
||||
def __init__(self, ball):
|
||||
super(Terrain, self).__init__("Terrain")
|
||||
self.ball = ball # This creates a reference point to access the ball without looking in global scope
|
||||
self.ball_collided = False
|
||||
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.connect_collision_handler()
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball struck PhysX Terrain")
|
||||
self.ball_collided = True
|
||||
|
||||
def connect_collision_handler(self):
|
||||
self.collision_handler.connect(self.id)
|
||||
self.collision_handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_ZeroLinearDampingDoesNothing")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Find/setup entities and handlers
|
||||
ball = Ball()
|
||||
region = ForceRegion(ball)
|
||||
terrain = Terrain(ball)
|
||||
|
||||
# 4) Check that the ball is over the force region
|
||||
Report.critical_result(Tests.ball_over_force_region, ball.check_alignment(region))
|
||||
|
||||
# 5) Wait for ball to hit the ground
|
||||
helper.wait_for_condition(lambda: terrain.ball_collided, TIMEOUT_SECONDS)
|
||||
|
||||
# 6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
|
||||
Report.result(Tests.ball_entered_force_region, region.ball_entered_trigger_area)
|
||||
ball.check_enter_vs_exit_locations()
|
||||
Report.result(Tests.ball_hit_ground, terrain.ball_collided)
|
||||
|
||||
# 7) Exit game mode and close the editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ZeroLinearDampingDoesNothing)
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
"""
|
||||
Test case ID : C6090551
|
||||
Test Case Title : Check that force region exerts local space force on rigid bodies (negative test)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
ball_exists = ("The Ball entity was found", "The Ball entity was not found")
|
||||
forceregion_exists = ("The ForceRegion entity was found", "The ForceRegion entity was not found")
|
||||
terrain_exists = ("The Terrain entity was found", "The Terrain entity was not found")
|
||||
ball_over_force_region = ("The ball is over the force region", "The ball is not over the force region")
|
||||
gravity_enabled_on_ball = ("The ball has gravity enabled", "The ball gravity failed to enable")
|
||||
ball_entered_force_region = ("The ball entered the force region", "The ball did not enter the force region before timeout")
|
||||
ball_fell_through_region = ("The ball fell through the force region", "The ball did not exit force region below enter location")
|
||||
enter_exit_distance_close = ("The ball has passed through the entire force region", "Ball did not move the height of the force region")
|
||||
ball_hit_ground = ("The ball collided with the PhysX Terrain", "The ball did not collide with the PhysX Terrain before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ZeroLocalSpaceForceDoesNothing():
|
||||
"""
|
||||
Summary:
|
||||
A ball is suspended over a box shaped force region and a PhysX Terrain. The force is a local space force
|
||||
pointed in the positive Z direction with a magnitude of 10.
|
||||
|
||||
Level Description:
|
||||
Ball - (entity): Mesh: Sphere shaped
|
||||
PhysX Rigid Body: Gravity is enabled; Default settings
|
||||
PhysX Collider: Sphere shape; offset (0.0, 0.0, 0.5); draw collider checked
|
||||
|
||||
ForceRegion - (entity): Box Shape: Game view checked; Shape dimensions (3.0, 3.0, 3.0)
|
||||
PhysX Force Region: Local Space force; direction (0.0, 0.0, 1.0); magnitude 10.0
|
||||
PhysX Collider: Box shape; Box dimensions (3.0, 3.0, 3.0); Trigger checked;
|
||||
Draw collider checked
|
||||
|
||||
Terrain - (entity): PhysX Terrain: Default settings
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the ball entity will fall due to gravity and enter the force region.
|
||||
The region has a very weak force and therefore the ball will fall through the force region,
|
||||
where it will then collide with the PhysX Terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find/setup entities and handlers
|
||||
4) Check that the ball is over the force region
|
||||
5) Wait for ball to hit the ground
|
||||
6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
|
||||
7) Exit game mode and close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
TIMEOUT_SECONDS = 3.0
|
||||
X_Y_Z_TOLERANCE = 1.5
|
||||
REGION_HEIGHT = 3.0
|
||||
TERRAIN_HEIGHT = 32.0
|
||||
|
||||
def is_close(value1, value2, tolerance):
|
||||
"""
|
||||
() -> bool
|
||||
Absolute value of difference of values being less than or equal to the tolerance
|
||||
"""
|
||||
return abs(value1 - value2) <= tolerance
|
||||
|
||||
def is_at_least(value1, value2, minimum):
|
||||
"""
|
||||
() -> bool
|
||||
Subtraction of value2 from value1 being greater than or equal to the minimum
|
||||
"""
|
||||
return (value1 - value2) >= minimum
|
||||
|
||||
class Entity(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(Tests.__dict__[self.name.lower() + "_exists"], self.id.IsValid())
|
||||
|
||||
def get_location(self):
|
||||
# () -> Vector3
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Ball(Entity):
|
||||
def __init__(self):
|
||||
super(Ball, self).__init__("Ball")
|
||||
self.location = self.get_location()
|
||||
self.enter_region_location = None
|
||||
self.exit_region_location = None
|
||||
self.enable_gravity()
|
||||
self.check_gravity_is_on()
|
||||
|
||||
def enable_gravity(self):
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id)
|
||||
|
||||
def check_gravity_is_on(self):
|
||||
gravity_status = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
Report.critical_result(Tests.__dict__["gravity_enabled_on_" + self.name.lower()], gravity_status)
|
||||
|
||||
def check_alignment(self, region):
|
||||
# () -> bool
|
||||
Report.info_vector3(self.location, "Location of Ball: ")
|
||||
Report.info_vector3(region.location, "Location of ForceRegion: ")
|
||||
|
||||
aligned = True
|
||||
if not is_close(self.location.x, region.location.x, X_Y_Z_TOLERANCE):
|
||||
aligned = False
|
||||
Report.info("The ball is not aligned in the X axis.")
|
||||
if not is_close(self.location.y, region.location.y, X_Y_Z_TOLERANCE):
|
||||
aligned = False
|
||||
Report.info("The ball is not aligned in the Y axis.")
|
||||
if (self.location.z - region.location.z) < X_Y_Z_TOLERANCE:
|
||||
aligned = False
|
||||
Report.info("The ball is not high enough to enter the force region.")
|
||||
if self.location.z < TERRAIN_HEIGHT:
|
||||
aligned = False
|
||||
Report.info("The ball is below the terrain.")
|
||||
return aligned
|
||||
|
||||
def did_ball_fall_in_region_as_expected(self):
|
||||
# () -> bool
|
||||
if not self.exit_region_location:
|
||||
Report.info("The ball did not exit the force region in time.")
|
||||
return False
|
||||
return (self.enter_region_location.z - self.exit_region_location.z) > X_Y_Z_TOLERANCE
|
||||
|
||||
def check_enter_vs_exit_locations(self):
|
||||
"""
|
||||
Check that the ball enter location is above the exit location (ball successfully fell through the force
|
||||
region) and check that the distance between the enter/exit locations is at least the height of the region
|
||||
|
||||
NOTE: The ball will "enter" the force region when the bottom edge of its collision box enters the region and
|
||||
will "exit" when it is completely outside the force region (top edge leaves region)
|
||||
"""
|
||||
Report.info_vector3(self.enter_region_location, "Ball entering location: ")
|
||||
Report.info_vector3(self.exit_region_location, "Ball exiting location: ")
|
||||
|
||||
Report.result(Tests.ball_fell_through_region, self.did_ball_fall_in_region_as_expected())
|
||||
Report.result(
|
||||
Tests.enter_exit_distance_close,
|
||||
is_at_least(self.enter_region_location.z, self.exit_region_location.z, REGION_HEIGHT),
|
||||
)
|
||||
|
||||
class ForceRegion(Entity):
|
||||
def __init__(self, ball):
|
||||
super(ForceRegion, self).__init__("ForceRegion")
|
||||
self.ball = ball # This creates a reference point to access the ball without looking in global scope
|
||||
self.location = self.get_location()
|
||||
self.ball_entered_trigger_area = False
|
||||
self.trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.connect_trigger_handler()
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball has entered the force region trigger area.")
|
||||
self.ball_entered_trigger_area = True
|
||||
self.ball.enter_region_location = self.ball.get_location()
|
||||
|
||||
def on_trigger_exit(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball has exited the force region trigger area.")
|
||||
self.ball.exit_region_location = self.ball.get_location()
|
||||
|
||||
def connect_trigger_handler(self):
|
||||
self.trigger_handler.connect(self.id)
|
||||
self.trigger_handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
self.trigger_handler.add_callback("OnTriggerExit", self.on_trigger_exit)
|
||||
|
||||
class Terrain(Entity):
|
||||
def __init__(self, ball):
|
||||
super(Terrain, self).__init__("Terrain")
|
||||
self.ball = ball # This creates a reference point to access the ball without looking in global scope
|
||||
self.ball_collided = False
|
||||
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.connect_collision_handler()
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball struck PhysX Terrain")
|
||||
self.ball_collided = True
|
||||
|
||||
def connect_collision_handler(self):
|
||||
self.collision_handler.connect(self.id)
|
||||
self.collision_handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_ZeroLocalSpaceForceDoesNothing")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Find/setup entities and handlers
|
||||
ball = Ball()
|
||||
region = ForceRegion(ball)
|
||||
terrain = Terrain(ball)
|
||||
|
||||
# 4) Check that the ball is over the force region
|
||||
Report.critical_result(Tests.ball_over_force_region, ball.check_alignment(region))
|
||||
|
||||
# 5) Wait for ball to hit the ground
|
||||
helper.wait_for_condition(lambda: terrain.ball_collided, TIMEOUT_SECONDS)
|
||||
|
||||
# 6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
|
||||
Report.result(Tests.ball_entered_force_region, region.ball_entered_trigger_area)
|
||||
ball.check_enter_vs_exit_locations()
|
||||
Report.result(Tests.ball_hit_ground, terrain.ball_collided)
|
||||
|
||||
# 7) Exit game mode and close the editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ZeroLocalSpaceForceDoesNothing)
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
"""
|
||||
Test case ID : C6090554
|
||||
Test Case Title : Check that force region exerts point force on rigid bodies (negative test)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
ball_exists = ("The Ball entity was found", "The Ball entity was not found")
|
||||
forceregion_exists = ("The ForceRegion entity was found", "The ForceRegion entity was not found")
|
||||
terrain_exists = ("The Terrain entity was found", "The Terrain entity was not found")
|
||||
ball_over_force_region = ("The ball is over the force region", "The ball is not over the force region")
|
||||
gravity_enabled_on_ball = ("The ball has gravity enabled", "The ball gravity failed to enable")
|
||||
ball_entered_force_region = ("The ball entered the force region", "The ball did not enter the force region before timeout")
|
||||
ball_fell_through_region = ("The ball fell through the force region", "The ball did not exit force region below enter location")
|
||||
enter_exit_distance_close = ("The ball has passed through the entire force region", "Ball did not move the height of the force region")
|
||||
ball_hit_ground = ("The ball collided with the PhysX Terrain", "The ball did not collide with the PhysX Terrain before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ZeroPointForceDoesNothing():
|
||||
"""
|
||||
Summary:
|
||||
A ball is suspended over a box shaped force region and a PhysX Terrain. The force is a point force
|
||||
pointed outward from center with a magnitude of 10.
|
||||
|
||||
Level Description:
|
||||
Ball - (entity): Mesh: Sphere shaped
|
||||
PhysX Rigid Body: Gravity is enabled; Default settings
|
||||
PhysX Collider: Sphere shape; offset (0.0, 0.0, 0.5); draw collider checked
|
||||
|
||||
ForceRegion - (entity): Box Shape: Game view checked; Shape dimensions (3.0, 3.0, 3.0)
|
||||
PhysX Force Region: Point force; magnitude 10.0
|
||||
PhysX Collider: Box shape; Box dimensions (3.0, 3.0, 3.0); Trigger checked;
|
||||
Draw collider checked
|
||||
|
||||
Terrain - (entity): PhysX Terrain: Default settings
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the ball entity will fall due to gravity and enter the force region.
|
||||
The region has a very weak force and therefore the ball will fall through the force region,
|
||||
where it will then collide with the PhysX Terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find/setup entities and handlers
|
||||
4) Check that the ball is over the force region
|
||||
5) Wait for ball to hit the ground
|
||||
6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
|
||||
7) Exit game mode and close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
TIMEOUT_SECONDS = 3.0
|
||||
X_Y_Z_TOLERANCE = 1.5
|
||||
REGION_HEIGHT = 3.0
|
||||
TERRAIN_HEIGHT = 32.0
|
||||
|
||||
def is_close(value1, value2, tolerance):
|
||||
"""
|
||||
() -> bool
|
||||
Absolute value of difference of values being less than or equal to the tolerance
|
||||
"""
|
||||
return abs(value1 - value2) <= tolerance
|
||||
|
||||
def is_at_least(value1, value2, minimum):
|
||||
"""
|
||||
() -> bool
|
||||
Subtraction of value2 from value1 being greater than or equal to the minimum
|
||||
"""
|
||||
return (value1 - value2) >= minimum
|
||||
|
||||
class Entity(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(Tests.__dict__[self.name.lower() + "_exists"], self.id.IsValid())
|
||||
|
||||
def get_location(self):
|
||||
# () -> Vector3
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Ball(Entity):
|
||||
def __init__(self):
|
||||
super(Ball, self).__init__("Ball")
|
||||
self.location = self.get_location()
|
||||
self.enter_region_location = None
|
||||
self.exit_region_location = None
|
||||
self.enable_gravity()
|
||||
self.check_gravity_is_on()
|
||||
|
||||
def enable_gravity(self):
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id)
|
||||
|
||||
def check_gravity_is_on(self):
|
||||
gravity_status = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
Report.critical_result(Tests.__dict__["gravity_enabled_on_" + self.name.lower()], gravity_status)
|
||||
|
||||
def check_alignment(self, region):
|
||||
# () -> bool
|
||||
Report.info_vector3(self.location, "Location of Ball: ")
|
||||
Report.info_vector3(region.location, "Location of ForceRegion: ")
|
||||
|
||||
aligned = True
|
||||
if not is_close(self.location.x, region.location.x, X_Y_Z_TOLERANCE):
|
||||
aligned = False
|
||||
Report.info("The ball is not aligned in the X axis.")
|
||||
if not is_close(self.location.y, region.location.y, X_Y_Z_TOLERANCE):
|
||||
aligned = False
|
||||
Report.info("The ball is not aligned in the Y axis.")
|
||||
if (self.location.z - region.location.z) < X_Y_Z_TOLERANCE:
|
||||
aligned = False
|
||||
Report.info("The ball is not high enough to enter the force region.")
|
||||
if self.location.z < TERRAIN_HEIGHT:
|
||||
aligned = False
|
||||
Report.info("The ball is below the terrain.")
|
||||
return aligned
|
||||
|
||||
def did_ball_fall_in_region_as_expected(self):
|
||||
# () -> bool
|
||||
if not self.exit_region_location:
|
||||
Report.info("The ball did not exit the force region in time.")
|
||||
return False
|
||||
return (self.enter_region_location.z - self.exit_region_location.z) > X_Y_Z_TOLERANCE
|
||||
|
||||
def check_enter_vs_exit_locations(self):
|
||||
"""
|
||||
Check that the ball enter location is above the exit location (ball successfully fell through the force
|
||||
region) and check that the distance between the enter/exit locations is at least the height of the region
|
||||
|
||||
NOTE: The ball will "enter" the force region when the bottom edge of its collision box enters the region and
|
||||
will "exit" when it is completely outside the force region (top edge leaves region)
|
||||
"""
|
||||
Report.info_vector3(self.enter_region_location, "Ball entering location: ")
|
||||
Report.info_vector3(self.exit_region_location, "Ball exiting location: ")
|
||||
|
||||
Report.result(Tests.ball_fell_through_region, self.did_ball_fall_in_region_as_expected())
|
||||
Report.result(
|
||||
Tests.enter_exit_distance_close,
|
||||
is_at_least(self.enter_region_location.z, self.exit_region_location.z, REGION_HEIGHT),
|
||||
)
|
||||
|
||||
class ForceRegion(Entity):
|
||||
def __init__(self, ball):
|
||||
super(ForceRegion, self).__init__("ForceRegion")
|
||||
self.ball = ball # This creates a reference point to access the ball without looking in global scope
|
||||
self.location = self.get_location()
|
||||
self.ball_entered_trigger_area = False
|
||||
self.trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.connect_trigger_handler()
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball has entered the force region trigger area.")
|
||||
self.ball_entered_trigger_area = True
|
||||
self.ball.enter_region_location = self.ball.get_location()
|
||||
|
||||
def on_trigger_exit(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball has exited the force region trigger area.")
|
||||
self.ball.exit_region_location = self.ball.get_location()
|
||||
|
||||
def connect_trigger_handler(self):
|
||||
self.trigger_handler.connect(self.id)
|
||||
self.trigger_handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
self.trigger_handler.add_callback("OnTriggerExit", self.on_trigger_exit)
|
||||
|
||||
class Terrain(Entity):
|
||||
def __init__(self, ball):
|
||||
super(Terrain, self).__init__("Terrain")
|
||||
self.ball = ball # This creates a reference point to access the ball without looking in global scope
|
||||
self.ball_collided = False
|
||||
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.connect_collision_handler()
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball struck PhysX Terrain")
|
||||
self.ball_collided = True
|
||||
|
||||
def connect_collision_handler(self):
|
||||
self.collision_handler.connect(self.id)
|
||||
self.collision_handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_ZeroPointForceDoesNothing")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Find/setup entities and handlers
|
||||
ball = Ball()
|
||||
region = ForceRegion(ball)
|
||||
terrain = Terrain(ball)
|
||||
|
||||
# 4) Check that the ball is over the force region
|
||||
Report.critical_result(Tests.ball_over_force_region, ball.check_alignment(region))
|
||||
|
||||
# 5) Wait for ball to hit the ground
|
||||
helper.wait_for_condition(lambda: terrain.ball_collided, TIMEOUT_SECONDS)
|
||||
|
||||
# 6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
|
||||
Report.result(Tests.ball_entered_force_region, region.ball_entered_trigger_area)
|
||||
ball.check_enter_vs_exit_locations()
|
||||
Report.result(Tests.ball_hit_ground, terrain.ball_collided)
|
||||
|
||||
# 7) Exit game mode and close the editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ZeroPointForceDoesNothing)
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C6090553
|
||||
# Test Case Title : Check that force region exerts simple drag force on rigid bodies (negative test)
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ball = ("Ball found", "Ball not found")
|
||||
find_force_region = ("Force Region found", "Force Region not found")
|
||||
ball_gravity_disabled = ("Ball gravity disabled", "Ball gravity not disabled")
|
||||
ball_fell = ("The ball fell", "The ball did not fall")
|
||||
ball_enters_force_region = ("Ball entered force region", "Ball did not enter force region")
|
||||
ball_exits_force_region = ("Ball exited force region", "Ball did not exit force region")
|
||||
force_region_slows_ball = ("Force Region did not slow ball", "Force Region slows ball")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ZeroSimpleDragForceDoesNothing():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that force region exerts simple drag force on rigid bodies(negative test).
|
||||
|
||||
Level Description:
|
||||
Ball (entity) - contains a sphere mesh, PhysX Collider (sphere shape) and PhysX RigidBody. Ball is
|
||||
placed above force region
|
||||
Force Region (entity) - contains Physx Force Region with Simple Drag force with Region Density as 0 and
|
||||
PhysX Collider (box shape)
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, Sphere falls through force region as though force region doesn't exist because
|
||||
region density for the simple drag force is zero and has no effect on the dropping sphere.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter Game mode
|
||||
3) Validate the entities in the scene
|
||||
4) Check ball gravity is disabled or not
|
||||
5) Get initial velocity of ball
|
||||
6) Wait for ball to enter force region
|
||||
7) Gets z velocity and position of ball
|
||||
8) Wait for ball to exit force region
|
||||
9) Gets new velocity and position of ball
|
||||
10) Check that the ball does not slow due to the force region
|
||||
11) Exits game mode and editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
# Holds details about the ball
|
||||
class Ball:
|
||||
id = None
|
||||
initial_velocity_z = 0.0
|
||||
start_velocity_z = 0.0
|
||||
end_velocity_z = 0.0
|
||||
ball_start_z_position = 0.0
|
||||
ball_end_z_position = 0.0
|
||||
entered_force_region = False
|
||||
exited_force_region = False
|
||||
ball_fell_down = False
|
||||
ball_slows = False
|
||||
|
||||
def on_trigger_enter(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(Ball.id):
|
||||
Ball.entered_force_region = True
|
||||
|
||||
def on_trigger_exit(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(Ball.id):
|
||||
Ball.exited_force_region = True
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 2.0
|
||||
CLOSE_ENOUGH_THRESHOLD = 0.01
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_ZeroSimpleDragForceDoesNothing")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Validate the entities in the scene
|
||||
Ball.id = general.find_game_entity("Sphere")
|
||||
Report.critical_result(Tests.find_ball, Ball.id.IsValid())
|
||||
|
||||
force_region_id = general.find_game_entity("Force Region")
|
||||
Report.critical_result(Tests.find_force_region, force_region_id.IsValid())
|
||||
|
||||
# 4) Check ball gravity is disabled or not
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", Ball.id)
|
||||
Report.critical_result(Tests.ball_gravity_disabled, not gravity_enabled)
|
||||
|
||||
# 5) Get initial velocity of ball
|
||||
# Ball linear velocity is set at (0, 0, -5) in the level
|
||||
Ball.initial_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Ball.id).z
|
||||
Report.info("Ball initial velocity = {}".format(Ball.initial_velocity_z))
|
||||
|
||||
# 6) Wait for ball to enter force region
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(force_region_id)
|
||||
handler.add_callback("OnTriggerEnter", on_trigger_enter)
|
||||
handler.add_callback("OnTriggerExit", on_trigger_exit)
|
||||
|
||||
helper.wait_for_condition(lambda: Ball.entered_force_region, TIMEOUT)
|
||||
Report.critical_result(Tests.ball_enters_force_region, Ball.entered_force_region)
|
||||
|
||||
# 7) Gets z velocity and position of ball
|
||||
Ball.start_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Ball.id).z
|
||||
Ball.ball_start_z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Ball.id)
|
||||
Report.info(
|
||||
"Ball Start Z position = {} Ball Start Z Velocity = {}".format(
|
||||
Ball.ball_start_z_position, Ball.start_velocity_z
|
||||
)
|
||||
)
|
||||
|
||||
# 8) Wait for ball to exit force region
|
||||
helper.wait_for_condition(lambda: Ball.exited_force_region, TIMEOUT)
|
||||
Report.critical_result(Tests.ball_exits_force_region, Ball.exited_force_region)
|
||||
|
||||
# 9) Gets new velocity and position of ball
|
||||
Ball.end_velocity_z = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", Ball.id).z
|
||||
Ball.ball_end_z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Ball.id)
|
||||
Report.info(
|
||||
"Ball End Z position = {} Ball End Z Velocity = {}".format(Ball.ball_end_z_position, Ball.end_velocity_z)
|
||||
)
|
||||
|
||||
# 10) Check that the ball does not slow due to the force region
|
||||
# Check ball fell down or not
|
||||
if (Ball.ball_end_z_position - CLOSE_ENOUGH_THRESHOLD) < Ball.ball_start_z_position:
|
||||
Ball.ball_fell_down = True
|
||||
|
||||
Report.critical_result(Tests.ball_fell, Ball.ball_fell_down)
|
||||
|
||||
# Ball initial velocity is -5.0. Check that the ball does not slow down in force region
|
||||
if ((Ball.end_velocity_z - Ball.initial_velocity_z) < CLOSE_ENOUGH_THRESHOLD) and (
|
||||
(Ball.start_velocity_z - Ball.initial_velocity_z) < CLOSE_ENOUGH_THRESHOLD
|
||||
):
|
||||
Ball.ball_slows = True
|
||||
|
||||
Report.critical_result(Tests.force_region_slows_ball, Ball.ball_slows)
|
||||
|
||||
# 11) Exits game mode and editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ZeroSimpleDragForceDoesNothing)
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
# Test case ID : C6090555
|
||||
# Test Case Title : Check that force region exerts spline follow force on rigid bodies(negative test)
|
||||
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("Sphere entity found", "Sphere entity not found")
|
||||
find_force_region = ("Force region found", "Force region not found")
|
||||
find_triggers = ("All triggers are found", "All triggers are not found")
|
||||
sphere_fell = ("The sphere fell", "The sphere did not fall")
|
||||
sphere_entered_force_region = ("The sphere entered the force region", "The sphere did not enter the force region before timeout")
|
||||
sphere_exited_force_region = ("The sphere exited the force region", "The sphere did not exit the force region before timeout")
|
||||
sphere_drops_force_region = ("Sphere drops through the force region", "Sphere did not drop through the force region due to spline force")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ZeroSplineForceDoesNothing():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that a PhysX force region exerts spline follow force on rigid bodies(negative test)
|
||||
|
||||
Level Description:
|
||||
A sphere entity is positioned above a force region entity
|
||||
The sphere has a PhysX collider (sphere) and rigidbody component with default values
|
||||
|
||||
The force region entity has a PhysX collider (Box) and force region with a 'spline follow' force.
|
||||
It also has a spline component with 4 nodes. Each node is connected linearly in the following pattern:
|
||||
___[0]
|
||||
[1] ___
|
||||
___ [2]
|
||||
[3]
|
||||
|
||||
There are 4 trigger entities, each with a PhysX collider (sphere). They are positioned at each node.
|
||||
|
||||
Expected Behavior:
|
||||
The Sphere drops through the force region as though spline follow force does not exist.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and Validate entities
|
||||
4) Get position of spline and sphere
|
||||
5) Wait till the sphere drops
|
||||
6) Get z position of sphere when it enters and exits from trigger area
|
||||
7) Verify sphere drops through force region without spline force effect
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 3
|
||||
CLOSE_ENOUGH = 0.001
|
||||
|
||||
class Sphere:
|
||||
id = None
|
||||
start_position_z = None
|
||||
fell = False
|
||||
entered_force_region = False
|
||||
exited_force_region = False
|
||||
|
||||
def on_trigger_enter(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(Sphere.id):
|
||||
Sphere.entered_force_region = True
|
||||
|
||||
def on_trigger_exit(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(Sphere.id):
|
||||
Sphere.exited_force_region = True
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_ZeroSplineForceDoesNothing")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and Validate entities
|
||||
Sphere.id = general.find_game_entity("Sphere")
|
||||
Report.critical_result(Tests.find_sphere, Sphere.id.IsValid())
|
||||
|
||||
force_region_id = general.find_game_entity("Force Region")
|
||||
Report.critical_result(Tests.find_force_region, force_region_id.IsValid())
|
||||
|
||||
all_triggers = ("Trigger0", "Trigger1", "Trigger2", "Trigger3")
|
||||
all_triggers_found = True
|
||||
for trigger in all_triggers:
|
||||
trigger_id = general.find_game_entity(trigger)
|
||||
if not trigger_id.IsValid():
|
||||
all_triggers_found = False
|
||||
Report.critical_result(Tests.find_triggers, all_triggers_found)
|
||||
|
||||
# 4) Get z position of spline and sphere
|
||||
# All triggers are arranged at each node in spline. Getting z position of Trigger3 is same as z position of spline
|
||||
spline_z_position = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldZ", general.find_game_entity("Trigger3")
|
||||
)
|
||||
Report.info("Spline z position is : {}".format(spline_z_position))
|
||||
Sphere.start_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Sphere.id)
|
||||
Report.info("Sphere z position is : {}".format(Sphere.start_position_z))
|
||||
|
||||
# 5) Wait till the sphere drops
|
||||
def sphere_fell():
|
||||
if not Sphere.fell:
|
||||
sphere_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Sphere.id)
|
||||
if sphere_position_z < (Sphere.start_position_z - CLOSE_ENOUGH):
|
||||
Report.info("Sphere position is lower than the starting position now")
|
||||
Sphere.fell = True
|
||||
return Sphere.fell
|
||||
|
||||
helper.wait_for_condition(sphere_fell, TIMEOUT)
|
||||
Report.critical_result(Tests.sphere_fell, Sphere.fell)
|
||||
|
||||
# 6) Get position of sphere when it enters and exits from trigger area
|
||||
# Wait for ball to enter force region
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(force_region_id)
|
||||
handler.add_callback("OnTriggerEnter", on_trigger_enter)
|
||||
|
||||
helper.wait_for_condition(lambda: Sphere.entered_force_region, TIMEOUT)
|
||||
Report.critical_result(Tests.sphere_entered_force_region, Sphere.entered_force_region)
|
||||
sphere_start_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
|
||||
Report.info_vector3(sphere_start_position, "Sphere start position in Force Region")
|
||||
|
||||
# Wait for ball to exit force region
|
||||
handler.add_callback("OnTriggerExit", on_trigger_exit)
|
||||
helper.wait_for_condition(lambda: Sphere.exited_force_region, TIMEOUT)
|
||||
Report.critical_result(Tests.sphere_exited_force_region, Sphere.exited_force_region)
|
||||
sphere_end_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Sphere.id)
|
||||
Report.info_vector3(sphere_end_position, "Sphere end position in Force Region")
|
||||
|
||||
# 7) Verify sphere drops through force region without spline force effect
|
||||
if (
|
||||
((sphere_start_position.x - sphere_end_position.x) < CLOSE_ENOUGH) and
|
||||
((sphere_start_position.y - sphere_end_position.y) < CLOSE_ENOUGH) and
|
||||
(sphere_end_position.z < (spline_z_position - CLOSE_ENOUGH))
|
||||
):
|
||||
Sphere.sphere_drops = True
|
||||
|
||||
Report.critical_result(Tests.sphere_drops_force_region, Sphere.sphere_drops)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ZeroSplineForceDoesNothing)
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
"""
|
||||
Test case ID : C6090550
|
||||
Test Case Title : Check that force region exerts world space force on rigid bodies (negative test)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
ball_exists = ("The Ball entity was found", "The Ball entity was not found")
|
||||
forceregion_exists = ("The ForceRegion entity was found", "The ForceRegion entity was not found")
|
||||
terrain_exists = ("The Terrain entity was found", "The Terrain entity was not found")
|
||||
ball_over_force_region = ("The ball is over the force region", "The ball is not over the force region")
|
||||
gravity_enabled_on_ball = ("The ball has gravity enabled", "The ball gravity failed to enable")
|
||||
ball_entered_force_region = ("The ball entered the force region", "The ball did not enter the force region before timeout")
|
||||
ball_fell_through_region = ("The ball fell through the force region", "The ball did not exit force region below enter location")
|
||||
enter_exit_distance_close = ("The ball has passed through the entire force region", "Ball did not move the height of the force region")
|
||||
ball_hit_ground = ("The ball collided with the PhysX Terrain", "The ball did not collide with the PhysX Terrain before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def ForceRegion_ZeroWorldSpaceForceDoesNothing():
|
||||
"""
|
||||
Summary:
|
||||
A ball is suspended above a cube shaped force region and a PhysX Terrain. The force is a world space force
|
||||
pointed in the positive Z direction with a magnitude of 10.
|
||||
|
||||
Level Description:
|
||||
Ball - (entity): Mesh: Sphere shaped
|
||||
PhysX Rigid Body: Gravity is enabled; Default settings
|
||||
PhysX Collider: Sphere shape; offset (0.0, 0.0, 0.5); draw collider checked
|
||||
|
||||
ForceRegion - (entity): Box Shape: Game view checked; Shape dimensions (3.0, 3.0, 3.0)
|
||||
PhysX Force Region: World Space force; direction (0.0, 0.0, 1.0); magnitude 10.0
|
||||
PhysX Collider: Box shape; Box dimensions (3.0, 3.0, 3.0); Trigger checked;
|
||||
Draw collider checked
|
||||
|
||||
Terrain - (entity): PhysX Terrain: Default settings
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the ball entity will fall due to gravity and enter the force region.
|
||||
The region has a very weak force and therefore the ball will fall through the force region,
|
||||
where it will then collide with the PhysX Terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find/setup entities and handlers
|
||||
4) Check that the ball is over the force region
|
||||
5) Wait for ball to hit the ground
|
||||
6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
|
||||
7) Exit game mode and close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Open 3D Engine Editor command terminal
|
||||
- Any passed and failed tests are written to the Editor.log file.
|
||||
Parsing the file or running a log_monitor are required to observe the test results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from editor_python_test_tools.utils import Report
|
||||
from editor_python_test_tools.utils import TestHelper as helper
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
TIMEOUT_SECONDS = 3.0
|
||||
X_Y_Z_TOLERANCE = 1.5
|
||||
REGION_HEIGHT = 3.0
|
||||
TERRAIN_HEIGHT = 32.0
|
||||
|
||||
def is_close(value1, value2, tolerance):
|
||||
"""
|
||||
() -> bool
|
||||
Absolute value of difference of values being less than or equal to the tolerance
|
||||
"""
|
||||
return abs(value1 - value2) <= tolerance
|
||||
|
||||
def is_at_least(value1, value2, minimum):
|
||||
"""
|
||||
() -> bool
|
||||
Subtraction of value2 from value1 being greater than or equal to the minimum
|
||||
"""
|
||||
return (value1 - value2) >= minimum
|
||||
|
||||
class Entity(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(Tests.__dict__[self.name.lower() + "_exists"], self.id.IsValid())
|
||||
|
||||
def get_location(self):
|
||||
# () -> Vector3
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Ball(Entity):
|
||||
def __init__(self):
|
||||
super(Ball, self).__init__("Ball")
|
||||
self.location = self.get_location()
|
||||
self.enter_region_location = None
|
||||
self.exit_region_location = None
|
||||
self.enable_gravity()
|
||||
self.check_gravity_is_on()
|
||||
|
||||
def enable_gravity(self):
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id)
|
||||
|
||||
def check_gravity_is_on(self):
|
||||
gravity_status = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
Report.critical_result(Tests.__dict__["gravity_enabled_on_" + self.name.lower()], gravity_status)
|
||||
|
||||
def check_alignment(self, region):
|
||||
# () -> bool
|
||||
Report.info_vector3(self.location, "Location of Ball: ")
|
||||
Report.info_vector3(region.location, "Location of ForceRegion: ")
|
||||
|
||||
aligned = True
|
||||
if not is_close(self.location.x, region.location.x, X_Y_Z_TOLERANCE):
|
||||
aligned = False
|
||||
Report.info("The ball is not aligned in the X axis.")
|
||||
if not is_close(self.location.y, region.location.y, X_Y_Z_TOLERANCE):
|
||||
aligned = False
|
||||
Report.info("The ball is not aligned in the Y axis.")
|
||||
if (self.location.z - region.location.z) < X_Y_Z_TOLERANCE:
|
||||
aligned = False
|
||||
Report.info("The ball is not high enough to enter the force region.")
|
||||
if self.location.z < TERRAIN_HEIGHT:
|
||||
aligned = False
|
||||
Report.info("The ball is below the terrain.")
|
||||
return aligned
|
||||
|
||||
def did_ball_fall_in_region_as_expected(self):
|
||||
# () -> bool
|
||||
if not self.exit_region_location:
|
||||
Report.info("The ball did not exit the force region in time.")
|
||||
return False
|
||||
return (self.enter_region_location.z - self.exit_region_location.z) > X_Y_Z_TOLERANCE
|
||||
|
||||
def check_enter_vs_exit_locations(self):
|
||||
"""
|
||||
Check that the ball enter location is above the exit location (ball successfully fell through the force
|
||||
region) and check that the distance between the enter/exit locations is at least the height of the region
|
||||
|
||||
NOTE: The ball will "enter" the force region when the bottom edge of its collision box enters the region and
|
||||
will "exit" when it is completely outside the force region (top edge leaves region)
|
||||
"""
|
||||
Report.info_vector3(self.enter_region_location, "Ball entering location: ")
|
||||
Report.info_vector3(self.exit_region_location, "Ball exiting location: ")
|
||||
|
||||
Report.result(Tests.ball_fell_through_region, self.did_ball_fall_in_region_as_expected())
|
||||
Report.result(
|
||||
Tests.enter_exit_distance_close,
|
||||
is_at_least(self.enter_region_location.z, self.exit_region_location.z, REGION_HEIGHT),
|
||||
)
|
||||
|
||||
class ForceRegion(Entity):
|
||||
def __init__(self, ball):
|
||||
super(ForceRegion, self).__init__("ForceRegion")
|
||||
self.ball = ball # This creates a reference point to access the ball without looking in global scope
|
||||
self.location = self.get_location()
|
||||
self.ball_entered_trigger_area = False
|
||||
self.trigger_handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.connect_trigger_handler()
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball has entered the force region trigger area.")
|
||||
self.ball_entered_trigger_area = True
|
||||
self.ball.enter_region_location = self.ball.get_location()
|
||||
|
||||
def on_trigger_exit(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball has exited the force region trigger area.")
|
||||
self.ball.exit_region_location = self.ball.get_location()
|
||||
|
||||
def connect_trigger_handler(self):
|
||||
self.trigger_handler.connect(self.id)
|
||||
self.trigger_handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
self.trigger_handler.add_callback("OnTriggerExit", self.on_trigger_exit)
|
||||
|
||||
class Terrain(Entity):
|
||||
def __init__(self, ball):
|
||||
super(Terrain, self).__init__("Terrain")
|
||||
self.ball = ball # This creates a reference point to access the ball without looking in global scope
|
||||
self.ball_collided = False
|
||||
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.connect_collision_handler()
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.ball.id):
|
||||
Report.info("Ball struck PhysX Terrain")
|
||||
self.ball_collided = True
|
||||
|
||||
def connect_collision_handler(self):
|
||||
self.collision_handler.connect(self.id)
|
||||
self.collision_handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "ForceRegion_ZeroWorldSpaceForceDoesNothing")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Find/setup entities and handlers
|
||||
ball = Ball()
|
||||
region = ForceRegion(ball)
|
||||
terrain = Terrain(ball)
|
||||
|
||||
# 4) Check that the ball is over the force region
|
||||
Report.critical_result(Tests.ball_over_force_region, ball.check_alignment(region))
|
||||
|
||||
# 5) Wait for ball to hit the ground
|
||||
helper.wait_for_condition(lambda: terrain.ball_collided, TIMEOUT_SECONDS)
|
||||
|
||||
# 6) Report if the ball entered the force region, fell through it, and collided with terrain in the alloted time
|
||||
Report.result(Tests.ball_entered_force_region, region.ball_entered_trigger_area)
|
||||
ball.check_enter_vs_exit_locations()
|
||||
Report.result(Tests.ball_hit_ground, terrain.ball_collided)
|
||||
|
||||
# 7) Exit game mode and close the editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from editor_python_test_tools.utils import Report
|
||||
Report.start_test(ForceRegion_ZeroWorldSpaceForceDoesNothing)
|
||||
Reference in New Issue
Block a user