You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/AutomatedTesting/Gem/PythonTests/physics/C5959760_PhysXForceRegion_P...

231 lines
9.6 KiB
Python

"""
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 C5959760_PhysXForceRegion_PointForceExertion():
# 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
import ImportPathHelper as imports
imports.init()
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", "C5959760_PhysXForceRegion_PointForceExertion")
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__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(C5959760_PhysXForceRegion_PointForceExertion)