Integrating latest 47acbe8
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.components
|
||||
import azlmbr.physics
|
||||
|
||||
class Box:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.distances = []
|
||||
|
||||
def find(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
self.start_position = self.position
|
||||
return self.id.IsValid()
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def is_stationary(self):
|
||||
velocity = azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
return velocity.IsZero()
|
||||
|
||||
def push(self, impulse):
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "ApplyLinearImpulse", self.id, impulse)
|
||||
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C100000
|
||||
# Test Case Title : Check that Gravity works
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/100000
|
||||
|
||||
|
||||
# fmt:off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ball = ("Entity Ball found", "Ball not found")
|
||||
gravity_started_disabled = ("Gravity started disabled", "Gravity didn't start disabled")
|
||||
gravity_set_enabled = ("Gravity has been enabled", "Gravity wasn't enabled")
|
||||
ball_fell = ("Ball fell", "Ball didn't fall")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt:on
|
||||
|
||||
|
||||
def C100000_RigidBody_EnablingGravityWorksPoC():
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "EnablingGravityWorks")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
general.idle_wait_frames(1)
|
||||
ball_id = general.find_game_entity("Ball")
|
||||
Report.critical_result(Tests.find_ball, ball_id.IsValid(), "Entity must be found")
|
||||
|
||||
# 4) Make sure gravity is off from the start
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ball_id)
|
||||
Report.critical_result(Tests.gravity_started_disabled, not gravity_enabled)
|
||||
|
||||
# 5) Get the Z position before enabling the physics
|
||||
class Ball:
|
||||
z_start = None
|
||||
|
||||
Ball.z_start = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
|
||||
# 6) Activate gravity
|
||||
Report.info("Enabling Gravity")
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "ForceAwake", ball_id)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", ball_id, True)
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ball_id)
|
||||
|
||||
def ball_fell():
|
||||
"""
|
||||
This is an example function to use with TestHelper.wait_for_condition
|
||||
It may take no parameters and it contains no wait_idle_* because that is
|
||||
already handled in TestHelper.wait_for_condition
|
||||
"""
|
||||
z_end = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", ball_id)
|
||||
return Ball.z_start > z_end
|
||||
|
||||
# 7) Validate ball fell by ensuring z is decreasing
|
||||
fell_down = helper.wait_for_condition(ball_fell, 1.0)
|
||||
Report.result(Tests.ball_fell, fell_down)
|
||||
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C100000_RigidBody_EnablingGravityWorksPoC)
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C111111
|
||||
# Test Case Title : Check that Gravity works
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/111111
|
||||
|
||||
# fmt:off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ball = ("Entity Ball found", "Ball not found")
|
||||
find_terrain = ("Entity Terrain found", "Terrain not found")
|
||||
gravity_started_disabled = ("Gravity started disabled", "Gravity didn't start disabled")
|
||||
gravity_set_enabled = ("Gravity has been enabled", "Gravity wasn't enabled")
|
||||
ball_fell = ("Ball fell", "Ball didn't fall")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt:on
|
||||
|
||||
|
||||
def C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC():
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "EnablingGravityWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
ball_id = general.find_game_entity("Ball")
|
||||
Report.result(Tests.find_ball, ball_id.IsValid())
|
||||
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.result(Tests.find_terrain, terrain_id.IsValid())
|
||||
|
||||
# 4) Make sure gravity is off from the start
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ball_id)
|
||||
Report.result(Tests.gravity_started_disabled, not gravity_enabled)
|
||||
|
||||
# 5) Activate gravity
|
||||
Report.info("Enabling Gravity")
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "ForceAwake", ball_id)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", ball_id, True)
|
||||
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ball_id)
|
||||
Report.result(Tests.gravity_set_enabled, gravity_enabled)
|
||||
|
||||
# 6) Listen to collision events seconds so it falls down
|
||||
|
||||
class TouchGround:
|
||||
value = False
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(terrain_id):
|
||||
Report.info("Touched ground")
|
||||
TouchGround.value = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(ball_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
helper.wait_for_condition(lambda: TouchGround.value, 3.0)
|
||||
Report.result(Tests.ball_fell, TouchGround.value)
|
||||
|
||||
# 7) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C111111_RigidBody_EnablingGravityWorksUsingNotificationsPoC)
|
||||
@@ -0,0 +1,213 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test Case ID : C12712452
|
||||
# Test Case Title : Verify ScriptCanvas Collision Events
|
||||
# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/12712452
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
terrain_found_valid = ("PhysX Terrain found and validated", "PhysX Terrain not found and validated")
|
||||
sphere_found_valid = ("Sphere found and validated", "Sphere not found and validated")
|
||||
begin_signal_found_valid = ("Begin Signal found and validated", "Begin Signal not found and validated")
|
||||
persist_signal_found_valid = ("Persist Signal found and validated", "Persist Signal not found and validated")
|
||||
end_signal_found_valid = ("End Signal found and validated", "End Signal not found and validated")
|
||||
sphere_above_terrain = ("Sphere is above terrain", "Sphere is not above terrain")
|
||||
sphere_gravity_enabled = ("Gravity is enabled on Sphere", "Gravity is not enabled on Sphere")
|
||||
sphere_started_bouncing = ("Sphere started bouncing", "Sphere did not start bouncing")
|
||||
sphere_stopped_bouncing = ("Sphere stopped bouncing", "Sphere did not stop bouncing")
|
||||
event_records_match = ("Event records match", "Event records do not match")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C12712452_ScriptCanvas_CollisionEvents():
|
||||
"""
|
||||
Summary:
|
||||
This script runs an automated test to verify that the Script Canvas nodes "On Collision Begin", "On Collision
|
||||
Persist", and "On Collision End" will function as intended for the entity to which their Script Canvas is attached.
|
||||
|
||||
Level Description:
|
||||
A sphere (entity: Sphere) is above a terrain (entity: PhysX Terrain). The sphere has a PhysX Rigid Body, a PhysX
|
||||
Collider with shape Sphere, and gravity enabled. The sphere has a Script Canvas attached to it which will toggle the
|
||||
activation of three signal entities (entity: Begin Signal), (entity: Persist Signal), and (entity: End Signal).
|
||||
Begin Signal's activation will toggle (switch from activated to deactivated or vice versa) when a collision begins
|
||||
with the sphere, Persist Signal's activation will toggle when a collision persists with the sphere, and End Signal's
|
||||
activation will toggle when a collision ends with the sphere.
|
||||
|
||||
Expected behavior:
|
||||
The sphere will fall toward and collide with the terrain. The sphere will bounce until it comes to rest. The Script
|
||||
Canvas will cause the signal entities to activate and deactivate in the same pattern as the collision events which
|
||||
occur on the sphere.
|
||||
|
||||
Test Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Retrieve and validate entities
|
||||
3) Check that the sphere is above the terrain
|
||||
4) Check that gravity is enabled on the sphere
|
||||
5) Wait for the initial collision between the sphere and the terrain or timeout
|
||||
6) Wait for the sphere to stop bouncing
|
||||
7 Check that the event records match
|
||||
8) Exit game mode and close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.components
|
||||
import azlmbr.entity
|
||||
import azlmbr.physics
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
# Constants
|
||||
TIME_OUT_SECONDS = 3.0
|
||||
TERRAIN_START_Z = 32.0
|
||||
SPHERE_RADIUS = 1.0
|
||||
|
||||
class Entity:
|
||||
def __init__(self, name, found_valid_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.found_valid_test = found_valid_test
|
||||
|
||||
class Sphere(Entity):
|
||||
def __init__(self, name, found_valid_test, event_records_match_test):
|
||||
Entity.__init__(self, name, found_valid_test)
|
||||
self.event_records_match_test = event_records_match_test
|
||||
self.collided = False
|
||||
self.stopped_bouncing = False
|
||||
self.collision_event_record = []
|
||||
self.script_canvas_event_record = []
|
||||
|
||||
# Set up collision notification handler
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
self.handler.add_callback("OnCollisionPersist", self.on_collision_persist)
|
||||
self.handler.add_callback("OnCollisionEnd", self.on_collision_end)
|
||||
|
||||
def get_z_position(self):
|
||||
z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", self.id)
|
||||
Report.info("{}'s z-position: {}".format(self.name, z_position))
|
||||
return z_position
|
||||
|
||||
def is_gravity_enabled(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
|
||||
# Set up reporting of the event records and whether they match
|
||||
def match_event_records(self):
|
||||
Report.info("{} collision event record: {}".format(self.name, self.collision_event_record))
|
||||
Report.info("Script Canvas event record: {}".format(self.script_canvas_event_record))
|
||||
return self.collision_event_record == self.script_canvas_event_record
|
||||
|
||||
# Set up collision event detection and update collision event record
|
||||
def on_collision(self, event, other_id):
|
||||
if not self.collided:
|
||||
self.collided = True
|
||||
other_name = azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", other_id)
|
||||
Report.info("{} collision {}s with {}".format(self.name, event, other_name))
|
||||
self.collision_event_record.append(event)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
self.on_collision("begin", args[0])
|
||||
|
||||
def on_collision_persist(self, args):
|
||||
self.on_collision("persist", args[0])
|
||||
|
||||
def on_collision_end(self, args):
|
||||
self.on_collision("end", args[0])
|
||||
|
||||
# Set up detection of the sphere coming to rest
|
||||
def bouncing_stopped(self):
|
||||
if not azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsAwake", self.id):
|
||||
self.stopped_bouncing = True
|
||||
return self.stopped_bouncing
|
||||
|
||||
class SignalEntity(Entity):
|
||||
def __init__(self, name, found_valid_test, monitored_entity, event):
|
||||
Entity.__init__(self, name, found_valid_test)
|
||||
self.monitored_entity = monitored_entity
|
||||
self.event = event
|
||||
|
||||
# Set up activation and deactivation notification handler
|
||||
self.handler = azlmbr.entity.EntityBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnEntityActivated", self.on_entity_activated)
|
||||
self.handler.add_callback("OnEntityDeactivated", self.on_entity_deactivated)
|
||||
|
||||
# Set up activation and deactivation detection and update Script Canvas event record
|
||||
def on_entity_activated(self, args):
|
||||
self.monitored_entity.script_canvas_event_record.append(self.event)
|
||||
|
||||
def on_entity_deactivated(self, args):
|
||||
self.monitored_entity.script_canvas_event_record.append(self.event)
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C12712452_ScriptCanvas_CollisionEvents")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve and validate entities
|
||||
terrain = Entity("PhysX Terrain", Tests.terrain_found_valid)
|
||||
sphere = Sphere("Sphere", Tests.sphere_found_valid, Tests.event_records_match)
|
||||
begin_signal = SignalEntity("Begin Signal", Tests.begin_signal_found_valid, sphere, "begin")
|
||||
persist_signal = SignalEntity("Persist Signal", Tests.persist_signal_found_valid, sphere, "persist")
|
||||
end_signal = SignalEntity("End Signal", Tests.end_signal_found_valid, sphere, "end")
|
||||
|
||||
entities = [terrain, sphere, begin_signal, persist_signal, end_signal]
|
||||
for entity in entities:
|
||||
Report.critical_result(entity.found_valid_test, entity.id.IsValid())
|
||||
|
||||
# 3) Check that the sphere is above the terrain
|
||||
Report.critical_result(Tests.sphere_above_terrain, sphere.get_z_position() - SPHERE_RADIUS > TERRAIN_START_Z)
|
||||
|
||||
# 4) Check that gravity is enabled on the sphere
|
||||
Report.critical_result(Tests.sphere_gravity_enabled, sphere.is_gravity_enabled())
|
||||
|
||||
# 5) Wait for the initial collision between the sphere and the terrain or timeout
|
||||
helper.wait_for_condition(lambda: sphere.collided, TIME_OUT_SECONDS)
|
||||
Report.critical_result(Tests.sphere_started_bouncing, sphere.collided)
|
||||
|
||||
# 6) Wait for the sphere to stop bouncing
|
||||
helper.wait_for_condition(sphere.bouncing_stopped, TIME_OUT_SECONDS)
|
||||
Report.result(Tests.sphere_stopped_bouncing, sphere.stopped_bouncing)
|
||||
|
||||
# 7 Check that the event records match
|
||||
Report.result(Tests.event_records_match, sphere.match_event_records())
|
||||
|
||||
# 8) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C12712452_ScriptCanvas_CollisionEvents)
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C12712453
|
||||
# Test Case Title : Verify Raycast Multiple Node
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12712453
|
||||
|
||||
# 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")
|
||||
test_completed = ("The test successfully completed", "The test timed out")
|
||||
# entities found
|
||||
caster_found = ("Caster entity found", "Caster entity NOT found")
|
||||
multi_target_close_found = ("Multicast close target entity found", "Multicast close target NOT found")
|
||||
multi_target_far_found = ("Multicast far target entity found", "Multicast far target NOT found")
|
||||
single_target_found = ("Single close target entity found", "Single close target NOT found")
|
||||
fail_entities_found = ("Found all fail entities", "Failed to find at least one fail entities")
|
||||
# entity results
|
||||
caster_result = ("Caster was activated", "Caster was NOT activated")
|
||||
multi_target_close_result = ("Multicast close target was hit with a ray", "Multicast close target WAS NOT hit with a ray")
|
||||
multi_target_far_result = ("Multicast far target was hit with a ray", "Multicast far target WAS NOT hit with a ray")
|
||||
single_target_result = ("Single target was hit with a ray", "Single target WAS NOT hit with a ray")
|
||||
|
||||
# fmt:on
|
||||
|
||||
|
||||
# Lines to search for by the log monitor
|
||||
class Lines:
|
||||
# FAILURE (in all caps) is used as the failure flag for both this python script and ScriptCanvas.
|
||||
# If this is present in the log, something has gone fatally wrong and the test will fail.
|
||||
# Additional details as to why the test fails will be printed in the log accompanying this entry.
|
||||
unexpected = ["FAILURE"]
|
||||
|
||||
|
||||
def C12712453_ScriptCanvas_MultipleRaycastNode():
|
||||
"""
|
||||
Summary:
|
||||
Uses script canvas to cast two types of rays in game mode (multiple raycast and single raycast). Script canvas
|
||||
validates the results from the raycasts, then deactivates any entities hit. This python script ensures only
|
||||
the expected entities were deactivated.
|
||||
|
||||
Level Description:
|
||||
Caster - A sphere with gravity disabled and a scriptcanvas component for emitting raycasts. Caster starts inactive.
|
||||
Targets - Three spheres with gravity disabled. They are the expected targets of the raycasts.
|
||||
Fail Boxes - These boxes are positioned in various places where no ray should collide with them. They are set
|
||||
up print a fail message if any ray should hit them.
|
||||
ScriptCanvas - The ScriptCanvas script is attached to the Caster entity, and is set to start on entity Activation.
|
||||
The script will activate two different raycast nodes, a single raycast and a multiple raycast. For every raycast
|
||||
hit, the script validates all properties of a raycast (while printing to log) and then deactivates any entity
|
||||
that is [raycast] hit. This ScriptCanvas file can be found in this test's level folder named
|
||||
"Raycast.scrpitcanvas".
|
||||
|
||||
Expected Behavior:
|
||||
The level should load and appear to instantly close. After setup the Caster sphere should emit the raycasts which
|
||||
should deactivate all expected Targets (via script canvas). The python script waits for the Targets to be
|
||||
deactivated then prints the results.
|
||||
|
||||
Steps:
|
||||
1) Load level / enter game mode
|
||||
2) Retrieve entities
|
||||
3) Start the test (activate the caster entity)
|
||||
4) Wait for Target entities to deactivate
|
||||
5) Exit game mode and close the editor
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 1.0
|
||||
FAIL_ENTITIES = 5
|
||||
|
||||
# Entity base class handles very general entity initialization
|
||||
class EntityBase:
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
|
||||
# Stores entity "is active" state. Most entities start as "Active"
|
||||
self.activated = True
|
||||
|
||||
# Caster starts deactivated. When the caster is activated the script canvas test will begin.
|
||||
class Caster(EntityBase):
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
found_tuple = Tests.__dict__[name.lower() + "_found"]
|
||||
Report.critical_result(found_tuple, self.id.isValid())
|
||||
|
||||
# Caster starts as "Inactive" because the script canvas starts when Caster is Activated
|
||||
self.activated = False
|
||||
|
||||
# Activates the Caster which starts it's ScriptCanvas script (and the test)
|
||||
def activate(self):
|
||||
# type: () -> None
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.id)
|
||||
self.activated = True
|
||||
Report.success(Tests.__dict__[self.name + "_result"])
|
||||
|
||||
# Targets start activated and only get deactivated by the ScriptCanvas script.
|
||||
# Successful execution means every Target gets deactivated via ScriptCanvas
|
||||
class Target(EntityBase):
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
found_tuple = Tests.__dict__[name.lower() + "_found"]
|
||||
Report.critical_result(found_tuple, self.id.isValid())
|
||||
|
||||
self.handler = azlmbr.entity.EntityBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnEntityDeactivated", self.on_deactivation)
|
||||
|
||||
# Callback: gets called when entity is deactivated. This is expected for Target entities.
|
||||
def on_deactivation(self, args):
|
||||
# type: ([EntityId, ...]) -> None
|
||||
if args[0].Equal(self.id):
|
||||
Report.success(Tests.__dict__[self.name + "_result"])
|
||||
self.activated = False
|
||||
|
||||
# Disconnects event handler.
|
||||
# (Needed so the deactivation callback doesn't not get called on level clean up)
|
||||
def disconnect(self):
|
||||
# type: () -> None
|
||||
self.handler.disconnect()
|
||||
self.handler = None
|
||||
|
||||
# Failure entities start activated and get deactivated via the ScriptCanvas. Ideally none will be deactivated
|
||||
# during the test. If one is deactivated, and Failure message is logged that will be caught by the log monitor.
|
||||
class Failure(EntityBase):
|
||||
def __init__(self, num):
|
||||
# type: (int) -> None
|
||||
EntityBase.__init__(self, "fail_" + str(num))
|
||||
if not self.id.IsValid():
|
||||
Report.info("FAILURE: {} could not be found".format(self.name))
|
||||
self.handler = azlmbr.entity.EntityBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnEntityDeactivated", self.on_deactivation)
|
||||
|
||||
# Callback: gets called when entity is deactivated. This is expected for Target entities.
|
||||
def on_deactivation(self, args):
|
||||
# type: ([EntityId, ...]) -> None
|
||||
if args[0].Equal(self.id):
|
||||
Report.info("{}: FAILURE".format(self.name))
|
||||
self.activated = False
|
||||
|
||||
# Disconnects event handler.
|
||||
# (Needed so the deactivation callback doesn't not get called on level clean up)
|
||||
def disconnect(self):
|
||||
# type: () -> None
|
||||
self.handler.disconnect()
|
||||
self.handler = None
|
||||
|
||||
# 1) Open level
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C12712453_ScriptCanvas_MultipleRaycastNode")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve entities
|
||||
caster = Caster("caster")
|
||||
targets = [Target("multi_target_close"), Target("multi_target_far"), Target("single_target")]
|
||||
fails = [Failure(i) for i in range(FAIL_ENTITIES)]
|
||||
Report.critical_result(Tests.fail_entities_found, all(entity.id.isValid() for entity in fails))
|
||||
|
||||
# 3) Start the test
|
||||
caster.activate()
|
||||
|
||||
# 4) Wait for Target entities to deactivate
|
||||
test_completed = helper.wait_for_condition(lambda: all(not target.activated for target in targets), TIME_OUT)
|
||||
Report.result(Tests.test_completed, test_completed)
|
||||
|
||||
# Disconnect all "on deactivated" event handlers so they don't get called implicitly on level cleanup
|
||||
for entity in targets + fails:
|
||||
entity.disconnect()
|
||||
|
||||
# 5) Close test
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
# Disabled until Script Canvas merges the new backend
|
||||
#Report.start_test(C12712453_ScriptCanvas_MultipleRaycastNode)
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : 12712454
|
||||
# Test Case Title : Verify overlap nodes in script canvas
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12712454
|
||||
|
||||
# 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")
|
||||
# comm entities are used for communication between script canvas and the python script.
|
||||
comm_entities_found = ("All comm entities found", "All comm entities not found")
|
||||
script_canvas_entity_found = ("Script_Canvas_Entity Found", "Script_Canvas_Entity not found")
|
||||
test_array_found = ("8x8 entity array found", "8x8 entity array not found")
|
||||
all_tests_passed = ("All tests have expected results", "Some tests had unexpected results")
|
||||
|
||||
# Test 0
|
||||
test_0_finished = ("Test 0 has ended", "Test 0 has not ended correctly")
|
||||
test_0_results_logged = ("Test 0 logged expected results", "Test 0 logged unexpected results")
|
||||
test_0_entity_reset = ("Test 0: entities moved back", "Test 0: not all entities moved back")
|
||||
|
||||
# Test 1
|
||||
test_1_finished = ("Test 1 has ended", "Test 1 has not ended correctly")
|
||||
test_1_results_logged = ("Test 1 logged expected results", "Test 1 logged unexpected results")
|
||||
test_1_entity_reset = ("Test 1: entities moved back", "Test 1: not all entities moved back")
|
||||
|
||||
# Test 2
|
||||
test_2_finished = ("Test 2 has ended", "Test 2 has not ended correctly")
|
||||
test_2_results_logged = ("Test 2 logged expected results", "Test 2 logged unexpected results")
|
||||
test_2_entity_reset = ("Test 2: entities moved back", "Test 2: not all entities moved back")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C12712454_ScriptCanvas_OverlapNodeVerification():
|
||||
"""
|
||||
Summary: Verifies that the three script canvas overlap nodes (box, sphere, capsule) work as expected. Test
|
||||
script starts and verifies each case one by one.
|
||||
|
||||
Level Description:
|
||||
Test Array - 8x8 array of sphere entities lined up edge to edge along an y-z plane, they are names with
|
||||
'Sphere_row_column' convention; has PhysX rigid body, sphere shaped PhysX collider, sphere shape.
|
||||
Comm From Test- 3 communication entities that start inactive, used by the test script to initiate each of the three cases
|
||||
by activation of the relevent sphere; has sphere shape.
|
||||
Comm To Test- 3 communication entities that start inactive, used by the script canvas to initiate when each of the three
|
||||
cases are completed by activation of the relevent sphere; has sphere shape.
|
||||
Script Canvas Entity - Entity is centered in the test array and oriented with a 90 degree angle along the y
|
||||
axis as to allow the capsule overlap node to overlap more entities; has script canvas component
|
||||
|
||||
Script Canvas - Has three execution cases, one for each overlap node type. Each case goes through a similar path
|
||||
- Waits for relevant Comm From Test entity to be activated by the test script
|
||||
- Creates an array of all entities in Test Array that overlap
|
||||
- Uses this array to draw a blue sphere at every sphere that overlaps
|
||||
- Uses the same array to move each overlapping entity 5 m along the +x axis
|
||||
- Activates the relevant Comm To Test entity
|
||||
The test script then logs the results and resets the Test Array before activating the next path.
|
||||
Values for the overlaps:
|
||||
Box: x = 4.5, y = 4.5, z = 4.5
|
||||
Sphere: r = 4
|
||||
Capsule: r = 0.5, h = 5
|
||||
Note: For this script canvas to run properly it requires the custom .physxconfiguration file.
|
||||
|
||||
Tests Runs - Each loop runs a different overlap node
|
||||
- test_0: Box
|
||||
- test_1: Sphere
|
||||
- test_2: Capsule
|
||||
|
||||
Expected Behavior: The script canvas will run it's three different overlap nodes. Each run the overlapped
|
||||
spheres in the Test Array will be drawn in over in blue and then offset in the x direction so that this test
|
||||
script can confirm that it worked correctly.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create Entity Objects
|
||||
4) Validate Entities
|
||||
5) Begin Testing
|
||||
6) Signal Script Canvas to begin test
|
||||
7) Wait until script canvas signals that it has completed the test
|
||||
8) Check which entities in array have been moved
|
||||
9) Validate and log results of test
|
||||
10) Place all entities back into correct positions in entity array
|
||||
11) Log results of all tests
|
||||
12) Exit Game Mode
|
||||
13) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as math
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = 0.0001
|
||||
# Timeout for a failed test in seconds
|
||||
TIMEOUT = 1.0
|
||||
ARRAY_X = 500.0
|
||||
ARRAY_COLUMN_0 = 530.0
|
||||
ARRAY_ROW_0 = 45.0
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name, activated):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.activated = activated
|
||||
self.handler = None
|
||||
|
||||
def activate_entity(self):
|
||||
Report.info("Activating Entity : " + self.name)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.id)
|
||||
self.activated = True
|
||||
|
||||
def entity_activated(self, args):
|
||||
self.activated = True
|
||||
|
||||
def set_handler(self):
|
||||
self.handler = azlmbr.entity.EntityBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnEntityActivated", self.entity_activated)
|
||||
|
||||
class Sphere_Entities:
|
||||
def __init__(self, row, column):
|
||||
self.name = "Sphere_{}_{}".format(row, column)
|
||||
self.id = general.find_game_entity(self.name)
|
||||
self.y = column + ARRAY_COLUMN_0
|
||||
self.z = ARRAY_ROW_0 - row
|
||||
self.array_position = math.Vector3(ARRAY_X, self.y, self.z)
|
||||
self.current_position = self.array_position
|
||||
|
||||
def check_id(self):
|
||||
return self.id.isValid()
|
||||
|
||||
def move_entity_back(self):
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetWorldTranslation", self.id, self.array_position)
|
||||
|
||||
def in_position(self):
|
||||
current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
return abs(current_position.x - self.array_position.x) < FLOAT_THRESHOLD
|
||||
|
||||
class Script_Canvas_Test:
|
||||
def __init__(self, index):
|
||||
self.index = index
|
||||
self.name = "test_{}".format(index)
|
||||
self.got_expected_result = False
|
||||
self.test_finish = None
|
||||
self.results_logged = None
|
||||
self.array_reset = None
|
||||
|
||||
def report_test_start(self):
|
||||
Report.info("Test {} has begun".format(self.index))
|
||||
|
||||
def report_test_finished(self, finished):
|
||||
self.test_finish = Tests.__dict__["test_{}_finished".format(self.index)]
|
||||
Report.result(self.test_finish, finished)
|
||||
|
||||
def report_results_logged(self):
|
||||
self.results_logged = Tests.__dict__["test_{}_results_logged".format(self.index)]
|
||||
Report.result(self.results_logged, self.got_expected_result)
|
||||
|
||||
def report_test_array_reset(self, entities_in_position):
|
||||
self.array_reset = Tests.__dict__["test_{}_entity_reset".format(self.index)]
|
||||
Report.result(self.array_reset, entities_in_position)
|
||||
|
||||
# fmt: off
|
||||
class Result_Arrays:
|
||||
true_bool_array = [[True for column in range(8)] for row in range(8)]
|
||||
# Comparison Array for box overlap node
|
||||
test_0_bool_array = [
|
||||
[True, True, True, True, True, True, True, True],
|
||||
[True, False, False, False, False, False, False, True],
|
||||
[True, True, False, False, False, False, False, True],
|
||||
[True, False, False, False, False, False, False, True],
|
||||
[True, True, False, False, False, False, False, True],
|
||||
[True, True, False, False, False, False, False, True],
|
||||
[True, True, False, False, False, False, False, True],
|
||||
[True, True, True, True, True, True, True, True],
|
||||
]
|
||||
|
||||
# Comparison Array for sphere overlap node
|
||||
test_1_bool_array = [
|
||||
[True, True, True, True, True, True, True, True],
|
||||
[True, True, False, False, False, False, True, True],
|
||||
[True, False, False, False, False, False, False, True],
|
||||
[True, False, False, False, False, False, False, True],
|
||||
[True, False, False, False, False, False, False, True],
|
||||
[True, False, False, False, False, False, False, True],
|
||||
[True, True, False, False, False, False, True, True],
|
||||
[True, True, True, True, True, True, True, True],
|
||||
]
|
||||
|
||||
# Comparison Array for capsule overlap node
|
||||
test_2_bool_array = [
|
||||
[True, True, True, True, True, True, True, True],
|
||||
[True, True, True, False, False, True, True, True],
|
||||
[True, True, True, False, False, True, True, True],
|
||||
[True, True, True, False, False, True, True, True],
|
||||
[True, True, True, False, False, True, True, True],
|
||||
[True, True, True, False, False, True, True, True],
|
||||
[True, True, True, False, False, True, True, True],
|
||||
[True, True, True, True, True, True, True, True],
|
||||
]
|
||||
# fmt on
|
||||
|
||||
def create_test_Sphere_Entities():
|
||||
return [[Sphere_Entities(row, column) for column in range(8)] for row in range(8)]
|
||||
|
||||
def sphere_array_isvalid(array):
|
||||
for row in range(len(array)):
|
||||
for column in range(len(array[0])):
|
||||
if not array[row][column].id.isValid():
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_comm_entities(comm_list):
|
||||
for entity in comm_list:
|
||||
if not entity.id.isValid():
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_array_movement(array):
|
||||
return [[True if entity.in_position() else False for entity in row] for row in array]
|
||||
|
||||
def reset_array(array):
|
||||
for row in array:
|
||||
for entity in row:
|
||||
entity.move_entity_back()
|
||||
|
||||
def is_array_in_position(array):
|
||||
return arrays_are_the_same(Result_Arrays.true_bool_array, check_array_movement(array))
|
||||
|
||||
def arrays_are_the_same(array_0, array_1):
|
||||
differences = [row for row in array_0 if row not in array_1] + [row for row in array_1 if row not in array_0]
|
||||
return differences == []
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C12712454_ScriptCanvas_OverlapNodeVerification")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create Entity and Script_Canvas_Test Objects
|
||||
test_Sphere_Entities = create_test_Sphere_Entities()
|
||||
script_canvas_entity = Entity("Script_Canvas_Entity", True)
|
||||
comm_from_test_0 = Entity("Comm_From_Test_0", False)
|
||||
comm_from_test_1 = Entity("Comm_From_Test_1", False)
|
||||
comm_from_test_2 = Entity("Comm_From_Test_2", False)
|
||||
comm_to_test_0 = Entity("Comm_To_Test_0", False)
|
||||
comm_to_test_1 = Entity("Comm_To_Test_1", False)
|
||||
comm_to_test_2 = Entity("Comm_To_Test_2", False)
|
||||
comm_from_entity_list = (comm_from_test_0, comm_from_test_1, comm_from_test_2)
|
||||
comm_to_entity_list = (comm_to_test_0, comm_to_test_1, comm_to_test_2)
|
||||
|
||||
test_0 = Script_Canvas_Test(0)
|
||||
test_1 = Script_Canvas_Test(1)
|
||||
test_2 = Script_Canvas_Test(2)
|
||||
test_list = (test_0, test_1, test_2)
|
||||
|
||||
# 4) Validate Entities
|
||||
Report.critical_result(Tests.script_canvas_entity_found, script_canvas_entity.id.isValid())
|
||||
Report.critical_result(
|
||||
Tests.comm_entities_found, check_comm_entities(comm_from_entity_list + comm_to_entity_list)
|
||||
)
|
||||
Report.critical_result(Tests.test_array_found, sphere_array_isvalid(test_Sphere_Entities))
|
||||
|
||||
# 5) Begin Testing
|
||||
for test in test_list:
|
||||
# 6) Signal Script Canvas to begin test
|
||||
comm_from_entity_list[test.index].activate_entity()
|
||||
test.report_test_start()
|
||||
|
||||
# 7) Wait until script canvas signals that it has completed the test
|
||||
comm_to_entity_list[test.index].set_handler()
|
||||
test.report_test_finished(helper.wait_for_condition(lambda: comm_to_entity_list[test.index].activated, TIMEOUT))
|
||||
|
||||
# 8) Check which entities in array have been moved
|
||||
result_bool_array = check_array_movement(test_Sphere_Entities)
|
||||
|
||||
# 9) Validate and log results of test
|
||||
test.got_expected_result = arrays_are_the_same(
|
||||
result_bool_array, Result_Arrays.__dict__["test_{}_bool_array".format(test.index)]
|
||||
)
|
||||
test.report_results_logged()
|
||||
|
||||
if test.index == 0:
|
||||
Report.info(result_bool_array)
|
||||
|
||||
# 10) Place all entities back into correct positions in entity array
|
||||
reset_array(test_Sphere_Entities)
|
||||
test.report_test_array_reset(is_array_in_position(test_Sphere_Entities))
|
||||
|
||||
# 11) Log results of all tests
|
||||
Report.result(
|
||||
Tests.all_tests_passed, test_0.got_expected_result and test_1.got_expected_result and test_2.got_expected_result
|
||||
)
|
||||
|
||||
# 12) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C12712454_ScriptCanvas_OverlapNodeVerification)
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C12712455
|
||||
# Test Case Title : Verify shape cast nodes in SC
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12712455
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
Ball_0_found = ("Ball_0 entity is found", "Ball_0 entity is not found")
|
||||
Ball_1_found = ("Ball_1 entity is found", "Ball_1 entity is not found")
|
||||
Notification_Entity_0_found = ("Notification_Entity_0 entity found", "Notification_Entity_0 entity invalid")
|
||||
Notification_Entity_1_found = ("Notification_Entity_1 entity found", "Notification_Entity_1 entity invalid")
|
||||
Notification_Entity_2_found = ("Notification_Entity_2 entity found", "Notification_Entity_2 entity invalid")
|
||||
Ball_0_gravity = ("Ball_0 gravity is disabled", "Ball_0 gravity is enabled")
|
||||
Ball_1_gravity = ("Ball_1 gravity is disabled", "Ball_1 gravity is enabled")
|
||||
Notification_Entity_0_gravity = ("Notification_Entity_0 gravity disabled", "Notification_Entity_0 gravity enabled")
|
||||
Notification_Entity_1_gravity = ("Notification_Entity_1 gravity disabled", "Notification_Entity_1 gravity enabled")
|
||||
Notification_Entity_2_gravity = ("Notification_Entity_2 gravity disabled", "Notification_Entity_2 gravity enabled")
|
||||
script_canvas_translation_node = ("Script canvas through translation node", "Script canvas failed translation node")
|
||||
script_canvas_sphere_cast_node = ("Script canvas through sphere cast node", "Script canvas failed sphere cast node")
|
||||
script_canvas_draw_sphere_node = ("Script canvas through draw sphere node", "Script canvas failed draw sphere node")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C12712455_ScriptCanvas_ShapeCastVerification():
|
||||
"""
|
||||
Summary: Verifies that shape cast nodes in script editor function properly.
|
||||
|
||||
Level Description:
|
||||
Ball_0 - Starts stationary with gravity disabled on -y axis from Ball_1; has Sphere shape collider, Rigid Body,
|
||||
Sphere Shape, Script Canvas
|
||||
Ball_1 - Starts stationary with gravity disabled on +y axis from Ball_0; has Sphere shape collider, Rigid Body,
|
||||
Sphere Shape
|
||||
Notification_Entity_0 - Starts stationary with gravity disabled; has rigid body, Sphere shape
|
||||
Notification_Entity_1 - Starts stationary with gravity disabled; has rigid body, Sphere shape
|
||||
Notification_Entity_2 - Starts stationary with gravity disabled; has rigid body, Sphere shape
|
||||
Script Canvas - Creates a sphere cast from Ball_0 to Ball_1 and draws the sphere in. As the script progresses
|
||||
through the translation, sphere cast, and sphere draw nodes it enables gravity on the spheres in order to
|
||||
show that each node has been passed.
|
||||
|
||||
Expected Behavior: A sphere will be drawn on ball_1 in from ball_0. The three notification_entities will fall as
|
||||
gravity is enabled on them in order.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create entity objects
|
||||
4) Verify entities and check gravity
|
||||
5) Wait for conditions or timeout
|
||||
6) Log results
|
||||
7) Exit Game Mode
|
||||
8) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
# Constants
|
||||
GRAVITY_WAIT_TIMEOUT = 1
|
||||
|
||||
def __init__(self, name, expected_initial_velocity=None, expected_final_velocity=None):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.gravity_was_enabled = False
|
||||
self.handler = None
|
||||
# 4) Verify entities and check gravity
|
||||
try:
|
||||
self.found_test = Tests.__dict__[self.name + "_found"]
|
||||
self.gravity_disabled_test = Tests.__dict__[self.name + "_gravity"]
|
||||
except Exception as e:
|
||||
Report.info("Can not find specified tuples in Tests class")
|
||||
Report.info(e)
|
||||
raise ValueError
|
||||
Report.critical_result(self.found_test, self.id.isValid())
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
Report.critical_result(self.gravity_disabled_test, not gravity_enabled)
|
||||
|
||||
def check_gravity_enabled(self):
|
||||
self.gravity_was_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
return self.gravity_was_enabled
|
||||
|
||||
def wait_for_gravity_enabled(self):
|
||||
helper.wait_for_condition(self.check_gravity_enabled, Entity.GRAVITY_WAIT_TIMEOUT)
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C12712455_ScriptCanvas_ShapeCastVerification")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create entity objects
|
||||
ball_0 = Entity("Ball_0")
|
||||
ball_1 = Entity("Ball_1")
|
||||
notification_entity_0 = Entity("Notification_Entity_0")
|
||||
notification_entity_1 = Entity("Notification_Entity_1")
|
||||
notification_entity_2 = Entity("Notification_Entity_2")
|
||||
|
||||
# 5) Wait for conditions or timeout
|
||||
notification_list = [notification_entity_0, notification_entity_1, notification_entity_2]
|
||||
for entity in notification_list:
|
||||
entity.wait_for_gravity_enabled()
|
||||
|
||||
# 6) Log results
|
||||
Report.result(Tests.script_canvas_translation_node, notification_entity_0.gravity_was_enabled)
|
||||
Report.result(Tests.script_canvas_sphere_cast_node, notification_entity_1.gravity_was_enabled)
|
||||
Report.result(Tests.script_canvas_draw_sphere_node, notification_entity_2.gravity_was_enabled)
|
||||
|
||||
# 7) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C12712455_ScriptCanvas_ShapeCastVerification)
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C12868578
|
||||
# Test Case Title : Check that World space and local space force direction doesn't affect magnitude of force exerted
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12868578
|
||||
|
||||
# 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 C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude():
|
||||
"""
|
||||
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 Lumberyard 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 utils import Report
|
||||
from 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", "C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude")
|
||||
|
||||
# 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__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C12868578_ForceRegion_DirectionHasNoAffectOnMagnitude)
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C12868580
|
||||
# Test Case Title : Check that spline follow force works if transform components of entity are altered
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12868580
|
||||
|
||||
|
||||
# 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 C12868580_ForceRegion_SplineModifiedTransform():
|
||||
"""
|
||||
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 Lumberyard 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 utils import Report
|
||||
from 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", "C12868580_ForceRegion_SplineModifiedTransform")
|
||||
|
||||
# 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__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C12868580_ForceRegion_SplineModifiedTransform)
|
||||
@@ -0,0 +1,154 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C12905527
|
||||
# Test Case Title : Check that deviation occurring in Force Magnitude due to Values in Force direction is not large
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12905527
|
||||
|
||||
# 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 C12905527_ForceRegion_MagnitudeDeviation():
|
||||
"""
|
||||
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 Lumberyard 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 utils import Report
|
||||
from 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", "C12905527_ForceRegion_MagnitudeDeviation")
|
||||
|
||||
# 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__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C12905527_ForceRegion_MagnitudeDeviation)
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C12905528
|
||||
Test Case Title : Check that user is warned if non-trigger collider component is used with force region
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/12905528
|
||||
"""
|
||||
|
||||
|
||||
# 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 run():
|
||||
"""
|
||||
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 Lumberyard 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
|
||||
"""
|
||||
# Helper file Imports
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import Tracer
|
||||
from editor_entity_utils import EditorEntity
|
||||
|
||||
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
|
||||
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"))
|
||||
|
||||
# ) 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__":
|
||||
run()
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C13351703
|
||||
# Test Case Title : Check that Center of Mass calculations should not include trigger shapes
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/13351703
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_entities = ("Entities are found", "Entities are not found")
|
||||
com_expected = ("COM value is equal to expected value", "COM value is not equal to expected value")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C13351703_COM_NotIncludeTriggerShapes():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Check that Center of Mass calculations should not include trigger shapes.
|
||||
|
||||
Level Description:
|
||||
RigidBody (entity) - Entity with 1 Rigid Body component and 2 PhysX Collider components
|
||||
Rigid Body Component - Debug Draw Collider, Compute COM are enabled, Gravity is disabled
|
||||
1st Collider - Offset(-1.0, 0.0, 0.0) - Trigger enabled
|
||||
2nd Collider - Offset(1.0, 0.0, 0.0) - Trigger disabled
|
||||
|
||||
Expected Behavior:
|
||||
We are checking if the entity is valid.
|
||||
We are verifying if the center of mass is close to the collider whose trigger has been disabled.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Validate if COM is same as expected value
|
||||
5) Exit game mode
|
||||
6) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 critical_results.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
# Constants
|
||||
OFFSET = lymath.Vector3(1.0, 0.0, 0.0) # Offset of the trigger disabled sphere
|
||||
CLOSE_THRESHOLD = sys.float_info.epsilon
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C13351703_COM_NotIncludeTriggerShapes")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
rigid_body_id = general.find_game_entity("RigidBody")
|
||||
Report.critical_result(Tests.find_entities, rigid_body_id.IsValid())
|
||||
|
||||
# 4) Validate if COM is same as expected value
|
||||
entity_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", rigid_body_id)
|
||||
com = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetCenterOfMassWorld", rigid_body_id)
|
||||
Report.result(Tests.com_expected, entity_position.Add(OFFSET).IsClose(com, CLOSE_THRESHOLD))
|
||||
|
||||
# 5) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C13351703_COM_NotIncludeTriggerShapes)
|
||||
@@ -0,0 +1,292 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C13352089
|
||||
# Test Case Title : Verify that maximum angular velocity interacts correctly with initial angular velocity
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/13352089
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
|
||||
# level
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
trigger_touch_times = ("Bar5 rotated more than bar6", "Bar5 did not stop more slowly than bar6")
|
||||
rotation_duration = ("Bar5 stopped faster than bar6", "Bar5 did not stop faster than bar6")
|
||||
|
||||
# bar 1
|
||||
bar1_gravity_disabled = ("Bar1 : Gravity is disabled", "Bar1 : Gravity is not disabled")
|
||||
bar1_found = ("Bar1 : Found entity", "Bar1 : Entity not found")
|
||||
bar1_rotation = ("Bar1 : Rotated on X axis", "Bar1 : Unexpected rotation")
|
||||
bar1_angular_velocity = ("Bar1 : Expected angular velocity", "Bar1 : Unexpected angular velocity")
|
||||
|
||||
# bar 2
|
||||
bar2_gravity_disabled = ("Bar2 : Gravity is disabled", "Bar2 : Gravity is not disabled")
|
||||
bar2_found = ("Bar2 : Found entity", "Bar2 : Entity not found")
|
||||
bar2_rotation = ("Bar2 : Rotated on X axis", "Bar2 : Unexpected rotation")
|
||||
bar2_angular_velocity = ("Bar2 : Expected angular velocity", "Bar2 : Unexpected angular velocity")
|
||||
|
||||
# bar 3
|
||||
bar3_gravity_disabled = ("Bar3 : Gravity is disabled", "Bar3 : Gravity is not disabled")
|
||||
bar3_found = ("Bar3 : Found entity", "Bar3 : Entity not found")
|
||||
bar3_rotation = ("Bar3 : Rotated on X axis", "Bar3 : Unexpected rotation")
|
||||
bar3_angular_velocity = ("Bar3 : Expected angular velocity", "Bar3 : Unexpected angular velocity")
|
||||
|
||||
# bar 4
|
||||
bar4_gravity_disabled = ("Bar4 : Gravity is disabled", "Bar4 : Gravity is not disabled")
|
||||
bar4_found = ("Bar4 : Found entity", "Bar4 : Entity not found")
|
||||
bar4_rotation = ("Bar4 : Did not rotate", "Bar4 : Unexpected rotation")
|
||||
bar4_angular_velocity = ("Bar4 : Expected angular velocity", "Bar4 : Unexpected angular velocity")
|
||||
|
||||
# bar 5
|
||||
bar5_gravity_disabled = ("Bar5 : Gravity is disabled", "Bar5 : Gravity is not disabled")
|
||||
bar5_found = ("Bar5 : Found entity", "Bar5 : Entity not found")
|
||||
bar5_rotation = ("Bar5 : Rotated on X axis", "Bar5 : Unexpected rotation")
|
||||
bar5_angular_velocity = ("Bar5 : Expected angular velocity", "Bar5 : Unexpected angular velocity")
|
||||
|
||||
# bar 6
|
||||
bar6_gravity_disabled = ("Bar6 : Gravity is disabled", "Bar6 : Gravity is not disabled")
|
||||
bar6_found = ("Bar6 : Found entity", "Bar6 : Entity not found")
|
||||
bar6_rotation = ("Bar6 : Rotated on X axis", "Bar6 : Unexpected rotation")
|
||||
bar6_angular_velocity = ("Bar6 : Expected angular velocity", "Bar6 : Unexpected angular velocity")
|
||||
|
||||
# trigger for bar 5
|
||||
bar5_trigger_found = ("Trigger for bar5 : Found entity", "Trigger for bar5 : Entity not found")
|
||||
|
||||
# trigger for bar 6
|
||||
bar6_trigger_found = ("Trigger for bar6 : Found entity", "Trigger for bar6 : Entity not found")
|
||||
|
||||
did_not_timeout = ("Should_wait did not time out", "Should wait timed out")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C13352089_RigidBodies_MaxAngularVelocity():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure maximum angular velocity and angular damping interact correctly with
|
||||
initial angular velocity
|
||||
|
||||
Level Description:
|
||||
bars:
|
||||
6 PhysX Rigid Bodies with PhysX Shape Collider (Box Shape, Dimensions: X=1, Y=1, Z=5), gravity disabled,
|
||||
start asleep, positioned above terrain
|
||||
bar 1: Initial Angular Velocity = 10 rad/s, Max Angular Velocity = 5 rad/s, Angular Damping = 0.0
|
||||
bar 2: Initial Angular Velocity = 10 rad/s, Max Angular Velocity = 10 rad/s, Angular Damping = 0.0
|
||||
bar 3: Initial Angular Velocity = 20 rad/s, Max Angular Velocity = 20 rad/s, Angular Damping = 0.0
|
||||
bar 4: Initial Angular Velocity = 20 rad/s, Max Angular Velocity = 0 rad/s, Angular Damping = 0.0
|
||||
bar 5: Initial Angular Velocity = 20 rad/s, Max Angular Velocity = 20 rad/s, Angular Damping = 2.0
|
||||
bar 6: Initial Angular Velocity = 20 rad/s, Max Angular Velocity = 20 rad/s, Angular Damping = 5.0
|
||||
triggers:
|
||||
2 PhysX Shape Collider (Box Shape), gravity disabled, trigger enabled, start asleep
|
||||
trigger for bar 5: positioned above terrain, in front of bar 5
|
||||
trigger for bar 6: positioned above terrain, in front of bar 6
|
||||
|
||||
Expected Behavior:
|
||||
bar 1: Should rotate at 5 rad/s on X axis
|
||||
bar 2: Should rotate at 10 rad/s on X axis
|
||||
bar 3: Should rotate at 20 rad/s on X axis
|
||||
bar 4: Should not rotate at all
|
||||
bar 5: Should start rotating at 20 rad/s on X axis and stop slowly (touching its trigger several times)
|
||||
bar 6: Should start rotating at 20 rad/s on X axis but stop quickly (touching its trigger 0 or very few times)
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level
|
||||
2) Enters game mode
|
||||
3) Set up bars values
|
||||
4) Loop for each bar
|
||||
4.1) Find the bar
|
||||
4.2) Validate ID
|
||||
4.3) Activate the bar
|
||||
4.4) Validate and log its position
|
||||
4.5) Confirm gravity is disabled for the bar
|
||||
4.6) Log bar's initial rotation
|
||||
4.7) Log bar's angular damping
|
||||
4.8) Log bar's angular velocity
|
||||
4.9) Set up trigger if the bar has one
|
||||
4.10) Wait for each bar to rotate or stop rotating
|
||||
4.11) Log current location
|
||||
4.12) Validate rotation based on expected behavior
|
||||
4.13) Destroy bar (and its trigger)
|
||||
5) Compare trigger touch times for bar5 and bar6
|
||||
6) Exit game mode
|
||||
7) Close editor
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
import time
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
import azlmbr.math as lymath
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
ANGULAR_VELOCITY_TOLERANCE = 0.05
|
||||
TIME_OUT = 15.0
|
||||
ROTATION_TOLERANCE = 0.001
|
||||
|
||||
def is_close(x, y, tolerance=ANGULAR_VELOCITY_TOLERANCE):
|
||||
return abs(x - y) < tolerance
|
||||
|
||||
def is_angle_close(x, y):
|
||||
r = (math.sin(x) - math.sin(y)) * (math.sin(x) - math.sin(y)) + (math.cos(x) - math.cos(y)) * (
|
||||
math.cos(x) - math.cos(y)
|
||||
)
|
||||
diff = math.acos((2.0 - r) / 2.0)
|
||||
return abs(diff) <= ROTATION_TOLERANCE
|
||||
|
||||
class Entity: # Parent class for bars and triggers
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.validate_ID()
|
||||
self.activate_entity()
|
||||
|
||||
def validate_ID(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
found_tuple = Tests.__dict__[self.name + "_found"]
|
||||
Report.critical_result(found_tuple, self.id.IsValid())
|
||||
|
||||
def activate_entity(self):
|
||||
Report.info("Activating Entity : " + self.name)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.id)
|
||||
general.idle_wait_frames(1)
|
||||
self.rotation_start_time = time.time()
|
||||
|
||||
class Bar(Entity):
|
||||
def __init__(self, name, init_ang_velocity_on_X, max_ang_velocity, has_trigger=False):
|
||||
# 1) Validate ID, then activate Bar
|
||||
Entity.__init__(self, name)
|
||||
self.rotation_duration = 999999.9
|
||||
self.init_angular_velocity = lymath.Vector3(init_ang_velocity_on_X, 0.0, 0.0)
|
||||
self.max_angular_velocity = max_ang_velocity
|
||||
self.max_valid_velocity = min(self.max_angular_velocity, self.init_angular_velocity.GetLength())
|
||||
self.angular_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularDamping", self.id)
|
||||
# ) Log initial rotation
|
||||
self.init_rotation = self.get_rotation()
|
||||
# ) Verify gravity is disabled for bar
|
||||
self.validate_gravity_is_disabled()
|
||||
# ) Validate angular velocity according to max angular velocity
|
||||
self.validate_angular_velocity()
|
||||
# ) Setup trigger
|
||||
if has_trigger:
|
||||
self.touched_trigger = 0
|
||||
self.setup_trigger()
|
||||
|
||||
def validate_gravity_is_disabled(self):
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
gravity_tuple = Tests.__dict__[self.name + "_gravity_disabled"]
|
||||
Report.result(gravity_tuple, not gravity_enabled)
|
||||
|
||||
def setup_trigger(self):
|
||||
self.trigger = Entity(self.name + "_trigger")
|
||||
self.trigger.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.trigger.handler.connect(self.trigger.id)
|
||||
self.trigger.handler.add_callback("OnTriggerEnter", self.count_touch_times)
|
||||
|
||||
def get_angular_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularVelocity", self.id)
|
||||
|
||||
def get_rotation(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", self.id)
|
||||
|
||||
def validate_angular_velocity(self):
|
||||
general.idle_wait_frames(10)
|
||||
self.angular_velocity = self.get_angular_velocity()
|
||||
angular_velocity_tuple = Tests.__dict__[self.name + "_angular_velocity"]
|
||||
if self.angular_damping == 0:
|
||||
velocity_is_valid = is_close(self.angular_velocity.GetLength(), self.max_valid_velocity)
|
||||
else:
|
||||
velocity_is_valid = self.max_valid_velocity >= self.angular_velocity.GetLength() >= 0.0
|
||||
|
||||
Report.result(angular_velocity_tuple, velocity_is_valid)
|
||||
|
||||
def validate_rotation(self):
|
||||
self.rotation = self.get_rotation()
|
||||
rotated_on_x = not is_angle_close(self.init_rotation.x, self.rotation.x)
|
||||
rotated_on_y = not is_angle_close(self.init_rotation.y, self.rotation.y)
|
||||
rotated_on_z = not is_angle_close(self.init_rotation.z, self.rotation.z)
|
||||
if self.max_valid_velocity != 0:
|
||||
return rotated_on_x and not rotated_on_y and not rotated_on_z
|
||||
else:
|
||||
return not rotated_on_x and not rotated_on_y and not rotated_on_z
|
||||
|
||||
def has_waited_enough(self):
|
||||
if self.angular_damping > 0:
|
||||
# Bar5 or bar6 should stop rotating to consider their movement as done
|
||||
if self.get_angular_velocity().IsZero():
|
||||
self.rotation_duration = time.time() - self.rotation_start_time
|
||||
return True
|
||||
elif self.angular_damping == 0:
|
||||
# Bar1, bar2, bar3 or bar4
|
||||
return True
|
||||
return False
|
||||
|
||||
def count_touch_times(self, args):
|
||||
entering_entity_id = args[0]
|
||||
if entering_entity_id.Equal(self.id):
|
||||
Report.info(self.name + " touched " + self.trigger.name)
|
||||
self.touched_trigger += 1
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "C13352089_RigidBodies_MaxAngularVelocity")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Init bars values
|
||||
bars = [
|
||||
Bar(name="bar1", init_ang_velocity_on_X=10.0, max_ang_velocity=5),
|
||||
Bar(name="bar2", init_ang_velocity_on_X=10.0, max_ang_velocity=20),
|
||||
Bar(name="bar3", init_ang_velocity_on_X=20.0, max_ang_velocity=20),
|
||||
Bar(name="bar4", init_ang_velocity_on_X=20.0, max_ang_velocity=0,),
|
||||
Bar(name="bar5", init_ang_velocity_on_X=20.0, max_ang_velocity=20, has_trigger=True,),
|
||||
Bar(name="bar6", init_ang_velocity_on_X=20.0, max_ang_velocity=20, has_trigger=True,),
|
||||
]
|
||||
|
||||
# 4.10) Wait
|
||||
Report.critical_result(
|
||||
Tests.did_not_timeout,
|
||||
helper.wait_for_condition(lambda: all([bar.has_waited_enough() for bar in bars]), TIME_OUT),
|
||||
)
|
||||
|
||||
# 4.12) Validate rotation
|
||||
for bar in bars:
|
||||
Report.result(Tests.__dict__["{}_rotation".format(bar.name)], bar.validate_rotation())
|
||||
|
||||
# 5) Compare number of times bar5 and bar6 touched their trigger
|
||||
Report.result(Tests.trigger_touch_times, bars[4].touched_trigger > bars[5].touched_trigger)
|
||||
|
||||
Report.info(bars[4].rotation_duration)
|
||||
Report.info(bars[5].rotation_duration)
|
||||
|
||||
Report.result(Tests.rotation_duration, bars[4].rotation_duration > bars[5].rotation_duration)
|
||||
|
||||
# 6) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C13352089_RigidBodies_MaxAngularVelocity)
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C13508019
|
||||
# Test Case Title : Verify terrain materials are updated after using terrain texture layer painter.
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/13508019
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_box_bounce = ("Entity Bounce Box found", "Bounce Box not found")
|
||||
find_box_no_bounce = ("Entity No Bounce Box found", "No Bounce Box not found")
|
||||
find_terrain = ("Terrain found", "Terrain not found")
|
||||
entities_actions_finished = ("Entity actions completed", "Entity actions not completed")
|
||||
nonbouncy_box_not_bounced = ("Non-Bouncy Box didn't bounce", "Non-Bouncy Box did bounce")
|
||||
bouncy_box_not_bounced = ("Bouncy Box did bounce", "Bouncy Box didn't bounce")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C13508019_Terrain_TerrainTexturePainterWorks():
|
||||
# run() will open a a level and validate terrain material are updated after using terrain texture painter
|
||||
# It does this by:
|
||||
# 1) Opens level with a terrain that has been painted by two different physical materials with a box above each
|
||||
# 2) Enters Game mode
|
||||
# 3) Finds the entities in the scene
|
||||
# 5) Listens for boxes colliding with terrain
|
||||
# 7) Listens for boxes to exit(bounce) the collision or not (not bounce)
|
||||
# 8) Validate the results (The box above the blue terrain does not bounce and the box above the red terrain does)
|
||||
# 9) Exits game mode and editor
|
||||
|
||||
# Expected Result: Both the boxes will be affected by the physical material on the terrain. One box will
|
||||
# bounce and the other wont.
|
||||
|
||||
# Setup path
|
||||
import os, sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report, TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
TIME_OUT = 5.0 # Max time to complete test
|
||||
|
||||
class Box: # Holds box object attributes
|
||||
def __init__(self, boxID):
|
||||
self.box_id = boxID
|
||||
self.collided_with_terrain = False
|
||||
self.bounced = False
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C13508019_Terrain_TerrainTexturePainterWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# Creat box objects and set id's
|
||||
# Box that is above terrain that will bounce
|
||||
bounce_box = Box(general.find_game_entity("Box_Yes_Bounce"))
|
||||
Report.result(Tests.find_box_bounce, bounce_box.box_id.IsValid())
|
||||
|
||||
# Box that is above terrain that will not bounce
|
||||
no_bounce_box = Box(general.find_game_entity("Box_No_Bounce"))
|
||||
Report.result(Tests.find_box_no_bounce, no_bounce_box.box_id.IsValid())
|
||||
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.result(Tests.find_terrain, terrain_id.IsValid())
|
||||
|
||||
# Listen for a collision to begin
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(no_bounce_box.box_id) and no_bounce_box.collided_with_terrain is False:
|
||||
Report.info("Non-Bouncy box collided with terrain ")
|
||||
no_bounce_box.collided_with_terrain = True
|
||||
if other_id.Equal(bounce_box.box_id) and bounce_box.collided_with_terrain is False:
|
||||
Report.info("Bouncy box collided with terrain ")
|
||||
bounce_box.collided_with_terrain = True
|
||||
|
||||
# Listen for a collision to end
|
||||
def on_collision_end(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(no_bounce_box.box_id):
|
||||
no_bounce_box.bounced = True
|
||||
if other_id.Equal(bounce_box.box_id):
|
||||
bounce_box.bounced = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(terrain_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
handler.add_callback("OnCollisionEnd", on_collision_end)
|
||||
|
||||
# The test_entities_actions_completed function below returns a boolean of if the following actions happened:
|
||||
# Did the bounce box hit the terrain
|
||||
# Did the no bounce box hit the terrain
|
||||
# Did the bounce box bounce
|
||||
# Did the no bounce box not bounce
|
||||
def test_entities_actions_completed():
|
||||
return (
|
||||
bounce_box.collided_with_terrain
|
||||
and no_bounce_box.collided_with_terrain
|
||||
and bounce_box.bounced
|
||||
and not no_bounce_box.bounced # not here because the no bounce box should not have bounced
|
||||
)
|
||||
|
||||
entities_actions_finished = helper.wait_for_condition(test_entities_actions_completed, TIME_OUT)
|
||||
|
||||
# Report is the entities in level finished their actions
|
||||
Report.result(Tests.entities_actions_finished, entities_actions_finished)
|
||||
|
||||
# Report Info
|
||||
Report.info("Bouncy Box hit terrain: Expected = True Actual = {}".format(bounce_box.collided_with_terrain))
|
||||
Report.info(
|
||||
"Non-Bouncy Box hit terrain: Expected = True Actual = {}".format(no_bounce_box.collided_with_terrain)
|
||||
)
|
||||
Report.info("Did Bouncy Box bounce: Expected = True Actual = {}".format(bounce_box.bounced))
|
||||
Report.info("Did Non-Bounce Box bounce: Expected = False Actual = {}".format(no_bounce_box.bounced))
|
||||
|
||||
# Check if the above test completed.
|
||||
if entities_actions_finished:
|
||||
Report.result(Tests.bouncy_box_not_bounced, bounce_box.bounced)
|
||||
Report.result(Tests.nonbouncy_box_not_bounced, not no_bounce_box.bounced)
|
||||
|
||||
# 4) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C13508019_Terrain_TerrainTexturePainterWorks)
|
||||
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C13895144
|
||||
# Test Case Title : Run a level with multiple ragdolls and then switch levels
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/13895144
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode_with_ragdolls = ("Entered game mode With Ragdolls", "Failed to enter game mode With Ragdolls")
|
||||
found_ragdolls = ("Found all ragdolls", "Did not find all ragdolls")
|
||||
exit_game_mode_with_ragdolls = ("Exited game mode With Ragdolls", "Couldn't exit game mode With Ragdolls")
|
||||
|
||||
enter_game_mode_without_ragdolls = ("Entered game mode Without Ragdolls", "Failed to enter game mode Without Ragdolls")
|
||||
exit_game_mode_without_ragdolls = ("Exited game mode Without Ragdolls", "Couldn't exit game mode Without Ragdolls")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C13895144_Ragdoll_ChangeLevel():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that switching from a level with many ragdolls to a level with no ragdolls does not
|
||||
crash the editor.
|
||||
|
||||
Level Description:
|
||||
C13895144_Ragdoll_WithRagdoll:
|
||||
10 Ragdoll entities are placed in a row. Each Entity has an Actor, Anim Graph and PhysX Ragdoll component.
|
||||
|
||||
C13895144_Ragdoll_NoRagdoll:
|
||||
The same default level configuration as previous, but with no ragdoll entities. Essentially an empty level.
|
||||
|
||||
Expected Behavior:
|
||||
C13895144_Ragdoll_WithRagdoll loads, all entities are found, then C13895144_Ragdoll_NoRagdoll loads correctly.
|
||||
No crash should occur.
|
||||
|
||||
Test Steps:
|
||||
1) Open level (with ragdolls)
|
||||
2) Enter game mode
|
||||
3) Find all of our ragdolls
|
||||
4) Exit game mode
|
||||
5) Open level (without ragdolls)
|
||||
6) Enter game mode
|
||||
7) Wait for no crash
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
WAIT = 3.0
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C13895144_Ragdoll_WithRagdoll")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode_with_ragdolls)
|
||||
|
||||
# 3) Find all of our ragdolls
|
||||
all_valid = True
|
||||
for id in range(0, 10):
|
||||
ragdoll_id = general.find_game_entity("Ragdoll_{}".format(id))
|
||||
if not ragdoll_id.IsValid():
|
||||
Report.info("Could not find Ragdoll_{}".format(id))
|
||||
all_valid = False
|
||||
|
||||
Report.result(Tests.found_ragdolls, all_valid)
|
||||
|
||||
# 4) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode_with_ragdolls)
|
||||
|
||||
# 5) Open level (without ragdolls)
|
||||
helper.open_level("Physics", "C13895144_Ragdoll_NoRagdoll")
|
||||
|
||||
# 6) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode_without_ragdolls)
|
||||
|
||||
# 7) Wait for no crash
|
||||
general.idle_wait(WAIT)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode_without_ragdolls)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C13895144_Ragdoll_ChangeLevel)
|
||||
@@ -0,0 +1,194 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C14195074
|
||||
# Test Case Title : Verify Post Update Events
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14195074
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
lead_sphere_found = ("Lead_Sphere is valid", "Lead_Sphere is not valid")
|
||||
follow_sphere_found = ("Follow_Sphere is valid", "Follow_Sphere is not valid")
|
||||
no_movement = ("Spheres start with no movement", "Spheres not stationary")
|
||||
initial_position = ("Initial position is valid", "Initial position is not valid")
|
||||
lead_sphere_velocity = ("Lead_Sphere has valid velocity", "Lead_Sphere velocity not valid")
|
||||
spheres_moving = ("Spheres have both moved", "Both sphere have not moved before timeout")
|
||||
follow_condition_true = ("Follow_Sphere is correctly trailing", "Follow_Sphere follow distance not valid")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C14195074_ScriptCanvas_PostUpdateEvent():
|
||||
"""
|
||||
Summary: Verifies that Post Update Event node in Script Canvas works as expected.
|
||||
|
||||
Level Description:
|
||||
Lead_Sphere - Directly next to Follow_sphere on the +x axis; has rigid body(gravity disabled), sphere shape
|
||||
collider, sphere shape
|
||||
Follow_Sphere - Directly next to Lead_Sphere on the -x axis; has rigid body (gravity disabled, kinematic
|
||||
enabled), sphere shape collider, sphere shape, script canvas
|
||||
|
||||
Script Canvas - After every PhysX frame is calculated Follow_Sphere is moved to directly next to the
|
||||
Lead_sphere before being presented in the viewer
|
||||
PhysX Configuration - The PhysX configuration file was modified to lower the frame rate to 20 Hz for visual debug
|
||||
|
||||
Expected Behavior: Follow_Sphere follows Lead_spheres position, staying within direct contact no matter the speed
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate entities
|
||||
4) Validate spheres are not moving
|
||||
5) Check Position of Spheres
|
||||
6) Start moving sphere and check that it acts correctly
|
||||
7) Wait until Lead_Sphere has moved a set distance
|
||||
8) Verify Follow_Sphere follow distance
|
||||
9) Log results
|
||||
10) Exit Game Mode
|
||||
11) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as math
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = sys.float_info.epsilon
|
||||
TIMEOUT = 5
|
||||
INITIAL_OFFSET = 1
|
||||
REQUIRED_MOVEMENT = 2
|
||||
LEAD_SPHERE_VELOCITY = 10.0
|
||||
FINAL_OFFSET = INITIAL_OFFSET
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
|
||||
self.initial_position = self.position
|
||||
self.final_position = None
|
||||
# Validate Entities
|
||||
found = Tests.__dict__[self.name.lower() + "_found"]
|
||||
Report.critical_result(found, self.id.isValid())
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
def set_velocity(self, x_velocity, y_velocity, z_velocity):
|
||||
velocity = math.Vector3(x_velocity, y_velocity, z_velocity)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, velocity)
|
||||
|
||||
def moved_enough(self):
|
||||
current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
return abs(self.initial_position.x - current_position.x) >= REQUIRED_MOVEMENT
|
||||
|
||||
def report_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 check_relative_position(lead_sphere_position, follow_sphere_position, offset):
|
||||
return (
|
||||
abs((lead_sphere_position.x - follow_sphere_position.x) - offset) < FLOAT_THRESHOLD
|
||||
and abs(lead_sphere_position.y - follow_sphere_position.y) < FLOAT_THRESHOLD
|
||||
and abs(lead_sphere_position.z - follow_sphere_position.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
def velocity_zero(sphere_velocity):
|
||||
return (
|
||||
abs(sphere_velocity.x) < FLOAT_THRESHOLD
|
||||
and abs(sphere_velocity.y) < FLOAT_THRESHOLD
|
||||
and abs(sphere_velocity.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
def velocity_valid(lead_sphere_velocity):
|
||||
return (
|
||||
lead_sphere_velocity.x == LEAD_SPHERE_VELOCITY
|
||||
and abs(lead_sphere_velocity.y) < FLOAT_THRESHOLD
|
||||
and abs(lead_sphere_velocity.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C14195074_ScriptCanvas_PostUpdateEvent")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and validate entities
|
||||
lead_sphere = Entity("Lead_Sphere")
|
||||
follow_sphere = Entity("Follow_Sphere")
|
||||
|
||||
# 4) Validate Spheres are not moving
|
||||
Report.critical_result(
|
||||
Tests.no_movement, velocity_zero(lead_sphere.velocity) and velocity_zero(follow_sphere.velocity)
|
||||
)
|
||||
|
||||
# 5) Check Position of Spheres
|
||||
Report.result(
|
||||
Tests.initial_position,
|
||||
check_relative_position(lead_sphere.initial_position, follow_sphere.initial_position, INITIAL_OFFSET),
|
||||
)
|
||||
|
||||
# 6) Start moving sphere and check that it acts correctly
|
||||
lead_sphere.set_velocity(LEAD_SPHERE_VELOCITY, 0.0, 0.0)
|
||||
Report.result(Tests.lead_sphere_velocity, velocity_valid(lead_sphere.velocity))
|
||||
|
||||
# 7) Wait until Lead_Sphere has moved a set distance
|
||||
Report.result(Tests.spheres_moving, helper.wait_for_condition(lead_sphere.moved_enough, TIMEOUT))
|
||||
|
||||
# 8) Verify Follow_Sphere follow distance
|
||||
lead_sphere.final_position = lead_sphere.position
|
||||
follow_sphere.final_position = follow_sphere.position
|
||||
Report.result(
|
||||
Tests.follow_condition_true, check_relative_position(lead_sphere.final_position, follow_sphere.final_position, FINAL_OFFSET)
|
||||
)
|
||||
|
||||
# 9) Log results
|
||||
lead_sphere.report_values()
|
||||
follow_sphere.report_values()
|
||||
|
||||
# 10) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14195074_ScriptCanvas_PostUpdateEvent)
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C14654881
|
||||
# Test Case Title : Switching levels from a level containing a character controller component
|
||||
# should not lead to a crash
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14654881
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
# level
|
||||
level1_enter_game_mode = ("Entered game mode level 1", "Failed to enter game mode level 1")
|
||||
level1_exit_game_mode = ("Exited game mode level 1", "Couldn't exit game mode level 1")
|
||||
CharacterController_found = ("Character controller was found", "Character controller was not found")
|
||||
level2_enter_game_mode = ("Entered game mode level 2", "Failed to enter game mode level 2")
|
||||
level2_exit_game_mode = ("Exited game mode level 2", "Couldn't exit game mode level 2")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C14654881_CharacterController_SwitchLevels():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to verify that switching levels from a level containing a character controller component
|
||||
does not lead to a crash
|
||||
|
||||
Level Description:
|
||||
There are 2 levels used in this test:
|
||||
One contains an entity with a PhysX Character Controller component,
|
||||
the other one is empty.
|
||||
|
||||
Expected Behavior:
|
||||
It should enter and then exit game mode without any errors in both levels.
|
||||
|
||||
|
||||
Test Steps:
|
||||
1.1) Load the level with PhysX Character Controller
|
||||
1.2) Enter game mode
|
||||
1.3) Find the entity with PhysX Character Controller component
|
||||
1.4) Exit game mode
|
||||
|
||||
2.1) Load the empty level
|
||||
2.2) Enter game mode
|
||||
2.3) Exit game mode
|
||||
|
||||
3) Close editor
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
WAIT_FOR_ERRORS = 3.0
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1.1) Load level 1 (with character controller)
|
||||
helper.open_level("Physics", "C14654881_CharacterController_SwitchLevels")
|
||||
|
||||
# 1.2) Enter game mode
|
||||
helper.enter_game_mode(Tests.level1_enter_game_mode)
|
||||
|
||||
# 1.3) Find and validate character controller entity
|
||||
characterController_id = general.find_game_entity("CharacterController")
|
||||
Report.critical_result(Tests.CharacterController_found, characterController_id.IsValid())
|
||||
|
||||
# 1.4) Exit Game mode
|
||||
helper.exit_game_mode(Tests.level1_exit_game_mode)
|
||||
|
||||
# 2.1) Load level 2 (empty level)
|
||||
helper.open_level("Physics", "C14654881_CharacterController_SwitchLevelsEmpty")
|
||||
|
||||
# 2.2) Enter game mode
|
||||
helper.enter_game_mode(Tests.level2_enter_game_mode)
|
||||
general.idle_wait(WAIT_FOR_ERRORS)
|
||||
|
||||
# 2.3) Exit Game mode
|
||||
helper.exit_game_mode(Tests.level2_exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14654881_CharacterController_SwitchLevels)
|
||||
@@ -0,0 +1,141 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
"""
|
||||
Test case ID : C14654882
|
||||
Test Case Title : Loading level with old PhysX Ragdoll component serialization should not produce asset processor errors
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14654882
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
log_modified = ("The log has been modified from the test run", "The test run failed to modify the logs")
|
||||
length_of_recorded_lines = ("Log lines were grabbed for checking", "There were no lines grabbed for checking")
|
||||
error_line_not_found = ("Specified lines were not found in the AP_GUI log file", "One or more specified lines were found in the AP_GUI log file")
|
||||
# fmt: on
|
||||
|
||||
|
||||
class ExpectedLines:
|
||||
# These lines are exported to the test runner code to be used via the test suite log monitor
|
||||
lines = []
|
||||
|
||||
|
||||
class UnexpectedLines:
|
||||
# These lines are exported to the test runner code to be used via the test suite log monitor
|
||||
lines = ["Assert", "RagdollComponent' is not registered with the serializer"]
|
||||
|
||||
|
||||
def C14654882_Ragdoll_ragdollAPTest():
|
||||
"""
|
||||
Summary:
|
||||
Opens level containing old PhysX Ragdoll component.
|
||||
|
||||
Level Description:
|
||||
Ragdoll - (entity): PhysX Ragdoll:
|
||||
Position iteration count: 16
|
||||
Velocity iteration count: 8
|
||||
Enable joint projection: checked
|
||||
Joint projection linear tolerance: 0.001
|
||||
Joint projection angular tolerance: 1.0 degrees
|
||||
|
||||
Expected Behavior:
|
||||
The level should load and not produce any errors for the asset processor component
|
||||
serialization of the old PhysX Ragdoll.
|
||||
|
||||
Test Steps
|
||||
0) Get the latest modified time for the AP_GUI.log
|
||||
1) Open the level
|
||||
2) Enter game mode
|
||||
3) Exit game mode
|
||||
4) Close the editor (Logs will be read after closing editor)
|
||||
5) Check the AP_GUI log file for the error
|
||||
5.1) Opens the log file to record the lines
|
||||
5.2) Check that recorded_lines size > 0
|
||||
5.3) Search the recorded lines for an Unexpected Line
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import TestHelper as helper
|
||||
from utils import Report
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 0) Get the latest modified time for the AP_GUI.log
|
||||
AP_GUI_log_path = os.path.join(os.getcwd(), "Bin64vc141", "logs", "AP_GUI.log")
|
||||
log_modified_time = None
|
||||
if os.path.exists(AP_GUI_log_path):
|
||||
log_modified_time = os.stat(AP_GUI_log_path).st_mtime
|
||||
|
||||
# 1) Open the level
|
||||
helper.open_level("Physics", "C14654882_Ragdoll_ragdollAPTest")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
# 5) Check the AP_GUI log file for the error
|
||||
recorded_lines = []
|
||||
if log_modified_time:
|
||||
log_modified_time_final = os.stat(AP_GUI_log_path).st_mtime
|
||||
Report.info("Log modified time: {}".format(log_modified_time))
|
||||
Report.info("Log final modified time: {}".format(log_modified_time))
|
||||
Report.critical_result(
|
||||
Tests.log_modified, log_modified_time_final > log_modified_time, "The log has not updated since last run."
|
||||
)
|
||||
|
||||
# 5.1) Opens the log file to read the lines
|
||||
with open(AP_GUI_log_path) as log_file:
|
||||
for line in log_file:
|
||||
if "~none~" in line: # "~none~" in line with the message for starting a new AzFramework File Logging run
|
||||
recorded_lines = [] # clear the recorded lines to look for the newer lines
|
||||
else:
|
||||
recorded_lines.append(line)
|
||||
|
||||
# The end of log file was reached which should produce the lines for the newest run
|
||||
# 5.2) Check that recorded_lines size > 0
|
||||
size = len(recorded_lines)
|
||||
Report.result(Tests.length_of_recorded_lines, size > 0)
|
||||
|
||||
# 5.3) Search the recorded lines for an Unexpected Line
|
||||
error_found = False
|
||||
for line in recorded_lines:
|
||||
if any(s in line for s in UnexpectedLines.lines):
|
||||
error_found = True
|
||||
Report.info("Unexpected line was found:")
|
||||
Report.info(line)
|
||||
|
||||
Report.result(Tests.error_line_not_found, not error_found)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14654882_Ragdoll_ragdollAPTest)
|
||||
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C14861498
|
||||
# Test Case Title : Confirm that when a PhysXCollider has no physics asset, the physics asset collider \
|
||||
# shape throw an error
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861498
|
||||
|
||||
|
||||
# fmt:off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
found_entity = ("Entity was found", "Entity WAS NOT found")
|
||||
warning_message_logged = ("The expected warning was logged", "The expected message was not logged")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt:on
|
||||
|
||||
|
||||
def C14861498_ConfirmError_NoPxMesh():
|
||||
"""
|
||||
Summary:
|
||||
This test looks for the presence of an error when an entity with a PhysXCollider has no physics mesh, but has its
|
||||
collider shape set to a physics mesh.
|
||||
|
||||
Level Description:
|
||||
One entity with a PhysXCollider with the collider shape set to "physics asset" and no actual physics mesh.
|
||||
That's it!
|
||||
|
||||
Steps:
|
||||
1) Load the level / enter game mode
|
||||
2) Find the entity
|
||||
3) Look for warning
|
||||
4) Exit game mode
|
||||
5) Close the editor
|
||||
|
||||
[Log Monitor] make sure error lines are present in the log
|
||||
|
||||
Expected Behavior:
|
||||
The editor should open, load the level and (seemingly) instantly close. The two error lines specified should
|
||||
print to the log.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import Tracer
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
with Tracer() as warning_tracer:
|
||||
def has_physx_warning():
|
||||
return warning_tracer.has_warnings and any(
|
||||
'PhysX' in warningInfo.window and
|
||||
'EditorColliderComponent' in warningInfo.message for warningInfo in warning_tracer.warnings)
|
||||
|
||||
# 1) Load level / enter game mode
|
||||
helper.open_level("Physics", "C14861498_ConfirmError_NoPxMesh")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Find game entity
|
||||
id = general.find_game_entity("test_entity")
|
||||
Report.result(Tests.found_entity, id.IsValid())
|
||||
|
||||
# 3) Look for warning
|
||||
helper.wait_for_condition(has_physx_warning, 1.0)
|
||||
Report.result(Tests.warning_message_logged, has_physx_warning())
|
||||
|
||||
# 4) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14861498_ConfirmError_NoPxMesh)
|
||||
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C14861500
|
||||
Test Case Title : Verify Default shape is Physics Asset
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861500
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_collider_entity = ("Created Collider Entity", "Failed to create Collider Entity")
|
||||
add_physx_collider = ("PhysX Collider added", "Failed to add PhysX Collider")
|
||||
shape_is_correct = ("PhysX Collider Shape is correct", "PhysX Collider Shape is not PhysicsAsset")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C14861500_DefaultSetting_ColliderShape():
|
||||
"""
|
||||
Summary:
|
||||
Check the default for Shape on the PhysX Collider component
|
||||
|
||||
Expected Behavior:
|
||||
When adding the PhysX Collider, the default Shape should be PhysX Asset
|
||||
|
||||
Test Steps:
|
||||
1) Load empty level
|
||||
2) Create an entity to hold the PhysX Shape Collider component
|
||||
3) Add the PhysX Collider component
|
||||
4) Check value of Shape property on PhysX Collider
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
|
||||
# Lumberyard Imports
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
PHYSICS_ASSET_INDEX = 7 # Hardcoded enum index value for Shape property
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load empty level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create an entity to hold the PhysX Shape Collider component
|
||||
collider_entity = Entity.create_editor_entity("Collider")
|
||||
Report.result(Tests.create_collider_entity, collider_entity.id.IsValid())
|
||||
|
||||
# 3) Add the PhysX Collider component
|
||||
test_component = collider_entity.add_component("PhysX Collider")
|
||||
Report.result(Tests.add_physx_collider, collider_entity.has_component("PhysX Collider"))
|
||||
|
||||
# 4) Check value of Shape property on PhysX Collider
|
||||
value_to_test = test_component.get_component_property_value("Shape Configuration|Shape")
|
||||
Report.result(Tests.shape_is_correct, value_to_test == PHYSICS_ASSET_INDEX)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14861500_DefaultSetting_ColliderShape)
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C14861501
|
||||
Test Case Title : Verify PxMesh is auto-assigned when Collider component is added after Rendering Mesh component
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861501
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_entity = ("Created test entity", "Failed to create test entity")
|
||||
mesh_added = ("Added Mesh component", "Failed to add Mesh component")
|
||||
physx_collider_added = ("Added PhysX Collider component", "Failed to add PhysX Collider component")
|
||||
assign_mesh_asset = ("Assigned Mesh asset to Mesh component", "Failed to assign mesh asset to Mesh component")
|
||||
automatic_shape_change = ("Shape was changed automatically", "Shape failed to change automatically")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Summary:
|
||||
Create entity with Mesh component and assign a render mesh to the Mesh component. Add Physics Collider component
|
||||
and Verify that the physics mesh asset is auto-assigned.
|
||||
|
||||
Expected Behavior:
|
||||
The physics asset in PhysX Collider component is auto-assigned
|
||||
|
||||
Test Steps:
|
||||
1) Load the empty level
|
||||
2) Create an entity
|
||||
3) Add Mesh component
|
||||
4) Assign a render mesh asset to Mesh component (the fbx mesh having both Static mesh and PhysX collision Mesh)
|
||||
5) Add PhysX Collider component
|
||||
6) The physics asset in PhysX Collider component is auto-assigned.
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Builtins
|
||||
import os
|
||||
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
from asset_utils import Asset
|
||||
|
||||
# Asset paths
|
||||
STATIC_MESH = os.path.join("assets", "c14861501_physxcollider_rendermeshautoassigned", "spherebot", "r0-b_body.cgf")
|
||||
PHYSX_MESH = os.path.join(
|
||||
"assets", "c14861501_physxcollider_rendermeshautoassigned", "spherebot", "r0-b_body.pxmesh"
|
||||
)
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the empty level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create an entity
|
||||
test_entity = Entity.create_editor_entity("test_entity")
|
||||
Report.result(Tests.create_entity, test_entity.id.IsValid())
|
||||
|
||||
# 3) Add Mesh component
|
||||
mesh_component = test_entity.add_component("Mesh")
|
||||
Report.result(Tests.mesh_added, test_entity.has_component("Mesh"))
|
||||
|
||||
# 4) Assign a render mesh asset to Mesh component (the fbx mesh having both Static mesh and PhysX collision Mesh)
|
||||
mesh_asset = Asset.find_asset_by_path(STATIC_MESH)
|
||||
mesh_component.set_component_property_value("MeshComponentRenderNode|Mesh asset", mesh_asset.id)
|
||||
mesh_asset.id = mesh_component.get_component_property_value("MeshComponentRenderNode|Mesh asset")
|
||||
Report.result(Tests.assign_mesh_asset, mesh_asset.get_path() == STATIC_MESH.replace(os.sep, "/"))
|
||||
|
||||
# 5) Add PhysX Collider component
|
||||
test_component = test_entity.add_component("PhysX Collider")
|
||||
Report.result(Tests.physx_collider_added, test_entity.has_component("PhysX Collider"))
|
||||
|
||||
# 6) The physics asset in PhysX Collider component is auto-assigned.
|
||||
asset_id = test_component.get_component_property_value("Shape Configuration|Asset|PhysX Mesh")
|
||||
test_asset = Asset(asset_id)
|
||||
Report.result(Tests.automatic_shape_change, test_asset.get_path() == PHYSX_MESH.replace(os.sep, "/"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C14861502
|
||||
Test Case Title : Verify PxMesh is auto-assigned in collider when Mesh is assigned in Rendering Mesh component
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861502
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_entity = ("Created test entity", "Failed to create test entity")
|
||||
mesh_added = ("Added Mesh component", "Failed to add Mesh component")
|
||||
physx_collider_added = ("Added PhysX Collider component", "Failed to add PhysX Collider component")
|
||||
shape_default_at_start = ("Shape was correct initially", "Default shape was not correct")
|
||||
automatic_shape_change = ("Shape was changed automatically", "Shape failed to change automatically")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C14861502_PhysXCollider_AssetAutoAssigned():
|
||||
"""
|
||||
Summary:
|
||||
Create entity with Mesh and PhysX Collider components, then assign a render mesh to the Mesh component
|
||||
|
||||
Expected Behavior:
|
||||
The physics asset in PhysX Collider component is auto-assigned after adding the render mesh
|
||||
|
||||
Test Steps:
|
||||
1) Load the empty level
|
||||
2) Create an entity
|
||||
3) Add Mesh and PhysX Collider component
|
||||
4) Verify no physics asset is auto-assigned.
|
||||
5) Assign a render mesh asset to Mesh component (the fbx mesh having both Static mesh and PhysX collision Mesh)
|
||||
6) The physics asset in PhysX Collider component is auto-assigned.
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Builtins
|
||||
import os
|
||||
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
from asset_utils import Asset
|
||||
|
||||
# Lumberyard Imports
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
MESH_ASSET_PATH = os.path.join("Objects", "SphereBot", "r0-b_body.cgf")
|
||||
MESH_PROPERTY_PATH = "MeshComponentRenderNode|Mesh asset"
|
||||
TESTED_PROPERTY_PATH = "Shape Configuration|Asset|PhysX Mesh"
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the empty level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create an entity
|
||||
test_entity = Entity.create_editor_entity("test_entity")
|
||||
Report.result(Tests.create_entity, test_entity.id.IsValid())
|
||||
|
||||
# 3) Add Mesh and PhysX Collider component
|
||||
mesh_component = test_entity.add_component("Mesh")
|
||||
Report.result(Tests.mesh_added, test_entity.has_component("Mesh"))
|
||||
|
||||
test_component = test_entity.add_component("PhysX Collider")
|
||||
Report.result(Tests.physx_collider_added, test_entity.has_component("PhysX Collider"))
|
||||
|
||||
# 4) Verify no physics asset is auto-assigned
|
||||
value_to_test = test_component.get_component_property_value(TESTED_PROPERTY_PATH)
|
||||
Report.result(Tests.shape_default_at_start, value_to_test == azlmbr.asset.AssetId())
|
||||
|
||||
# 5) Assign a render mesh asset to Mesh component (the fbx mesh having both Static mesh and PhysX collision Mesh)
|
||||
asset_value = Asset.find_asset_by_path(MESH_ASSET_PATH)
|
||||
mesh_component.set_component_property_value(MESH_PROPERTY_PATH, asset_value.id)
|
||||
|
||||
# 6) The physics asset in PhysX Collider component is auto-assigned.
|
||||
general.idle_wait(1.0) # Gives the script a moment for the value to update before grabbing it
|
||||
value_to_test = test_component.get_component_property_value(TESTED_PROPERTY_PATH)
|
||||
asset = Asset(value_to_test)
|
||||
Report.result(Tests.automatic_shape_change, "r0-b_body" in asset.get_path())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14861502_PhysXCollider_AssetAutoAssigned)
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C14861504
|
||||
Test Case Title : Verify if Rendering Mesh does not have a PhysX Collision Mesh fbx, then PxMesh is not auto-assigned
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14861504
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_entity = ("Created test entity", "Failed to create test entity")
|
||||
mesh_added = ("Added Mesh component", "Failed to add Mesh component")
|
||||
physx_collider_added = ("Added PhysX Collider component", "Failed to add PhysX Collider component")
|
||||
assign_mesh_asset = ("Assigned Mesh asset to Mesh component", "Failed to assign mesh asset to Mesh component")
|
||||
shape_not_assigned = ("Shape is not auto assigned", "Shape auto assigned unexpectedly")
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
warnings_found = ("Warnings found in logs", "No warnings found in logs")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Summary:
|
||||
Create entity with Mesh component and assign a render mesh that has no physics asset to the Mesh component.
|
||||
Add Physics Collider component and Verify that the physics mesh asset is not auto-assigned.
|
||||
|
||||
Expected Behavior:
|
||||
Following warning is logged in Game mode:
|
||||
"(PhysX) - EditorColliderComponent::BuildGameEntity. No asset assigned to Collider Component. Entity: <Entity Name>"
|
||||
|
||||
Test Steps:
|
||||
1) Load the empty level
|
||||
2) Create an entity
|
||||
3) Add Mesh component
|
||||
4) Assign a render mesh asset to Mesh component (the fbx mesh having only Static mesh and no PxMesh)
|
||||
5) Add PhysX Collider component
|
||||
6) The physics asset in PhysX Collider component is not auto-assigned.
|
||||
7) Enter GameMode and check for warnings
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Builtins
|
||||
import os
|
||||
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import Tracer
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
from asset_utils import Asset
|
||||
|
||||
# Lumberyard Imports
|
||||
import azlmbr.asset as azasset
|
||||
|
||||
# Asset paths
|
||||
STATIC_MESH = os.path.join("assets", "c14861504_rendermeshasset_withnopxasset", "test_asset.cgf")
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the empty level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create an entity
|
||||
test_entity = Entity.create_editor_entity("test_entity")
|
||||
Report.result(Tests.create_entity, test_entity.id.IsValid())
|
||||
|
||||
# 3) Add Mesh component
|
||||
mesh_component = test_entity.add_component("Mesh")
|
||||
Report.result(Tests.mesh_added, test_entity.has_component("Mesh"))
|
||||
|
||||
# 4) Assign a render mesh asset to Mesh component (the fbx mesh having both Static mesh and PhysX collision Mesh)
|
||||
mesh_asset = Asset.find_asset_by_path(STATIC_MESH)
|
||||
mesh_component.set_component_property_value("MeshComponentRenderNode|Mesh asset", mesh_asset.id)
|
||||
mesh_asset.id = mesh_component.get_component_property_value("MeshComponentRenderNode|Mesh asset")
|
||||
Report.result(Tests.assign_mesh_asset, mesh_asset.get_path() == STATIC_MESH.replace(os.sep, "/"))
|
||||
|
||||
# 5) Add PhysX Collider component
|
||||
test_component = test_entity.add_component("PhysX Collider")
|
||||
Report.result(Tests.physx_collider_added, test_entity.has_component("PhysX Collider"))
|
||||
|
||||
# 6) The physics asset in PhysX Collider component is not auto-assigned.
|
||||
asset_id = test_component.get_component_property_value("Shape Configuration|Asset|PhysX Mesh")
|
||||
# Comparing asset_id with Null/Invalid asset azlmbr.asset.AssetId() to check that asset is not auto assigned
|
||||
Report.result(Tests.shape_not_assigned, asset_id == azasset.AssetId())
|
||||
|
||||
# 7) Enter GameMode and check for warnings
|
||||
with Tracer() as section_tracer:
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
# Checking if warning exist and the exact warning is caught in the expected lines in Test file
|
||||
Report.result(Tests.warnings_found, section_tracer.has_warnings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C14902097
|
||||
# Test Case Title : Verify Pre Update Events
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14902097
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
lead_sphere_found = ("Lead_Sphere is valid", "Lead_Sphere is not valid")
|
||||
follow_sphere_found = ("Follow_Sphere is valid", "Follow_Sphere is not valid")
|
||||
no_movement = ("Spheres start with no movement", "Spheres not stationary")
|
||||
initial_position = ("Initial position is valid", "Initial position isn't valid")
|
||||
lead_sphere_velocity = ("Lead_Sphere has valid velocity", "Lead_Sphere velocity not valid")
|
||||
spheres_moving = ("Spheres have both moved", "Both sphere have not moved before timeout")
|
||||
follow_condition_true = ("Follow_Sphere is correctly trailing", "Follow_Sphere follow distance not valid")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C14902097_ScriptCanvas_PreUpdateEvent():
|
||||
"""
|
||||
Summary: Verifies that Pre Update Event node in Script Canvas works as expected.
|
||||
|
||||
Level Description:
|
||||
Lead_Sphere - Directly next to Follow_sphere on the +x axis; has rigid body (gravity disabled), sphere shape
|
||||
collider, sphere shape
|
||||
Follow_Sphere - Directly next to Lead_Sphere on the -x axis; has rigid body (gravity disabled, kinematic
|
||||
enabled), sphere shape collider, sphere shape, script canvas
|
||||
|
||||
Script Canvas - Before every frame of PhysX calculation the Follow_Sphere is moved directly next to the
|
||||
Lead_Sphere, then PhysX calculates and moves the Lead_Sphere to a new position before presenting to
|
||||
the viewer
|
||||
PhysX Configuration - The PhysX configuration file was modified to lower the frame rate to 20 Hz for visual debug
|
||||
|
||||
Expected Behavior: Follow_Sphere follows Lead_spheres position with a lag that is dependent on the speed and the
|
||||
inverse frame rate
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate entities
|
||||
4) Validate Spheres are not moving
|
||||
5) Check Position of Spheres
|
||||
6) Start moving sphere and check that it acts correctly
|
||||
7) Wait until Lead_Sphere has moved a set distance
|
||||
8) Verify Follow_Sphere follow distance
|
||||
9) Log results
|
||||
10) Exit Game Mode
|
||||
11) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as math
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = 0.1
|
||||
TIMEOUT = 5
|
||||
INITIAL_OFFSET = 1
|
||||
REQUIRED_MOVEMENT = 2
|
||||
FIXED_TIME_STEP = 0.05 # must be changed in level as well
|
||||
LEAD_SPHERE_VELOCITY = 10.0
|
||||
FINAL_OFFSET = (LEAD_SPHERE_VELOCITY * FIXED_TIME_STEP) + INITIAL_OFFSET
|
||||
OFFSET_TOLERANCE = FINAL_OFFSET * 0.25
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
|
||||
self.initial_position = self.position
|
||||
self.final_position = None
|
||||
# Validate Entities
|
||||
found = Tests.__dict__[self.name.lower() + "_found"]
|
||||
Report.critical_result(found, self.id.isValid())
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
def set_velocity(self, x_velocity, y_velocity, z_velocity):
|
||||
velocity = math.Vector3(x_velocity, y_velocity, z_velocity)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, velocity)
|
||||
|
||||
def moved_enough(self):
|
||||
current_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
return abs(self.initial_position.x - current_position.x) >= REQUIRED_MOVEMENT
|
||||
|
||||
def report_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 check_relative_position(lead_sphere_position, follow_sphere_position, offset):
|
||||
return (
|
||||
abs((lead_sphere_position.x - follow_sphere_position.x) - offset) < OFFSET_TOLERANCE
|
||||
and abs(lead_sphere_position.y - follow_sphere_position.y) < FLOAT_THRESHOLD
|
||||
and abs(lead_sphere_position.z - follow_sphere_position.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
def velocity_zero(sphere_velocity):
|
||||
return (
|
||||
abs(sphere_velocity.x) < FLOAT_THRESHOLD
|
||||
and abs(sphere_velocity.y) < FLOAT_THRESHOLD
|
||||
and abs(sphere_velocity.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
def velocity_valid(lead_sphere_velocity):
|
||||
return (
|
||||
lead_sphere_velocity.x == LEAD_SPHERE_VELOCITY
|
||||
and abs(lead_sphere_velocity.y) < FLOAT_THRESHOLD
|
||||
and abs(lead_sphere_velocity.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C14902097_ScriptCanvas_PreUpdateEvent")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and validate entities
|
||||
lead_sphere = Entity("Lead_Sphere")
|
||||
follow_sphere = Entity("Follow_Sphere")
|
||||
|
||||
# 4) Validate Spheres are not moving
|
||||
Report.critical_result(
|
||||
Tests.no_movement, velocity_zero(lead_sphere.velocity) and velocity_zero(follow_sphere.velocity)
|
||||
)
|
||||
|
||||
# 5) Check Position of Sphere
|
||||
Report.result(
|
||||
Tests.initial_position,
|
||||
check_relative_position(lead_sphere.initial_position, follow_sphere.initial_position, INITIAL_OFFSET),
|
||||
)
|
||||
|
||||
# 6) Start moving sphere and check that it acts correctly
|
||||
lead_sphere.set_velocity(LEAD_SPHERE_VELOCITY, 0.0, 0.0)
|
||||
Report.result(Tests.lead_sphere_velocity, velocity_valid(lead_sphere.velocity))
|
||||
|
||||
# 7) Wait until Lead_Sphere has moved a set distance
|
||||
Report.result(Tests.spheres_moving, helper.wait_for_condition(lead_sphere.moved_enough, TIMEOUT))
|
||||
|
||||
# 8) Verify Follow_Sphere follow distance
|
||||
lead_sphere.final_position = lead_sphere.position
|
||||
follow_sphere.final_position = follow_sphere.position
|
||||
|
||||
Report.result(
|
||||
Tests.follow_condition_true, check_relative_position(lead_sphere.final_position, follow_sphere.final_position, FINAL_OFFSET)
|
||||
)
|
||||
|
||||
# 9) Log results
|
||||
lead_sphere.report_values()
|
||||
follow_sphere.report_values()
|
||||
|
||||
Report.info("Offset:" + str(FINAL_OFFSET))
|
||||
|
||||
# 10) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14902097_ScriptCanvas_PreUpdateEvent)
|
||||
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C14902098
|
||||
# Test Case Title : Check that force region simulation with PostPhysicsUpdate works independently from rendering tick
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14902098
|
||||
|
||||
|
||||
# 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 is found", "Force Region is not found")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
class LogLines:
|
||||
"""
|
||||
These lines are added to the expected lines in the test suite.
|
||||
"""
|
||||
|
||||
expected_lines = [
|
||||
"OnTick Event: The Sphere position did not change from previous position",
|
||||
"OnTick Event: The Sphere position changed from previous position",
|
||||
"OnPostPhysicsSubtick Event: The Sphere position changed from previous position",
|
||||
]
|
||||
unexpected_lines = ["OnPostPhysicsSubtick Event: The Sphere position did not change from previous position"]
|
||||
|
||||
|
||||
def C14902098_ScriptCanvas_PostPhysicsUpdate():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Check that force region simulation with PostPhysicsUpdate works independently from rendering tick.
|
||||
|
||||
Level Description:
|
||||
A Sphere is placed inside a Force Region. The "Fixed Time Step" in PhysX Configuration is set to 0.05.
|
||||
ForceRegion (entity) - Entity with PhysX Collider, PhysX Force Region (World Space force with magnitude 5.0) and
|
||||
Box Shape components
|
||||
Sphere (entity) - Entity with PhysX Rigid Body, PhysX Collider, Mesh and 2 Script Canvas components
|
||||
Script Canvas:
|
||||
onpostphysicsupdate - The script checks the position of the sphere on every On Post Physics Update event and prints
|
||||
debug statements as per the position of the sphere relative to its previous position.
|
||||
ontick - The script checks the position of the sphere on every On Tick event and prints
|
||||
debug statements as per the position of the sphere relative to its previous position.
|
||||
|
||||
Expected Behavior:
|
||||
The position of the sphere needs to be changed relative to its previous position for every
|
||||
OnPostPhysicsSubtick event.
|
||||
The position of the sphere sometimes change and sometimes remains in the same position as before
|
||||
for OnTick event.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Wait for WAIT_TIME for the events to occur
|
||||
5) Exit game mode
|
||||
6) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
# Constants
|
||||
WAIT_TIME = 0.5
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C14902098_ScriptCanvas_PostPhysicsUpdate")
|
||||
|
||||
# 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("ForceRegion")
|
||||
Report.critical_result(Tests.find_force_region, force_region_id.IsValid())
|
||||
|
||||
# 4) Wait for WAIT_TIME for the events to occur
|
||||
general.idle_wait(WAIT_TIME)
|
||||
|
||||
# 5) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14902098_ScriptCanvas_PostPhysicsUpdate)
|
||||
@@ -0,0 +1,134 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C14976307
|
||||
# Test Case Title : Check that Set Gravity Enabled works on an entity with gravity that starts as disabled
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/14976307
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_entities = ("Entities are found", "Entities are not found")
|
||||
gravity_initially_disabled = ("Gravity was initially disabled", "Gravity was initially enabled")
|
||||
gravity_enabled = ("Enabled gravity successfully", "Failed to enable gravity")
|
||||
collision_occured = ("Sphere collided with terrain", "Sphere did not collide with terrain")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C14976307_Gravity_SetGravityWorks():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Check that Set Gravity Enabled works on an entity with gravity that starts as disabled
|
||||
|
||||
Level Description:
|
||||
Terrain (entity) - Terrain entity is created in the level
|
||||
Sphere (entity) - Entity with PhysX rigid body, mesh and collider with gravity disabled placed above
|
||||
the terrain
|
||||
|
||||
Expected Behavior:
|
||||
After 5 seconds, when SetGravity is called, the entity falls to the ground
|
||||
We are checking if entities are valid and enabling the gravity after 5 seconds in game mode to check if ball
|
||||
falls on the terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Gravity check for entity
|
||||
5) Enabling gravity after 5 seconds
|
||||
6) Adding collision handlers for terrain
|
||||
7) Checking if the object collides with terrain
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 3.0
|
||||
WAIT_TIME = 5.0
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C14976307_Gravity_SetGravityWorks")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
Report.critical_result(Tests.find_entities, terrain_id.IsValid() and sphere_id.IsValid())
|
||||
|
||||
sphere_gravity_enabled = False
|
||||
class Sphere:
|
||||
sphere_collision_occured = False
|
||||
|
||||
# 4) Gravity check for entities
|
||||
sphere_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", sphere_id)
|
||||
Report.result(Tests.gravity_initially_disabled, not sphere_gravity_enabled)
|
||||
|
||||
# 5) Adding collision handlers for terrain
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(sphere_id):
|
||||
Report.info("Sphere collided with the terrain")
|
||||
Sphere.sphere_collision_occured = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(terrain_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
# 6) Enabling gravity after 5 seconds
|
||||
general.idle_wait(WAIT_TIME)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", sphere_id, True)
|
||||
sphere_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", sphere_id)
|
||||
Report.result(Tests.gravity_enabled, sphere_gravity_enabled)
|
||||
|
||||
# 7) Checking if the object collides with terrain
|
||||
helper.wait_for_condition(lambda: Sphere.sphere_collision_occured, TIME_OUT)
|
||||
Report.result(Tests.collision_occured, Sphere.sphere_collision_occured)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14976307_Gravity_SetGravityWorks)
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test Case
|
||||
# ID : C14976308
|
||||
# Title : Verify that SetKinematicTarget on PhysX rigid body updates transform for kinematic entities and vice versa
|
||||
# URL : https://testrail.agscollab.com/index.php?/cases/view/14976308
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
sphere_found_valid = ("Sphere found and validated", "Sphere not found and validated")
|
||||
kinematic_target_found_valid = ("Kinematic_Target found and validated", "Kinematic_Target not found and validated")
|
||||
transform_target_found_valid = ("Transform_Target found and validated", "Transform_Target not found and validated")
|
||||
signal_found_valid = ("Signal found and validated", "Signal not found and validated")
|
||||
sphere_gravity_disabled = ("Gravity is disabled on Sphere", "Gravity is not disabled on Sphere")
|
||||
sphere_kinematic = ("Sphere is kinematic", "Sphere is not kinematic")
|
||||
entity_translations_differ = ("Each entity has a different initial translation", "Each entity does not have a different initial translation")
|
||||
entity_rotations_differ = ("Each entity has a different initial rotation", "Each entity does not have a different initial rotation")
|
||||
entity_scales_differ = ("Each entity has a different initial scale", "Each entity does not have a different initial scale")
|
||||
sphere_translation_1_valid = ("Set Kinematic Target updated Sphere's translation to the new value", "Set Kinematic Target failed to update Sphere's translation to the new value")
|
||||
sphere_rotation_1_valid = ("Set Kinematic Target updated Sphere's rotation to the new value", "Set Kinematic Target failed to update Sphere's rotation to the new value")
|
||||
sphere_transform_2_valid = ("Set World Transform updated Sphere's transform to the new value", "Set World Transform failed to update Sphere's transform to the new value")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C14976308_ScriptCanvas_SetKinematicTargetTransform():
|
||||
"""
|
||||
Summary:
|
||||
This script runs an automated test to verify the results of two Script Canvas nodes acting on a kinematic rigid
|
||||
body:
|
||||
1) Set Kinematic Target will update the kinematic rigid body's translation and rotation to those of the transform
|
||||
which the node passes to it.
|
||||
2) Set World Transform will update a kinematic rigid body's transform to the transform which the node passes to it.
|
||||
|
||||
Level Description:
|
||||
Entity: Sphere: PhysX Rigid Body, PhysX Collider with sphere shape, and Mesh with sphere asset
|
||||
Gravity disabled, kinematic enabled
|
||||
Translate (79.0, 39.0, 34.0) in meters
|
||||
Rotate (1.0, 2.0, 3.0) in degrees
|
||||
Scale (1.0, 1.0, 1.0)
|
||||
Entity: Kinematic_Target: Mesh with cube asset
|
||||
Translate (48.0, 56.0, 36.0) in meters
|
||||
Rotate (11.0, 12.0, 13.0) in degrees
|
||||
Scale (1.1, 1.2, 1.3)
|
||||
Entity: Transform_Target: Mesh with cube asset
|
||||
Translate (72.0, 80.0, 38.0) in meters
|
||||
Rotate (21.0, 22.0, 23.0) in degrees
|
||||
Scale (2.1, 2.2, 2.3)
|
||||
Entity: Signal: Start inactive, attached Script Canvas asset which will cause the following:
|
||||
On Signal Activated will Set Kinematic Target on Sphere to Kinematic_Target's transform
|
||||
On Signal Deactivated will Set World Transform on Sphere to Transform_Target's transform
|
||||
Other than the signal, the entities all have different translations, rotations, and scales.
|
||||
|
||||
Expected behavior:
|
||||
When the script activates Signal, Sphere's translation and rotation will update to those of Kinematic_Target. When
|
||||
the script deactivates Signal, Sphere's transform will update to that of Transform_Target.
|
||||
NOTE: There is a known bug (LY-107723) which causes the rotation to update to a value that is not sufficiently close
|
||||
to the expected result when using Set Kinematic Target which will cause the test to fail:
|
||||
https://jira.agscollab.com/browse/LY-107723
|
||||
|
||||
Test Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Retrieve and validate entities
|
||||
3) Check that gravity is disabled on the sphere
|
||||
4) Check that the sphere is kinematic
|
||||
5) Check that each entity except the signal has a different initial translation
|
||||
6) Check that each entity except the signal has a different initial rotation
|
||||
7) Check that each entity except the signal has a different initial scale
|
||||
8) Activate the signal entity to trigger the Set Kinematic Target node in Script Canvas
|
||||
9) Wait one frame and check that the sphere's translation has updated to that of Kinematic_Target
|
||||
10) Check that the sphere's rotation has updated to that of Kinematic_Target
|
||||
11) Deactivate the signal entity to trigger the Set World Transform node in Script Canvas
|
||||
12) Check that the sphere's transform has updated to that of Transform_Target
|
||||
13) Exit game mode and close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.components
|
||||
import azlmbr.math
|
||||
import azlmbr.physics
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
import itertools
|
||||
|
||||
class Entity:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.found_valid_test = Tests.__dict__[self.name.lower() + "_found_valid"]
|
||||
Report.critical_result(self.found_valid_test, self.id.IsValid())
|
||||
|
||||
def is_gravity_disabled(self):
|
||||
return not azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
|
||||
def is_kinematic(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsKinematic", self.id)
|
||||
|
||||
def get_world_transform(self):
|
||||
transform = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", self.id)
|
||||
Report.info_vector3(transform.position, "{}'s position:".format(self.name))
|
||||
Report.info_vector3(transform.basisX, "{}'s basisX:".format(self.name))
|
||||
Report.info_vector3(transform.basisY, "{}'s basisY:".format(self.name))
|
||||
Report.info_vector3(transform.basisZ, "{}'s basisZ:".format(self.name))
|
||||
return transform
|
||||
|
||||
def get_world_translation(self):
|
||||
translation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
Report.info_vector3(translation, "{}'s Translation:".format(self.name))
|
||||
return translation
|
||||
|
||||
def get_world_rotation(self):
|
||||
rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", self.id)
|
||||
Report.info_vector3(rotation, "{}'s Rotation:".format(self.name))
|
||||
return rotation
|
||||
|
||||
def get_world_scale(self):
|
||||
scale = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldScale", self.id)
|
||||
Report.info_vector3(scale, "{}'s Scale:".format(self.name))
|
||||
return scale
|
||||
|
||||
def transform_matches(self, other):
|
||||
return self.get_world_transform().Equal(other.get_world_transform())
|
||||
|
||||
def translation_matches(self, other):
|
||||
return self.get_world_translation().Equal(other.get_world_translation())
|
||||
|
||||
def rotation_matches(self, other):
|
||||
return self.get_world_rotation().Equal(other.get_world_rotation())
|
||||
|
||||
def scale_matches(self, other):
|
||||
return self.get_world_scale().Equal(other.get_world_scale())
|
||||
|
||||
def entities_translations_differ(entities):
|
||||
for entity_a, entity_b in itertools.combinations(entities, 2):
|
||||
if entity_a.translation_matches(entity_b):
|
||||
return False
|
||||
return True
|
||||
|
||||
def entities_rotations_differ(entities):
|
||||
for entity_a, entity_b in itertools.combinations(entities, 2):
|
||||
if entity_a.rotation_matches(entity_b):
|
||||
return False
|
||||
return True
|
||||
|
||||
def entities_scales_differ(entities):
|
||||
for entity_a, entity_b in itertools.combinations(entities, 2):
|
||||
if entity_a.scale_matches(entity_b):
|
||||
return False
|
||||
return True
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C14976308_ScriptCanvas_SetKinematicTargetTransform")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve and validate entities
|
||||
sphere = Entity("Sphere")
|
||||
kinematic_target = Entity("Kinematic_Target")
|
||||
transform_target = Entity("Transform_Target")
|
||||
signal = Entity("Signal")
|
||||
non_signal_entities = (sphere, kinematic_target, transform_target)
|
||||
|
||||
# 3) Check that gravity is disabled on the sphere
|
||||
Report.critical_result(Tests.sphere_gravity_disabled, sphere.is_gravity_disabled())
|
||||
|
||||
# 4) Check that the sphere is kinematic
|
||||
Report.critical_result(Tests.sphere_kinematic, sphere.is_kinematic())
|
||||
|
||||
# 5) Check that each entity except the signal has a different initial translation
|
||||
Report.critical_result(Tests.entity_translations_differ, entities_translations_differ(non_signal_entities))
|
||||
|
||||
# 6) Check that each entity except the signal has a different initial rotation
|
||||
Report.critical_result(Tests.entity_rotations_differ, entities_rotations_differ(non_signal_entities))
|
||||
|
||||
# 7) Check that each entity except the signal has a different initial scale
|
||||
Report.critical_result(Tests.entity_scales_differ, entities_scales_differ(non_signal_entities))
|
||||
|
||||
# 8) Activate the signal entity to trigger the Set Kinematic Target node in Script Canvas
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", signal.id)
|
||||
|
||||
# 9) Wait one frame and check that the sphere's translation has updated to that of Kinematic_Target
|
||||
general.idle_wait_frames(1)
|
||||
Report.result(Tests.sphere_translation_1_valid, sphere.translation_matches(kinematic_target))
|
||||
|
||||
# 10) Check that the sphere's rotation has updated to that of Kinematic_Target
|
||||
# NOTE: This test currently fails due to a known bug (LY-107723)
|
||||
Report.result(Tests.sphere_rotation_1_valid, sphere.rotation_matches(kinematic_target))
|
||||
|
||||
# 11) Deactivate the signal entity to trigger the Set World Transform node in Script Canvas
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", signal.id)
|
||||
|
||||
# 12) Check that the sphere's transform has updated to that of Transform_Target
|
||||
Report.result(Tests.sphere_transform_2_valid, sphere.transform_matches(transform_target))
|
||||
|
||||
# 13) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C14976308_ScriptCanvas_SetKinematicTargetTransform)
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C15096732
|
||||
# Test Case Title : Verify Default material library works across different levels
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096732
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
# Game Mode 2
|
||||
enter_game_mode_2 = ("Entered game mode 2", "Failed to enter game mode 2")
|
||||
sphere_found_2 = ("Test 2: Sphere was found", "Test 2: Sphere was not found")
|
||||
terrain_found_2 = ("Test 2: Terrrain Entity found", "Test 2: Terrain Entity was not found")
|
||||
trigger_found_2 = ("Test 2: trigger found", "Test 2: trigger not found")
|
||||
sphere_initial_position_2 = ("Test 2: Sphere initial position valid", "Test 2: Sphere initial position not valid")
|
||||
sphere_initial_velocity_2 = ("Test 2: Sphere initial velocity valid", "Test 2: Sphere initial velocity not valid")
|
||||
sphere_collision_2 = ("Test 2: Sphere collided with Terrain", "Test 2: Sphere did not collide")
|
||||
exit_game_mode_2 = ("Exited game mode 2", "Couldn't exit game mode 2")
|
||||
# Game Mode 3
|
||||
enter_game_mode_3 = ("Entered game mode 3", "Failed to enter game mode 3")
|
||||
sphere_found_3 = ("Test 3: Sphere was found", "Test 3: Sphere was not found")
|
||||
terrain_found_3 = ("Test 3: Terrrain Entity found", "Test 3: Terrain Entity was not found")
|
||||
trigger_found_3 = ("Test 3: trigger found", "Test 3: trigger not found")
|
||||
sphere_initial_position_3 = ("Test 3: Sphere initial position valid", "Test 3: Sphere initial position not valid")
|
||||
sphere_initial_velocity_3 = ("Test 3: Sphere initial velocity valid", "Test 3: Sphere initial velocity not valid")
|
||||
sphere_collision_3 = ("Test 3: Sphere collided with Terrain", "Test 3: Sphere did not collide")
|
||||
exit_game_mode_3 = ("Exited game mode 3", "Couldn't exit game mode 3")
|
||||
|
||||
# Test Verification
|
||||
levels_start_equal = ("Both levels are the same", "Both levels are not the same")
|
||||
material_library_switch = ("Library switch updated the sphere", "Library switch didn't update sphere")
|
||||
levels_stay_equal = ("Both levels are still the same", "Both levels are not the same post_change")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C15096732_Material_DefaultLibraryUpdatedAcrossLevels_after():
|
||||
"""
|
||||
Summary: Verify Default material library works across different levels, this is the second stage to the test.
|
||||
The reload was required for the editor to pick up changes in default material library in the
|
||||
default.physxconfiguration file. After the tests are run this script will load the data from the previous
|
||||
script and compare it to the two new tests to see if changing the default material library progpogated
|
||||
correctly. C15096732_Material_DefaultLibraryUpdatedAcrossLevels_b.physmaterial is the default material
|
||||
file for these two tests.
|
||||
|
||||
Level Description: There are two levels indexed 1 and 2 each are completely identical to the other.
|
||||
sphere - Placed between trigger and terrain with velocity in the negative z direction; has physx rigid body,
|
||||
physx collider with sphere shape and useful_material_0 assigned (before change in default material library)
|
||||
and sphere shape
|
||||
trigger - A trigger sitting above the sphere and terrain; has physx rigid body, physx collider with box
|
||||
shape, and box shape
|
||||
terrain - Terrain placeholder with transform inline with default terrain height; has physx terrain
|
||||
component with default characteristics
|
||||
|
||||
physxconfiguration files: The change in default material library is achieved by launching the editor twice and
|
||||
overriding the default.physxconfiguration file with files that are nearly identical other than having
|
||||
different default material libraries
|
||||
|
||||
Materials: no_bounce_0 and no_bounce_1 are set so that they do not bounce from the terrain. global
|
||||
Default and bounce_0 and bounce_1 are set so that a bounce does occur. During the test the sphere first has
|
||||
a no_bounce material applied after the change in default material library to one with the bounce material the
|
||||
sphere has the global Default material applied. The bounce materials exist to avoid any current or future
|
||||
issues with an empty material library.
|
||||
|
||||
Test run explanation:
|
||||
Test 0: Collect baseline for default material library in the first level
|
||||
Test 1: Collect baseline for default material library in the second level
|
||||
Test 2: Collect resulting data for changed material library in the first level
|
||||
Test 3: Collect resulting data for changed material library in the second level
|
||||
|
||||
Expected Behavior: For the two test run by this script the ball will bounce from the terrain and hit the trigger
|
||||
as the material for spheres is now the global Default material.
|
||||
|
||||
Iterated Game Mode steps:
|
||||
1) Open the correct level is open
|
||||
2) Enter Game Mode
|
||||
3) Create and Verify Entities
|
||||
4) Wait for Sphere collision with Terrain Entity
|
||||
5) Allow time to hit trigger
|
||||
6) Log Final Values
|
||||
7) Exit Game Mode
|
||||
|
||||
Test Steps:
|
||||
1) Create Test Objects
|
||||
2) Run Game Mode steps once for each test
|
||||
3) Read results from local tmp file
|
||||
4) Validate test wide results
|
||||
5) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as math
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = 0.0001
|
||||
TIMEOUT = 2.0
|
||||
INITIAL_VELOCITY = math.Vector3(0.0, 0.0, -10.0)
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name, index):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.index = index
|
||||
# ID validation
|
||||
self.found = Tests.__dict__["{}_found_{}".format(self.name, index)]
|
||||
Report.critical_result(self.found, self.id.IsValid())
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
class Material_Test:
|
||||
def __init__(self, test_index, level):
|
||||
self.test_index = test_index
|
||||
self.level = level
|
||||
self.entity_list = None
|
||||
# Setting Flags
|
||||
self.terrain_collision = False
|
||||
self.trigger_triggered = False
|
||||
|
||||
def set_handlers(self):
|
||||
trigger = self.entity_list[2]
|
||||
# Set handler for collision
|
||||
self.handler_0 = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler_0.connect(self.entity_list[0].id)
|
||||
self.handler_0.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
# Set handler for trigger
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(trigger.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
if args[0].equal(self.entity_list[1].id):
|
||||
self.terrain_collision = True
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
if args[0].equal(self.entity_list[0].id):
|
||||
self.trigger_triggered = True
|
||||
|
||||
def check_sphere_initial_position(self, position_valid):
|
||||
initial_position = Tests.__dict__["sphere_initial_position_{}".format(self.test_index)]
|
||||
Report.critical_result(initial_position, position_valid)
|
||||
|
||||
def check_sphere_initial_velocity(self):
|
||||
initial_velocity_string = Tests.__dict__["sphere_initial_velocity_{}".format(self.test_index)]
|
||||
Report.critical_result(initial_velocity_string, self.entity_list[0].velocity.IsClose(INITIAL_VELOCITY, 0.1))
|
||||
|
||||
def check_sphere_collision(self):
|
||||
collision = Tests.__dict__["sphere_collision_{}".format(self.test_index)]
|
||||
Report.result(collision, self.terrain_collision)
|
||||
|
||||
def default_material_library_changed_as_expected(velocity_list, hit_trigger_list):
|
||||
hit_trigger_change = not hit_trigger_list[1] and hit_trigger_list[2]
|
||||
velocity_change_valid = (
|
||||
abs(velocity_list[1].x - velocity_list[2].x) < FLOAT_THRESHOLD
|
||||
and abs(velocity_list[1].y - velocity_list[2].y) < FLOAT_THRESHOLD
|
||||
and velocity_list[1].z <= velocity_list[2].z
|
||||
)
|
||||
|
||||
return velocity_change_valid and hit_trigger_change
|
||||
|
||||
def compare_level_baseline(velocity_list, hit_trigger_list):
|
||||
velocities_valid = (
|
||||
abs(velocity_list[0].z - velocity_list[1].z) < FLOAT_THRESHOLD
|
||||
and abs(velocity_list[0].y - velocity_list[1].y) < FLOAT_THRESHOLD
|
||||
and abs(velocity_list[0].x - velocity_list[1].x) < FLOAT_THRESHOLD
|
||||
)
|
||||
hit_trigger_correct = hit_trigger_list[0] == hit_trigger_list[1]
|
||||
|
||||
return velocities_valid and hit_trigger_correct
|
||||
|
||||
def levels_coinsistent_after_modification(velocity_list, hit_trigger_list):
|
||||
velocities_valid = (
|
||||
abs(velocity_list[2].z - velocity_list[3].z) < 0.01
|
||||
and abs(velocity_list[2].y - velocity_list[3].y) < FLOAT_THRESHOLD
|
||||
and abs(velocity_list[2].x - velocity_list[3].x) < FLOAT_THRESHOLD
|
||||
)
|
||||
hit_trigger_correct = hit_trigger_list[2] == hit_trigger_list[3]
|
||||
|
||||
return velocities_valid and hit_trigger_correct
|
||||
|
||||
def get_data_from_previous_tests():
|
||||
from ast import literal_eval
|
||||
|
||||
try:
|
||||
with open(
|
||||
os.path.join(
|
||||
os.getcwd(), "AutomatedTesting", "Levels", "Physics", "C15096732_Material_DefaultLibraryUpdatedAcrossLevels", "_last_run_before_change_data.txt"
|
||||
)) as data_file:
|
||||
lines = data_file.readlines()
|
||||
for i, line in enumerate(lines):
|
||||
if i < 2:
|
||||
line = literal_eval(line)
|
||||
lines[i] = math.Vector3(float(line[0]), float(line[1]), float(line[2]))
|
||||
else:
|
||||
lines[i] = line == "True"
|
||||
except Exception as e:
|
||||
Report.info(e)
|
||||
helper.fail_fast("Could not save data of first two tests.")
|
||||
return lines[:2], lines[2:4]
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Create Test Objects
|
||||
test_2 = Material_Test(test_index=2, level=0)
|
||||
test_3 = Material_Test(test_index=3, level=1)
|
||||
test_list = [test_2, test_3]
|
||||
|
||||
# 2) Run Game Mode steps once for each test
|
||||
for test in test_list:
|
||||
# 1) Open the correct level is open
|
||||
helper.open_level(
|
||||
"physics",
|
||||
"C15096732_Material_DefaultLibraryUpdatedAcrossLevels.\\{}".format(
|
||||
test.level
|
||||
),
|
||||
)
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.__dict__["enter_game_mode_{}".format(test.test_index)])
|
||||
|
||||
# 3) Create and Verify Entities
|
||||
sphere = Entity("sphere", test.test_index)
|
||||
terrain = Entity("terrain", test.test_index)
|
||||
trigger = Entity("trigger", test.test_index)
|
||||
test.entity_list = [sphere, terrain, trigger]
|
||||
|
||||
position_valid = terrain.position.z < sphere.position.z < trigger.position.z
|
||||
test.check_sphere_initial_position(position_valid)
|
||||
test.check_sphere_initial_velocity()
|
||||
|
||||
# 4) Wait for Sphere collision with Terrain Entity
|
||||
test.set_handlers()
|
||||
helper.wait_for_condition(lambda: test.terrain_collision, TIMEOUT)
|
||||
test.check_sphere_collision()
|
||||
|
||||
# 5) Allow time for Sphere to hit trigger
|
||||
helper.wait_for_condition(lambda: test.trigger_triggered, TIMEOUT)
|
||||
|
||||
# 6) Log Final Values
|
||||
test.final_velocity = sphere.velocity
|
||||
|
||||
# 7) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.__dict__["exit_game_mode_{}".format(test.test_index)])
|
||||
|
||||
# 3) Verify that logged attributes show both levels are the same before and after the change in default material library
|
||||
# and show that there was a change before and after the change in default material library
|
||||
sphere_final_velocities_0, hit_trigger_list_0 = get_data_from_previous_tests()
|
||||
|
||||
sphere_final_velocities = sphere_final_velocities_0 + [test.final_velocity for test in test_list]
|
||||
hit_trigger_list = hit_trigger_list_0 + [test.trigger_triggered for test in test_list]
|
||||
Report.result(Tests.levels_start_equal, compare_level_baseline(sphere_final_velocities, hit_trigger_list))
|
||||
Report.result(
|
||||
Tests.material_library_switch,
|
||||
default_material_library_changed_as_expected(sphere_final_velocities, hit_trigger_list),
|
||||
)
|
||||
Report.result(
|
||||
Tests.levels_stay_equal, levels_coinsistent_after_modification(sphere_final_velocities, hit_trigger_list)
|
||||
)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15096732_Material_DefaultLibraryUpdatedAcrossLevels_after)
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C15096732
|
||||
# Test Case Title : Verify Default material library works across different levels
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096732
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
# Game Mode 0
|
||||
enter_game_mode_0 = ("Entered game mode 0", "Failed to enter game mode 0")
|
||||
sphere_found_0 = ("Test 0: Sphere was found", "Test 0: Sphere was not found")
|
||||
terrain_found_0 = ("Test 0: Terrrain Entity found", "Test 0: Terrain Entity was not found")
|
||||
trigger_found_0 = ("Test 0: trigger found", "Test 0: trigger not found")
|
||||
sphere_initial_position_0 = ("Test 0: Sphere initial position valid", "Test 0: Sphere initial position not valid")
|
||||
sphere_initial_velocity_0 = ("Test 0: Sphere initial velocity valid", "Test 0: Sphere initial velocity not valid")
|
||||
sphere_collision_0 = ("Test 0: Sphere collided with Terrain", "Test 0: Sphere did not collide")
|
||||
exit_game_mode_0 = ("Exited game mode 0", "Couldn't exit game mode 0")
|
||||
# Game Mode 1
|
||||
enter_game_mode_1 = ("Entered game mode 1", "Failed to enter game mode 1")
|
||||
sphere_found_1 = ("Test 1: Sphere was found", "Test 1: Sphere was not found")
|
||||
terrain_found_1 = ("Test 1: Terrrain Entity found", "Test 1: Terrain Entity was not found")
|
||||
trigger_found_1 = ("Test 1: trigger found", "Test 1: trigger not found")
|
||||
sphere_initial_position_1 = ("Test 1: Sphere initial position valid", "Test 1: Sphere initial position not valid")
|
||||
sphere_initial_velocity_1 = ("Test 1: Sphere initial velocity valid", "Test 1: Sphere initial velocity not valid")
|
||||
sphere_collision_1 = ("Test 1: Sphere collided with Terrain", "Test 1: Sphere did not collide")
|
||||
exit_game_mode_1 = ("Exited game mode 1", "Couldn't exit game mode 1")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C15096732_Material_DefaultLibraryUpdatedAcrossLevels_before():
|
||||
"""
|
||||
Summary: Verify Default material library works across different levels, this is the first stage to the test.
|
||||
After the tests are run this script will save data into a text file and the editor closed.
|
||||
C15096732_Material_DefaultLibraryUpdatedAcrossLevels_a.physmaterial is the default material file for
|
||||
these two tests.
|
||||
|
||||
Level Description: There are two levels indexed 1 and 2 each are completely identical to the other.
|
||||
sphere - Placed between trigger and terrain with velocity in the negative z direction; has physx rigid body,
|
||||
physx collider with sphere shape and useful_material_0 assigned (before change in default material library)
|
||||
and sphere shape
|
||||
trigger - A trigger sitting above the sphere and terrain; has physx rigid body, physx collider with box
|
||||
shape, and box shape
|
||||
terrain - Terrain placeholder with transform inline with default terrain height; has physx terrain
|
||||
component with default characteristics
|
||||
|
||||
physxconfiguration files: The change in default material library is achieved by launching the editor twice and
|
||||
overriding the default.physxconfiguration file with files that are nearly identical other than having
|
||||
different default material libraries
|
||||
|
||||
Materials: no_bounce_0 and no_bounce_1 are set so that they do not bounce from the terrain. global
|
||||
Default and bounce_0 and bounce_1 are set so that a bounce does occur. During the test the sphere first has
|
||||
a no_bounce material applied after the change in default material library to one with the bounce material the
|
||||
sphere has the global Default material applied. The bounce materials exist to avoid any current or future
|
||||
issues with an empty material library.
|
||||
|
||||
Test run explanation:
|
||||
Test 0: Collect baseline for default material library in the first level
|
||||
Test 1: Collect baseline for default material library in the second level
|
||||
Test 2: Collect resulting data for changed material library in the first level
|
||||
Test 3: Collect resulting data for changed material library in the second level
|
||||
|
||||
Expected Behavior: For the two test run by this script the ball will not bounce from the terrain and will
|
||||
not hit the trigger
|
||||
|
||||
Iterated Game Mode steps:
|
||||
1) Open the correct level is open
|
||||
2) Enter Game Mode
|
||||
3) Create and Verify Entities
|
||||
4) Wait for Sphere collision with Terrain Entity
|
||||
5) Allow time to hit trigger
|
||||
6) Log Final Values
|
||||
7) Exit Game Mode
|
||||
|
||||
Test Steps:
|
||||
1) Create Test Objects
|
||||
2) Run Game Mode steps once for each test
|
||||
3) Log results of two steps to a local tmp file
|
||||
4) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as math
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 2.0
|
||||
INITIAL_VELOCITY = math.Vector3(0.0, 0.0, -10.0)
|
||||
INITIAL_VELOCITY_THRESHOLD = 0.1
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name, index):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.index = index
|
||||
# ID validation
|
||||
self.found = Tests.__dict__["{}_found_{}".format(self.name, index)]
|
||||
Report.critical_result(self.found, self.id.IsValid())
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
class Material_Test:
|
||||
def __init__(self, test_index, level):
|
||||
self.test_index = test_index
|
||||
self.level = level
|
||||
self.entity_list = None
|
||||
# Setting Flags
|
||||
self.terrain_collision = False
|
||||
self.trigger_triggered = False
|
||||
|
||||
def set_handlers(self):
|
||||
trigger = self.entity_list[2]
|
||||
# Set handler for collision
|
||||
self.handler_0 = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler_0.connect(self.entity_list[0].id)
|
||||
self.handler_0.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
# Set handler for trigger
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(trigger.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
if args[0].equal(self.entity_list[1].id):
|
||||
self.terrain_collision = True
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
if args[0].Equal(self.entity_list[0].id):
|
||||
self.trigger_triggered = True
|
||||
|
||||
def check_sphere_initial_position(self, position_valid):
|
||||
initial_position = Tests.__dict__["sphere_initial_position_{}".format(self.test_index)]
|
||||
Report.critical_result(initial_position, position_valid)
|
||||
|
||||
def check_sphere_initial_velocity(self):
|
||||
initial_velocity_string = Tests.__dict__["sphere_initial_velocity_{}".format(self.test_index)]
|
||||
Report.critical_result(
|
||||
initial_velocity_string,
|
||||
self.entity_list[0].velocity.IsClose(INITIAL_VELOCITY, INITIAL_VELOCITY_THRESHOLD),
|
||||
)
|
||||
|
||||
def check_sphere_collision(self):
|
||||
collision = Tests.__dict__["sphere_collision_{}".format(self.test_index)]
|
||||
Report.result(collision, self.terrain_collision)
|
||||
|
||||
def save_test_data(data):
|
||||
try:
|
||||
with open(
|
||||
os.path.join(
|
||||
os.getcwd(), "AutomatedTesting", "Levels", "Physics", "C15096732_Material_DefaultLibraryUpdatedAcrossLevels", "_last_run_before_change_data.txt"
|
||||
),"w") as data_file:
|
||||
for data_point in data:
|
||||
data_file.write(str(data_point))
|
||||
data_file.write("\n")
|
||||
except Exception as e:
|
||||
Report.info(e)
|
||||
helper.fail_fast("Could not save data of first two tests.")
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Create Test Objects
|
||||
test_0 = Material_Test(test_index=0, level=0)
|
||||
test_1 = Material_Test(test_index=1, level=1)
|
||||
test_list = [test_0, test_1]
|
||||
|
||||
# 2) Run Game Mode steps once for each test
|
||||
for test in test_list:
|
||||
# 1) Open the correct level is open
|
||||
helper.open_level(
|
||||
"physics",
|
||||
"C15096732_Material_DefaultLibraryUpdatedAcrossLevels.\\{}".format(
|
||||
test.level
|
||||
),
|
||||
)
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.__dict__["enter_game_mode_{}".format(test.test_index)])
|
||||
|
||||
# 3) Create and Verify Entities
|
||||
sphere = Entity("sphere", test.test_index)
|
||||
terrain = Entity("terrain", test.test_index)
|
||||
trigger = Entity("trigger", test.test_index)
|
||||
test.entity_list = [sphere, terrain, trigger]
|
||||
|
||||
position_valid = terrain.position.z < sphere.position.z < trigger.position.z
|
||||
test.check_sphere_initial_position(position_valid)
|
||||
test.check_sphere_initial_velocity()
|
||||
|
||||
# 4) Wait for Sphere collision with Terrain Entity
|
||||
test.set_handlers()
|
||||
helper.wait_for_condition(lambda: test.terrain_collision, TIMEOUT)
|
||||
test.check_sphere_collision()
|
||||
|
||||
# 5) Allow time for Sphere to hit trigger
|
||||
helper.wait_for_condition(lambda: test.trigger_triggered, TIMEOUT)
|
||||
|
||||
# 6) Log Final Values
|
||||
test.final_velocity = sphere.velocity
|
||||
|
||||
# 7) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.__dict__["exit_game_mode_{}".format(test.test_index)])
|
||||
|
||||
# 3) Log results of two steps to a local tmp file
|
||||
sphere_final_velocities = [
|
||||
[test.final_velocity.x, test.final_velocity.y, test.final_velocity.z] for test in test_list
|
||||
]
|
||||
hit_trigger_list = [test.trigger_triggered for test in test_list]
|
||||
|
||||
save_test_data(sphere_final_velocities + hit_trigger_list)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15096732_Material_DefaultLibraryUpdatedAcrossLevels_before)
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C15096735
|
||||
# Test Case Title : Verify that default material library works consistently across all systems that use it
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096735
|
||||
|
||||
|
||||
# fmt:off
|
||||
class Tests:
|
||||
# *** Universal test tuples ***
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
no_time_out = ("No time out detected", "The test timed out")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
# *** Terrain test tuples ***
|
||||
terrain_rubber_result_found = ("Terrain's Rubber Result Entity Found", "Terrain's Rubber Result Entity NOT Found")
|
||||
terrain_concrete_result_found = ("Terrain's Concrete Result Entity Found", "Terrain's Concrete Result Entity NOT Found")
|
||||
terrain_rubber_result_stopped = ("Terrain's Rubber Result Entity Stopped", "Terrain's Rubber Result Entity DID NOT Stop")
|
||||
terrain_concrete_result_stopped = ("Terrain's Concrete Result Entity Stopped", "Terrain's Concrete Result Entity DID NOT Stop")
|
||||
terrain_found = ("Terrain Entity Found", "Terrain Entity NOT Found")
|
||||
terrain_expected_collisions = ("Terrain Entity Collisions Were Expected", "Terrain Entity DID NOT Collide With All Expected Entities")
|
||||
terrain_trigger_rubber_high_found = ("Terrain's Rubber High Trigger Found", "Terrain's Rubber High Trigger NOT Found")
|
||||
terrain_trigger_rubber_high_expected_collision = ("Terrain's Rubber High Trigger Collision Was As Expected", "Terrain's Rubber High Trigger Collision Was Not As Expected")
|
||||
terrain_trigger_rubber_low_found = ("Terrain's Rubber Low Trigger Found", "Terrain's Rubber Low Trigger NOT Found")
|
||||
terrain_trigger_rubber_low_expected_collision = ("Terrain's Rubber Low Trigger Collision Was As Expected", "Terrain's Rubber Low Trigger Collision Was Not As Expected")
|
||||
terrain_trigger_concrete_high_found = ("Terrain's Concrete High Trigger Found", "Terrain's Concrete High Trigger NOT Found")
|
||||
terrain_trigger_concrete_high_expected_collision = ("Terrain's Concrete High Trigger Collision Was As Expected", "Terrain's Concrete High Trigger Collision Was Not As Expected")
|
||||
terrain_trigger_concrete_low_found = ("Terrain's Concrete Low Trigger Found", "Terrain's Concrete Low Trigger NOT Found")
|
||||
terrain_trigger_concrete_low_expected_collision = ("Terrain's Concrete Low Trigger Collision Was As Expected", "Terrain's Concrete Low Trigger Collision Was Not As Expected")
|
||||
|
||||
# *** Platform test tuples ***
|
||||
platform_rubber_result_found = ("Platform's Rubber Result Entity Found", "Platform's Rubber Result Entity NOT Found")
|
||||
platform_concrete_result_found = ("Platform's Concrete Result Entity Found", "Platform's Concrete Result Entity NOT Found")
|
||||
platform_rubber_result_stopped = ("Platform's Rubber Result Entity Stopped", "Platform's Rubber Result Entity DID NOT Stop")
|
||||
platform_concrete_result_stopped = ("Platform's Concrete Result Entity Stopped", "Platform's Concrete Result Entity DID NOT Stop")
|
||||
platform_rubber_found = ("Platform Rubber Test Entity Found", "Platform Rubber Test Entity NOT Found")
|
||||
platform_rubber_expected_collisions = ("Platform Rubber Test Entity Collisions Were Expected", "Platform Rubber Test Entity DID NOT Collide With All Expected Entities")
|
||||
platform_concrete_found = ("Platform Concrete Test Entity Found", "Platform Concrete Test Entity NOT Found")
|
||||
platform_concrete_expected_collisions = ("Platform Concrete Test Entity Collisions Were Expected", "Platform Concrete Test Entity DID NOT Collide With All Expected Entities")
|
||||
platform_trigger_rubber_high_found = ("Platform's Rubber High Trigger Found", "Platform's Rubber High Trigger NOT Found")
|
||||
platform_trigger_rubber_high_expected_collision = ("Platform's Rubber High Trigger Collision Was As Expected", "Platform's Rubber High Trigger Collision Was Not As Expected")
|
||||
platform_trigger_rubber_low_found = ("Platform's Rubber Low Trigger Found", "Platform's Rubber Low Trigger NOT Found")
|
||||
platform_trigger_rubber_low_expected_collision = ("Platform's Rubber Low Trigger Collision Was As Expected", "Platform's Rubber Low Trigger Collision Was Not As Expected")
|
||||
platform_trigger_concrete_high_found = ("Platform's Concrete High Trigger Found", "Platform's Concrete High Trigger NOT Found")
|
||||
platform_trigger_concrete_high_expected_collision = ("Platform's Concrete High Trigger Collision Was As Expected", "Platform's Concrete High Trigger Collision Was Not As Expected")
|
||||
platform_trigger_concrete_low_found = ("Platform's Concrete Low Trigger Found", "Platform's Concrete Low Trigger NOT Found")
|
||||
platform_trigger_concrete_low_expected_collision = ("Platform's Concrete Low Trigger Collision Was As Expected", "Platform's Concrete Low Trigger Collision Was Not As Expected")
|
||||
|
||||
# *** Controller test tuples ***
|
||||
controller_rubber_result_found = ("Controller's Rubber Result Entity Found", "Controller's Rubber Result Entity NOT Found")
|
||||
controller_concrete_result_found = ("Controller's Concrete Result Entity Found", "Controller's Concrete Result Entity NOT Found")
|
||||
controller_rubber_result_stopped = ("Controller's Rubber Result Entity Stopped", "Controller's Rubber Result Entity DID NOT Stop")
|
||||
controller_concrete_result_stopped = ("Controller's Concrete Result Entity Stopped", "Controller's Concrete Result Entity DID NOT Stop")
|
||||
controller_rubber_found = ("Controller Rubber Test Entity Found", "Controller Rubber Test Entity NOT Found")
|
||||
controller_rubber_expected_collisions = ("Controller Rubber Test Entity Collisions Were Expected", "Controller Rubber Test Entity DID NOT Collide With All Expected Entities")
|
||||
controller_concrete_found = ("Controller Concrete Test Entity Found", "Controller Concrete Test Entity NOT Found")
|
||||
controller_concrete_expected_collisions = ("Controller Concrete Test Entity Collisions Were Expected", "Controller Concrete Test Entity DID NOT Collide With All Expected Entities")
|
||||
controller_trigger_rubber_high_found = ("Controller's Rubber High Trigger Found", "Controller's Rubber High Trigger NOT Found")
|
||||
controller_trigger_rubber_high_expected_collision = ("Controller's Rubber High Trigger Collision Was As Expected", "Controller's Rubber High Trigger Collision Was Not As Expected")
|
||||
controller_trigger_rubber_low_found = ("Controller's Rubber Low Trigger Found", "Controller's Rubber Low Trigger NOT Found")
|
||||
controller_trigger_rubber_low_expected_collision = ("Controller's Rubber Low Trigger Collision Was As Expected", "Controller's Rubber Low Trigger Collision Was Not As Expected")
|
||||
controller_trigger_concrete_high_found = ("Controller's Concrete High Trigger Found", "Controller's Concrete High Trigger NOT Found")
|
||||
controller_trigger_concrete_high_expected_collision = ("Controller's Concrete High Trigger Collision Was As Expected", "Controller's Concrete High Trigger Collision Was Not As Expected")
|
||||
controller_trigger_concrete_low_found = ("Controller's Concrete Low Trigger Found", "Controller's Concrete Low Trigger NOT Found")
|
||||
controller_trigger_concrete_low_expected_collision = ("Controller's Concrete Low Trigger Collision Was As Expected", "Controller's Concrete Low Trigger Collision Was Not As Expected")
|
||||
|
||||
# *** Ragdoll test tuples ***
|
||||
ragdoll_rubber_result_found = ("Ragdoll's Rubber Result Entity Found", "Ragdoll's Rubber Result Entity NOT Found")
|
||||
ragdoll_concrete_result_found = ("Ragdoll's Concrete Result Entity Found", "Ragdoll's Concrete Result Entity NOT Found")
|
||||
ragdoll_rubber_result_stopped = ("Ragdoll's Rubber Result Entity Stopped", "Ragdoll's Rubber Result Entity DID NOT Stop")
|
||||
ragdoll_concrete_result_stopped = ("Ragdoll's Concrete Result Entity Stopped", "Ragdoll's Concrete Result Entity DID NOT Stop")
|
||||
ragdoll_rubber_found = ("Ragdoll Rubber Test Entity Found", "Ragdoll Rubber Test Entity NOT Found")
|
||||
ragdoll_rubber_expected_collisions = ("Ragdoll Rubber Test Entity Collisions Were Expected", "Ragdoll Rubber Test Entity DID NOT Collide With All Expected Entities")
|
||||
ragdoll_concrete_found = ("Ragdoll Concrete Test Entity Found", "Ragdoll Concrete Test Entity NOT Found")
|
||||
ragdoll_concrete_expected_collisions = ("Ragdoll Concrete Test Entity Collisions Were Expected", "Ragdoll Concrete Test Entity DID NOT Collide With All Expected Entities")
|
||||
ragdoll_trigger_rubber_high_found = ("Ragdoll's Rubber High Trigger Found", "Ragdoll's Rubber High Trigger NOT Found")
|
||||
ragdoll_trigger_rubber_high_expected_collision = ("Ragdoll's Rubber High Trigger Collision Was As Expected", "Ragdoll's Rubber High Trigger Collision Was Not As Expected")
|
||||
ragdoll_trigger_rubber_low_found = ("Ragdoll's Rubber Low Trigger Found", "Ragdoll's Rubber Low Trigger NOT Found")
|
||||
ragdoll_trigger_rubber_low_expected_collision = ("Ragdoll's Rubber Low Trigger Collision Was As Expected", "Ragdoll's Rubber Low Trigger Collision Was Not As Expected")
|
||||
ragdoll_trigger_concrete_high_found = ("Ragdoll's Concrete High Trigger Found", "Ragdoll's Concrete High Trigger NOT Found")
|
||||
ragdoll_trigger_concrete_high_expected_collision = ("Ragdoll's Concrete High Trigger Collision Was As Expected", "Ragdoll's Concrete High Trigger Collision Was Not As Expected")
|
||||
ragdoll_trigger_concrete_low_found = ("Ragdoll's Concrete Low Trigger Found", "Ragdoll's Concrete Low Trigger NOT Found")
|
||||
ragdoll_trigger_concrete_low_expected_collision = ("Ragdoll's Concrete Low Trigger Collision Was As Expected", "Ragdoll's Concrete Low Trigger Collision Was Not As Expected")
|
||||
|
||||
@staticmethod
|
||||
# Accesses the Tests dictionary to retrieve test tuples
|
||||
def get_test(test_name):
|
||||
return Tests.__dict__[test_name.lower()]
|
||||
# fmt:on
|
||||
|
||||
|
||||
def C15096735_Materials_DefaultLibraryConsistency():
|
||||
"""
|
||||
Summary:
|
||||
This script tests the behavior of the default PhysXMaterial library. Two separate materials are applied to a variety
|
||||
of game entity types. The two materials have opposite restitution values (0.0 and 1.0). For each entity type and
|
||||
material another entity is made to "bounce" off it. The distance of the bounce is measured and validated via
|
||||
Triggers.
|
||||
|
||||
Level Description:
|
||||
Four tests are step up, each with 1 or 2 TestEntities. Each of these sub-tests have two ResultEntities (either
|
||||
spheres or boxes) each set to collide with either a rubber or concrete material. Each of these ResultEntities have
|
||||
two TriggerEntities associated with them (High and Low). These Triggers are set up so the bouncing ResultEntities
|
||||
should trigger the Low TriggerEntity, but not the High.
|
||||
The four TestEntities whose material properties are validated are:
|
||||
Terrain - Using the Terrain Texture Tools
|
||||
Platforms - Basic box entities with RigidBodies and Colliders
|
||||
Character Controllers - PhysXCharacterController entities
|
||||
Ragdolls - Entities with Actor, AnimGraph and PhysXRagdoll components
|
||||
|
||||
Expected Behavior:
|
||||
The four entity tests should run in series. Each test should have two spheres (or cubes) bounce off of their
|
||||
assigned test entity. Upon collision, Triggers should appear, and the spheres (or cubes) should only intersect
|
||||
with the lower trigger. When the spheres (or cubes) reach the highest point of their bounce they should disappear.
|
||||
At this time the Triggers should disappear and the next test should activate.
|
||||
|
||||
Test Steps:
|
||||
1) Load level and enter game mode
|
||||
2) Find entities and initialize test states
|
||||
For each test
|
||||
3) Activate ResultEntities
|
||||
4) Wait for expected collision(s)
|
||||
5) Activate Triggers
|
||||
6) Wait for ResultEntities to stop / test to conclude
|
||||
7) Deactivate Triggers
|
||||
4) Exit game mode / Close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
import azlmbr.math as azmath
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 2.5
|
||||
INITIAL_VELOCITY = azmath.Vector3(0.0, 0.0, -10.0)
|
||||
|
||||
# Entity Base class to be inherited by specific Entity classes
|
||||
# Handles as much "general entity" logic as possible to reduce code copying
|
||||
# Should be considered "virtual" and should not be directly instantiated
|
||||
class EntityBase:
|
||||
|
||||
# Initializes the core features for an Entity and reports the critical result for being located successfully
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
self.name = name
|
||||
self.active = True
|
||||
self.id = general.find_game_entity(self.name)
|
||||
# Report result
|
||||
found_test_tuple = Tests.get_test(self.name + "_Found")
|
||||
Report.critical_result(found_test_tuple, self.id.IsValid())
|
||||
|
||||
# Sets whether the Entity is activated or deactivated. Logs event
|
||||
def set_active(self, active):
|
||||
# type: (bool) -> None
|
||||
if active and not self.active:
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.id)
|
||||
elif not active and self.active:
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", self.id)
|
||||
self.active = active
|
||||
|
||||
# String cast, returns Entity name
|
||||
def __str__(self):
|
||||
# type: () -> str
|
||||
return self.name
|
||||
|
||||
# They are the default objects to be "bounced" off of TestEntities.
|
||||
# ResultEntities collect data about how far they bounce and deactivate themselves when done
|
||||
class ResultEntity(EntityBase):
|
||||
|
||||
# Instantiates a ResultEntity: calls EntityBase.__init__
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.collision_entity = None
|
||||
self.bounce_peak_pos = None
|
||||
self.result_tuple = Tests.get_test(self.name + "_Stopped")
|
||||
self.velocity = None
|
||||
self.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
self.initial_pos = self.current_pos
|
||||
# Double check that gravity is enabled
|
||||
if not azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id):
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id, True)
|
||||
self.set_active(False)
|
||||
|
||||
# Refreshes current velocity and checks if this Entity has stopped (or started "falling")
|
||||
# after expected collision, then deactivates itself
|
||||
def refresh(self):
|
||||
# type: () -> None
|
||||
if self.active:
|
||||
# 4) Wait for expected collision
|
||||
self.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
self.velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
if self.collision_entity is not None:
|
||||
# After collision takes place, track the highest bounce position
|
||||
if self.velocity.z <= 0.0:
|
||||
self.bounce_peak_pos = self.current_pos
|
||||
self.set_active(False)
|
||||
|
||||
# Overload of EntityBase::set_active
|
||||
# When activated, sets the linear velocity to the calibrated LINEAR_VELOCITY
|
||||
def set_active(self, active):
|
||||
# type: (bool) -> None
|
||||
EntityBase.set_active(self, active)
|
||||
if active:
|
||||
self.velocity = INITIAL_VELOCITY
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, self.velocity)
|
||||
|
||||
# Reports test result.
|
||||
# Successful if we collided with something and then came to a stop
|
||||
def report_result(self):
|
||||
# type: () -> None
|
||||
Report.result(self.result_tuple, self.collision_entity is not None and self.bounce_peak_pos is not None)
|
||||
|
||||
# Returns true if the entity is done with it's test
|
||||
def is_done(self):
|
||||
# type: () -> bool
|
||||
return self.bounce_peak_pos is not None
|
||||
|
||||
# TestEntities are the surfaces that have their physics material set.
|
||||
# When a ResultEntity collides with a TestEntity, relevant Triggers are Activated
|
||||
class TestEntity(EntityBase):
|
||||
|
||||
# Initializes a TestEntity: calls EntityBase.__init__
|
||||
def __init__(self, name, expected_entity, triggers):
|
||||
# type: (str, ResultEntity, [TriggerEntity,]) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.expected_entity = expected_entity
|
||||
self.triggers = triggers
|
||||
self.result_tuple = Tests.get_test(self.name + "_Expected_Collisions")
|
||||
self.collision = False
|
||||
# Assign event handler
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
# Event handler for when a collision begins
|
||||
def on_collision_begin(self, args):
|
||||
# type: ([EntityId]) -> None
|
||||
if self.expected_entity.id.Equal(args[0]):
|
||||
if not self.collision:
|
||||
self.collision = True
|
||||
self.expected_entity.collision_entity = self # Assign myself as their collision_entity
|
||||
# 5) Activate triggers associated with the colliding Entity's test
|
||||
for trigger in self.triggers:
|
||||
trigger.set_active(True)
|
||||
|
||||
# Reports result:
|
||||
# Successful if expected collision occurred
|
||||
def report_result(self):
|
||||
# type: () -> None
|
||||
Report.result(self.result_tuple, self.collision)
|
||||
|
||||
# TriggerEntities are quantitative test metrics. They are used to either look for
|
||||
# expected collisions (Low Triggers) or to look for unexpected collisions (High Triggers)
|
||||
class TriggerEntity(EntityBase):
|
||||
|
||||
def __init__(self, name, expected_entity):
|
||||
# type: (str, ResultEntity or None) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.expected_entity = expected_entity # Expected Entity to hit trigger (or None)
|
||||
self.result_entity = None # Actual Entity to hit trigger (or None)
|
||||
self.triggered = False
|
||||
self.handler = None
|
||||
self.result_tuple = Tests.get_test(self.name + "_Expected_Collision")
|
||||
self.set_active(False) # Triggers Deactivate after initialization and are activated by TestEntities
|
||||
|
||||
# Override for EntityBase::set_active(bool) -> None
|
||||
# Sets event handler and calls EntityBase.set_active(bool)
|
||||
def set_active(self, active):
|
||||
# type: (bool) -> None
|
||||
if not self.active and active:
|
||||
# Activating: register event handler
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
elif self.active and not active:
|
||||
# Deactivating: disconnect event handler and set to None
|
||||
if self.handler is not None:
|
||||
self.handler.disconnect()
|
||||
self.handler = None
|
||||
EntityBase.set_active(self, active)
|
||||
|
||||
# Event handler for when an entity enters trigger
|
||||
def on_trigger_enter(self, args):
|
||||
# type: ([EntityId]) -> None
|
||||
if not self.triggered:
|
||||
self.triggered = True
|
||||
self.result_entity = args[0]
|
||||
|
||||
# Reports result:
|
||||
# Successful if the expected_entity and the result_entity are the same
|
||||
# (Both None or both referencing the same Game Entity)
|
||||
def report_result(self):
|
||||
# type: () -> None
|
||||
if self.expected_entity is None:
|
||||
result = self.result_entity is None
|
||||
elif self.result_entity is None:
|
||||
result = False
|
||||
else:
|
||||
result = self.expected_entity.id.Equal(self.result_entity)
|
||||
Report.result(self.result_tuple, result)
|
||||
|
||||
# Tests manage all the Entities required for a specific Material Assignment Test.
|
||||
class Test:
|
||||
|
||||
# Initializes the test by setting up the required entities and lists for managing them.
|
||||
def __init__(self, base_str):
|
||||
# type: (str) -> None
|
||||
self.name = base_str
|
||||
|
||||
rubber_result = ResultEntity(base_str + "_Rubber_Result")
|
||||
concrete_result = ResultEntity(base_str + "_Concrete_Result")
|
||||
rubber_triggers = [
|
||||
# Trigger Entities associated with rubber Result Entity
|
||||
TriggerEntity(base_str + "_Trigger_Rubber_High", None),
|
||||
TriggerEntity(base_str + "_Trigger_Rubber_Low", rubber_result),
|
||||
]
|
||||
concrete_triggers = [
|
||||
# Trigger Entities associated with concrete Result Entity
|
||||
TriggerEntity(base_str + "_Trigger_Concrete_High", None),
|
||||
TriggerEntity(base_str + "_Trigger_Concrete_Low", concrete_result),
|
||||
]
|
||||
|
||||
# If base_str is "Terrain" both test entities should reference the same Terrain
|
||||
rubber_test_entity_name = base_str if base_str == "Terrain" else base_str + "_Rubber"
|
||||
concrete_test_entity_name = base_str if base_str == "Terrain" else base_str + "_Concrete"
|
||||
|
||||
# Test Entities
|
||||
rubber_test_entity = TestEntity(rubber_test_entity_name, rubber_result, rubber_triggers)
|
||||
concrete_test_entity = TestEntity(concrete_test_entity_name, concrete_result, concrete_triggers)
|
||||
|
||||
# Add entities to my lists
|
||||
self.results = [rubber_result, concrete_result]
|
||||
self.triggers = concrete_triggers + rubber_triggers
|
||||
self.test_objects = self.triggers + self.results + [rubber_test_entity, concrete_test_entity]
|
||||
|
||||
# Calls refresh on result entities.
|
||||
def refresh(self):
|
||||
# type: () -> None
|
||||
for result in self.results:
|
||||
result.refresh()
|
||||
|
||||
# Silently calls update, then returns True if all results are collected
|
||||
def is_done(self):
|
||||
# type: () -> bool
|
||||
self.refresh()
|
||||
if all(result.is_done() for result in self.results):
|
||||
# 7) Deactivate Triggers
|
||||
for trigger in self.triggers:
|
||||
trigger.set_active(False)
|
||||
return True
|
||||
return False
|
||||
|
||||
# Activates the result entity to start the test
|
||||
def start(self):
|
||||
# type: () -> None
|
||||
# 3 Activate ResultEntities
|
||||
for result in self.results:
|
||||
result.set_active(True)
|
||||
|
||||
# Reports results for all test objects
|
||||
def report_result(self):
|
||||
# type: () -> None
|
||||
for obj in self.test_objects:
|
||||
obj.report_result()
|
||||
|
||||
# *********** Execution Code ************
|
||||
|
||||
# 1) Open level and start game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C15096735_Materials_DefaultLibraryConsistency")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# Create and start Terrain Test
|
||||
tests = [
|
||||
# 2) Find entities and initialize test states
|
||||
Test("Terrain"),
|
||||
Test("Platform"),
|
||||
Test("Controller"),
|
||||
Test("Ragdoll")
|
||||
]
|
||||
|
||||
# 3) Run tests
|
||||
for test in tests:
|
||||
test.start()
|
||||
|
||||
# 6) Wait for ResultEntities to stop / test to conclude
|
||||
Report.result(Tests.no_time_out, helper.wait_for_condition(test.is_done, TIME_OUT))
|
||||
|
||||
test.report_result()
|
||||
|
||||
# 4) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15096735_Materials_DefaultLibraryConsistency)
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C15096737
|
||||
# Test Case Title : Verify that a change in the default material library material information
|
||||
# affects all the materials that reference it, even non-defaulted
|
||||
# exactly like if the library was selected
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096737
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
# level
|
||||
enter_game_mode_0 = ("Entered game mode 0", "Failed to enter game mode 0")
|
||||
exit_game_mode_0 = ("Exited game mode 0", "Couldn't exit game mode 0")
|
||||
enter_game_mode_1 = ("Entered game mode 1", "Failed to enter game mode 1")
|
||||
exit_game_mode_1 = ("Exited game mode 1", "Couldn't exit game mode 1")
|
||||
|
||||
# targets
|
||||
terrain_found = ("Terrain found in each test", "TERRAIN NOT FOUND in a test")
|
||||
target_character_rubber_found = ("target_character_rubber found in each test", "target_character_rubber NOT FOUND in a test")
|
||||
target_character_concrete_found = ("target_character_concrete found in each test", "target_character_concrete NOT FOUND in a test")
|
||||
|
||||
# collider activity
|
||||
rubber_sphere_found = ("rubber_sphere found in each test", "rubber_sphere NOT FOUND in a test in a test")
|
||||
rubber_sphere_trigger_found = ("rubber_sphere_trigger found in each test", "rubber_sphere_trigger NOT FOUND in a test")
|
||||
rubber_sphere_collided = ("rubber_sphere collided in each test", "rubber_sphere DIDN'T COLLIDE in a test")
|
||||
|
||||
concrete_sphere_found = ("concrete_sphere found in each test", "concrete_sphere NOT FOUND in a test")
|
||||
concrete_sphere_trigger_found = ("concrete_sphere_trigger found in each test", "concrete_sphere_trigger NOT FOUND in a test")
|
||||
concrete_sphere_collided = ("concrete_sphere collided in each test", "concrete_sphere DIDN'T COLLIDE in a test")
|
||||
|
||||
character_rubber_found = ("character_rubber found in each test", "character_rubber NOT FOUND in a test")
|
||||
character_rubber_trigger_found = ("character_rubber_trigger found in each test", "character_rubber_trigger NOT FOUND in a test")
|
||||
character_rubber_collided = ("character_rubber collided in each test", "character_rubber DIDN'T COLLIDE in a test")
|
||||
|
||||
character_concrete_found = ("character_concrete found in each test", "character_concrete NOT FOUND in a test")
|
||||
character_concrete_trigger_found = ("character_concrete_trigger found in each test", "character_concrete_trigger NOT FOUND in a test")
|
||||
character_concrete_collided = ("character_concrete collided in each test", "character_concrete DIDN'T COLLIDE in a test")
|
||||
|
||||
terrain_rubber_found = ("terrain_rubber found in each test", "terrain_rubber NOT FOUND in a test")
|
||||
terrain_rubber_trigger_found = ("terrain_rubber_trigger found in each test", "terrain_rubber_trigger NOT FOUND in a test")
|
||||
terrain_rubber_collided = ("terrain_rubber collided in each test", "terrain_rubber DIDN'T COLLIDE in a test")
|
||||
|
||||
terrain_concrete_found = ("terrain_concrete found in each test", "terrain_concrete NOT FOUND in a test")
|
||||
terrain_concrete_trigger_found = ("terrain_concrete_trigger found in each test", "terrain_concrete_trigger NOT FOUND in a test")
|
||||
terrain_concrete_collided = ("terrain_concrete collided in each test", "terrain_concrete DIDN'T COLLIDE in a test")
|
||||
|
||||
ragdoll_rubber_found = ("ragdoll_rubber found in each test", "ragdoll_rubber NOT FOUND in a test")
|
||||
ragdoll_rubber_trigger_found = ("ragdoll_rubber_trigger found in each test", "ragdoll_rubber_trigger NOT FOUND in a test")
|
||||
ragdoll_rubber_collided = ("ragdoll_rubber collided in each test", "ragdoll_rubber DIDN'T COLLIDE in a test")
|
||||
|
||||
ragdoll_concrete_found = ("ragdoll_concrete found in each test", "ragdoll_concrete NOT FOUND in a test")
|
||||
ragdoll_concrete_trigger_found = ("ragdoll_concrete_trigger found in each test", "ragdoll_concrete_trigger NOT FOUND in a test")
|
||||
ragdoll_concrete_collided = ("ragdoll_concrete collided in each test", "ragdoll_concrete DIDN'T COLLIDE in a test")
|
||||
|
||||
# Verification
|
||||
material_library_updated = ("Default material library updated", "Default material library not updated")
|
||||
rubber_material_changed = ("Rubber material changed correctly", "Rubber didn't react correctly")
|
||||
concrete_material_changed = ("Concrete material changed correctly", "Concrete didn't react correctly")
|
||||
# fmt: on
|
||||
|
||||
def C15096737_Materials_DefaultMaterialLibraryChanges():
|
||||
"""
|
||||
Summary: Runs an automated test to verify that material selected in the default material library is applied to PhysX
|
||||
colliders, character controller, terrain texture layers and ragdolls and that material can respond to change.
|
||||
|
||||
PhysX Config Description:
|
||||
A PhysX material library called all_ones is set as the default material library in PhysX Config File.
|
||||
The library has two materials surfaces: rubber with Restitution = 1.0, Restitution Combine = Maximum
|
||||
and concrete with Restitution = 0.0, Restitution combine = Multiply.
|
||||
The custom config file is loaded before editor is launched.
|
||||
|
||||
Level Description:
|
||||
Consists of 4 sets of entities.
|
||||
Each entity has either rubber or concrete material assigned to it. Each entity has a corresponding trigger placed
|
||||
between the entity and its collision target entity (terrain or character controller).
|
||||
The entities, their triggers and their target are colored blue if they have rubber material, or red for concrete.
|
||||
|
||||
Expected Behavior:
|
||||
The entities start their movement once the level is loaded. They should touch their corresponding triggers first,
|
||||
then collide with their target entity. The ones with rubber material are supposed to bounce back and touch the
|
||||
triggers. The ones with concrete material are supposed to stick to the target and stop moving, therefore not
|
||||
touching the triggers anymore. After the edits to material library the affect will be swapped.
|
||||
|
||||
Main Script Steps:
|
||||
1) Loads the level
|
||||
2) Setup targets and colliders
|
||||
3) Run Test 0
|
||||
4) Edit Material Library
|
||||
5) Run Test 1
|
||||
6) Validate Results
|
||||
7) Close editor
|
||||
|
||||
Test Steps:
|
||||
1) Enter Game Mode
|
||||
2) Validate target Id's
|
||||
3) Validate all Colliders and setup targets
|
||||
4) Wait for Collision, Report Results
|
||||
5) Allow Time to Hit trigger
|
||||
6) Exit Game Mode
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
from Physmaterial_Editor import Physmaterial_Editor
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 2.0
|
||||
PROPAGATION_FRAMES = 180
|
||||
|
||||
def get_test(entity_name, suffix):
|
||||
return Tests.__dict__[entity_name + suffix]
|
||||
|
||||
# Base class for triggers, targets and colliders
|
||||
class Entity(object):
|
||||
# Global Holding Variable for test index
|
||||
current_test = None
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.found_in_before_test = False
|
||||
|
||||
# Validates entity ids reports if the ids are valid for both test cases
|
||||
# Fast fails if any id is invalid
|
||||
def validate_id(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
if Entity.current_test == 0 and self.id.IsValid():
|
||||
self.found_in_before_test = True
|
||||
elif Entity.current_test == 1:
|
||||
Report.critical_result(get_test(self.name, "_found"), self.id.IsValid() and self.found_in_before_test)
|
||||
else:
|
||||
helper.fail_fast("{} was not found in test {}".format(self.name, Entity.current_test))
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
class Collider(Entity):
|
||||
def __init__(self, name, target):
|
||||
Entity.__init__(self, name)
|
||||
self.target = target
|
||||
# Data holding variables
|
||||
self.collided_with_target_0 = False
|
||||
self.collided_with_target_1 = False
|
||||
self.hit_trigger_0 = False
|
||||
self.hit_trigger_1 = False
|
||||
|
||||
# Initialized target collisions
|
||||
def setup_target(self):
|
||||
self.target.validate_id
|
||||
# Watch target for collision with collider
|
||||
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.collision_handler.connect(self.id)
|
||||
self.collision_handler.add_callback("OnCollisionBegin", self.detect_collision_target)
|
||||
|
||||
def activate_trigger(self):
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.trigger.id)
|
||||
Report.info("{} activated".format(self.trigger.name))
|
||||
|
||||
# Sets up trigger and activates it post-collision with target
|
||||
def setup_trigger(self):
|
||||
if Entity.current_test == 0:
|
||||
self.trigger = Entity(self.name + "_trigger")
|
||||
self.trigger.validate_id()
|
||||
self.activate_trigger()
|
||||
# Watch for collider entrance
|
||||
self.trigger.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.trigger.handler.connect(self.trigger.id)
|
||||
self.trigger.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
if self.id.equal(args[0]) and not getattr(self, "hit_trigger_{}".format(Entity.current_test)):
|
||||
Report.info("{} entered {} in test {}".format(self.name, self.trigger.name, Entity.current_test))
|
||||
setattr(self, "hit_trigger_{}".format(Entity.current_test), True)
|
||||
|
||||
def detect_collision_target(self, args):
|
||||
print("Collision_going_on")
|
||||
if self.target.id.equal(args[0]) and not getattr(self, "collided_with_target_{}".format(Entity.current_test)):
|
||||
Report.info("{} collided with {}".format(self.name, self.target.name))
|
||||
setattr(self, "collided_with_target_{}".format(Entity.current_test), True)
|
||||
self.setup_trigger()
|
||||
|
||||
def edit_material_library():
|
||||
# Flips the Restitution values of rubber and concrete
|
||||
material_library = Physmaterial_Editor("all_ones_1.physmaterial")
|
||||
rubber_restitution = material_library.modify_material("rubber", "Restitution", 0)
|
||||
rubber_restitution_combine = material_library.modify_material("rubber", "RestitutionCombine", "Multiply")
|
||||
concrete_restitution = material_library.modify_material("concrete", "Restitution", 1)
|
||||
concrete_restitution_combine = material_library.modify_material("concrete", "RestitutionCombine", "Average")
|
||||
|
||||
material_library.save_changes()
|
||||
return rubber_restitution and rubber_restitution_combine and concrete_restitution and concrete_restitution_combine
|
||||
|
||||
def check_rubber_material_updated(rubber_colliders):
|
||||
# Checks that all rubber colliders hit the trigger on test 0 and not on test 1
|
||||
before_test_passed = all([collider.hit_trigger_0 for collider in rubber_colliders])
|
||||
after_test_passed = all([not collider.hit_trigger_1 for collider in rubber_colliders])
|
||||
|
||||
return before_test_passed and after_test_passed
|
||||
|
||||
def check_concrete_material_updated(concrete_colliders):
|
||||
# Checks that all concrete colliders didn't hit the trigger on test 0 and did on test 1
|
||||
before_test_passed = all([not collider.hit_trigger_0 for collider in concrete_colliders])
|
||||
after_test_passed = all([collider.hit_trigger_1 for collider in concrete_colliders])
|
||||
|
||||
return before_test_passed and after_test_passed
|
||||
|
||||
def test_run(index, all_colliders):
|
||||
Entity.current_test = index
|
||||
# 1) Enter Game Mode
|
||||
helper.enter_game_mode(get_test("enter_game_mode_", str(index)))
|
||||
|
||||
# 2) Validate target Ids
|
||||
terrain.validate_id()
|
||||
target_character_concrete.validate_id()
|
||||
target_character_rubber.validate_id()
|
||||
|
||||
# 3) Validate all Colliders and setup targets
|
||||
for collider in all_colliders:
|
||||
collider.validate_id()
|
||||
collider.setup_target()
|
||||
|
||||
# 4) Wait for Collision, Report Results
|
||||
if not helper.wait_for_condition(lambda: all([getattr(collider, "collided_with_target_{}".format(index)) for collider in all_colliders]), TIME_OUT):
|
||||
failed_colliders = ", ".join([collider.name for collider in all_colliders if not getattr(collider, "collided_with_target_{}".format(index))])
|
||||
helper.fail_fast("A collision with target did not occur for these colliders: {}".format(failed_colliders))
|
||||
elif index == 1:
|
||||
for collider in all_colliders:
|
||||
Report.result(get_test(collider.name, "_collided"), collider.collided_with_target_0 and collider.collided_with_target_1)
|
||||
|
||||
# 5) Allow time to hit trigger
|
||||
general.idle_wait_frames(PROPAGATION_FRAMES)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(get_test("exit_game_mode_", str(index)))
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "C15096737_Materials_DefaultMaterialLibraryChanges")
|
||||
|
||||
# 2) Setup targets and colliders
|
||||
terrain = Entity("terrain")
|
||||
target_character_rubber = Entity("target_character_rubber")
|
||||
target_character_concrete = Entity("target_character_concrete")
|
||||
|
||||
rubber_sphere = Collider(name="rubber_sphere", target=terrain)
|
||||
concrete_sphere = Collider(name="concrete_sphere", target=terrain)
|
||||
character_rubber = Collider(name="character_rubber", target=target_character_rubber)
|
||||
character_concrete = Collider(name="character_concrete", target=target_character_concrete)
|
||||
terrain_rubber = Collider(name="terrain_rubber", target=terrain)
|
||||
terrain_concrete = Collider(name="terrain_concrete", target=terrain)
|
||||
ragdoll_rubber = Collider(name="ragdoll_rubber", target=terrain)
|
||||
ragdoll_concrete = Collider(name="ragdoll_concrete", target=terrain)
|
||||
|
||||
rubber_test_entities = [rubber_sphere, character_rubber, terrain_rubber, ragdoll_rubber]
|
||||
concrete_test_entities = [concrete_sphere, character_concrete, terrain_concrete, ragdoll_concrete]
|
||||
test_entities = rubber_test_entities + concrete_test_entities
|
||||
|
||||
# 3) Run test 0
|
||||
test_run(index=0, all_colliders=test_entities)
|
||||
|
||||
# 4) Edit Material Library
|
||||
Report.critical_result(Tests.material_library_updated, edit_material_library())
|
||||
|
||||
# Wait for material library changes to propagate
|
||||
general.idle_wait_frames(PROPAGATION_FRAMES)
|
||||
|
||||
# 5) Run test 1
|
||||
test_run(index=1, all_colliders=test_entities)
|
||||
|
||||
# 6) Validate Results
|
||||
Report.result(Tests.concrete_material_changed, check_concrete_material_updated(concrete_test_entities))
|
||||
Report.result(Tests.rubber_material_changed, check_rubber_material_updated(rubber_test_entities))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15096737_Materials_DefaultMaterialLibraryChanges)
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C15096740
|
||||
Test Case Title : Verify that clearing a material library on all systems that use it,
|
||||
assigns the default material library
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15096740
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_entity = ("Entity created successfully", "Failed to create Entity")
|
||||
add_physx_component = ("PhysX Component added successfully", "Failed to add PhysX Component")
|
||||
override_default_library = ("Material library overrided successfully", "Failed to override material library")
|
||||
update_to_default_library = ("Library updated to default", "Failed to update library to default")
|
||||
new_library_updated = ("New library updated successfully", "Failed to add new library")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C15096740_Material_LibraryUpdatedCorrectly():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Load level with Entity having PhysX Component. Override the material library to be the same one as the
|
||||
default material library. Change the default material library into another one.
|
||||
|
||||
Expected Behavior:
|
||||
The material library gets updated correctly when the default material is changed.
|
||||
|
||||
Test Steps:
|
||||
1) Load the level
|
||||
2) Create new Entity with PhysX Character Controller
|
||||
3) Override the material library to be the same one as the default material library
|
||||
4) Switch it back again to the default material library.
|
||||
5) Change the default material library into another one.
|
||||
6) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
|
||||
# Built-in Imports
|
||||
import os
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
# Helper file Imports
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity
|
||||
from asset_utils import Asset
|
||||
|
||||
# Lumberyard Imports
|
||||
import azlmbr.asset as azasset
|
||||
|
||||
# Constants
|
||||
library_property_path = "Configuration|Physics Material|Library"
|
||||
default_material_path = "surfacetypemateriallibrary.physmaterial"
|
||||
new_material_path = os.path.join("physicssurfaces", "default_phys_materials.physmaterial")
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create new Entity with PhysX Character Controller
|
||||
test_entity = EditorEntity.create_editor_entity("TestEntity")
|
||||
Report.result(Tests.create_entity, test_entity.id.IsValid())
|
||||
|
||||
test_component = test_entity.add_component("PhysX Character Controller")
|
||||
Report.result(Tests.add_physx_component, test_entity.has_component("PhysX Character Controller"))
|
||||
|
||||
# 3) Override the material library to be the same one as the default material library
|
||||
default_asset = Asset.find_asset_by_path(default_material_path)
|
||||
test_component.set_component_property_value(library_property_path, default_asset.id)
|
||||
default_asset.id = test_component.get_component_property_value(library_property_path)
|
||||
Report.result(Tests.override_default_library, default_asset.get_path() == default_material_path)
|
||||
|
||||
# 4) Switch it back again to the default material library.
|
||||
test_component.set_component_property_value(library_property_path, azasset.AssetId())
|
||||
Report.result(
|
||||
Tests.update_to_default_library,
|
||||
test_component.get_component_property_value(library_property_path) == azasset.AssetId(),
|
||||
)
|
||||
|
||||
# 5) Change the default material library into another one.
|
||||
new_asset = Asset.find_asset_by_path(new_material_path)
|
||||
test_component.set_component_property_value(library_property_path, new_asset.id)
|
||||
new_asset.id = test_component.get_component_property_value(library_property_path)
|
||||
Report.result(Tests.new_library_updated, new_asset.get_path() == new_material_path.replace(os.sep, '/'))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15096740_Material_LibraryUpdatedCorrectly)
|
||||
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C15308217
|
||||
# Test Case Title : Verify that the Terrain texture layer doesn't crash when changing
|
||||
# from on a level with a terrain component to another level without a terrain component
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15308217
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode_1 = ("Entered game mode for level1", "Failed to enter game mode for level1")
|
||||
enter_game_mode_2 = ("Entered game mode for level2", "Failed to enter game mode for level2")
|
||||
find_terrain = ("Terrain found", "Terrain not found")
|
||||
exit_game_mode_1 = ("Exited game mode for level1", "Couldn't exit game mode for level1")
|
||||
exit_game_mode_2 = ("Exited game mode for level2", "Couldn't exit game mode for level2")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C15308217_NoCrash_LevelSwitch():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Verify that the Terrain texture layer doesn't crash when changing from on a level with a
|
||||
terrain component to another level without a terrain component
|
||||
|
||||
Level Description:
|
||||
Level C15308217_NoCrash_LevelSwitchWithOutTerrain:
|
||||
No entities
|
||||
Level C15308217_NoCrash_LevelSwitchWithTerrain:
|
||||
Terrain (entity) - PhysX Terrain entity is created in the level
|
||||
|
||||
Expected Behavior:
|
||||
Editior should not crash.
|
||||
We are switching the levels and validating the entities and checking any crash is happening while
|
||||
switching between levels
|
||||
|
||||
Test Steps:
|
||||
1) Open level with PhysX Terrain component
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Exit game mode
|
||||
5) Open level which doesn't have a PhysX Terrain component
|
||||
6) Enter game mode
|
||||
7) Wait for WAIT_TIME to check if any crash happens
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
WAIT_TIME = 1.0
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level with PhysX Terrain component
|
||||
helper.open_level("Physics", "C15308217_NoCrash_LevelSwitchWithTerrain")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode_1)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.critical_result(Tests.find_terrain, terrain_id.IsValid())
|
||||
|
||||
# 4) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode_1)
|
||||
|
||||
# 5) Open level which doesn't have a PhysX Terrain component
|
||||
helper.open_level("Physics", "C15308217_NoCrash_LevelSwitchWithOutTerrain")
|
||||
|
||||
# 6) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode_2)
|
||||
|
||||
# 7) Wait for WAIT_TIME to check if any crash happens
|
||||
general.idle_wait(WAIT_TIME)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode_2)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15308217_NoCrash_LevelSwitch)
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test Case ID : C15308221
|
||||
# Test Case Title : Verify that material library and slots are always in sync and work consistently through the different places of usage
|
||||
# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/15308221
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_0 = ("Test 0) Entered game mode", "Test 0) Failed to enter game mode")
|
||||
find_terrain_0 = ("Test 0) The Terrain was found", "Test 0) The Terrain was not found")
|
||||
find_terrain_box_0 = ("Test 0) Terrain test box was found", "Test 0) Terrain test box was not found")
|
||||
find_collider_0 = ("Test 0) Box collider was found", "Test 0) Box collider was not found")
|
||||
find_ragdoll_0 = ("Test 0) Ragdoll was found", "Test 0) Ragdoll was not found")
|
||||
find_character_controller_0 = ("Test 0) Character controller was found", "Test 0) Character controller was not found")
|
||||
find_controller_box_0 = ("Test 0) Character controller test box was found", "Test 0) Character controller test box was not found")
|
||||
|
||||
terrain_box_bounced_0 = ("Test 0) Terrain test box bounced", "Test 0) Terrain test box did not bounce")
|
||||
collider_bounced_0 = ("Test 0) Box collider bounced", "Test 0) Box collider did not bounce")
|
||||
ragdoll_bounced_0 = ("Test 0) Modified ragdoll bounced", "Test 0) Modified ragdoll did not bounce")
|
||||
controller_box_bounced_0 = ("Test 0) Character controller test box bounced", "Test 0) Character controller test box did not bounce")
|
||||
exit_game_mode_0 = ("Test 0) Exited game mode", "Test 0) Failed to exit game mode")
|
||||
all_bounced_equal_0 = ("Test 0) All entities bounced the same height", "Test 0) All entities did not bounce the same height")
|
||||
|
||||
enter_game_mode_1 = ("Test 1) Entered game mode", "Test 1) Failed to enter game mode")
|
||||
find_terrain_1 = ("Test 1) The Terrain was found", "Test 1) The Terrain was not found")
|
||||
find_terrain_box_1 = ("Test 1) Terrain test box was found", "Test 1) Terrain test box was not found")
|
||||
find_collider_1 = ("Test 1) Box collider was found", "Test 1) Box collider was not found")
|
||||
find_ragdoll_1 = ("Test 1) Ragdoll was found", "Test 1) Ragdoll was not found")
|
||||
find_character_controller_1 = ("Test 1) Character controller was found", "Test 1) Character controller was not found")
|
||||
find_controller_box_1 = ("Test 1) Character controller test box was found", "Test 1) Character controller test box was not found")
|
||||
|
||||
terrain_box_bounced_1 = ("Test 1) Terrain test box bounced", "Test 1) Terrain test box did not bounce")
|
||||
collider_bounced_1 = ("Test 1) Box collider bounced", "Test 1) Box collider did not bounce")
|
||||
ragdoll_bounced_1 = ("Test 1) Modified ragdoll bounced", "Test 1) Modified ragdoll did not bounce")
|
||||
controller_box_bounced_1 = ("Test 1) Character controller test box bounced", "Test 1) Character controller test box did not bounce")
|
||||
exit_game_mode_1 = ("Test 1) Exited game mode", "Test 1) Failed to exit game mode")
|
||||
all_bounced_equal_1 = ("Test 1) All entities bounced the same height", "Test 1) All entities did not bounce the same height")
|
||||
|
||||
all_bounced_greater = ("All entities bounced higher on the second test", "All entities did not bounce higher on the second test")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C15308221_Material_ComponentsInSyncWithLibrary():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to verify that the material library is always in sync between the different PhysX components
|
||||
|
||||
Level Description:
|
||||
A new material library was created with 1 material, called "Modified":
|
||||
dynamic friction: 0.5
|
||||
static friction: 0.5
|
||||
restitution: 0.25
|
||||
|
||||
There are 4 types of components we want to test for:
|
||||
PhysX Ragdoll:
|
||||
A ragdoll ("ragdoll") with the "Modified" material applied to all of its colliders. Positioned above the
|
||||
terrain.
|
||||
PhysX collider:
|
||||
A PhysX box collider ("collider") with a the "Modified" material applied. Positioned above the terrain.
|
||||
PhysX terrain:
|
||||
A PhysX terrain ("terrain"), and a PhysX box collider ("terrain_box"). "terrain_box" is positioned above
|
||||
"terrain". A new layer was created with the "Modified" material and painted onto the terrain under
|
||||
"terrain_box". "terrain_box" has the default material applied.
|
||||
PhysX character controller:
|
||||
A character controller ("character_controller"), and a PhysX box collider ("controller_box").
|
||||
"controller_box" is positioned above "character_controller" and is assigned the default material.
|
||||
"character_controller" is assigned "Modified"
|
||||
|
||||
Expected behavior:
|
||||
For every iteration this test measures the bounce height of each entity. The entities save their traveled distances
|
||||
each iteration, to verify different behavior between each setup.
|
||||
|
||||
First the test verifies the entities all behave identically, without changing anything. All entities should bounce
|
||||
the same height.
|
||||
|
||||
Next, the test modifies the restitution value for 'Modified' (from 0.25 to 0.75). All entities should again bounce
|
||||
the same height. Additionally, all entities should bounce higher with the new restitution than they did previously.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Collect basis values without modifying anything
|
||||
2.1) Enter game mode
|
||||
2.2) Find entities
|
||||
2.3) Wait for entities to bounce
|
||||
2.4) Exit game mode
|
||||
3) Verify all entities behave the same as a baseline
|
||||
4) Modify the restitution value of 'modified'
|
||||
4.1 - 4.4) <same as 2.1 - 2.4>
|
||||
5) Verify the entities all still behave the same
|
||||
6) Verify that the material change was propagated correctly
|
||||
7) Close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.components
|
||||
import azlmbr.physics
|
||||
import azlmbr.math as lymath
|
||||
|
||||
from Physmaterial_Editor import Physmaterial_Editor
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
TIMEOUT = 3.0
|
||||
BOUNCE_TOLERANCE = 0.1
|
||||
|
||||
class Entity:
|
||||
def __init__(self, name, bounce_off_of_name):
|
||||
self.name = name
|
||||
self.bounce_off_of_name = bounce_off_of_name
|
||||
self.bounces = []
|
||||
|
||||
def find_and_reset(self):
|
||||
self.hit_position = None
|
||||
self.hit_terrain = False
|
||||
self.max_bounce = 0.0
|
||||
self.reached_max_bounce = False
|
||||
self.id = general.find_game_entity(self.name)
|
||||
self.setup_handler()
|
||||
return self.id.IsValid()
|
||||
|
||||
def on_collision_enter(self, args):
|
||||
entering = args[0]
|
||||
if entering.Equal(self.id):
|
||||
if not self.hit_terrain:
|
||||
self.hit_terrain_position = self.position
|
||||
self.hit_terrain = True
|
||||
|
||||
def setup_handler(self):
|
||||
self.bounce_off_of_id = general.find_game_entity(self.bounce_off_of_name)
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.bounce_off_of_id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_enter)
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def get_test(test_name):
|
||||
return Tests.__dict__[test_name]
|
||||
|
||||
def run_test(test_number):
|
||||
# x.1) Enter game mode
|
||||
helper.enter_game_mode(get_test("enter_game_mode_{}".format(test_number)))
|
||||
|
||||
# x.2) Find entities
|
||||
controller_valid = general.find_game_entity("character_controller").IsValid()
|
||||
terrain_valid = general.find_game_entity("terrain").IsValid()
|
||||
|
||||
Report.critical_result(get_test("find_character_controller_{}".format(test_number)), controller_valid)
|
||||
Report.critical_result(get_test("find_terrain_{}".format(test_number)), terrain_valid)
|
||||
|
||||
collider_valid = collider.find_and_reset()
|
||||
controller_box_valid = controller_box.find_and_reset()
|
||||
ragdoll_valid = ragdoll.find_and_reset()
|
||||
terrain_box_valid = terrain_box.find_and_reset()
|
||||
|
||||
Report.critical_result(get_test("find_collider_{}".format(test_number)), collider_valid)
|
||||
Report.critical_result(get_test("find_controller_box_{}".format(test_number)), controller_box_valid)
|
||||
Report.critical_result(get_test("find_ragdoll_{}".format(test_number)), ragdoll_valid)
|
||||
Report.critical_result(get_test("find_terrain_box_{}".format(test_number)), terrain_box_valid)
|
||||
|
||||
def wait_for_bounce():
|
||||
for entity in all_entities:
|
||||
if entity.hit_terrain:
|
||||
current_bounce_height = entity.position.z - entity.hit_terrain_position.z
|
||||
if current_bounce_height >= entity.max_bounce:
|
||||
entity.max_bounce = current_bounce_height
|
||||
elif entity.max_bounce > 0.0:
|
||||
entity.reached_max_bounce = True
|
||||
return all([entity.reached_max_bounce for entity in all_entities])
|
||||
|
||||
# x.3) Wait for entities to bounce
|
||||
helper.wait_for_condition(wait_for_bounce, TIMEOUT)
|
||||
|
||||
Report.result(get_test("collider_bounced_{}".format(test_number)), collider.reached_max_bounce)
|
||||
Report.result(get_test("controller_box_bounced_{}".format(test_number)), controller_box.reached_max_bounce)
|
||||
Report.result(get_test("ragdoll_bounced_{}".format(test_number)), ragdoll.reached_max_bounce)
|
||||
Report.result(get_test("terrain_box_bounced_{}".format(test_number)), terrain_box.reached_max_bounce)
|
||||
|
||||
for entity in all_entities:
|
||||
entity.bounces.append(entity.max_bounce)
|
||||
|
||||
# x.4) Exit game mode
|
||||
helper.exit_game_mode(get_test("exit_game_mode_{}".format(test_number)))
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C15308221_Material_ComponentsInSyncWithLibrary")
|
||||
|
||||
# Setup persisting entities
|
||||
collider = Entity("collider", "terrain")
|
||||
controller_box = Entity("controller_box", "character_controller")
|
||||
ragdoll = Entity("ragdoll", "terrain")
|
||||
terrain_box = Entity("terrain_box", "terrain")
|
||||
all_entities = [collider, controller_box, ragdoll, terrain_box]
|
||||
|
||||
# 2) Collect basis values without modifying anything
|
||||
run_test(0)
|
||||
|
||||
# 3) Verify all entities behave the same as a baseline
|
||||
test_0_max_bounce = max([entity.bounces[0] for entity in all_entities])
|
||||
test_0_min_bounce = min([entity.bounces[0] for entity in all_entities])
|
||||
Report.result(
|
||||
Tests.all_bounced_equal_0, lymath.Math_IsClose(test_0_max_bounce, test_0_min_bounce, BOUNCE_TOLERANCE)
|
||||
)
|
||||
|
||||
# 4) Modify the restitution value of 'modified'
|
||||
material_editor = Physmaterial_Editor("c15308221_material_componentsinsyncwithlibrary.physmaterial")
|
||||
material_editor.modify_material("Modified", "Restitution", 0.75)
|
||||
material_editor.save_changes()
|
||||
run_test(1)
|
||||
|
||||
# 5) Verify the entities all still behave the same
|
||||
test_1_max_bounce = max([entity.bounces[1] for entity in all_entities])
|
||||
test_1_min_bounce = min([entity.bounces[1] for entity in all_entities])
|
||||
Report.result(
|
||||
Tests.all_bounced_equal_1, lymath.Math_IsClose(test_1_max_bounce, test_1_min_bounce, BOUNCE_TOLERANCE)
|
||||
)
|
||||
|
||||
# 6) Verify that the material change was propagated correctly
|
||||
all_bounced_greater = all([entity.bounces[0] < entity.bounces[1] for entity in all_entities])
|
||||
Report.result(Tests.all_bounced_greater, all_bounced_greater)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15308221_Material_ComponentsInSyncWithLibrary)
|
||||
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C15425929
|
||||
# Test Case Title : Verify that undo - redo operations do not create any error
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15425929
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
entity_found = ("Entity was initially found", "Entity COULD NOT be found initially")
|
||||
entity_deleted = ("Entity was deleted", "Entity WAS NOT deleted")
|
||||
deletion_undone = ("Undo worked", "Undo DID NOT work")
|
||||
deletion_redone = ("Redo worked", "Redo DID NOT work")
|
||||
no_error_occurred = ("Undo/redo completed without errors", "An error occurred during undo/redo")
|
||||
# fmt: off
|
||||
|
||||
|
||||
def C15425929_Undo_Redo():
|
||||
"""
|
||||
Summary:
|
||||
Tests that no error messages arise when using the undo and redo functions in the editor.
|
||||
|
||||
Level Description:
|
||||
DeleteMe - an entity that just exists above the terrain with a sphere shape component on it.
|
||||
|
||||
Steps:
|
||||
1) Load level
|
||||
2) Initially find the entity
|
||||
3) Delete the entity
|
||||
4) Undo the deletion
|
||||
5) Redo the deletion
|
||||
6) Look for errors
|
||||
7) Close the editor
|
||||
|
||||
Expected Behavior:
|
||||
The entity should be deleted, un-deleted, and re-deleted.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import Tracer
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "C15425929_Undo_Redo")
|
||||
|
||||
with Tracer() as error_tracer:
|
||||
# Entity to delete and undo and re-delete
|
||||
entity_name = "DeleteMe"
|
||||
|
||||
# 2) Find entity initially
|
||||
entity_id = general.find_editor_entity(entity_name)
|
||||
Report.critical_result(Tests.entity_found, entity_id.IsValid())
|
||||
|
||||
# 3) Delete entity
|
||||
general.select_objects([entity_name])
|
||||
general.delete_selected()
|
||||
entity_id = general.find_editor_entity(entity_name)
|
||||
Report.result(Tests.entity_deleted, not entity_id.IsValid())
|
||||
|
||||
# 4) Undo deletion
|
||||
general.undo()
|
||||
entity_id = general.find_editor_entity(entity_name)
|
||||
Report.result(Tests.deletion_undone, entity_id.IsValid())
|
||||
|
||||
# 5) Redo deletion
|
||||
general.redo()
|
||||
entity_id = general.find_editor_entity(entity_name)
|
||||
Report.result(Tests.deletion_redone, not entity_id.IsValid())
|
||||
|
||||
# 6) Look for errors
|
||||
helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0)
|
||||
Report.result(Tests.no_error_occurred, not error_tracer.has_errors)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15425929_Undo_Redo)
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C15425935
|
||||
# Test Case Title : Verify that the change in Material Library gets updated across levels
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15425935
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
# Game Mode 0
|
||||
enter_game_mode_0 = ("Entered game mode 0", "Failed to enter game mode 0")
|
||||
modify_sphere_0_found = ("Test 0: modify_sphere was found", "Test 0: modify_sphere was not found")
|
||||
delete_sphere_0_found = ("Test 0: delete_sphere was found", "Test 0: delete_sphere was not found")
|
||||
terrain_0_found = ("Test 0: terrain Entity found", "Test 0: terrain Entity was not found")
|
||||
trigger_0_found = ("Test 0: trigger entity found", "Test 0: trigger entity wasn't found")
|
||||
sphere_initial_position_0 = ("Test 0: spheres initial position valid", "Test 0: spheres initial position not valid")
|
||||
sphere_initial_velocity_0 = ("Test 0: spheres initial velocity valid", "Test 0: spheres initial velocity not valid")
|
||||
sphere_collision_0 = ("Test 0: Both spheres collided", "Test 0: Both spheres did not collide")
|
||||
exit_game_mode_0 = ("Exited game mode 0", "Couldn't exit game mode 0")
|
||||
# Game Mode 1
|
||||
enter_game_mode_1 = ("Entered game mode 1", "Failed to enter game mode 1")
|
||||
modify_sphere_1_found = ("Test 1: modify_sphere was found", "Test 1: modify_sphere was not found")
|
||||
delete_sphere_1_found = ("Test 1: delete_sphere was found", "Test 1: delete_sphere was not found")
|
||||
terrain_1_found = ("Test 1: terrain Entity found", "Test 1: terrain Entity was not found")
|
||||
trigger_1_found = ("Test 1: trigger entity found", "Test 1: trigger entity wasn't found")
|
||||
sphere_initial_position_1 = ("Test 1: spheres initial position valid", "Test 1: spheres initial position not valid")
|
||||
sphere_initial_velocity_1 = ("Test 1: spheres initial velocity valid", "Test 1: spheres initial velocity not valid")
|
||||
sphere_collision_1 = ("Test 1: Both spheres collided", "Test 1: Both spheres did not collide")
|
||||
exit_game_mode_1 = ("Exited game mode 1", "Couldn't exit game mode 1")
|
||||
# Game Mode 2
|
||||
enter_game_mode_2 = ("Entered game mode 2", "Failed to enter game mode 2")
|
||||
modify_sphere_2_found = ("Test 2: modify_sphere was found", "Test 2: modify_sphere was not found")
|
||||
delete_sphere_2_found = ("Test 2: delete_sphere was found", "Test 2: delete_sphere was not found")
|
||||
terrain_2_found = ("Test 2: terrain Entity found", "Test 2: terrain Entity was not found")
|
||||
trigger_2_found = ("Test 2: trigger entity found", "Test 2: trigger entity wasn't found")
|
||||
sphere_initial_position_2 = ("Test 2: spheres initial position valid", "Test 2: spheres initial position not valid")
|
||||
sphere_initial_velocity_2 = ("Test 2: spheres initial velocity valid", "Test 2: spheres initial velocity not valid")
|
||||
sphere_collision_2 = ("Test 2: Both spheres collided", "Test 2: Both spheres did not collide")
|
||||
exit_game_mode_2 = ("Exited game mode 2", "Couldn't exit game mode 2")
|
||||
# Game Mode 3
|
||||
enter_game_mode_3 = ("Entered game mode 3", "Failed to enter game mode 3")
|
||||
modify_sphere_3_found = ("Test 3: modify_sphere was found", "Test 3: modify_sphere was not found")
|
||||
delete_sphere_3_found = ("Test 3: delete_sphere was found", "Test 3: delete_sphere was not found")
|
||||
terrain_3_found = ("Test 3: terrain Entity found", "Test 3: terrain Entity was not found")
|
||||
trigger_3_found = ("Test 3: trigger entity found", "Test 3: trigger entity wasn't found")
|
||||
sphere_initial_position_3 = ("Test 3: spheres initial position valid", "Test 3: spheres initial position not valid")
|
||||
sphere_initial_velocity_3 = ("Test 3: spheres initial velocity valid", "Test 3: spheres initial velocity not valid")
|
||||
sphere_collision_3 = ("Test 3: Both spheres collided", "Test 3: Both spheres did not collide")
|
||||
exit_game_mode_3 = ("Test 3: Exited game mode 3", "Couldn't exit game mode 3")
|
||||
|
||||
# Test Verification
|
||||
baseline_verified = ("Both levels are the same", "Both levels aren't the same")
|
||||
material_delete_verified = ("Material delete updated spheres", "Material delete not updated spheres")
|
||||
material_modify_verified = ("Material modify updated spheres", "Material modify not updated spheres")
|
||||
post_change_verified = ("Both levels are still the same", "Both levels are not the same")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C15425935_Material_LibraryUpdatedAcrossLevels():
|
||||
"""
|
||||
Summary: Verify that the change in a physmaterial library gets updated across levels
|
||||
|
||||
Level Description: There are two levels that are being compared. Each are exact replicas with a shared
|
||||
material library
|
||||
modify_sphere - Starts between terrain and trigger with initial velocity in negative z direction and gravity disabled;
|
||||
has physx collider in sphere shape with material "to_delete", had physx rigid body, and sphere_shape
|
||||
delete_sphere - Starts between terrain and trigger with initial velocity in negative z direction and gravity disabled;
|
||||
has physx collider in sphere shape with material "to_modify", had physx rigid body, and sphere_shape
|
||||
terrain - Default terrain with transform inline; has physx terrain component
|
||||
trigger - Above the spheres, trigger is enabled; has physx collider in box shape with dimensions (5.0, 10.0, 0.25)
|
||||
and box shape with the same dimensions
|
||||
|
||||
Expected Behavior: Materials deleted or modified have their changes update across levels. Initially the spheres will
|
||||
not bounce off the terrain after the change to the material library they will bounce up and hit the trigger
|
||||
|
||||
Material Tests:
|
||||
Test 0 - Tests level 0 before the material change
|
||||
Test 1 - Tests level 1 before the material change
|
||||
Test 2 - Tests level 0 after the material change
|
||||
Test 3 - Tests level 1 after the material change
|
||||
|
||||
Test 0 and 1 should be exactly the same. Test 2 and 3 should be exactly the same. Both modification to the material
|
||||
library should allow the spheres to bounce in Test 2 and 3. Therefore, both spheres will have a higher velocity and
|
||||
be able to trigger in Test 2 and 3 as compared to 0 and 1.
|
||||
|
||||
Iterated Game Mode steps:
|
||||
1) Open the correct level for the test
|
||||
2) Open Game Mode
|
||||
3) Create and Verify Entities
|
||||
4) Wait for Sphere collision with Terrain Entity
|
||||
5) Wait for spheres to have a chance to hit trigger
|
||||
6) Modify Material Library
|
||||
7) Exit Game Mode
|
||||
|
||||
Test Steps:
|
||||
1) Create Test Objects
|
||||
2) Run Game Mode steps once for each test
|
||||
3) Verify that spheres acted as expected
|
||||
4) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as math
|
||||
|
||||
from Physmaterial_Editor import Physmaterial_Editor
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = sys.float_info.epsilon
|
||||
TIMEOUT = 1
|
||||
INITIAL_VELOCITY = math.Vector3(0.0, 0.0, -10.0)
|
||||
VELOCITY_THRESHOLD = 0.1
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name, index):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.collision_happened = False
|
||||
self.index = index
|
||||
# ID validation
|
||||
self.found = Tests.__dict__[self.name + "_{}_found".format(index)]
|
||||
Report.critical_result(self.found, self.id.IsValid())
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
class Sphere(Entity):
|
||||
terrain_id = None
|
||||
|
||||
def __init__(self, name, index):
|
||||
Entity.__init__(self, name, index)
|
||||
# Set Handler
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
if args[0].equal(Sphere.terrain_id):
|
||||
self.collision_happened = True
|
||||
|
||||
|
||||
class Material_Test:
|
||||
def __init__(self, index, level_index):
|
||||
# index is the test index 0-3 this allows for tests from the Tests class to be fetched
|
||||
self.index = index
|
||||
# level_index determins which level will be opened at the start of the test loop
|
||||
self.level_index = level_index
|
||||
# Data
|
||||
self.modify_sphere_hit_trigger = False
|
||||
self.delete_sphere_hit_trigger = False
|
||||
self.entity_list = None
|
||||
self.modify_sphere_final_velocity = None
|
||||
self.delete_sphere_final_velocity = None
|
||||
|
||||
def sphere_initial_position(self, modify_sphere_position, delete_sphere_position, terrain_position, trigger_position):
|
||||
position_valid = (
|
||||
modify_sphere_position.z == delete_sphere_position.z
|
||||
and modify_sphere_position.z > terrain_position.z
|
||||
and trigger_position.z > modify_sphere_position.z
|
||||
)
|
||||
initial_position = Tests.__dict__["sphere_initial_position_{}".format(self.index)]
|
||||
Report.critical_result(initial_position, position_valid)
|
||||
|
||||
def sphere_initial_velocity(self, modify_sphere_velocity, delete_sphere_velocity):
|
||||
initial_velocity_string = Tests.__dict__["sphere_initial_velocity_{}".format(self.index)]
|
||||
Report.critical_result(
|
||||
initial_velocity_string,
|
||||
modify_sphere_velocity.IsClose(INITIAL_VELOCITY, VELOCITY_THRESHOLD) and delete_sphere_velocity.IsClose(INITIAL_VELOCITY, VELOCITY_THRESHOLD),
|
||||
)
|
||||
|
||||
def log_velocity(self):
|
||||
self.modify_sphere_final_velocity = self.entity_list[0].velocity
|
||||
self.delete_sphere_final_velocity = self.entity_list[1].velocity
|
||||
|
||||
def set_trigger(self):
|
||||
# Type (Entity) -> None
|
||||
# Sets handler for trigger
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(self.entity_list[3].id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
# Type () -> None
|
||||
# When trigger entered the correct sphere is found boolean is flipped
|
||||
if self.entity_list[0].id.Equal(args[0]):
|
||||
self.modify_sphere_hit_trigger = True
|
||||
if self.entity_list[1].id.Equal(args[0]):
|
||||
self.delete_sphere_hit_trigger = True
|
||||
|
||||
def are_levels_consistent(level_a, level_b):
|
||||
triggers_0 = level_a.modify_sphere_hit_trigger == level_b.modify_sphere_hit_trigger
|
||||
triggers_1 = level_a.delete_sphere_hit_trigger == level_b.delete_sphere_hit_trigger
|
||||
modify_sphere_velocities = (
|
||||
abs(level_a.modify_sphere_final_velocity.z - level_b.modify_sphere_final_velocity.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
delete_sphere_velocities = (
|
||||
abs(level_a.delete_sphere_final_velocity.z - level_b.delete_sphere_final_velocity.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
return triggers_0 and triggers_1 and modify_sphere_velocities and delete_sphere_velocities
|
||||
|
||||
def check_material_delete(test_0, test_3):
|
||||
triggers = test_0.modify_sphere_hit_trigger != test_3.modify_sphere_hit_trigger
|
||||
modify_sphere_velocities = test_0.modify_sphere_final_velocity.z < test_3.modify_sphere_final_velocity.z
|
||||
return triggers and modify_sphere_velocities
|
||||
|
||||
def check_material_modify(test_0, test_3):
|
||||
triggers = test_0.delete_sphere_hit_trigger != test_3.delete_sphere_hit_trigger
|
||||
delete_sphere_velocities = test_0.delete_sphere_final_velocity.z < test_3.delete_sphere_final_velocity.z
|
||||
return triggers and delete_sphere_velocities
|
||||
|
||||
def modify_material_library():
|
||||
physmaterial_object = Physmaterial_Editor("C15425935_Material_LibraryUpdatedAcrossLevels.physmaterial")
|
||||
physmaterial_object.delete_material("to_delete")
|
||||
physmaterial_object.modify_material("to_modify", "Restitution", 1.0)
|
||||
physmaterial_object.save_changes()
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Create Test Objects
|
||||
# Each test object is given an index that will determine what tuples are pulled from the Tests class and are indicative of the order that they will be run.
|
||||
# Each test object also has a level_index to determine which level will be opened during the test loop. Both levels 0 and 1 are looked at before and after
|
||||
# the change to the material library
|
||||
test_0 = Material_Test(index=0, level_index=0)
|
||||
test_1 = Material_Test(index=1, level_index=1)
|
||||
test_2 = Material_Test(index=2, level_index=0)
|
||||
test_3 = Material_Test(index=3, level_index=1)
|
||||
# Test list of all the tests in order of index
|
||||
test_list = [test_0, test_1, test_2, test_3]
|
||||
|
||||
# 2) Run Game Mode steps once for each test
|
||||
for test in test_list:
|
||||
# 1) Open the correct level for the test
|
||||
helper.open_level(
|
||||
"physics",
|
||||
"C15425935_Material_LibraryUpdatedAcrossLevels\\C15425935_Material_LibraryUpdatedAcrossLevels_{}".format(
|
||||
test.level_index
|
||||
),
|
||||
)
|
||||
|
||||
# 2) Open Game Mode
|
||||
helper.enter_game_mode(Tests.__dict__["enter_game_mode_{}".format(test.index)])
|
||||
|
||||
# 3) Create and Verify Entities
|
||||
terrain = Entity("terrain", test.index)
|
||||
Sphere.terrain_id = terrain.id
|
||||
modify_sphere = Sphere("modify_sphere", test.index)
|
||||
delete_sphere = Sphere("delete_sphere", test.index)
|
||||
trigger = Entity("trigger", test.index)
|
||||
test.entity_list = [modify_sphere, delete_sphere, terrain, trigger]
|
||||
test.set_trigger()
|
||||
|
||||
test.sphere_initial_position(modify_sphere.position, delete_sphere.position, terrain.position, trigger.position)
|
||||
test.sphere_initial_velocity(modify_sphere.velocity, delete_sphere.velocity)
|
||||
|
||||
# 4) Wait for Sphere collision with Terrain Entity
|
||||
collisions_happened = helper.wait_for_condition(lambda: modify_sphere.collision_happened and delete_sphere.collision_happened, TIMEOUT)
|
||||
Report.result(Tests.__dict__["sphere_collision_{}".format(test.index)], collisions_happened)
|
||||
|
||||
# 5) Wait for spheres to have a chance to hit trigger
|
||||
helper.wait_for_condition(lambda: test.modify_sphere_hit_trigger and test.delete_sphere_hit_trigger, TIMEOUT)
|
||||
# Report trigger
|
||||
Report.info("modify_sphere{} hit trigger in test {}".format("" if test.modify_sphere_hit_trigger else " didn't", test.index))
|
||||
Report.info("delete_sphere{} hit trigger in test {}".format("" if test.delete_sphere_hit_trigger else " didn't", test.index))
|
||||
|
||||
test.log_velocity()
|
||||
|
||||
# 6) Modify Material Library
|
||||
if test.index == 1:
|
||||
modify_material_library()
|
||||
|
||||
# 7) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.__dict__["exit_game_mode_{}".format(test.index)])
|
||||
|
||||
# 3) Verify that spheres acted as expected
|
||||
Report.result(Tests.baseline_verified, are_levels_consistent(test_0, test_1))
|
||||
Report.result(Tests.material_delete_verified, check_material_delete(test_0, test_3))
|
||||
Report.result(Tests.material_modify_verified, check_material_modify(test_0, test_3))
|
||||
Report.result(Tests.post_change_verified, are_levels_consistent(test_2, test_3))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15425935_Material_LibraryUpdatedAcrossLevels)
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C15556261
|
||||
# Test Case Title : Check that the material assignment works with Character Controller
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15556261
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
|
||||
# level
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
#balls
|
||||
ball_to_hit_rubber_char_controller_found = ("ball_to_hit_rubber_char_controller found", "ball_to_hit_rubber_char_controller NOT FOUND ")
|
||||
ball_to_hit_rubber_char_controller_gravity = ("ball_to_hit_rubber_char_controller gravity is disabled", "ball_to_hit_rubber_char_controller GRAVITY IS ENABLED ")
|
||||
ball_to_hit_rubber_char_controller_position = ("ball_to_hit_rubber_char_controller valid postion", "ball_to_hit_rubber_char_controller INVALID POSITION ")
|
||||
ball_to_hit_rubber_char_controller_collision = ("ball_to_hit_rubber_char_controller collided with its target", "ball_to_hit_rubber_char_controller DID NOT COLLIDE WITH its target")
|
||||
|
||||
ball_to_hit_glass_char_controller_found = ("ball_to_hit_glass_char_controller found", "ball_to_hit_glass_char_controller NOT FOUND ")
|
||||
ball_to_hit_glass_char_controller_gravity = ("ball_to_hit_glass_char_controller gravity is disabled", "ball_to_hit_glass_char_controller GRAVITY IS ENABLED ")
|
||||
ball_to_hit_glass_char_controller_position = ("ball_to_hit_glass_char_controller valid postion", "ball_to_hit_glass_char_controller INVALID POSITION ")
|
||||
ball_to_hit_glass_char_controller_collision = ("ball_to_hit_glass_char_controller collided with its target", "ball_to_hit_glass_char_controller DID NOT COLLIDE WITH its target")
|
||||
|
||||
ball_to_hit_rock_char_controller_found = ("ball_to_hit_rock_char_controller found", "ball_to_hit_rock_char_controller NOT FOUND ")
|
||||
ball_to_hit_rock_char_controller_gravity = ("ball_to_hit_rock_char_controller gravity is disabled", "ball_to_hit_rock_char_controller GRAVITY IS ENABLED ")
|
||||
ball_to_hit_rock_char_controller_position = ("ball_to_hit_rock_char_controller valid postion", "ball_to_hit_rock_char_controller INVALID POSITION ")
|
||||
ball_to_hit_rock_char_controller_collision = ("ball_to_hit_rock_char_controller collided with its target", "ball_to_hit_rock_char_controller DID NOT COLLIDE WITH its target")
|
||||
|
||||
# targets
|
||||
char_rubber_found = ("character controller rubber found", "character controller rubber NOT FOUND ")
|
||||
char_rock_found = ("character controller rock found", "character controller rock NOT FOUND ")
|
||||
char_glass_found = ("character controller glass found", "character controller glass NOT FOUND ")
|
||||
|
||||
# balls velocity
|
||||
balls_velocity = ("balls velocity : rubber > glass > rock", "unexpected balls velocity")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C15556261_PhysXMaterials_CharacterControllerMaterialAssignment():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to verify that character controllers with different surface materials behave accordingly.
|
||||
|
||||
Level Description:
|
||||
3 character controllers with capsule shape, surface materials: rubber, rock, glass.
|
||||
3 balls with sphere shape on same X and Z coordinates of each character controller, initial linear velocity
|
||||
of 5 m/s on Y axis. All 3 balls have rock surface material.
|
||||
|
||||
Expected Behavior:
|
||||
The balls should all hit their corresponding character controller.
|
||||
The character controller with rubber should make the ball bounce back with almost the same speed.
|
||||
The one with glass should make the ball bounce but with reduced speed.
|
||||
The ball should not bounce off the character controller with rock material.
|
||||
The balls linear velocity is checked at the end of the test. Expected results for linear velocities are:
|
||||
rubber > glass > rock
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level
|
||||
2) Enters game mode
|
||||
3) Setup balls
|
||||
3.1) Validate ball ID
|
||||
3.2) Validate ball gravity
|
||||
3.3) Connect ball to target
|
||||
3.4) Validate ball position
|
||||
4) Wait for balls to collide
|
||||
5) Get balls velocity
|
||||
6) Validate velocity is as rubber > glass > rock
|
||||
7) Exit game mode
|
||||
8) Close editor
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 3.0
|
||||
WAIT_TIME_AFTER_COLLISSION = 0.1
|
||||
|
||||
def is_close(value1, value2, tolerance=0.01):
|
||||
return abs(value1 - value2) <= tolerance
|
||||
|
||||
def get_test(entity_name, suffix):
|
||||
return Tests.__dict__[entity_name + suffix]
|
||||
|
||||
class Entity: # Base class for targets and balls
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = None
|
||||
self.position = None
|
||||
self.gravity = None
|
||||
|
||||
def validate_ID(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
found_tuple = get_test(self.name, "_found")
|
||||
Report.critical_result(found_tuple, self.id.IsValid())
|
||||
|
||||
class CharacterController(Entity):
|
||||
def __init__(self, name):
|
||||
Entity.__init__(self, name)
|
||||
self.material = self.name.rpartition("_")[2]
|
||||
|
||||
class Ball(Entity):
|
||||
def __init__(self, name, target_name):
|
||||
Entity.__init__(self, name)
|
||||
self.target_name = target_name
|
||||
self.entered_times = 0
|
||||
self.collided_with_target = False
|
||||
# 3.1) Validate ball ID
|
||||
self.validate_ID()
|
||||
# 3.2) Validate gravity is disabled
|
||||
self.validate_gravity()
|
||||
# 3.3) Setup collision targets
|
||||
self.setup_target()
|
||||
# 3.4) Validate ball position
|
||||
self.validate_position()
|
||||
|
||||
def validate_position(self):
|
||||
self.position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
self.target.position = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldTranslation", self.target.id
|
||||
)
|
||||
position_tuple = get_test(self.name, "_position")
|
||||
Report.critical_result(
|
||||
position_tuple,
|
||||
(is_close(self.position.x, self.target.position.x))
|
||||
and (is_close(self.position.z, self.target.position.z + 1)),
|
||||
)
|
||||
|
||||
def validate_gravity(self):
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
gravity_tuple = get_test(self.name, "_gravity")
|
||||
Report.critical_result(gravity_tuple, not gravity_enabled)
|
||||
|
||||
def detect_collision_target(self, args):
|
||||
entering_entity_id = args[0]
|
||||
if entering_entity_id.Equal(self.target.id):
|
||||
Report.info(self.name + " collided with " + self.target.name)
|
||||
self.collided_with_target = True
|
||||
collision_tuple = get_test(self.name, "_collision")
|
||||
Report.critical_result(collision_tuple, self.collided_with_target)
|
||||
|
||||
def setup_target(self):
|
||||
self.target = CharacterController(self.target_name)
|
||||
self.target.validate_ID()
|
||||
self.collision_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.collision_handler.connect(self.id)
|
||||
self.collision_handler.add_callback("OnCollisionBegin", self.detect_collision_target)
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "C15556261_PhysXMaterials_CharacterControllerMaterialAssignment")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Setup balls
|
||||
all_balls = [
|
||||
Ball(name="ball_to_hit_rubber_char_controller", target_name="char_rubber"),
|
||||
Ball(name="ball_to_hit_glass_char_controller", target_name="char_glass"),
|
||||
Ball(name="ball_to_hit_rock_char_controller", target_name="char_rock"),
|
||||
]
|
||||
|
||||
# 4) Wait for balls movement
|
||||
helper.wait_for_condition(lambda: all(ball.collided_with_target for ball in all_balls), TIME_OUT)
|
||||
general.idle_wait(WAIT_TIME_AFTER_COLLISSION)
|
||||
|
||||
# 5) Get each ball's linear velocity after collision
|
||||
for ball in all_balls:
|
||||
ball.linear_velocity_magnitude = azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "GetLinearVelocity", ball.id
|
||||
).GetLength()
|
||||
|
||||
# 6) Check ball's velocity
|
||||
Report.result(
|
||||
Tests.balls_velocity,
|
||||
all_balls[0].linear_velocity_magnitude
|
||||
> all_balls[1].linear_velocity_magnitude
|
||||
> all_balls[2].linear_velocity_magnitude,
|
||||
)
|
||||
|
||||
# 7) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15556261_PhysXMaterials_CharacterControllerMaterialAssignment)
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test Case ID : C15563573
|
||||
# Test Case Title : Check that any change (Add/Delete/Modify) made to the material surface in the material library reflects immediately in the PhysX Character Controller
|
||||
# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/15563573
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_0 = ("Test 0) Entered game mode", "Test 0) Failed to enter game mode")
|
||||
find_default_controller_0 = ("Test 0) The default controller entity was found", "Test 0) The default controller entity was not found")
|
||||
find_modified_controller_0 = ("Test 0) The modified controller entity was found", "Test 0) The modified controller entity was not found")
|
||||
find_on_default_box_0 = ("Test 0) Box on default was found", "Test 0) Box on default was not found")
|
||||
find_on_modified_box_0 = ("Test 0) Box on modified was found", "Test 0) Box on modified was not found")
|
||||
boxes_moved_0 = ("Test 0) All boxes moved", "Test 0) Boxes failed to move")
|
||||
boxes_at_rest_0 = ("Test 0) All boxes came to rest", "Test 0) Boxes failed to come to rest")
|
||||
exit_game_mode_0 = ("Test 0) Exited game mode", "Test 0) Failed to exit game mode")
|
||||
default_less_than_modified = ("Test 0) Modified box traveled farther than default", "Test 0) Modified box did not travel farther than default")
|
||||
|
||||
enter_game_mode_1 = ("Test 1) Entered game mode", "Test 1) Failed to enter game mode")
|
||||
find_default_controller_1 = ("Test 1) The default controller entity was found", "Test 1) The default controller entity was not found")
|
||||
find_modified_controller_1 = ("Test 1) The modified controller entity was found", "Test 1) The modified controller entity was not found")
|
||||
find_on_default_box_1 = ("Test 1) Box on default was found", "Test 1) Box on default was not found")
|
||||
find_on_modified_box_1 = ("Test 1) Box on modified was found", "Test 1) Box on modified was not found")
|
||||
boxes_moved_1 = ("Test 1) All boxes moved", "Test 1) Boxes failed to move")
|
||||
boxes_at_rest_1 = ("Test 1) All boxes came to rest", "Test 1) Boxes failed to come to rest")
|
||||
exit_game_mode_1 = ("Test 1) Exited game mode", "Test 1) Failed to exit game mode")
|
||||
modified_less_than_default = ("Test 1) Modified box traveled less than default", "Test 1) Modified box traveled farther than default")
|
||||
|
||||
enter_game_mode_2 = ("Test 2) Entered game mode", "Test 2) Failed to enter game mode")
|
||||
find_default_controller_2 = ("Test 2) The default controller entity was found", "Test 2) The default controller entity was not found")
|
||||
find_modified_controller_2 = ("Test 2) The modified controller entity was found", "Test 2) The modified controller entity was not found")
|
||||
find_on_default_box_2 = ("Test 2) Box on default was found", "Test 2) Box on default was not found")
|
||||
find_on_modified_box_2 = ("Test 2) Box on modified was found", "Test 2) Box on modified was not found")
|
||||
boxes_moved_2 = ("Test 2) All boxes moved", "Test 2) Boxes failed to move")
|
||||
boxes_at_rest_2 = ("Test 2) All boxes came to rest", "Test 2) Boxes failed to come to rest")
|
||||
exit_game_mode_2 = ("Test 2) Exited game mode", "Test 2) Failed to exit game mode")
|
||||
default_equals_modified = ("Test 2) Modified and Default boxes traveled the same distance", "Test 2) Modified and Default boxes did not travel the same distance")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C15563573_Material_AddModifyDeleteOnCharacterController():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to verify that any change (Add/Delete/Modify) made to the material surface in the material
|
||||
library reflects immediately in the PhysX Character Controller
|
||||
|
||||
Level Description:
|
||||
There are two groups of entities, one for "modified" and one for "default".
|
||||
Each group has two entities:
|
||||
one box, with PhysX Rigid Body and PhysX Box Collider
|
||||
one character controller, with PhysX Character Controller - configured as a box shape
|
||||
|
||||
The box entity for each group sits on top of its respective character controller entity. The boxes are identical and
|
||||
have the default physX material assigned.
|
||||
|
||||
The character controller "default_controller" is assigned the default physx material.
|
||||
A new material library was created with 1 material, called "Modified", this is assigned to "modified_controller"
|
||||
dynamic friction: 0.25
|
||||
static friction: 0.5
|
||||
restitution: 0.5
|
||||
|
||||
Expected behavior:
|
||||
For every iteration this test applies a force impulse in the X direction to each box. The boxes save their traveled
|
||||
distances each iteration, to verify different behavior between each setup.
|
||||
|
||||
First the test verifies the two controllers are assigned differing materials, without changing anything. With a
|
||||
lower dynamic friction coefficient, the box 'on_modified' should travel a longer distance than 'on_default'
|
||||
|
||||
Next, the test modifies the dynamic friction value for 'modified_controller' (from 0.25 to 0.75). 'on_modified'
|
||||
should travel a shorter distance than it did in the previous test, and less than 'default'
|
||||
|
||||
Finally, we delete the 'modified' material entirely. The box 'on_modified' should then behave as 'on_default' box,
|
||||
and travel the same distance.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Collect basis values without modifying anything
|
||||
2.1) Enter game mode
|
||||
2.2) Find entities
|
||||
2.3) Push the boxes and wait for them to come to rest
|
||||
2.4) Exit game mode
|
||||
3) Modify the dynamic friction value of 'modified'
|
||||
3.1 - 3.4) <same as above>
|
||||
4) Delete 'modified's' material
|
||||
4.1 - 4.4) <same as above>
|
||||
5) Close editor
|
||||
|
||||
Notes:
|
||||
- As of 20/02/2020, we do not have any capabilities to automate the UI part of the test case. Nor can we 'Add' any
|
||||
new mesh surface in a material library by modifying the ".physmaterial" file as it requires a UUID. Hence, in order
|
||||
to validate that the modification/deletion of mesh surfaces from material library are reflected in the allocated
|
||||
material in Character Controller, we will verify the change in behaviour of the Character Controller occurring due
|
||||
to change in mesh surfaces, during the game mode.
|
||||
|
||||
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as lymath
|
||||
|
||||
from Physmaterial_Editor import Physmaterial_Editor
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from AddModifyDelete_Utils import Box
|
||||
|
||||
FORCE_IMPULSE = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
TIMEOUT = 3.0
|
||||
DISTANCE_TOLERANCE = 0.001
|
||||
|
||||
def get_test(test_name):
|
||||
return Tests.__dict__[test_name]
|
||||
|
||||
def run_test(test_number):
|
||||
# x.1) Enter game mode
|
||||
helper.enter_game_mode(get_test("enter_game_mode_{}".format(test_number)))
|
||||
|
||||
# x.2) Find entities
|
||||
default_controller_id = general.find_game_entity("default_controller")
|
||||
modified_controller_id = general.find_game_entity("modified_controller")
|
||||
Report.result(get_test("find_default_controller_{}".format(test_number)), default_controller_id.IsValid())
|
||||
Report.result(get_test("find_modified_controller_{}".format(test_number)), modified_controller_id.IsValid())
|
||||
Report.result(get_test("find_on_default_box_{}".format(test_number)), default_box.find())
|
||||
Report.result(get_test("find_on_modified_box_{}".format(test_number)), modified_box.find())
|
||||
|
||||
# x.3) Push the boxes and wait for them to come to rest
|
||||
default_box.push(FORCE_IMPULSE)
|
||||
modified_box.push(FORCE_IMPULSE)
|
||||
|
||||
def boxes_are_moving():
|
||||
return not default_box.is_stationary() and not modified_box.is_stationary()
|
||||
|
||||
def boxes_are_stationary():
|
||||
return default_box.is_stationary() and modified_box.is_stationary()
|
||||
|
||||
Report.result(
|
||||
get_test("boxes_moved_{}".format(test_number)),
|
||||
helper.wait_for_condition(boxes_are_moving, TIMEOUT),
|
||||
)
|
||||
Report.result(
|
||||
get_test("boxes_at_rest_{}".format(test_number)),
|
||||
helper.wait_for_condition(boxes_are_stationary, TIMEOUT),
|
||||
)
|
||||
|
||||
default_box.distances.append(default_box.position.GetDistance(default_box.start_position))
|
||||
modified_box.distances.append(modified_box.position.GetDistance(modified_box.start_position))
|
||||
|
||||
# x.4) Exit game mode
|
||||
helper.exit_game_mode(get_test("exit_game_mode_{}".format(test_number)))
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C15563573_Material_AddModifyDeleteOnCharacterController")
|
||||
|
||||
# Setup persisting entities
|
||||
default_box = Box("on_default")
|
||||
modified_box = Box("on_modified")
|
||||
|
||||
# 2) Collect basis values without modifying anything
|
||||
run_test(0)
|
||||
# While sitting on a character controller with friction of 0.25, 'on_modified' should travel farther than 'default'
|
||||
Report.result(Tests.default_less_than_modified, default_box.distances[0] < modified_box.distances[0])
|
||||
|
||||
# 3) Modify the dynamic friction value of 'modified'
|
||||
material_editor = Physmaterial_Editor("c15563573_material_addmodifydeleteoncharactercontroller.physmaterial")
|
||||
material_editor.modify_material("Modified", "DynamicFriction", 0.75)
|
||||
material_editor.save_changes()
|
||||
run_test(1)
|
||||
# With greater friction, 'on_modified' should now travel a shorter distance than it did in the previous test.
|
||||
Report.result(Tests.modified_less_than_default, default_box.distances[0] > modified_box.distances[1])
|
||||
|
||||
# 4) Delete 'modified's material
|
||||
material_editor.delete_material("Modified")
|
||||
material_editor.save_changes()
|
||||
run_test(2)
|
||||
Report.result(
|
||||
Tests.default_equals_modified,
|
||||
lymath.Math_IsClose(default_box.distances[2], modified_box.distances[2], DISTANCE_TOLERANCE),
|
||||
)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15563573_Material_AddModifyDeleteOnCharacterController)
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C15845879
|
||||
# Test Case Title : Check that linear damping with high values do not make the object to quiver
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/15845879
|
||||
|
||||
# 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 C15845879_ForceRegion_HighLinearDampingForce():
|
||||
"""
|
||||
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 Lumberyard 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 utils import Report
|
||||
from 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", "C15845879_ForceRegion_HighLinearDampingForce")
|
||||
|
||||
# 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__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C15845879_ForceRegion_HighLinearDampingForce)
|
||||
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C17411467
|
||||
Test Case Title : Check that Physx Ragdoll component can be added without errors/warnings
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/17411467
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_test_entity = ("Entity created successfully", "Failed to create Entity")
|
||||
add_actor_component = ("Actor component added", "Failed to add Actor component")
|
||||
add_animgraph = ("AnimGraph component added", "Failed to add AnimGraph component")
|
||||
add_physx_ragdoll = ("PhysX Ragdoll added", "Failed to add PhysX Ragdoll")
|
||||
no_warnings_errors = ("Tracer found no errors or warnings", "Tracer found errors or warnings")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Summary:
|
||||
Load level with Entity having Actor, AnimGraph and PhysX Ragdoll components.
|
||||
Verify that editor remains stable.
|
||||
|
||||
Expected Behavior:
|
||||
Physx Ragdoll component can be added without any errors.
|
||||
|
||||
Test Steps:
|
||||
1) Load the level
|
||||
2) Create test entity
|
||||
3) Add Actor and AnimGraph components
|
||||
4) Start the Tracer to catch any warnings while adding the PhysX Ragdoll component
|
||||
5) Add PhysX Ragdoll component
|
||||
6) Verify there are no errors/warnings in the entity outliner
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
|
||||
# Helper file Imports
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import Tracer
|
||||
from editor_entity_utils import EditorEntity
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the 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 Actor and AnimGraph components
|
||||
test_entity.add_component("Actor")
|
||||
Report.result(Tests.add_actor_component, test_entity.has_component("Actor"))
|
||||
|
||||
test_entity.add_component("Anim Graph")
|
||||
Report.result(Tests.add_animgraph, test_entity.has_component("Anim Graph"))
|
||||
|
||||
# 4) Start the Tracer to catch any errors while adding the PhysX Ragdoll component
|
||||
with Tracer() as section_tracer:
|
||||
# 5) Add the PhysX Ragdoll component
|
||||
ragdoll_component = test_entity.add_component("PhysX Ragdoll")
|
||||
success_check = (
|
||||
ragdoll_component.id.get_entity_id() == test_entity.id
|
||||
and ragdoll_component.get_component_name() == "PhysX Ragdoll"
|
||||
)
|
||||
# Using this alternate way to check if PhysX Ragdoll is added to entity since there is an issue with the
|
||||
# usual method in case of this component. Returned False for test_entity.has_component("PhysX Ragdoll")
|
||||
Report.result(Tests.add_physx_ragdoll, success_check)
|
||||
|
||||
# 6) Verify there are no errors in the entity outliner
|
||||
success_condition = not (section_tracer.has_errors or section_tracer.has_warnings)
|
||||
Report.result(Tests.no_warnings_errors, success_condition)
|
||||
if not success_condition:
|
||||
if section_tracer.has_warnings:
|
||||
Report.info(f"Warnings found: {section_tracer.warnings}")
|
||||
if section_tracer.has_errors:
|
||||
Report.info(f"Errors found: {section_tracer.errors}")
|
||||
Report.failure(Tests.no_warnings_found)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243580
|
||||
# Test Case Title : Check that fixed joint constrains 2 bodies
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243580
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead moved in X direction", "Lead did not move in X direction")
|
||||
check_follower_position = ("Follower moved in X direction", "Follower did not move in X direction")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243580_Joints_Fixed2BodiesConstrained():
|
||||
"""
|
||||
Summary: Check that fixed joint constrains 2 bodies
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with fixed joint. Starts with initial velocity of (5, 0, 0) in positive X direction.
|
||||
|
||||
Expected Behavior: The follower entity moves in the positive X direction and the lead entity is dragged along towards the positive X direction.
|
||||
The x position of the lead entity is incremented from its original.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead entity and follower entity moved in positive X direction.
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243580_Joints_Fixed2BodiesConstrained")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position.x
|
||||
followerInitialPosition = follower.position.x
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(1.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead entity and follower entity moved in positive X direction.
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
Report.critical_result(Tests.check_lead_position, lead.position.x > leadInitialPosition)
|
||||
Report.critical_result(Tests.check_follower_position, follower.position.x > followerInitialPosition)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243580_Joints_Fixed2BodiesConstrained)
|
||||
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243581
|
||||
# Test Case Title : Check that fixed joint is breakable
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243581
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead did not move in X direction","Lead moved in X direction")
|
||||
check_follower_position = ("Follower moved in X direction", "Follower did not move in X direction")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243581_Joints_FixedBreakable():
|
||||
"""
|
||||
Summary: Check that fixed joint is breakable
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with fixed joint. Starts with initial velocity of (5, 0, 0) in positive X direction.
|
||||
|
||||
Expected Behavior: The follower entity moves in the positive X direction but the lead entity does not move in the positive X direction by more than a distance of 0.5.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243581_Joints_FixedBreakable")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position.x
|
||||
followerInitialPosition = follower.position.x
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(1.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
Report.critical_result(Tests.check_lead_position, (lead.position.x - leadInitialPosition) < 0.5)
|
||||
Report.critical_result(Tests.check_follower_position, (follower.position.x - followerInitialPosition) > 0.5)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243581_Joints_FixedBreakable)
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243582
|
||||
# Test Case Title : Check that fixed joint allows lead-follower collision
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243582
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_collision_happened = ("Lead and follower collided", "Lead and follower did not collide")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243582_Joints_FixedLeadFollowerCollide():
|
||||
"""
|
||||
Summary: Check that fixed joint allows lead-follower collision
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with fixed joint. Starts with initial velocity of (5, 0, 0) in positive X direction.
|
||||
|
||||
Expected Behavior:
|
||||
The follower entity moves in the positive X direction and the lead entity is dragged along towards the positive X direction.
|
||||
The x position of the lead entity is incremented from its original.
|
||||
The lead and follower entities are kept apart at a distance of approximately 1.0 due to collision.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected.
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from JointsHelper import JointEntityCollisionAware
|
||||
|
||||
# Helper Entity class - self.collided flag is set when instance receives collision event.
|
||||
class Entity(JointEntityCollisionAware):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243582_Joints_FixedLeadFollowerCollide")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(2.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead entity and follower collided
|
||||
Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243582_Joints_FixedLeadFollowerCollide)
|
||||
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243583
|
||||
# Test Case Title : Check that hinge joint constrains 2 bodies about X-axis
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243583
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead stays still", "Lead moved")
|
||||
check_follower_position = ("Follower moved in X and Z directions only", "Follower did not move in X and Z directions, or moved in Y direction")
|
||||
check_follower_below_lead = ("Follower remains below lead", "Follower moved above lead")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243583_Joints_Hinge2BodiesConstrained():
|
||||
"""
|
||||
Summary: Check that hinge joint constrains 2 bodies about X-axis
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with hinge joint. Starts with initial velocity of (5, 1, 0).
|
||||
|
||||
Expected Behavior:
|
||||
The follower entity moved in the positive X and Z directions, but not in the Y direction.
|
||||
The position of the lead entity does not change much.
|
||||
The follower entity did not move above the lead entity.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.1
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243583_Joints_Hinge2BodiesConstrained")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(1.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerMovedInXAndZOnly = ((follower.position.x - followerInitialPosition.x) > FLOAT_EPSILON and
|
||||
(follower.position.y - followerInitialPosition.y) < FLOAT_EPSILON and
|
||||
(follower.position.z - followerInitialPosition.z) > FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_follower_position, followerMovedInXAndZOnly)
|
||||
|
||||
followerBelowLead = follower.position.z < lead.position.z
|
||||
Report.critical_result(Tests.check_follower_below_lead, followerBelowLead)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243583_Joints_Hinge2BodiesConstrained)
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243584
|
||||
# Test Case Title : Check that hinge joint allows soft limit constraints on 2 bodies
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243584
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead stays still", "Lead moved")
|
||||
check_follower_position = ("Follower moved higher than lead, but does not swing over it", "Follower did not move higher than lead, or swinged over it")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243584_Joints_HingeSoftLimitsConstrained():
|
||||
"""
|
||||
Summary: Check that hinge joint allows soft limit constraints on 2 bodies
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with a hinge joint. Starts with initial velocity of (5, 1, 0).
|
||||
forceRegion - This force region has a suction (negative point) force that will hold the follower if it reaches the position that will pass the test.
|
||||
|
||||
Expected Behavior:
|
||||
Lead entity remains still.
|
||||
Follower moved higher than lead, but does not swing over it.
|
||||
Since the 45 degree limit is soft, the follower can swing to a position higher than the lead, but will not swing over it.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.2
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243584_Joints_HingeSoftLimitsConstrained")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(4.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerMovedInXOnly = ((follower.position.x > leadInitialPosition.x) > FLOAT_EPSILON and
|
||||
(follower.position.z - leadInitialPosition.z) > FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_follower_position, followerMovedInXOnly)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243584_Joints_HingeSoftLimitsConstrained)
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243585
|
||||
# Test Case Title : Check that hinge joint allows no limit constraints on 2 bodies
|
||||
# URL of the test case :https://testrail.agscollab.com/index.php?/cases/view/18243585
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead stays still", "Lead moved")
|
||||
check_follower_position = ("Follower moved higher than lead, and swinged over it", "Follower did not move higher than lead, or swing over it")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243585_Joints_HingeNoLimitsConstrained():
|
||||
"""
|
||||
Summary: Check that hinge joint allows no limit constraints on 2 bodies
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with a hinge joint. Starts with initial velocity of (5, 1, 0).
|
||||
forceRegion - Contains suction and damping force to hold follower position when it enters the region that will pass the test.
|
||||
|
||||
Expected Behavior:
|
||||
Lead entity remains still.
|
||||
Follower entity's Z position exceeds lead entity's Z position and swings past above the lead entity.
|
||||
The hinge joint constraint is not limited, the follower can swing to a position higher than the lead, and over it
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.2
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243585_Joints_HingeNoLimitsConstrained")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(4.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerSwingedOverLead = (follower.position.x < leadInitialPosition.x and
|
||||
follower.position.z > leadInitialPosition.z)
|
||||
Report.critical_result(Tests.check_follower_position, followerSwingedOverLead)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243585_Joints_HingeNoLimitsConstrained)
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243586
|
||||
# Test Case Title : Check that hinge joint allows lead-follower collision
|
||||
# URL of the test case :https://testrail.agscollab.com/index.php?/cases/view/18243586
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_collision_happened = ("Lead and follower collided", "Lead and follower did not collide")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243586_Joints_HingeLeadFollowerCollide():
|
||||
"""
|
||||
Summary: Check that hinge joint allows lead-follower collision
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with a hinge joint. Starts with initial velocity of (5, 1, 0).
|
||||
|
||||
Expected Behavior:
|
||||
Lead entity remains still.
|
||||
Follower entity swings up, collides with the lead entity, and falls back down.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected (they collided)
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from JointsHelper import JointEntityCollisionAware
|
||||
|
||||
# Helper Entity class - self.collided flag is set when instance receives collision event.
|
||||
class Entity(JointEntityCollisionAware):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243586_Joints_HingeLeadFollowerCollide")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(2.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243586_Joints_HingeLeadFollowerCollide)
|
||||
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243587
|
||||
# Test Case Title : Check that hinge joint is breakable
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243587
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead stays still", "Lead moved")
|
||||
check_follower_position = ("Follower moved in X direction only", "Follower did not just move in X direction, but also in Z direction")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243587_Joints_HingeBreakable():
|
||||
"""
|
||||
Summary: Check that hinge joint is breakable
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with hinge joint. Starts with initial velocity of (5, 1, 0).
|
||||
|
||||
Expected Behavior:
|
||||
Lead entity remains still.
|
||||
Follower entity moves in the positive X direction, and not much in the Z direction since the joint broke.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.2
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243587_Joints_HingeBreakable")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(1.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerMovedInXOnly = ((follower.position.x - followerInitialPosition.x) > FLOAT_EPSILON and
|
||||
(follower.position.z - followerInitialPosition.z) < FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_follower_position, followerMovedInXOnly)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243587_Joints_HingeBreakable)
|
||||
@@ -0,0 +1,126 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243588
|
||||
# Test Case Title : Check that ball joint constrains 2 bodies within cone limits
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243588
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead stays still", "Lead moved")
|
||||
check_follower_position = ("Follower moved in X, Y and Z directions", "Follower did not move in X, Y and Z directions")
|
||||
check_follower_below_lead = ("Follower remains below lead", "Follower moved above lead")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243588_Joints_Ball2BodiesConstrained():
|
||||
"""
|
||||
Summary: Check that ball joint constrains 2 bodies within cone limits
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with ball joint. Starts with initial velocity of (5, 2, 0).
|
||||
|
||||
Expected Behavior:
|
||||
The follower entity moved in the positive X, Y and Z directions.
|
||||
The position of the lead entity does not change much.
|
||||
The follower entity did not move above the lead entity.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.1 # Negligible float value for comparing with translation vectors. Values smaller than this are considered zero.
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243588_Joints_Ball2BodiesConstrained")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(1.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerPositionDelta = follower.position.Subtract(followerInitialPosition)
|
||||
followerMovedInXAndZOnly = JointsHelper.vector3LargerThanScalar(followerPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_follower_position, followerMovedInXAndZOnly)
|
||||
|
||||
followerBelowLead = follower.position.z < lead.position.z
|
||||
Report.critical_result(Tests.check_follower_below_lead, followerBelowLead)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243588_Joints_Ball2BodiesConstrained)
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : 18243589
|
||||
# Test Case Title : Check that ball joint allows soft limit constraints
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243589
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead stays still", "Lead moved")
|
||||
check_follower_position = ("Follower in X, Y and Z directions", "Follower did not move in X, Y, and Z directions")
|
||||
check_follower_above_joint = ("Follower swings above joint", "Follower did not swing above joint")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243589_Joints_BallSoftLimitsConstrained():
|
||||
"""
|
||||
Summary: Check that ball joint allows soft limit constraints
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with a ball joint. Starts with initial velocity of (5, 1, 0).
|
||||
|
||||
Expected Behavior:
|
||||
Lead entity remains still.
|
||||
Follower entity moves in the positive X, Y and Z directions.
|
||||
Follower entity's Z position exceeds its original Z position + 2.5, above the position where the joint is located.
|
||||
Because the cone limit is 45 degrees, if the follower manages to swing above the joint position, it is evident that the limit is soft.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.2
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243589_Joints_BallSoftLimitsConstrained")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(1.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerPositionDelta = follower.position.Subtract(followerInitialPosition)
|
||||
followerMovedinXYZ = JointsHelper.vector3LargerThanScalar(followerPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_follower_position, followerMovedinXYZ)
|
||||
|
||||
followerMovedAboveJoint = follower.position.z > (followerInitialPosition.z + 2.5) # (followerInitialPosition.z + 2.5) is the z position past the 45 degree limit. This is to show that the follower swinged past the 45 degree cone limit, above the joint position.
|
||||
Report.critical_result(Tests.check_follower_above_joint, followerMovedAboveJoint)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243589_Joints_BallSoftLimitsConstrained)
|
||||
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243590
|
||||
# Test Case Title : Check that ball joint allows no limit constraints
|
||||
# URL of the test case :https://testrail.agscollab.com/index.php?/cases/view/18243590
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead stays still", "Lead moved")
|
||||
check_follower_position = ("Follower moved higher than lead", "Follower did not move higher than lead")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243590_Joints_BallNoLimitsConstrained():
|
||||
"""
|
||||
Summary: Check that ball joint allows no limit constraints
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with a ball joint. The ball joint is located in between the lead and follower. Starts with initial velocity of (5, 1, 0).
|
||||
dampingRegion - A force region cube is placed at the position of the lead. If the follower swings to the position above and near the lead, the force region's damping holds the follower in the place.'
|
||||
|
||||
Expected Behavior:
|
||||
Lead entity remains still.
|
||||
Follower entity's Z position exceeds lead entity's Z position.
|
||||
Because the ball joint is somewhere in the middle of the follower and the lead,
|
||||
if the follower manages to go above the lead,
|
||||
it is evident that the ball joint without limits managed to keep the follower constrained to the lead but did not impose a limit.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.2
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243590_Joints_BallNoLimitsConstrained")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(3.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 2.5 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 2.5 second:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerAboveLead = follower.position.z > leadInitialPosition.z
|
||||
Report.critical_result(Tests.check_follower_position, followerAboveLead)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243590_Joints_BallNoLimitsConstrained)
|
||||
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243591
|
||||
# Test Case Title : Check that ball joint allows lead-follower collision
|
||||
# URL of the test case :https://testrail.agscollab.com/index.php?/cases/view/18243591
|
||||
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_collision_happened = ("Lead and follower collided", "Lead and follower did not collide")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243591_Joints_BallLeadFollowerCollide():
|
||||
"""
|
||||
Summary: Check that ball joint allows lead-follower collision
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with a ball joint. Starts with initial velocity of (5, 2, 0).
|
||||
|
||||
Expected Behavior:
|
||||
Lead entity remains still.
|
||||
Follower entity swings up, collides with the lead entity, and falls back down.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected (they collided)
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from JointsHelper import JointEntityCollisionAware
|
||||
|
||||
# Helper Entity class - self.collided flag is set when instance receives collision event.
|
||||
class Entity(JointEntityCollisionAware):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243591_Joints_BallLeadFollowerCollide")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(2.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.critical_result(Tests.check_collision_happened, lead.collided and follower.collided)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243591_Joints_BallLeadFollowerCollide)
|
||||
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243592
|
||||
# Test Case Title : Check that ball joint is breakable
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243592
|
||||
# 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")
|
||||
lead_found = ("Found lead", "Did not find lead")
|
||||
follower_found = ("Found follower", "Did not find follower")
|
||||
check_lead_position = ("Lead stays still", "Lead moved")
|
||||
check_follower_position = ("Follower moved in X direction only", "Follower did not just move in X direction, but also in Z direction")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243592_Joints_BallBreakable():
|
||||
"""
|
||||
Summary: Check that ball joint is breakable
|
||||
|
||||
Level Description:
|
||||
lead - Starts above follower entity
|
||||
follower - Starts below lead entity. Constrained to lead entity with ball joint. Starts with initial velocity of (5, 2, 0).
|
||||
|
||||
Expected Behavior:
|
||||
Lead entity remains still.
|
||||
Follower entity moves in the positive X direction, and not much in the Z direction since the joint broke.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.2
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243592_Joints_BallBreakable")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
lead = Entity("lead")
|
||||
follower = Entity("follower")
|
||||
Report.info_vector3(lead.position, "lead initial position:")
|
||||
Report.info_vector3(follower.position, "follower initial position:")
|
||||
leadInitialPosition = lead.position
|
||||
followerInitialPosition = follower.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(1.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(lead.position, "lead position after 1 second:")
|
||||
Report.info_vector3(follower.position, "follower position after 1 second:")
|
||||
|
||||
leadPositionDelta = lead.position.Subtract(leadInitialPosition)
|
||||
leadRemainedStill = JointsHelper.vector3SmallerThanScalar(leadPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_lead_position, leadRemainedStill)
|
||||
|
||||
followerMovedInXOnly = ((follower.position.x - followerInitialPosition.x) > FLOAT_EPSILON and
|
||||
(follower.position.z - followerInitialPosition.z) < FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_follower_position, followerMovedInXOnly)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243592_Joints_BallBreakable)
|
||||
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18243593
|
||||
# Test Case Title : Check that fixed/hinge/ball joints allow constraints to global frame
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18243593
|
||||
|
||||
# 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")
|
||||
follower_fixed_found = ("Found follower for fixed joint", "Did not find follower for fixed joint")
|
||||
follower_hinge_found = ("Found follower for hinge joint", "Did not find follower for hinge joint")
|
||||
follower_ball_found = ("Found follower for ball joint", "Did not find follower for ball joint")
|
||||
check_fixed_follower_position = ("Fixed joint follower remained still", "Fixed joint follower did not remain still")
|
||||
check_hinge_follower_position = ("Hinge joint follower moved in X and Z directions only", "Hinge joint follower did not move in X and Z directions, or moved in Y direction")
|
||||
check_ball_follower_position = ("Ball joint follower moved in X, Y and Z directions", "Ball joint follower did not move in X, Y and Z directions")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18243593_Joints_GlobalFrameConstrained():
|
||||
"""
|
||||
Summary: Check that fixed/hinge/ball joints allow constraints to global frame
|
||||
|
||||
Level Description:
|
||||
follower_fixed - Constrained to fixed joint at global frame placed above the entity.
|
||||
follower_hinge - Constrained to hinge joint at global frame placed above the entity.
|
||||
follower_ball - Constrained to ball joint at global frame placed above the entity.
|
||||
|
||||
Expected Behavior:
|
||||
The follower-fixed entity should remain still.
|
||||
The follower_hinge and follower_ball entities move in the positive X and Z directions.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create and Validate Entities
|
||||
4) Wait for several seconds
|
||||
5) Check to see if lead and follower behaved as expected
|
||||
6) Exit Game Mode
|
||||
7) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
import JointsHelper
|
||||
from JointsHelper import JointEntity
|
||||
|
||||
# Constants
|
||||
FLOAT_EPSILON = 0.1
|
||||
|
||||
# Helper Entity class
|
||||
class Entity(JointEntity):
|
||||
def criticalEntityFound(self): # Override function to use local Test dictionary
|
||||
Report.critical_result(Tests.__dict__[self.name + "_found"], self.id.isValid())
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C18243593_Joints_GlobalFrameConstrained")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create and Validate Entities
|
||||
followerFixed = Entity("follower_fixed")
|
||||
followerHinge = Entity("follower_hinge")
|
||||
followerBall = Entity("follower_ball")
|
||||
Report.info_vector3(followerFixed.position, "follower_fixed initial position:")
|
||||
Report.info_vector3(followerHinge.position, "follower_hinge initial position:")
|
||||
Report.info_vector3(followerBall.position, "follower_ball initial position:")
|
||||
followerFixedInitialPosition = followerFixed.position
|
||||
followerHingeInitialPosition = followerHinge.position
|
||||
followerBallInitialPosition = followerBall.position
|
||||
|
||||
# 4) Wait for several seconds
|
||||
general.idle_wait(1.0) # wait for lead and follower to move
|
||||
|
||||
# 5) Check to see if lead and follower behaved as expected
|
||||
Report.info_vector3(followerFixed.position, "follower_fixed initial position after 1 second:")
|
||||
Report.info_vector3(followerHinge.position, "follower_hinge initial position after 1 second:")
|
||||
Report.info_vector3(followerBall.position, "follower_ball initial position after 1 second:")
|
||||
|
||||
followerFixedPositionDelta = followerFixed.position.Subtract(followerFixedInitialPosition)
|
||||
fixedFollowerRemainedStill = JointsHelper.vector3SmallerThanScalar(followerFixedPositionDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_fixed_follower_position, fixedFollowerRemainedStill)
|
||||
|
||||
hingeFollowerMovedInXAndZOnly = ((followerHinge.position.x - followerHingeInitialPosition.x) > FLOAT_EPSILON and
|
||||
(followerHinge.position.y - followerHingeInitialPosition.y) < FLOAT_EPSILON and
|
||||
(followerHinge.position.z - followerHingeInitialPosition.z) > FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_hinge_follower_position, hingeFollowerMovedInXAndZOnly)
|
||||
|
||||
followerBallPositinDelta = followerBall.position.Subtract(followerBallInitialPosition)
|
||||
ballFollowerMovedinXYZ = JointsHelper.vector3LargerThanScalar(followerBallPositinDelta, FLOAT_EPSILON)
|
||||
Report.critical_result(Tests.check_ball_follower_position, ballFollowerMovedinXYZ)
|
||||
|
||||
# 6) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18243593_Joints_GlobalFrameConstrained)
|
||||
+368
@@ -0,0 +1,368 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18977601
|
||||
# Test Case Title : Verify that when two objects with different materials collide, the friction combine priority works
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18977601
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_box_average = ("Box entity 'average' found", "Box entity 'average' not found")
|
||||
find_box_minimum = ("Box entity 'minimum' found", "Box entity 'minimum' not found")
|
||||
find_box_multiply = ("Box entity 'multiply' found", "Box entity 'multiply' not found")
|
||||
find_box_maximum = ("Box entity 'maximum' found", "Box entity 'maximum' not found")
|
||||
find_ramp_average = ("Ramp entity 'average' found", "Ramp entity 'average' not found")
|
||||
find_ramp_minimum = ("Ramp entity 'minimum' found", "Ramp entity 'minimum' not found")
|
||||
find_ramp_multiply = ("Ramp entity 'multiply' found", "Ramp entity 'multiply' not found")
|
||||
find_ramp_maximum = ("Ramp entity 'maximum' found", "Ramp entity 'maximum' not found")
|
||||
|
||||
# Test 0, first row of matrix
|
||||
boxes_at_rest_start_0 = ("Test 0): All boxes began test motionless", "Test 0): All boxes did not begin test motionless")
|
||||
boxes_were_pushed_0 = ("Test 0): All boxes moved", "Test 0): All boxes did not move before timeout")
|
||||
boxes_at_rest_end_0 = ("Test 0): All boxes came to rest", "Test 0): All boxes did not come to rest before timeout")
|
||||
|
||||
# Test 1, second row of matrix
|
||||
boxes_at_rest_start_1 = ("Test 1): All boxes began test motionless", "Test 1): All boxes did not begin test motionless")
|
||||
boxes_were_pushed_1 = ("Test 1): All boxes moved", "Test 1): All boxes did not move before timeout")
|
||||
boxes_at_rest_end_1 = ("Test 1): All boxes came to rest", "Test 1): All boxes did not come to rest before timeout")
|
||||
|
||||
# Test 2, third row of matrix
|
||||
boxes_at_rest_start_2 = ("Test 2): All boxes began test motionless", "Test 2): All boxes did not begin test motionless")
|
||||
boxes_were_pushed_2 = ("Test 2): All boxes moved", "Test 2): All boxes did not move before timeout")
|
||||
boxes_at_rest_end_2 = ("Test 2): All boxes came to rest", "Test 2): All boxes did not come to rest before timeout")
|
||||
|
||||
# Test 3, fourth row of matrix
|
||||
boxes_at_rest_start_3 = ("Test 3): All boxes began test motionless", "Test 3): All boxes did not begin test motionless")
|
||||
boxes_were_pushed_3 = ("Test 3): All boxes moved", "Test 3): All boxes did not move before timeout")
|
||||
boxes_at_rest_end_3 = ("Test 3): All boxes came to rest", "Test 3): All boxes did not come to rest before timeout")
|
||||
|
||||
basis_row_unique = ("All distances in Test 0 were unique", "All distances in Test 0 were not unique")
|
||||
basis_row_ordered = ("All distances in Test 0 were correctly ordered", "All distances in Test 0 were correctly ordered")
|
||||
distance_matrix_valid = ("The resulting distance matrix was valid", "The resulting distance matrix was invalid")
|
||||
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18977601_Material_FrictionCombinePriority():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that the friction combine mode is assigned according to the correct priority.
|
||||
|
||||
Level Description:
|
||||
Four boxes sit on one of 4 horizontal ramps.
|
||||
The ramps are identical, as are the boxes, save for their physX material:
|
||||
|
||||
A new material library was created with 8 materials:
|
||||
minimum_box, multiply_box, average_box, maximum_box, minimum_ramp, multiply_ramp, average_ramp, maximum_ramp,
|
||||
Each 'box' material has its 'friction combine' mode assigned as named; as well as the following properties:
|
||||
dynamic friction: 0.25
|
||||
static friction: 0.25
|
||||
restitution: 0.25
|
||||
The 'ramp' materials are assigned similarly, with the following values:
|
||||
dynamic friction: 0.5
|
||||
static friction: 0.5
|
||||
restitution: 0.5
|
||||
|
||||
The values were specifically chosen to give a well-ordered and distributed result across all 4 combine modes
|
||||
(each progressive tier in priority gives a result 0.125 away from the last)
|
||||
|
||||
Each box and ramp is assigned its corresponding friction material
|
||||
Each box and ramp also has a PhysX box collider with default settings
|
||||
The COM of the boxes is placed on the plane (0, 0, -0.5), so as to remove any torque moments and resulting rotations
|
||||
|
||||
Expected Behavior:
|
||||
When two bodies with different materials are in contact, the physics system chooses which combine mode to use based
|
||||
on which combine mode has the highest priority.
|
||||
|
||||
The priority order is as follows: Average < Minimum < Multiply < Maximum.
|
||||
|
||||
For each ramp, this script applies a force impulse in the world X direction to all four boxes.
|
||||
|
||||
Upon collecting all data, the script evaluates the traveled distances against an expected pattern.
|
||||
This pattern is derived from the priority order, to determine which combine mode should "win" over the others.
|
||||
Boxes with greater friction combine coefficients should travel a shorter distance.
|
||||
|
||||
[Coefficient Combination Mode Results]
|
||||
average: (0.25 + 0.5) / 2 -> 0.375
|
||||
minimum: 0.25 vs 0.5 -> 0.25
|
||||
multiply: 0.25 * 0.5 -> 0.125
|
||||
maximum: 0.25 vs 0.5 -> 0.5
|
||||
|
||||
[Coefficient Combination Matrix]
|
||||
Boxes
|
||||
avg min mul max
|
||||
avg 0.375 0.25 0.125 0.5 # Test 0
|
||||
Ramps min 0.25 0.25 0.125 0.5 # Test 1
|
||||
mul 0.125 0.125 0.125 0.5 # Test 2
|
||||
max 0.5 0.5 0.5 0.5 # Test 3
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Validate entities
|
||||
|
||||
For each ramp:
|
||||
4) Replace the ramp under the boxes
|
||||
5) Ensure all boxes are stationary
|
||||
6) Push the boxes and wait for them to come to rest
|
||||
|
||||
7) Validate matrix
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
NUMBER_OF_TESTS = 4
|
||||
FORCE_IMPULSE = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
STANDBY_OFFSET = lymath.Vector3(0.0, 0.0, 4.0)
|
||||
DISTANCE_TOLERANCE = 0.002
|
||||
TIMEOUT = 5
|
||||
|
||||
# region Entity Classes
|
||||
class Box:
|
||||
def __init__(self, name, valid_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.start_position = self.get_position()
|
||||
self.valid_test = valid_test
|
||||
|
||||
def is_stationary(self):
|
||||
velocity = azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
return velocity.IsZero()
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Ramp:
|
||||
def __init__(self, name, valid_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.start_position = self.get_position()
|
||||
self.valid_test = valid_test
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def set_position(self, value):
|
||||
return azlmbr.components.TransformBus(bus.Event, "SetWorldTranslation", self.id, value)
|
||||
|
||||
def get_test(test_name, test_number):
|
||||
return Tests.__dict__["{}_{}".format(test_name, test_number)]
|
||||
|
||||
class TestInfo:
|
||||
def __init__(self):
|
||||
self.at_rest_start_tests = []
|
||||
self.moved_tests = []
|
||||
self.at_rest_end_tests = []
|
||||
for i in range(NUMBER_OF_TESTS):
|
||||
self.at_rest_start_tests.append(get_test("boxes_at_rest_start", i))
|
||||
self.moved_tests.append(get_test("boxes_were_pushed", i))
|
||||
self.at_rest_end_tests.append(get_test("boxes_at_rest_end", i))
|
||||
|
||||
# endregion
|
||||
|
||||
# region wait_for_condition() Functions
|
||||
def push_boxes():
|
||||
for box in all_boxes:
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "ApplyLinearImpulse", box.id, FORCE_IMPULSE)
|
||||
|
||||
def all_boxes_stationary():
|
||||
for box in all_boxes:
|
||||
if not box.is_stationary():
|
||||
return False
|
||||
return True
|
||||
|
||||
def all_boxes_moving():
|
||||
for box in all_boxes:
|
||||
if box.is_stationary():
|
||||
return False
|
||||
return True
|
||||
|
||||
# endregion
|
||||
|
||||
# region Matrix Validation
|
||||
def list_is_unique(target_list):
|
||||
return len(set(target_list)) == len(target_list)
|
||||
|
||||
def float_is_close(value, target, tolerance):
|
||||
return abs(value - target) <= tolerance
|
||||
|
||||
def validate_matrix(matrix):
|
||||
# type: (list[list]) -> bool
|
||||
"""
|
||||
Returns True if the matrix matches the pattern expected based on the friction combine priority.
|
||||
|
||||
:param matrix: the distance matrix
|
||||
|
||||
:return: True if the matrix closely matches the expected pattern
|
||||
"""
|
||||
# The first test represents a basis value for what is expected when each of the combine modes "wins" the other.
|
||||
# This is because every mode beats 'average' (the first ramp we test on). We can compare the rest of the matrix
|
||||
# against this basis row to validate the rest of the matrix as long as we also guarantee that the basis is valid
|
||||
#
|
||||
# Resulting matrix should follow the pattern:
|
||||
# A B C D <- Test 0
|
||||
# B B C D <- Test 1
|
||||
# C C C D <- Test 2
|
||||
# D D D D <- Test 3
|
||||
|
||||
basis_row = matrix[0]
|
||||
Report.critical_result(Tests.basis_row_unique, list_is_unique(basis_row))
|
||||
|
||||
average = basis_row[0]
|
||||
minimum = basis_row[1]
|
||||
multiply = basis_row[2]
|
||||
maximum = basis_row[3]
|
||||
# Based on the resulting coefficients, we can expect each slide distance to be ordered a specific way
|
||||
Report.critical_result(Tests.basis_row_ordered, maximum < average < minimum < multiply)
|
||||
|
||||
def report_failure(test_index, box_index, expected):
|
||||
box_name = all_boxes[box_index].name
|
||||
Report.info(
|
||||
"Matrix validation failure:\n"
|
||||
"Distance for box '{}' on test {} was not close to the expected basis value\n"
|
||||
"Actual: {:.3f}\n"
|
||||
"Expected: {:.3f}".format(box_name, test_index, matrix[test_index][box_index], expected)
|
||||
)
|
||||
|
||||
valid = True
|
||||
for row_index, row in enumerate(matrix):
|
||||
for column_index, value in enumerate(row):
|
||||
max_index = max(row_index, column_index)
|
||||
|
||||
if not float_is_close(value, basis_row[max_index], DISTANCE_TOLERANCE):
|
||||
report_failure(row_index, column_index, basis_row[max_index])
|
||||
valid = False
|
||||
return valid
|
||||
|
||||
def log_matrix(matrix):
|
||||
matrix_display_string = "\nResulting Distance Matrix:\n"
|
||||
for row in matrix:
|
||||
for value in row:
|
||||
matrix_display_string += "{:.3f},".format(value)
|
||||
matrix_display_string += "\n"
|
||||
Report.info(matrix_display_string)
|
||||
|
||||
# endregion
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C18977601_Material_FrictionCombinePriority")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# Set up our boxes
|
||||
box_average = Box("Average", Tests.find_box_average)
|
||||
box_minimum = Box("Minimum", Tests.find_box_minimum)
|
||||
box_multiply = Box("Multiply", Tests.find_box_multiply)
|
||||
box_maximum = Box("Maximum", Tests.find_box_maximum)
|
||||
all_boxes = (box_average, box_minimum, box_multiply, box_maximum)
|
||||
|
||||
# Set up our ramps
|
||||
ramp_average = Ramp("RampAverage", Tests.find_ramp_average)
|
||||
ramp_minimum = Ramp("RampMinimum", Tests.find_ramp_minimum)
|
||||
ramp_multiply = Ramp("RampMultiply", Tests.find_ramp_multiply)
|
||||
ramp_maximum = Ramp("RampMaximum", Tests.find_ramp_maximum)
|
||||
all_ramps = (ramp_average, ramp_minimum, ramp_multiply, ramp_maximum)
|
||||
|
||||
# Init our tests
|
||||
test_info = TestInfo()
|
||||
|
||||
# 3) Validate entities
|
||||
for box in all_boxes:
|
||||
Report.critical_result(box.valid_test, box.id.IsValid())
|
||||
|
||||
for ramp in all_ramps:
|
||||
Report.critical_result(ramp.valid_test, ramp.id.IsValid())
|
||||
|
||||
# Setup ramp active and standby positions
|
||||
active_position = ramp_average.get_position()
|
||||
stand_by_position = active_position.Subtract(STANDBY_OFFSET)
|
||||
|
||||
# fmt: off
|
||||
distance_matrix = [[0.0, 0.0, 0.0, 0.0], # Test 0 - Ramp 'Average'
|
||||
[0.0, 0.0, 0.0, 0.0], # Test 1 - Ramp 'Minimum'
|
||||
[0.0, 0.0, 0.0, 0.0], # Test 2 - Ramp 'Multiply'
|
||||
[0.0, 0.0, 0.0, 0.0]] # Test 3 - Ramp 'Maximum'
|
||||
# fmt: on
|
||||
|
||||
for row_index in range(NUMBER_OF_TESTS):
|
||||
Report.info("********Starting Test {}********".format(row_index))
|
||||
|
||||
# 4) Replace the ramp under the boxes
|
||||
ramp = all_ramps[row_index]
|
||||
ramp.set_position(active_position)
|
||||
|
||||
# 5) Ensure all boxes are stationary
|
||||
Report.result(
|
||||
test_info.at_rest_start_tests[row_index], helper.wait_for_condition(all_boxes_stationary, TIMEOUT)
|
||||
)
|
||||
|
||||
# 6) Push the boxes and wait for them to come to rest
|
||||
push_boxes()
|
||||
|
||||
moved_test = test_info.moved_tests[row_index]
|
||||
at_rest_end_test = test_info.at_rest_end_tests[row_index]
|
||||
Report.result(moved_test, helper.wait_for_condition(all_boxes_moving, TIMEOUT))
|
||||
Report.result(at_rest_end_test, helper.wait_for_condition(all_boxes_stationary, TIMEOUT))
|
||||
|
||||
for column_index in range(NUMBER_OF_TESTS):
|
||||
# Register the distance the boxes travelled
|
||||
box = all_boxes[column_index]
|
||||
end_position = box.get_position()
|
||||
distance = end_position.GetDistance(box.start_position)
|
||||
|
||||
distance_matrix[row_index][column_index] = distance
|
||||
Report.info("Box {} travelled {:.3f} meters".format(box.name, distance))
|
||||
box.start_position = end_position
|
||||
|
||||
ramp.set_position(stand_by_position)
|
||||
|
||||
# 7) Validate matrix
|
||||
log_matrix(distance_matrix)
|
||||
Report.result(Tests.distance_matrix_valid, validate_matrix(distance_matrix))
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18977601_Material_FrictionCombinePriority)
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C18981526
|
||||
# Test Case Title : Verify when two objects with different materials collide, the restitution combine priority works
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/18981526
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_box_average = ("Box entity 'average' found", "Box entity 'average' not found")
|
||||
find_box_minimum = ("Box entity 'minimum' found", "Box entity 'minimum' not found")
|
||||
find_box_multiply = ("Box entity 'multiply' found", "Box entity 'multiply' not found")
|
||||
find_box_maximum = ("Box entity 'maximum' found", "Box entity 'maximum' not found")
|
||||
find_ramp_average = ("Ramp entity 'average' found", "Ramp entity 'average' not found")
|
||||
find_ramp_minimum = ("Ramp entity 'minimum' found", "Ramp entity 'minimum' not found")
|
||||
find_ramp_multiply = ("Ramp entity 'multiply' found", "Ramp entity 'multiply' not found")
|
||||
find_ramp_maximum = ("Ramp entity 'maximum' found", "Ramp entity 'maximum' not found")
|
||||
|
||||
# Test 0, first row of matrix
|
||||
boxes_fell_0 = ("Test 0): All boxes fell", "Test 0): All boxes did not fall")
|
||||
boxes_hit_ramp_0 = ("Test 0): All boxes hit the ramp", "Test 0): All boxes did not hit the ramp")
|
||||
boxes_peaked_0 = ("Test 0): All boxes reached their max height", "Test 0): All boxes did not reach their max height before timeout")
|
||||
|
||||
# Test 1, second row of matrix
|
||||
boxes_fell_1 = ("Test 1): All boxes fell", "Test 1): All boxes did not fall")
|
||||
boxes_hit_ramp_1 = ("Test 1): All boxes hit the ramp", "Test 1): All boxes did not hit the ramp")
|
||||
boxes_peaked_1 = ("Test 1): All boxes reached their max height", "Test 1): All boxes did not reach their max height before timeout")
|
||||
|
||||
# Test 2, third row of matrix
|
||||
boxes_fell_2 = ("Test 2): All boxes fell", "Test 2): All boxes did not fall")
|
||||
boxes_hit_ramp_2 = ("Test 2): All boxes hit the ramp", "Test 2): All boxes did not hit the ramp")
|
||||
boxes_peaked_2 = ("Test 2): All boxes reached their max height", "Test 2): All boxes did not reach their max height before timeout")
|
||||
|
||||
# Test 3, fourth row of matrix
|
||||
boxes_fell_3 = ("Test 3): All boxes fell", "Test 3): All boxes did not fall")
|
||||
boxes_hit_ramp_3 = ("Test 3): All boxes hit the ramp", "Test 3): All boxes did not hit the ramp")
|
||||
boxes_peaked_3 = ("Test 3): All boxes reached their max height", "Test 3): All boxes did not reach their max height before timeout")
|
||||
|
||||
basis_row_unique = ("All distances in Test 0 were unique", "All distances in Test 0 were not unique")
|
||||
basis_row_ordered = ("All distances in Test 0 were correctly ordered", "All distances in Test 0 were not correctly ordered")
|
||||
height_matrix_valid = ("The resulting height matrix was valid", "The resulting height matrix was invalid")
|
||||
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C18981526_Material_RestitutionCombinePriority():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that the restitution combine mode is assigned according to the correct priority.
|
||||
|
||||
Level Description:
|
||||
Four boxes sit above one of 4 horizontal ramps.
|
||||
The ramps are identical, as are the boxes, save for their physX material:
|
||||
|
||||
A new material library was created with 8 materials:
|
||||
minimum_box, multiply_box, average_box, maximum_box, minimum_ramp, multiply_ramp, average_ramp, maximum_ramp,
|
||||
Each 'box' material has its 'restitution combine' mode assigned as named; as well as the following properties:
|
||||
dynamic friction: 0.25
|
||||
static friction: 0.25
|
||||
restitution: 0.25
|
||||
The 'ramp' materials are assigned similarly, with the following values:
|
||||
dynamic friction: 0.5
|
||||
static friction: 0.5
|
||||
restitution: 0.5
|
||||
|
||||
The values were specifically chosen to give a well-ordered and distributed result across all 4 combine modes
|
||||
(each progressive tier in priority gives a result 0.125 away from the last)
|
||||
|
||||
Each box and ramp is assigned its corresponding restitution material
|
||||
Each box and ramp also has a PhysX box collider with default settings
|
||||
|
||||
Expected Behavior:
|
||||
When two bodies with different materials are in contact, the physics system chooses which combine mode to use based
|
||||
on which combine mode has the highest priority.
|
||||
|
||||
The priority order is as follows: Average < Minimum < Multiply < Maximum.
|
||||
|
||||
For each ramp, this script drops the four boxes and measures their bounce height
|
||||
|
||||
Upon collecting all data, the script evaluates the bounce height against an expected pattern.
|
||||
This pattern is derived from the priority order, to determine which combine mode should "win" over the others.
|
||||
Boxes with greater restitution combine coefficients should bounce higher.
|
||||
|
||||
[Coefficient Combination Mode Results]
|
||||
average: (0.25 + 0.5) / 2 -> 0.375
|
||||
minimum: 0.25 vs 0.5 -> 0.25
|
||||
multiply: 0.25 * 0.5 -> 0.125
|
||||
maximum: 0.25 vs 0.5 -> 0.5
|
||||
|
||||
[Coefficient Combination Matrix]
|
||||
Boxes
|
||||
avg min mul max
|
||||
avg 0.375 0.25 0.125 0.5 # Test 0
|
||||
Ramps min 0.25 0.25 0.125 0.5 # Test 1
|
||||
mul 0.125 0.125 0.125 0.5 # Test 2
|
||||
max 0.5 0.5 0.5 0.5 # Test 3
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Validate entities
|
||||
|
||||
For each ramp:
|
||||
4) Replace the ramp under the boxes
|
||||
5) Drop the boxes
|
||||
6) Wait for the box to hit the ground
|
||||
7) Measure the bounce height
|
||||
|
||||
8) Validate matrix
|
||||
9) Exit game mode
|
||||
10) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
NUMBER_OF_TESTS = 4
|
||||
DISTANCE_TOLERANCE = 0.005
|
||||
TIMEOUT = 5.0
|
||||
STANDBY_OFFSET = lymath.Vector3(0.0, 0.0, 4.0)
|
||||
SET_PHYSICS_WAIT = 10
|
||||
|
||||
# region Entity Classes
|
||||
class Box:
|
||||
def __init__(self, name, valid_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.start_position = self.get_position()
|
||||
self.hit_ramp_position = None
|
||||
self.valid_test = valid_test
|
||||
self.peaked = False
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def set_position(self, value):
|
||||
return azlmbr.components.TransformBus(bus.Event, "SetWorldTranslation", self.id, value)
|
||||
|
||||
def get_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
def set_velocity(self, value):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetLinearVelocity", self.id, value)
|
||||
|
||||
def set_gravity_enabled(self, value):
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetGravityEnabled", self.id, value)
|
||||
|
||||
def set_physics_enabled(self, value):
|
||||
if value:
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "EnablePhysics", self.id)
|
||||
else:
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "DisablePhysics", self.id)
|
||||
|
||||
def force_awake(self):
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "ForceAwake", self.id)
|
||||
|
||||
class Ramp:
|
||||
def __init__(self, name, valid_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.start_position = self.get_position()
|
||||
self.valid_test = valid_test
|
||||
self.create_handler()
|
||||
self.collided_with_boxes = set()
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
for box in all_boxes:
|
||||
if box.id.Equal(other_id):
|
||||
Report.info("Collided with {}".format(box.name))
|
||||
self.collided_with_boxes.add(box)
|
||||
box.hit_ramp_position = box.get_position()
|
||||
|
||||
def create_handler(self):
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def set_position(self, value):
|
||||
return azlmbr.components.TransformBus(bus.Event, "SetWorldTranslation", self.id, value)
|
||||
|
||||
def all_boxes_hit(self):
|
||||
return len(self.collided_with_boxes) == 4
|
||||
|
||||
class TestInfo:
|
||||
def __init__(self):
|
||||
self.fell_tests = []
|
||||
self.hit_ramp_tests = []
|
||||
self.peaked_tests = []
|
||||
for i in range(NUMBER_OF_TESTS):
|
||||
self.fell_tests.append(get_test("boxes_fell", i))
|
||||
self.hit_ramp_tests.append(get_test("boxes_hit_ramp", i))
|
||||
self.peaked_tests.append(get_test("boxes_peaked", i))
|
||||
# endregion
|
||||
|
||||
# region Helper Functions
|
||||
def get_test(test_name, test_number):
|
||||
return Tests.__dict__["{}_{}".format(test_name, test_number)]
|
||||
|
||||
def reset_boxes():
|
||||
for box in all_boxes:
|
||||
box.peaked = False
|
||||
box.set_physics_enabled(False)
|
||||
|
||||
# We can't enable the boxes as kinematic and set their position on the same frame
|
||||
general.idle_wait_frames(SET_PHYSICS_WAIT)
|
||||
|
||||
for box in all_boxes:
|
||||
box.set_position(box.start_position)
|
||||
|
||||
general.idle_wait_frames(SET_PHYSICS_WAIT)
|
||||
|
||||
for box in all_boxes:
|
||||
box.set_physics_enabled(True)
|
||||
box.force_awake()
|
||||
# endregion
|
||||
|
||||
# region wait_for_condition() Functions
|
||||
def drop_boxes():
|
||||
for box in all_boxes:
|
||||
box.set_gravity_enabled(True)
|
||||
|
||||
def all_boxes_falling():
|
||||
for box in all_boxes:
|
||||
if box.get_velocity().z >= 0.0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def all_boxes_peaked():
|
||||
peaked_boxes = 0
|
||||
for box in all_boxes:
|
||||
if box.peaked:
|
||||
peaked_boxes += 1
|
||||
else:
|
||||
current_position = box.get_position()
|
||||
current_height = current_position.z - box.hit_ramp_position.z
|
||||
current_linear_velocity = box.get_velocity()
|
||||
if current_linear_velocity.z > 0.0:
|
||||
box.bounce_height = current_height
|
||||
else:
|
||||
Report.info("Box {} reached {:.3f}M high".format(box.name, box.bounce_height))
|
||||
box.set_gravity_enabled(False)
|
||||
box.set_velocity(lymath.Vector3(0.0, 0.0, 0.0))
|
||||
box.peaked = True
|
||||
return peaked_boxes == 4
|
||||
# endregion
|
||||
|
||||
# region Matrix Validation
|
||||
def validate_matrix(matrix):
|
||||
# type: (list[list]) -> bool
|
||||
"""
|
||||
Returns True if the matrix matches the pattern expected based on the friction combine priority.
|
||||
|
||||
:param matrix: the height matrix
|
||||
|
||||
:return: True if the matrix closely matches the expected pattern
|
||||
"""
|
||||
# The first test represents a basis value for what is expected when each of the combine modes "wins" the other.
|
||||
# This is because every mode beats 'average' (the first ramp we test with) We can compare the rest of the matrix
|
||||
# against this basis row to validate the rest of the matrix as long as we also guarantee that the basis is valid
|
||||
#
|
||||
# Resulting matrix should follow the pattern:
|
||||
# A B C D <- Test 0
|
||||
# B B C D <- Test 1
|
||||
# C C C D <- Test 2
|
||||
# D D D D <- Test 3
|
||||
|
||||
basis_row = matrix[0]
|
||||
Report.critical_result(Tests.basis_row_unique, list_is_unique(basis_row))
|
||||
|
||||
average = basis_row[0]
|
||||
minimum = basis_row[1]
|
||||
multiply = basis_row[2]
|
||||
maximum = basis_row[3]
|
||||
# Based on the resulting coefficients, we can expect each bounce height to be ordered in a specific way
|
||||
Report.critical_result(Tests.basis_row_ordered, maximum > average > minimum > multiply)
|
||||
|
||||
def report_failure(test_index, box_index, expected):
|
||||
box_name = all_boxes[box_index].name
|
||||
Report.info(
|
||||
"Matrix validation failure:\n"
|
||||
"Bounce height for box '{}' on test {} was not close to the expected basis value\n"
|
||||
"Bounce height: {:.3f}\n"
|
||||
"Expected: {:.3f}".format(box_name, test_index, matrix[test_index][box_index], expected)
|
||||
)
|
||||
|
||||
valid = True
|
||||
for row_index, row in enumerate(matrix):
|
||||
for column_index, value in enumerate(row):
|
||||
max_index = max(row_index, column_index)
|
||||
|
||||
if not float_is_close(value, basis_row[max_index], DISTANCE_TOLERANCE):
|
||||
report_failure(row_index, column_index, basis_row[max_index])
|
||||
valid = False
|
||||
return valid
|
||||
|
||||
def log_matrix(matrix):
|
||||
matrix_display_string = "\nResulting Height Matrix:\n"
|
||||
for row in matrix:
|
||||
for value in row:
|
||||
matrix_display_string += "{:.3f},".format(value)
|
||||
matrix_display_string += "\n"
|
||||
Report.info(matrix_display_string)
|
||||
|
||||
def list_is_unique(target_list):
|
||||
return len(set(target_list)) == len(target_list)
|
||||
|
||||
def float_is_close(value, target, tolerance):
|
||||
return abs(value - target) <= tolerance
|
||||
# endregion
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C18981526_Material_RestitutionCombinePriority")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# Set up our boxes
|
||||
box_average = Box("Average", Tests.find_box_average)
|
||||
box_minimum = Box("Minimum", Tests.find_box_minimum)
|
||||
box_multiply = Box("Multiply", Tests.find_box_multiply)
|
||||
box_maximum = Box("Maximum", Tests.find_box_maximum)
|
||||
all_boxes = (box_average, box_minimum, box_multiply, box_maximum)
|
||||
|
||||
# Set up our ramps
|
||||
ramp_average = Ramp("RampAverage", Tests.find_ramp_average)
|
||||
ramp_minimum = Ramp("RampMinimum", Tests.find_ramp_minimum)
|
||||
ramp_multiply = Ramp("RampMultiply", Tests.find_ramp_multiply)
|
||||
ramp_maximum = Ramp("RampMaximum", Tests.find_ramp_maximum)
|
||||
all_ramps = (ramp_average, ramp_minimum, ramp_multiply, ramp_maximum)
|
||||
|
||||
# Init our tests
|
||||
test_info = TestInfo()
|
||||
|
||||
# 3) Validate entities
|
||||
for box in all_boxes:
|
||||
Report.critical_result(box.valid_test, box.id.IsValid())
|
||||
|
||||
for ramp in all_ramps:
|
||||
Report.critical_result(ramp.valid_test, ramp.id.IsValid())
|
||||
|
||||
# Setup ramp active position. The 'average' ramp is the first ramp, so we init to that.
|
||||
active_position = ramp_average.get_position()
|
||||
|
||||
# fmt: off
|
||||
height_matrix = [[0.0, 0.0, 0.0, 0.0], # Test 0 - Ramp 'Average'
|
||||
[0.0, 0.0, 0.0, 0.0], # Test 1 - Ramp 'Minimum'
|
||||
[0.0, 0.0, 0.0, 0.0], # Test 2 - Ramp 'Multiply'
|
||||
[0.0, 0.0, 0.0, 0.0]] # Test 3 - Ramp 'Maximum'
|
||||
# fmt: on
|
||||
|
||||
for row_index in range(len(height_matrix)):
|
||||
Report.info("********Starting Test {}********".format(row_index))
|
||||
reset_boxes()
|
||||
|
||||
# 4) Replace the ramp under the boxes
|
||||
ramp = all_ramps[row_index]
|
||||
ramp.set_position(active_position)
|
||||
|
||||
# 5) Drop the boxes
|
||||
drop_boxes()
|
||||
|
||||
fell_test = test_info.fell_tests[row_index]
|
||||
Report.critical_result(fell_test, helper.wait_for_condition(all_boxes_falling, TIMEOUT))
|
||||
|
||||
# 6) Wait for the box to hit the ground
|
||||
hit_ramp_test = test_info.hit_ramp_tests[row_index]
|
||||
Report.critical_result(hit_ramp_test, helper.wait_for_condition(ramp.all_boxes_hit, TIMEOUT))
|
||||
|
||||
# 7) Measure the bounce height
|
||||
peaked_test = test_info.peaked_tests[row_index]
|
||||
Report.critical_result(peaked_test, helper.wait_for_condition(all_boxes_peaked, TIMEOUT))
|
||||
|
||||
for column_index in range(len(height_matrix[row_index])):
|
||||
# Register the height the boxes bounced
|
||||
box = all_boxes[column_index]
|
||||
height_matrix[row_index][column_index] = box.bounce_height
|
||||
|
||||
ramp.set_position(ramp.start_position.Subtract(STANDBY_OFFSET))
|
||||
|
||||
# 8) Validate matrix
|
||||
log_matrix(height_matrix)
|
||||
Report.result(Tests.height_matrix_valid, validate_matrix(height_matrix))
|
||||
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C18981526_Material_RestitutionCombinePriority)
|
||||
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C19536274
|
||||
Test Case Title : Verify that the Get Collision Layer Name node prints the name of the collision layer
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19536274
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
test_entity_enabled = ("Test entity was enabled", "Test entity failed to enable")
|
||||
game_mode_entered = ("Successfully entered Game Mode", "Failed to enter Game Mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Summary:
|
||||
Loads a level that contains an entity with script canvas and PhysX Collider components
|
||||
|
||||
Level Description:
|
||||
Mostly empty level that contains a few different entities (one for each test using the level).
|
||||
Each entity is named after the testrail id for the respective test. Each entity contains PhysX Collider component
|
||||
and a Script Canvas Component with a matching .scriptcanvas file provided in the testrail.
|
||||
|
||||
Expected Behavior:
|
||||
The level loads, enters game mode, and the script canvas prints out "Layer Name: Right"
|
||||
|
||||
Test Steps:
|
||||
1) Load the test level
|
||||
2) Find and enable the test entity
|
||||
3) Enter game mode
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
|
||||
ACTIVE_STATUS = azlmbr.globals.property.EditorEntityStartStatus_StartActive
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the test level
|
||||
helper.open_level("Physics", "NameNode_Prints")
|
||||
|
||||
# 2) Find and enable the test entity
|
||||
test_entity = Entity.find_editor_entity("C19536274")
|
||||
test_entity.set_start_status("active")
|
||||
Report.result(Tests.test_entity_enabled, test_entity.get_start_status() == ACTIVE_STATUS)
|
||||
|
||||
# 3) Enter game mode
|
||||
helper.enter_game_mode(Tests.game_mode_entered)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C19536277
|
||||
Test Case Title : Verify that when a group is modified using ToggleCollisionLayer node such that the new group is not in the pre-existing groups, GetCollisionGroupName node prints no value
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19536277
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
test_entity_enabled = ("Test Entity successfully enabled", "Failed to enable Test Entity")
|
||||
game_mode_entered = ("Successfully entered Game Mode", "Failed to enter Game Mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Summary:
|
||||
Loads a level that contains an entity with script canvas and PhysX Collider components
|
||||
|
||||
Level Description:
|
||||
Mostly empty level that contains a few different entities (one for each test using the level).
|
||||
Each entity is named after the testrail id for the respective test. Each entity contains PhysX Collider component
|
||||
and a Script Canvas Component with a matching .scriptcanvas file provided in the testrail.
|
||||
|
||||
Expected Behavior:
|
||||
The level loads, enters game mode, and the script canvas prints out "GroupName: "
|
||||
|
||||
Test Steps:
|
||||
1) Load the test level
|
||||
2) Find and enable the test entity
|
||||
3) Enter game mode
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
|
||||
ACTIVE_STATUS = azlmbr.globals.property.EditorEntityStartStatus_StartActive
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the test level
|
||||
helper.open_level("Physics", "NameNode_Prints")
|
||||
|
||||
# 2) Find and enable the test entity
|
||||
test_entity = Entity.find_editor_entity("C19536277")
|
||||
test_entity.set_start_status("active")
|
||||
Report.result(Tests.test_entity_enabled, test_entity.get_start_status() == ACTIVE_STATUS)
|
||||
|
||||
# 3) Enter game mode
|
||||
helper.enter_game_mode(Tests.game_mode_entered)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C19578018
|
||||
Test Case Title : Verify that a shape collider component with no shape component indicates a missing service
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19578018
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_collider_entity = ("Created Collider Entity", "Failed to create Collider Entity")
|
||||
add_physx_shape_collider = ("PhysX Shape Collider added", "Failed to add PhysX Shape Collider")
|
||||
collider_component_inactive = ("Collider component is Inactive", "Collider component is Active")
|
||||
add_shape_component = ("Shape component added", "Failed to add Shape component")
|
||||
collider_component_active = ("Collider component is active", "Collider component is inactive")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C19578018_ShapeColliderWithNoShapeComponent():
|
||||
"""
|
||||
Summary:
|
||||
Create an Entity with PhysX Shape Collider component and verify that PhysX Shape Collider Component
|
||||
is inactive without shape component.
|
||||
|
||||
Expected Behavior:
|
||||
The PhysX Shape Collider component should be inactive.
|
||||
Verify that after a shape component is added, the warning goes away.
|
||||
|
||||
Test Steps:
|
||||
1) Load the level
|
||||
2) Add an entity with a PhysX Shape Collider component.
|
||||
3) Validate Collider Entity
|
||||
4) Validate PhysX Shape Collider component is inactive.
|
||||
5) Add Shape component to Entity
|
||||
6) Validate PhysX Shape Collider component is Active.
|
||||
7) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
|
||||
# Built-in Imports
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
# Helper Imports
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity
|
||||
|
||||
# Lumberyard Imports
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.editor as editor
|
||||
|
||||
def is_component_active(component_id) -> bool:
|
||||
"""
|
||||
Used to check if component is Active
|
||||
:return: boolean, True if component is active, else False
|
||||
"""
|
||||
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", component_id)
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Add an entity with a PhysX Shape Collider component.
|
||||
collider = EditorEntity.create_editor_entity("Collider")
|
||||
physx_component = collider.add_component("PhysX Shape Collider")
|
||||
|
||||
# 3) Validate Collider Entity
|
||||
Report.result(Tests.create_collider_entity, collider.id.IsValid())
|
||||
|
||||
# 4) Validate PhysX Shape Collider component is inactive.
|
||||
Report.result(Tests.add_physx_shape_collider, collider.has_component("PhysX Shape Collider"))
|
||||
Report.result(Tests.collider_component_inactive, not is_component_active(physx_component.id))
|
||||
|
||||
# 5) Add Shape component to Entity
|
||||
collider.add_component("Box Shape")
|
||||
Report.result(Tests.add_shape_component, collider.has_component("Box Shape"))
|
||||
|
||||
# 6) Validate PhysX Shape Collider component is Active.
|
||||
Report.result(Tests.collider_component_active, is_component_active(physx_component.id))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C19578018_ShapeColliderWithNoShapeComponent)
|
||||
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C19578021
|
||||
Test Case Title : Verify that a shape collider component may be added to an entity along with one or more PhysX collider components
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19578021
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_collider_entity = ("Created Collider Entity", "Failed to create Collider Entity")
|
||||
add_physx_shape_collider = ("PhysX Shape Collider added", "Failed to add PhysX Shape Collider")
|
||||
add_box_shape = ("Box Shape added", "Failed to add Box Shape")
|
||||
add_physx_collider = ("PhysX Collider added", "Failed to add PhysX Collider")
|
||||
no_warnings_found = ("Trace found no warnings", "One or more components has been removed")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C19578021_ShapeCollider_CanBeAdded():
|
||||
"""
|
||||
Summary:
|
||||
Adding a PhysX Collider component when a PhysX Shape Collider and Box Shape components are already present
|
||||
|
||||
Expected Behavior:
|
||||
When adding the PhysX Collider, there should be no warnings in the entity outliner
|
||||
|
||||
Test Steps:
|
||||
1) Load the empty level
|
||||
2) Create an entity
|
||||
3) Add the PhysX Shape Collider and a Box Shape components
|
||||
4) Start the Tracer to catch any warnings while adding the PhysX Collider
|
||||
5) Add the PhysX Collider component
|
||||
6) Verify there are no warnings in the entity outliner
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import Tracer
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
|
||||
# Lumberyard Imports
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the empty level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create an entity
|
||||
collider_entity = Entity.create_editor_entity("Collider")
|
||||
Report.result(Tests.create_collider_entity, collider_entity.id.IsValid())
|
||||
|
||||
# 3) Add the PhysX Shape Collider and a Box Shape components
|
||||
collider_entity.add_component("PhysX Shape Collider")
|
||||
Report.result(Tests.add_physx_shape_collider, collider_entity.has_component("PhysX Shape Collider"))
|
||||
|
||||
collider_entity.add_component("Box Shape")
|
||||
Report.result(Tests.add_box_shape, collider_entity.has_component("Box Shape"))
|
||||
|
||||
# 4) Start the Tracer to catch any warnings while adding the PhysX Collider
|
||||
with Tracer() as section_tracer:
|
||||
# 5) Add the PhysX Collider component
|
||||
collider_entity.add_component("PhysX Collider")
|
||||
Report.result(Tests.add_physx_collider, collider_entity.has_component("PhysX Collider"))
|
||||
|
||||
# 6) Verify there are no warnings in the entity outliner
|
||||
success_condition = not section_tracer.has_warnings and not section_tracer.has_errors
|
||||
Report.result(Tests.no_warnings_found, success_condition)
|
||||
|
||||
if not success_condition:
|
||||
exception_str = ""
|
||||
if section_tracer.has_warnings:
|
||||
exception_str += f"Warnings found: {section_tracer.warnings}\n"
|
||||
if section_tracer.has_errors:
|
||||
exception_str += f"Errors found: {section_tracer.errors}"
|
||||
Report.failure(exception_str)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C19578021_ShapeCollider_CanBeAdded)
|
||||
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C19723164
|
||||
Test Case Title : Verify that if we had 512 shape colliders in the level, the level does not crash
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/19723164
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
all_entities_created = ("All 512 entities have been created", "Failed to create all 512 entities")
|
||||
game_mode_entered = ("Entered Game Mode", "Failed to enter Game Mode")
|
||||
game_mode_exited = ("Exited Game Mode", "Failed to exit Game Mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C19723164_ShapeColliders_WontCrashEditor():
|
||||
"""
|
||||
Summary:
|
||||
Create 512 entities with shape colliders and verify stability
|
||||
|
||||
Expected Behavior:
|
||||
After 512 Shape Collider entities exist, the editor should not crash or dip in FPS
|
||||
|
||||
Test Steps:
|
||||
1) Load the empty level
|
||||
2) Create 512 entities with PhysX Shape Collider and Sphere Shape components
|
||||
3) Enter/Exit game mode and wait to see if editor crashes
|
||||
4) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
|
||||
# Lumberyard Imports
|
||||
import azlmbr.legacy.general as general
|
||||
|
||||
def idle_editor_for_check():
|
||||
"""
|
||||
This will be used to verify that the editor has not crashed by increasing the duration the editor is kept open
|
||||
"""
|
||||
# Enter game mode
|
||||
helper.enter_game_mode(Tests.game_mode_entered)
|
||||
# Wait 60 frames
|
||||
general.idle_wait_frames(60)
|
||||
# Exit game mode
|
||||
helper.exit_game_mode(Tests.game_mode_exited)
|
||||
# Wait 60 frames more
|
||||
general.idle_wait_frames(60)
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the empty level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create 512 entities with PhysX Shape Collider and Sphere Shape components
|
||||
entity_failure = False
|
||||
for i in range(1, 513):
|
||||
# Create Entity
|
||||
entity = Entity.create_editor_entity(f"Entity_{i}")
|
||||
|
||||
# Add components
|
||||
entity.add_component("PhysX Shape Collider")
|
||||
if i % 3 == 0:
|
||||
shape_component_name = "Capsule Shape"
|
||||
elif i % 2 == 0:
|
||||
shape_component_name = "Box Shape"
|
||||
else:
|
||||
shape_component_name = "Sphere Shape"
|
||||
|
||||
entity.add_component(shape_component_name)
|
||||
|
||||
# Verify the entity contains the components
|
||||
components_added = entity.has_component("PhysX Shape Collider") and entity.has_component(shape_component_name)
|
||||
if not components_added:
|
||||
entity_failure = True
|
||||
Report.info(f"Entity_{i} failed to add either PhysX Shape Collider or {shape_component_name}")
|
||||
Report.result(Tests.all_entities_created, not entity_failure)
|
||||
|
||||
# 3) Enter/Exit game mode and wait to see if editor crashes
|
||||
idle_editor_for_check()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C19723164_ShapeColliders_WontCrashEditor)
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C24308873
|
||||
# Test Case Title : Check that cylinder shape collider collides with terrain
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/24308873
|
||||
# A cylinder is suspended slightly over PhysX Terrain to check that it collides when dropped
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_cylinder = ("Cylinder entity found", "Cylinder entity not found")
|
||||
find_terrain = ("Terrain found", "Terrain not found")
|
||||
cylinder_above_terrain = ("Cylinder position above ground", "Cylinder is not above the ground")
|
||||
time_out = ("No time out occurred", "A time out occurred, please validate level setup")
|
||||
touched_ground = ("Touched ground before time out", "Did not touch ground before time out")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain():
|
||||
"""
|
||||
Summary:
|
||||
Runs a test to make sure that a PhysX Rigid Body and Cylinder Shape Collider can successfully collide with a PhysX Terrain entity.
|
||||
|
||||
Level Description:
|
||||
A cylinder with rigid body and collider is positioned above a PhysX terrain.
|
||||
|
||||
Expected Outcome:
|
||||
Once game mode is entered, the cylinder should fall toward and collide with the terrain.
|
||||
|
||||
Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Retrieve entities and positions
|
||||
3) Wait for cylinder to collide with terrain OR time out
|
||||
4) Exit game mode
|
||||
5) Close the editor
|
||||
|
||||
:return:
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
# Global time out
|
||||
TIME_OUT = 1.0
|
||||
|
||||
# 1) Open level / Enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve entities and positions
|
||||
cylinder_id = general.find_game_entity("PhysX_Cylinder")
|
||||
Report.critical_result(Tests.find_cylinder, cylinder_id.IsValid())
|
||||
|
||||
terrain_id = general.find_game_entity("PhysX_Terrain")
|
||||
Report.critical_result(Tests.find_terrain, terrain_id.IsValid())
|
||||
|
||||
cylinder_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", cylinder_id).GetPosition()
|
||||
terrain_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", terrain_id).GetPosition()
|
||||
Report.info_vector3(cylinder_pos, "Cylinder:")
|
||||
Report.info_vector3(terrain_pos, "Terrain:")
|
||||
Report.critical_result(
|
||||
Tests.cylinder_above_terrain,
|
||||
(cylinder_pos.z - terrain_pos.z) > 0.5,
|
||||
"Please make sure the cylinder entity is set above the terrain",
|
||||
)
|
||||
|
||||
# Enable gravity (just in case it is not enabled)
|
||||
if not azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", cylinder_id):
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", cylinder_id, True)
|
||||
|
||||
class TouchGround:
|
||||
value = False
|
||||
|
||||
# Collision event handler
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(terrain_id):
|
||||
Report.info("Touched ground")
|
||||
TouchGround.value = True
|
||||
|
||||
# Assign event handler to cylinder
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(cylinder_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
# 3) Wait for the cylinder to hit the ground OR time out
|
||||
test_completed = helper.wait_for_condition((lambda: TouchGround.value), TIME_OUT)
|
||||
|
||||
Report.critical_result(Tests.time_out, test_completed)
|
||||
Report.result(Tests.touched_ground, TouchGround.value)
|
||||
|
||||
# 4) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C24308873_CylinderShapeCollider_CollidesWithPhysXTerrain)
|
||||
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C28978033
|
||||
# Test Case Title : Check that WorldRequestBus works with PhysX ragdoll
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/28978033
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ragdoll = ("Ragdoll found", "Ragdoll not found")
|
||||
aabb_correct = ("Ragdoll AABB is correct", "Ragdoll AABB is incorrect")
|
||||
raycast_ref_found = ("Raycast reference entities found", "Raycast reference entities not found")
|
||||
raycast_hit = ("Raycast hit the ragdoll", "Raycast didn't hit the ragdoll")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C28978033_Ragdoll_WorldBodyBusTests():
|
||||
r"""
|
||||
Summary:
|
||||
Runs a test to make sure that a WorldBodyBus works property for ragdolls
|
||||
|
||||
Level Description:
|
||||
- TestRagdoll: ragdoll entity in the position(500, 500, 50)
|
||||
- RayStart: entity that points where to start the raycast_correct
|
||||
- RayEnd: entity that points where to end the raycast_correct
|
||||
|
||||
| ( ) |
|
||||
o - - - | ¦ | - - > o
|
||||
RayStart | / \ | RayEnd
|
||||
TestRagdoll
|
||||
|
||||
Expected Outcome:
|
||||
Once game mode is entered, the gravity is disabled, ragdoll AABB is checked and
|
||||
Raycast is done from RayStart to RayEnd. It should it hit the ragdoll.
|
||||
|
||||
:return:
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import vector3_str, aabb_str
|
||||
|
||||
# Global time out
|
||||
TIME_OUT = 1.0
|
||||
|
||||
EXPECTED_AABB = azlmbr.math.Aabb_CreateFromMinMax(azlmbr.math.Vector3(499.413513, 499.843201, 49.9907875),
|
||||
azlmbr.math.Vector3(500.586487, 500.178009, 51.7091103)) # By observation
|
||||
|
||||
AABB_THRESHOLD = 0.2 # Big threshold, even if we waited a single frame only, it can vary a lot in the simulation
|
||||
|
||||
# 1) Open level / Enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C28978033_Ragdoll_WorldBodyBusTests")
|
||||
|
||||
# Needs better solution
|
||||
general.idle_wait(6) # wait a little bit for ragdoll data to load
|
||||
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
general.idle_wait_frames(1)
|
||||
|
||||
gravity = azlmbr.math.Vector3(0.0, 0.0, 0.0)
|
||||
azlmbr.physics.WorldRequestBus(azlmbr.bus.Broadcast, "SetGravity", gravity)
|
||||
|
||||
|
||||
# 2) Retrieve entities and positions
|
||||
ragdoll_id = general.find_game_entity("TestRagdoll")
|
||||
Report.critical_result(Tests.find_ragdoll, ragdoll_id.IsValid())
|
||||
|
||||
aabb = azlmbr.physics.WorldBodyRequestBus(azlmbr.bus.Event, "GetAabb", ragdoll_id)
|
||||
Report.info("Ragdoll AABB:" + aabb_str(aabb))
|
||||
Report.info("Target Ragdoll AABB:" + aabb_str(EXPECTED_AABB))
|
||||
|
||||
is_expected_aabb_size = aabb.min.IsClose(EXPECTED_AABB.min, AABB_THRESHOLD) and aabb.max.IsClose(EXPECTED_AABB.max, AABB_THRESHOLD)
|
||||
Report.result(Tests.aabb_correct, is_expected_aabb_size)
|
||||
|
||||
raystart_id = general.find_game_entity("RayStart")
|
||||
rayend_id = general.find_game_entity("RayEnd")
|
||||
Report.critical_result(Tests.raycast_ref_found, raystart_id.IsValid(), rayend_id.IsValid())
|
||||
|
||||
raystart_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", raystart_id).GetPosition()
|
||||
rayend_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", rayend_id).GetPosition()
|
||||
|
||||
raycast_request = azlmbr.physics.RayCastRequest()
|
||||
raycast_request.Start = raystart_pos
|
||||
raycast_request.Distance = (rayend_pos.Subtract(raystart_pos)).GetLength()
|
||||
raycast_request.Direction = (rayend_pos.Subtract(raystart_pos)).GetNormalized()
|
||||
|
||||
result = azlmbr.physics.WorldBodyRequestBus(azlmbr.bus.Event, "RayCast", ragdoll_id, raycast_request)
|
||||
|
||||
# Following line crashes due to a hydra bug, use distance for now
|
||||
# has_hit_ragdoll = ragdoll_id.Equal(result.EntityId)
|
||||
has_hit_ragdoll = result.Distance > 0.1
|
||||
|
||||
Report.result(Tests.raycast_hit, has_hit_ragdoll)
|
||||
|
||||
# 4) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C28978033_Ragdoll_WorldBodyBusTests)
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C29032500
|
||||
# Test Case Title : Check that WorldRequestBus works with editor components
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/29032500
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
find_staticshapebox = ("Found StaticShapeBox", "Failed to find StaticShapeBox")
|
||||
find_staticsphere = ("Found StaticSphere", "Failed to find StaticSphere")
|
||||
find_staticbox = ("Found StaticBox", "Failed to find StaticBox")
|
||||
find_staticcapsule = ("Found StaticCapsule", "Failed to find StaticCapsule")
|
||||
find_staticmesh = ("Found StaticMesh", "Failed to find StaticMesh")
|
||||
find_shapebox = ("Found ShapeBox", "Failed to find ShapeBox")
|
||||
find_sphere = ("Found Sphere", "Failed to find Sphere")
|
||||
find_box = ("Found Box", "Failed to find Box")
|
||||
find_capsule = ("Found Capsule", "Failed to find Capsule")
|
||||
find_mesh = ("Found Mesh", "Failed to find Mesh")
|
||||
|
||||
aabb_staticshapebox = ("Correct AABB for StaticShapeBox", "Incorrect AABB for StaticShapeBox")
|
||||
aabb_staticsphere = ("Correct AABB for StaticSphere", "Incorrect AABB for StaticSphere")
|
||||
aabb_staticbox = ("Correct AABB for StaticBox", "Incorrect AABB for StaticBox")
|
||||
aabb_staticcapsule = ("Correct AABB for StaticCapsule", "Incorrect AABB for StaticCapsule")
|
||||
aabb_staticmesh = ("Correct AABB for StaticMesh", "Incorrect AABB for StaticMesh")
|
||||
aabb_shapebox = ("Correct AABB for ShapeBox", "Incorrect AABB for ShapeBox")
|
||||
aabb_sphere = ("Correct AABB for Sphere", "Incorrect AABB for Sphere")
|
||||
aabb_box = ("Correct AABB for Box", "Incorrect AABB for Box")
|
||||
aabb_capsule = ("Correct AABB for Capsule", "Incorrect AABB for Capsule")
|
||||
aabb_mesh = ("Correct AABB for Mesh", "Incorrect AABB for Mesh")
|
||||
|
||||
raycast_staticshapebox = ("Correct raycast for StaticShapeBox", "Incorrect raycast for StaticShapeBox")
|
||||
raycast_staticsphere = ("Correct raycast for StaticSphere", "Incorrect raycast for StaticSphere")
|
||||
raycast_staticbox = ("Correct raycast for StaticBox", "Incorrect raycast for StaticBox")
|
||||
raycast_staticcapsule = ("Correct raycast for StaticCapsule", "Incorrect raycast for StaticCapsule")
|
||||
raycast_staticmesh = ("Correct raycast for StaticMesh", "Incorrect raycast for StaticMesh")
|
||||
raycast_shapebox = ("Correct raycast for ShapeBox", "Incorrect raycast for ShapeBox")
|
||||
raycast_sphere = ("Correct raycast for Sphere", "Incorrect raycast for Sphere")
|
||||
raycast_box = ("Correct raycast for Box", "Incorrect raycast for Box")
|
||||
raycast_capsule = ("Correct raycast for Capsule", "Incorrect raycast for Capsule")
|
||||
raycast_mesh = ("Correct raycast for Mesh", "Incorrect raycast for Mesh")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C29032500_EditorComponents_WorldBodyBusWorks():
|
||||
r"""
|
||||
Summary:
|
||||
Runs a test to make sure that a WorldBodyBus works property for components
|
||||
|
||||
Level Description:
|
||||
- Dynamic
|
||||
- Sphere: Sphere with Rigid body
|
||||
- Box: Box with Rigid body
|
||||
- Capsule: Capsule with Rigid body
|
||||
- Mesh: Sedan car Mesh with Rigid body
|
||||
- ShapeBox: Shape Collider component + Box with rigidBody
|
||||
- Static
|
||||
- StaticSphere: Sphere with only Coollider component
|
||||
- StaticBox: Box with only Coollider component
|
||||
- StaticCapsule: Capsule with only Coollider component
|
||||
- StaticMesh: Sedan car Mesh with only Coollider component
|
||||
- StaticShapeBox: Only Shape Collider component + Box
|
||||
|
||||
TopDown view:
|
||||
____
|
||||
[!] o [ ] ( ) (____)
|
||||
ShapeBox Sphere Box Capsule Mesh
|
||||
____
|
||||
[!] o [ ] ( ) (____)
|
||||
StaticShapeBox StaticSphere StaticBox StaticCapsule StaticMesh
|
||||
|
||||
Expected Outcome:
|
||||
Checks AABB and RayCast functions of WorldBodyBus against the All the entities in the level
|
||||
|
||||
:return:
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import math
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import vector3_str, aabb_str
|
||||
|
||||
AABB_THRESHOLD = 0.01 # Entities won't move in the simulation
|
||||
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C29032500_EditorComponents_WorldBodyBusWorks")
|
||||
|
||||
def create_aabb(aabb_min_tuple, aabb_max_tuple):
|
||||
return azlmbr.math.Aabb_CreateFromMinMax(azlmbr.math.Vector3(aabb_min_tuple[0], aabb_min_tuple[1], aabb_min_tuple[2]),
|
||||
azlmbr.math.Vector3(aabb_max_tuple[0], aabb_max_tuple[1], aabb_max_tuple[2]))
|
||||
|
||||
class EntityData:
|
||||
def __init__(self, name, target_aabb):
|
||||
self.name = name
|
||||
self.target_aabb = target_aabb
|
||||
|
||||
def get_test_tuple_for_entity(testprefix, entity_name):
|
||||
return Tests.__dict__[testprefix.lower() + "_" + entity_name.lower()]
|
||||
|
||||
ENTITY_DATA = [ EntityData("ShapeBox", create_aabb((509.82, 523.08, 32.81), (510.82, 524.08, 33.81))),
|
||||
EntityData("Sphere", create_aabb((509.82, 526.39, 32.81), (510.82, 527.39, 33.81))),
|
||||
EntityData("Box", create_aabb((509.82, 529.66, 32.81), (510.82, 530.66, 33.81))),
|
||||
EntityData("Capsule", create_aabb((510.07, 533.70, 32.81), (510.57, 534.20, 33.81))),
|
||||
EntityData("Mesh", create_aabb((509.48, 536.30, 33.31), (511.16, 540.38, 34.38))),
|
||||
EntityData("StaticShapeBox", create_aabb((512.08, 523.08, 32.81), (513.08, 524.08, 33.81))),
|
||||
EntityData("StaticSphere", create_aabb((512.08, 526.39, 32.81), (513.08, 527.39, 33.81))),
|
||||
EntityData("StaticBox", create_aabb((512.08, 529.66, 32.81), (513.08, 530.66, 33.81))),
|
||||
EntityData("StaticCapsule", create_aabb((512.33, 533.70, 32.81), (512.83, 534.20, 33.81))),
|
||||
EntityData("StaticMesh", create_aabb((511.74, 536.30, 33.31), (513.42, 540.38, 34.38))) ] # AABB data obtained by observation
|
||||
|
||||
|
||||
for entity_data in ENTITY_DATA:
|
||||
entity_id = general.find_editor_entity(entity_data.name);
|
||||
Report.result(get_test_tuple_for_entity("find", entity_data.name), entity_id.IsValid())
|
||||
if entity_id.IsValid():
|
||||
# AABB test
|
||||
aabb = azlmbr.physics.WorldBodyRequestBus(azlmbr.bus.Event, "GetAabb", entity_id)
|
||||
Report.info("%s AABB -> %s" % (entity_data.name, aabb_str(aabb)))
|
||||
Report.info("%s expected AABB -> %s" % (entity_data.name, aabb_str(entity_data.target_aabb)))
|
||||
is_expected_aabb_size = aabb.min.IsClose(entity_data.target_aabb.min, AABB_THRESHOLD) and aabb.max.IsClose(entity_data.target_aabb.max, AABB_THRESHOLD)
|
||||
Report.result(get_test_tuple_for_entity("aabb", entity_data.name), is_expected_aabb_size)
|
||||
|
||||
# Raycast test
|
||||
entity_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", entity_id).GetPosition()
|
||||
raycast_request = azlmbr.physics.RayCastRequest()
|
||||
raycast_request.Start = entity_pos.Add(azlmbr.math.Vector3(0.0, 0.0, 100.0))
|
||||
raycast_request.Distance = 500.0
|
||||
raycast_request.Direction = azlmbr.math.Vector3(0.0, 0.0, -1.0)
|
||||
result = azlmbr.physics.WorldBodyRequestBus(azlmbr.bus.Event, "RayCast", entity_id, raycast_request)
|
||||
if result:
|
||||
# Following line crashes due to a hydra bug, use distance for now
|
||||
# has_hit = ragdoll_id.Equal(result.EntityId)
|
||||
has_hit = result.Distance > 0.1 and math.isclose(result.Position.x, entity_pos.x) and math.isclose(result.Position.y, entity_pos.y)
|
||||
Report.info("Hit: %s" % vector3_str(result.Position))
|
||||
Report.result(get_test_tuple_for_entity("raycast", entity_data.name), has_hit)
|
||||
else:
|
||||
Report.failure(get_test_tuple_for_entity("raycast", entity_data.name))
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C29032500_EditorComponents_WorldBodyBusWorks)
|
||||
@@ -0,0 +1,180 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C3510642
|
||||
# Test Case Title : Check that when no physX terrain component is added, collision of a PhysX object
|
||||
# with terrain does not work. Consequently, PhysX material assignment to terrain cannot be tested.
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/3510642
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_box = ("Box entity found", "Box entity not found")
|
||||
find_bumper = ("Bumper box found", "Bumper box not found")
|
||||
box_above_terrain = ("The tester box is above terrain", "The test box is not higher than the terrain")
|
||||
bumper_below_terrain = ("The bumper is below terrain", "The bumper is not lower than the terrain")
|
||||
gravity_works = ("Box fell", "Box did not fall")
|
||||
falls_below_terrain_height = ("Box is below terrain", "Box did not fall below terrain before timeout")
|
||||
collision_underground = ("Box collided underground", "Box did not collide underground before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C3510642_Terrain_NotCollideWithTerrain():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that when no PhysX Terrain component is added,
|
||||
PhysX objects will not collide with terrain.
|
||||
|
||||
Level Description:
|
||||
Box (entity) - suspended over the terrain with gravity enabled; contains a box mesh,
|
||||
PhysX Collider (Box shape), and PhysX RigidBody
|
||||
Bumper (entity) - suspended under the terrain with gravity disabled; contains box mesh,
|
||||
PhysX Collider (Box shape), and PhysX RigidBody
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the Box entity will experience gravity and fall toward the terrain.
|
||||
Since there is no PhysX Terrain component in the level, it should fall through the terrain.
|
||||
Once it passes through the terrain, it will collide with the Bumper entity in order to prove that
|
||||
it has passed the terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find the entities
|
||||
4) Get the starting z position of the boxes
|
||||
5) Check and report that the entities are at the correct heights
|
||||
6) Check that the gravity works and the box falls
|
||||
7) Check that the box hits the trigger and is below the terrain
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 2.0
|
||||
TERRAIN_HEIGHT = 32.0 # Default height of the terrain
|
||||
MIN_BELOW_TERRAIN = 0.5 # Minimum height below terrain the box must be in order to be 'under' it
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C3510642_Terrain_NotCollideWithTerrain")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Find the entities
|
||||
box_id = general.find_game_entity("Box")
|
||||
Report.critical_result(Tests.find_box, box_id.IsValid())
|
||||
|
||||
bumper_id = general.find_game_entity("Bumper")
|
||||
Report.critical_result(Tests.find_bumper, bumper_id.IsValid())
|
||||
|
||||
# 4) Get the starting z position of the boxes
|
||||
class Box:
|
||||
"""
|
||||
Class to hold boolean values for test checks.
|
||||
Attributes:
|
||||
start_position_z: The initial z position of the box
|
||||
position_z : The z position of the box
|
||||
fell : When the box falls any distance below its original position, the value should be set True
|
||||
below_terrain : When the box falls below the specified terrain height, the value should be set True
|
||||
"""
|
||||
|
||||
start_position_z = None
|
||||
position_z = None
|
||||
fell = False
|
||||
below_terrain = False
|
||||
|
||||
Box.start_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", box_id)
|
||||
bumper_start_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", bumper_id)
|
||||
|
||||
# 5) Check that the test box is above the terrain and the bumper box is below terrain
|
||||
Report.info("Terrain Height: {}".format(TERRAIN_HEIGHT))
|
||||
Report.info("Box start height: {}".format(Box.start_position_z))
|
||||
Report.result(Tests.box_above_terrain, Box.start_position_z > TERRAIN_HEIGHT)
|
||||
Report.info("Bumper start height: {}".format(bumper_start_z))
|
||||
Report.result(Tests.bumper_below_terrain, bumper_start_z < TERRAIN_HEIGHT)
|
||||
|
||||
# 6) Check that the gravity works and the box falls
|
||||
def box_fell():
|
||||
if not Box.fell:
|
||||
Box.position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", box_id)
|
||||
if Box.position_z < Box.start_position_z:
|
||||
Report.info("Box position is now lower than the starting position")
|
||||
Box.fell = True
|
||||
return Box.fell
|
||||
|
||||
helper.wait_for_condition(box_fell, TIMEOUT)
|
||||
Report.result(Tests.gravity_works, Box.fell)
|
||||
|
||||
# 7) Check that the box hits the trigger and is below the terrain
|
||||
# Setup for collision check
|
||||
class BumperTriggerEntered:
|
||||
value = False
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(bumper_id):
|
||||
BumperTriggerEntered.value = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(box_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
def box_below_terrain():
|
||||
if not Box.below_terrain:
|
||||
Box.position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", box_id)
|
||||
if Box.position_z < (TERRAIN_HEIGHT - MIN_BELOW_TERRAIN):
|
||||
Report.info("Box position is now below the terrain")
|
||||
Box.below_terrain = True
|
||||
return Box.below_terrain
|
||||
|
||||
def box_below_and_trigger_entered():
|
||||
return box_below_terrain() and BumperTriggerEntered.value
|
||||
|
||||
helper.wait_for_condition(box_below_and_trigger_entered, TIMEOUT)
|
||||
Report.result(Tests.collision_underground, BumperTriggerEntered.value)
|
||||
Report.result(Tests.falls_below_terrain_height, Box.below_terrain)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C3510642_Terrain_NotCollideWithTerrain)
|
||||
@@ -0,0 +1,374 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C3510644
|
||||
# Test Case Title : Check that the collision layer and collision group of the terrain can be changed
|
||||
# and the collision behavior of the terrain changes accordingly
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/3510644
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
box_1_a_valid = ("Box 1 A has been validated", "Box 1 A COULD NOT be validated")
|
||||
box_2_a_valid = ("Box 2 A has been validated", "Box 2 A COULD NOT be validated")
|
||||
terrain_a_valid = ("Terrain A has been validated", "Terrain A COULD NOT be validated")
|
||||
box_1_b_valid = ("Box 1 B has been validated", "Box 1 B COULD NOT be validated")
|
||||
box_2_b_valid = ("Box 2 B has been validated", "Box 2 B COULD NOT be validated")
|
||||
terrain_b_valid = ("Terrain B has been validated", "Terrain B COULD NOT be validated")
|
||||
box_1_a_pos_found = ("Box 1 A position found", "Box 1 A position NOT found")
|
||||
box_2_a_pos_found = ("Box 2 A position found", "Box 2 A position NOT found")
|
||||
terrain_a_pos_found = ("Terrain A position found", "Terrain A position NOT found")
|
||||
box_1_b_pos_found = ("Box 1 B position found", "Box 1 B position NOT found")
|
||||
box_2_b_pos_found = ("Box 2 B position found", "Box 2 B position NOT found")
|
||||
terrain_b_pos_found = ("Terrain B position found", "Terrain B position NOT found")
|
||||
box_1_a_did_collide_with_terrain = ("Box 1 A did collide with terrain", "Box 1 A DID NOT collide with terrain")
|
||||
box_1_a_did_not_pass_through_terrain = ("Box 1 A did not fall past the terrain", "Box 1 A DID fall past the terrain")
|
||||
box_2_a_did_not_collide_with_terrain = ("Box 2 A did not collide with terrain", "Box 2 A DID collide with terrain")
|
||||
box_2_a_did_pass_through_terrain = ("Box 2 A did fall past the terrain", "Box 2 A DID NOT fall past the terrain")
|
||||
box_1_b_did_not_collide_with_terrain = ("Box 1 B did not collide with terrain", "Box 1 B DID collide with terrain")
|
||||
box_1_b_did_pass_through_terrain = ("Box 1 B did fall past the terrain", "Box 1 B DID NOT fall past the terrain")
|
||||
box_2_b_did_collide_with_terrain = ("Box 2 B did collide with terrain", "Box 2 B DID NOT collide with terrain")
|
||||
box_2_b_did_not_pass_through_terrain = ("Box 2 B did not fall past the terrain", "Box 2 B DID fall past the terrain")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C3510644_Collider_CollisionGroups():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure PhysX collision groups dictate whether collisions happen or not.
|
||||
The test has two phases (A and B) for testing collision groups under different circumstances. Phase A
|
||||
is run first and upon success Phase B starts.
|
||||
|
||||
Level Description:
|
||||
Entities can be divided into 2 groups for the two phases, A and B. Each phase has identical entities with exception
|
||||
to Terrain, where Terrain_A has a collision group/layer set for demo_group1/demo1 and Terrain_B has a collision
|
||||
group/layer set for demo_group2/demo2.
|
||||
|
||||
Each Phase has two boxes, Box_1 and Box_2, where each box has it's collision group/layer set to it's number
|
||||
(1 or 2). Each box is positioned just above the Terrain with gravity enabled.
|
||||
|
||||
All entities for Phase B are deactivated by default. If Phase A is setup and executed successfully it's
|
||||
entities are deactivated and Phase B's entities are activated and validated before running the Phase B test.
|
||||
|
||||
Expected behavior:
|
||||
When Phase A starts, it's two boxes should fall toward the terrain. Once the boxes' behavior is validated the
|
||||
entities from Phase A are deactivated and Phase B's entities are activated. Like in Phase A, the boxes in Phase B
|
||||
should fall towards the terrain. If all goes as expected Box_1_A and Box_2_B should collide with teh terrain, and
|
||||
Box_2A and Box_1_B should fall through the terrain.
|
||||
|
||||
Test Steps:
|
||||
0) [Define helper classes and functions]
|
||||
1) Load the level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Phase A
|
||||
a) set up
|
||||
b) execute test
|
||||
c) log results (deactivate Phase A entities)
|
||||
5) Phase B
|
||||
a) set up (activate Phase B entities)
|
||||
b) execute test
|
||||
c) log results
|
||||
6) close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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.
|
||||
- The level for this test uses two PhysX Terrains and must be run with cmdline argument "-autotest_mode"
|
||||
to suppress the warning for having multiple terrains.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
|
||||
# ******* Helper Classes ********
|
||||
|
||||
# Phase A's test results
|
||||
class PhaseATestData:
|
||||
total_results = 2
|
||||
box_1_collided = False
|
||||
box_1_fell_through = True
|
||||
box_2_collided = False
|
||||
box_2_fell_through = False
|
||||
box_1 = None
|
||||
box_2 = None
|
||||
terrain = None
|
||||
box_1_pos = None
|
||||
box_2_pos = None
|
||||
terrain_pos = None
|
||||
|
||||
@staticmethod
|
||||
# Quick check for validating results for Phase A
|
||||
def valid():
|
||||
return (
|
||||
PhaseATestData.box_1_collided
|
||||
and PhaseATestData.box_2_fell_through
|
||||
and not PhaseATestData.box_1_fell_through
|
||||
and not PhaseATestData.box_2_collided
|
||||
)
|
||||
|
||||
# Phase B's test results
|
||||
class PhaseBTestData:
|
||||
total_results = 2
|
||||
box_1_collided = False
|
||||
box_1_fell_through = False
|
||||
box_2_collided = False
|
||||
box_2_fell_through = True
|
||||
box_1 = None
|
||||
box_2 = None
|
||||
terrain = None
|
||||
box_1_pos = None
|
||||
box_2_pos = None
|
||||
terrain_pos = None
|
||||
|
||||
@staticmethod
|
||||
# Quick check for validating results for Phase B
|
||||
def valid():
|
||||
return (
|
||||
not PhaseBTestData.box_1_collided
|
||||
and not PhaseBTestData.box_2_fell_through
|
||||
and PhaseBTestData.box_1_fell_through
|
||||
and PhaseBTestData.box_2_collided
|
||||
)
|
||||
|
||||
# **** Helper Functions ****
|
||||
|
||||
# ** Validation helpers **
|
||||
|
||||
# Attempts to validate an entity based on the name parameter
|
||||
def validate_entity(entity_name, msg_tuple):
|
||||
# type: (str, (str, str)) -> EntityId
|
||||
entity_id = general.find_game_entity(entity_name)
|
||||
Report.critical_result(msg_tuple, entity_id.IsValid())
|
||||
return entity_id
|
||||
|
||||
# Attempts to retrieve an entity's initial position and logs result
|
||||
def validate_initial_position(entity_id, msg_tuple):
|
||||
# type: (EntityId, (str, str)) -> azlmbr.math.Vector3
|
||||
# Attempts to validate and return the entity's initial position.
|
||||
# logs the result to Report.result() using the tuple parameter
|
||||
|
||||
pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", entity_id)
|
||||
valid = not (pos is None or pos.IsZero())
|
||||
entity_name = azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", entity_id)
|
||||
Report.critical_result(msg_tuple, valid)
|
||||
Report.info_vector3(pos, "{} initial position:".format(entity_name))
|
||||
return pos
|
||||
|
||||
# ** Phase completion checks checks **
|
||||
|
||||
# Checks if we are done collecting data for phase A
|
||||
def done_collecting_results_a():
|
||||
# type: () -> bool
|
||||
|
||||
# Update positions
|
||||
PhaseATestData.box_1_pos = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldTranslation", PhaseATestData.box_1
|
||||
)
|
||||
PhaseATestData.box_2_pos = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldTranslation", PhaseATestData.box_2
|
||||
)
|
||||
|
||||
# Check for boxes to fall through terrain
|
||||
if PhaseATestData.box_1_pos.z < PhaseATestData.terrain_pos.z:
|
||||
PhaseATestData.box_1_fell_through = True
|
||||
else:
|
||||
PhaseATestData.box_1_fell_through = False
|
||||
|
||||
if PhaseATestData.box_2_pos.z < PhaseATestData.terrain_pos.z:
|
||||
PhaseATestData.box_2_fell_through = True
|
||||
else:
|
||||
PhaseATestData.box_2_fell_through = False
|
||||
|
||||
results = 0
|
||||
if PhaseATestData.box_1_collided or PhaseATestData.box_1_fell_through:
|
||||
results += 1
|
||||
if PhaseATestData.box_2_collided or PhaseATestData.box_2_fell_through:
|
||||
results += 1
|
||||
return results == PhaseATestData.total_results
|
||||
|
||||
# Checks if we are done collecting data for phase B
|
||||
def done_collecting_results_b():
|
||||
# type: () -> bool
|
||||
|
||||
# Update positions
|
||||
PhaseBTestData.box_1_pos = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldTranslation", PhaseBTestData.box_1
|
||||
)
|
||||
PhaseBTestData.box_2_pos = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldTranslation", PhaseBTestData.box_2
|
||||
)
|
||||
|
||||
# Check for boxes to fall through terrain
|
||||
if PhaseBTestData.box_1_pos.z < PhaseBTestData.terrain_pos.z:
|
||||
PhaseBTestData.box_1_fell_through = True
|
||||
else:
|
||||
PhaseBTestData.box_1_fell_through = False
|
||||
|
||||
if PhaseBTestData.box_2_pos.z < PhaseBTestData.terrain_pos.z:
|
||||
PhaseBTestData.box_2_fell_through = True
|
||||
else:
|
||||
PhaseBTestData.box_2_fell_through = False
|
||||
|
||||
results = 0
|
||||
if PhaseBTestData.box_1_collided or PhaseBTestData.box_1_fell_through:
|
||||
results += 1
|
||||
if PhaseBTestData.box_2_collided or PhaseBTestData.box_2_fell_through:
|
||||
results += 1
|
||||
return results == PhaseBTestData.total_results
|
||||
|
||||
# **** Event Handlers ****
|
||||
|
||||
# Collision even handler for Phase A
|
||||
def on_collision_begin_a(args):
|
||||
# type: ([EntityId]) -> None
|
||||
collider_id = args[0]
|
||||
if (not PhaseATestData.box_1_collided) and PhaseATestData.box_1.Equal(collider_id):
|
||||
Report.info("Box_1_A / Terrain_A collision detected")
|
||||
PhaseATestData.box_1_collided = True
|
||||
if (not PhaseATestData.box_2_collided) and PhaseATestData.box_2.Equal(collider_id):
|
||||
Report.info("Box_2_A / Terrain_A collision detected")
|
||||
PhaseATestData.box_2_collided = True
|
||||
|
||||
# Collision event handler for Phase B
|
||||
def on_collision_begin_b(args):
|
||||
# type: ([EntityId]) -> None
|
||||
collider_id = args[0]
|
||||
if (not PhaseBTestData.box_1_collided) and PhaseBTestData.box_1.Equal(collider_id):
|
||||
Report.info("Box_1_B / Terrain_B collision detected")
|
||||
PhaseBTestData.box_1_collided = True
|
||||
if (not PhaseBTestData.box_2_collided) and PhaseBTestData.box_2.Equal(collider_id):
|
||||
Report.info("Box_2_B / Terrain_B collision detected")
|
||||
PhaseBTestData.box_2_collided = True
|
||||
|
||||
TIME_OUT = 1.5
|
||||
|
||||
# 1) Open level
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C3510644_Collider_CollisionGroups")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
PhaseATestData.box_1 = validate_entity("Box_1_A", Tests.box_1_a_valid)
|
||||
PhaseATestData.box_2 = validate_entity("Box_2_A", Tests.box_2_a_valid)
|
||||
PhaseATestData.terrain = validate_entity("Terrain_Entity_A", Tests.terrain_a_valid)
|
||||
PhaseBTestData.box_1 = validate_entity("Box_1_B", Tests.box_1_b_valid)
|
||||
PhaseBTestData.box_2 = validate_entity("Box_2_B", Tests.box_2_b_valid)
|
||||
PhaseBTestData.terrain = validate_entity("Terrain_Entity_B", Tests.terrain_b_valid)
|
||||
|
||||
# Make sure Phase B objects are disabled
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseBTestData.box_1)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseBTestData.box_2)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseBTestData.terrain)
|
||||
|
||||
# 4) *********** Phase A *****************
|
||||
|
||||
# 4.a) ** Set Up **
|
||||
|
||||
Report.info(" **** Beginning Phase A **** ")
|
||||
|
||||
# Locate Phase A entities
|
||||
PhaseATestData.box_1_pos = validate_initial_position(PhaseATestData.box_1, Tests.box_1_a_pos_found)
|
||||
PhaseATestData.box_2_pos = validate_initial_position(PhaseATestData.box_2, Tests.box_2_a_pos_found)
|
||||
PhaseATestData.terrain_pos = validate_initial_position(PhaseATestData.terrain, Tests.terrain_a_pos_found)
|
||||
|
||||
# Assign Phase A event handler
|
||||
handler_a = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler_a.connect(PhaseATestData.terrain)
|
||||
handler_a.add_callback("OnCollisionBegin", on_collision_begin_a)
|
||||
|
||||
# 4.b) Execute Phase A
|
||||
if not helper.wait_for_condition(done_collecting_results_a, TIME_OUT):
|
||||
Report.info("Phase A timed out: make sure the level is set up properly or adjust time out threshold")
|
||||
|
||||
# 4.c) Log results for Phase A
|
||||
Report.result(Tests.box_1_a_did_collide_with_terrain, PhaseATestData.box_1_collided)
|
||||
Report.result(Tests.box_1_a_did_not_pass_through_terrain, not PhaseATestData.box_1_fell_through)
|
||||
Report.info_vector3(PhaseATestData.box_1_pos, "Box_1_A's final position:")
|
||||
|
||||
Report.result(Tests.box_2_a_did_pass_through_terrain, PhaseATestData.box_2_fell_through)
|
||||
Report.result(Tests.box_2_a_did_not_collide_with_terrain, not PhaseATestData.box_2_collided)
|
||||
Report.info_vector3(PhaseATestData.box_2_pos, "Box_2_A's final position:")
|
||||
|
||||
if not PhaseATestData.valid():
|
||||
Report.info("Phase A failed test")
|
||||
|
||||
# Deactivate entities for Phase A
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseATestData.box_1)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseATestData.box_2)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", PhaseATestData.terrain)
|
||||
|
||||
# 5) *********** Phase B *****************
|
||||
|
||||
# 5.a) ** Set Up **
|
||||
Report.info(" *** Beginning Phase B *** ")
|
||||
# Activate entities for Phase B
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", PhaseBTestData.box_1)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", PhaseBTestData.box_2)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", PhaseBTestData.terrain)
|
||||
|
||||
# Initialize positions for Phase B
|
||||
PhaseBTestData.box_1_pos = validate_initial_position(PhaseBTestData.box_1, Tests.box_1_b_pos_found)
|
||||
PhaseBTestData.box_2_pos = validate_initial_position(PhaseBTestData.box_2, Tests.box_2_b_pos_found)
|
||||
PhaseBTestData.terrain_pos = validate_initial_position(PhaseBTestData.terrain, Tests.terrain_b_pos_found)
|
||||
|
||||
# Assign Phase B event handler
|
||||
handler_b = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler_b.connect(PhaseBTestData.terrain)
|
||||
handler_b.add_callback("OnCollisionBegin", on_collision_begin_b)
|
||||
|
||||
# 5.b) Execute Phase B
|
||||
if not helper.wait_for_condition(done_collecting_results_b, TIME_OUT):
|
||||
Report.info("Phase B timed out: make sure the level is set up properly or adjust time out threshold")
|
||||
|
||||
# 5.c) Log results for Phase B
|
||||
Report.result(Tests.box_1_b_did_not_collide_with_terrain, not PhaseBTestData.box_1_collided)
|
||||
Report.result(Tests.box_1_b_did_pass_through_terrain, PhaseBTestData.box_1_fell_through)
|
||||
Report.info_vector3(PhaseBTestData.box_1_pos, "Box_1_B's final position:")
|
||||
|
||||
Report.result(Tests.box_2_b_did_not_pass_through_terrain, not PhaseBTestData.box_2_fell_through)
|
||||
Report.result(Tests.box_2_b_did_collide_with_terrain, PhaseBTestData.box_2_collided)
|
||||
Report.info_vector3(PhaseBTestData.box_2_pos, "Box_2_B's final position:")
|
||||
|
||||
if not PhaseBTestData.valid():
|
||||
Report.info("Phase B failed test")
|
||||
|
||||
# 6) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
Report.info(" **** TEST FINISHED ****")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C3510644_Collider_CollisionGroups)
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : 4044455
|
||||
# Test Case Title : Verify that any change in any of the values including the name of the material,
|
||||
# once saved, is immediately reflected in the component and functionality
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044455
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_0 = ("Entered game mode 0", "Failed to enter game mode 0")
|
||||
exit_game_mode_0 = ("Exited game mode 0", "Couldn't exit game mode 0")
|
||||
enter_game_mode_1 = ("Entered game mode 1", "Failed to enter game mode 1")
|
||||
exit_game_mode_1 = ("Exited game mode 1", "Couldn't exit game mode 1")
|
||||
terrain_found_0 = ("terrain entity found 0", "terrain entity not found 0")
|
||||
block_found_0 = ("block entity found 0", "block entity not found 0")
|
||||
trigger_found_0 = ("trigger entity found 0", "trigger entity not found 0")
|
||||
terrain_found_1 = ("terrain entity found 1", "terrain entity not found 1")
|
||||
block_found_1 = ("block entity found 1", "block entity not found 1")
|
||||
trigger_found_1 = ("trigger entity found 1", "trigger entity not found 1")
|
||||
material_changes = ("material changes were made", "material changes couldn't be made")
|
||||
# Material Modifications
|
||||
static_friction = ("Static friction was modified", "Static friction wasn't modified")
|
||||
dynamic_friction = ("Dynamic friction was modified", "Dynamic friction wasn't modified")
|
||||
restitution = ("Restitution was modified", "Restition wasn't modified")
|
||||
friction_combine = ("Friction combine was modified", "Friction combine wasn't modified")
|
||||
restitution_combine = ("Restition combine was modified", "Restitution combine wasn't modified")
|
||||
delete_material = ("Material deleted successfully", "Material wasn't deleted")
|
||||
|
||||
# sphere_0 test 0
|
||||
sphere_0_found_0 = ("Test 0: sphere_0 found", "Test 0: sphere_0 not found")
|
||||
sphere_0_initial_position_0 = ("Test 0: sphere_0 is in valid position", "Test 0: sphere_0 isn't in valid position")
|
||||
sphere_0_initial_velocity_0 = ("Test 0: sphere_0 initial velocity valid", "Test 0: sphere_0 initial velocity invalid")
|
||||
sphere_0_collision_0 = ("Test 0: sphere_0 collided with terrain", "Test 0: sphere_0 collided with terrain")
|
||||
sphere_0_final_position_0 = ("Test 0: sphere_0 final position valid", "Test 0: sphere_0 final position invalid")
|
||||
sphere_0_final_velocity_0 = ("Test 0: sphere_0 final velocity valid", "Test 0: sphere_0 final velocity invalid")
|
||||
# sphere_0 test 1
|
||||
sphere_0_found_1 = ("Test 1: sphere_0 found", "Test 1: sphere_0 not found")
|
||||
sphere_0_initial_position_1 = ("Test 1: sphere_0 is in valid position", "Test 1: sphere_0 isn't in valid position")
|
||||
sphere_0_initial_velocity_1 = ("Test 1: sphere_0 initial velocity valid", "Test 1: sphere_0 initial velocity invalid")
|
||||
sphere_0_collision_1 = ("Test 1: sphere_0 collided with terrain", "Test 1: sphere_0 collided with terrain")
|
||||
sphere_0_final_position_1 = ("Test 1: sphere_0 final position valid", "Test 1: sphere_0 final position invalid")
|
||||
sphere_0_final_velocity_1 = ("Test 1: sphere_0 final velocity valid", "Test 1: sphere_0 final velocity invalid")
|
||||
# sphere_1 test 0
|
||||
sphere_1_found_0 = ("Test 0: sphere_1 found", "Test 0: sphere_1 not found")
|
||||
sphere_1_initial_position_0 = ("Test 0: sphere_1 is in valid position", "Test 0: sphere_1 isn't in valid position")
|
||||
sphere_1_initial_velocity_0 = ("Test 0: sphere_1 initial velocity valid", "Test 0: sphere_1 initial velocity invalid")
|
||||
sphere_1_collision_0 = ("Test 0: sphere_1 collided with terrain", "Test 0: sphere_1 collided with terrain")
|
||||
sphere_1_final_position_0 = ("Test 0: sphere_1 final position valid", "Test 0: sphere_1 final position invalid")
|
||||
sphere_1_final_velocity_0 = ("Test 0: sphere_1 final velocity valid", "Test 0: sphere_1 final velocity invalid")
|
||||
# sphere_1 test 1
|
||||
sphere_1_found_1 = ("Test 1: sphere_1 found", "Test 1: sphere_1 not found")
|
||||
sphere_1_initial_position_1 = ("Test 1: sphere_1 is in valid position", "Test 1: sphere_1 isn't in valid position")
|
||||
sphere_1_initial_velocity_1 = ("Test 1: sphere_1 initial velocity valid", "Test 1: sphere_1 initial velocity invalid")
|
||||
sphere_1_collision_1 = ("Test 1: sphere_1 collided with terrain", "Test 1: sphere_1 collided with terrain")
|
||||
sphere_1_final_position_1 = ("Test 1: sphere_1 final position valid", "Test 1: sphere_1 final position invalid")
|
||||
sphere_1_final_velocity_1 = ("Test 1: sphere_1 final velocity valid", "Test 1: sphere_1 final velocity invalid")
|
||||
# sphere_2 test 0
|
||||
sphere_2_found_0 = ("Test 0: sphere_2 found", "Test 0: sphere_2 not found")
|
||||
sphere_2_initial_position_0 = ("Test 0: sphere_2 is in valid position", "Test 0: sphere_2 isn't in valid position")
|
||||
sphere_2_initial_velocity_0 = ("Test 0: sphere_2 initial velocity valid", "Test 0: sphere_2 initial velocity invalid")
|
||||
sphere_2_collision_0 = ("Test 0: sphere_2 collided with terrain", "Test 0: sphere_2 collided with terrain")
|
||||
sphere_2_final_position_0 = ("Test 0: sphere_2 final position valid", "Test 0: sphere_2 final position invalid")
|
||||
sphere_2_final_velocity_0 = ("Test 0: sphere_2 final velocity valid", "Test 0: sphere_2 final velocity invalid")
|
||||
# sphere_2 test 1
|
||||
sphere_2_found_1 = ("Test 1: sphere_2 found", "Test 1: sphere_2 not found")
|
||||
sphere_2_initial_position_1 = ("Test 1: sphere_2 is in valid position", "Test 1: sphere_2 isn't in valid position")
|
||||
sphere_2_initial_velocity_1 = ("Test 1: sphere_2 initial velocity valid", "Test 1: sphere_2 initial velocity invalid")
|
||||
sphere_2_collision_1 = ("Test 1: sphere_2 collided with terrain", "Test 1: sphere_2 collided with terrain")
|
||||
sphere_2_final_position_1 = ("Test 1: sphere_2 final position valid", "Test 1: sphere_2 final position invalid")
|
||||
sphere_2_final_velocity_1 = ("Test 1: sphere_2 final velocity valid", "Test 1: sphere_2 final velocity invalid")
|
||||
|
||||
# cube_0 test 0
|
||||
cube_0_found_0 = ("Test 0: cube_0 found", "Test 0: cube_0 not found")
|
||||
cube_0_initial_position_0 = ("Test 0: cube_0 is in correct position", "Test 0: cube_0 isn't in correct position")
|
||||
cube_0_initial_velocity_0 = ("Test 0: cube_0 initial velocity valid", "Test 0: cube_0 initial velocity invalid")
|
||||
cube_0_final_position_0 = ("Test 0: cube_0 has stopped moving", "Test 0: cube_0 hasn't stopped moving")
|
||||
cube_0_final_velocity_0 = ("Test 0: cube_0 final velocity valid", "Test 0: cube_0 final velocity invalid")
|
||||
# cube_0 test 1
|
||||
cube_0_found_1 = ("Test 1: cube_0 found", "Test 1: cube_0 not found")
|
||||
cube_0_initial_position_1 = ("Test 1: cube_0 is in correct position", "Test 1: cube_0 isn't in correct position")
|
||||
cube_0_initial_velocity_1 = ("Test 1: cube_0 initial velocity valid", "Test 1: cube_0 initial velocity invalid")
|
||||
cube_0_final_position_1 = ("Test 1: cube_0 has stopped moving", "Test 1: cube_0 has not stopped moving")
|
||||
cube_0_final_velocity_1 = ("Test 1: cube_0 final velocity valid", "Test 1: cube_0 final velocity invalid")
|
||||
# cube_1 test 0
|
||||
cube_1_found_0 = ("Test 0: cube_1 found", "Test 0: cube_1 not found")
|
||||
cube_1_initial_position_0 = ("Test 0: cube_1 is in correct position", "Test 0: cube_1 isn't in correct position")
|
||||
cube_1_initial_velocity_0 = ("Test 0: cube_1 initial velocity valid", "Test 0: cube_1 initial velocity invalid")
|
||||
cube_1_final_position_0 = ("Test 0: cube_1 has stopped moving", "Test 0: cube_1 hasn't stopped moving")
|
||||
cube_1_final_velocity_0 = ("Test 0: cube_1 final velocity valid", "Test 0: cube_1 final velocity invalid")
|
||||
# cube_1 test 1
|
||||
cube_1_found_1 = ("Test 1: cube_1 found", "Test 1: cube_1 not found")
|
||||
cube_1_initial_position_1 = ("Test 1: cube_1 is in correct position", "Test 1: cube_1 isn't in correct position")
|
||||
cube_1_initial_velocity_1 = ("Test 1: cube_1 initial velocity valid", "Test 1: cube_1 initial velocity invalid")
|
||||
cube_1_final_position_1 = ("Test 1: cube_1 has stopped moving", "Test 1: cube_1 hasn't stopped moving")
|
||||
cube_1_final_velocity_1 = ("Test 1: cube_1 final velocity valid", "Test 1: cube_1 final velocity invalid")
|
||||
# cube_2 test 0
|
||||
cube_2_found_0 = ("Test 0: cube_2 found", "Test 0: cube_2 not found")
|
||||
cube_2_initial_position_0 = ("Test 0: cube_2 is in correct position", "Test 0: cube_2 isn't in correct position")
|
||||
cube_2_initial_velocity_0 = ("Test 0: cube_2 initial velocity valid", "Test 0: cube_2 initial velocity invalid")
|
||||
cube_2_final_position_0 = ("Test 0: cube_2 has stopped moving", "Test 0: cube_2 hasn't stopped moving")
|
||||
cube_2_final_velocity_0 = ("Test 0: cube_2 final velocity valid", "Test 0: cube_2 final velocity invalid")
|
||||
# cube_2 test 1
|
||||
cube_2_found_1 = ("Test 1: cube_2 found", "Test 1: cube_2 not found")
|
||||
cube_2_initial_position_1 = ("Test 1: cube_2 is in correct position", "Test 1: cube_2 isn't in correct position")
|
||||
cube_2_initial_velocity_1 = ("Test 1: cube_2 initial velocity valid", "Test 1: cube_2 initial velocity invalid")
|
||||
cube_2_final_position_1 = ("Test 1: cube_2 has stopped moving", "Test 1: cube_2 hasn't stopped moving")
|
||||
cube_2_final_velocity_1 = ("Test 1: cube_2 final velocity valid", "Test 1: cube_2 final velocity invalid")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4044455_Material_libraryChangesInstantly():
|
||||
"""
|
||||
Summary: Verify that any change in any of the values of the material, once saved, is immediately reflected
|
||||
in the component and functionality
|
||||
|
||||
Level Description:
|
||||
Three sphere entities (sphere_0, sphere_1, sphere_2) - They start between the terrain and trigger with
|
||||
velocity of 10 m/s in the negative z direction; has physx collider with sphere shape, physx rigid body,
|
||||
sphere shape, has "to_change_restitution", "to_change_restitution_combine", and "to_delete" materials
|
||||
applied respectively.
|
||||
Three cube entities (cube_0, cube_1, cube_2) - On top of the negative y side of the block, gravity enabled, no
|
||||
initial velocity, 0.0 linear damping; has physx collider with box shape, physx rigid body, box shape, and
|
||||
has "to_change_static_friction", "to_change_dynamic_friction", and "to_change_friction_combine" materials
|
||||
applied respectively
|
||||
trigger - Stationary trigger above the three spheres, used to indicate if the material was modified correctly; has
|
||||
physx collider with box shape (20.0, 5.0, 0.25) and trigger enabled and box shape (20.0, 5.0, 0.25)
|
||||
block - Stationary block that has all cubes sitting on it. Used as a controlled surface for friction testing; has
|
||||
physx collider with box shape (10.0, 10.0, 10.0) and box shape (10.0, 10.0, 10.0)
|
||||
terrain - terrain component holder lined up with terrain default height; has terrain component
|
||||
|
||||
Material Library: Contains a different material for each entity with distinct collider shape. These materials are
|
||||
designed to provide the largest difference in result after change (sphere: velocity, cube: distance). All spheres
|
||||
should not bounce off of the terrain initially but will be able to hit the trigger post change. The cubes will
|
||||
experience higher friction after the change and not travel as far along the ramp entity.
|
||||
|
||||
Expected Behavior: Before editing the material library the spheres in both levels will not bounce off of the terrain
|
||||
and the cubes will go some distance along the ramp. After the material file is edited the spheres will bounce off
|
||||
of the terrain and hit the trigger and the cubes will travel a smaller distance than before
|
||||
|
||||
Main Script Steps:
|
||||
1) Open Level
|
||||
2) Create test objects
|
||||
3) Run test 0
|
||||
4) Modify material library
|
||||
5) Run test 1
|
||||
6) Validate results
|
||||
7) Close Editor
|
||||
|
||||
Test Loop Steps:
|
||||
1) Enter game mode
|
||||
2) Find and Validate entities
|
||||
3) Wait for spheres to collide with terrain
|
||||
4) Wait for spheres to enter the trigger
|
||||
5) Log sphere results
|
||||
6) Push cubes
|
||||
7) Wait for cubes to stop moving
|
||||
8) Log and validate cube results
|
||||
9) Exit game mode
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as math
|
||||
|
||||
from Physmaterial_Editor import Physmaterial_Editor
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = 0.001
|
||||
# Timeout in seconds
|
||||
TIMEOUT = 2.0
|
||||
CUBE_IMPULSE = math.Vector3(0.0, 5.0, 0.0)
|
||||
CUBE_Y_POSITION = 536.0
|
||||
CUBE_INITIAL_VELOCITY = math.Vector3(0.0, 0.0, 0.0)
|
||||
PROPAGATION_FRAMES = 500
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
terrain_id = None
|
||||
def __init__(self, name, test_index):
|
||||
# Type (str, int, int, Entity) -> None
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.test_index = test_index
|
||||
self.collision_happened = False
|
||||
self.hit_trigger = False
|
||||
# Check Entity ID
|
||||
found = Tests.__dict__["{}_found_{}".format(self.name, self.test_index)]
|
||||
Report.critical_result(found, self.id.IsValid())
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
@property
|
||||
def velocity(self):
|
||||
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
|
||||
)
|
||||
|
||||
@property
|
||||
def is_not_moving(self):
|
||||
# Type () -> bool
|
||||
return (
|
||||
abs(self.velocity.x) < FLOAT_THRESHOLD
|
||||
and abs(self.velocity.y) < FLOAT_THRESHOLD
|
||||
and abs(self.velocity.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
# Type ([]) -> None
|
||||
if Entity.terrain_id.equal(args[0]):
|
||||
self.collision_happened = True
|
||||
|
||||
class Sphere(Entity):
|
||||
def __init__(self, name, test_index):
|
||||
Entity.__init__(self, name, test_index)
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
class Material_Test:
|
||||
def __init__(self, index):
|
||||
self.index = index
|
||||
self.sphere_list = None
|
||||
# List to hold how far the cube traveled
|
||||
self.cube_distances = []
|
||||
# List to hold wether the sphere hit the trigger and its velocities
|
||||
self.sphere_values = []
|
||||
|
||||
def verify_sphere_initial_position(self, sphere, terrain, trigger):
|
||||
# Type (Entity, Entity, Entity) -> None
|
||||
# Validates sphere is where it should be
|
||||
position_valid = terrain.position.z < sphere.position.z < trigger.position.z
|
||||
initial_position = Tests.__dict__["{}_initial_position_{}".format(sphere.name, self.index)]
|
||||
Report.critical_result(initial_position, position_valid)
|
||||
|
||||
def verify_sphere_initial_velocity(self, sphere):
|
||||
# Type (Entity) -> None
|
||||
# Validates that sphere in moving in the correct direction
|
||||
initial_velocity = Tests.__dict__["{}_initial_velocity_{}".format(sphere.name, self.index)]
|
||||
Report.critical_result(initial_velocity, not sphere.is_moving_up)
|
||||
|
||||
def verify_sphere_collision(self, sphere):
|
||||
# Type (Entity) -> None
|
||||
# Reports sphere collision, ends test if it hasn't occurred
|
||||
collision = Tests.__dict__["{}_collision_{}".format(sphere.name, self.index)]
|
||||
Report.critical_result(collision, sphere.collision_happened)
|
||||
|
||||
def verify_sphere_final_velocity(self, sphere):
|
||||
# Type (Entity) -> None
|
||||
# Validates that sphere is moving in the correct direction
|
||||
final_velocity = Tests.__dict__["{}_final_velocity_{}".format(sphere.name, self.index)]
|
||||
Report.result(final_velocity, sphere.is_moving_up or sphere.is_not_moving)
|
||||
|
||||
def verify_sphere_final_position(self, sphere, terrain):
|
||||
# Type (Entity, Entity) -> None
|
||||
# Validats that sphere is not where it shouldn't be
|
||||
final_position = Tests.__dict__["{}_final_position_{}".format(sphere.name, self.index)]
|
||||
Report.result(final_position, sphere.position.z > terrain.position.z)
|
||||
|
||||
def verify_cube_initial_position(self, cube, block):
|
||||
# Type (Entity, Entity) -> None
|
||||
# Cube initially starts at a standstill
|
||||
initial_position = Tests.__dict__["{}_initial_position_{}".format(cube.name, self.index)]
|
||||
Report.result(
|
||||
initial_position,
|
||||
cube.position.z > block.position.z and abs(cube.position.y - CUBE_Y_POSITION) < FLOAT_THRESHOLD,
|
||||
)
|
||||
|
||||
def verify_cube_initial_velocity(self, cube):
|
||||
# Type (Entity) -> None
|
||||
# Ensures that the cube starts not moving
|
||||
initial_velocity = Tests.__dict__["{}_initial_velocity_{}".format(cube.name, self.index)]
|
||||
Report.result(initial_velocity, cube.velocity.IsClose(CUBE_INITIAL_VELOCITY, 0.01))
|
||||
|
||||
def push_cubes(self, cube_list):
|
||||
# Type ([Entity]) -> None
|
||||
# Imparts a velocity into each cube in the y-direction
|
||||
for cube in cube_list:
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", cube.id, CUBE_IMPULSE)
|
||||
|
||||
def verify_cube_final_velocity(self, cube):
|
||||
# Type (Entity) -> None
|
||||
# Ensures that cube has stopped moving
|
||||
final_velocity = Tests.__dict__["{}_final_velocity_{}".format(cube.name, self.index)]
|
||||
Report.result(final_velocity, cube.velocity.IsClose(CUBE_INITIAL_VELOCITY, 0.01))
|
||||
|
||||
def verify_cube_final_position(self, cube, block):
|
||||
# Type (Entity, Entity) -> None
|
||||
# Validates that cube is not somewhere it shouldn't be
|
||||
final_position = Tests.__dict__["{}_final_position_{}".format(cube.name, self.index)]
|
||||
Report.result(final_position, cube.position.z > block.position.z)
|
||||
|
||||
def log_values(self, entity):
|
||||
# Type (Entity) -> None
|
||||
# Logs needed values for comparison
|
||||
if isinstance(entity, Sphere):
|
||||
self.sphere_values.append([entity.velocity, entity.hit_trigger])
|
||||
else:
|
||||
self.cube_distances.append(entity.position)
|
||||
|
||||
def set_trigger(self, trigger):
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(trigger.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
for sphere in self.sphere_list:
|
||||
if sphere.id.equal(args[0]):
|
||||
sphere.hit_trigger = True
|
||||
|
||||
def modify_material_library():
|
||||
# Type () -> bool
|
||||
# Uses a Physmaterial_Editor option to modify the material library associated with this level.
|
||||
# Changes are made to maximize the in level affect.
|
||||
material_library = Physmaterial_Editor("c4044455_material_librarychangesinstantly.physmaterial")
|
||||
dynamic_friction_modified = material_library.modify_material("to_change_dynamic_friction", "DynamicFriction", 10.0)
|
||||
static_friction_modified = material_library.modify_material("to_change_static_friction", "StaticFriction", 10.0)
|
||||
friction_combine_modified = material_library.modify_material(
|
||||
"to_change_friction_combine", "FrictionCombine", "Maximum"
|
||||
)
|
||||
restitution_combine_modified = material_library.modify_material(
|
||||
"to_change_restitution_combine", "RestitutionCombine", "Maximum"
|
||||
)
|
||||
restitution_modified = material_library.modify_material("to_change_restitution", "Restitution", 1.0)
|
||||
material_deleted = material_library.delete_material("to_delete")
|
||||
material_library.save_changes()
|
||||
return (
|
||||
material_deleted
|
||||
and dynamic_friction_modified
|
||||
and static_friction_modified
|
||||
and friction_combine_modified
|
||||
and restitution_combine_modified
|
||||
and restitution_modified
|
||||
)
|
||||
|
||||
def check_sphere(sphere_values_0, sphere_values_1, index):
|
||||
# Type ([[vector3, bool]], [[vector3, bool]]) -> bool
|
||||
hit_trigger = not sphere_values_0[index][1] and sphere_values_1[index][1]
|
||||
velocity_valid = sphere_values_0[index][0].z < sphere_values_1[index][0].z
|
||||
return hit_trigger and velocity_valid
|
||||
|
||||
def check_static_friction(cube_distances_0, cube_distances_1):
|
||||
# Type ([float],[float]) -> bool
|
||||
return cube_distances_0[0].y > cube_distances_1[0].y
|
||||
|
||||
def check_dynamic_friction(cube_distances_0, cube_distances_1):
|
||||
# Type ([float],[float]) -> bool
|
||||
return cube_distances_0[1].y > cube_distances_1[1].y
|
||||
|
||||
def check_friction_combine(cube_distances_0, cube_distances_1):
|
||||
# Type ([float],[float]) -> bool
|
||||
return cube_distances_0[2].y > cube_distances_1[2].y
|
||||
|
||||
def run_test(test):
|
||||
# Type (Material_Test) -> None
|
||||
# This loop runs the test steps and logs data to the given Material_Test object
|
||||
# 1) Enter game mode
|
||||
helper.enter_game_mode(Tests.__dict__["enter_game_mode_{}".format(test.index)])
|
||||
|
||||
# 2) Find and Validate entities
|
||||
terrain = Entity("terrain", test.index)
|
||||
Entity.terrain_id = terrain.id
|
||||
block = Entity("block", test.index)
|
||||
trigger = Entity("trigger", test.index)
|
||||
sphere_0 = Sphere("sphere_0", test.index)
|
||||
sphere_1 = Sphere("sphere_1", test.index)
|
||||
sphere_2 = Sphere("sphere_2", test.index)
|
||||
sphere_list = [sphere_0, sphere_1, sphere_2]
|
||||
cube_0 = Entity("cube_0", test.index)
|
||||
cube_1 = Entity("cube_1", test.index)
|
||||
cube_2 = Entity("cube_2", test.index)
|
||||
cube_list = [cube_0, cube_1, cube_2]
|
||||
test.sphere_list = sphere_list
|
||||
test.set_trigger(trigger)
|
||||
|
||||
for sphere in sphere_list:
|
||||
test.verify_sphere_initial_position(sphere, terrain, trigger)
|
||||
test.verify_sphere_initial_velocity(sphere)
|
||||
|
||||
for cube in cube_list:
|
||||
test.verify_cube_initial_position(cube, block)
|
||||
test.verify_cube_initial_velocity(cube)
|
||||
|
||||
# 3) Wait for spheres to collide with terrain
|
||||
helper.wait_for_condition(lambda: all([sphere.collision_happened for sphere in sphere_list]), TIMEOUT)
|
||||
|
||||
# 4) Wait for spheres to enter the trigger
|
||||
helper.wait_for_condition(lambda: all([sphere.hit_trigger for sphere in sphere_list]), TIMEOUT)
|
||||
for sphere in sphere_list:
|
||||
test.log_values(sphere)
|
||||
|
||||
# 5) Log sphere results
|
||||
for sphere in sphere_list:
|
||||
test.verify_sphere_collision(sphere)
|
||||
test.verify_sphere_final_position(sphere, terrain)
|
||||
test.verify_sphere_final_velocity(sphere)
|
||||
|
||||
# 6) Push cubes
|
||||
test.push_cubes(cube_list)
|
||||
|
||||
# 7) Wait for cubes to stop moving
|
||||
helper.wait_for_condition(lambda: all([cube.is_not_moving for cube in cube_list]), TIMEOUT)
|
||||
|
||||
# 8) Log and validate cube results
|
||||
for cube in cube_list:
|
||||
test.verify_cube_final_position(cube, block)
|
||||
test.verify_cube_final_velocity(cube)
|
||||
test.log_values(cube)
|
||||
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(Tests.__dict__["exit_game_mode_{}".format(test.index)])
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C4044455_Material_LibraryChangesInstantly")
|
||||
|
||||
# 2) Create test objects
|
||||
test_0 = Material_Test(0)
|
||||
test_1 = Material_Test(1)
|
||||
|
||||
# 3) Run test 0
|
||||
run_test(test_0)
|
||||
|
||||
# 4) Modify material library
|
||||
Report.result(Tests.material_changes, modify_material_library())
|
||||
|
||||
# Wait for modifications to the material library to propagate.
|
||||
general.idle_wait_frames(PROPAGATION_FRAMES)
|
||||
|
||||
# 5) Run test 1
|
||||
run_test(test_1)
|
||||
|
||||
# 6) Validate results
|
||||
# Restitution Modification Successful
|
||||
Report.result(Tests.restitution, check_sphere(test_0.sphere_values, test_1.sphere_values, index=0))
|
||||
# Static Friction Modification Successful
|
||||
Report.result(Tests.static_friction, check_static_friction(test_0.cube_distances, test_1.cube_distances))
|
||||
# Dynamic Friction Modification Successful
|
||||
Report.result(Tests.dynamic_friction, check_dynamic_friction(test_0.cube_distances, test_1.cube_distances))
|
||||
# Friction Combine Modification Successful
|
||||
Report.result(Tests.friction_combine, check_friction_combine(test_0.cube_distances, test_1.cube_distances))
|
||||
# Restitution Combine Modification Successful
|
||||
Report.result(Tests.restitution_combine, check_sphere(test_0.sphere_values, test_1.sphere_values, index=1))
|
||||
# Material Delete Successful
|
||||
Report.result(Tests.delete_material, check_sphere(test_0.sphere_values, test_1.sphere_values, index=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4044455_Material_libraryChangesInstantly)
|
||||
@@ -0,0 +1,225 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4044456
|
||||
# Test Case Title : Verify that when two objects with different materials collide, the friction combine works
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044456
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ramp = ("Ramp entity found", "Ramp entity not found")
|
||||
find_box_minimum = ("Box entity 'minimum' found", "Box entity 'minimum' not found")
|
||||
find_box_multiply = ("Box entity 'multiply' found", "Box entity 'multiply' not found")
|
||||
find_box_average = ("Box entity 'average' found", "Box entity 'average' not found")
|
||||
find_box_maximum = ("Box entity 'maximum' found", "Box entity 'maximum' not found")
|
||||
box_at_rest_start_minimum = ("Box 'minimum ' began test motionless", "Box 'minimum' did not begin test motionless")
|
||||
box_at_rest_start_multiply = ("Box 'multiply' began test motionless", "Box 'multiply' did not begin test motionless")
|
||||
box_at_rest_start_average = ("Box 'average' began test motionless", "Box 'average' did not begin test motionless")
|
||||
box_at_rest_start_maximum = ("Box 'maximum' began test motionless", "Box 'maximum' did not begin test motionless")
|
||||
box_was_pushed_minimum = ("Box 'minimum' moved", "Box 'minimum' did not move before timeout")
|
||||
box_was_pushed_multiply = ("Box 'multiply' moved", "Box 'multiply' did not move before timeout")
|
||||
box_was_pushed_average = ("Box 'average' moved", "Box 'average' did not move before timeout")
|
||||
box_was_pushed_maximum = ("Box 'maximum' moved", "Box 'maximum' did not move before timeout")
|
||||
box_at_rest_end_minimum = ("Box 'minimum' came to rest", "Box 'minimum' did not come to rest before timeout")
|
||||
box_at_rest_end_multiply = ("Box 'multiply' came to rest", "Box 'multiply' did not come to rest before timeout")
|
||||
box_at_rest_end_average = ("Box 'average' came to rest", "Box 'average' did not come to rest before timeout")
|
||||
box_at_rest_end_maximum = ("Box 'maximum' came to rest", "Box 'maximum' did not come to rest before timeout")
|
||||
minimum_equals_multiply = ("Box 'minimum' and 'multiply' traveled equal distances", "Box 'minimum' and 'multiply' did not travel equal distances")
|
||||
distance_ordered = ("Box travel distance was ordered as expected", "Box travel distance was not ordered as expected")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4044456_Material_FrictionCombine():
|
||||
"""
|
||||
Summary:
|
||||
|
||||
Level Description:
|
||||
Four boxes sit on a horizontal 'ramp'. The boxes are identical, save for their physX material:
|
||||
|
||||
A new material library was created with 4 materials, minimum, multiply, average, and maximum.
|
||||
Each material has its 'friction combine' mode assigned as named; as well as the following properties:
|
||||
dynamic friction: 0.1
|
||||
static friction: 0.1
|
||||
restitution: 0.1
|
||||
|
||||
An additional material was created for the ramp entity. It has the following properties:
|
||||
dynamic friction: 1.0
|
||||
static friction: 1.0
|
||||
restitution: 1.0
|
||||
friction combine: Average
|
||||
|
||||
Each box is assigned its corresponding friction material
|
||||
Each box also has a PhysX box collider with default settings
|
||||
The COM of the boxes is placed on the plane (0, 0, -0.5), so as to remove any torque moments and resulting rotations
|
||||
|
||||
Expected Behavior:
|
||||
For each box, this script will apply a force impulse in the world X direction
|
||||
Boxes with greater friction combine mode results should travel a shorter distance.
|
||||
minimum: 0.1 vs 1 -> 0.1
|
||||
multiply: 0.1 * 1 -> 0.1
|
||||
average: (0.1 + 1) / 2 -> 0.55
|
||||
maximum: 0.1 vs 1 -> 1
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find the ramp
|
||||
|
||||
For each box:
|
||||
4) Find the box
|
||||
5) Ensure the box is stationary
|
||||
6) Push the box and wait for it to come to rest
|
||||
|
||||
7) Special case: assert that minimum and multiply travel the same distance
|
||||
8) Assert that greater friction combine modes travel a shorter distance
|
||||
9) Exit game mode
|
||||
10) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
FORCE_IMPULSE = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
VECTOR_TOLERANCE = 0.001
|
||||
DISTANCE_TOLERANCE = 0.002
|
||||
TIMEOUT = 5
|
||||
|
||||
class Box:
|
||||
def __init__(self, name, valid_test, stationary_start_test, moved_test, stationary_end_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.start_position = self.get_position()
|
||||
self.distance = 0.0
|
||||
self.valid_test = valid_test
|
||||
self.stationary_start_test = stationary_start_test
|
||||
self.moved_test = moved_test
|
||||
self.stationary_end_test = stationary_end_test
|
||||
|
||||
def is_stationary(self):
|
||||
velocity = azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
return vector_is_close_to_zero(velocity)
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def vector_is_close_to_zero(vector):
|
||||
return (
|
||||
abs(vector.x) <= VECTOR_TOLERANCE
|
||||
and abs(vector.y) <= VECTOR_TOLERANCE
|
||||
and abs(vector.z) <= VECTOR_TOLERANCE
|
||||
)
|
||||
|
||||
def push(box):
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "ApplyLinearImpulse", box.id, FORCE_IMPULSE)
|
||||
|
||||
def float_is_close(value, target, tolerance):
|
||||
return abs(value - target) <= tolerance
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4044456_Material_FrictionCombine")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# fmt: off
|
||||
# Set up our boxes
|
||||
box_minimum = Box(
|
||||
name = "Minimum",
|
||||
valid_test = Tests.find_box_minimum,
|
||||
stationary_start_test = Tests.box_at_rest_start_minimum,
|
||||
moved_test = Tests.box_was_pushed_minimum,
|
||||
stationary_end_test = Tests.box_at_rest_end_minimum
|
||||
)
|
||||
box_multiply = Box(
|
||||
name = "Multiply",
|
||||
valid_test = Tests.find_box_multiply,
|
||||
stationary_start_test = Tests.box_at_rest_start_multiply,
|
||||
moved_test = Tests.box_was_pushed_multiply,
|
||||
stationary_end_test = Tests.box_at_rest_end_multiply
|
||||
)
|
||||
box_average = Box(
|
||||
name = "Average",
|
||||
valid_test = Tests.find_box_average,
|
||||
stationary_start_test = Tests.box_at_rest_start_average,
|
||||
moved_test = Tests.box_was_pushed_average,
|
||||
stationary_end_test = Tests.box_at_rest_end_average
|
||||
)
|
||||
box_maximum = Box(
|
||||
name = "Maximum",
|
||||
valid_test = Tests.find_box_maximum,
|
||||
stationary_start_test = Tests.box_at_rest_start_maximum,
|
||||
moved_test = Tests.box_was_pushed_maximum,
|
||||
stationary_end_test = Tests.box_at_rest_end_maximum
|
||||
)
|
||||
all_boxes = (box_minimum, box_multiply, box_average, box_maximum)
|
||||
# fmt: on
|
||||
|
||||
# 3) Find the ramp
|
||||
ramp_id = general.find_game_entity("Ramp")
|
||||
Report.critical_result(Tests.find_ramp, ramp_id.IsValid())
|
||||
|
||||
for box in all_boxes:
|
||||
Report.info("********Pushing Box {}********".format(box.name))
|
||||
# 4) Find the box
|
||||
Report.critical_result(box.valid_test, box.id.IsValid())
|
||||
# 5) Ensure the box is stationary
|
||||
Report.result(box.stationary_start_test, box.is_stationary())
|
||||
# 6) Push the box
|
||||
push(box)
|
||||
Report.result(box.moved_test, helper.wait_for_condition(lambda: not box.is_stationary(), TIMEOUT))
|
||||
Report.result(box.stationary_end_test, helper.wait_for_condition(lambda: box.is_stationary(), TIMEOUT))
|
||||
|
||||
end_position = box.get_position()
|
||||
box.distance = end_position.GetDistance(box.start_position)
|
||||
Report.info("Box {} travelled {:.3f} meters".format(box.name, box.distance))
|
||||
|
||||
# 7) Special case: assert that minimum and multiply travel the same distance
|
||||
boxes_are_close = float_is_close(box_minimum.distance, box_multiply.distance, DISTANCE_TOLERANCE)
|
||||
Report.result(Tests.minimum_equals_multiply, boxes_are_close)
|
||||
|
||||
# 8) Assert that greater coefficients result in shorter travelled distance
|
||||
distance_ordered = boxes_are_close and box_minimum.distance > box_average.distance > box_maximum.distance
|
||||
Report.result(Tests.distance_ordered, distance_ordered)
|
||||
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4044456_Material_FrictionCombine)
|
||||
@@ -0,0 +1,257 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4044457
|
||||
# Test Case Title : Verify that when two objects with different materials collide, the restitution combine works
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044457
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ramp = ("Ramp entity found", "Ramp entity not found")
|
||||
find_box_minimum = ("Box entity 'minimum' found", "Box entity 'minimum' not found")
|
||||
find_box_multiply = ("Box entity 'multiply' found", "Box entity 'multiply' not found")
|
||||
find_box_average = ("Box entity 'average' found", "Box entity 'average' not found")
|
||||
find_box_maximum = ("Box entity 'maximum' found", "Box entity 'maximum' not found")
|
||||
box_fell_minimum = ("Box 'minimum' fell", "Box 'minimum' did not fall")
|
||||
box_fell_multiply = ("Box 'multiply' fell", "Box 'multiply' did not fall")
|
||||
box_fell_average = ("Box 'average' fell", "Box 'average' did not fall")
|
||||
box_fell_maximum = ("Box 'maximum' fell", "Box 'maximum' did not fall")
|
||||
box_hit_ramp_minimum = ("Box 'minimum' hit the ramp", "Box 'minimum' did not hit the ramp before timeout")
|
||||
box_hit_ramp_multiply = ("Box 'multiply' hit the ramp", "Box 'multiply' did not hit the ramp before timeout")
|
||||
box_hit_ramp_average = ("Box 'average' hit the ramp", "Box 'average' did not hit the ramp before timeout")
|
||||
box_hit_ramp_maximum = ("Box 'maximum' hit the ramp", "Box 'maximum' did not hit the ramp before timeout")
|
||||
box_peaked_minimum = ("Box 'minimum' reached its max height", "Box 'minimum' did not reach its' max height before timeout")
|
||||
box_peaked_multiply = ("Box 'multiply' reached its max height", "Box 'multiply' did not reach its' max height before timeout")
|
||||
box_peaked_average = ("Box 'average' reached its max height", "Box 'average' did not reach its' max height before timeout")
|
||||
box_peaked_maximum = ("Box 'maximum' reached its max height", "Box 'maximum' did not reach its' max height before timeout")
|
||||
minimum_equals_multiply = ("Box 'minimum' and 'multiply' bounced equal heights", "Box 'minimum' and 'multiply' did not bounce equal heights")
|
||||
distance_ordered = ("Boxes with greater restitution values bounced higher", "Boxes with greater restitution values did not bounce higher")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4044457_Material_RestitutionCombine():
|
||||
"""
|
||||
Summary:
|
||||
|
||||
Level Description:
|
||||
Four boxes sit above a horizontal 'ramp'. Gravity on each rigidbody component is set to disabled.
|
||||
The boxes are identical, save for their physX material.
|
||||
|
||||
A new material library was created with 4 materials, minimum, multiply, average, and maximum.
|
||||
Each material has its 'restitution combine' mode assigned as named; as well as the following properties:
|
||||
dynamic friction: 0.1
|
||||
static friction: 0.1
|
||||
restitution: 0.1
|
||||
|
||||
An additional material was created for the ramp entity. It has the following properties:
|
||||
dynamic friction: 1.0
|
||||
static friction: 1.0
|
||||
restitution: 1.0
|
||||
friction combine: Average
|
||||
|
||||
Each box is assigned its corresponding material
|
||||
Each box also has a PhysX box collider with default settings
|
||||
|
||||
Expected Behavior:
|
||||
For each box, this script will enable gravity, then wait for the box to collide with the ramp.
|
||||
It then measures the height of the bounce relative to when it first came in contact with the ramp.
|
||||
When the z component of the box's velocity reaches zero (or below zero), the box latches its bounce height.
|
||||
The box is then frozen in place and the steps run for the next box in the list.
|
||||
|
||||
Boxes with greater restitution combine mode retain more energy between collisions, therefore bouncing higher.
|
||||
minimum: 0.1 vs 1 -> 0.1
|
||||
multiply: 0.1 * 1 -> 0.1
|
||||
average: (0.1 + 1) / 2 -> 0.55
|
||||
maximum: 0.1 vs 1 -> 1
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find the ramp
|
||||
|
||||
For each box:
|
||||
4) Find the box
|
||||
5) Drop the box
|
||||
6) Ensure the box collides with the ramp
|
||||
7) Ensure the box reaches its peak height
|
||||
|
||||
8) Special case: assert that minimum and multiply bounce the same height
|
||||
9) Assert that greater restitution combine modes bounce higher
|
||||
10) Exit game mode
|
||||
11) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
DISTANCE_TOLERANCE = 0.005
|
||||
TIMEOUT = 5
|
||||
|
||||
class Box:
|
||||
def __init__(self, name, valid_test, fell_test, hit_ramp_test, peaked_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.start_position = self.get_position()
|
||||
self.hit_ramp = False
|
||||
self.hit_ramp_position = None
|
||||
self.bounce_height = 0.0
|
||||
self.valid_test = valid_test
|
||||
self.fell_test = fell_test
|
||||
self.hit_ramp_test = hit_ramp_test
|
||||
self.peaked_test = peaked_test
|
||||
self.set_gravity_enabled(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)
|
||||
|
||||
def set_velocity(self, value):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetLinearVelocity", self.id, value)
|
||||
|
||||
def set_gravity_enabled(self, value):
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetGravityEnabled", self.id, value)
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
for box in all_boxes:
|
||||
if box.id.Equal(other_id):
|
||||
box.hit_ramp_position = box.get_position()
|
||||
box.hit_ramp = True
|
||||
|
||||
def reached_max_height(box):
|
||||
current_position = box.get_position()
|
||||
current_height = current_position.z - box.hit_ramp_position.z
|
||||
current_linear_velocity = box.get_velocity()
|
||||
if current_linear_velocity.z > 0.0:
|
||||
box.bounce_height = current_height
|
||||
return False
|
||||
else:
|
||||
Report.info("Box {} reached {:.3f}M high".format(box.name, box.bounce_height))
|
||||
return True
|
||||
|
||||
def is_falling(box):
|
||||
return box.get_velocity().z < 0.0
|
||||
|
||||
def float_is_close(value, target, tolerance):
|
||||
return abs(value - target) <= tolerance
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4044457_Material_RestitutionCombine")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# fmt: off
|
||||
# Set up our boxes
|
||||
box_minimum = Box(
|
||||
name = "Minimum",
|
||||
valid_test = Tests.find_box_minimum,
|
||||
fell_test = Tests.box_fell_minimum,
|
||||
hit_ramp_test = Tests.box_hit_ramp_minimum,
|
||||
peaked_test = Tests.box_peaked_minimum,
|
||||
)
|
||||
box_multiply = Box(
|
||||
name = "Multiply",
|
||||
valid_test = Tests.find_box_multiply,
|
||||
fell_test = Tests.box_fell_multiply,
|
||||
hit_ramp_test = Tests.box_hit_ramp_multiply,
|
||||
peaked_test = Tests.box_peaked_multiply,
|
||||
)
|
||||
box_average = Box(
|
||||
name = "Average",
|
||||
valid_test = Tests.find_box_average,
|
||||
fell_test = Tests.box_fell_average,
|
||||
hit_ramp_test = Tests.box_hit_ramp_average,
|
||||
peaked_test = Tests.box_peaked_average,
|
||||
)
|
||||
box_maximum = Box(
|
||||
name = "Maximum",
|
||||
valid_test = Tests.find_box_maximum,
|
||||
fell_test = Tests.box_fell_maximum,
|
||||
hit_ramp_test = Tests.box_hit_ramp_maximum,
|
||||
peaked_test = Tests.box_peaked_maximum,
|
||||
)
|
||||
all_boxes = (box_minimum, box_multiply, box_average, box_maximum)
|
||||
# fmt: on
|
||||
|
||||
# 3) Find the ramp
|
||||
ramp_id = general.find_game_entity("Ramp")
|
||||
Report.critical_result(Tests.find_ramp, ramp_id.IsValid())
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(ramp_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
for box in all_boxes:
|
||||
Report.info("********Dropping Box {}********".format(box.name))
|
||||
# 4) Find the box
|
||||
Report.critical_result(box.valid_test, box.id.IsValid())
|
||||
|
||||
# 5) Drop the box
|
||||
box.set_gravity_enabled(True)
|
||||
Report.critical_result(box.fell_test, helper.wait_for_condition(lambda: is_falling(box), TIMEOUT))
|
||||
|
||||
# 6) Wait for the box to hit the ground
|
||||
Report.result(box.hit_ramp_test, helper.wait_for_condition(lambda: box.hit_ramp, TIMEOUT))
|
||||
|
||||
# 7) Measure the bounce height
|
||||
Report.result(box.peaked_test, helper.wait_for_condition(lambda: reached_max_height(box), TIMEOUT))
|
||||
|
||||
# Freeze the box so it does not interfere with the other boxes
|
||||
box.set_velocity(lymath.Vector3(0.0, 0.0, 0.0))
|
||||
box.set_gravity_enabled(False)
|
||||
|
||||
# 8) Special case: assert that minimum and multiply bounce the same height
|
||||
boxes_are_close = float_is_close(box_minimum.bounce_height, box_multiply.bounce_height, DISTANCE_TOLERANCE)
|
||||
Report.result(Tests.minimum_equals_multiply, boxes_are_close)
|
||||
|
||||
# 9) Assert that greater coefficients result in higher bounces
|
||||
distance_ordered = (
|
||||
boxes_are_close and box_minimum.bounce_height < box_average.bounce_height < box_maximum.bounce_height
|
||||
)
|
||||
Report.result(Tests.distance_ordered, distance_ordered)
|
||||
|
||||
# 10) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4044457_Material_RestitutionCombine)
|
||||
@@ -0,0 +1,203 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4044459
|
||||
# Test Case Title : Verify the functionality of dynamic friction
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044459
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ramp = ("Ramp entity found", "Ramp entity not found")
|
||||
find_box_zero = ("Box entity 'zero' found", "Box entity 'zero' not found")
|
||||
find_box_low = ("Box entity 'low' found", "Box entity 'low' not found")
|
||||
find_box_mid = ("Box entity 'mid' found", "Box entity 'mid' not found")
|
||||
find_box_high = ("Box entity 'high' found", "Box entity 'high' not found")
|
||||
box_at_rest_start_zero = ("Box 'zero' began test motionless", "Box 'zero' did not begin test motionless")
|
||||
box_at_rest_start_low = ("Box 'low' began test motionless", "Box 'low' did not begin test motionless")
|
||||
box_at_rest_start_mid = ("Box 'mid' began test motionless", "Box 'mid' did not begin test motionless")
|
||||
box_at_rest_start_high = ("Box 'high' began test motionless", "Box 'high' did not begin test motionless")
|
||||
box_was_pushed_zero = ("Box 'zero' moved", "Box 'zero' did not move before timeout")
|
||||
box_was_pushed_low = ("Box 'low' moved", "Box 'low' did not move before timeout")
|
||||
box_was_pushed_mid = ("Box 'mid' moved", "Box 'mid' did not move before timeout")
|
||||
box_was_pushed_high = ("Box 'high' moved", "Box 'high' did not move before timeout")
|
||||
box_at_rest_end_zero = ("Box 'zero' came to rest", "Box 'zero' did not come to rest before timeout")
|
||||
box_at_rest_end_low = ("Box 'low' came to rest", "Box 'low' did not come to rest before timeout")
|
||||
box_at_rest_end_mid = ("Box 'mid' came to rest", "Box 'mid' did not come to rest before timeout")
|
||||
box_at_rest_end_high = ("Box 'high' came to rest", "Box 'high' did not come to rest before timeout")
|
||||
distance_ordered = ("Boxes with greater dynamic friction traveled shorter", "Boxes with greater dynamic friction traveled further")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4044459_Material_DynamicFriction():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that greater dynamic friction coefficient settings on a physX material results in
|
||||
rigidbody entities (with that material) that require a greater force in order to remain in motion
|
||||
|
||||
Level Description:
|
||||
Four boxes sit on a horizontal 'ramp'. The boxes are identical, save for their physX material:
|
||||
|
||||
A new material library was created with 4 materials and their dynamic friction coefficient:
|
||||
zero_dynamic_friction: 0.00
|
||||
low_dynamic_friction: 0.50
|
||||
mid_dynamic_friction: 1.00
|
||||
high_dynamic_friction: 1.50
|
||||
Each material is identical otherwise.
|
||||
|
||||
Each box is assigned its corresponding friction material
|
||||
Each box also has a PhysX box collider with default settings
|
||||
The COM of the boxes is placed on the plane (0, 0, -0.5), so as to remove any torque moments and resulting rotations
|
||||
|
||||
Expected Behavior:
|
||||
For each box, this script will apply a force impulse in the world X direction
|
||||
Boxes with greater dynamic friction coefficients should travel a shorter distance along the ramp.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find the ramp
|
||||
|
||||
For each box:
|
||||
4) Find the box
|
||||
5) Ensure the box is stationary
|
||||
6) Push the box and wait for it to come to rest
|
||||
|
||||
7) Assert that greater coefficients result in a shorter distance travelled
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
FORCE_IMPULSE = lymath.Vector3(10.0, 0.0, 0.0)
|
||||
TIMEOUT = 5
|
||||
|
||||
class Box:
|
||||
def __init__(self, name, valid_test, stationary_start_test, moved_test, stationary_end_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.start_position = self.get_position()
|
||||
self.distance = 0.0
|
||||
self.valid_test = valid_test
|
||||
self.stationary_start_test = stationary_start_test
|
||||
self.moved_test = moved_test
|
||||
self.stationary_end_test = stationary_end_test
|
||||
|
||||
def is_stationary(self):
|
||||
velocity = azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
return vector_is_close_to_zero(velocity)
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def vector_is_close_to_zero(vector):
|
||||
return abs(vector.x) <= 0.001 and abs(vector.y) <= 0.001 and abs(vector.z) <= 0.001
|
||||
|
||||
def push(box):
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "ApplyLinearImpulse", box.id, FORCE_IMPULSE)
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4044459_Material_DynamicFriction")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# fmt: off
|
||||
# Set up our boxes
|
||||
box_zero = Box(
|
||||
name = "Zero",
|
||||
valid_test = Tests.find_box_zero,
|
||||
stationary_start_test = Tests.box_at_rest_start_zero,
|
||||
moved_test = Tests.box_was_pushed_zero,
|
||||
stationary_end_test = Tests.box_at_rest_end_zero
|
||||
)
|
||||
box_low = Box(
|
||||
name = "Low",
|
||||
valid_test = Tests.find_box_low,
|
||||
stationary_start_test = Tests.box_at_rest_start_low,
|
||||
moved_test = Tests.box_was_pushed_low,
|
||||
stationary_end_test = Tests.box_at_rest_end_low
|
||||
)
|
||||
box_mid = Box(
|
||||
name = "Mid",
|
||||
valid_test = Tests.find_box_mid,
|
||||
stationary_start_test = Tests.box_at_rest_start_mid,
|
||||
moved_test = Tests.box_was_pushed_mid,
|
||||
stationary_end_test = Tests.box_at_rest_end_mid
|
||||
)
|
||||
box_high = Box(
|
||||
name = "High",
|
||||
valid_test = Tests.find_box_high,
|
||||
stationary_start_test = Tests.box_at_rest_start_high,
|
||||
moved_test = Tests.box_was_pushed_high,
|
||||
stationary_end_test = Tests.box_at_rest_end_high
|
||||
)
|
||||
all_boxes = (box_zero, box_low, box_mid, box_high)
|
||||
# fmt: on
|
||||
|
||||
# 3) Find the ramp
|
||||
ramp_id = general.find_game_entity("Ramp")
|
||||
Report.critical_result(Tests.find_ramp, ramp_id.IsValid())
|
||||
|
||||
for box in all_boxes:
|
||||
Report.info("********Pushing Box {}********".format(box.name))
|
||||
# 4) Find the box
|
||||
Report.critical_result(box.valid_test, box.id.IsValid())
|
||||
# 5) Ensure the box is stationary
|
||||
Report.result(box.stationary_start_test, box.is_stationary())
|
||||
# 6) Push the box
|
||||
push(box)
|
||||
Report.result(box.moved_test, helper.wait_for_condition(lambda: not box.is_stationary(), TIMEOUT))
|
||||
Report.result(box.stationary_end_test, helper.wait_for_condition(lambda: box.is_stationary(), TIMEOUT))
|
||||
|
||||
end_position = box.get_position()
|
||||
box.distance = end_position.GetDistance(box.start_position)
|
||||
Report.info("Box {} travelled {:.3f} meters".format(box.name, box.distance))
|
||||
|
||||
# 7) Assert that greater coefficients result in shorter travelled distance
|
||||
distance_ordered = box_high.distance < box_mid.distance < box_low.distance < box_zero.distance
|
||||
Report.result(Tests.distance_ordered, distance_ordered)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4044459_Material_DynamicFriction)
|
||||
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4044460
|
||||
# Test Case Title : Verify the functionality of static friction
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044460
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ramp = ("Ramp entity found", "Ramp entity not found")
|
||||
find_box_zero = ("Box entity 'zero' found", "Box entity 'zero' not found")
|
||||
find_box_low = ("Box entity 'low' found", "Box entity 'low' not found")
|
||||
find_box_mid = ("Box entity 'mid' found", "Box entity 'mid' not found")
|
||||
find_box_high = ("Box entity 'high' found", "Box entity 'high' not found")
|
||||
box_at_rest_zero = ("Box 'zero' began test motionless", "Box 'zero' did not begin test motionless")
|
||||
box_at_rest_low = ("Box 'low' began test motionless", "Box 'low' did not begin test motionless")
|
||||
box_at_rest_mid = ("Box 'mid' began test motionless", "Box 'mid' did not begin test motionless")
|
||||
box_at_rest_high = ("Box 'high' began test motionless", "Box 'high' did not begin test motionless")
|
||||
box_was_pushed_zero = ("Box 'zero' moved", "Box 'zero' did not move before timeout")
|
||||
box_was_pushed_low = ("Box 'low' moved", "Box 'low' did not move before timeout")
|
||||
box_was_pushed_mid = ("Box 'mid' moved", "Box 'mid' did not move before timeout")
|
||||
box_was_pushed_high = ("Box 'high' moved", "Box 'high' did not move before timeout")
|
||||
force_impulse_ordered = ("Boxes with greater static friction required greater impulses", "Boxes with greater static friction did not require greater impulses")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4044460_Material_StaticFriction():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that greater static friction coefficient settings on a physX material results in
|
||||
rigidbodys (with that material) requiring a greater force in order to be set into motion
|
||||
|
||||
Level Description:
|
||||
Four boxes sit on a horizontal 'ramp'. The boxes are identical, save for their physX material.
|
||||
|
||||
A new material library was created with 4 materials and their static friction coefficient:
|
||||
zero_static_friction: 0.00
|
||||
low_static_friction: 0.50
|
||||
mid_static_friction: 1.00
|
||||
high_static_friction: 1.50
|
||||
Each material is identical otherwise
|
||||
Each box is assigned its corresponding friction material, the ramp is assigned low_static_friction
|
||||
The COM of the boxes is placed on the plane (0, 0, -0.5), so as to remove any torque moments and resulting rotations
|
||||
|
||||
Expected Behavior:
|
||||
For each box, this script will apply a force impulse in the world X direction (starting at magnitude 0.0).
|
||||
Every frame, it checks if the box moved:
|
||||
If it didn't, we increase the magnitude slightly and try again
|
||||
If it did, the box retains the magnitude required to move it, and we move to the next box.
|
||||
|
||||
Boxes with greater static friction coefficients should require greater forces in order to set them in motion.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find the ramp
|
||||
|
||||
For each box:
|
||||
4) Find the box
|
||||
5) Ensure the box is stationary
|
||||
6) Push the box until it moves
|
||||
|
||||
7) Assert that greater coefficients result in greater required force impulses
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
FORCE_IMPULSE_INCREMENT = 0.005 # How much we increase the force every frame
|
||||
MIN_MOVE_DISTANCE = 0.02 # Distance magnitude that a box must travel in order to be considered moved
|
||||
STATIONARY_TOLERANCE = 0.0001 # Boxes must have velocities under this magnitude in order to be stationary
|
||||
TIMEOUT = 10
|
||||
|
||||
class Box:
|
||||
def __init__(self, name, valid_test, stationary_test, moved_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.start_position = self.get_position()
|
||||
self.force_impulse = 0.0
|
||||
self.valid_test = valid_test
|
||||
self.stationary_test = stationary_test
|
||||
self.moved_test = moved_test
|
||||
|
||||
def is_stationary(self):
|
||||
velocity = azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
return vector_close_to_zero(velocity, STATIONARY_TOLERANCE)
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def vector_close_to_zero(vector, tolerance):
|
||||
return abs(vector.x) <= tolerance and abs(vector.y) <= tolerance and abs(vector.z) <= tolerance
|
||||
|
||||
def push(box):
|
||||
delta = box.start_position.Subtract(box.get_position())
|
||||
if vector_close_to_zero(delta, MIN_MOVE_DISTANCE):
|
||||
box.force_impulse += FORCE_IMPULSE_INCREMENT
|
||||
impulse_vector = lymath.Vector3(box.force_impulse, 0.0, 0.0)
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "ApplyLinearImpulse", box.id, impulse_vector)
|
||||
return False
|
||||
else:
|
||||
Report.info("Box {} required force was {:.3f}".format(box.name, box.force_impulse))
|
||||
return True
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4044460_Material_StaticFriction")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# fmt: off
|
||||
# Set up our boxes
|
||||
box_zero = Box(
|
||||
name = "Zero",
|
||||
valid_test = Tests.find_box_zero,
|
||||
stationary_test = Tests.box_at_rest_zero,
|
||||
moved_test = Tests.box_was_pushed_zero
|
||||
)
|
||||
box_low = Box(
|
||||
name = "Low",
|
||||
valid_test = Tests.find_box_low,
|
||||
stationary_test = Tests.box_at_rest_low,
|
||||
moved_test = Tests.box_was_pushed_low
|
||||
)
|
||||
box_mid = Box(
|
||||
name = "Mid",
|
||||
valid_test = Tests.find_box_mid,
|
||||
stationary_test = Tests.box_at_rest_mid,
|
||||
moved_test = Tests.box_was_pushed_mid
|
||||
)
|
||||
box_high = Box(
|
||||
name = "High",
|
||||
valid_test = Tests.find_box_high,
|
||||
stationary_test = Tests.box_at_rest_high,
|
||||
moved_test = Tests.box_was_pushed_high
|
||||
)
|
||||
all_boxes = (box_zero, box_low, box_mid, box_high)
|
||||
# fmt: on
|
||||
|
||||
# 3) Find the ramp
|
||||
ramp_id = general.find_game_entity("Ramp")
|
||||
Report.critical_result(Tests.find_ramp, ramp_id.IsValid())
|
||||
|
||||
for box in all_boxes:
|
||||
Report.info("********Pushing Box {}********".format(box.name))
|
||||
# 4) Find the box
|
||||
Report.critical_result(box.valid_test, box.id.IsValid())
|
||||
# 5) Ensure the box is stationary
|
||||
Report.result(box.stationary_test, box.is_stationary())
|
||||
# 6) Push the box until it moves
|
||||
Report.critical_result(box.moved_test, helper.wait_for_condition(lambda: push(box), TIMEOUT))
|
||||
|
||||
# 7) Assert that greater coefficients result in greater required force impulses
|
||||
ordered_impulses = box_high.force_impulse > box_mid.force_impulse > box_low.force_impulse > box_zero.force_impulse
|
||||
Report.result(Tests.force_impulse_ordered, ordered_impulses)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4044460_Material_StaticFriction)
|
||||
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4044461
|
||||
# Test Case Title : Verify the functionality of restitution
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044461
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_ramp = ("Ramp entity found", "Ramp entity not found")
|
||||
find_box_zero = ("Box entity 'zero' found", "Box entity 'zero' not found")
|
||||
find_box_low = ("Box entity 'low' found", "Box entity 'low' not found")
|
||||
find_box_mid = ("Box entity 'mid' found", "Box entity 'mid' not found")
|
||||
find_box_high = ("Box entity 'high' found", "Box entity 'high' not found")
|
||||
box_fell_zero = ("Box 'zero' fell", "Box 'zero' did not fall")
|
||||
box_fell_low = ("Box 'low' fell", "Box 'low' did not fall")
|
||||
box_fell_mid = ("Box 'mid' fell", "Box 'mid' did not fall")
|
||||
box_fell_high = ("Box 'high' fell", "Box 'high' did not fall")
|
||||
box_hit_ramp_zero = ("Box 'zero' hit the ramp", "Box 'zero' did not hit the ramp before timeout")
|
||||
box_hit_ramp_low = ("Box 'low' hit the ramp", "Box 'low' did not hit the ramp before timeout")
|
||||
box_hit_ramp_mid = ("Box 'mid' hit the ramp", "Box 'mid' did not hit the ramp before timeout")
|
||||
box_hit_ramp_high = ("Box 'high' hit the ramp", "Box 'high' did not hit the ramp before timeout")
|
||||
box_peaked_zero = ("Box 'zero' reached its max height", "Box 'zero' did not reach max height before timeout")
|
||||
box_peaked_low = ("Box 'low' reached its max height", "Box 'low' did not reach max height before timeout")
|
||||
box_peaked_mid = ("Box 'mid' reached its max height", "Box 'mid' did not reach max height before timeout")
|
||||
box_peaked_high = ("Box 'high' reached its max height", "Box 'high' did not reach max height before timeout")
|
||||
box_zero_did_not_bounce = ("Box 'zero' did not bounce", "Box 'zero' bounced - this should not happen")
|
||||
bounce_height_ordered = ("Boxes with greater restitution values bounced higher", "Boxes with greater restitution values did not bounce higher")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4044461_Material_Restitution():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that greater restitution coefficient settings on a physX material results in
|
||||
rigid bodies (with that material) that bounce higher
|
||||
|
||||
Level Description:
|
||||
Four boxes sit above a horizontal 'ramp'. Gravity on each rigid body component is set to disabled.
|
||||
The boxes are identical, save for their physX material.
|
||||
|
||||
A new material library was created with 4 materials and their restitution coefficient:
|
||||
zero_restitution: 0.00
|
||||
low_restitution: 0.30
|
||||
mid_restitution: 0.60
|
||||
high_restitution: 1.00
|
||||
Each material is identical otherwise
|
||||
Each box is assigned its corresponding physX material
|
||||
|
||||
Expected Behavior:
|
||||
For each box, this script will enable gravity, then wait for the box to collide with the ramp.
|
||||
It then measures the height of the bounce relative to when it first came in contact with the ramp.
|
||||
When the z component of the box's velocity reaches zero (or below zero), the box latches its bounce height.
|
||||
The box is then frozen in place and the steps run for the next box in the list.
|
||||
|
||||
Boxes with greater restitution values should retain more energy between collisions, therefore bouncing higher
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find the ramp
|
||||
|
||||
For each box:
|
||||
4) Find the box
|
||||
5) Drop the box
|
||||
6) Ensure the box collides with the ramp
|
||||
7) Ensure the box reaches its peak height
|
||||
|
||||
8) Special case: assert that a box with zero restitution does not bounce
|
||||
9) Assert that greater restitution coefficients result in higher bounces
|
||||
10) Exit game mode
|
||||
11) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
ZERO_RESTITUTION_BOUNCE_TOLERANCE = 0.001
|
||||
TIMEOUT = 5
|
||||
FALLING_TIMEOUT = 0.1
|
||||
|
||||
class Box:
|
||||
def __init__(self, name, valid_test, fell_test, hit_ramp_test, peaked_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.hit_ramp = False
|
||||
self.hit_ramp_position = None
|
||||
self.bounce_height = 0.0
|
||||
self.valid_test = valid_test
|
||||
self.fell_test = fell_test
|
||||
self.hit_ramp_test = hit_ramp_test
|
||||
self.peaked_test = peaked_test
|
||||
self.set_gravity_enabled(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)
|
||||
|
||||
def set_velocity(self, value):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetLinearVelocity", self.id, value)
|
||||
|
||||
def set_gravity_enabled(self, value):
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "SetGravityEnabled", self.id, value)
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
for box in all_boxes:
|
||||
if box.id.Equal(other_id):
|
||||
box.hit_ramp_position = box.get_position()
|
||||
box.hit_ramp = True
|
||||
|
||||
def reached_max_height(box):
|
||||
current_position = box.get_position()
|
||||
current_height = current_position.z - box.hit_ramp_position.z
|
||||
current_linear_velocity = box.get_velocity()
|
||||
if current_linear_velocity.z > 0.0:
|
||||
box.bounce_height = current_height
|
||||
return False
|
||||
else:
|
||||
Report.info("Box {} reached {:.3f}M high".format(box.name, box.bounce_height))
|
||||
return True
|
||||
|
||||
def is_falling(box):
|
||||
return box.get_velocity().z < 0.0
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4044461_Material_Restitution")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# fmt: off
|
||||
# Set up our boxes
|
||||
box_zero = Box(
|
||||
name = "Zero",
|
||||
valid_test = Tests.find_box_zero,
|
||||
fell_test = Tests.box_fell_zero,
|
||||
hit_ramp_test = Tests.box_hit_ramp_zero,
|
||||
peaked_test = Tests.box_peaked_zero
|
||||
)
|
||||
box_low = Box(
|
||||
name = "Low",
|
||||
valid_test = Tests.find_box_low,
|
||||
fell_test = Tests.box_fell_low,
|
||||
hit_ramp_test = Tests.box_hit_ramp_low,
|
||||
peaked_test = Tests.box_peaked_low
|
||||
)
|
||||
box_mid = Box(
|
||||
name = "Mid",
|
||||
valid_test = Tests.find_box_mid,
|
||||
fell_test = Tests.box_fell_mid,
|
||||
hit_ramp_test = Tests.box_hit_ramp_mid,
|
||||
peaked_test = Tests.box_peaked_mid
|
||||
)
|
||||
box_high = Box(
|
||||
name = "High",
|
||||
valid_test = Tests.find_box_high,
|
||||
fell_test = Tests.box_fell_high,
|
||||
hit_ramp_test = Tests.box_hit_ramp_high,
|
||||
peaked_test = Tests.box_peaked_high
|
||||
)
|
||||
all_boxes = (box_zero, box_low, box_mid, box_high)
|
||||
# fmt:on
|
||||
|
||||
# 3) Find the ramp
|
||||
ramp_id = general.find_game_entity("Ramp")
|
||||
Report.critical_result(Tests.find_ramp, ramp_id.IsValid())
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(ramp_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
for box in all_boxes:
|
||||
Report.info("********Dropping Box {}********".format(box.name))
|
||||
# 4) Find the box
|
||||
Report.critical_result(box.valid_test, box.id.IsValid())
|
||||
|
||||
# 5) Drop the box
|
||||
box.set_gravity_enabled(True)
|
||||
Report.critical_result(box.fell_test, helper.wait_for_condition(lambda: is_falling(box), FALLING_TIMEOUT))
|
||||
|
||||
# 6) Wait for the box to hit the ramp
|
||||
Report.result(box.hit_ramp_test, helper.wait_for_condition(lambda: box.hit_ramp, TIMEOUT))
|
||||
|
||||
# 7) Measure the bounce height
|
||||
Report.result(box.peaked_test, helper.wait_for_condition(lambda: reached_max_height(box), TIMEOUT))
|
||||
|
||||
# Freeze the box so it does not interfere with the other boxes
|
||||
box.set_velocity(lymath.Vector3(0.0, 0.0, 0.0))
|
||||
box.set_gravity_enabled(False)
|
||||
|
||||
# 8) Special case: Assert the a box with zero restitution did not bounce
|
||||
Report.result(Tests.box_zero_did_not_bounce, box_zero.bounce_height < ZERO_RESTITUTION_BOUNCE_TOLERANCE)
|
||||
|
||||
# 9) Assert that greater restitution coefficients result in higher bounces
|
||||
ordered_bounces = box_high.bounce_height > box_mid.bounce_height > box_low.bounce_height > box_zero.bounce_height
|
||||
Report.result(Tests.bounce_height_ordered, ordered_bounces)
|
||||
|
||||
# 10) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4044461_Material_Restitution)
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test Case ID : C4044694
|
||||
# Test Case Title : Verify that if we add an empty Material library in Collider Component, the object continues to use Default material values
|
||||
# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4044694
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_terrain = ("The Terrain was found", "The Terrain was not found")
|
||||
find_default_box = ("'default_box' was found", "'default_box' was not found")
|
||||
find_empty_box = ("'empty_box' was found", "'empty_box' was not found")
|
||||
find_default_sphere = ("'default_sphere' was found", "'default_sphere' was not found")
|
||||
find_empty_sphere = ("'empty_sphere' was found", "'empty_sphere' was not found")
|
||||
boxes_moved = ("All boxes moved", "Boxes failed to move")
|
||||
boxes_at_rest = ("All boxes came to rest", "Boxes failed to come to rest")
|
||||
default_sphere_bounced = ("'default_sphere' bounced", "'default_sphere' did not bounce")
|
||||
empty_sphere_bounced = ("'empty_sphere' bounced", "'empty_sphere' did not bounce")
|
||||
default_box_equals_empty = ("'default_box' and 'empty_box' traveled the same distance", "'default_box' and 'empty_box' did not travel the same distance")
|
||||
default_sphere_equals_empty = ("'default_sphere' and 'empty_sphere' bounce heights were equal", "'default_sphere' and 'empty_sphere' bounce heights were not equal")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4044694_Material_EmptyLibraryUsesDefault():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to verify that an object with an empty Material library in a Collider Component continues to
|
||||
use the default material values
|
||||
|
||||
Level Description:
|
||||
There are 5 entities.
|
||||
One terrain entity ('terrain') with PhysX Terrain,
|
||||
Two sphere entities ('empty_sphere' and 'default_sphere') with PhysX Rigid Body and PhysX Sphere Collider,
|
||||
Two box entities ('empty_box' and 'default_box') with PhysX Rigid Body and PhysX Box Collider,
|
||||
|
||||
The spheres are positioned above the terrain, and the boxes are placed on the terrain.
|
||||
The "empty" entities are assigned a material library that contains no materials. The "default" entities are assigned
|
||||
the default material from the default material library.
|
||||
|
||||
Expected behavior:
|
||||
The spheres fall and bounce the same height.
|
||||
The boxes are pushed and travel the same distance.
|
||||
|
||||
Test Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Find entities
|
||||
3) Wait for spheres to bounce
|
||||
4) Compare 'default_sphere' to 'empty_sphere'
|
||||
5) Push the boxes and wait for them to come to rest
|
||||
6) Compare 'default_box' to 'empty_box'
|
||||
7) Exit game mode and close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.components
|
||||
import azlmbr.physics
|
||||
import azlmbr.math as lymath
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
FORCE_IMPULSE = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
TIMEOUT = 3.0
|
||||
DISTANCE_TOLERANCE = 0.001
|
||||
|
||||
class Entity:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(self.name)
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Box(Entity):
|
||||
def __init__(self, name):
|
||||
Entity.__init__(self, name)
|
||||
self.start_position = self.position
|
||||
|
||||
def is_stationary(self):
|
||||
velocity = azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetLinearVelocity", self.id)
|
||||
return velocity.IsZero()
|
||||
|
||||
def push(self):
|
||||
azlmbr.physics.RigidBodyRequestBus(bus.Event, "ApplyLinearImpulse", self.id, FORCE_IMPULSE)
|
||||
|
||||
class Sphere(Entity):
|
||||
def __init__(self, name):
|
||||
Entity.__init__(self, name)
|
||||
self.hit_terrain_position = None
|
||||
self.hit_terrain = False
|
||||
self.max_bounce = 0.0
|
||||
self.reached_max_bounce = False
|
||||
|
||||
def on_collision_enter(args):
|
||||
entering = args[0]
|
||||
for sphere in [default_sphere, empty_sphere]:
|
||||
if sphere.id.Equal(entering):
|
||||
if not sphere.hit_terrain:
|
||||
sphere.hit_terrain_position = sphere.position
|
||||
sphere.hit_terrain = True
|
||||
|
||||
# region wait_for_condition() functions
|
||||
def wait_for_bounce():
|
||||
for sphere in [default_sphere, empty_sphere]:
|
||||
if sphere.hit_terrain:
|
||||
current_bounce_height = sphere.position.z - sphere.hit_terrain_position.z
|
||||
if current_bounce_height >= sphere.max_bounce:
|
||||
sphere.max_bounce = current_bounce_height
|
||||
elif sphere.max_bounce > 0.0:
|
||||
sphere.reached_max_bounce = True
|
||||
return default_sphere.reached_max_bounce and empty_sphere.reached_max_bounce
|
||||
|
||||
def boxes_moved():
|
||||
return not default_box.is_stationary() and not empty_box.is_stationary()
|
||||
|
||||
def boxes_are_stationary():
|
||||
return default_box.is_stationary() and empty_box.is_stationary()
|
||||
|
||||
# endregion
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4044694_Material_EmptyLibraryUsesDefault")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Find entities
|
||||
terrain_id = general.find_game_entity("terrain")
|
||||
default_box = Box("default_box")
|
||||
empty_box = Box("empty_box")
|
||||
default_sphere = Sphere("default_sphere")
|
||||
empty_sphere = Sphere("empty_sphere")
|
||||
|
||||
Report.result(Tests.find_terrain, terrain_id.IsValid())
|
||||
Report.result(Tests.find_default_box, default_box.id.IsValid())
|
||||
Report.result(Tests.find_empty_box, empty_box.id.IsValid())
|
||||
Report.result(Tests.find_default_sphere, default_sphere.id.IsValid())
|
||||
Report.result(Tests.find_empty_sphere, empty_sphere.id.IsValid())
|
||||
|
||||
# Setup terrain collision handler
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(terrain_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_enter)
|
||||
|
||||
# 3) Wait for spheres to bounce
|
||||
helper.wait_for_condition(wait_for_bounce, TIMEOUT)
|
||||
Report.result(Tests.default_sphere_bounced, default_sphere.reached_max_bounce)
|
||||
Report.result(Tests.empty_sphere_bounced, empty_sphere.reached_max_bounce)
|
||||
|
||||
# 4) Compare 'default_sphere' to 'empty_sphere'
|
||||
sphere_bounces_equal = lymath.Math_IsClose(default_sphere.max_bounce, empty_sphere.max_bounce, DISTANCE_TOLERANCE)
|
||||
Report.result(Tests.default_sphere_equals_empty, sphere_bounces_equal)
|
||||
|
||||
# 5) Push the boxes and wait for them to come to rest
|
||||
default_box.push()
|
||||
empty_box.push()
|
||||
Report.result(Tests.boxes_moved, helper.wait_for_condition(boxes_moved, TIMEOUT))
|
||||
Report.result(Tests.boxes_at_rest, helper.wait_for_condition(boxes_are_stationary, TIMEOUT))
|
||||
|
||||
# 6) Compare 'default_box' to 'empty_box'
|
||||
default_distance = default_box.position.GetDistance(default_box.start_position)
|
||||
empty_distance = empty_box.position.GetDistance(empty_box.start_position)
|
||||
box_distances_equal = lymath.Math_IsClose(default_distance, empty_distance, DISTANCE_TOLERANCE)
|
||||
Report.result(Tests.default_box_equals_empty, box_distances_equal)
|
||||
|
||||
# 7) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4044694_Material_EmptyLibraryUsesDefault)
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C4044695
|
||||
Test Case Title : Verify that when you add a multiple surface fbx in PxMesh in PhysxCollider,
|
||||
multiple number of Material Slots populate in the Materials Section
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044695
|
||||
"""
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_entity = ("Created test entity", "Failed to create test entity")
|
||||
mesh_added = ("Added Mesh component", "Failed to add Mesh component")
|
||||
physx_collider_added = ("Added PhysX Collider component", "Failed to add PhysX Collider component")
|
||||
shape_is_correct = ("PhysX Collider Shape is correct", "PhysX Collider Shape is not PhysicsAsset")
|
||||
assign_mesh_asset = ("Assigned Mesh asset to Mesh component", "Failed to assign mesh asset to Mesh component")
|
||||
assign_px_mesh_asset = ("Assigned PxMesh asset to Collider component", "Failed to assign PxMesh asset to Collider component")
|
||||
count_mesh_surface = ("Multiple slots show under materials", "Failed to show required surface materials")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Summary:
|
||||
Create entity with Mesh and PhysX Collider components and assign a fbx file in both the components.
|
||||
Verify that the fbx is properly fitting the mesh.
|
||||
|
||||
Expected Behavior:
|
||||
1) The fbx is properly fitting the mesh.
|
||||
2) Multiple material slots show up under Materials section in the PhysX Collider component and that
|
||||
they correspond to the number of surfaces as designed in the mesh.
|
||||
|
||||
Test Steps:
|
||||
1) Load the empty level
|
||||
2) Create an entity
|
||||
3) Add Mesh and Physics collider components
|
||||
4) Select the PhysicsAsset shape in the PhysX Collider component
|
||||
5) Assign the fbx file in PhysX Mesh and Mesh component
|
||||
6) Check if multiple material slots show up under Materials section in the PhysX Collider component
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Builtins
|
||||
import os
|
||||
|
||||
# Helper Files
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from editor_entity_utils import EditorEntity as Entity
|
||||
from asset_utils import Asset
|
||||
|
||||
# Constants
|
||||
PHYSICS_ASSET_INDEX = 7 # Hardcoded enum index value for Shape property
|
||||
SURFACE_TAG_COUNT = 4 # Number of surface tags included in used asset
|
||||
|
||||
# Asset paths
|
||||
STATIC_MESH = os.path.join("assets", "c4044695_physxcollider_addmultiplesurfacefbx", "test.cgf")
|
||||
PHYSX_MESH = os.path.join("assets", "c4044695_physxcollider_addmultiplesurfacefbx", "test.pxmesh")
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the empty level
|
||||
helper.open_level("Physics", "Base")
|
||||
|
||||
# 2) Create an entity
|
||||
test_entity = Entity.create_editor_entity("test_entity")
|
||||
Report.result(Tests.create_entity, test_entity.id.IsValid())
|
||||
|
||||
# 3) Add Mesh and Physics collider components
|
||||
mesh_component = test_entity.add_component("Mesh")
|
||||
Report.result(Tests.mesh_added, test_entity.has_component("Mesh"))
|
||||
|
||||
collider_component = test_entity.add_component("PhysX Collider")
|
||||
Report.result(Tests.physx_collider_added, test_entity.has_component("PhysX Collider"))
|
||||
|
||||
# 4) Select the PhysicsAsset shape in the PhysX Collider component
|
||||
collider_component.set_component_property_value("Shape Configuration|Shape", PHYSICS_ASSET_INDEX)
|
||||
value_to_test = collider_component.get_component_property_value("Shape Configuration|Shape")
|
||||
Report.result(Tests.shape_is_correct, value_to_test == PHYSICS_ASSET_INDEX)
|
||||
|
||||
# 5) Assign the fbx file in PhysX Mesh and Mesh component
|
||||
px_asset = Asset.find_asset_by_path(PHYSX_MESH)
|
||||
collider_component.set_component_property_value("Shape Configuration|Asset|PhysX Mesh", px_asset.id)
|
||||
px_asset.id = collider_component.get_component_property_value("Shape Configuration|Asset|PhysX Mesh")
|
||||
Report.result(Tests.assign_px_mesh_asset, px_asset.get_path() == PHYSX_MESH.replace(os.sep, "/"))
|
||||
|
||||
mesh_asset = Asset.find_asset_by_path(STATIC_MESH)
|
||||
mesh_component.set_component_property_value("MeshComponentRenderNode|Mesh asset", mesh_asset.id)
|
||||
mesh_asset.id = mesh_component.get_component_property_value("MeshComponentRenderNode|Mesh asset")
|
||||
Report.result(Tests.assign_mesh_asset, mesh_asset.get_path() == STATIC_MESH.replace(os.sep, "/"))
|
||||
|
||||
# 6) Check if multiple material slots show up under Materials section in the PhysX Collider component
|
||||
pte = collider_component.get_property_tree()
|
||||
def get_surface_count():
|
||||
count = pte.get_container_count("Collider Configuration|Physics Material|Mesh Surfaces")
|
||||
return count.GetValue()
|
||||
|
||||
Report.result(
|
||||
Tests.count_mesh_surface, helper.wait_for_condition(lambda: get_surface_count() == SURFACE_TAG_COUNT, 1.0)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4044697
|
||||
# Test Case Title : Verify that each surface picks up the material assigned to it and behaves accordingly.
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4044697
|
||||
|
||||
# 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")
|
||||
initial_orientaition_valid = ("Initial entity orientation valid", "Initial entity orientation not valid")
|
||||
final_orientation_valid = ("Final entity orientation valid", "Final entity orientation not valid")
|
||||
speed_comparision = ("Sphere 1 is faster than Sphere 2", "Sphere 1 is not faster than Sphere 2")
|
||||
|
||||
# Sphere 0
|
||||
Sphere_0_found = ("Sphere 0 is valid", "Sphere 0 is not valid")
|
||||
Sphere_0_position_found = ("Sphere 0 position is found", "Sphere 0 position is not found")
|
||||
Sphere_0_velocity_found = ("Sphere 0 velocity is found", "Sphere 0 velocity is not found")
|
||||
Sphere_0_velocity_valid = ("Sphere 0 velocity is valid", "Sphere 0 velocity is not valid")
|
||||
Sphere_0_collided_with_perface = ("Sphere 0 collided w/Perface Entity", "Sphere 0 has not collided")
|
||||
Sphere_0_final_velocity_valid = ("Sphere 0 final velocity is valid", "Sphere 0 final velocity is not valid")
|
||||
|
||||
# Sphere 1
|
||||
Sphere_1_found = ("Sphere 1 is valid", "Sphere 1 is not valid")
|
||||
Sphere_1_position_found = ("Sphere 1 position is found", "Sphere 1 position is not found")
|
||||
Sphere_1_velocity_found = ("Sphere 1 velocity is found", "Sphere 1 velocity is not found")
|
||||
Sphere_1_velocity_valid = ("Sphere 1 velocity is valid", "Sphere 1 velocity is not valid")
|
||||
Sphere_1_collided_with_perface = ("Sphere 1 collided w/Perface Entity", "Sphere 1 has not collided")
|
||||
Sphere_1_final_velocity_valid = ("Sphere 1 final velocity is valid", "Sphere 1 final velocity is not valid")
|
||||
|
||||
# Sphere 2
|
||||
Sphere_2_found = ("Sphere 2 is valid", "Sphere 2 is not valid")
|
||||
Sphere_2_position_found = ("Sphere 2 position is found", "Sphere 2 position is not found")
|
||||
Sphere_2_velocity_found = ("Sphere 2 velocity is found", "Sphere 2 velocity is not found")
|
||||
Sphere_2_velocity_valid = ("Sphere 2 velocity is valid", "Sphere 2 velocity is not valid")
|
||||
Sphere_2_collided_with_perface = ("Sphere 2 collided w/Perface Entity", "Sphere 2 has not collided")
|
||||
Sphere_2_final_velocity_valid = ("Sphere 2 final velocity is valid", "Sphere 2 final velocity is not valid")
|
||||
|
||||
# Perface Entity
|
||||
Perface_Entity_found = ("Perface entity is valid", "Perface entity is not valid")
|
||||
Perface_Entity_position_found = ("Perface entity position found", "Perface entity position not found")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4044697_Material_PerfaceMaterialValidation():
|
||||
"""
|
||||
Summary: The perface has three different faces that can pick up different materials. To check that each face
|
||||
picks up the material assigned to it I send three spheres of the same material at each face to see the
|
||||
different reactions. If each sphere bounces away with the correct relative velocity it can be assumed
|
||||
that the Perface entity is picking up the materials properly.
|
||||
|
||||
Level Description:
|
||||
Perface Entity - The perface entity is an entity with a custom mesh that allows for multiple materials to be
|
||||
applied to different parts of the mesh. In this case there seems to be three different areas of the mesh
|
||||
that can be assigned with different materials and interacted. One of three spheres is lined up to interact
|
||||
with one of each of the three areas. The mesh is included in the level "test.fbx". The entity is stationary
|
||||
with three spheres inline along the x and y axis: has a PhysX collider and a Mesh component.
|
||||
Sphere 0 - This entity is inline with the perface entity on the y axis and heading torward it with a velocity in
|
||||
the -y direction: has a sphere shaped PhysX Collider, a PhysX Rigid Body, and a Sphere Shape.
|
||||
Sphere 1 - This entity is inline with the perface entity on the x axis and heading torward it with a velocity in
|
||||
the -x direction: has a sphere shaped PhysX Collider, a PhysX Rigid Body, and a Sphere Shape.
|
||||
Sphere 2 - This entity is inline with the perface entity on the x axis and heading torward it with a velocity in
|
||||
the +x direction: has a sphere shaped PhysX Collider, a PhysX Rigid Body, and a Sphere Shape.
|
||||
|
||||
Materials:
|
||||
Bounce - All three spheres and the Perface mesh area lined up with Sphere 1 have the Bounce material applied to
|
||||
them. This material interacts with other materials by bouncing with the restitution factor of an average of
|
||||
each entity that collides restitution value. Has restitution value: 1
|
||||
PartialBounce - The Perface mesh area lined up with Sphere 2 has the partial bounce material applied. This material
|
||||
interacts with other materials by responding with a restitution factor that is an average of the two materials
|
||||
that interact. Has restitution value: 0
|
||||
NoBounce - The Perface mesh area lined up with Sphere 0 have the NoBounce material. This material interacts with
|
||||
other materials by bouncing with the restitution factor of the material with the lowest restitution value.
|
||||
Has restitution value: 0
|
||||
|
||||
Expected Behavior: Sphere 0 will not bounce, Sphere 1 will bounce away from the Perface Entity faster than
|
||||
Sphere 2 will.
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter Game Mode
|
||||
3) Create Entity objects
|
||||
4) Iterate through all entities and validate them
|
||||
5) Validate that Entities Exist
|
||||
6) Iterate through each of the three spheres and test their bounces
|
||||
7) Further evaluate that sphere entities exist
|
||||
8) Validate Initial Positions and Velocities
|
||||
9) Set up handler and wait for collision
|
||||
10) Get and Validate Final Positions and Velocities
|
||||
11) Log Results
|
||||
12) Validate Orientations and Final Velocities
|
||||
13) Exit Game Mode
|
||||
14) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as math
|
||||
|
||||
# Constants
|
||||
FLOAT_THRESHOLD = sys.float_info.epsilon
|
||||
FINAL_VELOCITY_THRESHOLD = 0.01
|
||||
STATIONARY_SPHERE_THRESHOLD = 2
|
||||
TIMEOUT = 1.0
|
||||
|
||||
# Helper Functions
|
||||
class Entity:
|
||||
def __init__(self, name, expected_initial_velocity=None, expected_final_velocity=None):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.name = name
|
||||
self.EXPECTED_INITIAL_VELOCITY = expected_initial_velocity
|
||||
self.EXPECTED_FINAL_VELOCITY = expected_final_velocity
|
||||
self.initial_velocity = None
|
||||
self.final_velocity = None
|
||||
self.initial_position = None
|
||||
self.final_position = None
|
||||
self.collision_happened = False
|
||||
self.handler = None
|
||||
|
||||
class Entity_Tests:
|
||||
found = None
|
||||
found_position = None
|
||||
found_velocity = None
|
||||
valid_init_velocity = None
|
||||
valid_final_velocity = None
|
||||
collision_happened = None
|
||||
|
||||
def check_id(self):
|
||||
self.Entity_Tests.found = Tests.__dict__[self.name + "_found"]
|
||||
Report.critical_result(self.Entity_Tests.found, self.id.isValid())
|
||||
|
||||
def activate_entity(self):
|
||||
Report.info("Activating Entity : " + self.name)
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.id)
|
||||
|
||||
def values_found(self):
|
||||
self.Entity_Tests.found_position = Tests.__dict__[self.name + "_position_found"]
|
||||
self.Entity_Tests.found_velocity = Tests.__dict__[self.name + "_velocity_found"]
|
||||
|
||||
Report.critical_result(self.Entity_Tests.found_position, vector_valid(self.initial_position, False))
|
||||
Report.critical_result(self.Entity_Tests.found_velocity, vector_valid(self.initial_velocity, False))
|
||||
|
||||
def perface_values_found(self):
|
||||
self.Entity_Tests.found_position = Tests.__dict__[self.name + "_position_found"]
|
||||
Report.critical_result(self.Entity_Tests.found_position, vector_valid(self.initial_position, False))
|
||||
|
||||
def get_initial_position_and_velocity(self):
|
||||
self.initial_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
self.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
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 validate_sphere_velocity(self):
|
||||
if self.collision_happened:
|
||||
velocity_valid = (
|
||||
abs(self.final_velocity.x - self.EXPECTED_FINAL_VELOCITY.x) < FINAL_VELOCITY_THRESHOLD
|
||||
and abs(self.final_velocity.y - self.EXPECTED_FINAL_VELOCITY.y) < FINAL_VELOCITY_THRESHOLD
|
||||
and abs(self.final_velocity.z - self.EXPECTED_FINAL_VELOCITY.z) < FINAL_VELOCITY_THRESHOLD
|
||||
)
|
||||
self.Entity_Tests.valid_final_velocity = Tests.__dict__[self.name + "_final_velocity_valid"]
|
||||
Report.result(self.Entity_Tests.valid_final_velocity, velocity_valid)
|
||||
else:
|
||||
velocity_valid = (
|
||||
abs(self.initial_velocity.x - self.EXPECTED_INITIAL_VELOCITY.x) < FLOAT_THRESHOLD
|
||||
and abs(self.initial_velocity.y - self.EXPECTED_INITIAL_VELOCITY.y) < FLOAT_THRESHOLD
|
||||
and abs(self.initial_velocity.z - self.EXPECTED_INITIAL_VELOCITY.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
self.Entity_Tests.valid_init_velocity = Tests.__dict__[self.name + "_velocity_valid"]
|
||||
Report.critical_result(self.Entity_Tests.valid_init_velocity, velocity_valid)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
if self.id.equal(args[0]):
|
||||
self.collision_happened = True
|
||||
|
||||
def set_handler(self, id):
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
def report_sphere_values(entity):
|
||||
Report.info_vector3(entity.initial_position, "{} initial position: ".format(entity.name))
|
||||
Report.info_vector3(entity.initial_velocity, "{} initial velocity: ".format(entity.name))
|
||||
|
||||
Report.info_vector3(entity.final_position, "{} final position: ".format(entity.name))
|
||||
Report.info_vector3(entity.final_velocity, "{} final velocity: ".format(entity.name))
|
||||
|
||||
def report_perface_values(entity):
|
||||
Report.info_vector3(entity.initial_position, "{} initial position: ".format(entity.name))
|
||||
|
||||
def validate_positions():
|
||||
# Initial orientation is confirmed by z axis values, if there are further issues a collision
|
||||
# will not as expected.
|
||||
Report.info("Checking Initial Orientation")
|
||||
initial_orientaition = (
|
||||
sphere_0.initial_position.z
|
||||
== sphere_1.initial_position.z
|
||||
== sphere_2.initial_position.z
|
||||
== perface_entity.initial_position.z
|
||||
)
|
||||
Report.result(Tests.initial_orientaition_valid, initial_orientaition)
|
||||
# Final orientation is confirmed if Sphere 0 stopped next to the Perface Entity.
|
||||
Report.info("Checking Final Orientation")
|
||||
final_orientation = (
|
||||
abs(perface_entity.final_position.x - sphere_0.final_position.x) < FLOAT_THRESHOLD
|
||||
and abs(perface_entity.final_position.z - sphere_0.final_position.z) < FLOAT_THRESHOLD
|
||||
and abs(perface_entity.final_position.y - sphere_0.final_position.y) < STATIONARY_SPHERE_THRESHOLD
|
||||
)
|
||||
|
||||
Report.result(Tests.final_orientation_valid, final_orientation)
|
||||
|
||||
def vector_valid(vector, can_be_zero):
|
||||
if can_be_zero:
|
||||
return vector != None
|
||||
else:
|
||||
return vector != None and not vector.IsZero()
|
||||
|
||||
# Main Script
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C4044697_Material_PerfaceMaterialValidation")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Create entity objects
|
||||
sphere_0 = Entity("Sphere_0", math.Vector3(0.0, -10.0, 0.0), math.Vector3(0.0, 0.0, 0.0))
|
||||
sphere_1 = Entity("Sphere_1", math.Vector3(-10.0, 0.0, 0.0), math.Vector3(8.35, -0.71, 4.56))
|
||||
sphere_2 = Entity("Sphere_2", math.Vector3(10.0, 0.0, 0.0), math.Vector3(-5.0, 0.0, 0.0))
|
||||
perface_entity = Entity("Perface_Entity")
|
||||
entity_list = [sphere_0, sphere_1, sphere_2, perface_entity]
|
||||
spheres = [sphere_0, sphere_1, sphere_2]
|
||||
|
||||
# 4) Iterate through all entities and validate them
|
||||
for entity in entity_list:
|
||||
# 5) Validate that Entities Exist
|
||||
entity.check_id()
|
||||
# Extra steps for Perface Entity as it will no longer be iterated
|
||||
perface_entity.get_initial_position_and_velocity()
|
||||
perface_entity.perface_values_found()
|
||||
perface_entity.get_final_position_and_velocity()
|
||||
report_perface_values(perface_entity)
|
||||
|
||||
# 6) Iterate through each of the three spheres and test their bounces
|
||||
for entity in spheres:
|
||||
# 7) Further evaluate that sphere entities exist
|
||||
entity.activate_entity()
|
||||
entity.get_initial_position_and_velocity()
|
||||
|
||||
# 8) Validate Initial Positions and Velocities
|
||||
entity.values_found()
|
||||
entity.validate_sphere_velocity()
|
||||
|
||||
# 9) Set up handler and wait for collision
|
||||
entity.set_handler(perface_entity.id)
|
||||
|
||||
# Wait for collision
|
||||
helper.wait_for_condition(lambda: entity.collision_happened, TIMEOUT)
|
||||
# Report Collision
|
||||
entity.Entity_Tests.collision_happened = Tests.__dict__[entity.name + "_collided_with_perface"]
|
||||
Report.result(entity.Entity_Tests.collision_happened, entity.collision_happened)
|
||||
|
||||
# 10) Get and Validate Final Positions and Velocities
|
||||
entity.get_final_position_and_velocity()
|
||||
entity.validate_sphere_velocity()
|
||||
|
||||
# 11) Log Results
|
||||
report_sphere_values(entity)
|
||||
|
||||
# 12) Validate Orientations and Final Velocities
|
||||
validate_positions()
|
||||
Report.result(
|
||||
Tests.speed_comparision, sphere_1.final_velocity.GetLength() > sphere_2.final_velocity.GetLength()
|
||||
)
|
||||
|
||||
# 13) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4044697_Material_PerfaceMaterialValidation)
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test Case ID : C4888315
|
||||
# Test Case Title : Check that any change (Add/Delete/Modify) made to the material surface in the material library reflects immediately in the PhysX Collider component
|
||||
# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4888315
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_0 = ("Test 0) Entered game mode", "Test 0) Failed to enter game mode")
|
||||
find_terrain_0 = ("Test 0) The Terrain was found", "Test 0) The Terrain was not found")
|
||||
find_default_box_0 = ("Test 0) Default box was found", "Test 0) Default box was not found")
|
||||
find_modified_box_0 = ("Test 0) Modified box was found", "Test 0) Modified box was not found")
|
||||
boxes_moved_0 = ("Test 0) All boxes moved", "Test 0) Boxes failed to move")
|
||||
boxes_at_rest_0 = ("Test 0) All boxes came to rest", "Test 0) Boxes failed to come to rest")
|
||||
exit_game_mode_0 = ("Test 0) Exited game mode", "Test 0) Failed to exit game mode")
|
||||
default_less_than_modified = ("Test 0) Modified box traveled farther than default", "Test 0) Modified box did not travel farther than default")
|
||||
|
||||
enter_game_mode_1 = ("Test 1) Entered game mode", "Test 1) Failed to enter game mode")
|
||||
find_terrain_1 = ("Test 1) The Terrain was found", "Test 1) The Terrain was not found")
|
||||
find_default_box_1 = ("Test 1) Default box was found", "Test 1) Default box was not found")
|
||||
find_modified_box_1 = ("Test 1) Modified box was found", "Test 1) Modified box was not found")
|
||||
boxes_moved_1 = ("Test 1) All boxes moved", "Test 1) Boxes failed to move")
|
||||
boxes_at_rest_1 = ("Test 1) All boxes came to rest", "Test 1) Boxes failed to come to rest")
|
||||
exit_game_mode_1 = ("Test 1) Exited game mode", "Test 1) Failed to exit game mode")
|
||||
modified_less_than_previous = ("Test 1) Modified box traveled less than previous", "Test 1) Modified box traveled further than previous")
|
||||
|
||||
enter_game_mode_2 = ("Test 2) Entered game mode", "Test 2) Failed to enter game mode")
|
||||
find_terrain_2 = ("Test 2) The Terrain was found", "Test 2) The Terrain was not found")
|
||||
find_default_box_2 = ("Test 2) Default box was found", "Test 2) Default box was not found")
|
||||
find_modified_box_2 = ("Test 2) Modified box was found", "Test 2) Modified box was not found")
|
||||
boxes_moved_2 = ("Test 2) All boxes moved", "Test 2) Boxes failed to move")
|
||||
boxes_at_rest_2 = ("Test 2) All boxes came to rest", "Test 2) Boxes failed to come to rest")
|
||||
exit_game_mode_2 = ("Test 2) Exited game mode", "Test 2) Failed to exit game mode")
|
||||
default_equals_modified = ("Test 2) Modified and Default boxes traveled the same distance", "Test 2) Modified and Default boxes did not travel the same distance")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4888315_Material_AddModifyDeleteOnCollider():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to verify that any change (Add/Delete/Modify) made to the material surface in the material
|
||||
library reflects immediately in the PhysX Collider component
|
||||
|
||||
Level Description:
|
||||
Two boxes ("default" and "modified") sit on the terrain. The boxes are identical, save for their physX material.
|
||||
The box "default" is assigned the default physx material.
|
||||
A new material library was created with 1 material, called "Modified", this is assigned to the "modified" box:
|
||||
dynamic friction: 0.25
|
||||
static friction: 0.5
|
||||
restitution: 0.5
|
||||
|
||||
Expected behavior:
|
||||
For every iteration this test applies a force impulse in the X direction. The boxes save their traveled distances
|
||||
each iteration, to verify different behavior between each setup.
|
||||
|
||||
First the test verifies the two entities are assigned differing materials, without changing anything. With a lower
|
||||
dynamic friction coefficient, the 'modified' should travel a longer distance than 'default'
|
||||
|
||||
Next, the test modifies the dynamic friction value for 'modified' (from 0.25 to 0.75). 'modified' should travel a
|
||||
shorter distance than it did in the previous test.
|
||||
|
||||
Finally, we delete the 'modified' material entirely. The 'modified' box should then behave as the 'default' box, and
|
||||
travel the same distance.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Collect basis values without modifying anything
|
||||
2.1) Enter game mode
|
||||
2.2) Find entities
|
||||
2.3) Push the boxes and wait for them to come to rest
|
||||
2.4) Exit game mode
|
||||
3) Modify the dynamic friction value of 'modified'
|
||||
3.1 - 3.4) <same as above>
|
||||
4) Delete 'modified's' material
|
||||
4.1 - 4.4) <same as above>
|
||||
5) Close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as lymath
|
||||
|
||||
from Physmaterial_Editor import Physmaterial_Editor
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from AddModifyDelete_Utils import Box
|
||||
|
||||
FORCE_IMPULSE = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
TIMEOUT = 3.0
|
||||
DISTANCE_TOLERANCE = 0.001
|
||||
|
||||
def get_test(test_name):
|
||||
return Tests.__dict__[test_name]
|
||||
|
||||
def run_test(test_number):
|
||||
# x.1) Enter game mode
|
||||
helper.enter_game_mode(get_test("enter_game_mode_{}".format(test_number)))
|
||||
|
||||
# x.2) Find entities
|
||||
Report.result(get_test("find_terrain_{}".format(test_number)), general.find_game_entity("terrain").IsValid())
|
||||
Report.result(get_test("find_default_box_{}".format(test_number)), default_box.find())
|
||||
Report.result(get_test("find_modified_box_{}".format(test_number)), modified_box.find())
|
||||
|
||||
# x.3) Push the boxes and wait for them to come to rest
|
||||
default_box.push(FORCE_IMPULSE)
|
||||
modified_box.push(FORCE_IMPULSE)
|
||||
|
||||
def boxes_are_moving():
|
||||
return not default_box.is_stationary() and not modified_box.is_stationary()
|
||||
|
||||
def boxes_are_stationary():
|
||||
return default_box.is_stationary() and modified_box.is_stationary()
|
||||
|
||||
Report.result(
|
||||
get_test("boxes_moved_{}".format(test_number)),
|
||||
helper.wait_for_condition(boxes_are_moving, TIMEOUT),
|
||||
)
|
||||
Report.result(
|
||||
get_test("boxes_at_rest_{}".format(test_number)),
|
||||
helper.wait_for_condition(boxes_are_stationary, TIMEOUT),
|
||||
)
|
||||
|
||||
default_box.distances.append(default_box.position.GetDistance(default_box.start_position))
|
||||
modified_box.distances.append(modified_box.position.GetDistance(modified_box.start_position))
|
||||
|
||||
# x.4) Exit game mode
|
||||
helper.exit_game_mode(get_test("exit_game_mode_{}".format(test_number)))
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4888315_Material_AddModifyDeleteOnCollider")
|
||||
|
||||
# Setup persisting entities
|
||||
default_box = Box("default")
|
||||
modified_box = Box("modified")
|
||||
|
||||
# 2) Collect basis values without modifying anything
|
||||
run_test(0)
|
||||
# With a friction of 0.25, 'modified' should travel farther than 'default'
|
||||
Report.result(Tests.default_less_than_modified, default_box.distances[0] < modified_box.distances[0])
|
||||
|
||||
# 3) Modify the dynamic friction value of 'modified'
|
||||
material_editor = Physmaterial_Editor("c4888315_material_addmodifydeleteoncollider.physmaterial")
|
||||
material_editor.modify_material("Modified", "DynamicFriction", 0.75)
|
||||
material_editor.save_changes()
|
||||
run_test(1)
|
||||
# With greater friction, 'modified' should now travel a shorter distance than it did in the previous test.
|
||||
Report.result(Tests.modified_less_than_previous, modified_box.distances[0] > modified_box.distances[1])
|
||||
|
||||
# 4) Delete 'modified's material
|
||||
material_editor.delete_material("Modified")
|
||||
material_editor.save_changes()
|
||||
run_test(2)
|
||||
Report.result(
|
||||
Tests.default_equals_modified,
|
||||
lymath.Math_IsClose(default_box.distances[2], modified_box.distances[2], DISTANCE_TOLERANCE),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4888315_Material_AddModifyDeleteOnCollider)
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4925577
|
||||
# Test Case Title : Verify that material can be assigned to PhysX terrain in Terrain Texture Layers
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4925577
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
game_mode_enter = ("Game mode was successfully entered", "Game mode could not be entered")
|
||||
find_terrain = ("Terrain was found", "Terrain was not found")
|
||||
find_ball_default = ("Ball_Default was found", "Ball_Default was not found")
|
||||
find_ball_rubber = ("Ball_Rubber was found", "Ball_Rubber was not found")
|
||||
find_ball_concrete = ("Ball_Concrete was found", "Ball_Concrete was not found")
|
||||
all_gravity_disabled = ("All the balls started with gravity disabled", "Not all the balls started with gravity disabled")
|
||||
same_starting_height = ("The 3 balls started at the same height", "The 3 balls were not the same height at start")
|
||||
balls_are_aligned = ("The balls are initially lined up properly", "The balls are not initially lined up properly")
|
||||
terrain_collide_default = ("Ball_Default has collided with terrain", "Ball_Default timed out before colliding with terrain")
|
||||
terrain_collide_rubber = ("Ball_Rubber has collided with terrain", "Ball_Rubber timed out before colliding with terrain")
|
||||
terrain_collide_concrete = ("Ball_Concrete has collided with terrain", "Ball_Concrete timed out before colliding with terrain")
|
||||
peak_reached_default = ("Ball_Default has reached peak height", "Ball_Default timed out before reaching peak")
|
||||
peak_reached_rubber = ("Ball_Rubber has reached peak height", "Ball_Rubber timed out before reaching peak")
|
||||
peak_reached_concrete = ("Ball_Concrete has reached peak height", "Ball_Concrete timed out before reaching peak")
|
||||
bounce_height_order_correct = ("The ball bounce heights are correctly ordered", "The ball bounce heights are not correctly ordered")
|
||||
game_mode_exit = ("Game mode was successfully exited", "Game mode could not exit properly")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4925577_Materials_MaterialAssignedToTerrain():
|
||||
"""
|
||||
Summary:
|
||||
Three spheres are suspended above the terrain. Beneath two of the balls,
|
||||
there is a different material painted on the terrain.
|
||||
They should all bounce at different heights per their respective terrains
|
||||
|
||||
Terrain entity: PhysX Terrain component: default settings
|
||||
Ball Entities: Sphere shaped Mesh component
|
||||
Sphere shaped PhysX Collider component: default settings
|
||||
PhysX Rigid Body component: Gravity disabled, default settings
|
||||
|
||||
Concrete Material: Restitution: 0.0; Restitution Combine: Average
|
||||
Rubber Material: Restitution: 1.0; Restitution Combine: Average
|
||||
|
||||
Expected Behavior:
|
||||
The three balls start off at the same height. When game mode is entered they will fall towards the terrain.
|
||||
After the ball collides with the terrain, they will bounce back at different heights respective to their
|
||||
terrain material collisions. Ball_Default is the control and is dropped on default terrain material.
|
||||
Ball_Rubber bounces off the rubber terrain material and should bounce higher than the default.
|
||||
Ball_Concrete strikes the concrete terrain material and should not bounce as high as the default material.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find entities
|
||||
4) Check that gravity is disabled for all the balls initially
|
||||
5) Check that the balls are aligned and all falling from the same height
|
||||
Steps 6-9 run for each ball
|
||||
6) Assign the tests and enable handlers to their respective spheres
|
||||
7) Enable gravity on ball entities
|
||||
8) Check that the balls collide with the PhysX Terrain
|
||||
9) Wait for the ball to reach its peak height; record height and freeze it
|
||||
10) Compare the bounce heights of the balls
|
||||
11) Exit game mode and close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.physics as phys
|
||||
import azlmbr.math as mathazon
|
||||
|
||||
# fmt: off
|
||||
ZERO_VECTOR = mathazon.Vector3(0.0, 0.0, 0.0)
|
||||
X_POSITION_RUBBER = 60.0 # Point on X axis material was painted rubber during level setup
|
||||
X_POSITION_DEFAULT = 70.0 # Area in between other materials where Default material exists
|
||||
X_POSITION_CONCRETE = 80.0 # Point on X axis material was painted concrete during level setup
|
||||
Y_POSITION_VALUE = 42.0 # Point on Y axis materials were painted during level setup
|
||||
POSITION_BUFFER = 4.0 # Material paint radius is 4.0 m
|
||||
TIMEOUT_IN_SECONDS = 3.0
|
||||
NUM_WAIT_FRAMES_ENTITY_LOAD = 2 # Frames to wait to allow entities to load in level
|
||||
# fmt: on
|
||||
|
||||
class Terrain:
|
||||
id = None
|
||||
name = None
|
||||
handler = None
|
||||
|
||||
class Sphere:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(self.name)
|
||||
self.gravity_enabled = phys.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
self.world_location_start = self.get_location()
|
||||
self.handler = None
|
||||
self.hit_ground = False
|
||||
self.bounced = False
|
||||
self.peak_reached = False
|
||||
self.ground_height = 0.0
|
||||
self.peak_height = 0.0
|
||||
|
||||
def assign_tests(self):
|
||||
if self.name == "Ball_Default":
|
||||
self.test_find_ball = Tests.find_ball_default
|
||||
self.test_terrain_collide = Tests.terrain_collide_default
|
||||
self.test_peak_reached = Tests.peak_reached_default
|
||||
|
||||
elif self.name == "Ball_Rubber":
|
||||
self.test_find_ball = Tests.find_ball_rubber
|
||||
self.test_terrain_collide = Tests.terrain_collide_rubber
|
||||
self.test_peak_reached = Tests.peak_reached_rubber
|
||||
|
||||
elif self.name == "Ball_Concrete":
|
||||
self.test_find_ball = Tests.find_ball_concrete
|
||||
self.test_terrain_collide = Tests.terrain_collide_concrete
|
||||
self.test_peak_reached = Tests.peak_reached_concrete
|
||||
|
||||
def get_location(self):
|
||||
# () -> Vector3
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def get_linear_velocity(self):
|
||||
# () -> Vector3
|
||||
return phys.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
def set_linear_velocity(self, vector):
|
||||
# (Vector3) -> None
|
||||
phys.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, vector)
|
||||
|
||||
def freeze_self(self):
|
||||
# () -> None
|
||||
self.set_linear_velocity(ZERO_VECTOR)
|
||||
self.enable_gravity(False)
|
||||
|
||||
def check_gravity(self):
|
||||
# () -> bool
|
||||
return phys.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", self.id)
|
||||
|
||||
def enable_gravity(self, bool_to_set=True):
|
||||
# (bool) -> None
|
||||
phys.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", self.id, bool_to_set)
|
||||
|
||||
def peak_height_reached(self):
|
||||
"""
|
||||
Used for conditional waiting;
|
||||
If peak is reached: sets the value for self.peak_reached to True, saves peak world height,
|
||||
freezes self to keep it from continuing to bounce and possibly interfering with another ball instance
|
||||
"""
|
||||
current_location = self.get_location()
|
||||
if current_location.z < self.peak_height:
|
||||
self.peak_reached = True
|
||||
Report.info("{} has peaked at {:.6} in the world.".format(self.name, self.peak_height))
|
||||
self.freeze_self()
|
||||
return True
|
||||
self.peak_height = current_location.z
|
||||
return False
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
# Ball collides with the ground
|
||||
other_id = args[0]
|
||||
other_name = azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", other_id)
|
||||
if other_name == Terrain.name:
|
||||
self.hit_ground = True
|
||||
Report.info("{} has collided with the terrain.".format(self.name))
|
||||
location = self.get_location()
|
||||
self.ground_height = location.z
|
||||
|
||||
def on_collision_end(self, args):
|
||||
# Ball bounces off the ground
|
||||
other_id = args[0]
|
||||
other_name = azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", other_id)
|
||||
if other_name == Terrain.name:
|
||||
self.bounced = True
|
||||
Report.info("{} has bounced off the terrain.".format(self.name))
|
||||
|
||||
def enable_handler(self):
|
||||
self.handler = phys.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
self.handler.add_callback("OnCollisionEnd", self.on_collision_end)
|
||||
|
||||
def is_close(actual, expected, buffer):
|
||||
return abs(actual - expected) < buffer
|
||||
|
||||
def balls_are_aligned(balls_list):
|
||||
aligned = True
|
||||
|
||||
for ball in balls_list:
|
||||
# check x axis per level setup
|
||||
if ball.name == "Ball_Default":
|
||||
if not is_close(ball.world_location_start.x, X_POSITION_DEFAULT, POSITION_BUFFER):
|
||||
Report.info("Ball_Default is not close enough to expected X position")
|
||||
aligned = False
|
||||
|
||||
elif ball.name == "Ball_Rubber":
|
||||
if not is_close(ball.world_location_start.x, X_POSITION_RUBBER, POSITION_BUFFER):
|
||||
Report.info("Ball_Rubber is not close enough to expected X position")
|
||||
aligned = False
|
||||
|
||||
elif ball.name == "Ball_Concrete":
|
||||
if not is_close(ball.world_location_start.x, X_POSITION_CONCRETE, POSITION_BUFFER):
|
||||
Report.info("Ball_Concrete is not close enough to expected X position")
|
||||
aligned = False
|
||||
|
||||
# check y axis per level setup
|
||||
if not is_close(ball.world_location_start.y, Y_POSITION_VALUE, POSITION_BUFFER):
|
||||
aligned = False
|
||||
Report.info("One or more balls are not close enough to expected Y position")
|
||||
|
||||
return aligned
|
||||
|
||||
def ball_heights_match(balls_list):
|
||||
heights_match = True
|
||||
|
||||
for ball in balls_list:
|
||||
# check ball heights match each other (z axis)
|
||||
if ball.world_location_start.z != balls[0].world_location_start.z:
|
||||
heights_match = False
|
||||
Report.info("The balls are not falling from the same height.")
|
||||
Report.failure(Tests.same_starting_height)
|
||||
|
||||
return heights_match
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4925577_PhysXMaterials_MaterialAssignedToTerrain")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.game_mode_enter)
|
||||
general.idle_wait_frames(NUM_WAIT_FRAMES_ENTITY_LOAD)
|
||||
|
||||
# 3) Find entities
|
||||
Terrain.id = general.find_game_entity("Terrain")
|
||||
Terrain.name = azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", Terrain.id)
|
||||
|
||||
ball_default = Sphere("Ball_Default")
|
||||
ball_rubber = Sphere("Ball_Rubber")
|
||||
ball_concrete = Sphere("Ball_Concrete")
|
||||
balls = (ball_rubber, ball_default, ball_concrete)
|
||||
|
||||
Report.critical_result(Tests.find_terrain, Terrain.id.IsValid())
|
||||
Report.critical_result(Tests.find_ball_default, ball_default.id.IsValid())
|
||||
Report.critical_result(Tests.find_ball_rubber, ball_rubber.id.IsValid())
|
||||
Report.critical_result(Tests.find_ball_concrete, ball_concrete.id.IsValid())
|
||||
|
||||
# 4) Check that gravity is disabled for all the balls initially
|
||||
gravity_disabled_for_all = True
|
||||
for ball in balls:
|
||||
if ball.gravity_enabled is True:
|
||||
gravity_disabled_for_all = False
|
||||
|
||||
Report.result(Tests.all_gravity_disabled, gravity_disabled_for_all)
|
||||
|
||||
# 5) Check that the balls are aligned and all falling from the same height
|
||||
balls_are_aligned = balls_are_aligned(balls)
|
||||
Report.critical_result(Tests.balls_are_aligned, balls_are_aligned)
|
||||
|
||||
same_starting_height = ball_heights_match(balls)
|
||||
Report.critical_result(Tests.same_starting_height, same_starting_height)
|
||||
|
||||
# Steps 6-9 run for each ball
|
||||
for ball in balls:
|
||||
# 6) Assign the tests and enable handlers to their respective spheres
|
||||
ball.assign_tests()
|
||||
ball.enable_handler()
|
||||
|
||||
# 7) Enable gravity on ball entities
|
||||
ball.enable_gravity()
|
||||
|
||||
# 8) Check that the balls collide with the PhysX Terrain
|
||||
helper.wait_for_condition(lambda: ball.bounced, TIMEOUT_IN_SECONDS)
|
||||
Report.result(ball.test_terrain_collide, ball.hit_ground)
|
||||
|
||||
# 9) Wait for the ball to reach its peak height; record height and freeze it
|
||||
helper.wait_for_condition(ball.peak_height_reached, TIMEOUT_IN_SECONDS)
|
||||
Report.result(ball.test_peak_reached, ball.peak_reached)
|
||||
|
||||
# 10) Compare the bounce heights of the balls
|
||||
# The restitution of rubber is greater than the default; the restitution of concrete is less than the default
|
||||
height_order_correct = ball_rubber.peak_height > ball_default.peak_height > ball_concrete.peak_height
|
||||
Report.result(Tests.bounce_height_order_correct, height_order_correct)
|
||||
|
||||
# 11) Exit game mode and close the editor
|
||||
helper.exit_game_mode(Tests.game_mode_exit)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4925577_Materials_MaterialAssignedToTerrain)
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test Case ID : C4925579
|
||||
# Test Case Title : Check that any change (Add/Delete/Modify) made to the material surface in the material library reflects immediately in the PhysX Terrain layers
|
||||
# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4925579
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_0 = ("Test 0) Entered game mode", "Test 0) Failed to enter game mode")
|
||||
find_terrain_0 = ("Test 0) The Terrain was found", "Test 0) The Terrain was not found")
|
||||
find_on_default_box_0 = ("Test 0) Box on default was found", "Test 0) Box on default was not found")
|
||||
find_on_modified_box_0 = ("Test 0) Box on modified was found", "Test 0) Box on modified was not found")
|
||||
boxes_moved_0 = ("Test 0) All boxes moved", "Test 0) Boxes failed to move")
|
||||
boxes_at_rest_0 = ("Test 0) All boxes came to rest", "Test 0) Boxes failed to come to rest")
|
||||
exit_game_mode_0 = ("Test 0) Exited game mode", "Test 0) Failed to exit game mode")
|
||||
on_default_less_than_on_modified = ("Test 0) Box on modified traveled farther than default", "Test 0) Box on modified did not travel farther than default")
|
||||
|
||||
enter_game_mode_1 = ("Test 1) Entered game mode", "Test 1) Failed to enter game mode")
|
||||
find_terrain_1 = ("Test 1) The Terrain was found", "Test 1) The Terrain was not found")
|
||||
find_on_default_box_1 = ("Test 1) Box on default was found", "Test 1) Box on default was not found")
|
||||
find_on_modified_box_1 = ("Test 1) Box on modified was found", "Test 1) Box on modified was not found")
|
||||
boxes_moved_1 = ("Test 1) All boxes moved", "Test 1) Boxes failed to move")
|
||||
boxes_at_rest_1 = ("Test 1) All boxes came to rest", "Test 1) Boxes failed to come to rest")
|
||||
exit_game_mode_1 = ("Test 1) Exited game mode", "Test 1) Failed to exit game mode")
|
||||
on_modified_less_than_previous = ("Test 1) Box on modified traveled less than previous", "Test 1) Box on modified traveled further than previous")
|
||||
|
||||
enter_game_mode_2 = ("Test 2) Entered game mode", "Test 2) Failed to enter game mode")
|
||||
find_terrain_2 = ("Test 2) The Terrain was found", "Test 2) The Terrain was not found")
|
||||
find_on_default_box_2 = ("Test 2) Box on default was found", "Test 2) Box on default was not found")
|
||||
find_on_modified_box_2 = ("Test 2) Box on modified was found", "Test 2) Box on modified was not found")
|
||||
boxes_moved_2 = ("Test 2) All boxes moved", "Test 2) Boxes failed to move")
|
||||
boxes_at_rest_2 = ("Test 2) All boxes came to rest", "Test 2) Boxes failed to come to rest")
|
||||
exit_game_mode_2 = ("Test 2) Exited game mode", "Test 2) Failed to exit game mode")
|
||||
on_default_equals_on_modified = ("Test 2) The boxes on modified and default traveled the same distance", "Test 2) The boxes on modified and default did not travel the same distance")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4925579_Material_AddModifyDeleteOnTerrain():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to verify that any change (Add/Delete/Modify) made to the material surface in the material
|
||||
library reflects immediately in the PhysX Terrain layer component
|
||||
|
||||
Level Description:
|
||||
Two boxes ("on_default" and "on_modified") sit on a terrain.
|
||||
|
||||
The box "on_default" is placed on the terrain where the painted layer is the default physx material.
|
||||
A new material library was created with 1 material, called "Modified", this is painted on the terrain beneath "on_modified"
|
||||
dynamic friction: 0.25
|
||||
static friction: 0.5
|
||||
restitution: 0.5
|
||||
|
||||
Expected behavior:
|
||||
For every iteration this test applies a force impulse in the X direction. The boxes save their traveled distances
|
||||
each iteration, to verify different behavior between each setup.
|
||||
|
||||
First the test verifies the two entities sit upon differing materials, without changing anything. With a lower
|
||||
dynamic friction coefficient, the box 'on_modified' should travel a longer distance than 'on_default'
|
||||
|
||||
Next, the test modifies the dynamic friction value for 'modified' (from 0.25 to 0.75). 'on_modified' should travel a
|
||||
shorter distance than it did in the previous test.
|
||||
|
||||
Finally, we delete the 'modified' material entirely. The 'on_modified' box should then behave as the 'on_default'
|
||||
box, and travel the same distance.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Collect basis values without modifying anything
|
||||
2.1) Enter game mode
|
||||
2.2) Find entities
|
||||
2.3) Push the boxes and wait for them to come to rest
|
||||
2.4) Exit game mode
|
||||
3) Modify the dynamic friction value of 'modified'
|
||||
3.1 - 3.4) <same as above>
|
||||
4) Delete 'on_modified's material
|
||||
4.1 - 4.4) <same as above>
|
||||
5) Close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as lymath
|
||||
|
||||
from Physmaterial_Editor import Physmaterial_Editor
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from AddModifyDelete_Utils import Box
|
||||
|
||||
FORCE_IMPULSE = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
TIMEOUT = 3.0
|
||||
DISTANCE_TOLERANCE = 0.001
|
||||
|
||||
def get_test(test_name):
|
||||
return Tests.__dict__[test_name]
|
||||
|
||||
def run_test(test_number):
|
||||
|
||||
# x.1) Enter game mode
|
||||
helper.enter_game_mode(get_test("enter_game_mode_{}".format(test_number)))
|
||||
|
||||
# x.2) Find entities
|
||||
Report.result(get_test("find_terrain_{}".format(test_number)), general.find_game_entity("terrain").IsValid())
|
||||
Report.result(get_test("find_on_default_box_{}".format(test_number)), default_box.find())
|
||||
Report.result(get_test("find_on_modified_box_{}".format(test_number)), modified_box.find())
|
||||
|
||||
# x.3) Push the boxes and wait for them to come to rest
|
||||
default_box.push(FORCE_IMPULSE)
|
||||
modified_box.push(FORCE_IMPULSE)
|
||||
|
||||
def boxes_are_moving():
|
||||
return not default_box.is_stationary() and not modified_box.is_stationary()
|
||||
|
||||
def boxes_are_stationary():
|
||||
return default_box.is_stationary() and modified_box.is_stationary()
|
||||
|
||||
Report.result(
|
||||
get_test("boxes_moved_{}".format(test_number)), helper.wait_for_condition(boxes_are_moving, TIMEOUT),
|
||||
)
|
||||
Report.result(
|
||||
get_test("boxes_at_rest_{}".format(test_number)), helper.wait_for_condition(boxes_are_stationary, TIMEOUT),
|
||||
)
|
||||
|
||||
default_box.distances.append(default_box.position.GetDistance(default_box.start_position))
|
||||
modified_box.distances.append(modified_box.position.GetDistance(modified_box.start_position))
|
||||
|
||||
# x.4) Exit game mode
|
||||
helper.exit_game_mode(get_test("exit_game_mode_{}".format(test_number)))
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4925579_Material_AddModifyDeleteOnTerrain")
|
||||
|
||||
# Setup persisting entities
|
||||
default_box = Box("on_default")
|
||||
modified_box = Box("on_modified")
|
||||
|
||||
# 2) Collect basis values without modifying anything
|
||||
run_test(0)
|
||||
# While sitting on a terrain with friction of 0.25, 'on_modified' should travel farther than 'default'
|
||||
Report.result(Tests.on_default_less_than_on_modified, default_box.distances[0] < modified_box.distances[0])
|
||||
|
||||
# 3) Modify the dynamic friction value of 'modified'
|
||||
material_editor = Physmaterial_Editor("c4925579_material_addmodifydeleteonterrain.physmaterial")
|
||||
material_editor.modify_material("Modified", "DynamicFriction", 0.75)
|
||||
material_editor.save_changes()
|
||||
run_test(1)
|
||||
# With greater friction, 'on_modified' should now travel a shorter distance than it did in the previous test.
|
||||
Report.result(Tests.on_modified_less_than_previous, modified_box.distances[0] > modified_box.distances[1])
|
||||
|
||||
# 4) Delete 'modified's material
|
||||
material_editor.delete_material("Modified")
|
||||
material_editor.save_changes()
|
||||
run_test(2)
|
||||
Report.result(
|
||||
Tests.on_default_equals_on_modified,
|
||||
lymath.Math_IsClose(default_box.distances[2], modified_box.distances[2], DISTANCE_TOLERANCE),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4925579_Material_AddModifyDeleteOnTerrain)
|
||||
@@ -0,0 +1,206 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test Case ID : C4925580
|
||||
# Test Case Title : Verify that Material can be assigned to Ragdoll Bones and they behave as per their material
|
||||
# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4925580
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
terrain_found_valid = ("PhysX Terrain found and validated", "PhysX Terrain not found and validated")
|
||||
concrete_ragdoll_found_valid = ("Concrete Ragdoll found and validated", "Concrete Ragdoll not found and validated")
|
||||
rubber_ragdoll_found_valid = ("Rubber Ragdoll found and validated", "Rubber Ragdoll not found and validated")
|
||||
concrete_ragdoll_above_terrain = ("Concrete Ragdoll is above terrain", "Concrete Ragdoll is not above terrain")
|
||||
rubber_ragdoll_above_terrain = ("Rubber Ragdoll is above terrain", "Rubber Ragdoll is not above terrain")
|
||||
terrain_collision_detected = ("Collision was detected on a ragdoll with terrain", "Collision detection timed out")
|
||||
concrete_ragdoll_contacted_terrain = ("Concrete Ragdoll contacted terrain", "Concrete Ragdoll did not contact terrain")
|
||||
rubber_ragdoll_contacted_terrain = ("Rubber Ragdoll contacted terrain", "Rubber Ragdoll did not contact terrain")
|
||||
rubber_ragdoll_bounced_higher = ("Rubber Ragdoll bounced higher than Concrete Ragdoll", "Rubber Ragdoll did not bounce higher than Concrete Ragdoll")
|
||||
concrete_ragdoll_bounced_as_expected = ("Concrete ragdoll bounced to expected height", "Concrete ragdoll did not bounce to expected height")
|
||||
rubber_ragdoll_bounced_as_expected = ("Rubber ragdoll bounced to expected height", "Rubber ragdoll did not bounce to expected height")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4925580_Material_RagdollBonesMaterial():
|
||||
"""
|
||||
Summary:
|
||||
This script runs an automated test to verify that assigning material to the skeleton of an actor entity with PhysX
|
||||
ragdoll will cause the entity to behave according to the nature of the material.
|
||||
|
||||
Level Description:
|
||||
Two ragdoll entities (entity: Concrete Ragdoll) and (entity: Rubber Ragdoll) are above a PhysX terrain (entity:
|
||||
PhysX Terrain). Each ragdoll has an actor, an animation graph, and a PhysX ragdoll component. Gravity is enabled for
|
||||
each joint which is present on the ragdolls. The ragdolls are identical except for their textures, skeleton
|
||||
materials, and x-positions. Concrete Ragdoll's texture is blue, while Rubber Ragdoll's texture is red. Concrete
|
||||
Ragdoll's skeleton material is concrete, while Rubber Ragdoll's skeleton material is rubber.
|
||||
|
||||
Expected behavior:
|
||||
The ragdolls will fall and hit the terrain at the same time. The rubber ragdoll will bounce higher than the concrete
|
||||
ragdoll.
|
||||
|
||||
Test Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Retrieve and validate entities
|
||||
3) Check that each ragdoll is above the terrain
|
||||
4) Wait for the initial collision between a ragdoll and the terrain or timeout
|
||||
5) Check for the maximum bounce height of each ragdoll for a given period of time
|
||||
6) Verify that the rubber ragdoll bounced higher than the concrete ragdoll
|
||||
7) Verify that each ragdoll bounced approximately to its expected maximum height
|
||||
8) Exit game mode and close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.components
|
||||
import azlmbr.physics
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
# Constants
|
||||
TIME_OUT_SECONDS = 3.0
|
||||
TERRAIN_START_Z = 32.0
|
||||
CONCRETE_EXPECTED_MAX_BOUNCE_HEIGHT = 0.039
|
||||
RUBBER_EXPECTED_MAX_BOUNCE_HEIGHT = 1.2
|
||||
TOLERANCE = 0.5
|
||||
|
||||
class Entity:
|
||||
def __init__(self, name, found_valid_test):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.found_valid_test = found_valid_test
|
||||
|
||||
class Ragdoll(Entity):
|
||||
def __init__(self, name, found_valid_test, target_terrain, above_terrain_test, contacted_terrain_test):
|
||||
Entity.__init__(self, name, found_valid_test)
|
||||
self.target_terrain = target_terrain
|
||||
self.above_terrain_test = above_terrain_test
|
||||
self.contacted_terrain_test = contacted_terrain_test
|
||||
self.contacted_terrain = False
|
||||
self.max_bounce_height = 0
|
||||
self.reached_max_bounce = False
|
||||
|
||||
# Set up collision notification handler
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
def get_z_position(self):
|
||||
z_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", self.id)
|
||||
return z_position
|
||||
|
||||
# Set up collision detection with the terrain
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.target_terrain.id):
|
||||
Report.info("{} collision began with {}".format(self.name, self.target_terrain.name))
|
||||
if not self.contacted_terrain:
|
||||
self.hit_terrain_z = self.get_z_position()
|
||||
self.contacted_terrain = True
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4925580_Material_RagdollBonesMaterial")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve and validate entities
|
||||
terrain = Entity("PhysX Terrain", Tests.terrain_found_valid)
|
||||
Report.critical_result(terrain.found_valid_test, terrain.id.IsValid())
|
||||
|
||||
concrete_ragdoll = Ragdoll(
|
||||
"Concrete Ragdoll",
|
||||
Tests.concrete_ragdoll_found_valid,
|
||||
terrain,
|
||||
Tests.concrete_ragdoll_above_terrain,
|
||||
Tests.concrete_ragdoll_contacted_terrain,
|
||||
)
|
||||
|
||||
rubber_ragdoll = Ragdoll(
|
||||
"Rubber Ragdoll",
|
||||
Tests.rubber_ragdoll_found_valid,
|
||||
terrain,
|
||||
Tests.rubber_ragdoll_above_terrain,
|
||||
Tests.rubber_ragdoll_contacted_terrain,
|
||||
)
|
||||
|
||||
ragdolls = [concrete_ragdoll, rubber_ragdoll]
|
||||
for ragdoll in ragdolls:
|
||||
Report.critical_result(ragdoll.found_valid_test, ragdoll.id.IsValid())
|
||||
|
||||
# 3) Check that each ragdoll is above the terrain
|
||||
Report.critical_result(ragdoll.above_terrain_test, ragdoll.get_z_position() > TERRAIN_START_Z)
|
||||
|
||||
# 4) Wait for the initial collision between the ragdolls and the terrain or timeout
|
||||
terrain_collision_detected = helper.wait_for_condition(
|
||||
lambda: concrete_ragdoll.contacted_terrain and rubber_ragdoll.contacted_terrain, TIME_OUT_SECONDS
|
||||
)
|
||||
Report.critical_result(Tests.terrain_collision_detected, terrain_collision_detected)
|
||||
for ragdoll in ragdolls:
|
||||
Report.result(ragdoll.contacted_terrain_test, ragdoll.contacted_terrain)
|
||||
|
||||
# 5) Check for the maximum bounce height of each ragdoll for a given period of time
|
||||
def check_for_max_bounce_heights(ragdolls):
|
||||
for ragdoll in ragdolls:
|
||||
if ragdoll.contacted_terrain:
|
||||
bounce_height = ragdoll.get_z_position() - ragdoll.hit_terrain_z
|
||||
if bounce_height >= ragdoll.max_bounce_height:
|
||||
ragdoll.max_bounce_height = bounce_height
|
||||
elif ragdoll.max_bounce_height > 0.0:
|
||||
ragdoll.reached_max_bounce = True
|
||||
return concrete_ragdoll.reached_max_bounce and rubber_ragdoll.reached_max_bounce
|
||||
|
||||
helper.wait_for_condition(lambda: check_for_max_bounce_heights(ragdolls), TIME_OUT_SECONDS)
|
||||
for ragdoll in ragdolls:
|
||||
Report.info("{}'s maximum bounce height: {}".format(ragdoll.name, ragdoll.max_bounce_height))
|
||||
|
||||
# 6) Verify that the rubber ragdoll bounced higher than the concrete ragdoll
|
||||
Report.result(
|
||||
Tests.rubber_ragdoll_bounced_higher, rubber_ragdoll.max_bounce_height > concrete_ragdoll.max_bounce_height
|
||||
)
|
||||
|
||||
# 7) Verify that each ragdoll bounced approximately to its expected maximum height
|
||||
Report.result(
|
||||
Tests.concrete_ragdoll_bounced_as_expected,
|
||||
abs(concrete_ragdoll.max_bounce_height - CONCRETE_EXPECTED_MAX_BOUNCE_HEIGHT) < TOLERANCE,
|
||||
)
|
||||
Report.result(
|
||||
Tests.rubber_ragdoll_bounced_as_expected,
|
||||
abs(rubber_ragdoll.max_bounce_height - RUBBER_EXPECTED_MAX_BOUNCE_HEIGHT) < TOLERANCE,
|
||||
)
|
||||
|
||||
# 8) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4925580_Material_RagdollBonesMaterial)
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test Case ID : C4925582
|
||||
# Test Case Title : Check that any change (Add/Delete/Modify) made to the material surface in the material library reflects immediately in the ragdoll bones
|
||||
# Test Case URL : https://testrail.agscollab.com/index.php?/cases/view/4925582
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_0 = ("Test 0) Entered game mode", "Test 0) Failed to enter game mode")
|
||||
find_terrain_0 = ("Test 0) The Terrain was found", "Test 0) The Terrain was not found")
|
||||
find_default_ragdoll_0 = ("Test 0) Default ragdoll was found", "Test 0) Default ragdoll was not found")
|
||||
find_modified_ragdoll_0 = ("Test 0) Modified ragdoll was found", "Test 0) Modified ragdoll was not found")
|
||||
default_ragdoll_bounced_0 = ("Test 0) Default ragdoll bounced", "Test 0) Default ragdoll did not bounce")
|
||||
modified_ragdoll_bounced_0 = ("Test 0) Modified ragdoll bounced", "Test 0) Modified ragdoll did not bounce")
|
||||
exit_game_mode_0 = ("Test 0) Exited game mode", "Test 0) Failed to exit game mode")
|
||||
modified_less_than_default = ("Test 0) Modified ragdoll's bounce height was shorter than default", "Test 0) Modified ragdoll's bounce height was greater than default")
|
||||
|
||||
enter_game_mode_1 = ("Test 1) Entered game mode", "Test 1) Failed to enter game mode")
|
||||
find_terrain_1 = ("Test 1) The Terrain was found", "Test 1) The Terrain was not found")
|
||||
find_default_ragdoll_1 = ("Test 1) Default ragdoll was found", "Test 1) Default ragdoll was not found")
|
||||
find_modified_ragdoll_1 = ("Test 1) Modified ragdoll was found", "Test 1) Modified ragdoll was not found")
|
||||
default_ragdoll_bounced_1 = ("Test 1) Default ragdoll bounced", "Test 1) Default ragdoll did not bounce")
|
||||
modified_ragdoll_bounced_1 = ("Test 1) Modified ragdoll bounced", "Test 1) Modified ragdoll did not bounce")
|
||||
exit_game_mode_1 = ("Test 1) Exited game mode", "Test 1) Failed to exit game mode")
|
||||
modified_greater_than_default = ("Test 1) Modified ragdoll's bounce height was higher than default's", "Test 1) Modified ragdoll's bounce height was not higher than default's")
|
||||
|
||||
enter_game_mode_2 = ("Test 2) Entered game mode", "Test 2) Failed to enter game mode")
|
||||
find_terrain_2 = ("Test 2) The Terrain was found", "Test 2) The Terrain was not found")
|
||||
find_default_ragdoll_2 = ("Test 2) Default ragdoll was found", "Test 2) Default ragdoll was not found")
|
||||
find_modified_ragdoll_2 = ("Test 2) Modified ragdoll was found", "Test 2) Modified ragdoll was not found")
|
||||
default_ragdoll_bounced_2 = ("Test 2) Default ragdoll bounced", "Test 2) Default ragdoll did not bounce")
|
||||
modified_ragdoll_bounced_2 = ("Test 2) Modified ragdoll bounced", "Test 2) Modified ragdoll did not bounce")
|
||||
exit_game_mode_2 = ("Test 2) Exited game mode", "Test 2) Failed to exit game mode")
|
||||
default_equals_modified = ("Test 2) Modified and default ragdoll's bounce height were equal", "Test 2) Modified and default ragdoll's bounce height were not equal")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4925582_Material_AddModifyDeleteOnRagdollBones():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to verify that any change (Add/Delete/Modify) made to the material surface in the material
|
||||
library reflects immediately in the ragdoll bones
|
||||
|
||||
Level Description:
|
||||
Two ragdolls ("default_ragdoll" and "modified_ragdoll") sit above a terrain. The ragdolls are identical, save for
|
||||
their physX material.
|
||||
|
||||
The ragdoll "default_ragdoll" is assigned the default physx material.
|
||||
A new material library was created with 1 material, called "Modified", this is assigned to "modified_ragdoll":
|
||||
dynamic friction: 0.5
|
||||
static friction: 0.5
|
||||
restitution: 0.25
|
||||
|
||||
Expected behavior:
|
||||
For every iteration this test measures the bounce height of each entity. The ragdolls save their traveled distances
|
||||
each iteration, to verify different behavior between each setup.
|
||||
|
||||
First the test verifies the two entities are assigned differing materials, without changing anything. With a lower
|
||||
restitution value, the 'modified' should bounce much lower than 'default'
|
||||
|
||||
Next, the test modifies the restitution value for 'modified' (from 0.25 to 0.75). 'modified' should bounce height
|
||||
than it did in the previous test, and greater than default.
|
||||
|
||||
Finally, we delete the 'modified' material entirely. 'modified_ragdoll' should then behave the same as
|
||||
'default_ragdoll' box, and bounce the same distance.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Collect basis values without modifying anything
|
||||
2.1) Enter game mode
|
||||
2.2) Find entities
|
||||
2.3) Wait for entities to bounce
|
||||
2.4) Exit game mode
|
||||
3) Modify the restitution value of 'modified'
|
||||
3.1 - 3.4) <same as above>
|
||||
4) Delete 'modified_ragdoll's material
|
||||
4.1 - 4.4) <same as above>
|
||||
5) Close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.components
|
||||
import azlmbr.physics
|
||||
import azlmbr.math as lymath
|
||||
|
||||
from Physmaterial_Editor import Physmaterial_Editor
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
TIMEOUT = 3.0
|
||||
BOUNCE_TOLERANCE = 0.05
|
||||
|
||||
class Ragdoll:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.bounces = []
|
||||
|
||||
def find_and_reset(self):
|
||||
self.hit_terrain_position = None
|
||||
self.hit_terrain = False
|
||||
self.max_bounce = 0.0
|
||||
self.reached_max_bounce = False
|
||||
self.id = general.find_game_entity(self.name)
|
||||
return self.id.IsValid()
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
def get_test(test_name):
|
||||
return Tests.__dict__[test_name]
|
||||
|
||||
def run_test(test_number):
|
||||
# x.1) Enter game mode
|
||||
helper.enter_game_mode(get_test("enter_game_mode_{}".format(test_number)))
|
||||
|
||||
# x.2) Find entities
|
||||
terrain_id = general.find_game_entity("terrain")
|
||||
Report.result(get_test("find_terrain_{}".format(test_number)), terrain_id.IsValid())
|
||||
Report.result(get_test("find_default_ragdoll_{}".format(test_number)), default_ragdoll.find_and_reset())
|
||||
Report.result(get_test("find_modified_ragdoll_{}".format(test_number)), modified_ragdoll.find_and_reset())
|
||||
|
||||
def on_collision_enter(args):
|
||||
entering = args[0]
|
||||
for ragdoll in ragdolls:
|
||||
if ragdoll.id.Equal(entering):
|
||||
if not ragdoll.hit_terrain:
|
||||
ragdoll.hit_terrain_position = ragdoll.position
|
||||
ragdoll.hit_terrain = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(terrain_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_enter)
|
||||
|
||||
def wait_for_bounce():
|
||||
for ragdoll in ragdolls:
|
||||
if ragdoll.hit_terrain:
|
||||
current_bounce_height = ragdoll.position.z - ragdoll.hit_terrain_position.z
|
||||
if current_bounce_height >= ragdoll.max_bounce:
|
||||
ragdoll.max_bounce = current_bounce_height
|
||||
elif ragdoll.max_bounce > 0.0:
|
||||
ragdoll.reached_max_bounce = True
|
||||
return default_ragdoll.reached_max_bounce and modified_ragdoll.reached_max_bounce
|
||||
|
||||
# x.3) Wait for entities to bounce
|
||||
helper.wait_for_condition(wait_for_bounce, TIMEOUT)
|
||||
Report.result(get_test("default_ragdoll_bounced_{}".format(test_number)), default_ragdoll.reached_max_bounce)
|
||||
Report.result(get_test("modified_ragdoll_bounced_{}".format(test_number)), modified_ragdoll.reached_max_bounce)
|
||||
|
||||
for ragdoll in ragdolls:
|
||||
ragdoll.bounces.append(ragdoll.max_bounce)
|
||||
|
||||
# x.4) Exit game mode
|
||||
helper.exit_game_mode(get_test("exit_game_mode_{}".format(test_number)))
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4925582_Material_AddModifyDeleteOnRagdollBones")
|
||||
|
||||
# Setup persisting entities
|
||||
default_ragdoll = Ragdoll("default")
|
||||
modified_ragdoll = Ragdoll("modified")
|
||||
ragdolls = [default_ragdoll, modified_ragdoll]
|
||||
|
||||
# 2) Collect basis values without modifying anything
|
||||
run_test(0)
|
||||
Report.result(Tests.modified_less_than_default, default_ragdoll.bounces[0] > modified_ragdoll.bounces[0])
|
||||
|
||||
# 3) Modify the restitution value of 'modified'
|
||||
material_editor = Physmaterial_Editor("ragdollbones.physmaterial")
|
||||
material_editor.modify_material("Modified", "Restitution", 0.75)
|
||||
material_editor.save_changes()
|
||||
run_test(1)
|
||||
Report.result(Tests.modified_greater_than_default, default_ragdoll.bounces[0] < modified_ragdoll.bounces[1])
|
||||
|
||||
# 4) Delete 'modified's material
|
||||
material_editor.delete_material("Modified")
|
||||
material_editor.save_changes()
|
||||
run_test(2)
|
||||
Report.result(
|
||||
Tests.default_equals_modified,
|
||||
lymath.Math_IsClose(default_ragdoll.bounces[2], modified_ragdoll.bounces[2], BOUNCE_TOLERANCE),
|
||||
)
|
||||
|
||||
Report.info("Default hit terrain: " + str(default_ragdoll.hit_terrain))
|
||||
Report.info("Modified hit terrain: " + str(modified_ragdoll.hit_terrain))
|
||||
|
||||
Report.info("Default max bounce: " + str(default_ragdoll.reached_max_bounce))
|
||||
Report.info("Modified max bouce: " + str(modified_ragdoll.reached_max_bounce))
|
||||
|
||||
Report.info("Default max bounce: " + str(default_ragdoll.bounces[0]))
|
||||
Report.info("Modified max bouce: " + str(modified_ragdoll.bounces[0]))
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4925582_Material_AddModifyDeleteOnRagdollBones)
|
||||
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976194
|
||||
# Test Case Title : Verify that you can add PhysX Rigid Bodies Physics component to an Entity without any warning or Error.
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976194
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_cube = ("Entity Cube found", "Cube not found")
|
||||
linear_damp = ("Cube Linear Damping equal", "Cube Linear Damping not equal")
|
||||
angular_damp = ("Cube Angular Damping equal", "Cube Angular Damping not equal")
|
||||
mass = ("Cube Mass equal", "Cube Mass not equal")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976194_RigidBody_PhysXComponentIsValid():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Open a Project that already has a PhysxRigidBody component in it and verify PhysxRigidBody is working in Game Mode
|
||||
|
||||
Level Description:
|
||||
PhysxRigidBody (entity) - PhysxRigidBody entity is created in the level
|
||||
|
||||
Expected Behavior:
|
||||
The PhysxRigidBody entity should be working in the game mode
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entity
|
||||
4) Validate Linear damping, Angular Damping and Mass of PhysxRigidBody
|
||||
5) Set a new values for Linear damping, Angular Damping and Mass of PhysxRigidBody and validate it
|
||||
6) Exit game mode
|
||||
7) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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, sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
DEFAULT_LINEAR_DAMPING = 0.05
|
||||
DEFAULT_ANGULAR_DAMPING = 0.15
|
||||
DEFAULT_MASS = 1.0
|
||||
NEW_LINEAR_DAMPING = 1.05
|
||||
NEW_ANGULAR_DAMPING = 1.15
|
||||
NEW_MASS = 2.0
|
||||
CLOSE_ENOUGH = 0.001
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4976194_RigidBody_PhysXComponentIsValid")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate Entity
|
||||
cube_id = general.find_game_entity("CubeRigidBody")
|
||||
Report.critical_result(Tests.find_cube, cube_id.IsValid())
|
||||
|
||||
# 4) Validate Linear damping, Angular Damping and Mass of PhysxRigidBody
|
||||
cube_linear_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearDamping", cube_id)
|
||||
Report.result(Tests.linear_damp, abs(cube_linear_damping - DEFAULT_LINEAR_DAMPING) < CLOSE_ENOUGH)
|
||||
cube_angular_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularDamping", cube_id)
|
||||
Report.result(Tests.angular_damp, abs(cube_angular_damping - DEFAULT_ANGULAR_DAMPING) < CLOSE_ENOUGH)
|
||||
cube_mass = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetMass", cube_id)
|
||||
Report.result(Tests.mass, abs(cube_mass - DEFAULT_MASS) < CLOSE_ENOUGH)
|
||||
|
||||
# 5) Set a new values for Linear damping, Angular Damping and Mass of PhysxRigidBody and validate it
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearDamping", cube_id, NEW_LINEAR_DAMPING)
|
||||
cube_linear_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearDamping", cube_id)
|
||||
Report.result(Tests.linear_damp, abs(cube_linear_damping - NEW_LINEAR_DAMPING) < CLOSE_ENOUGH)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetAngularDamping", cube_id, NEW_ANGULAR_DAMPING)
|
||||
cube_angular_damping = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularDamping", cube_id)
|
||||
Report.result(Tests.angular_damp, abs(cube_angular_damping - NEW_ANGULAR_DAMPING) < CLOSE_ENOUGH)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetMass", cube_id, NEW_MASS)
|
||||
cube_mass = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetMass", cube_id)
|
||||
Report.result(Tests.mass, abs(cube_mass - NEW_MASS) < CLOSE_ENOUGH)
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976194_RigidBody_PhysXComponentIsValid)
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976195
|
||||
# Test Case Title : Verify that when you assign an Initial Linear Velocity to an object,
|
||||
# ... it moves with that linear velocity when we switch to game mode.
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976195
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
entities_found = ("Entities found", "Entities not found")
|
||||
entities_pos = ("Entities positions are valid", "Entities positions are not valid")
|
||||
sphere_moved = ("Sphere moved correctly", "Sphere did not move correctly")
|
||||
sphere_velocity = ("The magnitude of velocity is close to 5", "The magnitude of velocity is not close to 5")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
def C4976195_RigidBodies_InitialLinearVelocity():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure intial linear velocity of 5.0 on X axis is exerted on a rigid body object.
|
||||
|
||||
Level Description:
|
||||
A sphere (entity: Sphere) positioned above terrain which is assigned with
|
||||
an initial linear velocity of 5.0 in x direction. The Sphere has gravity disabled.
|
||||
A trigger cube positioned on the same Y and Z but different X of the sphere.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the sphere should move on x direction with 5 m/s speed.
|
||||
The sphere is supposed to touch the trigger cube as it moves.
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level
|
||||
2) Enters game mode
|
||||
3) Retrieves test entities (Sphere and Trigger Cube)
|
||||
4) Ensures that the sphere and the cube are located
|
||||
5) Captures the velocity of sphere
|
||||
5.1) Sets up trigger cube
|
||||
6) Waits for the shpere to move and touch the trigger cube
|
||||
7) Captures the sphere new location
|
||||
8) Exits game mode
|
||||
9) Closes the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "C4976195_RigidBodies_InitialLinearVelocity")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
trigger_cube_id = general.find_game_entity("TriggerCube")
|
||||
valid_entity_ids = (sphere_id.IsValid() and trigger_cube_id.IsValid())
|
||||
Report.critical_result(Tests.entities_found, valid_entity_ids)
|
||||
|
||||
# 4) Log sphere and cube initial position
|
||||
init_sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
trigger_cube_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", trigger_cube_id)
|
||||
sphere_pos_found = ((init_sphere_pos is not None) and (not init_sphere_pos.IsZero()))
|
||||
trigger_cube_pos_found = ((trigger_cube_pos is not None) and (not trigger_cube_pos.IsZero()))
|
||||
pos_found = sphere_pos_found and trigger_cube_pos_found
|
||||
Report.critical_result(Tests.entities_pos, pos_found)
|
||||
Report.info_vector3(init_sphere_pos, "Sphere initial position:")
|
||||
|
||||
# 5) Log Sphere's velocity
|
||||
sphere_linear_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere_id)
|
||||
Report.info("Sphere linear velocity {}".format(sphere_linear_velocity.ToString()))
|
||||
sphere_linear_velocity_magnitude = sphere_linear_velocity.GetLength()
|
||||
Report.info("Magnitude = {}".format(sphere_linear_velocity_magnitude))
|
||||
# We found no bus to retrieve physics rigid body initial linear velocity
|
||||
# So we are hardcoding 5 as its initial linear velocity
|
||||
SPHERE_INITIAL_VELOCITY = 5
|
||||
outcome = abs(sphere_linear_velocity_magnitude - SPHERE_INITIAL_VELOCITY) < 0.05
|
||||
Report.result(Tests.sphere_velocity, outcome)
|
||||
|
||||
# 5.1) Set up trigger cube
|
||||
class SphereTouchedTrigger:
|
||||
value = False
|
||||
|
||||
def set_touched_value(args):
|
||||
# Called when the sphere touches the trigger cube
|
||||
SphereTouchedTrigger.value = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(trigger_cube_id)
|
||||
handler.add_callback("OnTriggerEnter", set_touched_value)
|
||||
|
||||
# 6) Wait a maximum of 3 seconds for the sphere to touch the trigger
|
||||
helper.wait_for_condition(lambda: SphereTouchedTrigger.value, 3.0)
|
||||
|
||||
# 7) Log Sphere's new location and validates sphere's movement
|
||||
new_sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
# The sphere is supposed to move on X axis, but not on Y nor Z
|
||||
valid_movement = (new_sphere_pos.x > init_sphere_pos.x and
|
||||
abs(new_sphere_pos.y - init_sphere_pos.y) < 0.001 and
|
||||
abs(new_sphere_pos.z - init_sphere_pos.z) < 0.001)
|
||||
Report.result(Tests.sphere_moved, valid_movement)
|
||||
Report.info_vector3(new_sphere_pos, "Sphere new position:")
|
||||
|
||||
# 8) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976195_RigidBodies_InitialLinearVelocity)
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976197
|
||||
# Test Case Title : Verify that when you assign an Initial Angular Velocity to an object,
|
||||
# it moves with that Angular velocity when we switch to game mode
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976197
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
gravity_disabled = ("Gravity is disabled", "Gravity is not disabled")
|
||||
cube_found = ("Cube was found", "Cube was not found")
|
||||
trigger_found = ("Trigger was found", "Trigger was not found")
|
||||
cube_pos = ("Cube position is valid", "Cube position is not valid")
|
||||
trigger_pos = ("Trigger position is valid", "Trigger position is not valid")
|
||||
cube_init_rotation = ("Cube rotation is valid", "Cube rotation is not valid")
|
||||
cube_touched_trigger = ("Cube touched Trigger", "Cube did not touch Trigger")
|
||||
cube_rotated_on_x = ("Cube rotated on X axis", "Cube did not rotate on X axis")
|
||||
cube_not_rotated_on_y = ("Cube did not rotate on Y axis", "Cube rotated on Y axis")
|
||||
cube_not_rotated_on_z = ("Cube did not rotate on Z axis", "Cube rotated on Z axis")
|
||||
cube_velocity = ("The velocity is close to expected", "The velocity is not close to expected")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976197_RigidBodies_InitialAngularVelocity():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure initial angular velocity of 5.0 rad/sec on X axis is exerted on a rigid body object
|
||||
|
||||
Level Description:
|
||||
A cube positioned above terrain, with PhysX Shape Collider component with Box shape (dimensions: x=1, y=1, z=3),
|
||||
and with PhysX Rigid Body component, gravity disabled, initial angular velocity: (x=5, y=0, z=0) rad/s and
|
||||
angular damping set to 0.0
|
||||
A trigger with PhysX Shape Collider component with Box shape (dimensions: x=1, y=1, z=1), trigger enabled,
|
||||
positioned above terrain close to the Cube but not touching.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, the cube should rotate on x axis with 5 radian per second speed.
|
||||
The cube is supposed to touch the trigger as it rotates.
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level
|
||||
2) Enters game mode
|
||||
3) Retrieves test entities (Cube and Trigger)
|
||||
4) Checks if gravity is disabled
|
||||
5) Checks the Cube and Trigger locations
|
||||
6) Checks the Cube initial rotation
|
||||
7) Captures the angular velocity of cube
|
||||
8) Sets up Trigger
|
||||
9) Waits for the Cube to rotate and touch the Trigger
|
||||
10) Captures the Cube new rotation and reports results
|
||||
11) Exits game mode
|
||||
12) Closes the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 math
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
class Cube:
|
||||
id = None
|
||||
position = None
|
||||
init_rotation = None
|
||||
rotation = None
|
||||
angular_velocity = None
|
||||
touched_trigger = False
|
||||
# We found no bus to retrieve physics rigid body initial angular velocity
|
||||
# So we are hardcoding 5 as its initial angular velocity
|
||||
INITIAL_ANGULAR_VELOCITY = 5 # radian per second on X axis
|
||||
|
||||
class Trigger:
|
||||
id = None
|
||||
position = None
|
||||
|
||||
# Constants
|
||||
INITIAL_ANGULAR_VELOCITY_TOLERANCE = 0.05
|
||||
TIME_OUT = 2.0
|
||||
ROTATION_TOLERANCE = 0.001
|
||||
|
||||
def is_angle_close(x, y):
|
||||
r = (math.sin(x) - math.sin(y)) * (math.sin(x) - math.sin(y)) + (math.cos(x) - math.cos(y)) * (
|
||||
math.cos(x) - math.cos(y)
|
||||
)
|
||||
diff = math.acos((2.0 - r) / 2.0)
|
||||
return abs(diff) <= ROTATION_TOLERANCE
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Load the level
|
||||
helper.open_level("Physics", "C4976197_RigidBodies_InitialAngularVelocity")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
Cube.id = general.find_game_entity("Cube")
|
||||
Trigger.id = general.find_game_entity("Trigger")
|
||||
Report.critical_result(Tests.cube_found, Cube.id.IsValid())
|
||||
Report.critical_result(Tests.trigger_found, Trigger.id.IsValid())
|
||||
|
||||
# 4) Check gravity is disabled
|
||||
gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", Cube.id)
|
||||
Report.result(Tests.gravity_disabled, not gravity_enabled)
|
||||
|
||||
# 5) Log Cube and Trigger positions
|
||||
Cube.position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Cube.id)
|
||||
valid_cube_pos = (Cube.position is not None) and (not Cube.position.IsZero())
|
||||
Report.info_vector3(Cube.position, "Cube initial position:")
|
||||
Report.critical_result(Tests.cube_pos, valid_cube_pos)
|
||||
|
||||
Trigger.position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", Trigger.id)
|
||||
valid_trigger_pos = (Trigger.position is not None) and (not Trigger.position.IsZero())
|
||||
Report.info_vector3(Trigger.position, "Trigger initial position:")
|
||||
Report.critical_result(Tests.trigger_pos, valid_trigger_pos)
|
||||
|
||||
# 6) Log Cube initial rotation
|
||||
Cube.init_rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", Cube.id)
|
||||
Report.info_vector3(Cube.init_rotation, "Cube initial rotation:")
|
||||
Report.critical_result(Tests.cube_init_rotation, (Cube.init_rotation is not None))
|
||||
|
||||
# 7) Log Cube angular velocity
|
||||
Cube.angular_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetAngularVelocity", Cube.id)
|
||||
Report.info_vector3(Cube.angular_velocity, "Cube angular velocity:")
|
||||
valid_velocity = abs(Cube.angular_velocity.x - Cube.INITIAL_ANGULAR_VELOCITY) < INITIAL_ANGULAR_VELOCITY_TOLERANCE
|
||||
Report.result(Tests.cube_velocity, valid_velocity)
|
||||
|
||||
# 8) Set up Trigger
|
||||
def set_touched_value(args):
|
||||
entering_entity_id = args[0]
|
||||
if entering_entity_id.Equal(Cube.id):
|
||||
Report.info("Cube touched the Trigger")
|
||||
Cube.touched_trigger = True
|
||||
|
||||
handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
handler.connect(Trigger.id)
|
||||
handler.add_callback("OnTriggerEnter", set_touched_value)
|
||||
|
||||
# 9) Wait a maximum of 2 seconds for the sphere to touch the trigger
|
||||
helper.wait_for_condition(lambda: Cube.touched_trigger, TIME_OUT)
|
||||
Report.result(Tests.cube_touched_trigger, Cube.touched_trigger)
|
||||
|
||||
# 10) Log Cube's new rotation and validates it
|
||||
Cube.rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", Cube.id)
|
||||
Report.info_vector3(Cube.rotation, "Cube new rotation:")
|
||||
Report.result(Tests.cube_rotated_on_x, not is_angle_close(Cube.rotation.x, Cube.init_rotation.x))
|
||||
Report.result(Tests.cube_not_rotated_on_y, is_angle_close(Cube.rotation.y, Cube.init_rotation.y))
|
||||
Report.result(Tests.cube_not_rotated_on_z, is_angle_close(Cube.rotation.z, Cube.init_rotation.z))
|
||||
|
||||
# 11) Exit Game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976197_RigidBodies_InitialAngularVelocity)
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4976199
|
||||
# Test Case Title : Verify that with higher linear damping, the object in motion comes to rest faster
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976199
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_low = ("Entered game mode low", "Failed to enter game mode low")
|
||||
enter_game_mode_medium = ("Entered game mode medium", "Failed to enter game medium")
|
||||
enter_game_mode_high = ("Entered game mode high", "Failed to enter game mode high")
|
||||
find_sphere_low = ("Find sphere low", "Failed to find sphere low")
|
||||
find_sphere_medium = ("Find sphere medium", "Failed to find sphere medium")
|
||||
find_sphere_high = ("Find sphere high", "Failed to find sphere high")
|
||||
find_triggers_low = ("Find triggers low", "Failed to find triggers low")
|
||||
find_triggers_medium = ("Find triggers medium", "Failed to find triggers medium")
|
||||
find_triggers_high = ("Find triggers high", "Failed to find triggers high")
|
||||
x_movement = ("x direction movement decreases with increased damping", "x direction movement does not decrease with increased damping")
|
||||
high_damping_no_movement = ("High damping IsClose to 0 movement", "High damping is not IsClose to 0 movement")
|
||||
triggers_tripped_low = ("Low damping trips all 3 triggers", "Low damping did not trip all 3 triggers")
|
||||
triggers_tripped_medium = ("Medium damping trips 2/3 triggers", "Medium damping does not trip 2/3 triggers")
|
||||
triggers_tripped_high = ("High damping trips no triggers", "High damping trips triggers and should not")
|
||||
timeout = ("All spheres velocity IsClose to 0 before timeout", "All spheres velocity are not IsClose to 0 before timeout")
|
||||
exit_game_mode_low = ("Exited game mode low", "Couldn't exit game mode low")
|
||||
exit_game_mode_medium = ("Exited game mode medium", "Couldn't exit game mode medium")
|
||||
exit_game_mode_high = ("Exited game mode high", "Couldn't exit game mode high")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976199_RigidBodies_LinearDampingObjectMotion():
|
||||
"""
|
||||
The level consists of a PhysX Terrain component that is not interacted with (per the test case)
|
||||
and a PhysX collider with shape sphere, PhysX rigid bodies physics, and mesh with shape sphere.
|
||||
3 colliders with trigger are added to ensure x movement length is decreased quantitatively not
|
||||
just comparatively.
|
||||
The sphere starts asleep.
|
||||
We will enter game mode for each state defined
|
||||
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Set sphere attributes, measure initial position, and then ForceAwake
|
||||
5) Measure
|
||||
6) Exit game mode
|
||||
7) Run and repeat steps 2-6
|
||||
8) Report results
|
||||
|
||||
Notes:
|
||||
Initially we calculated time to stop by calling time.time() both when awakening the sphere and when velocity equaled zero.
|
||||
Comparing the time to stop accross sphere settings proved flaky.
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
class SphereInfo:
|
||||
def __init__(
|
||||
self,
|
||||
initial_linear_velocity,
|
||||
linear_damping,
|
||||
enter_game_mode_test,
|
||||
find_sphere_test,
|
||||
exit_game_mode_test,
|
||||
triggers_test,
|
||||
):
|
||||
self.initial_linear_velocity = initial_linear_velocity
|
||||
self.linear_damping = linear_damping
|
||||
self.entity = None
|
||||
self.initial_position = None
|
||||
self.final_position = None
|
||||
self.timeout = False
|
||||
self.triggers = []
|
||||
self.enter_game_mode_test = enter_game_mode_test
|
||||
self.find_sphere_test = find_sphere_test
|
||||
self.exit_game_mode_test = exit_game_mode_test
|
||||
self.triggers_test = triggers_test
|
||||
|
||||
def __str__(self):
|
||||
return """
|
||||
initial_linear_velocity = {}
|
||||
linear_damping = {}
|
||||
timeout = {}
|
||||
""".format(
|
||||
self.initial_linear_velocity.GetLength(), self.linear_damping, self.timeout
|
||||
)
|
||||
|
||||
def report(self):
|
||||
Report.info(self.__str__())
|
||||
Report.info_vector3(self.initial_position, "Initial position")
|
||||
Report.info_vector3(self.final_position, "Final position")
|
||||
for trigger in self.triggers:
|
||||
Report.info(trigger.__str__())
|
||||
|
||||
class Trigger:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.entity = None
|
||||
self.handler = None
|
||||
self.triggering_entity = None
|
||||
self.triggered = False
|
||||
|
||||
def on_trigger(self, args):
|
||||
self.triggered = True
|
||||
self.triggering_entity = args[0]
|
||||
Report.info("{} was triggered by {}".format(self.name, self.triggering_entity_name()))
|
||||
|
||||
def __str__(self):
|
||||
return """
|
||||
name = {}
|
||||
triggering entity name = {}
|
||||
triggered = {}
|
||||
""".format(
|
||||
self.name, self.triggering_entity_name(), self.triggered
|
||||
)
|
||||
|
||||
def triggering_entity_name(self):
|
||||
if self.triggering_entity:
|
||||
return azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", self.triggering_entity)
|
||||
return None
|
||||
|
||||
INITIAL_LINEAR_VELOCITY = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
ZERO_VELOCITY = lymath.Vector3(0.0, 0.0, 0.0)
|
||||
TOLERANCE = 0.001
|
||||
TIMEOUT = 3.0
|
||||
TRIGGER1 = "Trigger1"
|
||||
TRIGGER2 = "Trigger2"
|
||||
TRIGGER3 = "Trigger3"
|
||||
|
||||
# sphere state under test
|
||||
# fmt: off
|
||||
low_damping = SphereInfo(
|
||||
INITIAL_LINEAR_VELOCITY,
|
||||
5.0,
|
||||
Tests.enter_game_mode_low,
|
||||
Tests.find_sphere_low,
|
||||
Tests.exit_game_mode_low,
|
||||
Tests.find_triggers_low,
|
||||
)
|
||||
medium_damping = SphereInfo(
|
||||
INITIAL_LINEAR_VELOCITY,
|
||||
10.0,
|
||||
Tests.enter_game_mode_medium,
|
||||
Tests.find_sphere_medium,
|
||||
Tests.exit_game_mode_medium,
|
||||
Tests.find_triggers_medium,
|
||||
)
|
||||
high_damping = SphereInfo(
|
||||
INITIAL_LINEAR_VELOCITY,
|
||||
10000.0,
|
||||
Tests.enter_game_mode_high,
|
||||
Tests.find_sphere_high,
|
||||
Tests.exit_game_mode_high,
|
||||
Tests.find_triggers_high,
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
spheres = [low_damping, medium_damping, high_damping]
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4976199_RigidBodies_LinearDampingObjectMotion")
|
||||
|
||||
def run_test_steps(sphere):
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(sphere.enter_game_mode_test)
|
||||
|
||||
# 3) Retrieve entities
|
||||
sphere.entity = general.find_game_entity("Sphere")
|
||||
Report.critical_result(sphere.find_sphere_test, sphere.entity.IsValid())
|
||||
|
||||
triggers = [Trigger(TRIGGER1), Trigger(TRIGGER2), Trigger(TRIGGER3)]
|
||||
for trigger in triggers:
|
||||
trigger.entity = general.find_game_entity(trigger.name)
|
||||
|
||||
invalid_triggers = [t for t in triggers if not t.entity.IsValid()]
|
||||
for trigger in invalid_triggers:
|
||||
Report.info("Trigger {} is invalid".format(trigger.name))
|
||||
Report.critical_result(sphere.triggers_test, len(invalid_triggers) == 0)
|
||||
|
||||
# 4) Set sphere attributes, measure initial position, and then ForceAwake
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", sphere.entity, sphere.initial_linear_velocity)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearDamping", sphere.entity, sphere.linear_damping)
|
||||
sphere.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", sphere.entity).GetPosition()
|
||||
|
||||
# add handler for each trigger
|
||||
for trigger in triggers:
|
||||
trigger.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
trigger.handler.connect(trigger.entity)
|
||||
trigger.handler.add_callback("OnTriggerEnter", trigger.on_trigger)
|
||||
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "ForceAwake", sphere.entity)
|
||||
general.idle_wait_frames(1) # wait one frame for changes to apply
|
||||
|
||||
# 5) Measure
|
||||
|
||||
def sphere_stopped():
|
||||
sphere_linear_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", sphere.entity)
|
||||
if sphere_linear_velocity.IsClose(ZERO_VELOCITY, TOLERANCE):
|
||||
sphere.final_position = azlmbr.components.TransformBus(
|
||||
azlmbr.bus.Event, "GetWorldTM", sphere.entity
|
||||
).GetPosition()
|
||||
for trigger in [t for t in triggers if t.triggered]:
|
||||
if trigger.triggering_entity.Equal(sphere.entity):
|
||||
sphere.triggers.append(trigger)
|
||||
return True
|
||||
return False
|
||||
|
||||
if not helper.wait_for_condition(sphere_stopped, TIMEOUT):
|
||||
sphere.timeout = True
|
||||
sphere.report()
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(sphere.exit_game_mode_test)
|
||||
|
||||
# 7) Run and repeat steps 2-6
|
||||
for sphere in spheres:
|
||||
run_test_steps(sphere)
|
||||
|
||||
# 8) Report results
|
||||
no_timeout = True
|
||||
for sphere in spheres:
|
||||
if sphere.timeout:
|
||||
Report.info(
|
||||
"Timeout occurred. Sphere with damping = {} and Initial velocity = {} did not come to rest in the timeout of {}".format(
|
||||
sphere.linear_damping, sphere.initial_linear_velocity, TIMEOUT
|
||||
)
|
||||
)
|
||||
no_timeout = False
|
||||
|
||||
# fast fail if timeout occurred, comparisons will be meaningless and all info is in log to determine which sphere(s) timed out
|
||||
Report.critical_result(Tests.timeout, no_timeout)
|
||||
Report.result(
|
||||
Tests.high_damping_no_movement, high_damping.final_position.IsClose(high_damping.initial_position, TOLERANCE)
|
||||
)
|
||||
# comparative movement length
|
||||
Report.result(
|
||||
Tests.x_movement, high_damping.final_position.x < medium_damping.final_position.x < low_damping.final_position.x
|
||||
)
|
||||
# quantitative movement length
|
||||
all_three_tripped = (
|
||||
len(low_damping.triggers) == 3
|
||||
and len([t for t in low_damping.triggers if t.name == TRIGGER1]) == 1
|
||||
and len([t for t in low_damping.triggers if t.name == TRIGGER2]) == 1
|
||||
and len([t for t in low_damping.triggers if t.name == TRIGGER3]) == 1
|
||||
)
|
||||
Report.result(Tests.triggers_tripped_low, all_three_tripped)
|
||||
first_two_triggers_tripped = (
|
||||
len(medium_damping.triggers) == 2
|
||||
and len([t for t in medium_damping.triggers if t.name == TRIGGER1]) == 1
|
||||
and len([t for t in medium_damping.triggers if t.name == TRIGGER2]) == 1
|
||||
)
|
||||
Report.result(Tests.triggers_tripped_medium, first_two_triggers_tripped)
|
||||
Report.result(Tests.triggers_tripped_high, len(high_damping.triggers) == 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976199_RigidBodies_LinearDampingObjectMotion)
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4976200
|
||||
# Test Case Title : Verify that with higher angular damping, the object in rotation comes to rest faster
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976200
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
low_find_bar = ("Find bar low", "Failed to find bar low")
|
||||
medium_find_bar = ("Find bar medium", "Failed to find bar medium")
|
||||
high_find_bar = ("Find bar high", "Failed to find bar high")
|
||||
low_no_translation_change = ("The low bar had no tanslation change", "The low bar did have a translation change")
|
||||
medium_no_translation_change = ("The medium bar had no tanslation change", "The medium bar did have a translation change")
|
||||
high_no_translation_change = ("The high bar had no tanslation change", "The high bar did have a translation change")
|
||||
low_trigger_a = ("LowBarTriggerA triggered by the low bar", "LowBarTriggerA not triggered by the low bar")
|
||||
low_trigger_b = ("LowBarTriggerB not triggered by the low bar", "LowBarTriggerB triggered by the low bar")
|
||||
medium_trigger_a = ("MediumTriggerA triggered by the medium bar", "MediumTriggerA not triggered by the medium bar")
|
||||
medium_trigger_b = ("MediumTriggerB not triggered by the medium bar", "MediumTriggerB triggered by the medium bar")
|
||||
high_trigger_a = ("HighTriggerA not triggered by the high bar", "HighTriggerA triggered by the high bar")
|
||||
high_trigger_b = ("HighTriggerB not triggered by the high bar", "HighTriggerB triggered by the high bar")
|
||||
low_rotation = ("The low bar rotated only on the x axis", "The low bar did not rotate only on the x axis")
|
||||
medium_rotation = ("The medium bar rotated only on the x axis", "The medium bar did not only rotate on the x axis")
|
||||
high_rotation = ("The high bar did not rotate", "The high bar did rotate")
|
||||
comparative_rotation = ("The rotation decreased with increased angular damping", "The rotation din not decrease with increased angular damping")
|
||||
timeout = ("All spheres rotation IsClose to 0 before timeout", "All spheres rotation are not IsClose to 0 before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976200_RigidBody_AngularDampingObjectRotation():
|
||||
"""
|
||||
The level consists of a PhysX Terrain component that is not interacted with (per the test case)
|
||||
3 PhysX colliders with shape box, PhysX rigid bodies physics, and mesh with shape box.
|
||||
In the test we use the term bar - this means a box shape changed to be a rectangle, think thin plank.
|
||||
The bar shape is chosen for debugging, it is easy to see rotation.
|
||||
LowBar has 2 associated triggers LowBarTriggerA and LowBarTriggerB
|
||||
- LowBarTriggerA should be tripped to measure the rotation of the bar
|
||||
- LowBarTriggerB should not be tripped to measure that the rotation does not start increasing unexpectedly
|
||||
MediumBar has 2 associated triggers
|
||||
- MediumBarTriggerA should be tripped to measure the rotation of the bar
|
||||
- MediumBarTriggerB should not be tripped to measure that the rotation does not start increasing unexpectedly
|
||||
HighBar has 2 associated triggers
|
||||
- HighBarTriggerA is above the bar and should not be tripped
|
||||
- HighBarTriggerB is below the bar and should not be tripped
|
||||
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Find bars, set bars attributes, and measure initial position
|
||||
4) Awaken bars
|
||||
5) Measure
|
||||
6) Report results
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import AngleHelper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
class Bar:
|
||||
def __init__(
|
||||
self, name, initial_angular_velocity, angular_damping, triggers, find_bar_test, no_translation_change_test
|
||||
):
|
||||
self.name = name
|
||||
self.initial_angular_velocity = initial_angular_velocity
|
||||
self.angular_damping = angular_damping
|
||||
self.entity = None
|
||||
self.initial_position = None
|
||||
self.final_position = None
|
||||
self.initial_rotation = None
|
||||
self.final_rotation = None
|
||||
self.timeout = False
|
||||
self.triggers = triggers
|
||||
self.find_bar_test = find_bar_test
|
||||
self.no_translation_change_test = no_translation_change_test
|
||||
|
||||
def find_trigger(self, name):
|
||||
return next(t for t in self.triggers if t.name == name)
|
||||
|
||||
def __str__(self):
|
||||
return """
|
||||
name = {}
|
||||
angular_damping = {}
|
||||
timeout = {}
|
||||
""".format(
|
||||
self.name, self.angular_damping, self.timeout
|
||||
)
|
||||
|
||||
def report(self):
|
||||
Report.info(self)
|
||||
Report.info(
|
||||
"Initial angular velocity {} for {}".format(self.initial_angular_velocity.GetLength(), self.name)
|
||||
)
|
||||
Report.info_vector3(self.initial_position, "Initial position {}".format(self.name))
|
||||
if self.final_position:
|
||||
Report.info_vector3(self.final_position, "Final position {}".format(self.name))
|
||||
Report.info_vector3(self.initial_rotation, "Initial rotation {}".format(self.name))
|
||||
if self.final_rotation:
|
||||
Report.info_vector3(self.final_rotation, "Final rotation {}".format(self.name))
|
||||
for trigger in self.triggers:
|
||||
Report.info(trigger)
|
||||
|
||||
class Trigger:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.entity = None
|
||||
self.handler = None
|
||||
self.triggering_entity = None
|
||||
self.triggered = False
|
||||
|
||||
def on_trigger(self, args):
|
||||
self.triggered = True
|
||||
self.triggering_entity = args[0]
|
||||
Report.info("{} was triggered by {}".format(self.name, self.triggering_entity_name()))
|
||||
|
||||
def __str__(self):
|
||||
return """
|
||||
name = {}
|
||||
triggering entity name = {}
|
||||
triggered = {}
|
||||
""".format(
|
||||
self.name, self.triggering_entity_name(), self.triggered
|
||||
)
|
||||
|
||||
def triggering_entity_name(self):
|
||||
if self.triggering_entity:
|
||||
return azlmbr.entity.GameEntityContextRequestBus(
|
||||
azlmbr.bus.Broadcast, "GetEntityName", self.triggering_entity
|
||||
)
|
||||
return None
|
||||
|
||||
INITIAL_ANGULAR_VELOCITY = lymath.Vector3(5.0, 0.0, 0.0)
|
||||
ZERO_ANGULAR_VELOCITY = lymath.Vector3(0.0, 0.0, 0.0)
|
||||
TOLERANCE = 0.01
|
||||
TIMEOUT = 2.0
|
||||
LOW_TRIGGER_A = "LowBarTriggerA"
|
||||
LOW_TRIGGER_B = "LowBarTriggerB"
|
||||
MEDIUM_TRIGGER_A = "MediumBarTriggerA"
|
||||
MEDIUM_TRIGGER_B = "MediumBarTriggerB"
|
||||
HIGH_TRIGGER_A = "HighBarTriggerA"
|
||||
HIGH_TRIGGER_B = "HighBarTriggerB"
|
||||
|
||||
# bars
|
||||
# fmt: off
|
||||
low_damping = Bar(
|
||||
"LowBar",
|
||||
INITIAL_ANGULAR_VELOCITY,
|
||||
5.0,
|
||||
[Trigger(LOW_TRIGGER_A), Trigger(LOW_TRIGGER_B)],
|
||||
Tests.low_find_bar,
|
||||
Tests.low_no_translation_change,
|
||||
)
|
||||
medium_damping = Bar(
|
||||
"MediumBar",
|
||||
INITIAL_ANGULAR_VELOCITY,
|
||||
10.0,
|
||||
[Trigger(MEDIUM_TRIGGER_A), Trigger(MEDIUM_TRIGGER_B)],
|
||||
Tests.medium_find_bar,
|
||||
Tests.medium_no_translation_change,
|
||||
)
|
||||
high_damping = Bar(
|
||||
"HighBar",
|
||||
INITIAL_ANGULAR_VELOCITY,
|
||||
100.0,
|
||||
[Trigger(HIGH_TRIGGER_A), Trigger(HIGH_TRIGGER_B)],
|
||||
Tests.high_find_bar,
|
||||
Tests.high_no_translation_change,
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
bars = [low_damping, medium_damping, high_damping]
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4976200_RigidBody_AngularDampingObjectRotation")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
# 3) Find bars, set bars attributes, and measure initial position
|
||||
for bar in bars:
|
||||
bar.entity = general.find_game_entity(bar.name)
|
||||
Report.critical_result(bar.find_bar_test, bar.entity.IsValid())
|
||||
azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "SetAngularVelocity", bar.entity, bar.initial_angular_velocity
|
||||
)
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetAngularDamping", bar.entity, bar.angular_damping)
|
||||
general.idle_wait_frames(1) # wait one frame for changes to apply
|
||||
bar.initial_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", bar.entity)
|
||||
bar.initial_rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", bar.entity)
|
||||
# add handler for each trigger
|
||||
for trigger in bar.triggers:
|
||||
trigger.entity = general.find_game_entity(trigger.name)
|
||||
trigger.handler = azlmbr.bus.NotificationHandler("TriggerNotificationBus")
|
||||
trigger.handler.connect(trigger.entity)
|
||||
trigger.handler.add_callback("OnTriggerEnter", trigger.on_trigger)
|
||||
|
||||
def all_bars_stopped_rotating():
|
||||
bar_results = [False for bar in bars]
|
||||
for i, bar in enumerate(bars):
|
||||
bar_angular_velocity = azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "GetAngularVelocity", bar.entity
|
||||
)
|
||||
if bar_angular_velocity.IsClose(ZERO_ANGULAR_VELOCITY, TOLERANCE):
|
||||
bar.final_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", bar.entity)
|
||||
bar.final_rotation = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", bar.entity)
|
||||
bar_results[i] = True
|
||||
return all(bar_results)
|
||||
|
||||
# 4) Awaken bars
|
||||
for bar in bars:
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "ForceAwake", bar.entity)
|
||||
|
||||
# 5) Measure
|
||||
if not helper.wait_for_condition(all_bars_stopped_rotating, TIMEOUT):
|
||||
bar.timeout = True
|
||||
|
||||
# 6) Report results
|
||||
no_timeout = True
|
||||
for bar in bars:
|
||||
if bar.timeout:
|
||||
Report.info(
|
||||
"Timeout occurred. bar with damping = {} and initial angular velocity = {} did not stop rotating in the timeout of {}".format(
|
||||
bar.angular_damping, bar.initial_angular_velocity, TIMEOUT
|
||||
)
|
||||
)
|
||||
no_timeout = False
|
||||
|
||||
# fast fail if timeout occurred, comparisons will be meaningless and all info is in log to determine which bar(s) timed out
|
||||
Report.critical_result(Tests.timeout, no_timeout)
|
||||
# no translation
|
||||
for bar in bars:
|
||||
Report.result(bar.no_translation_change_test, bar.initial_position.IsClose(bar.final_position, TOLERANCE))
|
||||
# rotation (comparisons are safe because our test setup and triggers ensure the bar rotates < 360 degrees)
|
||||
# if a bar rotates to exactly the expected position + n(360), the triggers will fail the test
|
||||
outcome = (
|
||||
low_damping.final_rotation.x > low_damping.initial_rotation.x
|
||||
and AngleHelper.is_angle_close_deg(low_damping.initial_rotation.y, low_damping.final_rotation.y, TOLERANCE)
|
||||
and AngleHelper.is_angle_close_deg(low_damping.initial_rotation.z, low_damping.final_rotation.z, TOLERANCE)
|
||||
)
|
||||
Report.result(Tests.low_rotation, outcome)
|
||||
outcome = (
|
||||
medium_damping.final_rotation.x > medium_damping.initial_rotation.x
|
||||
and AngleHelper.is_angle_close_deg(
|
||||
medium_damping.initial_rotation.y, medium_damping.final_rotation.y, TOLERANCE
|
||||
)
|
||||
and AngleHelper.is_angle_close_deg(
|
||||
medium_damping.initial_rotation.z, medium_damping.final_rotation.z, TOLERANCE
|
||||
)
|
||||
)
|
||||
Report.result(Tests.medium_rotation, outcome)
|
||||
# we do not need worry about is_angle_close here because we expect this to not change at all
|
||||
# if it rotates 360, the triggers will fail the test
|
||||
Report.result(Tests.high_rotation, high_damping.initial_rotation.IsClose(high_damping.final_rotation, TOLERANCE))
|
||||
# comparative rotation (comparisons are safe because our test setup and triggers ensure the bar rotates < 360 degrees)
|
||||
# if the bars rotate to exactly the expected position + n(360), the triggers will fail the test
|
||||
Report.result(
|
||||
Tests.comparative_rotation,
|
||||
high_damping.final_rotation.x < medium_damping.final_rotation.x < low_damping.final_rotation.x,
|
||||
)
|
||||
# triggers (quantitative measure of rotation)
|
||||
Report.result(Tests.low_trigger_a, low_damping.find_trigger(LOW_TRIGGER_A).triggered)
|
||||
Report.result(Tests.low_trigger_b, not low_damping.find_trigger(LOW_TRIGGER_B).triggered)
|
||||
Report.result(Tests.medium_trigger_a, medium_damping.find_trigger(MEDIUM_TRIGGER_A).triggered)
|
||||
Report.result(Tests.medium_trigger_b, not medium_damping.find_trigger(MEDIUM_TRIGGER_B).triggered)
|
||||
Report.result(Tests.high_trigger_a, not high_damping.find_trigger(HIGH_TRIGGER_A).triggered)
|
||||
Report.result(Tests.high_trigger_b, not high_damping.find_trigger(HIGH_TRIGGER_B).triggered)
|
||||
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976200_RigidBody_AngularDampingObjectRotation)
|
||||
@@ -0,0 +1,385 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976201
|
||||
# Test Case Title : Verify that the value assigned to the Mass of the object, gets actually assigned to the object
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976201
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
# test iteration 1
|
||||
enter_game_mode_1 = ("Entered game mode first time", "Failed to enter game mode first time")
|
||||
ProjectileSphere_exists_1 = ("ProjectileSphere entity found first time", "ProjectileSphere entity not found first time")
|
||||
TargetSphere_exists_1 = ("TargetSphere entity found first time", "TargetSphere entity not found first time")
|
||||
Trigger1_exists_1 = ("Trigger1 entity found first time", "Trigger1 entity not found first time")
|
||||
Trigger2_exists_1 = ("Trigger2 entity found first time", "Trigger2 entity not found first time")
|
||||
Trigger3_exists_1 = ("Trigger3 entity found first time", "Trigger3 entity not found first time")
|
||||
TargetSphere_mass_1 = ("Mass of TargetSphere was set to 1.0", "Mass of TargetSphere was not set to 1.0")
|
||||
spheres_collided_1 = ("ProjectileSphere and TargetSphere collided first time", "Timed out before ProjectileSphere & 2 collided first time")
|
||||
stopped_correctly_1 = ("TargetSphere hit Trigger1 & Trigger2 but not Trigger_3", "TargetSphere did not stop correctly")
|
||||
check_y_1 = ("sphere did not move far from expected in Y direction _1", "TargetSphere moved an unexpected distance in Y direction _1")
|
||||
check_z_1 = ("sphere did not move far from expected in Z direction _1", "TargetSphere moved an unexpected distance in Z direction _1")
|
||||
exit_game_mode_1 = ("Exited game mode first time", "Couldn't exit game mode first time")
|
||||
|
||||
# test iteration 2
|
||||
enter_game_mode_2 = ("Entered game mode second time", "Failed to enter game mode second time")
|
||||
ProjectileSphere_exists_2 = ("ProjectileSphere entity found second time", "ProjectileSphere entity not found second time")
|
||||
TargetSphere_exists_2 = ("TargetSphere entity found second time", "TargetSphere entity not found second time")
|
||||
Trigger1_exists_2 = ("Trigger1 entity found second time", "Trigger1 entity not found second time")
|
||||
Trigger2_exists_2 = ("Trigger2 entity found second time", "Trigger2 entity not found second time")
|
||||
Trigger3_exists_2 = ("Trigger3 entity found second time", "Trigger3 entity not found second time")
|
||||
TargetSphere_mass_2 = ("Mass of TargetSphere was set to 10.0", "Mass of TargetSphere was not set to 10.0")
|
||||
spheres_collided_2 = ("ProjectileSphere and TargetSphere collided second time", "Timed out before ProjectileSphere & 2 collided second time")
|
||||
stopped_correctly_2 = ("TargetSphere hit Trigger1 but not Trigger2 or Trigger3", "TargetSphere did not stop correctly")
|
||||
check_y_2 = ("sphere did not move far from expected in Y direction _2", "TargetSphere moved an unexpected distance in Y direction _2")
|
||||
check_z_2 = ("sphere did not move far from expected in Z direction _2", "TargetSphere moved an unexpected distance in Z direction _2")
|
||||
exit_game_mode_2 = ("Exited game mode second time", "Couldn't exit game mode second time")
|
||||
|
||||
# test iteration 3
|
||||
enter_game_mode_3 = ("Entered game mode third time", "Failed to enter game mode third time")
|
||||
ProjectileSphere_exists_3 = ("ProjectileSphere entity found third time", "ProjectileSphere entity not found third time")
|
||||
TargetSphere_exists_3 = ("TargetSphere entity found third time", "TargetSphere entity not found third time")
|
||||
Trigger1_exists_3 = ("Trigger1 entity found third time", "Trigger1 entity not found third time")
|
||||
Trigger2_exists_3 = ("Trigger2 entity found third time", "Trigger2 entity not found third time")
|
||||
Trigger3_exists_3 = ("Trigger3 entity found third time", "Trigger3 entity not found third time")
|
||||
TargetSphere_mass_3 = ("Mass of TargetSphere was set to 100.0", "Mass of TargetSphere was not set to 100.0")
|
||||
spheres_collided_3 = ("ProjectileSphere and TargetSphere collided third time", "Timed out before ProjectileSphere & 2 collided third time")
|
||||
stopped_correctly_3 = ("TargetSphere did not hit Trigger1, Trigger2, or Trigger3", "TargetSphere hit one or more triggers before stopping")
|
||||
check_y_3 = ("sphere did not move far from expected in Y direction _3", "TargetSphere moved an unexpected distance in Y direction _3")
|
||||
check_z_3 = ("sphere did not move far from expected in Z direction _3", "TargetSphere moved an unexpected distance in Z direction _3")
|
||||
exit_game_mode_3 = ("Exited game mode third time", "Couldn't exit game mode third time")
|
||||
|
||||
# general
|
||||
velocity_sizing = ("The velocities are in the correct order of magnitude", "The velocities are not correctly ordered in magnitude")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976201_RigidBody_MassIsAssigned():
|
||||
"""
|
||||
Summary:
|
||||
Checking that the mass set to the object is actually applied via colliding entities
|
||||
|
||||
Level Description:
|
||||
ProjectileSphere (entity) - Sphere shaped Mesh; Sphere shaped PhysX Collider;
|
||||
PhysX Rigid Body: initial linear velocity in X direction is 5m/s, initial mass 1kg,
|
||||
gravity disabled, linear damping default (0.05)
|
||||
TargetSphere (entity) - Sphere shaped Mesh; Sphere shaped PhysX Collider;
|
||||
PhysX Rigid Body: no initial velocity, initial mass 1kg, gravity disabled, linear damping 1.0
|
||||
|
||||
Expected Behavior:
|
||||
The ProjectileSphere entity will float towards TargetSphere entity and then collide with it.
|
||||
Because they are the same mass initially, the second sphere will move after collision.
|
||||
TargetSphere's mass will be increased and scenario will run again,
|
||||
but TargetSphere will have a smaller velocity after collision.
|
||||
TargetSphere will then increase mass again and should barely move after the final collision.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Repeat steps 3-9
|
||||
3) Enter game mode
|
||||
4) Find and setup entities
|
||||
5) Set mass of the TargetSphere
|
||||
6) Check for collision
|
||||
7) Wait for TargetSphere x velocity = 0
|
||||
8) Check the triggers
|
||||
9) Exit game mode
|
||||
10) Verify the velocity of TargetSphere decreased after collision as mass increased
|
||||
11) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
MOVEMENT_TIMEOUT = 7.0
|
||||
COLLISION_TIMEOUT = 2.0
|
||||
VELOCITY_ZERO = 0.01
|
||||
Y_Z_BUFFER = 0.01
|
||||
TARGET_SPHERE_NAME = "TargetSphere"
|
||||
PROJECTILE_SPHERE_NAME = "ProjectileSphere"
|
||||
TRIGGER_1_NAME = "Trigger1"
|
||||
TRIGGER_2_NAME = "Trigger2"
|
||||
TRIGGER_3_NAME = "Trigger3"
|
||||
|
||||
class ProjectileSphere:
|
||||
def __init__(self, test_iteration):
|
||||
self.name = PROJECTILE_SPHERE_NAME
|
||||
self.test_iteration = test_iteration
|
||||
self.timeout_reached = True
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(
|
||||
Tests.__dict__["ProjectileSphere_exists_" + str(self.test_iteration)], self.id.IsValid()
|
||||
)
|
||||
|
||||
def destroy_me(self):
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DestroyGameEntity", self.id)
|
||||
|
||||
class TargetSphere:
|
||||
def __init__(self, mass_to_assign, stop_before_trigger_name, expected_trigger_pattern, test_iteration):
|
||||
self.id = None
|
||||
self.name = TARGET_SPHERE_NAME
|
||||
self.start_mass = None
|
||||
self.mass_to_assign = mass_to_assign
|
||||
self.collision_begin = False
|
||||
self.after_collision_velocity = None
|
||||
self.x_movement_timeout = True
|
||||
self.stop_before_trigger_name = stop_before_trigger_name
|
||||
self.expected_trigger_pattern = expected_trigger_pattern
|
||||
self.collision_ended = False
|
||||
self.test_iteration = test_iteration
|
||||
self.test_set_mass = self.get_test("TargetSphere_mass_")
|
||||
self.test_enter_game_mode = self.get_test("enter_game_mode_")
|
||||
self.test_ProjectileSphere_exist = self.get_test("ProjectileSphere_exists_")
|
||||
self.test_TargetSphere_exist = self.get_test("TargetSphere_exists_")
|
||||
self.test_spheres_collided = self.get_test("spheres_collided_")
|
||||
self.test_stop_properly = self.get_test("stopped_correctly_")
|
||||
self.test_check_y = self.get_test("check_y_")
|
||||
self.test_check_z = self.get_test("check_z_")
|
||||
self.test_exit_game_mode = self.get_test("exit_game_mode_")
|
||||
|
||||
def get_test(self, test_prefix):
|
||||
return Tests.__dict__[test_prefix + str(self.test_iteration)]
|
||||
|
||||
def find(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(Tests.__dict__["TargetSphere_exists_" + str(self.test_iteration)], self.id.IsValid())
|
||||
|
||||
def setup_mass(self):
|
||||
self.start_mass = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetMass", self.id)
|
||||
Report.info("{} starting mass: {}".format(self.name, self.start_mass))
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetMass", self.id, self.mass_to_assign)
|
||||
general.idle_wait_frames(1) # wait for mass to apply
|
||||
mass_after_set = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetMass", self.id)
|
||||
Report.info("{} mass after setting: {}".format(self.name, mass_after_set))
|
||||
Report.result(self.test_set_mass, self.mass_to_assign == mass_after_set)
|
||||
|
||||
def current_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.id):
|
||||
Report.info("spheres collision begin")
|
||||
self.collision_begin = True
|
||||
|
||||
def on_collision_end(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.id):
|
||||
Report.info("spheres collision end")
|
||||
self.after_collision_velocity = self.current_velocity()
|
||||
self.collision_ended = True
|
||||
|
||||
def add_collision_handlers(self, projectile_sphere_id):
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(projectile_sphere_id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
self.handler.add_callback("OnCollisionEnd", self.on_collision_end)
|
||||
|
||||
def x_velocity_zero(self):
|
||||
if abs(self.current_velocity().x) < VELOCITY_ZERO:
|
||||
Report.info("TargetSphere has stopped moving.")
|
||||
self.x_movement_timeout = False
|
||||
return True
|
||||
return False
|
||||
|
||||
def collision_complete(self):
|
||||
return self.collision_begin and self.collision_ended
|
||||
|
||||
def check_y_z_movement_from_collision(self):
|
||||
"""
|
||||
Used to check that the entity has not moved too far in either the Y or Z direction
|
||||
"""
|
||||
|
||||
def is_within_tolerance(velocity_one_direction):
|
||||
return abs(velocity_one_direction) < Y_Z_BUFFER
|
||||
|
||||
Report.info_vector3(self.after_collision_velocity, "Initial Velocity: ")
|
||||
Report.result(self.test_check_y, is_within_tolerance(self.after_collision_velocity.y))
|
||||
Report.result(self.test_check_z, is_within_tolerance(self.after_collision_velocity.z))
|
||||
|
||||
class Trigger:
|
||||
"""
|
||||
Used in the level to tell if the TargetSphere entity has moved a certain distance.
|
||||
There are three triggers set up in the level.
|
||||
"""
|
||||
|
||||
def __init__(self, name, test_iteration):
|
||||
self.name = name
|
||||
self.handler = None
|
||||
self.triggered = False
|
||||
self.test_iteration = test_iteration
|
||||
self.id = general.find_game_entity(self.name)
|
||||
Report.critical_result(Tests.__dict__[self.name + "_exists_" + str(self.test_iteration)], self.id.IsValid())
|
||||
self.setup_handler()
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
"""
|
||||
This is passed into this object's handler.add_callback().
|
||||
"""
|
||||
other_id = args[0]
|
||||
self.triggered = True
|
||||
triggered_by_name = azlmbr.entity.GameEntityContextRequestBus(
|
||||
azlmbr.bus.Broadcast, "GetEntityName", other_id
|
||||
)
|
||||
Report.info("{} was triggered by {}.".format(self.name, triggered_by_name))
|
||||
|
||||
def setup_handler(self):
|
||||
"""
|
||||
This is called to setup the handler for this trigger object
|
||||
"""
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
class TriggerResultPattern:
|
||||
"""
|
||||
Used to store and determine which triggers were activated and compare to expected
|
||||
"""
|
||||
|
||||
def __init__(self, trigger1_activated, trigger2_activated, trigger3_activated):
|
||||
self.trigger1_activated = trigger1_activated
|
||||
self.trigger2_activated = trigger2_activated
|
||||
self.trigger3_activated = trigger3_activated
|
||||
|
||||
def __eq__(self, other_pattern):
|
||||
"""
|
||||
Used to determine if two patterns equal/match each other (i.e. Expected VS Actual)
|
||||
"""
|
||||
if isinstance(other_pattern, self.__class__):
|
||||
return (
|
||||
self.trigger1_activated == other_pattern.trigger1_activated
|
||||
and self.trigger2_activated == other_pattern.trigger2_activated
|
||||
and self.trigger3_activated == other_pattern.trigger3_activated
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
||||
def report(self, expect_actual):
|
||||
Report.info(
|
||||
"""TargetSphere {} Triggers:
|
||||
Trigger_1: {}
|
||||
Trigger_2: {}
|
||||
Trigger_3: {}
|
||||
""".format(
|
||||
expect_actual, self.trigger1_activated, self.trigger2_activated, self.trigger3_activated
|
||||
)
|
||||
)
|
||||
|
||||
target_sphere_1kg = TargetSphere(
|
||||
mass_to_assign=1.0,
|
||||
stop_before_trigger_name=TRIGGER_3_NAME,
|
||||
expected_trigger_pattern=TriggerResultPattern(True, True, False),
|
||||
test_iteration=1,
|
||||
)
|
||||
|
||||
target_sphere_10kg = TargetSphere(
|
||||
mass_to_assign=10.0,
|
||||
stop_before_trigger_name=TRIGGER_2_NAME,
|
||||
expected_trigger_pattern=TriggerResultPattern(True, False, False),
|
||||
test_iteration=2,
|
||||
)
|
||||
|
||||
target_sphere_100kg = TargetSphere(
|
||||
mass_to_assign=100.0,
|
||||
stop_before_trigger_name=TRIGGER_1_NAME,
|
||||
expected_trigger_pattern=TriggerResultPattern(False, False, False),
|
||||
test_iteration=3,
|
||||
)
|
||||
|
||||
target_spheres = [target_sphere_1kg, target_sphere_10kg, target_sphere_100kg]
|
||||
|
||||
target_sphere_velocities = {}
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4976201_RigidBody_MassIsAssigned")
|
||||
|
||||
# 2) Repeat steps 3-9
|
||||
for target_sphere in target_spheres:
|
||||
Report.info("***************** Begin Test Iteration {} ******************".format(target_sphere.test_iteration))
|
||||
|
||||
# 3) Enter game mode
|
||||
helper.enter_game_mode(target_sphere.test_enter_game_mode)
|
||||
|
||||
# 4) Find and setup entities
|
||||
projectile_sphere = ProjectileSphere(target_sphere.test_iteration)
|
||||
target_sphere.find()
|
||||
target_sphere.add_collision_handlers(projectile_sphere.id)
|
||||
|
||||
trigger_1 = Trigger(TRIGGER_1_NAME, target_sphere.test_iteration)
|
||||
trigger_2 = Trigger(TRIGGER_2_NAME, target_sphere.test_iteration)
|
||||
trigger_3 = Trigger(TRIGGER_3_NAME, target_sphere.test_iteration)
|
||||
|
||||
# 5) Set mass of the TargetSphere
|
||||
target_sphere.setup_mass()
|
||||
|
||||
# 6) Check for collision
|
||||
|
||||
helper.wait_for_condition(target_sphere.collision_complete, COLLISION_TIMEOUT)
|
||||
Report.critical_result(target_sphere.test_spheres_collided, target_sphere.collision_complete())
|
||||
projectile_sphere.destroy_me()
|
||||
Report.info_vector3(
|
||||
target_sphere.after_collision_velocity, "Velocity of {} after the collision: ".format(target_sphere.name)
|
||||
)
|
||||
|
||||
Report.info("The sphere should stop before touching {}".format(target_sphere.stop_before_trigger_name))
|
||||
|
||||
# 7) Wait for TargetSphere x velocity = 0
|
||||
|
||||
helper.wait_for_condition(target_sphere.x_velocity_zero, MOVEMENT_TIMEOUT)
|
||||
if target_sphere.x_movement_timeout is True:
|
||||
Report.info("TargetSphere failed to stop moving in the x direction before timeout was reached.")
|
||||
|
||||
# 8) Check the triggers
|
||||
actual_trigger_pattern = TriggerResultPattern(trigger_1.triggered, trigger_2.triggered, trigger_3.triggered)
|
||||
|
||||
patterns_match = actual_trigger_pattern == target_sphere.expected_trigger_pattern
|
||||
target_sphere.expected_trigger_pattern.report("Expected")
|
||||
actual_trigger_pattern.report("Actual")
|
||||
Report.result(target_sphere.test_stop_properly, patterns_match)
|
||||
|
||||
target_sphere.check_y_z_movement_from_collision()
|
||||
target_sphere_velocities.update({target_sphere.test_iteration: target_sphere.after_collision_velocity.x})
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(target_sphere.test_exit_game_mode)
|
||||
Report.info("~~~~~~~~~~~~~~ Test Iteration {} End ~~~~~~~~~~~~~~~~~~".format(target_sphere.test_iteration))
|
||||
|
||||
# 10) Verify the velocity of TargetSphere decreased after collision as mass increased
|
||||
outcome = target_sphere_velocities[1] > target_sphere_velocities[2] > target_sphere_velocities[3]
|
||||
Report.result(Tests.velocity_sizing, outcome)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976201_RigidBody_MassIsAssigned)
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
"""
|
||||
Test case ID : C4976202
|
||||
Test Case Title : Verify that if the object is moving with Kinetic energy less than
|
||||
the sleep threshold value, then physX will put it to stop after 0.4 secs (once the
|
||||
wake counter goes to zero) if the KE is still below the threshold
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976202
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode_1 = ("Entered game mode_1", "Failed to enter game mode_1" )
|
||||
Trigger1_exists_1 = ("Trigger1 entity was found_1", "Trigger1 entity was not found_1" )
|
||||
Trigger2_exists_1 = ("Trigger2 entity was found_1", "Trigger2 entity was not found_1" )
|
||||
Trigger3_exists_1 = ("Trigger3 entity was found_1", "Trigger3 entity was not found_1" )
|
||||
Pushing_Cube_exists_1 = ("Pushing_Cube entity was found_1", "Pushing_Cube entity was not found_1" )
|
||||
Target_Ball_exists_1 = ("Target_Ball entity was found_1", "Target_Ball entity was not found_1" )
|
||||
threshold_setup_1 = ("The sleep threshold was setup properly_1", "The sleep threshold was not setup properly_1" )
|
||||
cube_collided_with_ball_1 = ("The cube hit the ball the_1", "The cube did not hit the ball the_1" )
|
||||
ball_stopped_moving_1 = ("The ball has stopped moving before timeout_1", "The ball did not stop moving before timeout was reached_1" )
|
||||
trigger_patterns_match_1 = ("The actual trigger pattern matches expected_1", "The actual trigger pattern did not match expected_1" )
|
||||
check_y_z_movement_1 = ("The ball did not move too far in Y or Z_1", "The ball moved farther than expected in Y or Z after collision_1")
|
||||
exit_game_mode_1 = ("Exited game mode_1", "Couldn't exit game mode_1" )
|
||||
|
||||
enter_game_mode_2 = ("Entered game mode_2", "Failed to enter game mode_2" )
|
||||
Trigger1_exists_2 = ("Trigger1 entity was found_2", "Trigger1 entity was not found_2" )
|
||||
Trigger2_exists_2 = ("Trigger2 entity was found_2", "Trigger2 entity was not found_2" )
|
||||
Trigger3_exists_2 = ("Trigger3 entity was found_2", "Trigger3 entity was not found_2" )
|
||||
Pushing_Cube_exists_2 = ("Pushing_Cube entity was found_2", "Pushing_Cube entity was not found_2" )
|
||||
Target_Ball_exists_2 = ("Target_Ball entity was found_2", "Target_Ball entity was not found_2" )
|
||||
threshold_setup_2 = ("The sleep threshold was setup properly_2", "The sleep threshold was not setup properly_2" )
|
||||
cube_collided_with_ball_2 = ("The cube hit the ball the_2", "The cube did not hit the ball the_2" )
|
||||
ball_stopped_moving_2 = ("The ball has stopped moving before timeout_2", "The ball did not stop moving before timeout was reached_2" )
|
||||
trigger_patterns_match_2 = ("The actual trigger pattern matches expected_2", "The actual trigger pattern did not match expected_2" )
|
||||
check_y_z_movement_2 = ("The ball did not move too far in Y or Z_2", "The ball moved farther than expected in Y or Z after collision_2")
|
||||
exit_game_mode_2 = ("Exited game mode_2", "Couldn't exit game mode_2" )
|
||||
|
||||
enter_game_mode_3 = ("Entered game mode_3", "Failed to enter game mode_3" )
|
||||
Trigger1_exists_3 = ("Trigger1 entity was found_3", "Trigger1 entity was not found_3" )
|
||||
Trigger2_exists_3 = ("Trigger2 entity was found_3", "Trigger2 entity was not found_3" )
|
||||
Trigger3_exists_3 = ("Trigger3 entity was found_3", "Trigger3 entity was not found_3" )
|
||||
Pushing_Cube_exists_3 = ("Pushing_Cube entity was found_3", "Pushing_Cube entity was not found_3" )
|
||||
Target_Ball_exists_3 = ("Target_Ball entity was found_3", "Target_Ball entity was not found_3" )
|
||||
threshold_setup_3 = ("The sleep threshold was setup properly_3", "The sleep threshold was not setup properly_3" )
|
||||
cube_collided_with_ball_3 = ("The cube hit the ball the_3", "The cube did not hit the ball the_3" )
|
||||
ball_stopped_moving_3 = ("The ball has stopped moving before timeout_3", "The ball did not stop moving before timeout was reached_3" )
|
||||
trigger_patterns_match_3 = ("The actual trigger pattern matches expected_3", "The actual trigger pattern did not match expected_3" )
|
||||
check_y_z_movement_3 = ("The ball did not move too far in Y or Z_3", "The ball moved farther than expected in Y or Z after collision_3")
|
||||
exit_game_mode_3 = ("Exited game mode_3", "Couldn't exit game mode_3" )
|
||||
|
||||
stop_locations_comparison = ("The stop locations are correctly ordered", "The stop locations are not ordered correctly" )
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976202_RigidBody_StopsWhenBelowKineticThreshold():
|
||||
"""
|
||||
Summary:
|
||||
A Pushing Cube and a Sphere are suspended over the PhysX Terrain.
|
||||
The cube entity has an initial velocity and on start will move toward the ball
|
||||
and collide with it. When the kinetic energy of the ball is below a certain threshold,
|
||||
it should stop moving.
|
||||
|
||||
Level Description:
|
||||
Terrain (entity):
|
||||
PhysX Terrain component: default settings
|
||||
|
||||
PushingCube (entity): Start Inactive
|
||||
Mesh component: Box shape
|
||||
PhysX Collider component: Box shape; default settings
|
||||
PhysX Rigid Body component: Initial linear velocity: 5m/s in positive X direction;
|
||||
gravity disabled; mass 1.0kg;
|
||||
|
||||
TargetBall (entity):
|
||||
Mesh component: Sphere shape
|
||||
PhysX Collider component: Sphere shape; default settings
|
||||
PhysX Rigid Body component: Linear damping: 0.5; mass 10kg; gravity disabled; sleep threshold: 1.0;
|
||||
|
||||
Trigger1 (entity):
|
||||
Box Shape component: Dimensions (0.1, 1.0, 1.0); Visible: Checked; Game View: Checked
|
||||
PhysX Collider component: Box shape; Dimensions (0.1, 1.0, 1.0);
|
||||
|
||||
Trigger2 (entity):
|
||||
Box Shape component: Dimensions (0.1, 1.0, 1.0); Visible: Checked; Game View: Checked
|
||||
PhysX Collider component: Box shape; Dimensions (0.1, 1.0, 1.0);
|
||||
|
||||
Trigger3 (entity):
|
||||
Box Shape component: Dimensions (0.1, 1.0, 1.0); Visible: Checked; Game View: Checked
|
||||
PhysX Collider component: Box shape; Dimensions (0.1, 1.0, 1.0);
|
||||
|
||||
Expected Behavior:
|
||||
The cube entity should push the ball entity into motion.
|
||||
When the Kinetic Energy of the ball drops below the sleep threshold, it should stop moving.
|
||||
The test steps will run for each sleep threshold value (1.0, 5.0, and 10.0). The ball should stop
|
||||
before touching a specific Trigger (1.0 & Trigger3, 5.0 & Trigger 2, 10.0 & Trigger 1)
|
||||
|
||||
Test Steps:
|
||||
1) Open the level
|
||||
# Steps 2-9 are repeated for each threshold value
|
||||
2) Enter game mode
|
||||
3) Find entities and setup handlers or values
|
||||
4) Activate the PushingCube entity to start movement
|
||||
5) Wait for the ball to be hit by the pushing block
|
||||
6) Wait for the ball to come to a stop in X direction
|
||||
7) Check that the triggers match the expected trigger results
|
||||
8) Check that the ball didn't move too far in Y or Z directions
|
||||
9) Exit game mode
|
||||
10) Run test_steps_per_threshold for remaining threshold values
|
||||
11) Compare the stop locations to each other
|
||||
12) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
TRIGGER_TIMEOUT = 5.0
|
||||
TIMEOUT_SECONDS = 2.0
|
||||
VELOCITY_TOLERANCE = 0.05
|
||||
Y_Z_BUFFER = 0.01
|
||||
|
||||
class TestData:
|
||||
"""
|
||||
This is a placeholder to store the values persisting the duration of the test
|
||||
"""
|
||||
|
||||
threshold_count = 0
|
||||
stop_locations = []
|
||||
|
||||
class Entity(object):
|
||||
"""
|
||||
Base class for Entities to add basic common functionality such as fetch_id, validate_existence
|
||||
"""
|
||||
|
||||
def __init__(self, name, test_steps_run):
|
||||
self.name = name
|
||||
self.test_steps_run = test_steps_run
|
||||
self.id = None
|
||||
self.fetch_id()
|
||||
self.validate_exist()
|
||||
|
||||
def fetch_id(self):
|
||||
self.id = general.find_game_entity(self.name)
|
||||
|
||||
def validate_exist(self):
|
||||
Report.critical_result(Tests.__dict__[self.name + "_exists_" + str(self.test_steps_run)], self.id.IsValid())
|
||||
|
||||
def get_location(self):
|
||||
return azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
class Trigger(Entity):
|
||||
"""
|
||||
Trigger entities used to tell if the Target_Ball has moved far enough/too far compared to expected distances
|
||||
"""
|
||||
|
||||
def __init__(self, test_steps_run, name):
|
||||
super(Trigger, self).__init__(name, test_steps_run)
|
||||
self.handler = None
|
||||
self.triggered = False
|
||||
self.triggered_by = None
|
||||
self.attach_handler()
|
||||
|
||||
def on_trigger_enter(self, args):
|
||||
other_id = args[0]
|
||||
self.triggered = True
|
||||
self.triggered_by = azlmbr.entity.GameEntityContextRequestBus(
|
||||
azlmbr.bus.Broadcast, "GetEntityName", other_id
|
||||
)
|
||||
Report.info("{} was triggered by {}.".format(self.name, self.triggered_by))
|
||||
|
||||
def attach_handler(self):
|
||||
self.handler = azlmbr.physics.TriggerNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnTriggerEnter", self.on_trigger_enter)
|
||||
|
||||
class PushingCube(Entity):
|
||||
def __init__(self, test_steps_run, name="Pushing_Cube"):
|
||||
super(PushingCube, self).__init__(name, test_steps_run)
|
||||
|
||||
def activate(self):
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "ActivateGameEntity", self.id)
|
||||
|
||||
class TargetBall(Entity):
|
||||
def __init__(self, test_steps_run, sleep_threshold, name="Target_Ball"):
|
||||
super(TargetBall, self).__init__(name, test_steps_run)
|
||||
self.expected_sleep_threshold = sleep_threshold
|
||||
self.handler = None
|
||||
self.init_velocity = None
|
||||
self.grabbed_velocity = None
|
||||
self.collided_with_box = False
|
||||
self.stopped_moving_x = False
|
||||
self.trigger_timed_out = True
|
||||
self.pushing_cube_id = None
|
||||
self.setup_sleep_threshold()
|
||||
self.attach_collision_handler()
|
||||
|
||||
def setup_sleep_threshold(self):
|
||||
azlmbr.physics.RigidBodyRequestBus(
|
||||
azlmbr.bus.Event, "SetSleepThreshold", self.id, self.expected_sleep_threshold
|
||||
)
|
||||
grabbed_value = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetSleepThreshold", self.id)
|
||||
Report.info("Expected sleep threshold value: {}".format(self.expected_sleep_threshold))
|
||||
Report.info("Actual value: {}".format(grabbed_value))
|
||||
Report.critical_result(
|
||||
Tests.__dict__["threshold_setup_" + str(TestData.threshold_count)],
|
||||
grabbed_value == self.expected_sleep_threshold,
|
||||
)
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(self.pushing_cube_id):
|
||||
Report.info("Push_Box collided with ball.")
|
||||
self.init_velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
Report.info_vector3(self.init_velocity, "Initial Velocity of {}".format(self.name))
|
||||
self.collided_with_box = True
|
||||
|
||||
def attach_collision_handler(self):
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
def has_stopped_moving_in_x(self):
|
||||
"""
|
||||
This method is used as a condition for helper.wait_for_condition()
|
||||
"""
|
||||
velocity = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
if abs(velocity.x) < VELOCITY_TOLERANCE:
|
||||
Report.info("{} has stopped moving in the X direction.".format(self.name))
|
||||
self.stopped_moving_x = True
|
||||
self.trigger_timed_out = False
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_y_z_delta(self):
|
||||
"""
|
||||
Used to check that the entity has not moved too far in either the Y or Z direction
|
||||
"""
|
||||
|
||||
def is_within_tolerance(velocity_one_direction):
|
||||
return abs(velocity_one_direction) < Y_Z_BUFFER
|
||||
|
||||
Report.info_vector3(self.init_velocity, "Initial Velocity: ")
|
||||
Report.result(
|
||||
Tests.__dict__["check_y_z_movement_" + str(TestData.threshold_count)],
|
||||
is_within_tolerance(self.init_velocity.y) and is_within_tolerance(self.init_velocity.z),
|
||||
)
|
||||
|
||||
# 1) Open the level
|
||||
helper.open_level("Physics", "C4976202_RigidBody_StopsWhenBelowKineticThreshold")
|
||||
|
||||
def test_steps(sleep_threshold_value, trigger_pattern):
|
||||
TestData.threshold_count += 1
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.__dict__["enter_game_mode_" + str(TestData.threshold_count)])
|
||||
|
||||
# 3) Find entities and setup handlers or values
|
||||
target_ball = TargetBall(TestData.threshold_count, sleep_threshold_value)
|
||||
pushing_cube = PushingCube(TestData.threshold_count)
|
||||
target_ball.pushing_cube_id = pushing_cube.id
|
||||
|
||||
triggers = []
|
||||
for i in range(1, 4):
|
||||
triggers.append(Trigger(TestData.threshold_count, "Trigger" + str(i)))
|
||||
|
||||
# 4) Activate the Pushing_Box entity to start movement
|
||||
pushing_cube.activate()
|
||||
|
||||
# 5) Wait for the ball to be hit by the pushing block
|
||||
helper.wait_for_condition(lambda: target_ball.collided_with_box, TIMEOUT_SECONDS)
|
||||
Report.result(
|
||||
Tests.__dict__["cube_collided_with_ball_" + str(TestData.threshold_count)], target_ball.collided_with_box
|
||||
)
|
||||
|
||||
# 6) Wait for the ball to come to a stop in X direction
|
||||
helper.wait_for_condition(target_ball.has_stopped_moving_in_x, TRIGGER_TIMEOUT)
|
||||
Report.result(
|
||||
Tests.__dict__["ball_stopped_moving_" + str(TestData.threshold_count)], target_ball.stopped_moving_x
|
||||
)
|
||||
|
||||
# record the location of the stopping point
|
||||
TestData.stop_locations.append(target_ball.get_location())
|
||||
|
||||
# 7) Check that the triggers match the expected trigger results
|
||||
trigger_result = (triggers[0].triggered, triggers[1].triggered, triggers[2].triggered)
|
||||
triggers_matched = trigger_result == trigger_pattern
|
||||
Report.result(Tests.__dict__["trigger_patterns_match_" + str(TestData.threshold_count)], triggers_matched)
|
||||
|
||||
# 8) Check that the ball didn't move too far in Y or Z directions
|
||||
target_ball.check_y_z_delta()
|
||||
|
||||
# 9) Exit game mode
|
||||
helper.exit_game_mode(Tests.__dict__["exit_game_mode_" + str(TestData.threshold_count)])
|
||||
|
||||
# 10) Run test_steps_per_threshold for each threshold value
|
||||
test_steps(1.0, (True, True, False))
|
||||
test_steps(5.0, (True, False, False))
|
||||
test_steps(10.0, (False, False, False))
|
||||
|
||||
# 11) Compare the stop locations to each other
|
||||
order = TestData.stop_locations[0].x > TestData.stop_locations[1].x > TestData.stop_locations[2].x
|
||||
Report.result(Tests.stop_locations_comparison, order)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976202_RigidBody_StopsWhenBelowKineticThreshold)
|
||||
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976204
|
||||
# Test Case Title : Verify that when Start Asleep is checked, the object in air does not fall down due to
|
||||
# gravity or does not start moving with initial linear velocity assigned to it when switched to game mode
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976204
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_terrain = ("Terrain found", "Terrain not found")
|
||||
find_sphere = ("Sphere found", "Sphere not found")
|
||||
gravity_enabled = ("Gravity is enabled", "Gravity is disabled")
|
||||
start_asleep_enabled = ("Start asleep is enabled", "Start asleep is disabled")
|
||||
sphere_position_fixed = ("Sphere position did not change", "Sphere position changed")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976204_Verify_Start_Asleep_Condition():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Verify that when Start Asleep is checked, the object in air does not fall down due to gravity or
|
||||
does not start moving with initial linear velocity assigned to it when switched to game mode
|
||||
|
||||
Level Description:
|
||||
Terrain (entity) - Entity with PhysX Terrain component
|
||||
Sphere (entity) - Entity with components PhysX Rigid Body, PhysX Collider and Rendering Mesh (sphere shape)
|
||||
initial velocity in x direction - 5 m/s
|
||||
gravity enabled, Start asleep enabled.
|
||||
|
||||
Expected Behavior:
|
||||
Nothing happens. The sphere is in its position in the air.
|
||||
We are checking if the sphere is in the same position as it was earlier.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Get the initial x,y,z positions of the sphere
|
||||
5) Check gravity, start asleep values for the entity
|
||||
6) Wait until timeout to check if sphere position changed
|
||||
7) Check the final x, y, z positions of the sphere
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 3.0 # waits for 3 seconds to verify if the sphere moved
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4976204_Verify_Start_Asleep_Condition")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
Report.critical_result(Tests.find_terrain, terrain_id.IsValid())
|
||||
Report.critical_result(Tests.find_sphere, sphere_id.IsValid())
|
||||
|
||||
# 4) Get the initial x,y,z positions of the sphere
|
||||
init_sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
|
||||
# 5) Check gravity, start asleep values for the entity
|
||||
is_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", sphere_id)
|
||||
Report.critical_result(Tests.gravity_enabled, is_gravity_enabled)
|
||||
is_awake = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsAwake", sphere_id)
|
||||
Report.critical_result(Tests.start_asleep_enabled, not is_awake)
|
||||
|
||||
# 6) Wait until timeout to check if sphere position changed
|
||||
general.idle_wait(TIME_OUT)
|
||||
|
||||
# 7) Check the final x, y, z positions of the sphere
|
||||
final_sphere_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
Report.critical_result(Tests.sphere_position_fixed, init_sphere_pos.Equal(final_sphere_pos))
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976204_Verify_Start_Asleep_Condition)
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976206
|
||||
# Test Case Title : VErify that when Gravity enables is checked, the object falls down due to gravity [sic]
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976206
|
||||
|
||||
# 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_terrain = ("Terrain entity found", "Terrain entity not found")
|
||||
gravity_started_disabled = ("Gravity started disabled", "Gravity didn't start disabled")
|
||||
gravity_set_enabled = ("Gravity has been enabled", "Gravity wasn't enabled")
|
||||
ball_fell = ("Ball fell", "Ball didn't fall")
|
||||
touched_ground = ("Ball touched the ground", "Ball did not touch the ground")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
# Wait times defined in frames for waiting while in game mode
|
||||
class Frames:
|
||||
entity_load = 2
|
||||
delta_frames = 1
|
||||
|
||||
|
||||
def C4976206_RigidBodies_GravityEnabledActive():
|
||||
"""
|
||||
Summary:
|
||||
Runs automated test to verify that when the gravity enabled checkbox is ticked,
|
||||
the object will fall down due to gravity.
|
||||
|
||||
Level Description:
|
||||
A ball entity is suspended in the air with gravity disabled by default
|
||||
Below the ball is a terrain entity with a PhysX Terrain component
|
||||
|
||||
Expected Behavior:
|
||||
The level opens and by default gravity is not enabled on the ball.
|
||||
When game mode is entered, the ball should not fall until gravity is enabled,
|
||||
then the ball should fall and collide with the terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Find the entities
|
||||
3) Make sure that gravity is turned off by default
|
||||
4) Get the Z position before enabling gravity
|
||||
5) Activate gravity
|
||||
6) Check that the ball is falling towards the terrain
|
||||
7) Check that there is a collision with the PhysX Terrain
|
||||
8) Exit game mode and close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
timeout_seconds = 3.0
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.open_level("Physics", "C4976206_RigidBodies_VerifyGravity")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve entities
|
||||
class Ball:
|
||||
id = None
|
||||
gravity_enabled = None
|
||||
starting_height = None
|
||||
current_height = None
|
||||
fell = False
|
||||
touched_ground = False
|
||||
|
||||
general.idle_wait_frames(Frames.entity_load)
|
||||
|
||||
ball_id = general.find_game_entity("RigidBody")
|
||||
Ball.id = ball_id
|
||||
Report.critical_result(Tests.find_ball, Ball.id.IsValid())
|
||||
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.critical_result(Tests.find_terrain, terrain_id.IsValid())
|
||||
|
||||
# 3) Make sure gravity is off from the start
|
||||
Ball.gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", Ball.id)
|
||||
Report.result(Tests.gravity_started_disabled, not Ball.gravity_enabled)
|
||||
|
||||
# 4) Get the Z position before enabling the physics
|
||||
Ball.starting_height = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Ball.id)
|
||||
|
||||
# 5) Activate gravity
|
||||
general.idle_wait_frames(Frames.delta_frames)
|
||||
Report.info("Enabling Gravity")
|
||||
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetGravityEnabled", Ball.id, True)
|
||||
|
||||
Ball.gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", Ball.id)
|
||||
Report.critical_result(Tests.gravity_set_enabled, Ball.gravity_enabled)
|
||||
|
||||
# 6) Compare the Z position after enabling the gravity and check that there is a collision with the PhysX Terrain
|
||||
def ball_moved_down():
|
||||
Ball.current_height = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", Ball.id)
|
||||
if Ball.current_height < (Ball.starting_height - 1):
|
||||
Ball.fell = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(terrain_id):
|
||||
Report.info("Touched ground")
|
||||
Ball.touched_ground = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(Ball.id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
helper.wait_for_condition(lambda : (ball_moved_down() and Ball.touched_ground), timeout_seconds)
|
||||
Report.info("Ball.start_height: {} Ball.current_height: {}".format(Ball.starting_height, Ball.current_height))
|
||||
Report.result(Tests.ball_fell, Ball.fell)
|
||||
Report.result(Tests.touched_ground, Ball.touched_ground)
|
||||
|
||||
# 8) Exit game mode and close the editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976206_RigidBodies_GravityEnabledActive)
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976207
|
||||
# Test Case Title : Verify that when Kinematic is checked, the object behaves as a Kinematic object
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976207
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_box = ("Box found", "Box not found")
|
||||
find_ramp = ("Ramp found", "Ramp not found")
|
||||
box_is_not_kinematic = ("Box is not kinematic", "Box is kinematic")
|
||||
ramp_is_kinematic = ("Ramp is kinematic", "Ramp is not kinematic")
|
||||
box_gravity_enabled = ("Gravity enabled on the box", "Gravity not enabled on the box")
|
||||
ramp_gravity_enabled = ("Gravity enabled on the ramp", "Gravity not enabled on the ramp")
|
||||
box_touched_ramp = ("Box touched ramp", "Box did not touch ramp")
|
||||
ramp_did_not_move = ("Ramp did not move", "Ramp moved")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976207_PhysXRigidBodies_KinematicBehavior():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure kinematic property causes entity to remain in place when gravity acts upon it
|
||||
and when another entity collides with it.
|
||||
|
||||
Level Description:
|
||||
A box (entity: Box) set above a kinematic ramp (entity: Ramp). Gravity is enabled for the the box and the ramp.
|
||||
|
||||
Expected behavior:
|
||||
The box collides with the ramp, and the ramp does not fall.
|
||||
|
||||
Test Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Retrieve entities
|
||||
3) Check for kinematic ramp and not kinematic box
|
||||
4) Check that gravity is enabled on the entities
|
||||
5) Get the initial position of the ramp
|
||||
6) Check to see that the box hits the ramp
|
||||
6.5) Wait for the box to touch the ramp or timeout
|
||||
7) Check to see that the ramp stayed at the same position
|
||||
8) Exit game mode and close editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
# Specific wait times in seconds
|
||||
TIME_OUT = 3.0
|
||||
REACTION = 0.1
|
||||
|
||||
# 1) Open level and enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4976207_PhysXRigidBodies_KinematicBehavior")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve entities
|
||||
box_id = general.find_game_entity("Box")
|
||||
Report.result(Tests.find_box, box_id.IsValid())
|
||||
|
||||
ramp_id = general.find_game_entity("Ramp")
|
||||
Report.result(Tests.find_ramp, ramp_id.IsValid())
|
||||
|
||||
# 3) Check for kinematic ramp and not kinematic box
|
||||
box_kinematic = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsKinematic", box_id)
|
||||
Report.result(Tests.box_is_not_kinematic, not box_kinematic)
|
||||
|
||||
ramp_kinematic = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsKinematic", ramp_id)
|
||||
Report.result(Tests.ramp_is_kinematic, ramp_kinematic)
|
||||
|
||||
# 4) Check that gravity is enabled on the entities
|
||||
box_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", box_id)
|
||||
Report.result(Tests.box_gravity_enabled, box_gravity_enabled)
|
||||
|
||||
ramp_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", ramp_id)
|
||||
Report.result(Tests.ramp_gravity_enabled, ramp_gravity_enabled)
|
||||
|
||||
# 5) Get the initial position of the ramp
|
||||
ramp_pos_start = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", ramp_id)
|
||||
Report.info("Ramp's initial position: {}".format(ramp_pos_start))
|
||||
|
||||
# 6) Check to see that the box hits the ramp
|
||||
class RampTouched:
|
||||
value = False
|
||||
|
||||
def on_collision_begin(args):
|
||||
other_id = args[0]
|
||||
if other_id.Equal(ramp_id):
|
||||
Report.info("Box touched ramp")
|
||||
RampTouched.value = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(box_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
# 6.5) Wait for the box to touch the ramp or timeout
|
||||
helper.wait_for_condition(lambda: RampTouched.value, TIME_OUT)
|
||||
Report.result(Tests.box_touched_ramp, RampTouched.value)
|
||||
|
||||
# 7) Check to see that the ramp stayed at the same position
|
||||
general.idle_wait(REACTION) # wait for collision reaction
|
||||
ramp_pos_end = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", ramp_id)
|
||||
Report.info("Ramp's final position: {}".format(ramp_pos_end))
|
||||
Report.result(Tests.ramp_did_not_move, ramp_pos_start.Equal(ramp_pos_end))
|
||||
|
||||
# 8) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976207_PhysXRigidBodies_KinematicBehavior)
|
||||
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976209
|
||||
# Test Case Title : Verify that when Compute COM is enabled, the PhysX system computes the COM of the object on its own
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976209
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
|
||||
find_enabled_sphere = ("Enabled Sphere Found", "Enabled Sphere not found")
|
||||
find_enabled_capsule = ("Enabled Capsule Found", "Enabled Capsule not found")
|
||||
find_enabled_box = ("Enabled Box Found", "Enabled Box not found")
|
||||
find_enabled_physics_asset = ("Enabled Physics Asset Found", "Enabled Physics Asset not found")
|
||||
|
||||
find_disabled_sphere = ("Disabled Sphere Found", "Disabled Sphere not found")
|
||||
find_disabled_capsule = ("Disabled Capsule Found", "Disabled Capsule not found")
|
||||
find_disabled_box = ("Disabled Box Found", "Disabled Box not found")
|
||||
find_disabled_physics_asset = ("Disabled Physics Asset Found", "Disabled Physics Asset not found")
|
||||
|
||||
enabled_sphere_COM_expected = ("Enabled Sphere COM was close to expected value", "Enabled Sphere COM was not close to expected value")
|
||||
enabled_capsule_COM_expected = ("Enabled Capsule COM was close to expected value", "Enabled Capsule COM was not close to expected value")
|
||||
enabled_box_COM_expected = ("Enabled Box COM was close to expected value", "Enabled Box COM was not close to expected value")
|
||||
enabled_physics_asset_COM_expected = ("Enabled Physics Asset COM was close to expected value", "Enabled Physics Asset COM was not close to expected value")
|
||||
|
||||
disabled_sphere_COM_expected = ("Disabled Sphere COM was close to expected value", "Disabled Sphere COM was not close to expected value")
|
||||
disabled_capsule_COM_expected = ("Disabled Capsule COM was close to expected value", "Disabled Capsule COM was not close to expected value")
|
||||
disabled_box_COM_expected = ("Disabled Box COM was close to expected value", "Disabled Box not COM was not close to expected value")
|
||||
disabled_physics_asset_COM_expected = ("Disabled Physics Asset COM was close to expected value", "Disabled Physics Asset not COM was close to expected value")
|
||||
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976209_RigidBody_ComputesCOM():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure when compute COM is enabled, that the COM is automatically calculated.
|
||||
|
||||
Level Description:
|
||||
There are 4 sets of entities:
|
||||
Spheres (entity: SphereEnabled), (entity: SphereDisabled)
|
||||
Boxes (entity: BoxEnabled), (entity: BoxDisabled)
|
||||
Capsules (entity: CapsuleEnabled), (entity: CapsuleDisabled)
|
||||
Physics Assets with sedan mesh (entity: PhysicsAssetEnabled), (entity: PhysicsAssetDisabled)
|
||||
|
||||
Each set has two entities, both of which are identical to one another - save for the Compute COM setting on their
|
||||
rigid body. One has it set to enabled, and the other has it set to disabled.
|
||||
|
||||
Each entity has a rigid body and two PhysX colliders. The first collider has no offset. The second collider is
|
||||
positioned +2 units in the X direction.
|
||||
|
||||
Expected Behavior:
|
||||
The entities with 'Compute COM' disabled will have local COM offsets at the objects origin ((0.0, 0.0, 0.0) Locally)
|
||||
The entities with 'Compute COM' enabled will have local COM offsets at the average position between the two
|
||||
colliders.
|
||||
For every entity that isn't a physics asset, that's +1 unit to the right (1/2 the offset value of the second
|
||||
collider). The physics asset has a mesh whose origin is not at the natural center of mass of the object,
|
||||
so we check its COM offset as a special case.
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Validate entities
|
||||
4) Check center of mass for each entity
|
||||
5) Special Case: Check center of mass for each physics asset
|
||||
6) Exit game mode
|
||||
7) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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)
|
||||
"""
|
||||
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.math as lymath
|
||||
|
||||
CONTROL_COM = lymath.Vector3(0.0, 0.0, 0.0)
|
||||
EXPECTED_COM = lymath.Vector3(1.0, 0.0, 0.0)
|
||||
PHYSICS_ASSET_EXPECTED_COM = lymath.Vector3(1.0, -0.08, 0.43) # Derived from the sedan mesh specifically
|
||||
CLOSE_THRESHOLD = 0.01
|
||||
|
||||
class Shape:
|
||||
def __init__(self, name, valid_test, com_valid_test):
|
||||
self.id = general.find_game_entity(name)
|
||||
self.center_of_mass = self.get_com()
|
||||
self.position = self.get_position()
|
||||
self.com_local = self.center_of_mass.Subtract(self.position)
|
||||
self.valid_test = valid_test
|
||||
self.com_valid_test = com_valid_test
|
||||
|
||||
def get_com(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetCenterOfMassWorld", self.id)
|
||||
|
||||
def get_position(self):
|
||||
return azlmbr.components.TransformBus(bus.Event, "GetWorldTranslation", self.id)
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4976209_RigidBody_ComputesCOM")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Validate entities
|
||||
sphere_enabled = Shape("SphereEnabled", Tests.find_enabled_sphere, Tests.enabled_sphere_COM_expected)
|
||||
sphere_disabled = Shape("SphereDisabled", Tests.find_disabled_sphere, Tests.disabled_sphere_COM_expected)
|
||||
|
||||
capsule_enabled = Shape("CapsuleEnabled", Tests.find_enabled_capsule, Tests.enabled_capsule_COM_expected)
|
||||
capsule_disabled = Shape("CapsuleDisabled", Tests.find_disabled_capsule, Tests.disabled_capsule_COM_expected)
|
||||
|
||||
box_enabled = Shape("BoxEnabled", Tests.find_enabled_box, Tests.enabled_box_COM_expected)
|
||||
box_disabled = Shape("BoxDisabled", Tests.find_disabled_box, Tests.disabled_box_COM_expected)
|
||||
|
||||
physics_asset_enabled = Shape(
|
||||
"PhysicsAssetEnabled", Tests.find_enabled_physics_asset, Tests.enabled_physics_asset_COM_expected
|
||||
)
|
||||
physics_asset_disabled = Shape(
|
||||
"PhysicsAssetDisabled", Tests.find_disabled_physics_asset, Tests.disabled_physics_asset_COM_expected
|
||||
)
|
||||
|
||||
# physics asset is a special case, and thus not included
|
||||
enabled_shapes = [sphere_enabled, capsule_enabled, box_enabled]
|
||||
disabled_shapes = [sphere_disabled, capsule_disabled, box_disabled]
|
||||
|
||||
all_shapes = enabled_shapes + disabled_shapes + [physics_asset_enabled, physics_asset_disabled]
|
||||
|
||||
for shape in all_shapes:
|
||||
Report.critical_result(shape.valid_test, shape.id.IsValid())
|
||||
|
||||
# 4) Check center of mass for each entity
|
||||
for enabled_shape in enabled_shapes:
|
||||
enabled_is_close = enabled_shape.com_local.IsClose(EXPECTED_COM, CLOSE_THRESHOLD)
|
||||
Report.result(enabled_shape.com_valid_test, enabled_is_close)
|
||||
|
||||
for disabled_shape in disabled_shapes:
|
||||
disabled_is_close = disabled_shape.com_local.IsClose(CONTROL_COM, CLOSE_THRESHOLD)
|
||||
Report.result(disabled_shape.com_valid_test, disabled_is_close)
|
||||
|
||||
# 5) Check center of mass for each physics asset
|
||||
enabled_is_close = physics_asset_enabled.com_local.IsClose(PHYSICS_ASSET_EXPECTED_COM, CLOSE_THRESHOLD)
|
||||
Report.result(physics_asset_enabled.com_valid_test, enabled_is_close)
|
||||
|
||||
disabled_is_close = physics_asset_disabled.com_local.IsClose(CONTROL_COM, CLOSE_THRESHOLD)
|
||||
Report.result(physics_asset_disabled.com_valid_test, disabled_is_close)
|
||||
|
||||
# 6) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976209_RigidBody_ComputesCOM)
|
||||
@@ -0,0 +1,309 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976210
|
||||
# Test Case Title : Verify that when Compute COM is disabled, the user gets an option to add the co-ordinates of
|
||||
# the COM and the COM gets implemented at those co-ordinates.
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976210
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
boxes_validated = ("All box entities were found and validated", "Couldn't find or validate at least one box")
|
||||
test_objects_validated = ("All test entities were found and validated", "Not all test entities could be found and validated")
|
||||
sphere_com_set = ("The Test Sphere's COM was successfully set", "The Test Sphere's COM WAS NOT successfully set")
|
||||
capsule_com_set = ("The Test Capsule's COM was successfully set", "The Test Capsule's COM WAS NOT successfully set")
|
||||
box_com_set = ("The Test Box's COM was successfully set", "The Test Box's COM WAS NOT successfully set")
|
||||
sphere_confirm = ("The Test Sphere rolled uphill due to COM offset", "The Test Sphere rolled downhill-- COM did not work as expected")
|
||||
capsule_confirm = ("The Test Capsule fell against gravity due to COM offset", "The Test Capsule fell with gravity-- COM did not work as expected")
|
||||
box_confirm = ("The Test Box fell against gravity due to COM offset", "The Test Box fell with gravity-- COM did not work as expected")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976210_COM_ManualSetting():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Tests that when an entity has "Compute COM" disabled, a user can set a custom center of mass and that
|
||||
the set center of mass behaves as expected.
|
||||
|
||||
Level Description:
|
||||
Three test objects (Box, Capsule and Sphere) are positioned on a platform that is rotated at an acute angle. Two
|
||||
plates (Pass and Fail) are perpendicular to the platform so that the Fail Plate is underneath the test objects, and
|
||||
the Pass Plate is above. Gravity is enabled for all test objects. Stationary objects (plates, platform... etc)
|
||||
have gravity disabled and have masses set for "very large numbers" (10,000 kg) to resist the test objects'
|
||||
movements. Test objects have their "compute COM" property disabled, but in the editor have a computed COM of
|
||||
(0.0, 0.0, 0.0) and gravity enabled.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode starts, the script manually sets each test objects center of mass in a way where that entity
|
||||
will "fall against gravity". The box and capsule should tilt against gravity into the Pass Plate, and the sphere
|
||||
should "roll up hill" to the Pass Plate.
|
||||
|
||||
Test Steps:
|
||||
0) Define data and functions
|
||||
1) Open level, enter game mode
|
||||
2) Find and validate entities
|
||||
3) set and verify center of mass (COM)
|
||||
4) Wait for results or timeout
|
||||
5) Log results
|
||||
6) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
|
||||
# internal editor imports
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
import azlmbr.math as azmath
|
||||
|
||||
# 0) Set up data and functions used in the test
|
||||
|
||||
# EntityData class for storing and organizing useful test information/results
|
||||
class EntityData:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = None
|
||||
self.init_pos = None
|
||||
self.current_pos = None
|
||||
self.init_rot = None
|
||||
self.current_rot = None
|
||||
self.result = None
|
||||
self.fail_info = None
|
||||
|
||||
# ***** Global variables ****
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 1.5
|
||||
CAPSULE_BOX_OFFSET = azmath.Vector3(3.0, 0.0, 0.0)
|
||||
SPHERE_OFFSET = azmath.Vector3(0.5, 0.0, 0.0)
|
||||
PASS = "pass"
|
||||
FAIL = "fail"
|
||||
|
||||
# Test entities
|
||||
test_box = EntityData("Test_Box")
|
||||
test_capsule = EntityData("Test_Capsule")
|
||||
test_sphere = EntityData("Test_Sphere")
|
||||
test_entities = [test_box, test_capsule, test_sphere]
|
||||
|
||||
# Plate entities
|
||||
pass_plate = EntityData("Pass_Plate")
|
||||
fail_plate = EntityData("Fail_Plate")
|
||||
|
||||
# All non-moving entities
|
||||
non_movers = [EntityData("Platform"), EntityData("Pass_Buffer"), EntityData("Fail_Buffer"), pass_plate, fail_plate]
|
||||
|
||||
# Full entity list
|
||||
all_entities = non_movers + test_entities
|
||||
|
||||
# ******** Helper Functions ********
|
||||
|
||||
# Attempts to set COM, and manually computes expected COM to verify it was applied correctly
|
||||
def set_and_validate_COM(entity, com_offset):
|
||||
# type: (EntityData, Vector3) -> None
|
||||
CLOSE_ENOUGH_THRESHOLD = 0.01
|
||||
# Set COM offset
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetCenterOfMassOffset", entity.id, com_offset)
|
||||
entity_world_com_pos = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetCenterOfMassWorld", entity.id)
|
||||
# Get world rotation matrix
|
||||
tm_matrix = azmath.Matrix3x3_CreateFromTransform(
|
||||
azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTM", entity.id)
|
||||
)
|
||||
# Calculate expected world COM
|
||||
world_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", entity.id)
|
||||
calculated_com = tm_matrix.MultiplyVector3(com_offset).Add(world_pos)
|
||||
# If expected COM and actual COM differ, log data collected
|
||||
if not entity_world_com_pos.IsClose(calculated_com, CLOSE_ENOUGH_THRESHOLD):
|
||||
Report.info("COM not applied to entity: {}".format(entity.name))
|
||||
Report.info_vector3(world_pos, " Entity position:")
|
||||
Report.info_vector3(com_offset, " COM offset:")
|
||||
Report.info_vector3(entity_world_com_pos, " Entity world COM:")
|
||||
Report.info_vector3(calculated_com, " Expected COM")
|
||||
return False
|
||||
return True
|
||||
|
||||
# ** Entity batch operation helpers **
|
||||
|
||||
# Attempts to validate entities' IDs and initial positions and initial rotations.
|
||||
# Returns True if all entities were successfully validated.
|
||||
# Print to the log if there are any problems retrieving vital information
|
||||
def validate_entities(entity_list):
|
||||
# type: ([EntityData]) -> int
|
||||
count = 0
|
||||
for entity in entity_list:
|
||||
valid = True
|
||||
entity.id = general.find_game_entity(entity.name)
|
||||
if not entity.id.IsValid():
|
||||
valid = False
|
||||
Report.info("Entity: {} could not be validated".format(entity.name))
|
||||
entity.init_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", entity.id)
|
||||
entity.current_pos = entity.init_pos
|
||||
if entity.init_pos is None or entity.init_pos.IsZero():
|
||||
valid = False
|
||||
Report.info("Entity: {}'s initial position could not be found".format(entity.name))
|
||||
entity.init_rot = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", entity.id)
|
||||
entity.current_rot = entity.init_rot
|
||||
if entity.init_rot is None:
|
||||
valid = False
|
||||
Report.info("Entity: {}'s initial rotation could not be found".format(entity.name))
|
||||
if valid:
|
||||
count += 1
|
||||
return count == len(entity_list)
|
||||
|
||||
# Updates entities' current position
|
||||
def update_pos_and_rot(entity_list):
|
||||
# type: ([EntityData]) -> None
|
||||
for entity in entity_list:
|
||||
entity.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", entity.id)
|
||||
entity.current_rot = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldRotation", entity.id)
|
||||
|
||||
# Checks for unexpected movement in stationary objects
|
||||
# Prints to the log if there is a difference between initial position and current position
|
||||
def check_for_unexpected_movement(entity_list):
|
||||
# type: ([EntityData]) -> None
|
||||
CLOSE_ENOUGH_THRESHOLD = 0.1
|
||||
for entity in entity_list:
|
||||
if not entity.init_pos.IsClose(entity.current_pos, CLOSE_ENOUGH_THRESHOLD):
|
||||
entity.result = FAIL
|
||||
entity.fail_info = "Unexpected movement detected"
|
||||
|
||||
# Checks if we have enough information to end the test
|
||||
def done_collecting_results(entity_list, num_results):
|
||||
# type: ([EntityData], int) -> bool
|
||||
result_count = 0
|
||||
for entity in entity_list:
|
||||
if entity.result is not None:
|
||||
result_count += 1
|
||||
|
||||
# when all spheres have a result we are done
|
||||
if result_count == num_results:
|
||||
return True
|
||||
return False
|
||||
|
||||
# ****** single update function to pass ******
|
||||
# to helper.wait_for_condition()
|
||||
|
||||
# Should be called every frame:
|
||||
# Updates all entities positions and rotations
|
||||
# Returns True if exit conditions for test are met
|
||||
# see @done_collecting_results(..) for exit conditions
|
||||
def is_done_updating():
|
||||
# type: () -> bool
|
||||
update_pos_and_rot(all_entities)
|
||||
check_for_unexpected_movement(non_movers)
|
||||
return done_collecting_results(test_entities, len(test_entities))
|
||||
|
||||
# ******** Event Handlers ********
|
||||
|
||||
# Fail A Box collision event handler
|
||||
def on_collision_begin_fail(args):
|
||||
# type: ([EntityId, ...]) -> None
|
||||
collider_id = args[0]
|
||||
for entity in test_entities:
|
||||
if entity.id.Equal(collider_id) and entity.result is None:
|
||||
entity.result = FAIL
|
||||
entity.fail_info = "Collide with fail plate: COM did not apply properly"
|
||||
return
|
||||
|
||||
# Pass Box Collision Event Handler
|
||||
def on_collision_begin_pass(args):
|
||||
# type: ([EntityId, ...]) -> None
|
||||
collider_id = args[0]
|
||||
for entity in test_entities:
|
||||
if entity.id.Equal(collider_id) and entity.result is None:
|
||||
Report.info("Entity: {} collided with the Pass Plate".format(entity.name))
|
||||
entity.result = PASS
|
||||
return
|
||||
# it wasn't a test entity that collided, something went wrong
|
||||
if collider_id.IsValid():
|
||||
entity_name = azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "GetEntityName", collider_id)
|
||||
Report.info("Pass Plate collided with unexpected entity: {}".format(entity_name))
|
||||
|
||||
# ******** Execution Code *********
|
||||
|
||||
# 1) Open level and start game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4976210_COM_ManualSetting")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Find and validate test entities
|
||||
Report.critical_result(Tests.test_objects_validated, validate_entities(test_entities))
|
||||
Report.critical_result(Tests.boxes_validated, validate_entities(non_movers))
|
||||
|
||||
# 3) Set and verify COM offsets
|
||||
Report.critical_result(Tests.box_com_set, set_and_validate_COM(test_box, CAPSULE_BOX_OFFSET))
|
||||
Report.critical_result(Tests.capsule_com_set, set_and_validate_COM(test_capsule, CAPSULE_BOX_OFFSET))
|
||||
Report.critical_result(Tests.sphere_com_set, set_and_validate_COM(test_sphere, SPHERE_OFFSET))
|
||||
|
||||
# Assign handlers
|
||||
pass_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
pass_handler.connect(pass_plate.id)
|
||||
pass_handler.add_callback("OnCollisionBegin", on_collision_begin_pass)
|
||||
|
||||
fail_handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
fail_handler.connect(fail_plate.id)
|
||||
fail_handler.add_callback("OnCollisionBegin", on_collision_begin_fail)
|
||||
|
||||
# 4) wait for either time out or for the test to complete
|
||||
if not helper.wait_for_condition(is_done_updating, TIME_OUT):
|
||||
Report.info("The test timed out, check log for possible solutions or adjust the time out time")
|
||||
|
||||
# 5) Log results
|
||||
|
||||
# verify entities results
|
||||
Report.result(Tests.box_confirm, test_box.result is not None and test_box.result == PASS)
|
||||
Report.result(Tests.capsule_confirm, test_capsule.result is not None and test_capsule.result == PASS)
|
||||
Report.result(Tests.sphere_confirm, test_sphere.result is not None and test_sphere.result == PASS)
|
||||
|
||||
# Data dump at bottom of log
|
||||
Report.info("******** Collected Data *********")
|
||||
for entity in all_entities:
|
||||
Report.info("Entity: {}".format(entity.name))
|
||||
Report.info_vector3(entity.init_pos, " Initial position:")
|
||||
Report.info_vector3(entity.current_pos, " Final position:")
|
||||
Report.info_vector3(entity.init_rot, " Initial rotation:")
|
||||
Report.info_vector3(entity.current_rot, " Final rotation:")
|
||||
Report.info(" Result: {}".format(entity.result))
|
||||
if entity.fail_info is not None:
|
||||
Report.info(" Fail info: {}".format(entity.fail_info))
|
||||
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 utils import Report
|
||||
Report.start_test(C4976210_COM_ManualSetting)
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
# Test case ID : C4976218
|
||||
# Test Case Title: Verify that when compute inertia is checked, the physX engine does compute the inertia of the objects
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976218
|
||||
# fmt: off
|
||||
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_upper_boxes = ("Upper Boxes found", "Upper Boxes not found")
|
||||
find_lower_boxes = ("Lower Boxes found", "Lower Boxes not found")
|
||||
boxes_collided = ("All Boxes Collided", "Not all Boxes Collided")
|
||||
upper_box_x_did_topple = ("Upper Box X did topple", "Upper Box X did not toppled")
|
||||
upper_box_y_did_topple = ("Upper Box Y did topple", "Upper Box Y did not toppled")
|
||||
upper_box_z_did_topple = ("Upper Box Z did topple", "Upper Box Z did not toppled")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
def C4976218_RigidBodies_InertiaObjectsNotComputed():
|
||||
"""
|
||||
Summary:
|
||||
Level Description:
|
||||
A box (entity: Upper Box) set above and askew to another box (entity: Lower Box)
|
||||
Lower box and Upper box has computed inertia.
|
||||
Test Steps:
|
||||
--> Open level
|
||||
--> Enter game mode
|
||||
--> Retrieve entities
|
||||
--> Check for collision between the top box and lower box for the 3 pairs (6 boxes total)
|
||||
--> Check that the upper box's x, y, or z angular velocity decreases past -1.0
|
||||
--> Exit game mode
|
||||
--> Close editor
|
||||
:return: None
|
||||
"""
|
||||
# Setup path
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus as bus
|
||||
import azlmbr.components
|
||||
import azlmbr.physics
|
||||
|
||||
from utils import Report, TestHelper as helper
|
||||
|
||||
class UpperBox:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.id = general.find_game_entity(name)
|
||||
self.collided_with_lower_box = False
|
||||
self.handler = None
|
||||
self.lower_box_list = None
|
||||
|
||||
def on_boxes_collision_begin(self, args):
|
||||
other_id = args[0]
|
||||
for boxes in self.lower_box_list:
|
||||
if other_id.Equal(boxes.id):
|
||||
Report.info("{} hit lower box".format(self.name))
|
||||
self.collided_with_lower_box = True
|
||||
|
||||
def create_handler(self):
|
||||
self.handler = bus.NotificationHandler("CollisionNotificationBus")
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_boxes_collision_begin)
|
||||
|
||||
@property
|
||||
def angular_velocity(self):
|
||||
return azlmbr.physics.RigidBodyRequestBus(bus.Event, "GetAngularVelocity", self.id)
|
||||
|
||||
class LowerBox:
|
||||
def __init__(self, name):
|
||||
self.id = general.find_game_entity(name)
|
||||
|
||||
# Amount of time before test times out
|
||||
TIME_OUT = 3.0
|
||||
MINIMUM_ANGUlAR_VELOCITY = -1.0
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4976218_RigidBodies_InertiaObjectNotComputed")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve entities
|
||||
upper_box_x = UpperBox("Upper Box X")
|
||||
upper_box_y = UpperBox("Upper Box Y")
|
||||
upper_box_z = UpperBox("Upper Box Z")
|
||||
|
||||
lower_box_x = LowerBox("Lower Box X")
|
||||
lower_box_y = LowerBox("Lower Box Y")
|
||||
lower_box_z = LowerBox("Lower Box Z")
|
||||
|
||||
# Store boxes in two lists
|
||||
upper_box_list = [upper_box_x, upper_box_y, upper_box_z]
|
||||
lower_box_list = [lower_box_x, lower_box_y, lower_box_z]
|
||||
|
||||
|
||||
upper_boxes_found = True
|
||||
lower_boxes_found = True
|
||||
|
||||
for upper_boxes in upper_box_list:
|
||||
upper_boxes.lower_box_list = lower_box_list
|
||||
if upper_boxes.id is None:
|
||||
upper_boxes_found = False
|
||||
|
||||
for lower_boxes in lower_box_list:
|
||||
if lower_boxes.id is None:
|
||||
lower_boxes_found = False
|
||||
|
||||
Report.critical_result(Tests.find_upper_boxes, upper_boxes_found)
|
||||
Report.critical_result(Tests.find_lower_boxes, lower_boxes_found)
|
||||
|
||||
for box in upper_box_list:
|
||||
box.create_handler()
|
||||
|
||||
def all_boxes_collided():
|
||||
for boxes in upper_box_list:
|
||||
if not boxes.collided_with_lower_box:
|
||||
return False
|
||||
return True
|
||||
|
||||
# 4) Wait for the boxes to collide
|
||||
boxes_collided = helper.wait_for_condition(all_boxes_collided, TIME_OUT)
|
||||
Report.result(Tests.boxes_collided, boxes_collided)
|
||||
|
||||
# 5) Check that the upper box's corresponding angular velocity is lower than minimum (higher negative number)
|
||||
def validate_angular_velocities():
|
||||
velocities_set = True
|
||||
if upper_box_x.angular_velocity.x > MINIMUM_ANGUlAR_VELOCITY:
|
||||
velocities_set = False
|
||||
if upper_box_y.angular_velocity.y > MINIMUM_ANGUlAR_VELOCITY:
|
||||
velocities_set = False
|
||||
if upper_box_z.angular_velocity.z > MINIMUM_ANGUlAR_VELOCITY:
|
||||
velocities_set = False
|
||||
return velocities_set
|
||||
|
||||
helper.wait_for_condition(validate_angular_velocities, TIME_OUT)
|
||||
|
||||
# Checking to see if the X angular has a greater negative number (higher negative value) than MINIMUM
|
||||
Report.info("Angular Velocity for X Box = {}".format(upper_box_x.angular_velocity.x))
|
||||
Report.info("Angular Velocity for Y Box = {}".format(upper_box_y.angular_velocity.y))
|
||||
Report.info("Angular Velocity for Z Box = {}".format(upper_box_z.angular_velocity.z))
|
||||
|
||||
Report.result(Tests.upper_box_x_did_topple, upper_box_x.angular_velocity.x < MINIMUM_ANGUlAR_VELOCITY)
|
||||
Report.result(Tests.upper_box_y_did_topple, upper_box_y.angular_velocity.y < MINIMUM_ANGUlAR_VELOCITY)
|
||||
Report.result(Tests.upper_box_z_did_topple, upper_box_z.angular_velocity.z < MINIMUM_ANGUlAR_VELOCITY)
|
||||
|
||||
# 7) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976218_RigidBodies_InertiaObjectsNotComputed)
|
||||
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C4976227
|
||||
# Test Case Title : Validate that a Collision Group can be added
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976227
|
||||
# Level has entity with custom collision group added.
|
||||
# If level enters game mode, collision group addition is validated.
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_sphere = ("Sphere entity found", "Sphere entity not found")
|
||||
collision_group = ("Collision group addition validated", "Collision group addition not valid")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976227_Collider_NewGroup():
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure that a collision group can be added.
|
||||
|
||||
Level Description:
|
||||
Sphere (Entity) - PhysX Collider(shape:sphere): Collision Layer (Default), Collides With (Test_Group)
|
||||
|
||||
Test_Group (Collision Group) - Collision Group that is custom made for this test.
|
||||
Requires a custom ".physxconfiguration" file in addition to the level file
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered the entity id should be valid and position should be found
|
||||
|
||||
Test Steps:
|
||||
1) Open Level
|
||||
2) Enter game mode
|
||||
3) Validate entities
|
||||
4) Exit game mode
|
||||
5) Close Editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Open Level
|
||||
helper.open_level("Physics", "C4976227_Collider_NewGroup")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Validate entities
|
||||
sphere_id = general.find_game_entity("Sphere")
|
||||
|
||||
Report.result(Tests.find_sphere, sphere_id.IsValid())
|
||||
|
||||
sphere_position = None
|
||||
sphere_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", sphere_id)
|
||||
|
||||
Report.result(Tests.collision_group, sphere_id.isValid() and sphere_position != None)
|
||||
|
||||
# 4) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976227_Collider_NewGroup)
|
||||
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
Test case ID : C4976236
|
||||
Test Case Title : Verify that you can add the physX collider component to an entity
|
||||
without it throwing an error or warning
|
||||
URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976236
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
create_test_entity = ("Entity created successfully", "Failed to create Entity")
|
||||
add_physx_collider = ("PhysX Collider component added", "Failed to add PhysX Collider component")
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
|
||||
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976236_AddPhysxColliderComponent():
|
||||
"""
|
||||
Summary:
|
||||
Load level with Entity having PhysX Collider component. Verify that editor remains stable in Game mode.
|
||||
|
||||
Expected Behavior:
|
||||
The Editor is stable there are no warnings or errors.
|
||||
|
||||
Test Steps:
|
||||
1) Load the level
|
||||
2) Create test entity
|
||||
3) Start the Tracer to catch any errors and warnings
|
||||
4) Add the PhysX Collider component and change shape to box
|
||||
5) Add Mesh component and an asset
|
||||
6) Enter game mode
|
||||
7) Verify there are no errors and warnings in the logs
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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
|
||||
"""
|
||||
|
||||
# Helper file Imports
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
from utils import Tracer
|
||||
from editor_entity_utils import EditorEntity
|
||||
from asset_utils import Asset
|
||||
|
||||
helper.init_idle()
|
||||
# 1) Load the 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) Start the Tracer to catch any errors and warnings
|
||||
with Tracer() as section_tracer:
|
||||
# 4) Add the PhysX Collider component and change shape to box
|
||||
collider_component = test_entity.add_component("PhysX Collider")
|
||||
Report.result(Tests.add_physx_collider, test_entity.has_component("PhysX Collider"))
|
||||
collider_component.set_component_property_value('Shape Configuration|Shape', 1)
|
||||
|
||||
# 5) Add Mesh component and an asset
|
||||
mesh_component = test_entity.add_component("Mesh")
|
||||
asset = Asset.find_asset_by_path(r"Objects\default\primitive_cube.cgf")
|
||||
mesh_component.set_component_property_value('MeshComponentRenderNode|Mesh asset', asset.id)
|
||||
|
||||
# 6) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 7) Verify there are no errors and warnings in the logs
|
||||
success_condition = not (section_tracer.has_errors or section_tracer.has_warnings)
|
||||
Report.result(Tests.no_errors_and_warnings_found, success_condition)
|
||||
if not success_condition:
|
||||
if section_tracer.has_warnings:
|
||||
Report.info(f"Warnings found: {section_tracer.warnings}")
|
||||
if section_tracer.has_errors:
|
||||
Report.info(f"Errors found: {section_tracer.errors}")
|
||||
Report.failure(Tests.no_errors_and_warnings_found)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976236_AddPhysxColliderComponent)
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976242
|
||||
# Test Case Title : Assign same collision layer and same collision group to two entities and
|
||||
# verify that they collide or not
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976242
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_moving = ("Moving entity found", "Moving entity not found")
|
||||
find_stationary = ("Stationary entity found", "Stationary entity not found")
|
||||
find_terrain = ("Terrain entity found", "Terrain entity not found")
|
||||
stationary_above_terrain = ("Stationary is above terrain", "Stationary is not above terrain")
|
||||
moving_above_stationary = ("Moving is above stationary", "Moving is not above stationary")
|
||||
gravity_works = ("Moving Sphere fell down", "Moving Sphere did not fall")
|
||||
collisions = ("Collision occurred in between entities", "Collision did not occur between entities")
|
||||
falls_below_terrain_height = ("Moving is below terrain", "Moving did not fall below terrain before timeout")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976242_Collision_SameCollisionlayerSameCollisiongroup():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Open a Project that already has two entities with same collision layer and same collision group and verify collision
|
||||
|
||||
Level Description:
|
||||
Moving and Stationary entities are created in level with same collision layer and same collision group.
|
||||
Moving entity is placed above the Stationary entity.Terrain is placed below the Stationary entity.
|
||||
So Moving and Stationary entities collide with each other and they go through terrain after collision.
|
||||
|
||||
Expected Behavior:
|
||||
The Moving and Stationary entities should collide with each other.After Collision,they go through terrain.
|
||||
|
||||
Test Steps:
|
||||
1) Open level and Enter game mode
|
||||
2) Retrieve and validate Entities
|
||||
3) Get the starting z position of the Moving entity,Stationary entity and Terrain
|
||||
4) Check and report that the entities are at the correct heights before collision
|
||||
5) Check that the gravity works and the Moving entity falls down
|
||||
6) Check Spheres collide only with each other, but not with terrain
|
||||
7) Check Moving Entity should be below terrain after collision
|
||||
8) Exit game mode
|
||||
9) Close the editor
|
||||
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 2.0
|
||||
TERRAIN_HEIGHT = 32.0 # Default height of the terrain
|
||||
MIN_BELOW_TERRAIN = 0.5 # Minimum height below terrain the sphere must be in order to be 'under' it
|
||||
CLOSE_ENOUGH_THRESHOLD = 0.0001
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level and Enter game mode
|
||||
helper.open_level("Physics", "C4976242_Collision_SameCollisionlayerSameCollisiongroup")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve and validate Entities
|
||||
moving_id = general.find_game_entity("Sphere_Moving")
|
||||
Report.critical_result(Tests.find_moving, moving_id.IsValid())
|
||||
|
||||
stationary_id = general.find_game_entity("Sphere_Stationary")
|
||||
Report.critical_result(Tests.find_stationary, stationary_id.IsValid())
|
||||
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.critical_result(Tests.find_terrain, terrain_id.IsValid())
|
||||
|
||||
# 3) Get the starting z position of the Moving entity,Stationary entity and Terrain
|
||||
class Sphere:
|
||||
"""
|
||||
Class to hold values for test checks.
|
||||
Attributes:
|
||||
start_position_z: The initial z position of the sphere
|
||||
position_z : The z position of the sphere
|
||||
fell : When the sphere falls any distance below its original position, the value should be set True
|
||||
below_terrain : When the box falls below the specified terrain height, the value should be set True
|
||||
"""
|
||||
|
||||
start_position_z = None
|
||||
position_z = None
|
||||
fell = False
|
||||
below_terrain = False
|
||||
|
||||
Sphere.start_position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", moving_id)
|
||||
stationary_start_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", stationary_id)
|
||||
|
||||
# 4)Check and report that the entities are at the correct heights before collision
|
||||
Report.info(
|
||||
"Terrain Height: {} \n Stationary Sphere height: {} \n Moving Sphere height: {}".format(
|
||||
TERRAIN_HEIGHT, stationary_start_z, Sphere.start_position_z
|
||||
)
|
||||
)
|
||||
Report.result(Tests.stationary_above_terrain, TERRAIN_HEIGHT < (stationary_start_z - CLOSE_ENOUGH_THRESHOLD))
|
||||
Report.result(
|
||||
Tests.moving_above_stationary, stationary_start_z < (Sphere.start_position_z - CLOSE_ENOUGH_THRESHOLD)
|
||||
)
|
||||
|
||||
# 5)Check that the gravity works and the Moving entity falls down
|
||||
def sphere_fell():
|
||||
if not Sphere.fell:
|
||||
Sphere.position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", moving_id)
|
||||
if Sphere.position_z < (Sphere.start_position_z - CLOSE_ENOUGH_THRESHOLD):
|
||||
Report.info("Sphere position is now lower than the starting position")
|
||||
Sphere.fell = True
|
||||
return Sphere.fell
|
||||
|
||||
helper.wait_for_condition(sphere_fell, TIMEOUT)
|
||||
Report.result(Tests.gravity_works, Sphere.fell)
|
||||
|
||||
# 6) Check Spheres collide only with each other, but not with terrain
|
||||
class Collision:
|
||||
entity_collision = False
|
||||
terrain_collision = False
|
||||
|
||||
class CollisionHandler:
|
||||
def __init__(self, id, func):
|
||||
self.id = id
|
||||
self.func = func
|
||||
self.create_collision_handler()
|
||||
|
||||
def on_collision_begin(self, args):
|
||||
self.func(args[0])
|
||||
|
||||
def create_collision_handler(self):
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
def on_collision_terrain(other_id):
|
||||
Collision.terrain_collision = True
|
||||
Report.info("Collision occured in between Moving or Stationary entity with Terrain")
|
||||
|
||||
def on_moving_entity_collision(other_id):
|
||||
if other_id.Equal(stationary_id):
|
||||
Collision.entity_collision = True
|
||||
|
||||
# collision handler for entities
|
||||
CollisionHandler(terrain_id, on_collision_terrain)
|
||||
CollisionHandler(moving_id, on_moving_entity_collision)
|
||||
# wait till timeout to check for any collisions happening in the level
|
||||
helper.wait_for_condition(lambda: Collision.entity_collision, TIMEOUT)
|
||||
Report.result(Tests.collisions, Collision.entity_collision and not Collision.terrain_collision)
|
||||
|
||||
# 7)Check Moving Entity should be below terrain after collision
|
||||
def sphere_below_terrain():
|
||||
if not Sphere.below_terrain:
|
||||
Sphere.position_z = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", moving_id)
|
||||
if Sphere.position_z < (TERRAIN_HEIGHT - MIN_BELOW_TERRAIN):
|
||||
Sphere.below_terrain = True
|
||||
return Sphere.below_terrain
|
||||
|
||||
sphere_under_terrain = helper.wait_for_condition(sphere_below_terrain, TIMEOUT)
|
||||
Report.result(Tests.falls_below_terrain_height, sphere_under_terrain)
|
||||
|
||||
# 8) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976242_Collision_SameCollisionlayerSameCollisiongroup)
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976243
|
||||
# Test Case Title : Assign different collision layers and same collision group
|
||||
# (such that this group has both these collision layers enabled) to two entities and verify that they collide
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976243
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests():
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
find_terrain = ("Terrain found", "Terrain not found")
|
||||
find_entity1 = ("Entity1 found", "Entity1 not found")
|
||||
find_entity2 = ("Entity2 found", "Entity2 not found")
|
||||
gravity_enabled = ("Gravity is enabled", "Gravity is disabled")
|
||||
gravity_disabled = ("Gravity is disabled", "Gravity is enabled")
|
||||
collision_occurance = ("Entity1 and Entity2 collided", "Entity1 and Entity2 did not collide")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976243_Collision_SameCollisionGroupDiffCollisionLayers():
|
||||
|
||||
"""
|
||||
Summary:
|
||||
Assign different collision layers and same collision group (such that this group has both these
|
||||
collision layers enabled) to two entities and verify that they collide
|
||||
|
||||
Level Description:
|
||||
Entity1 (entity) - Entity with components PhysX Rigid Body, PhysX Collider, Terrain and Rendering Mesh
|
||||
"Collision Layer" as "A" and "Collides with" as "B" with gravity enabled.
|
||||
Entity1 is placed exactly above Terrain and Entity2 along z axis
|
||||
Entity2 (entity) - Entity with components PhysX Rigid Body, PhysX Collider, Terrain and Rendering Mesh
|
||||
"Collision Layer" as "Default" and "Collides with" as "B" with gravity disabled
|
||||
Entity2 is placed exactly in between the Terrain and Entity1 along z axis
|
||||
Terrain (entity) - Entity with Terrain component.
|
||||
"Collision Layer" as "Default" and "Collides with" as "All"
|
||||
|
||||
Expected Behavior:
|
||||
Created entities should collide with each other.
|
||||
We are checking created entities collided (Entity1 and Entity2)
|
||||
|
||||
Test Steps:
|
||||
1) Open level
|
||||
2) Enter game mode
|
||||
3) Retrieve and validate entities
|
||||
4) Check if gravity is enabled for entity1 and disabled for entity2
|
||||
5) Create collision event handlers
|
||||
6) Check for collisions between entities
|
||||
7) Exit game mode
|
||||
8) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# Constants
|
||||
TIMEOUT = 2 # waits for 2 secs to verify if the collision occured
|
||||
|
||||
helper.init_idle()
|
||||
|
||||
# 1) Open level
|
||||
helper.open_level("Physics", "C4976243_Collision_SameCollisionGroupDiffCollisionLayers")
|
||||
|
||||
# 2) Enter game mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Retrieve and validate entities
|
||||
terrain_id = general.find_game_entity("Terrain")
|
||||
Report.info(dir(terrain_id))
|
||||
entity1_id = general.find_game_entity("Entity1")
|
||||
entity2_id = general.find_game_entity("Entity2")
|
||||
Report.critical_result(Tests.find_terrain, terrain_id.IsValid())
|
||||
Report.critical_result(Tests.find_entity1, entity1_id.IsValid())
|
||||
Report.critical_result(Tests.find_entity2, entity2_id.IsValid())
|
||||
|
||||
# 4) Check if gravity is enabled for entity1 and disabled for entity2
|
||||
is_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", entity1_id)
|
||||
Report.info("Gravity check for entity1")
|
||||
Report.result(Tests.gravity_enabled, is_gravity_enabled)
|
||||
is_gravity_enabled = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "IsGravityEnabled", entity2_id)
|
||||
Report.info("Gravity check for entity2")
|
||||
Report.result(Tests.gravity_disabled, not is_gravity_enabled)
|
||||
|
||||
class Collision:
|
||||
entity_collision = False
|
||||
|
||||
# 5) Create collision event handler
|
||||
def on_collision_begin(args):
|
||||
if args[0].Equal(entity1_id):
|
||||
Report.info("Collision occurred")
|
||||
Collision.entity_collision = True
|
||||
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(entity2_id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
# 6) Check for collisions between entities
|
||||
helper.wait_for_condition(lambda: Collision.entity_collision, TIMEOUT)
|
||||
Report.critical_result(Tests.collision_occurance, Collision.entity_collision)
|
||||
|
||||
# 7) Exit game mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976243_Collision_SameCollisionGroupDiffCollisionLayers)
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976244
|
||||
# Test Case Title : Checks that two entities of similar custom layer collide
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976244
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
moving_sphere_found = ("Moving sphere found", "Moving sphere not found")
|
||||
stationary_sphere_found = ("Stationary sphere found", "Stationary sphere not found")
|
||||
velocities_before_collision_valid = ("Sphere velocities are valid", "Sphere velocities are not valid")
|
||||
orientation_before_collision = ("Both spheres are aligned properly", "Spheres are not aligned properly")
|
||||
spheres_collided = ("Collision was detected", "A collision was not detected")
|
||||
orientation_after_collision = ("Spheres are aligned properly", "Spheres are not aligned properly")
|
||||
velocities_after_collision_valid = ("Velocity after collision valid", "Velocity after collision not valid")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976244_Collider_SameGroupSameLayerCollision():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure to rigid bodies on similar collision layer and group collide.
|
||||
|
||||
Level Description:
|
||||
Moving Sphere (Entity) - On the same x axis as the Stationary Sphere, moving in the positive x direction;
|
||||
has sphere shaped PhysX Collider, PhysX Rigid Body, Sphere Shape. Collision Group All, layer A
|
||||
Stationary Sphere (Entity) - On the same x axis as the Moving Sphere;
|
||||
has sphere shaped PhysX Collider, PhysX Rigid Body, Sphere Shape Collision Group All, layer A
|
||||
|
||||
Expected Behavior: The moving sphere will move torward the stationary sphere in the positive x direction
|
||||
and collide with it. Both spheres will then separate and move in opposite directions.
|
||||
|
||||
Test Steps:
|
||||
1) Load the level
|
||||
2) Enter Game Mode
|
||||
3) Validate entities
|
||||
4) Validate positions and velocities
|
||||
5) Start handlers
|
||||
6) Wait for collision
|
||||
7) Validated and logs results
|
||||
8) Exit Game Mode
|
||||
9) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from 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
|
||||
# Callback function for the collision handler
|
||||
def on_collision_begin(args):
|
||||
# type (list) -> None
|
||||
Report.info("Collision Occurred")
|
||||
other_id = args[0]
|
||||
if other_id.Equal(moving_sphere.id):
|
||||
stationary_sphere.collision_happened = True
|
||||
|
||||
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
|
||||
self.collision_happened = False
|
||||
Report.info_vector3(self.initial_position, "{} initial position: ".format(self.name))
|
||||
Report.info_vector3(self.initial_velocity, "{} initial velocity: ".format(self.name))
|
||||
|
||||
def get_final_position_and_velocity(self):
|
||||
# type () -> None
|
||||
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_final_values(self):
|
||||
# type () -> None
|
||||
Report.info_vector3(self.final_position, "{} final position: ".format(self.name))
|
||||
Report.info_vector3(self.final_velocity, "{} final velocity: ".format(self.name))
|
||||
|
||||
def moving_in_x_direction(self, positive_x_direction):
|
||||
# type (bool) -> bool
|
||||
velocity_vector = azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "GetLinearVelocity", self.id)
|
||||
|
||||
if positive_x_direction:
|
||||
correct_direction = velocity_vector.x > 0
|
||||
else:
|
||||
correct_direction = velocity_vector.x < 0
|
||||
return (
|
||||
correct_direction and abs(velocity_vector.y) < FLOAT_THRESHOLD and abs(velocity_vector.z) < FLOAT_THRESHOLD
|
||||
)
|
||||
|
||||
# Checks if spheres are in the correct orientation
|
||||
def validate_positions(moving_entity_position, stationary_entity_position):
|
||||
# type (Vector3, Vector3) -> bool
|
||||
return (
|
||||
abs(moving_entity_position.z - stationary_entity_position.z) < FLOAT_THRESHOLD
|
||||
and abs(moving_entity_position.y - stationary_entity_position.y) < FLOAT_THRESHOLD
|
||||
and moving_entity_position.x < stationary_entity_position.x
|
||||
)
|
||||
|
||||
# Main Script
|
||||
# 1) Load the level
|
||||
helper.init_idle()
|
||||
helper.open_level("physics", "C4976244_Collider_SameGroupSameLayerCollision")
|
||||
|
||||
# 2) Enter Game Mode
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 3) Validate entities
|
||||
moving_sphere = Entity("Moving_Sphere")
|
||||
stationary_sphere = Entity("Stationary_Sphere")
|
||||
|
||||
Report.critical_result(Tests.moving_sphere_found, moving_sphere.id.isValid())
|
||||
Report.critical_result(Tests.stationary_sphere_found, stationary_sphere.id.isValid())
|
||||
|
||||
# 4) Validate positions and velocities
|
||||
Report.critical_result(
|
||||
Tests.orientation_before_collision,
|
||||
validate_positions(moving_sphere.initial_position, stationary_sphere.initial_position),
|
||||
)
|
||||
Report.critical_result(
|
||||
Tests.velocities_before_collision_valid,
|
||||
moving_sphere.moving_in_x_direction(positive_x_direction = True)
|
||||
and stationary_sphere.initial_velocity.IsZero(FLOAT_THRESHOLD),
|
||||
)
|
||||
|
||||
# 5) Start handler
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(stationary_sphere.id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
# 6) Wait for collision
|
||||
helper.wait_for_condition(lambda: stationary_sphere.collision_happened, TIMEOUT)
|
||||
|
||||
# 7) Validated and logs results
|
||||
Report.result(Tests.spheres_collided, stationary_sphere.collision_happened)
|
||||
moving_sphere.get_final_position_and_velocity()
|
||||
stationary_sphere.get_final_position_and_velocity()
|
||||
|
||||
Report.result(
|
||||
Tests.orientation_after_collision,
|
||||
validate_positions(moving_sphere.final_position, stationary_sphere.final_position),
|
||||
)
|
||||
Report.result(
|
||||
Tests.velocities_after_collision_valid,
|
||||
moving_sphere.moving_in_x_direction(positive_x_direction = False) and stationary_sphere.moving_in_x_direction(positive_x_direction = True)
|
||||
)
|
||||
|
||||
moving_sphere.report_final_values()
|
||||
stationary_sphere.report_final_values()
|
||||
|
||||
# 8) Exit Game Mode
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C4976244_Collider_SameGroupSameLayerCollision)
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4976245
|
||||
# Test Case Title : Check that two entities of collision group "None" do not collide,
|
||||
# even though they have the same collision layer
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4976245
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
moving_entity_found = ("Moving entity found", "Moving entity not found")
|
||||
stationary_entity_found = ("Stationary entity found", "Stationary entity not found")
|
||||
moving_pos_found = ("Moving sphere position found", "Moving sphere position not found")
|
||||
stationary_pos_found = ("Stationary sphere position found", "Stationary sphere position not found")
|
||||
spheres_share_x_axis = ("Both spheres are aligned properly", "Spheres are not aligned properly")
|
||||
spheres_not_collided = ("No collision was detected", "A collision was detected")
|
||||
spheres_switched_sides = ("The moving sphere passed through the other", "Moving sphere did not pass through")
|
||||
no_y_movement = ("There was no Y movement", "Some Y movement was detected")
|
||||
no_z_movement = ("There was no Z movement", "Some Z movement was detected")
|
||||
timed_out = ("Test did not time out", "Test TIMED OUT")
|
||||
stationary_didnt_move = ("Stationary sphere did not move", "Stationary sphere moved")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4976245_PhysXCollider_CollisionLayerTest():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure to rigid bodies on the same collision layer, but no collision group
|
||||
DO NOT collide.
|
||||
|
||||
Level Description:
|
||||
Moving (entity) - a spherical entity (colored yellow) that is set up with a collision layer of "demo1"
|
||||
collision group of "None" gravity as disabled and an initial velocity of (3, 0, 0).
|
||||
Stationary (entity) - a spherical entity (colored purple) that is set up with a collision layer of "demo1"
|
||||
collision group of "None" gravity disabled, and is positioned at location (+2, 0, 0) relative to
|
||||
Moving's starting position with no initial velocity.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, Moving will begin moving in the positive X direction. The entity should pass through
|
||||
Stationary with no collision detection triggered.
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level / Enter game mode
|
||||
2) Retrieve test entities
|
||||
3) Ensures that the test objects (Moving and Stationary) are located
|
||||
4) set up variables and handlers
|
||||
5) Wait for Moving to pass through Stationary
|
||||
6) Logs results
|
||||
7) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# **** Helper class ****
|
||||
|
||||
class Sphere:
|
||||
def __init__(self, name):
|
||||
self.id = None
|
||||
self.name = name
|
||||
self.initial_pos = None
|
||||
self.current_position = None
|
||||
|
||||
# Tests the deltas for for significant change. Prints a message to log if change is detected.
|
||||
# returns True if no change detected between both deltas-- False otherwise
|
||||
def no_movement(delta_moving, delta_stationary, axis):
|
||||
result = True
|
||||
if delta_moving > CLOSE_ENOUGH_THRESHOLD:
|
||||
Report.info("Moving entity {} movement detected. This should not happen".format(axis))
|
||||
result = False
|
||||
|
||||
if delta_stationary > CLOSE_ENOUGH_THRESHOLD:
|
||||
Report.info("Stationary entity {} movement detected. This should not happen".format(axis))
|
||||
result = False
|
||||
|
||||
return result
|
||||
|
||||
# *** Executable Code ***
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 1.5
|
||||
CLOSE_ENOUGH_THRESHOLD = 0.0001
|
||||
SPHERE_RADIUS = 0.5 # Radius of both sphere entities
|
||||
COMPARISON_BUFFER = 0.2 # used for position comparisons to offset game physics anomalies
|
||||
|
||||
# 1) Open level / Enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4976245_PhysxCollider_CollisionLayerTest")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve/validate entities
|
||||
moving = Sphere("Moving")
|
||||
moving.id = general.find_game_entity(moving.name)
|
||||
Report.critical_result(Tests.moving_entity_found, moving.id.IsValid())
|
||||
|
||||
stationary = Sphere("Stationary")
|
||||
stationary.id = general.find_game_entity(stationary.name)
|
||||
Report.critical_result(Tests.stationary_entity_found, stationary.id.IsValid())
|
||||
|
||||
# 3) Log starting positions
|
||||
moving.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", moving.id)
|
||||
stationary.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", stationary.id)
|
||||
|
||||
# Ensure that spheres are aligned properly along the y and z axises (so x axis movement will cause collision)
|
||||
spheres_aligned = (abs(moving.initial_pos.y - stationary.initial_pos.y) < SPHERE_RADIUS) and (
|
||||
abs(moving.initial_pos.z - stationary.initial_pos.z) < SPHERE_RADIUS
|
||||
)
|
||||
|
||||
# Report critical level integrity results
|
||||
Report.critical_result(Tests.moving_pos_found, moving.initial_pos is not None and not moving.initial_pos.IsZero())
|
||||
Report.critical_result(
|
||||
Tests.stationary_pos_found, stationary.initial_pos is not None and not moving.initial_pos.IsZero()
|
||||
)
|
||||
Report.critical_result(
|
||||
Tests.spheres_share_x_axis,
|
||||
spheres_aligned,
|
||||
"Please check the level to make sure Moving Sphere and Stationary Sphere share y and z positions",
|
||||
)
|
||||
|
||||
# 4) Set up variables and handler for observing force region interaction
|
||||
|
||||
class TestData:
|
||||
collision_occurred = False
|
||||
spheres_switched = False
|
||||
|
||||
# Force Region Event Handler
|
||||
def on_collision_begin(args):
|
||||
collider_id = args[0]
|
||||
if collider_id.Equal(moving.id):
|
||||
if not TestData.collision_occurred:
|
||||
TestData.collision_occurred = True
|
||||
Report.info("Collision detected")
|
||||
|
||||
# Assign the handler
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(stationary.id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
moving.current_pos = moving.initial_pos
|
||||
stationary.current_pos = stationary.initial_pos
|
||||
|
||||
# Tests if we are done collecting results and can exit the test
|
||||
def done_collecting_results():
|
||||
|
||||
if not TestData.collision_occurred:
|
||||
|
||||
moving.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", moving.id)
|
||||
stationary.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", stationary.id)
|
||||
|
||||
if moving.current_pos.x > (stationary.current_pos.x + COMPARISON_BUFFER):
|
||||
# Moving sphere passed the stationary sphere's x coordinate
|
||||
TestData.spheres_switched = True
|
||||
return True
|
||||
else:
|
||||
# A collision was detected
|
||||
Report.info("A collision was detected unfortunately")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
# 5) Wait for results to be collected or for time out
|
||||
Report.result(Tests.timed_out, helper.wait_for_condition(done_collecting_results, TIME_OUT))
|
||||
|
||||
# 6) Log results
|
||||
Report.result(Tests.spheres_switched_sides, TestData.spheres_switched)
|
||||
Report.result(Tests.spheres_not_collided, not TestData.collision_occurred)
|
||||
|
||||
# Look for movement in Y direction. Report results
|
||||
no_movement_y = no_movement(
|
||||
abs(moving.initial_pos.y - moving.current_pos.y), abs(stationary.initial_pos.y - stationary.current_pos.y), "Y"
|
||||
)
|
||||
|
||||
Report.result(Tests.no_y_movement, no_movement_y)
|
||||
|
||||
# Look for movement in Z direction. Report results
|
||||
no_movement_z = no_movement(
|
||||
abs(moving.initial_pos.z - moving.current_pos.z), abs(stationary.initial_pos.z - stationary.current_pos.z), "Z"
|
||||
)
|
||||
Report.result(Tests.no_z_movement, no_movement_z)
|
||||
|
||||
Report.result(Tests.stationary_didnt_move, stationary.current_pos.Equal(stationary.initial_pos))
|
||||
|
||||
# Collected data dump
|
||||
Report.info(" ********** Collected Data ***************")
|
||||
Report.info("Moving sphere's positions:")
|
||||
Report.info_vector3(moving.initial_pos, " initial:")
|
||||
Report.info_vector3(moving.current_pos, " final:")
|
||||
Report.info("*****************************")
|
||||
Report.info("Stationary sphere's positions:")
|
||||
Report.info_vector3(stationary.initial_pos, " initial:")
|
||||
Report.info_vector3(stationary.current_pos, " final:")
|
||||
Report.info("*****************************")
|
||||
|
||||
# 7) 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 utils import Report
|
||||
Report.start_test(C4976245_PhysXCollider_CollisionLayerTest)
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
|
||||
# Test case ID : C4982593
|
||||
# Test Case Title : Check that two entities with different collision groups and layers do not collide.
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/4982593
|
||||
|
||||
|
||||
# fmt: off
|
||||
class Tests:
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
moving_entity_found = ("Moving entity found", "Moving entity not found")
|
||||
stationary_entity_found = ("Stationary entity found", "Stationary entity not found")
|
||||
moving_pos_found = ("Moving sphere position found", "Moving sphere position not found")
|
||||
stationary_pos_found = ("Stationary sphere position found", "Stationary sphere position not found")
|
||||
spheres_share_x_axis = ("Both spheres are aligned properly", "Spheres are not aligned properly")
|
||||
spheres_not_collided = ("No collision was detected", "A collision was detected")
|
||||
spheres_switched_sides = ("The moving sphere passed through the other", "Moving sphere did not pass through")
|
||||
no_y_movement = ("There was no Y movement", "Some Y movement was detected")
|
||||
no_z_movement = ("There was no Z movement", "Some Z movement was detected")
|
||||
timed_out = ("Test did not time out", "Test TIMED OUT")
|
||||
stationary_didnt_move = ("Stationary sphere did not move", "Stationary sphere moved")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
|
||||
# fmt: on
|
||||
|
||||
|
||||
def C4982593_PhysXCollider_CollisionLayerTest():
|
||||
# type: () -> None
|
||||
"""
|
||||
Summary:
|
||||
Runs an automated test to ensure to rigid bodies on the different collision group and collision layer
|
||||
DO NOT collide.
|
||||
|
||||
Level Description:
|
||||
Moving (entity) - a spherical entity (colored yellow) that is set up with a collision layer of "demo1"
|
||||
collision group of "demo_group1" gravity as disabled and an initial velocity of (3, 0, 0).
|
||||
Stationary (entity) - a spherical entity (colored purple) that is set up with a collision layer of "demo2"
|
||||
collision group of "demo_group2" gravity disabled, and is positioned at location (+2, 0, 0) relative to
|
||||
Moving's starting position.
|
||||
|
||||
Expected Behavior:
|
||||
When game mode is entered, Moving will begin moving in the positive X direction. The entity should pass through
|
||||
Stationary with no collision detection triggered.
|
||||
|
||||
Test Steps:
|
||||
1) Loads the level / Enter game mode
|
||||
2) Retrieve test entities
|
||||
3) Ensures that the test objects (Moving and Stationary) are located
|
||||
3.5) set up variables and handlers
|
||||
4) Wait for Moving to pass through Stationary
|
||||
5) Logs results
|
||||
6) Close the editor
|
||||
|
||||
Note:
|
||||
- This test file must be called from the Lumberyard 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 utils import Report
|
||||
from utils import TestHelper as helper
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.bus
|
||||
|
||||
# **** Helper class ****
|
||||
|
||||
class Sphere:
|
||||
def __init__(self, name):
|
||||
self.id = None
|
||||
self.name = name
|
||||
self.initial_pos = None
|
||||
self.current_position = None
|
||||
|
||||
# Tests for significant change between a set of float pairs
|
||||
# returns a list of booleans where the i-th index hold the result for the i-th float pair
|
||||
def detect_significant_change(float_pairs):
|
||||
# type: ([(float, float)]) -> [bool]
|
||||
return [abs(p[0] - p[1]) >= CLOSE_ENOUGH_THRESHOLD for p in float_pairs]
|
||||
|
||||
# *** Executable Code ***
|
||||
|
||||
# Constants
|
||||
TIME_OUT = 1.5
|
||||
CLOSE_ENOUGH_THRESHOLD = 0.0001
|
||||
SPHERE_RADIUS = 0.5 # Radius of both sphere entities
|
||||
|
||||
# 1) Open level / Enter game mode
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C4982593_PhysxCollider_CollisionLayerTest")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Retrieve/validate entities
|
||||
moving = Sphere("Moving")
|
||||
moving.id = general.find_game_entity(moving.name)
|
||||
Report.critical_result(Tests.moving_entity_found, moving.id.IsValid())
|
||||
|
||||
stationary = Sphere("Stationary")
|
||||
stationary.id = general.find_game_entity(stationary.name)
|
||||
Report.critical_result(Tests.stationary_entity_found, stationary.id.IsValid())
|
||||
|
||||
# 3) Log starting positions
|
||||
moving.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", moving.id)
|
||||
stationary.initial_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", stationary.id)
|
||||
|
||||
# Ensure that spheres are aligned properly along the y and z axises (so x axis movement will cause collision)
|
||||
spheres_aligned = (abs(moving.initial_pos.y - stationary.initial_pos.y) < SPHERE_RADIUS) and (
|
||||
abs(moving.initial_pos.z - stationary.initial_pos.z) < SPHERE_RADIUS
|
||||
)
|
||||
|
||||
# Report critical level integrity results
|
||||
Report.critical_result(Tests.moving_pos_found, moving.initial_pos is not None and not moving.initial_pos.IsZero())
|
||||
Report.critical_result(
|
||||
Tests.stationary_pos_found, stationary.initial_pos is not None and not moving.initial_pos.IsZero()
|
||||
)
|
||||
Report.critical_result(
|
||||
Tests.spheres_share_x_axis,
|
||||
spheres_aligned,
|
||||
"Please check the level to make sure Moving Sphere and Stationary Sphere share y and z positions",
|
||||
)
|
||||
|
||||
# 3.5) Set up variables and handler for observing force region interaction
|
||||
|
||||
class TestData:
|
||||
collision_occurred = False
|
||||
spheres_switched = False
|
||||
|
||||
# Force Region Event Handler
|
||||
def on_collision_begin(args):
|
||||
collider_id = args[0]
|
||||
if collider_id.Equal(moving.id):
|
||||
if not TestData.collision_occurred:
|
||||
TestData.collision_occurred = True
|
||||
Report.info("Collision detected")
|
||||
|
||||
# Assign the handler
|
||||
handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
handler.connect(stationary.id)
|
||||
handler.add_callback("OnCollisionBegin", on_collision_begin)
|
||||
|
||||
moving.current_pos = moving.initial_pos
|
||||
stationary.current_pos = stationary.initial_pos
|
||||
|
||||
# Tests if we are done collecting results and can exit the test
|
||||
def done_collecting_results():
|
||||
|
||||
COMPARISON_BUFFER = 0.2
|
||||
|
||||
if not TestData.collision_occurred:
|
||||
|
||||
moving.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", moving.id)
|
||||
stationary.current_pos = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", stationary.id)
|
||||
|
||||
if moving.current_pos.x > stationary.current_pos.x + COMPARISON_BUFFER:
|
||||
# Moving sphere passed the stationary sphere's x coordinate
|
||||
TestData.spheres_switched = True
|
||||
return True
|
||||
else:
|
||||
# A collision was detected
|
||||
Report.info("A collision was detected unfortunately")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
# 4) Wait for results to be collected or for time out
|
||||
Report.result(Tests.timed_out, helper.wait_for_condition(done_collecting_results, TIME_OUT))
|
||||
|
||||
# 5) Log results
|
||||
Report.result(Tests.spheres_switched_sides, TestData.spheres_switched)
|
||||
Report.result(Tests.spheres_not_collided, not TestData.collision_occurred)
|
||||
|
||||
# Look for movement in Y direction. Report results
|
||||
y_movement = detect_significant_change(
|
||||
[(moving.initial_pos.y, moving.current_pos.y), (stationary.initial_pos.y, stationary.current_pos.y)]
|
||||
)
|
||||
|
||||
if not any(y_movement):
|
||||
Report.success(Tests.no_y_movement)
|
||||
else:
|
||||
Report.failure(Tests.no_y_movement)
|
||||
if y_movement[0]:
|
||||
Report.info("Moving entity Y movement detected. This should not happen")
|
||||
if y_movement[1]:
|
||||
Report.info("Stationary entity Y movement detected. This should not happen")
|
||||
|
||||
# Look for movement in Z direction. Report Results
|
||||
z_movement = detect_significant_change(
|
||||
[(moving.initial_pos.z, moving.current_pos.z), (stationary.initial_pos.z, stationary.current_pos.z)]
|
||||
)
|
||||
|
||||
if not any(z_movement):
|
||||
Report.success(Tests.no_z_movement)
|
||||
else:
|
||||
Report.failure(Tests.no_z_movement)
|
||||
if z_movement[0]:
|
||||
Report.info("Moving entity Z movement detected. This should not happen")
|
||||
if z_movement[1]:
|
||||
Report.info("Stationary entity Z movement detected. This should not happen")
|
||||
|
||||
Report.result(Tests.stationary_didnt_move, stationary.current_pos.Equal(stationary.initial_pos))
|
||||
|
||||
# Collected data dump
|
||||
Report.info(" ********** Collected Data ***************")
|
||||
Report.info("Moving sphere's positions:")
|
||||
Report.info_vector3(moving.initial_pos, " initial:")
|
||||
Report.info_vector3(moving.current_pos, " final:")
|
||||
Report.info("*****************************")
|
||||
Report.info("Stationary sphere's positions:")
|
||||
Report.info_vector3(stationary.initial_pos, " initial:")
|
||||
Report.info_vector3(stationary.current_pos, " final:")
|
||||
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 utils import Report
|
||||
Report.start_test(C4982593_PhysXCollider_CollisionLayerTest)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user