Tick_CharacterGameplayComponentMotionIsSmooth.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. Test Case Title : Verify that an entity with a character gameplay component moves smoothly.
  6. """
  7. # fmt: off
  8. class Tests():
  9. create_entity = ("Created test entity", "Failed to create test entity")
  10. character_controller_added = ("Added PhysX Character Controller component", "Failed to add PhysX Character Controller component")
  11. character_gameplay_added = ("Added PhysX Character Gameplay component", "Failed to add PhysX Character Gameplay component")
  12. enter_game_mode = ("Entered game mode", "Failed to enter game mode")
  13. exit_game_mode = ("Exited game mode", "Failed to exit game mode")
  14. character_motion_smooth = ("Character motion passed smoothness threshold", "Failed to meet smoothness threshold for character motion")
  15. # fmt: on
  16. def Tick_CharacterGameplayComponentMotionIsSmooth():
  17. """
  18. Summary:
  19. Create entity with PhysX Character Controller and PhysX Character Gameplay components.
  20. Verify that the motion of the character controller under gravity is smooth.
  21. Expected Behavior:
  22. 1) The motion of the character controller under gravity is a smooth curve, rather than an erratic/jittery movement.
  23. Test Steps:
  24. 1) Load the empty level
  25. 2) Create an entity
  26. 3) Add a PhysX Character Controller Component and PhysX Character Gameplay component
  27. 4) Enter game mode and collect data for the character controller's z co-ordinate and the time values for a series of frames
  28. 5) Check if the motion of the character controller was sufficiently smooth
  29. :return: None
  30. """
  31. # imports
  32. import os
  33. import azlmbr.legacy.general as general
  34. import azlmbr.math as math
  35. from editor_python_test_tools.editor_entity_utils import EditorEntity as Entity
  36. from editor_python_test_tools.utils import Report
  37. from editor_python_test_tools.utils import TestHelper as helper
  38. from editor_python_test_tools.asset_utils import Asset
  39. import numpy as np
  40. # constants
  41. COEFFICIENT_OF_DETERMINATION_THRESHOLD = 1 - 1e-4 # curves with values below this are not considered sufficiently smooth
  42. helper.init_idle()
  43. # 1) Load the empty level
  44. helper.open_level("", "Base")
  45. # 2) Create an entity
  46. test_entity = Entity.create_editor_entity("test_entity")
  47. Report.result(Tests.create_entity, test_entity.id.IsValid())
  48. azlmbr.components.TransformBus(
  49. azlmbr.bus.Event, "SetWorldTranslation", test_entity.id, math.Vector3(0.0, 0.0, 0.0))
  50. # 3) Add character controller and character gameplay components
  51. character_controller_component = test_entity.add_component("PhysX Character Controller")
  52. Report.result(Tests.character_controller_added, test_entity.has_component("PhysX Character Controller"))
  53. character_gameplay_component = test_entity.add_component("PhysX Character Gameplay")
  54. Report.result(Tests.character_gameplay_added, test_entity.has_component("PhysX Character Gameplay"))
  55. # 4) Enter game mode and collect data for the rigid body's z co-ordinate and the time values for a series of frames
  56. t = []
  57. z = []
  58. helper.enter_game_mode(Tests.enter_game_mode)
  59. general.idle_wait_frames(1)
  60. game_entity_id = general.find_game_entity("test_entity")
  61. for frame in range(100):
  62. t.append(azlmbr.components.TickRequestBus(azlmbr.bus.Broadcast, "GetTimeAtCurrentTick").GetSeconds())
  63. z.append(azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldZ", game_entity_id))
  64. general.idle_wait_frames(1)
  65. helper.exit_game_mode(Tests.exit_game_mode)
  66. # 5) Test that the z vs t curve is sufficiently smooth (if the interpolation is not working well, the curve will be less smooth)
  67. # normalize the t and z data
  68. t = np.array(t) - np.mean(t)
  69. z = np.array(z) - np.mean(z)
  70. # fit a polynomial to the z vs t curve
  71. fit = np.poly1d(np.polyfit(t, z, 4))
  72. residual = fit(t) - z
  73. # calculate the coefficient of determination (a measure of how closely the polynomial curve fits the data)
  74. # if the coefficient is very close to 1, then the curve fits the data very well, suggesting that the rigid body motion is smooth
  75. # if the coefficient is significantly less than 1, then the z values vary more erratically relative to the smooth curve,
  76. # indicating that the motion of the rigid body is not smooth
  77. coefficient_of_determination = (1 - np.sum(residual * residual) / np.sum(z * z))
  78. Report.result(Tests.character_motion_smooth, bool(coefficient_of_determination > COEFFICIENT_OF_DETERMINATION_THRESHOLD))
  79. if __name__ == "__main__":
  80. from editor_python_test_tools.utils import Report
  81. Report.start_test(Tick_CharacterGameplayComponentMotionIsSmooth)