Integrating up through commit 90f050496
This commit is contained in:
@@ -9,12 +9,9 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
add_subdirectory(Physics)
|
||||
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
|
||||
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
|
||||
ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/Common)
|
||||
|
||||
ly_add_target(
|
||||
NAME AzFrameworkTestShared STATIC
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzFramework/Engine/Engine.h>
|
||||
#include <Utils/Utils.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace EngineTests
|
||||
{
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
class EngineTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void SetUp() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void TearDown() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
TEST_F(EngineTest, FindEngineRoot_EngineBelowExecutable_Success)
|
||||
{
|
||||
// We assume in this test that our engine lives above our binaries. If that's not the case we should pass in
|
||||
// a valid path to a folder below the engine root to verify this functionality
|
||||
auto foundRoot = AzFramework::Engine::FindEngineRoot();
|
||||
EXPECT_FALSE(foundRoot.empty());
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_FRAMEWORK_TESTS
|
||||
TEST_F(EngineTest, DISABLED_FindEngineRoot_StartTempFolder_NoRootFound)
|
||||
#else
|
||||
TEST_F(EngineTest, FindEngineRoot_StartTempFolder_NoRootFound)
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_FRAMEWORK_TESTS
|
||||
{
|
||||
UnitTest::ScopedTemporaryDirectory tempDir;
|
||||
auto foundRoot = AzFramework::Engine::FindEngineRoot(tempDir.GetDirectory());
|
||||
EXPECT_TRUE(foundRoot.empty());
|
||||
}
|
||||
|
||||
} // namespace EngineTests
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzFramework/Components/AzFrameworkConfigurationSystemComponent.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
void EntityOwnershipServiceTestFixture::SetUpEntityOwnershipServiceTest()
|
||||
@@ -40,6 +42,9 @@ namespace UnitTest
|
||||
AZ::ComponentTypeList EntityOwnershipServiceTestFixture::EntityOwnershipServiceApplication::GetRequiredSystemComponents() const
|
||||
{
|
||||
AZ::ComponentTypeList defaultRequiredComponents = AzFramework::Application::GetRequiredSystemComponents();
|
||||
|
||||
defaultRequiredComponents.emplace_back(azrtti_typeid<AzToolsFramework::Prefab::PrefabSystemComponent>());
|
||||
|
||||
auto findComponentIterator = AZStd::find(defaultRequiredComponents.begin(), defaultRequiredComponents.end(),
|
||||
azrtti_typeid<AzFramework::GameEntityContextComponent>());
|
||||
if (findComponentIterator != defaultRequiredComponents.end())
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
|
||||
ly_add_target(
|
||||
NAME AzPhysicsTests HEADERONLY
|
||||
NAMESPACE AZ
|
||||
FILES_CMAKE
|
||||
azphysicstests_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
INTERFACE
|
||||
..
|
||||
BUILD_DEPENDENCIES
|
||||
INTERFACE
|
||||
AZ::AzFramework
|
||||
)
|
||||
|
||||
endif()
|
||||
@@ -1,988 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
TEST_F(PhysicsComponentBusTest, SetLinearDamping_DynamicSphere_MoreDampedBodyFallsSlower)
|
||||
{
|
||||
auto sphereA = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
|
||||
auto sphereB = AddSphereEntity(AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
|
||||
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::SetLinearDamping, 0.1f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::SetLinearDamping, 0.2f);
|
||||
|
||||
UpdateDefaultWorld(60);
|
||||
|
||||
float dampingA, dampingB;
|
||||
Physics::RigidBodyRequestBus::EventResult(dampingA, sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetLinearDamping);
|
||||
Physics::RigidBodyRequestBus::EventResult(dampingB, sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetLinearDamping);
|
||||
EXPECT_NEAR(dampingA, 0.1f, 1e-3f);
|
||||
EXPECT_NEAR(dampingB, 0.2f, 1e-3f);
|
||||
|
||||
float zA = GetPositionElement(sphereA, 2);
|
||||
float zB = GetPositionElement(sphereB, 2);
|
||||
EXPECT_GT(zB, zA);
|
||||
|
||||
AZ::Vector3 vA = AZ::Vector3::CreateZero();
|
||||
AZ::Vector3 vB = AZ::Vector3::CreateZero();
|
||||
Physics::RigidBodyRequestBus::EventResult(vA, sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetLinearVelocity);
|
||||
Physics::RigidBodyRequestBus::EventResult(vB, sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetLinearVelocity);
|
||||
EXPECT_GT(static_cast<float>(vA.GetLength()), static_cast<float>(vB.GetLength()));
|
||||
|
||||
delete sphereA;
|
||||
delete sphereB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, SetLinearDampingNegative_DynamicSphere_NegativeValueRejected)
|
||||
{
|
||||
ErrorHandler errorHandler("Negative linear damping value");
|
||||
|
||||
auto sphere = AddSphereEntity(AZ::Vector3::CreateZero(), 0.5f);
|
||||
|
||||
float damping = 0.0f, initialDamping = 0.0f;
|
||||
Physics::RigidBodyRequestBus::EventResult(initialDamping, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetLinearDamping);
|
||||
|
||||
// a negative damping value should be rejected and the damping should remain at its previous value
|
||||
Physics::RigidBodyRequestBus::Event(sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::SetLinearDamping, -0.1f);
|
||||
Physics::RigidBodyRequestBus::EventResult(damping, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetLinearDamping);
|
||||
|
||||
EXPECT_NEAR(damping, initialDamping, 1e-3f);
|
||||
EXPECT_TRUE(errorHandler.GetWarningCount() > 0);
|
||||
|
||||
delete sphere;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, SetAngularDamping_DynamicSphere_MoreDampedBodyRotatesSlower)
|
||||
{
|
||||
auto sphereA = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 1.0f), 0.5f);
|
||||
auto sphereB = AddSphereEntity(AZ::Vector3(0.0f, 5.0f, 1.0f), 0.5f);
|
||||
auto floor = AddStaticBoxEntity(AZ::Vector3::CreateZero(), AZ::Vector3(100.0f, 100.0f, 1.0f));
|
||||
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::SetAngularDamping, 0.1f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::SetAngularDamping, 0.2f);
|
||||
|
||||
float dampingA, dampingB;
|
||||
Physics::RigidBodyRequestBus::EventResult(dampingA, sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularDamping);
|
||||
Physics::RigidBodyRequestBus::EventResult(dampingB, sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularDamping);
|
||||
EXPECT_NEAR(dampingA, 0.1f, 1e-3f);
|
||||
EXPECT_NEAR(dampingB, 0.2f, 1e-3f);
|
||||
|
||||
AZ::Vector3 impulse(10.0f, 0.0f, 0.0f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::ApplyLinearImpulse, impulse);
|
||||
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::ApplyLinearImpulse, impulse);
|
||||
|
||||
UpdateDefaultWorld(10);
|
||||
auto angularVelocityA = AZ::Vector3::CreateZero();
|
||||
auto angularVelocityB = AZ::Vector3::CreateZero();
|
||||
|
||||
for (int timestep = 0; timestep < 10; timestep++)
|
||||
{
|
||||
Physics::RigidBodyRequestBus::EventResult(angularVelocityA, sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularVelocity);
|
||||
Physics::RigidBodyRequestBus::EventResult(angularVelocityB, sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularVelocity);
|
||||
EXPECT_GT(static_cast<float>(angularVelocityA.GetLength()), static_cast<float>(angularVelocityB.GetLength()));
|
||||
UpdateDefaultWorld(1);
|
||||
}
|
||||
|
||||
delete floor;
|
||||
delete sphereA;
|
||||
delete sphereB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, SetAngularDampingNegative_DynamicSphere_NegativeValueRejected)
|
||||
{
|
||||
ErrorHandler errorHandler("Negative angular damping value");
|
||||
|
||||
auto sphere = AddSphereEntity(AZ::Vector3::CreateZero(), 0.5f);
|
||||
|
||||
float damping = 0.0f, initialDamping = 0.0f;
|
||||
Physics::RigidBodyRequestBus::EventResult(initialDamping, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularDamping);
|
||||
|
||||
// a negative damping value should be rejected and the damping should remain at its previous value
|
||||
Physics::RigidBodyRequestBus::Event(sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::SetAngularDamping, -0.1f);
|
||||
Physics::RigidBodyRequestBus::EventResult(damping, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularDamping);
|
||||
|
||||
EXPECT_NEAR(damping, initialDamping, 1e-3f);
|
||||
EXPECT_TRUE(errorHandler.GetWarningCount() > 0);
|
||||
|
||||
delete sphere;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, AddImpulse_DynamicSphere_AffectsTrajectory)
|
||||
{
|
||||
auto sphereA = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
|
||||
auto sphereB = AddSphereEntity(AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
|
||||
|
||||
AZ::Vector3 impulse(10.0f, 0.0f, 0.0f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::ApplyLinearImpulse, impulse);
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
float xPreviousA = GetPositionElement(sphereA, 0);
|
||||
float xPreviousB = GetPositionElement(sphereB, 0);
|
||||
UpdateDefaultWorld(10);
|
||||
EXPECT_GT(GetPositionElement(sphereA, 0), xPreviousA);
|
||||
EXPECT_NEAR(GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
|
||||
}
|
||||
|
||||
delete sphereA;
|
||||
delete sphereB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, SetLinearVelocity_DynamicSphere_AffectsTrajectory)
|
||||
{
|
||||
auto sphereA = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
|
||||
auto sphereB = AddSphereEntity(AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
|
||||
|
||||
AZ::Vector3 velocity(10.0f, 0.0f, 0.0f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::SetLinearVelocity, velocity);
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
float xPreviousA = GetPositionElement(sphereA, 0);
|
||||
float xPreviousB = GetPositionElement(sphereB, 0);
|
||||
UpdateDefaultWorld(10);
|
||||
EXPECT_GT(GetPositionElement(sphereA, 0), xPreviousA);
|
||||
EXPECT_NEAR(GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
|
||||
}
|
||||
|
||||
delete sphereA;
|
||||
delete sphereB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, AddImpulseAtWorldPoint_DynamicSphere_AffectsTrajectoryAndRotation)
|
||||
{
|
||||
auto sphereA = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
|
||||
auto sphereB = AddSphereEntity(AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
|
||||
|
||||
AZ::Vector3 impulse(10.0f, 0.0f, 0.0f);
|
||||
AZ::Vector3 worldPoint(0.0f, -5.0f, 0.25f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::ApplyLinearImpulseAtWorldPoint, impulse, worldPoint);
|
||||
|
||||
AZ::Vector3 angularVelocityA = AZ::Vector3::CreateZero();
|
||||
AZ::Vector3 angularVelocityB = AZ::Vector3::CreateZero();
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
float xPreviousA = GetPositionElement(sphereA, 0);
|
||||
float xPreviousB = GetPositionElement(sphereB, 0);
|
||||
UpdateDefaultWorld(10);
|
||||
EXPECT_GT(GetPositionElement(sphereA, 0), xPreviousA);
|
||||
EXPECT_NEAR(GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
|
||||
|
||||
Physics::RigidBodyRequestBus::EventResult(angularVelocityA, sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularVelocity);
|
||||
Physics::RigidBodyRequestBus::EventResult(angularVelocityB, sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularVelocity);
|
||||
EXPECT_FALSE(angularVelocityA.IsClose(AZ::Vector3::CreateZero()));
|
||||
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetX()), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetZ()), 0.0f, 1e-3f);
|
||||
EXPECT_TRUE(angularVelocityB.IsClose(AZ::Vector3::CreateZero()));
|
||||
}
|
||||
|
||||
delete sphereA;
|
||||
delete sphereB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, AddAngularImpulse_DynamicSphere_AffectsRotation)
|
||||
{
|
||||
auto sphereA = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
|
||||
auto sphereB = AddSphereEntity(AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
|
||||
|
||||
AZ::Vector3 angularImpulse(0.0f, 10.0f, 0.0f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::ApplyAngularImpulse, angularImpulse);
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
float xPreviousA = GetPositionElement(sphereA, 0);
|
||||
float xPreviousB = GetPositionElement(sphereB, 0);
|
||||
UpdateDefaultWorld(10);
|
||||
EXPECT_NEAR(GetPositionElement(sphereA, 0), xPreviousA, 1e-3f);
|
||||
EXPECT_NEAR(GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
|
||||
|
||||
AZ::Vector3 angularVelocityA = AZ::Vector3::CreateZero();
|
||||
AZ::Vector3 angularVelocityB = AZ::Vector3::CreateZero();
|
||||
Physics::RigidBodyRequestBus::EventResult(angularVelocityA, sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularVelocity);
|
||||
Physics::RigidBodyRequestBus::EventResult(angularVelocityB, sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularVelocity);
|
||||
EXPECT_FALSE(angularVelocityA.IsClose(AZ::Vector3::CreateZero()));
|
||||
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetX()), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetZ()), 0.0f, 1e-3f);
|
||||
EXPECT_TRUE(angularVelocityB.IsClose(AZ::Vector3::CreateZero()));
|
||||
}
|
||||
|
||||
delete sphereA;
|
||||
delete sphereB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, SetAngularVelocity_DynamicSphere_AffectsRotation)
|
||||
{
|
||||
auto sphereA = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
|
||||
auto sphereB = AddSphereEntity(AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
|
||||
|
||||
AZ::Vector3 angularVelocity(0.0f, 10.0f, 0.0f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::SetAngularVelocity, angularVelocity);
|
||||
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
float xPreviousA = GetPositionElement(sphereA, 0);
|
||||
float xPreviousB = GetPositionElement(sphereB, 0);
|
||||
UpdateDefaultWorld(10);
|
||||
EXPECT_NEAR(GetPositionElement(sphereA, 0), xPreviousA, 1e-3f);
|
||||
EXPECT_NEAR(GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
|
||||
|
||||
AZ::Vector3 angularVelocityA = AZ::Vector3::CreateZero();
|
||||
AZ::Vector3 angularVelocityB = AZ::Vector3::CreateZero();
|
||||
Physics::RigidBodyRequestBus::EventResult(angularVelocityA, sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularVelocity);
|
||||
Physics::RigidBodyRequestBus::EventResult(angularVelocityB, sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAngularVelocity);
|
||||
EXPECT_FALSE(angularVelocityA.IsClose(AZ::Vector3::CreateZero()));
|
||||
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetX()), 0.0f, 1e-3f);
|
||||
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetZ()), 0.0f, 1e-3f);
|
||||
EXPECT_TRUE(angularVelocityB.IsClose(AZ::Vector3::CreateZero()));
|
||||
}
|
||||
|
||||
delete sphereA;
|
||||
delete sphereB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, GetLinearVelocity_FallingSphere_VelocityIncreasesOverTime)
|
||||
{
|
||||
auto sphere = AddSphereEntity(AZ::Vector3(0.0f, 0.0f, 0.0f), 0.5f);
|
||||
Physics::RigidBodyRequestBus::Event(sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::SetLinearDamping, 0.0f);
|
||||
|
||||
float previousSpeed = 0.0f;
|
||||
|
||||
for (int timestep = 0; timestep < 60; timestep++)
|
||||
{
|
||||
UpdateDefaultWorld(1);
|
||||
AZ::Vector3 velocity;
|
||||
Physics::RigidBodyRequestBus::EventResult(velocity, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetLinearVelocity);
|
||||
float speed = velocity.GetLength();
|
||||
EXPECT_GT(speed, previousSpeed);
|
||||
previousSpeed = speed;
|
||||
}
|
||||
|
||||
delete sphere;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, SetSleepThreshold_RollingSpheres_LowerThresholdSphereTravelsFurther)
|
||||
{
|
||||
auto sphereA = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 1.0f), 0.5f);
|
||||
auto sphereB = AddSphereEntity(AZ::Vector3(0.0f, 5.0f, 1.0f), 0.5f);
|
||||
auto floor = AddStaticBoxEntity(AZ::Vector3::CreateZero(), AZ::Vector3(100.0f, 100.0f, 1.0f));
|
||||
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::SetAngularDamping, 0.75f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::SetAngularDamping, 0.75f);
|
||||
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::SetSleepThreshold, 1.0f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::SetSleepThreshold, 0.5f);
|
||||
|
||||
float sleepThresholdA, sleepThresholdB;
|
||||
Physics::RigidBodyRequestBus::EventResult(sleepThresholdA, sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetSleepThreshold);
|
||||
Physics::RigidBodyRequestBus::EventResult(sleepThresholdB, sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetSleepThreshold);
|
||||
|
||||
EXPECT_NEAR(sleepThresholdA, 1.0f, 1e-3f);
|
||||
EXPECT_NEAR(sleepThresholdB, 0.5f, 1e-3f);
|
||||
|
||||
AZ::Vector3 impulse(0.0f, 0.1f, 0.0f);
|
||||
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
|
||||
&Physics::RigidBodyRequests::ApplyAngularImpulse, impulse);
|
||||
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
|
||||
&Physics::RigidBodyRequests::ApplyAngularImpulse, impulse);
|
||||
|
||||
UpdateDefaultWorld(300);
|
||||
|
||||
EXPECT_GT(GetPositionElement(sphereB, 0), GetPositionElement(sphereA, 0));
|
||||
|
||||
delete floor;
|
||||
delete sphereA;
|
||||
delete sphereB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, SetSleepThresholdNegative_DynamicSphere_NegativeValueRejected)
|
||||
{
|
||||
ErrorHandler errorHandler("Negative sleep threshold value");
|
||||
|
||||
auto sphere = AddSphereEntity(AZ::Vector3(0.0f, -5.0f, 1.0f), 0.5f);
|
||||
|
||||
float threshold = 0.0f, initialThreshold = 0.0f;
|
||||
Physics::RigidBodyRequestBus::EventResult(initialThreshold, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetSleepThreshold);
|
||||
Physics::RigidBodyRequestBus::Event(sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::SetSleepThreshold, -0.5f);
|
||||
Physics::RigidBodyRequestBus::EventResult(threshold, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetSleepThreshold);
|
||||
|
||||
EXPECT_NEAR(threshold, initialThreshold, 1e-3f);
|
||||
EXPECT_TRUE(errorHandler.GetWarningCount() > 0);
|
||||
|
||||
delete sphere;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, SetMass_Seesaw_TipsDownAtHeavierEnd)
|
||||
{
|
||||
auto floor = AddStaticBoxEntity(AZ::Vector3::CreateZero(), AZ::Vector3(100.0f, 100.0f, 1.0f));
|
||||
auto pivot = AddStaticBoxEntity(AZ::Vector3(0.0f, 0.0f, 0.7f), AZ::Vector3(0.4f, 1.0f, 0.4f));
|
||||
auto seesaw = AddBoxEntity(AZ::Vector3(0.0f, 0.0f, 0.95f), AZ::Vector3(20.0f, 1.0f, 0.1f));
|
||||
auto boxA = AddBoxEntity(AZ::Vector3(-9.0f, 0.0f, 1.5f), AZ::Vector3::CreateOne());
|
||||
auto boxB = AddBoxEntity(AZ::Vector3(9.0f, 0.0f, 1.5f), AZ::Vector3::CreateOne());
|
||||
|
||||
Physics::RigidBodyRequestBus::Event(boxA->GetId(), &Physics::RigidBodyRequests::SetMass, 5.0f);
|
||||
float mass = 0.0f;
|
||||
Physics::RigidBodyRequestBus::EventResult(mass, boxA->GetId(),
|
||||
&Physics::RigidBodyRequests::GetMass);
|
||||
EXPECT_NEAR(mass, 5.0f, 1e-3f);
|
||||
|
||||
UpdateDefaultWorld(30);
|
||||
EXPECT_GT(1.5f, GetPositionElement(boxA, 2));
|
||||
EXPECT_LT(1.5f, GetPositionElement(boxB, 2));
|
||||
|
||||
Physics::RigidBodyRequestBus::Event(boxB->GetId(), &Physics::RigidBodyRequests::SetMass, 20.0f);
|
||||
Physics::RigidBodyRequestBus::EventResult(mass, boxB->GetId(),
|
||||
&Physics::RigidBodyRequests::GetMass);
|
||||
EXPECT_NEAR(mass, 20.0f, 1e-3f);
|
||||
|
||||
UpdateDefaultWorld(60);
|
||||
EXPECT_LT(1.5f, GetPositionElement(boxA, 2));
|
||||
EXPECT_GT(1.5f, GetPositionElement(boxB, 2));
|
||||
|
||||
delete floor;
|
||||
delete pivot;
|
||||
delete seesaw;
|
||||
delete boxA;
|
||||
delete boxB;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, GetAabb_Sphere_ValidExtents)
|
||||
{
|
||||
AZ::Vector3 spherePosition(2.0f, -3.0f, 1.0f);
|
||||
auto sphere = AddSphereEntity(spherePosition, 0.5f);
|
||||
|
||||
AZ::Aabb sphereAabb;
|
||||
Physics::RigidBodyRequestBus::EventResult(sphereAabb, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAabb);
|
||||
|
||||
EXPECT_TRUE(sphereAabb.GetMin().IsClose(spherePosition - 0.5f * AZ::Vector3::CreateOne()));
|
||||
EXPECT_TRUE(sphereAabb.GetMax().IsClose(spherePosition + 0.5f * AZ::Vector3::CreateOne()));
|
||||
|
||||
// rotate the sphere and check the bounding box is still correct
|
||||
AZ::Quaternion quat = AZ::Quaternion::CreateRotationZ(0.25f * AZ::Constants::Pi);
|
||||
AZ::TransformBus::Event(sphere->GetId(), &AZ::TransformInterface::SetWorldTM,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(quat, spherePosition));
|
||||
sphere->Deactivate();
|
||||
sphere->Activate();
|
||||
|
||||
Physics::RigidBodyRequestBus::EventResult(sphereAabb, sphere->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAabb);
|
||||
|
||||
EXPECT_TRUE(sphereAabb.GetMin().IsClose(spherePosition - 0.5f * AZ::Vector3::CreateOne()));
|
||||
EXPECT_TRUE(sphereAabb.GetMax().IsClose(spherePosition + 0.5f * AZ::Vector3::CreateOne()));
|
||||
|
||||
delete sphere;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, GetAabb_Box_ValidExtents)
|
||||
{
|
||||
AZ::Vector3 boxPosition(2.0f, -3.0f, 1.0f);
|
||||
AZ::Vector3 boxDimensions(3.0f, 4.0f, 5.0f);
|
||||
auto box = AddBoxEntity(boxPosition, boxDimensions);
|
||||
|
||||
AZ::Aabb boxAabb;
|
||||
Physics::RigidBodyRequestBus::EventResult(boxAabb, box->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAabb);
|
||||
|
||||
EXPECT_TRUE(boxAabb.GetMin().IsClose(boxPosition - 0.5f * boxDimensions));
|
||||
EXPECT_TRUE(boxAabb.GetMax().IsClose(boxPosition + 0.5f * boxDimensions));
|
||||
|
||||
// rotate the box and check the bounding box is still correct
|
||||
AZ::Quaternion quat = AZ::Quaternion::CreateRotationZ(0.25f * AZ::Constants::Pi);
|
||||
AZ::TransformBus::Event(box->GetId(), &AZ::TransformInterface::SetWorldTM,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(quat, boxPosition));
|
||||
box->Deactivate();
|
||||
box->Activate();
|
||||
|
||||
Physics::RigidBodyRequestBus::EventResult(boxAabb, box->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAabb);
|
||||
|
||||
AZ::Vector3 expectedRotatedDimensions(3.5f * sqrtf(2.0f), 3.5f * sqrtf(2.0f), 5.0f);
|
||||
EXPECT_TRUE(boxAabb.GetMin().IsClose(boxPosition - 0.5f * expectedRotatedDimensions));
|
||||
EXPECT_TRUE(boxAabb.GetMax().IsClose(boxPosition + 0.5f * expectedRotatedDimensions));
|
||||
|
||||
delete box;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, GetAabb_Capsule_ValidExtents)
|
||||
{
|
||||
AZ::Vector3 capsulePosition(1.0f, -3.0f, 5.0f);
|
||||
float capsuleHeight = 2.0f;
|
||||
float capsuleRadius = 0.3f;
|
||||
auto capsule = AddCapsuleEntity(capsulePosition, capsuleHeight, capsuleRadius);
|
||||
|
||||
AZ::Aabb capsuleAabb;
|
||||
Physics::RigidBodyRequestBus::EventResult(capsuleAabb, capsule->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAabb);
|
||||
|
||||
AZ::Vector3 expectedCapsuleHalfExtents(capsuleRadius, capsuleRadius, 0.5f * capsuleHeight);
|
||||
|
||||
EXPECT_TRUE(capsuleAabb.GetMin().IsClose(capsulePosition - expectedCapsuleHalfExtents));
|
||||
EXPECT_TRUE(capsuleAabb.GetMax().IsClose(capsulePosition + expectedCapsuleHalfExtents));
|
||||
|
||||
// rotate the capsule and check the bounding box is still correct
|
||||
AZ::Quaternion quat = AZ::Quaternion::CreateRotationY(0.25f * AZ::Constants::Pi);
|
||||
AZ::TransformBus::Event(capsule->GetId(), &AZ::TransformInterface::SetWorldTM,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(quat, capsulePosition));
|
||||
capsule->Deactivate();
|
||||
capsule->Activate();
|
||||
|
||||
Physics::RigidBodyRequestBus::EventResult(capsuleAabb, capsule->GetId(),
|
||||
&Physics::RigidBodyRequests::GetAabb);
|
||||
|
||||
float rotatedHalfHeight = 0.25f * sqrtf(2.0f) * capsuleHeight + (1.0f - 0.5f * sqrt(2.0f)) * capsuleRadius;
|
||||
expectedCapsuleHalfExtents = AZ::Vector3(rotatedHalfHeight, capsuleRadius, rotatedHalfHeight);
|
||||
EXPECT_TRUE(capsuleAabb.GetMin().IsClose(capsulePosition - expectedCapsuleHalfExtents));
|
||||
EXPECT_TRUE(capsuleAabb.GetMax().IsClose(capsulePosition + expectedCapsuleHalfExtents));
|
||||
|
||||
delete capsule;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, ForceAwakeForceAsleep_DynamicSphere_SleepStateCorrect)
|
||||
{
|
||||
auto floor = AddStaticBoxEntity(AZ::Vector3::CreateZero(), AZ::Vector3(100.0f, 100.0f, 1.0f));
|
||||
auto boxA = AddBoxEntity(AZ::Vector3(-5.0f, 0.0f, 1.0f), AZ::Vector3::CreateOne());
|
||||
auto boxB = AddBoxEntity(AZ::Vector3(5.0f, 0.0f, 100.0f), AZ::Vector3::CreateOne());
|
||||
|
||||
UpdateDefaultWorld(60);
|
||||
bool isAwakeA = false;
|
||||
bool isAwakeB = false;
|
||||
Physics::RigidBodyRequestBus::EventResult(isAwakeA, boxA->GetId(),
|
||||
&Physics::RigidBodyRequests::IsAwake);
|
||||
Physics::RigidBodyRequestBus::EventResult(isAwakeB, boxB->GetId(),
|
||||
&Physics::RigidBodyRequests::IsAwake);
|
||||
|
||||
EXPECT_FALSE(isAwakeA);
|
||||
EXPECT_TRUE(isAwakeB);
|
||||
|
||||
Physics::RigidBodyRequestBus::Event(boxA->GetId(), &Physics::RigidBodyRequests::ForceAwake);
|
||||
Physics::RigidBodyRequestBus::Event(boxB->GetId(), &Physics::RigidBodyRequests::ForceAsleep);
|
||||
|
||||
UpdateDefaultWorld(1);
|
||||
|
||||
Physics::RigidBodyRequestBus::EventResult(isAwakeA, boxA->GetId(),
|
||||
&Physics::RigidBodyRequests::IsAwake);
|
||||
Physics::RigidBodyRequestBus::EventResult(isAwakeB, boxB->GetId(),
|
||||
&Physics::RigidBodyRequests::IsAwake);
|
||||
|
||||
EXPECT_TRUE(isAwakeA);
|
||||
EXPECT_FALSE(isAwakeB);
|
||||
|
||||
UpdateDefaultWorld(60);
|
||||
|
||||
Physics::RigidBodyRequestBus::EventResult(isAwakeA, boxA->GetId(),
|
||||
&Physics::RigidBodyRequests::IsAwake);
|
||||
Physics::RigidBodyRequestBus::EventResult(isAwakeB, boxB->GetId(),
|
||||
&Physics::RigidBodyRequests::IsAwake);
|
||||
|
||||
EXPECT_FALSE(isAwakeA);
|
||||
EXPECT_FALSE(isAwakeB);
|
||||
|
||||
delete boxA;
|
||||
delete boxB;
|
||||
delete floor;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, DisableEnablePhysics_DynamicSphere)
|
||||
{
|
||||
using namespace AzFramework;
|
||||
|
||||
auto sphere = AddSphereEntity(AZ::Vector3(0.0f, 0.0f, 0.0f), 0.5f);
|
||||
Physics::RigidBodyRequestBus::Event(sphere->GetId(), &Physics::RigidBodyRequests::SetLinearDamping, 0.0f);
|
||||
|
||||
AZ::Vector3 velocity;
|
||||
float previousSpeed = 0.0f;
|
||||
for (int timestep = 0; timestep < 30; timestep++)
|
||||
{
|
||||
UpdateDefaultWorld(1);
|
||||
Physics::RigidBodyRequestBus::EventResult(velocity, sphere->GetId(), &Physics::RigidBodyRequests::GetLinearVelocity);
|
||||
previousSpeed = velocity.GetLength();
|
||||
}
|
||||
|
||||
// Disable physics
|
||||
Physics::RigidBodyRequestBus::Event(sphere->GetId(), &Physics::RigidBodyRequests::DisablePhysics);
|
||||
|
||||
// Check speed is not changing
|
||||
for (int timestep = 0; timestep < 60; timestep++)
|
||||
{
|
||||
UpdateDefaultWorld(1);
|
||||
Physics::RigidBodyRequestBus::EventResult(velocity, sphere->GetId(), &Physics::RigidBodyRequests::GetLinearVelocity);
|
||||
float speed = velocity.GetLength();
|
||||
EXPECT_FLOAT_EQ(speed, previousSpeed);
|
||||
previousSpeed = speed;
|
||||
}
|
||||
|
||||
// Check physics is disabled
|
||||
bool physicsEnabled = true;
|
||||
Physics::RigidBodyRequestBus::EventResult(physicsEnabled, sphere->GetId(), &Physics::RigidBodyRequests::IsPhysicsEnabled);
|
||||
EXPECT_FALSE(physicsEnabled);
|
||||
|
||||
// Enable physics
|
||||
Physics::RigidBodyRequestBus::Event(sphere->GetId(), &Physics::RigidBodyRequests::EnablePhysics);
|
||||
|
||||
// Check speed is increasing
|
||||
for (int timestep = 0; timestep < 60; timestep++)
|
||||
{
|
||||
UpdateDefaultWorld(1);
|
||||
Physics::RigidBodyRequestBus::EventResult(velocity, sphere->GetId(), &Physics::RigidBodyRequests::GetLinearVelocity);
|
||||
float speed = velocity.GetLength();
|
||||
EXPECT_GT(speed, previousSpeed);
|
||||
previousSpeed = speed;
|
||||
}
|
||||
|
||||
delete sphere;
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, Shape_Box_GetAabbIsCorrect)
|
||||
{
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
Physics::BoxShapeConfiguration shapeConfiguration;
|
||||
shapeConfiguration.m_dimensions = AZ::Vector3(20.f, 20.f, 20.f);
|
||||
AZStd::shared_ptr<Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
|
||||
const AZ::Aabb localAabb = shape->GetAabbLocal();
|
||||
EXPECT_TRUE(localAabb.GetMin().IsClose(-shapeConfiguration.m_dimensions / 2.f)
|
||||
&& localAabb.GetMax().IsClose(shapeConfiguration.m_dimensions / 2.f));
|
||||
|
||||
AZ::Vector3 worldOffset = AZ::Vector3(0, 0, 40.f);
|
||||
AZ::Transform worldTransform = AZ::Transform::Identity();
|
||||
worldTransform.SetTranslation(worldOffset);
|
||||
const AZ::Aabb worldAabb = shape->GetAabb(worldTransform);
|
||||
EXPECT_TRUE(worldAabb.GetMin().IsClose((-shapeConfiguration.m_dimensions / 2.f) + worldOffset)
|
||||
&& worldAabb.GetMax().IsClose((shapeConfiguration.m_dimensions / 2.f) + worldOffset));
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, Shape_Sphere_GetAabbIsCorrect)
|
||||
{
|
||||
const float radius = 20.f;
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
Physics::SphereShapeConfiguration shapeConfiguration;
|
||||
shapeConfiguration.m_radius = radius;
|
||||
AZStd::shared_ptr<Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
|
||||
const AZ::Aabb localAabb = shape->GetAabbLocal();
|
||||
EXPECT_TRUE(localAabb.GetMin().IsClose(AZ::Vector3(-radius, -radius, -radius))
|
||||
&& localAabb.GetMax().IsClose(AZ::Vector3(radius, radius, radius)));
|
||||
|
||||
AZ::Vector3 worldOffset = AZ::Vector3(0, 0, 40.f);
|
||||
AZ::Transform worldTransform = AZ::Transform::Identity();
|
||||
worldTransform.SetTranslation(worldOffset);
|
||||
const AZ::Aabb worldAabb = shape->GetAabb(worldTransform);
|
||||
EXPECT_TRUE(worldAabb.GetMin().IsClose(AZ::Vector3(-radius, -radius, -radius) + worldOffset)
|
||||
&& worldAabb.GetMax().IsClose(AZ::Vector3(radius, radius, radius) + worldOffset));
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, Shape_Capsule_GetAabbIsCorrect)
|
||||
{
|
||||
const float radius = 20.f;
|
||||
const float height = 80.f;
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
Physics::CapsuleShapeConfiguration shapeConfiguration;
|
||||
shapeConfiguration.m_radius = radius;
|
||||
shapeConfiguration.m_height = height;
|
||||
AZStd::shared_ptr<Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
|
||||
const AZ::Aabb localAabb = shape->GetAabbLocal();
|
||||
EXPECT_TRUE(localAabb.GetMin().IsClose(AZ::Vector3(-radius, -radius, -height / 2.f))
|
||||
&& localAabb.GetMax().IsClose(AZ::Vector3(radius, radius, height / 2.f)));
|
||||
|
||||
AZ::Vector3 worldOffset = AZ::Vector3(0, 0, 40.f);
|
||||
AZ::Transform worldTransform = AZ::Transform::Identity();
|
||||
worldTransform.SetTranslation(worldOffset);
|
||||
const AZ::Aabb worldAabb = shape->GetAabb(worldTransform);
|
||||
EXPECT_TRUE(worldAabb.GetMin().IsClose(AZ::Vector3(-radius, -radius, -height / 2.f) + worldOffset)
|
||||
&& worldAabb.GetMax().IsClose(AZ::Vector3(radius, radius, height / 2.f) + worldOffset));
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, WorldBodyBus_RigidBodyColliders_AABBAreCorrect)
|
||||
{
|
||||
// Create 3 colliders, one of each type and check that the AABB of their body is the expected
|
||||
AZStd::unique_ptr<AZ::Entity> box = AZStd::unique_ptr<AZ::Entity>(AddBoxEntity(AZ::Vector3(0, 0, 0), AZ::Vector3(32, 32, 32)));
|
||||
AZ::Aabb boxAABB;
|
||||
Physics::WorldBodyRequestBus::EventResult(boxAABB, box->GetId(), &Physics::WorldBodyRequests::GetAabb);
|
||||
EXPECT_TRUE(boxAABB.GetMin().IsClose(AZ::Vector3(-16, -16, -16)) && boxAABB.GetMax().IsClose(AZ::Vector3(16, 16, 16)));
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> sphere = AZStd::unique_ptr<AZ::Entity>(AddSphereEntity(AZ::Vector3(-100, 0, 0), 16));
|
||||
AZ::Aabb sphereAABB;
|
||||
Physics::WorldBodyRequestBus::EventResult(sphereAABB, sphere->GetId(), &Physics::WorldBodyRequests::GetAabb);
|
||||
EXPECT_TRUE(sphereAABB.GetMin().IsClose(AZ::Vector3(-16 -100, -16, -16)) && sphereAABB.GetMax().IsClose(AZ::Vector3(16 -100, 16, 16)));
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> capsule = AZStd::unique_ptr<AZ::Entity>(AddCapsuleEntity(AZ::Vector3(100, 0, 0), 128, 16));
|
||||
AZ::Aabb capsuleAABB;
|
||||
Physics::WorldBodyRequestBus::EventResult(capsuleAABB, capsule->GetId(), &Physics::WorldBodyRequests::GetAabb);
|
||||
EXPECT_TRUE(capsuleAABB.GetMin().IsClose(AZ::Vector3(-16 +100, -16, -64)) && capsuleAABB.GetMax().IsClose(AZ::Vector3(16 +100, 16, 64)));
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, WorldBodyBus_StaticRigidBodyColliders_AABBAreCorrect)
|
||||
{
|
||||
// Create 3 colliders, one of each type and check that the AABB of their body is the expected
|
||||
AZStd::unique_ptr<AZ::Entity> box = AZStd::unique_ptr<AZ::Entity>(AddStaticBoxEntity(AZ::Vector3(0, 0, 0), AZ::Vector3(32, 32, 32)));
|
||||
AZ::Aabb boxAABB;
|
||||
Physics::WorldBodyRequestBus::EventResult(boxAABB, box->GetId(), &Physics::WorldBodyRequests::GetAabb);
|
||||
EXPECT_TRUE(boxAABB.GetMin().IsClose(AZ::Vector3(-16, -16, -16)) && boxAABB.GetMax().IsClose(AZ::Vector3(16, 16, 16)));
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> sphere = AZStd::unique_ptr<AZ::Entity>(AddStaticSphereEntity(AZ::Vector3(-100, 0, 0), 16));
|
||||
AZ::Aabb sphereAABB;
|
||||
Physics::WorldBodyRequestBus::EventResult(sphereAABB, sphere->GetId(), &Physics::WorldBodyRequests::GetAabb);
|
||||
EXPECT_TRUE(sphereAABB.GetMin().IsClose(AZ::Vector3(-16 -100, -16, -16)) && sphereAABB.GetMax().IsClose(AZ::Vector3(16 -100, 16, 16)));
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> capsule = AZStd::unique_ptr<AZ::Entity>(AddStaticCapsuleEntity(AZ::Vector3(100, 0, 0), 128, 16));
|
||||
AZ::Aabb capsuleAABB;
|
||||
Physics::WorldBodyRequestBus::EventResult(capsuleAABB, capsule->GetId(), &Physics::WorldBodyRequests::GetAabb);
|
||||
EXPECT_TRUE(capsuleAABB.GetMin().IsClose(AZ::Vector3(-16 +100, -16, -64)) && capsuleAABB.GetMax().IsClose(AZ::Vector3(16 +100, 16, 64)));
|
||||
}
|
||||
|
||||
using CreateEntityFunc = AZStd::function<AZStd::unique_ptr<AZ::Entity>(const AZ::Vector3&)>;
|
||||
|
||||
void CheckDisableEnablePhysics(AZStd::vector<CreateEntityFunc> entityCreations)
|
||||
{
|
||||
// Fake Pointer for filling result to make sure that m_body has changed
|
||||
Physics::WorldBody* fakeBody = (Physics::WorldBody*)(0x1234);
|
||||
|
||||
int i = 0;
|
||||
for (CreateEntityFunc entityCreation : entityCreations)
|
||||
{
|
||||
AZ::Vector3 entityPos = AZ::Vector3(128.f * i, 0, 0);
|
||||
AZStd::unique_ptr<AZ::Entity> entity = entityCreation(entityPos);
|
||||
|
||||
Physics::RayCastHit hit;
|
||||
Physics::RayCastRequest request;
|
||||
request.m_start = entityPos + AZ::Vector3(0, 0, 100);
|
||||
request.m_direction = AZ::Vector3(0, 0, -1);
|
||||
request.m_distance = 200.f;
|
||||
|
||||
Physics::WorldBodyRequestBus::Event(entity->GetId(), &Physics::WorldBodyRequests::DisablePhysics);
|
||||
|
||||
bool enabled = true;
|
||||
Physics::WorldBodyRequestBus::EventResult(enabled, entity->GetId(), &Physics::WorldBodyRequests::IsPhysicsEnabled);
|
||||
EXPECT_FALSE(enabled);
|
||||
|
||||
hit.m_body = fakeBody;
|
||||
Physics::WorldRequestBus::BroadcastResult(hit, &Physics::WorldRequests::RayCast, request);
|
||||
EXPECT_FALSE(hit);
|
||||
|
||||
Physics::WorldBodyRequestBus::Event(entity->GetId(), &Physics::WorldBodyRequests::EnablePhysics);
|
||||
|
||||
enabled = false;
|
||||
Physics::WorldBodyRequestBus::EventResult(enabled, entity->GetId(), &Physics::WorldBodyRequests::IsPhysicsEnabled);
|
||||
EXPECT_TRUE(enabled);
|
||||
|
||||
hit.m_body = nullptr;
|
||||
Physics::WorldRequestBus::BroadcastResult(hit, &Physics::WorldRequests::RayCast, request);
|
||||
EXPECT_TRUE(hit && hit.m_body->GetEntityId() == entity->GetId());
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, WorldBodyBus_EnableDisablePhysics_StaticRigidBody)
|
||||
{
|
||||
AZStd::vector<CreateEntityFunc> entityCreations =
|
||||
{
|
||||
[this](const AZ::Vector3& position) { return AZStd::unique_ptr<AZ::Entity>(AddStaticBoxEntity(position, AZ::Vector3(32, 32, 32))); },
|
||||
[this](const AZ::Vector3& position) { return AZStd::unique_ptr<AZ::Entity>(AddStaticSphereEntity(position, 16)); },
|
||||
[this](const AZ::Vector3& position) { return AZStd::unique_ptr<AZ::Entity>(AddStaticCapsuleEntity(position, 16, 16)); }
|
||||
};
|
||||
CheckDisableEnablePhysics(entityCreations);
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, WorldBodyBus_EnableDisablePhysics_RigidBody)
|
||||
{
|
||||
AZStd::vector<CreateEntityFunc> entityCreations =
|
||||
{
|
||||
[this](const AZ::Vector3& position) { return AZStd::unique_ptr<AZ::Entity>(AddBoxEntity(position, AZ::Vector3(32, 32, 32))); },
|
||||
[this](const AZ::Vector3& position) { return AZStd::unique_ptr<AZ::Entity>(AddSphereEntity(position, 16)); },
|
||||
[this](const AZ::Vector3& position) { return AZStd::unique_ptr<AZ::Entity>(AddCapsuleEntity(position, 16, 16)); }
|
||||
};
|
||||
CheckDisableEnablePhysics(entityCreations);
|
||||
}
|
||||
|
||||
TEST_F(PhysicsComponentBusTest, WorldBodyRayCast_CastAgainstStaticBox_ReturnsHit)
|
||||
{
|
||||
AZStd::unique_ptr<AZ::Entity> staticBoxEntity(AddStaticBoxEntity(AZ::Vector3(0.0f), AZ::Vector3(10.f, 10.f, 10.f)));
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
Physics::RayCastHit hit;
|
||||
Physics::WorldBodyRequestBus::EventResult(hit, staticBoxEntity->GetId(), &Physics::WorldBodyRequests::RayCast, request);
|
||||
|
||||
EXPECT_TRUE(hit);
|
||||
|
||||
bool hitIncludeSphereEntity = (hit.m_body->GetEntityId() == staticBoxEntity->GetId());
|
||||
EXPECT_TRUE(hitIncludeSphereEntity);
|
||||
}
|
||||
|
||||
using RayCastFunc = AZStd::function<Physics::RayCastHit(AZ::EntityId, const Physics::RayCastRequest&)>;
|
||||
class PhysicsRigidBodyRayBusTest
|
||||
: public Physics::GenericPhysicsInterfaceTest
|
||||
, public ::testing::WithParamInterface<RayCastFunc>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstNothing_ReturnsNoHit)
|
||||
{
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
auto rayCastFunction = GetParam();
|
||||
RayCastHit hit = rayCastFunction(AZ::EntityId(), request);
|
||||
|
||||
EXPECT_FALSE(hit);
|
||||
}
|
||||
|
||||
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstSphere_ReturnsHit)
|
||||
{
|
||||
AZStd::unique_ptr<AZ::Entity> sphereEntity(AddSphereEntity(AZ::Vector3(0.0f), 10.0f));
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
auto rayCastFunction = GetParam();
|
||||
RayCastHit hit = rayCastFunction(sphereEntity->GetId(), request);
|
||||
|
||||
EXPECT_TRUE(hit);
|
||||
|
||||
bool hitsIncludeSphereEntity = (hit.m_body->GetEntityId() == sphereEntity->GetId());
|
||||
EXPECT_TRUE(hitsIncludeSphereEntity);
|
||||
}
|
||||
|
||||
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstBoxEntityWithLocalOffset_ReturnsHit)
|
||||
{
|
||||
const AZ::Vector3 boxExtent = AZ::Vector3(10.0f, 10.0f, 10.0f);
|
||||
const AZ::Vector3 box1Offset(0.0f, 0.0f, 30.0f);
|
||||
const AZ::Vector3 box2Offset(0.0f, 0.0f, -30.0f);
|
||||
|
||||
MultiShapeConfig config;
|
||||
config.m_position = AZ::Vector3(0.0f, 100.0f, 20.0f);
|
||||
config.m_shapes.AddBox(boxExtent, box1Offset);
|
||||
config.m_shapes.AddBox(boxExtent, box2Offset);
|
||||
|
||||
auto shapeWithTwoBoxes = AddMultiShapeEntity(config);
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 100.0f, 50.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
auto rayCastFunction = GetParam();
|
||||
RayCastHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
|
||||
bool hitIncludeEntity = (result.m_body->GetEntityId() == shapeWithTwoBoxes->GetId());
|
||||
EXPECT_TRUE(hitIncludeEntity);
|
||||
}
|
||||
|
||||
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstBoxEntityWithMultipleShapesLocalOffset_ReturnsHits)
|
||||
{
|
||||
// Entity at (0, 100, 20) with two box childs with offsets +30 and -30 in Z.
|
||||
// Child boxes world position centers are at (0, 100, 50) and (0, 100, -10)
|
||||
// 4 rays tests that should retrieves the correct boxes
|
||||
|
||||
const AZ::Vector3 boxExtent = AZ::Vector3(10.0f, 10.0f, 10.0f);
|
||||
const AZ::Vector3 box1Offset(0.0f, 0.0f, 30.0f);
|
||||
const AZ::Vector3 box2Offset(0.0f, 0.0f, -30.0f);
|
||||
|
||||
MultiShapeConfig config;
|
||||
config.m_position = AZ::Vector3(0.0f, 100.0f, 20.0f);
|
||||
config.m_shapes.AddBox(boxExtent, box1Offset);
|
||||
config.m_shapes.AddBox(boxExtent, box2Offset);
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> shapeWithTwoBoxes(AddMultiShapeEntity(config));
|
||||
AZStd::vector<AZStd::shared_ptr<Physics::Shape>> shapes;
|
||||
PhysX::ColliderComponentRequestBus::EventResult(shapes, shapeWithTwoBoxes->GetId(), &PhysX::ColliderComponentRequests::GetShapes);
|
||||
|
||||
// Upper box part z=50 (-x to +x)
|
||||
{
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 100.0f, 50.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
auto rayCastFunction = GetParam();
|
||||
RayCastHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
bool hitIncludesEntity = (result.m_body->GetEntityId() == shapeWithTwoBoxes->GetId());
|
||||
EXPECT_TRUE(hitIncludesEntity);
|
||||
bool hitIncludesShape = (result.m_shape == shapes[0].get());
|
||||
EXPECT_TRUE(hitIncludesShape);
|
||||
}
|
||||
|
||||
// Lower box part z=-10 (-x to +x)
|
||||
{
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 100.0f, -10.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
auto rayCastFunction = GetParam();
|
||||
RayCastHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
bool hitIncludesEntity = (result.m_body->GetEntityId() == shapeWithTwoBoxes->GetId());
|
||||
EXPECT_TRUE(hitIncludesEntity);
|
||||
bool hitIncludesShape = (result.m_shape == shapes[1].get());
|
||||
EXPECT_TRUE(hitIncludesShape);
|
||||
}
|
||||
|
||||
// Trace Vertically from top, it should retrieve the upper box shape
|
||||
{
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(0.0f, 100.0f, 80.0f);
|
||||
request.m_direction = AZ::Vector3(0.0f, 0.0f, -1.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
auto rayCastFunction = GetParam();
|
||||
RayCastHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
bool hitIncludesEntity = (result.m_body->GetEntityId() == shapeWithTwoBoxes->GetId());
|
||||
EXPECT_TRUE(hitIncludesEntity);
|
||||
bool hitIncludesShape = (result.m_shape == shapes[0].get());
|
||||
EXPECT_TRUE(hitIncludesShape);
|
||||
}
|
||||
|
||||
// Trace Vertically from bottom, it should retrieve the lower box shape
|
||||
{
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(0.0f, 100.0f, -80.0f);
|
||||
request.m_direction = AZ::Vector3(0.0f, 0.0f, 1.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
auto rayCastFunction = GetParam();
|
||||
RayCastHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
bool hitIncludesEntity = (result.m_body->GetEntityId() == shapeWithTwoBoxes->GetId());
|
||||
EXPECT_TRUE(hitIncludesEntity);
|
||||
bool hitIncludesShape = (result.m_shape == shapes[1].get());
|
||||
EXPECT_TRUE(hitIncludesShape);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstBoxEntityLocalOffsetAndRotation_ReturnsHits)
|
||||
{
|
||||
// Entity at (0,0,0) rotated by 90 degrees and child box offset (0,100,0).
|
||||
// World position of the child should be (-100, 0, 0).
|
||||
// This tests raycasts from (0, 0, 0) to (-200, 0 ,0) checking that collides with the box
|
||||
|
||||
const AZ::Vector3 boxExtent = AZ::Vector3(10.0f, 10.0f, 10.0f);
|
||||
const AZ::Vector3 boxOffset(0.0f, 100.0f, 0.0f);
|
||||
|
||||
MultiShapeConfig config;
|
||||
config.m_position = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
config.m_rotation = AZ::Vector3(0, 0, AZ::Constants::HalfPi);
|
||||
config.m_shapes.AddBox(boxExtent, boxOffset);
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> shapeWithOneBox(AddMultiShapeEntity(config));
|
||||
AZStd::vector<AZStd::shared_ptr<Physics::Shape>> shapes;
|
||||
PhysX::ColliderComponentRequestBus::EventResult(shapes, shapeWithOneBox->GetId(), &PhysX::ColliderComponentRequests::GetShapes);
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(-1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
auto rayCastFunction = GetParam();
|
||||
RayCastHit result = rayCastFunction(shapeWithOneBox->GetId(), request);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
bool hitIncludesEntity = (result.m_body->GetEntityId() == shapeWithOneBox->GetId());
|
||||
EXPECT_TRUE(hitIncludesEntity);
|
||||
bool hitIncludesShape = (result.m_shape == shapes[0].get());
|
||||
EXPECT_TRUE(hitIncludesShape);
|
||||
}
|
||||
|
||||
static const RayCastFunc RigidBodyRaycastEBusCall = [](AZ::EntityId entityId, const Physics::RayCastRequest& request)
|
||||
{
|
||||
Physics::RayCastHit ret;
|
||||
Physics::RigidBodyRequestBus::EventResult(ret, entityId, &Physics::RigidBodyRequests::RayCast, request);
|
||||
return ret;
|
||||
};
|
||||
|
||||
static const RayCastFunc WorldBodyRaycastEBusCall = [](AZ::EntityId entityId, const Physics::RayCastRequest& request)
|
||||
{
|
||||
Physics::RayCastHit ret;
|
||||
Physics::WorldBodyRequestBus::EventResult(ret, entityId, &Physics::WorldBodyRequests::RayCast, request);
|
||||
return ret;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(, PhysicsRigidBodyRayBusTest,
|
||||
::testing::Values(RigidBodyRaycastEBusCall, WorldBodyRaycastEBusCall),
|
||||
// Provide nice names for the tests runs
|
||||
[](const testing::TestParamInfo<PhysicsRigidBodyRayBusTest::ParamType>& info)
|
||||
{
|
||||
const char* name = "";
|
||||
switch (info.index)
|
||||
{
|
||||
case 0:
|
||||
name = "RigidBodyRequestBus";
|
||||
break;
|
||||
case 1:
|
||||
name = "WorldBodyRequestBus";
|
||||
break;
|
||||
}
|
||||
return name;
|
||||
});
|
||||
|
||||
|
||||
} // namespace Physics
|
||||
@@ -1,919 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzFramework/Physics/Shape.h>
|
||||
#include <AzFramework/Physics/RigidBodyBus.h>
|
||||
#include <AzFramework/Physics/World.h>
|
||||
#include <PhysX/ColliderComponentBus.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
static auto GetEntityInRayCastHitCallBack = [](AZ::EntityId entityId)
|
||||
{
|
||||
return [entityId](const RayCastHit& hit)
|
||||
{
|
||||
return hit.m_body->GetEntityId() == entityId;
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, World_CreateNewWorld_ReturnsNewWorld)
|
||||
{
|
||||
EXPECT_TRUE(CreateTestWorld() != nullptr);
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_CastAgainstNothing_ReturnsNoHits)
|
||||
{
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
RayCastHit hit;
|
||||
WorldRequestBus::BroadcastResult(hit, &WorldRequests::RayCast, request);
|
||||
|
||||
EXPECT_FALSE(hit);
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_CastAgainstSphere_ReturnsHits)
|
||||
{
|
||||
auto sphereEntity = AddSphereEntity(AZ::Vector3(0.0f), 10.0f);
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
RayCastHit hit;
|
||||
WorldRequestBus::BroadcastResult(hit, &WorldRequests::RayCast, request);
|
||||
|
||||
EXPECT_TRUE(hit);
|
||||
|
||||
bool hitsIncludeSphereEntity = (hit.m_body->GetEntityId() == sphereEntity->GetId());
|
||||
EXPECT_TRUE(hitsIncludeSphereEntity);
|
||||
|
||||
delete sphereEntity;
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_CastAgainstSphere_ReturnsCorrectShapeAndMaterial)
|
||||
{
|
||||
auto sphereEntity = AZStd::shared_ptr<AZ::Entity>(AddSphereEntity(AZ::Vector3(0.0f), 10.0f));
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
RayCastHit result;
|
||||
WorldRequestBus::BroadcastResult(result, &WorldRequests::RayCast, request);
|
||||
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
RigidBody* rigidBody;
|
||||
Physics::RigidBodyRequestBus::EventResult(rigidBody, sphereEntity->GetId(), &RigidBodyRequestBus::Events::GetRigidBody);
|
||||
|
||||
ASSERT_NE(rigidBody->GetShape(0), nullptr);
|
||||
ASSERT_NE(result.m_material, nullptr);
|
||||
ASSERT_EQ(result.m_shape, rigidBody->GetShape(0).get());
|
||||
ASSERT_EQ(result.m_material, rigidBody->GetShape(0).get()->GetMaterial().get());
|
||||
|
||||
const AZStd::string& typeName = result.m_material->GetSurfaceTypeName();
|
||||
ASSERT_EQ(typeName, AZStd::string("Default"));
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_CastAgainstStaticObject_ReturnsHits)
|
||||
{
|
||||
auto boxEntity = AZStd::shared_ptr<AZ::Entity>(AddStaticBoxEntity(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(10.0f, 10.0f, 10.0f)));
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_distance = 200.0f;
|
||||
|
||||
RayCastHit result;
|
||||
WorldRequestBus::BroadcastResult(result, &WorldRequests::RayCast, request);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
|
||||
bool hitsIncludeEntity = (result.m_body->GetEntityId() == boxEntity->GetId());
|
||||
EXPECT_TRUE(hitsIncludeEntity);
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_CastAgainstFilteredSpheres_ReturnsHits)
|
||||
{
|
||||
auto entity1 = AddSphereEntity(AZ::Vector3(0.0f, 0.0f, 10.0f), 10.0f, CollisionLayer(0));
|
||||
auto entity2 = AddCapsuleEntity(AZ::Vector3(0.0f, 0.0f, 20.0f), 10.0f, 2.0f, CollisionLayer(1));
|
||||
auto entity3 = AddStaticBoxEntity(AZ::Vector3(0.0f, 0.0f, 30.0f), AZ::Vector3(20.0f, 20.0f, 20.0f), CollisionLayer(2));
|
||||
|
||||
CollisionGroup group = CollisionGroup::All;
|
||||
group.SetLayer(CollisionLayer(0), true);
|
||||
group.SetLayer(CollisionLayer(1), false);
|
||||
group.SetLayer(CollisionLayer(2), true);
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(0.0f, 0.0f, 1.0f);
|
||||
request.m_distance = 200.0f;
|
||||
request.m_collisionGroup = group;
|
||||
|
||||
AZStd::vector<RayCastHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::RayCastMultiple, request);
|
||||
|
||||
ASSERT_TRUE(hits.size() == 2);
|
||||
EXPECT_TRUE(hits[1].m_body->GetEntityId() == entity1->GetId());
|
||||
EXPECT_TRUE(hits[0].m_body->GetEntityId() == entity3->GetId());
|
||||
|
||||
delete entity1;
|
||||
delete entity2;
|
||||
delete entity3;
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_AgainstStaticOnly_ReturnsStaticBox)
|
||||
{
|
||||
auto dynamicSphere = AddSphereEntity(AZ::Vector3(0.0f, 0.0f, 10.0f), 10.0f);
|
||||
auto staticBox = AddStaticBoxEntity(AZ::Vector3(0.0f, 0.0f, 30.0f), AZ::Vector3(20.0f, 20.0f, 20.0f));
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(0.0f, 0.0f, 1.0f);
|
||||
request.m_queryType = Physics::QueryType::Static;
|
||||
|
||||
AZStd::vector<RayCastHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::RayCastMultiple, request);
|
||||
|
||||
ASSERT_EQ(hits.size(), 1);
|
||||
ASSERT_EQ(hits[0].m_body->GetEntityId(), staticBox->GetId());
|
||||
|
||||
delete dynamicSphere;
|
||||
delete staticBox;
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_AgainstDynamicOnly_ReturnsDynamicSphere)
|
||||
{
|
||||
auto dynamicSphere = AddSphereEntity(AZ::Vector3(0.0f, 0.0f, 10.0f), 10.0f);
|
||||
auto staticBox = AddStaticBoxEntity(AZ::Vector3(0.0f, 0.0f, 30.0f), AZ::Vector3(20.0f, 20.0f, 20.0f));
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(0.0f, 0.0f, 1.0f);
|
||||
request.m_queryType = Physics::QueryType::Dynamic;
|
||||
|
||||
AZStd::vector<RayCastHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::RayCastMultiple, request);
|
||||
|
||||
ASSERT_EQ(hits.size(), 1);
|
||||
ASSERT_EQ(hits[0].m_body->GetEntityId(), dynamicSphere->GetId());
|
||||
|
||||
delete dynamicSphere;
|
||||
delete staticBox;
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_AgainstStaticAndDynamic_ReturnsBothObjects)
|
||||
{
|
||||
auto dynamicSphere = AddSphereEntity(AZ::Vector3(0.0f, 0.0f, 10.0f), 10.0f);
|
||||
auto staticBox = AddStaticBoxEntity(AZ::Vector3(0.0f, 0.0f, 30.0f), AZ::Vector3(20.0f, 20.0f, 20.0f));
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(0.0f, 0.0f, 1.0f);
|
||||
request.m_queryType = Physics::QueryType::StaticAndDynamic;
|
||||
|
||||
AZStd::vector<RayCastHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::RayCastMultiple, request);
|
||||
|
||||
ASSERT_EQ(hits.size(), 2);
|
||||
ASSERT_EQ(hits[0].m_body->GetEntityId(), staticBox->GetId());
|
||||
ASSERT_EQ(hits[1].m_body->GetEntityId(), dynamicSphere->GetId());
|
||||
|
||||
delete dynamicSphere;
|
||||
delete staticBox;
|
||||
}
|
||||
TEST_F(GenericPhysicsInterfaceTest, RayCast_AgainstMultipleTouchAndBlockHits_ReturnsClosestBlockAndTouches)
|
||||
{
|
||||
auto dynamicSphere = AddSphereEntity(AZ::Vector3(20.0f, 0.0f, 0.0f), 10.0f);
|
||||
auto staticBox = AddStaticBoxEntity(AZ::Vector3(40.0f, 0.0f, 0.0f), AZ::Vector3(5.0f, 5.0f, 5.0f));
|
||||
auto blockingSphere = AddSphereEntity(AZ::Vector3(60.0f, 0.0f, 0.0f), 5.0f);
|
||||
auto blockingBox = AddStaticBoxEntity(AZ::Vector3(80.0f, 0.0f, 0.0f), AZ::Vector3(5.0f, 5.0f, 5.0f));
|
||||
auto farSphere = AddSphereEntity(AZ::Vector3(120.0f, 0.0f, 0.0f), 10.0f);
|
||||
|
||||
RayCastRequest request;
|
||||
request.m_start = AZ::Vector3(0.0f, 0.0f, 0.0f);
|
||||
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
|
||||
request.m_queryType = Physics::QueryType::StaticAndDynamic;
|
||||
request.m_filterCallback = [&](const Physics::WorldBody* body, [[maybe_unused]] const Physics::Shape* shape)
|
||||
{
|
||||
if (body->GetEntityId() == blockingBox->GetId() || body->GetEntityId() == blockingSphere->GetId())
|
||||
{
|
||||
return QueryHitType::Block;
|
||||
}
|
||||
return QueryHitType::Touch;
|
||||
};
|
||||
|
||||
AZStd::vector<RayCastHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::RayCastMultiple, request);
|
||||
|
||||
ASSERT_EQ(hits.size(), 3);
|
||||
|
||||
ASSERT_EQ(1, AZStd::count_if(hits.begin(), hits.end(), GetEntityInRayCastHitCallBack(dynamicSphere->GetId())));
|
||||
ASSERT_EQ(1, AZStd::count_if(hits.begin(), hits.end(), GetEntityInRayCastHitCallBack(staticBox->GetId())));
|
||||
ASSERT_EQ(1, AZStd::count_if(hits.begin(), hits.end(), GetEntityInRayCastHitCallBack(blockingSphere->GetId())));
|
||||
|
||||
delete dynamicSphere;
|
||||
delete staticBox;
|
||||
delete blockingSphere;
|
||||
delete blockingBox;
|
||||
delete farSphere;
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ShapeCast_CastAgainstNothing_ReturnsNoHits)
|
||||
{
|
||||
Physics::RayCastHit hit;
|
||||
WorldRequestBus::BroadcastResult(hit, &WorldRequests::SphereCast,
|
||||
1.0f,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(-20.0f, 0.0f, 0.0f)),
|
||||
AZ::Vector3(1.0f, 0.0f, 0.0f),
|
||||
20.0f, Physics::QueryType::StaticAndDynamic,
|
||||
Physics::CollisionGroup::All,
|
||||
nullptr
|
||||
);
|
||||
|
||||
EXPECT_FALSE(hit);
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ShapeCast_CastAgainstSphere_ReturnsHits)
|
||||
{
|
||||
auto sphereEntity = AddSphereEntity(AZ::Vector3(0.0f), 10.0f);
|
||||
|
||||
Physics::RayCastHit hit;
|
||||
WorldRequestBus::BroadcastResult(hit, &WorldRequests::SphereCast,
|
||||
1.0f,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(-20.0f, 0.0f, 0.0f)),
|
||||
AZ::Vector3(1.0f, 0.0f, 0.0f),
|
||||
20.0f, Physics::QueryType::StaticAndDynamic,
|
||||
Physics::CollisionGroup::All,
|
||||
nullptr
|
||||
);
|
||||
|
||||
EXPECT_TRUE(hit);
|
||||
EXPECT_EQ(hit.m_body->GetEntityId(), sphereEntity->GetId());
|
||||
|
||||
// clear up scene
|
||||
delete sphereEntity;
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ShapeCast_SphereCastAgainstStaticObject_ReturnsHits)
|
||||
{
|
||||
auto boxEntity = AZStd::shared_ptr<AZ::Entity>(AddStaticBoxEntity(AZ::Vector3(0.0f, 0.0f, 0.0f), AZ::Vector3(1.0f, 1.0f, 1.0f)));
|
||||
|
||||
Physics::RayCastHit hit;
|
||||
WorldRequestBus::BroadcastResult(hit, &WorldRequests::SphereCast,
|
||||
1.5f,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(-20.0f, 0.0f, 0.0f)),
|
||||
AZ::Vector3(1.0f, 0.0f, 0.0f),
|
||||
20.0f, Physics::QueryType::StaticAndDynamic,
|
||||
Physics::CollisionGroup::All,
|
||||
nullptr
|
||||
);
|
||||
|
||||
EXPECT_TRUE(hit);
|
||||
EXPECT_EQ(hit.m_body->GetEntityId(), boxEntity->GetId());
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ShapeCast_SphereCastAgainstFilteredObjects_ReturnsHits)
|
||||
{
|
||||
auto entity1 = AddSphereEntity(AZ::Vector3(0.0f, 0.0f, 10.0f), 10.0f, CollisionLayer(0));
|
||||
auto entity2 = AddCapsuleEntity(AZ::Vector3(0.0f, 0.0f, 20.0f), 10.0f, 2.0f, CollisionLayer(1));
|
||||
auto entity3 = AddStaticBoxEntity(AZ::Vector3(0.0f, 0.0f, 30.0f), AZ::Vector3(20.0f, 20.0f, 20.0f), CollisionLayer(2));
|
||||
|
||||
CollisionGroup group = CollisionGroup::All;
|
||||
group.SetLayer(CollisionLayer(0), true);
|
||||
group.SetLayer(CollisionLayer(1), false);
|
||||
group.SetLayer(CollisionLayer(2), true);
|
||||
|
||||
AZStd::vector<Physics::RayCastHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::SphereCastMultiple,
|
||||
1.5f,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)),
|
||||
AZ::Vector3(0.0f, 0.0f, 1.0f),
|
||||
200.0f, Physics::QueryType::StaticAndDynamic,
|
||||
group,
|
||||
nullptr
|
||||
);
|
||||
|
||||
ASSERT_TRUE(hits.size() == 2);
|
||||
EXPECT_TRUE(hits[1].m_body->GetEntityId() == entity1->GetId());
|
||||
EXPECT_TRUE(hits[0].m_body->GetEntityId() == entity3->GetId());
|
||||
|
||||
delete entity1;
|
||||
delete entity2;
|
||||
delete entity3;
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ShapeCast_AgainstMultipleTouchAndBlockHits_ReturnsClosestBlockAndTouches)
|
||||
{
|
||||
auto dynamicSphere = AddSphereEntity(AZ::Vector3(20.0f, 0.0f, 0.0f), 10.0f);
|
||||
auto staticBox = AddStaticBoxEntity(AZ::Vector3(40.0f, 0.0f, 0.0f), AZ::Vector3(5.0f, 5.0f, 5.0f));
|
||||
auto blockingSphere = AddSphereEntity(AZ::Vector3(60.0f, 0.0f, 0.0f), 5.0f);
|
||||
auto blockingBox = AddStaticBoxEntity(AZ::Vector3(80.0f, 0.0f, 0.0f), AZ::Vector3(5.0f, 5.0f, 5.0f));
|
||||
auto farSphere = AddSphereEntity(AZ::Vector3(120.0f, 0.0f, 0.0f), 10.0f);
|
||||
|
||||
AZStd::vector<Physics::RayCastHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::SphereCastMultiple,
|
||||
1.5f,
|
||||
AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 0.0f, 0.0f)),
|
||||
AZ::Vector3(1.0f, 0.0f, 0.0f),
|
||||
200.0f, Physics::QueryType::StaticAndDynamic,
|
||||
Physics::CollisionGroup::All,
|
||||
[&](const Physics::WorldBody* body, [[maybe_unused]] const Physics::Shape* shape)
|
||||
{
|
||||
if (body->GetEntityId() == blockingBox->GetId() || body->GetEntityId() == blockingSphere->GetId())
|
||||
{
|
||||
return QueryHitType::Block;
|
||||
}
|
||||
return QueryHitType::Touch;
|
||||
}
|
||||
);
|
||||
|
||||
ASSERT_EQ(hits.size(), 3);
|
||||
|
||||
ASSERT_EQ(1, AZStd::count_if(hits.begin(), hits.end(), GetEntityInRayCastHitCallBack(dynamicSphere->GetId())));
|
||||
ASSERT_EQ(1, AZStd::count_if(hits.begin(), hits.end(), GetEntityInRayCastHitCallBack(staticBox->GetId())));
|
||||
ASSERT_EQ(1, AZStd::count_if(hits.begin(), hits.end(), GetEntityInRayCastHitCallBack(blockingSphere->GetId())));
|
||||
|
||||
delete dynamicSphere;
|
||||
delete staticBox;
|
||||
delete blockingSphere;
|
||||
delete blockingBox;
|
||||
delete farSphere;
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, Overlap_OverlapMultipleObjects_ReturnsHits)
|
||||
{
|
||||
AZStd::shared_ptr<AZ::Entity> sphereEntity(AddSphereEntity(AZ::Vector3(10.0f, 0.0f, 0.0f), 3.0f));
|
||||
AZStd::shared_ptr<AZ::Entity> boxEntity(AddBoxEntity(AZ::Vector3(7.0f, 4.0f, 0.0f), AZ::Vector3(1.0f)));
|
||||
AZStd::shared_ptr<AZ::Entity> capsuleEntity(AddCapsuleEntity(AZ::Vector3(15.0f, 0.0f, 0.0f), 3.0f, 1.0f));
|
||||
|
||||
BoxShapeConfiguration overlapShape;
|
||||
overlapShape.m_dimensions = AZ::Vector3(3.0f);
|
||||
|
||||
OverlapRequest request;
|
||||
request.m_pose = AZ::Transform::CreateTranslation(AZ::Vector3(13.0f, 0.0f, 0.0f));
|
||||
request.m_shapeConfiguration = &overlapShape;
|
||||
|
||||
AZStd::vector<OverlapHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::Overlap, request);
|
||||
|
||||
EXPECT_EQ(hits.size(), 2);
|
||||
|
||||
// boxEntity shouldn't be included in the result
|
||||
EXPECT_FALSE(AZStd::any_of(hits.begin(), hits.end(),
|
||||
[idToFind = boxEntity->GetId()](const OverlapHit& hit) { return hit.m_body->GetEntityId() == idToFind; }));
|
||||
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, Overlap_OverlapMultipleObjectsUseFriendlyFunctions_ReturnsHits)
|
||||
{
|
||||
AZStd::shared_ptr<AZ::Entity> sphereEntity(AddSphereEntity(AZ::Vector3(10.0f, 0.0f, 0.0f), 3.0f));
|
||||
AZStd::shared_ptr<AZ::Entity> boxEntity(AddBoxEntity(AZ::Vector3(7.0f, 4.0f, 0.0f), AZ::Vector3(1.0f)));
|
||||
AZStd::shared_ptr<AZ::Entity> capsuleEntity(AddCapsuleEntity(AZ::Vector3(15.0f, 0.0f, 0.0f), 3.0f, 1.0f));
|
||||
|
||||
AZStd::shared_ptr<World> defaultWorld;
|
||||
DefaultWorldBus::BroadcastResult(defaultWorld, &DefaultWorldRequests::GetDefaultWorld);
|
||||
|
||||
{
|
||||
AZStd::vector<OverlapHit> hits = defaultWorld->OverlapBox(AZ::Vector3(3.0f), AZ::Transform::CreateTranslation(AZ::Vector3(13.0f, 0.0f, 0.0f)));
|
||||
|
||||
EXPECT_EQ(hits.size(), 2);
|
||||
|
||||
// boxEntity shouldn't be included in the result
|
||||
EXPECT_FALSE(AZStd::any_of(hits.begin(), hits.end(),
|
||||
[idToFind = boxEntity->GetId()](const OverlapHit& hit) { return hit.m_body->GetEntityId() == idToFind; }));
|
||||
}
|
||||
|
||||
{
|
||||
AZStd::vector<OverlapHit> hits = defaultWorld->OverlapSphere(3.0f, AZ::Transform::CreateTranslation(AZ::Vector3(13.0f, 0.0f, 0.0f)));
|
||||
|
||||
EXPECT_EQ(hits.size(), 2);
|
||||
|
||||
// boxEntity shouldn't be included in the result
|
||||
EXPECT_FALSE(AZStd::any_of(hits.begin(), hits.end(),
|
||||
[idToFind = boxEntity->GetId()](const OverlapHit& hit) { return hit.m_body->GetEntityId() == idToFind; }));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, Overlap_OverlapMultipleObjectsUseFriendlyFunctionsCustomFiltering_ReturnsHits)
|
||||
{
|
||||
AZStd::shared_ptr<AZ::Entity> sphereEntity(AddSphereEntity(AZ::Vector3(10.0f, 0.0f, 0.0f), 3.0f));
|
||||
AZStd::shared_ptr<AZ::Entity> boxEntity(AddBoxEntity(AZ::Vector3(7.0f, 4.0f, 0.0f), AZ::Vector3(1.0f)));
|
||||
AZStd::shared_ptr<AZ::Entity> capsuleEntity(AddCapsuleEntity(AZ::Vector3(15.0f, 0.0f, 0.0f), 3.0f, 1.0f));
|
||||
|
||||
AZStd::shared_ptr<World> defaultWorld;
|
||||
DefaultWorldBus::BroadcastResult(defaultWorld, &DefaultWorldRequests::GetDefaultWorld);
|
||||
|
||||
// Here we do an overlap test that covers all objects in the scene
|
||||
// However we provide a custom filtering function that filters out a specific entity
|
||||
{
|
||||
AZ::EntityId entityIdToFilterOut = capsuleEntity->GetId();
|
||||
|
||||
AZStd::vector<Physics::OverlapHit> hits = defaultWorld->OverlapCapsule(100.0f, 30.0f, AZ::Transform::CreateTranslation(AZ::Vector3(13.0f, 0.0f, 0.0f)),
|
||||
[entityIdToFilterOut](const Physics::WorldBody* body, [[maybe_unused]] const Physics::Shape* shape)
|
||||
{
|
||||
return body->GetEntityId() != entityIdToFilterOut;
|
||||
});
|
||||
|
||||
EXPECT_EQ(hits.size(), 2);
|
||||
|
||||
EXPECT_FALSE(AZStd::any_of(hits.begin(), hits.end(),
|
||||
[entityIdToFilterOut](const OverlapHit& hit) { return hit.m_body->GetEntityId() == entityIdToFilterOut; }));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, Overlap_OverlapMultipleObjects_ReturnsFilteredHits)
|
||||
{
|
||||
AZStd::shared_ptr<AZ::Entity> sphereEntity(AddSphereEntity(AZ::Vector3(10.0f, 0.0f, 0.0f), 3.0f, CollisionLayer(0)));
|
||||
AZStd::shared_ptr<AZ::Entity> boxEntity(AddStaticBoxEntity(AZ::Vector3(12.0f, 0.0f, 0.0f), AZ::Vector3(1.0f), CollisionLayer(1)));
|
||||
AZStd::shared_ptr<AZ::Entity> capsuleEntity(AddCapsuleEntity(AZ::Vector3(14.0f, 0.0f, 0.0f), 3.0f, 1.0f, CollisionLayer(2)));
|
||||
|
||||
BoxShapeConfiguration overlapShape;
|
||||
overlapShape.m_dimensions = AZ::Vector3(1.0f);
|
||||
|
||||
OverlapRequest request;
|
||||
request.m_pose = AZ::Transform::CreateTranslation(AZ::Vector3(13.0f, 0.0f, 0.0f));
|
||||
request.m_shapeConfiguration = &overlapShape;
|
||||
request.m_collisionGroup = CollisionGroup::All;
|
||||
request.m_collisionGroup.SetLayer(CollisionLayer(0), false); // Filter out the sphere
|
||||
request.m_collisionGroup.SetLayer(CollisionLayer(1), true);
|
||||
request.m_collisionGroup.SetLayer(CollisionLayer(2), true);
|
||||
|
||||
AZStd::vector<OverlapHit> hits;
|
||||
WorldRequestBus::BroadcastResult(hits, &WorldRequests::Overlap, request);
|
||||
|
||||
EXPECT_EQ(hits.size(), 2);
|
||||
EXPECT_FALSE(AZStd::any_of(hits.begin(), hits.end(),
|
||||
[sphereEntity](const OverlapHit& hit)
|
||||
{
|
||||
// Make sure the sphere was not included
|
||||
return hit.m_body->GetEntityId() == sphereEntity->GetId();
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, Gravity_DynamicBody_BodyFalls)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto rigidBody = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 0.0f, 100.0f));
|
||||
UpdateWorld(world.get(), 1.0f / 60.f, 60);
|
||||
|
||||
// expect velocity to be -gt and distance fallen to be 1/2gt^2, but allow quite a lot of tolerance
|
||||
// due to potential differences in back end integration schemes etc.
|
||||
EXPECT_NEAR(rigidBody->GetLinearVelocity().GetZ(), -10.0f, 0.5f);
|
||||
EXPECT_NEAR(rigidBody->GetTransform().GetTranslation().GetZ(), 95.0f, 0.5f);
|
||||
EXPECT_NEAR(rigidBody->GetCenterOfMassWorld().GetZ(), 95.0f, 0.5f);
|
||||
EXPECT_NEAR(rigidBody->GetPosition().GetZ(), 95.0f, 0.5f);
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, World_SplitSimulation_BodyFallsTheSameInBothWorlds)
|
||||
{
|
||||
auto worldA = CreateTestWorld();
|
||||
auto worldB = CreateTestWorld();
|
||||
|
||||
AZ::Vector3 initialPosition(0.0f, 0.0f, 100.0f);
|
||||
|
||||
auto rigidBodyA = AddUnitBoxToWorld(worldA.get(), initialPosition);
|
||||
auto rigidBodyB = AddUnitBoxToWorld(worldB.get(), initialPosition);
|
||||
|
||||
Physics::WorldConfiguration worldConfiguration;
|
||||
float deltaTime = worldConfiguration.m_fixedTimeStep;
|
||||
|
||||
AZ::u32 numSteps = 60;
|
||||
|
||||
UpdateWorld(worldA.get(), deltaTime, numSteps);
|
||||
UpdateWorldSplitSim(worldB.get(), deltaTime, numSteps);
|
||||
|
||||
// expect velocity to be -gt and distance fallen to be 1/2gt^2, but allow quite a lot of tolerance
|
||||
// due to potential differences in back end integration schemes etc.
|
||||
EXPECT_NEAR(rigidBodyA->GetLinearVelocity().GetZ(), -10.0f, 0.5f);
|
||||
EXPECT_NEAR(rigidBodyA->GetTransform().GetTranslation().GetZ(), 95.0f, 0.5f);
|
||||
EXPECT_NEAR(rigidBodyA->GetCenterOfMassWorld().GetZ(), 95.0f, 0.5f);
|
||||
EXPECT_NEAR(rigidBodyA->GetPosition().GetZ(), 95.0f, 0.5f);
|
||||
|
||||
// Verify simulation results are the same
|
||||
EXPECT_TRUE(rigidBodyA->GetLinearVelocity().IsClose(rigidBodyB->GetLinearVelocity()));
|
||||
EXPECT_TRUE(rigidBodyA->GetTransform().GetTranslation().IsClose(rigidBodyB->GetTransform().GetTranslation()));
|
||||
EXPECT_TRUE(rigidBodyA->GetCenterOfMassWorld().IsClose(rigidBodyB->GetCenterOfMassWorld()));
|
||||
EXPECT_TRUE(rigidBodyA->GetPosition().IsClose(rigidBodyB->GetPosition()));
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, IncreaseMass_StaggeredTowerOfBoxes_TowerOverbalances)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
|
||||
// make a tower of boxes which is staggered but should still balance if all the blocks are the same mass
|
||||
auto boxA = AddStaticUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 0.0f, 0.5f));
|
||||
auto boxB = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.3f, 0.0f, 1.5f));
|
||||
auto boxC = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.6f, 0.0f, 2.5f));
|
||||
|
||||
// check that the tower balances
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 60);
|
||||
EXPECT_NEAR(2.5f, boxC->GetPosition().GetZ(), 0.01f);
|
||||
|
||||
// increasing the mass of the top block in the tower should overbalance it
|
||||
boxC->SetMass(5.0f);
|
||||
EXPECT_NEAR(1.0f, boxB->GetMass(), 0.01f);
|
||||
EXPECT_NEAR(1.0f, boxB->GetInverseMass(), 0.01f);
|
||||
EXPECT_NEAR(5.0f, boxC->GetMass(), 0.01f);
|
||||
EXPECT_NEAR(0.2f, boxC->GetInverseMass(), 0.01f);
|
||||
boxB->ForceAwake();
|
||||
boxC->ForceAwake();
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 300);
|
||||
EXPECT_GT(0.0f, static_cast<float>(boxC->GetPosition().GetZ()));
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, GetCenterOfMass_FallingBody_CenterOfMassCorrectDuringFall)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto boxStatic = AddStaticUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 0.0f, 0.0f));
|
||||
auto boxDynamic = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 0.0f, 2.0f));
|
||||
auto tolerance = 1e-3f;
|
||||
|
||||
EXPECT_TRUE(boxDynamic->GetCenterOfMassWorld().IsClose(AZ::Vector3(0.0f, 0.0f, 2.0f), tolerance));
|
||||
EXPECT_TRUE(boxDynamic->GetCenterOfMassLocal().IsClose(AZ::Vector3(0.0f, 0.0f, 0.0f), tolerance));
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 300);
|
||||
EXPECT_NEAR(static_cast<float>(boxDynamic->GetCenterOfMassWorld().GetZ()), 1.0f, 1e-3f);
|
||||
EXPECT_TRUE(boxDynamic->GetCenterOfMassLocal().IsClose(AZ::Vector3(0.0f, 0.0f, 0.0f), tolerance));
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, SetLinearVelocity_DynamicBox_AffectsTrajectory)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto boxA = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, -5.0f, 10.0f));
|
||||
auto boxB = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 5.0f, 10.0f));
|
||||
|
||||
boxA->SetLinearVelocity(AZ::Vector3(10.0f, 0.0f, 0.0f));
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
float xPreviousA = boxA->GetPosition().GetX();
|
||||
float xPreviousB = boxB->GetPosition().GetX();
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 10);
|
||||
EXPECT_GT(static_cast<float>(boxA->GetPosition().GetX()), xPreviousA);
|
||||
EXPECT_NEAR(boxB->GetPosition().GetX(), xPreviousB, 1e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ApplyLinearImpulse_DynamicBox_AffectsTrajectory)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto boxA = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 0.0f, 100.0f));
|
||||
auto boxB = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 10.0f, 100.0f));
|
||||
|
||||
boxA->ApplyLinearImpulse(AZ::Vector3(10.0f, 0.0f, 0.0f));
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
float xPreviousA = boxA->GetPosition().GetX();
|
||||
float xPreviousB = boxB->GetPosition().GetX();
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 10);
|
||||
EXPECT_GT(static_cast<float>(boxA->GetPosition().GetX()), xPreviousA);
|
||||
EXPECT_NEAR(boxB->GetPosition().GetX(), xPreviousB, 1e-3f);
|
||||
}
|
||||
}
|
||||
|
||||
// allow a more generous tolerance on tests involving objects in contact, since the way physics engines normally
|
||||
// handle multiple contacts between objects can lead to slight imbalances in contact forces
|
||||
static constexpr float ContactTestTolerance = 0.01f;
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, GetAngularVelocity_DynamicCapsuleOnSlope_GainsAngularVelocity)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
AZ::Transform slopeTransform = AZ::Transform::CreateRotationY(0.1f);
|
||||
auto slope = AddStaticFloorToWorld(world.get(), slopeTransform);
|
||||
auto capsule = AddCapsuleToWorld(world.get(), slopeTransform.TransformPoint(AZ::Vector3::CreateAxisZ()));
|
||||
|
||||
// the capsule should roll down the slope, picking up angular velocity parallel to the Y axis
|
||||
float angularVelocityMagnitude = capsule->GetAngularVelocity().GetLength();
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 60);
|
||||
angularVelocityMagnitude = capsule->GetAngularVelocity().GetLength();
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
world->Update(1.0f / 60.0f);
|
||||
auto angularVelocity = capsule->GetAngularVelocity();
|
||||
EXPECT_TRUE(angularVelocity.IsPerpendicular(AZ::Vector3::CreateAxisX(), ContactTestTolerance));
|
||||
EXPECT_TRUE(angularVelocity.IsPerpendicular(AZ::Vector3::CreateAxisZ(), ContactTestTolerance));
|
||||
EXPECT_TRUE(capsule->GetAngularVelocity().GetLength() > angularVelocityMagnitude);
|
||||
angularVelocityMagnitude = angularVelocity.GetLength();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, SetAngularVelocity_DynamicCapsule_StartsRolling)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto floor = AddStaticFloorToWorld(world.get());
|
||||
auto capsule = AddCapsuleToWorld(world.get(), AZ::Vector3::CreateAxisZ());
|
||||
|
||||
// capsule should remain stationary
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
world->Update(1.0f / 60.0f);
|
||||
EXPECT_TRUE(capsule->GetPosition().IsClose(AZ::Vector3::CreateAxisZ(), ContactTestTolerance));
|
||||
EXPECT_TRUE(capsule->GetLinearVelocity().IsClose(AZ::Vector3::CreateZero(), ContactTestTolerance));
|
||||
EXPECT_TRUE(capsule->GetAngularVelocity().IsClose(AZ::Vector3::CreateZero(), ContactTestTolerance));
|
||||
}
|
||||
|
||||
// apply an angular velocity and it should start rolling
|
||||
auto angularVelocity = AZ::Vector3::CreateAxisY(10.0f);
|
||||
capsule->SetAngularVelocity(angularVelocity);
|
||||
EXPECT_TRUE(capsule->GetAngularVelocity().IsClose(angularVelocity));
|
||||
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
float xPrevious = capsule->GetPosition().GetX();
|
||||
world->Update(1.0f / 60.0f);
|
||||
EXPECT_TRUE(capsule->GetPosition().GetX() > xPrevious);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, GetLinearVelocityAtWorldPoint_FallingRotatingCapsule_EdgeVelocitiesCorrect)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
|
||||
// create dynamic capsule and start it falling and rotating
|
||||
auto capsule = AddCapsuleToWorld(world.get(), AZ::Vector3::CreateAxisZ());
|
||||
float angularVelocityMagnitude = 1.0f;
|
||||
capsule->SetAngularVelocity(AZ::Vector3::CreateAxisY(angularVelocityMagnitude));
|
||||
capsule->SetAngularDamping(0.0f);
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 60);
|
||||
|
||||
// check the velocities at some points on the rim of the capsule are as expected
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
world->Update(1.0f / 60.0f);
|
||||
auto position = capsule->GetPosition();
|
||||
float fallingSpeed = capsule->GetLinearVelocity().GetZ();
|
||||
float radius = 0.5f;
|
||||
AZ::Vector3 z = AZ::Vector3::CreateAxisZ(radius);
|
||||
AZ::Vector3 x = AZ::Vector3::CreateAxisX(radius);
|
||||
|
||||
auto v1 = capsule->GetLinearVelocityAtWorldPoint(position - z);
|
||||
auto v2 = capsule->GetLinearVelocityAtWorldPoint(position - x);
|
||||
auto v3 = capsule->GetLinearVelocityAtWorldPoint(position + x);
|
||||
|
||||
EXPECT_TRUE(v1.IsClose(AZ::Vector3(-radius * angularVelocityMagnitude, 0.0f, fallingSpeed)));
|
||||
EXPECT_TRUE(v2.IsClose(AZ::Vector3(0.0f, 0.0f, fallingSpeed + radius * angularVelocityMagnitude)));
|
||||
EXPECT_TRUE(v3.IsClose(AZ::Vector3(0.0f, 0.0f, fallingSpeed - radius * angularVelocityMagnitude)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, GetPosition_RollingCapsule_OrientationCorrect)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto floor = AddStaticFloorToWorld(world.get());
|
||||
|
||||
// create dynamic capsule and start it rolling
|
||||
auto capsule = AddCapsuleToWorld(world.get(), AZ::Vector3::CreateAxisZ());
|
||||
capsule->SetLinearVelocity(AZ::Vector3::CreateAxisX(5.0f));
|
||||
capsule->SetAngularVelocity(AZ::Vector3::CreateAxisY(10.0f));
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 60);
|
||||
|
||||
// check the capsule orientation evolves as expected
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
auto orientationPrevious = capsule->GetOrientation();
|
||||
float xPrevious = capsule->GetPosition().GetX();
|
||||
world->Update(1.0f / 60.0f);
|
||||
float angle = 2.0f * (capsule->GetPosition().GetX() - xPrevious);
|
||||
EXPECT_TRUE(capsule->GetOrientation().IsClose(orientationPrevious * AZ::Quaternion::CreateRotationY(angle)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, OffCenterImpulse_DynamicCapsule_StartsRotating)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto floor = AddStaticFloorToWorld(world.get());
|
||||
AZ::Vector3 posA(0.0f, -5.0f, 1.0f);
|
||||
AZ::Vector3 posB(0.0f, 0.0f, 1.0f);
|
||||
AZ::Vector3 posC(0.0f, 5.0f, 1.0f);
|
||||
auto capsuleA = AddCapsuleToWorld(world.get(), posA);
|
||||
auto capsuleB = AddCapsuleToWorld(world.get(), posB);
|
||||
auto capsuleC = AddCapsuleToWorld(world.get(), posC);
|
||||
|
||||
// all the capsules should be stationary initially
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
world->Update(1.0f / 60.0f);
|
||||
EXPECT_TRUE(capsuleA->GetPosition().IsClose(posA));
|
||||
EXPECT_TRUE(capsuleA->GetAngularVelocity().IsClose(AZ::Vector3::CreateZero(), ContactTestTolerance));
|
||||
EXPECT_TRUE(capsuleB->GetPosition().IsClose(posB));
|
||||
EXPECT_TRUE(capsuleB->GetAngularVelocity().IsClose(AZ::Vector3::CreateZero(), ContactTestTolerance));
|
||||
EXPECT_TRUE(capsuleC->GetPosition().IsClose(posC));
|
||||
EXPECT_TRUE(capsuleC->GetAngularVelocity().IsClose(AZ::Vector3::CreateZero(), ContactTestTolerance));
|
||||
}
|
||||
|
||||
// apply off-center impulses to capsule A and C, and an impulse through the center of B
|
||||
AZ::Vector3 impulse(0.0f, 0.0f, 10.0f);
|
||||
capsuleA->ApplyLinearImpulseAtWorldPoint(impulse, posA + AZ::Vector3::CreateAxisX(0.5f));
|
||||
capsuleB->ApplyLinearImpulseAtWorldPoint(impulse, posB);
|
||||
capsuleC->ApplyLinearImpulseAtWorldPoint(impulse, posC + AZ::Vector3::CreateAxisX(-0.5f));
|
||||
|
||||
// A and C should be rotating in opposite directions, B should still have 0 angular velocity
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
world->Update(1.0f / 60.0f);
|
||||
EXPECT_TRUE(capsuleA->GetAngularVelocity().GetY() < 0.0f);
|
||||
EXPECT_TRUE(capsuleB->GetAngularVelocity().IsClose(AZ::Vector3::CreateZero(), ContactTestTolerance));
|
||||
EXPECT_TRUE(capsuleC->GetAngularVelocity().GetY() > 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ApplyAngularImpulse_DynamicSphere_StartsRotating)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto floor = AddStaticFloorToWorld(world.get());
|
||||
|
||||
AZStd::shared_ptr<RigidBody> spheres[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
spheres[i] = AddSphereToWorld(world.get(), AZ::Vector3(0.0f, -5.0f + 5.0f * i, 1.0f));
|
||||
}
|
||||
|
||||
// all the spheres should start stationary
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 10);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
EXPECT_TRUE(spheres[i]->GetAngularVelocity().IsClose(AZ::Vector3::CreateZero()));
|
||||
}
|
||||
|
||||
// apply angular impulses and they should gain angular velocity parallel to the impulse direction
|
||||
AZ::Vector3 impulses[3] = { AZ::Vector3(2.0f, 4.0f, 0.0f), AZ::Vector3(-3.0f, 1.0f, 0.0f),
|
||||
AZ::Vector3(-2.0f, 3.0f, 0.0f) };
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
spheres[i]->ApplyAngularImpulse(impulses[i]);
|
||||
}
|
||||
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 10);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
auto angVel = spheres[i]->GetAngularVelocity();
|
||||
EXPECT_TRUE(angVel.GetProjected(impulses[i]).IsClose(angVel, 0.1f));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, StartAsleep_FallingBox_DoesNotFall)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
|
||||
// Box should start asleep
|
||||
RigidBodyConfiguration config;
|
||||
config.m_startAsleep = true;
|
||||
|
||||
// Create rigid body
|
||||
AZStd::shared_ptr<RigidBody> box;
|
||||
SystemRequestBus::BroadcastResult(box, &SystemRequests::CreateRigidBody, config);
|
||||
world->AddBody(*box);
|
||||
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 100);
|
||||
|
||||
// Check the box is still at 0 and hasn't dropped
|
||||
EXPECT_NEAR(0.0f, box->GetPosition().GetZ(), 0.01f);
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ForceAsleep_FallingBox_BecomesStationary)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto floor = AddStaticFloorToWorld(world.get());
|
||||
auto box = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 0.0f, 10.0f));
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 60);
|
||||
|
||||
EXPECT_TRUE(box->IsAwake());
|
||||
|
||||
auto pos = box->GetPosition();
|
||||
box->ForceAsleep();
|
||||
EXPECT_FALSE(box->IsAwake());
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 30);
|
||||
EXPECT_FALSE(box->IsAwake());
|
||||
// the box should be asleep so it shouldn't have moved
|
||||
EXPECT_TRUE(box->GetPosition().IsClose(pos));
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, ForceAwake_SleepingBox_SleepStateCorrect)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
auto floor = AddStaticFloorToWorld(world.get());
|
||||
auto box = AddUnitBoxToWorld(world.get(), AZ::Vector3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 60);
|
||||
EXPECT_FALSE(box->IsAwake());
|
||||
|
||||
box->ForceAwake();
|
||||
EXPECT_TRUE(box->IsAwake());
|
||||
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 60);
|
||||
// the box should have gone back to sleep
|
||||
EXPECT_FALSE(box->IsAwake());
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, GetAabb_Box_ValidExtents)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
AZ::Vector3 posBox(0.0f, 0.0f, 0.0f);
|
||||
auto box = AddUnitBoxToWorld(world.get(), posBox);
|
||||
|
||||
EXPECT_TRUE(box->GetAabb().GetMin().IsClose(posBox - 0.5f * AZ::Vector3::CreateOne()));
|
||||
EXPECT_TRUE(box->GetAabb().GetMax().IsClose(posBox + 0.5f * AZ::Vector3::CreateOne()));
|
||||
|
||||
// rotate the box and check the bounding box is still correct
|
||||
AZ::Quaternion quat = AZ::Quaternion::CreateRotationZ(0.25f * AZ::Constants::Pi);
|
||||
box->SetTransform(AZ::Transform::CreateFromQuaternionAndTranslation(quat, posBox));
|
||||
|
||||
AZ::Vector3 boxExtent(sqrtf(0.5f), sqrtf(0.5f), 0.5f);
|
||||
EXPECT_TRUE(box->GetAabb().GetMin().IsClose(posBox - boxExtent));
|
||||
EXPECT_TRUE(box->GetAabb().GetMax().IsClose(posBox + boxExtent));
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, GetAabb_Sphere_ValidExtents)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
AZ::Vector3 posSphere(0.0f, 0.0f, 0.0f);
|
||||
auto sphere = AddSphereToWorld(world.get(), posSphere);
|
||||
|
||||
EXPECT_TRUE(sphere->GetAabb().GetMin().IsClose(posSphere - 0.5f * AZ::Vector3::CreateOne()));
|
||||
EXPECT_TRUE(sphere->GetAabb().GetMax().IsClose(posSphere + 0.5f * AZ::Vector3::CreateOne()));
|
||||
|
||||
// rotate the sphere and check the bounding box is still correct
|
||||
AZ::Quaternion quat = AZ::Quaternion::CreateRotationZ(0.25f * AZ::Constants::Pi);
|
||||
sphere->SetTransform(AZ::Transform::CreateFromQuaternionAndTranslation(quat, posSphere));
|
||||
|
||||
EXPECT_TRUE(sphere->GetAabb().GetMin().IsClose(posSphere - 0.5f * AZ::Vector3::CreateOne()));
|
||||
EXPECT_TRUE(sphere->GetAabb().GetMax().IsClose(posSphere + 0.5f * AZ::Vector3::CreateOne()));
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, GetAabb_Capsule_ValidExtents)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
AZ::Vector3 posCapsule(0.0f, 0.0f, 0.0f);
|
||||
auto capsule = AddCapsuleToWorld(world.get(), posCapsule);
|
||||
|
||||
EXPECT_TRUE(capsule->GetAabb().GetMin().IsClose(posCapsule - AZ::Vector3(0.5f, 1.0f, 0.5f)));
|
||||
EXPECT_TRUE(capsule->GetAabb().GetMax().IsClose(posCapsule + AZ::Vector3(0.5f, 1.0f, 0.5f)));
|
||||
|
||||
// rotate the bodies and check the bounding boxes are still correct
|
||||
AZ::Quaternion quat = AZ::Quaternion::CreateRotationZ(0.25f * AZ::Constants::Pi);
|
||||
capsule->SetTransform(AZ::Transform::CreateFromQuaternionAndTranslation(quat, posCapsule));
|
||||
|
||||
AZ::Vector3 capsuleExtent(0.5f + sqrt(0.125f), 0.5f + sqrt(0.125f), 0.5f);
|
||||
EXPECT_TRUE(capsule->GetAabb().GetMin().IsClose(posCapsule - capsuleExtent));
|
||||
EXPECT_TRUE(capsule->GetAabb().GetMax().IsClose(posCapsule + capsuleExtent));
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, Materials_BoxesSharingDefaultMaterial_JumpingSameHeight)
|
||||
{
|
||||
auto world = CreateTestWorld();
|
||||
|
||||
auto boxA = AddStaticFloorToWorld(world.get());
|
||||
auto boxB = AddUnitBoxToWorld(world.get(), AZ::Vector3(1.0f, 0.0f, 10.0f));
|
||||
auto boxC = AddUnitBoxToWorld(world.get(), AZ::Vector3(-1.0f, 0.0f, 10.0f));
|
||||
|
||||
auto material = boxC->GetShape(0)->GetMaterial();
|
||||
material->SetRestitution(1.0f);
|
||||
|
||||
UpdateWorld(world.get(), 1.0f / 60.0f, 150);
|
||||
|
||||
// boxB and boxC should have the same material (default)
|
||||
// so they should both bounce high
|
||||
EXPECT_NEAR(boxB->GetPosition().GetZ(), boxC->GetPosition().GetZ(), 0.5f);
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, World_GetNativePtrByWorldName_ReturnsNativePtr)
|
||||
{
|
||||
void* validNativePtr = nullptr;
|
||||
WorldRequestBus::EventResult(validNativePtr, Physics::DefaultPhysicsWorldId, &WorldRequests::GetNativePointer);
|
||||
EXPECT_TRUE(validNativePtr != nullptr);
|
||||
|
||||
void* invalidNativePtr = nullptr;
|
||||
WorldRequestBus::EventResult(invalidNativePtr, AZ_CRC("Bad World Name"), &WorldRequests::GetNativePointer);
|
||||
EXPECT_TRUE(invalidNativePtr == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(GenericPhysicsInterfaceTest, Collider_ColliderTag_IsSetFromConfiguration)
|
||||
{
|
||||
const AZStd::string colliderTagName = "ColliderTestTag";
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
colliderConfig.m_tag = colliderTagName;
|
||||
Physics::SphereShapeConfiguration shapeConfig;
|
||||
|
||||
AZStd::shared_ptr<Physics::Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfig);
|
||||
|
||||
EXPECT_EQ(shape->GetTag(), AZ::Crc32(colliderTagName));
|
||||
}
|
||||
|
||||
} // namespace Physics
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzFramework/Physics/PhysicsScene.h>
|
||||
#include <AzFramework/Physics/RigidBody.h>
|
||||
#include <AzFramework/Physics/SystemBus.h>
|
||||
#include <AzFramework/Physics/World.h>
|
||||
#include <AzCore/Debug/TraceMessageBus.h>
|
||||
#include <AzFramework/Physics/Common/PhysicsTypes.h>
|
||||
#include <AzFramework/Physics/Collision/CollisionLayers.h>
|
||||
#include <AzFramework/Physics/Configuration/SystemConfiguration.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
class GenericPhysicsFixture
|
||||
: protected Physics::DefaultWorldBus::Handler
|
||||
{
|
||||
public:
|
||||
// Helper functions for setting up test worlds using API only
|
||||
// These can be implemented here as they should not require any gem specific functions
|
||||
AZStd::shared_ptr<Physics::World> CreateTestWorld();
|
||||
void DestroyTestScene();
|
||||
|
||||
void SetUpInternal();
|
||||
void TearDownInternal();
|
||||
|
||||
// Helper functions for setting up entities used in tests
|
||||
// These need to be implemented in the gem as they may require gem specific components etc.
|
||||
AZ::Entity* AddSphereEntity(const AZ::Vector3& position, const float radius, const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default);
|
||||
AZ::Entity* AddBoxEntity(const AZ::Vector3& position, const AZ::Vector3& dimensions, const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default);
|
||||
AZ::Entity* AddCapsuleEntity(const AZ::Vector3& position, const float height, const float radius, const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default);
|
||||
|
||||
AZ::Entity* AddStaticSphereEntity(const AZ::Vector3& position, const float radius, const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default);
|
||||
AZ::Entity* AddStaticBoxEntity(const AZ::Vector3& position, const AZ::Vector3& dimensions, const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default);
|
||||
AZ::Entity* AddStaticCapsuleEntity(const AZ::Vector3& position, const float height, const float radius, const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default);
|
||||
|
||||
// Helper function for creating multishape entity
|
||||
struct MultiShapeConfig
|
||||
{
|
||||
AZ::Vector3 m_position; // Position of entity
|
||||
AZ::Vector3 m_rotation = AZ::Vector3(0.f, 0.f, 0.f); // Euler rotation of entity in radians
|
||||
AzPhysics::CollisionLayer m_layer = AzPhysics::CollisionLayer::Default; // Collision layer
|
||||
|
||||
struct ShapeList
|
||||
{
|
||||
struct ShapeData
|
||||
{
|
||||
struct Box
|
||||
{
|
||||
AZ::Vector3 m_extent;
|
||||
};
|
||||
struct Sphere
|
||||
{
|
||||
float m_radius;
|
||||
};
|
||||
struct Capsule
|
||||
{
|
||||
float m_height;
|
||||
float m_radius;
|
||||
};
|
||||
|
||||
AZ::Vector3 m_offset;
|
||||
AZStd::variant<AZStd::monostate, Box, Sphere, Capsule> m_data;
|
||||
};
|
||||
|
||||
void AddBox(AZ::Vector3 extent, AZ::Vector3 offset)
|
||||
{
|
||||
ShapeData box;
|
||||
ShapeData::Box boxData{ extent };
|
||||
box.m_data = boxData;
|
||||
box.m_offset = offset;
|
||||
m_shapesData.push_back(box);
|
||||
}
|
||||
void AddSphere(float radius, AZ::Vector3 offset)
|
||||
{
|
||||
ShapeData sphere;
|
||||
ShapeData::Sphere sphereData{ radius };
|
||||
sphere.m_data = sphereData;
|
||||
sphere.m_offset = offset;
|
||||
m_shapesData.push_back(sphere);
|
||||
}
|
||||
void AddCapsule(float height, float radius, AZ::Vector3 offset)
|
||||
{
|
||||
ShapeData capsule;
|
||||
ShapeData::Capsule capsuleData{ height, radius };
|
||||
capsule.m_data = capsuleData;
|
||||
capsule.m_offset = offset;
|
||||
m_shapesData.push_back(capsule);
|
||||
}
|
||||
|
||||
AZStd::vector<ShapeData> m_shapesData;
|
||||
};
|
||||
ShapeList m_shapes;
|
||||
};
|
||||
AZStd::unique_ptr<AZ::Entity> AddMultiShapeEntity(const MultiShapeConfig& config);
|
||||
|
||||
protected:
|
||||
// DefaultWorldBus
|
||||
AZStd::shared_ptr<World> GetDefaultWorld();
|
||||
|
||||
AzPhysics::Scene* m_defaultScene = nullptr;
|
||||
AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle;
|
||||
};
|
||||
|
||||
/// Class to contain tests which any implementation of the AzFramework::Physics API should pass
|
||||
/// Each gem which implements the common physics API can run the generic API tests by:
|
||||
/// - including this header file and the appropriate .inl files.
|
||||
/// - deriving from AZ::Test::ITestEnvironment and extending the environment functions to set up the gem system component etc.
|
||||
/// - implementing the helper functions required for the tests using gem specific components etc.
|
||||
/// - adding a AZ_UNIT_TEST_HOOK with the derived environment class
|
||||
class GenericPhysicsInterfaceTest
|
||||
: protected GenericPhysicsFixture
|
||||
, public testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
SetUpInternal();
|
||||
}
|
||||
void TearDown() override
|
||||
{
|
||||
TearDownInternal();
|
||||
|
||||
DestroyTestScene(); //cleanup any physics scene if created.
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class PhysicsComponentBusTest
|
||||
: public GenericPhysicsInterfaceTest
|
||||
{
|
||||
void SetUp() override
|
||||
{
|
||||
GenericPhysicsInterfaceTest::SetUp();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
GenericPhysicsInterfaceTest::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
// helper functions
|
||||
AZStd::shared_ptr<RigidBodyStatic> AddStaticFloorToWorld(World* world, const AZ::Transform& transform = AZ::Transform::CreateIdentity());
|
||||
AZStd::shared_ptr<RigidBodyStatic> AddStaticUnitBoxToWorld(World* world, const AZ::Vector3& position);
|
||||
|
||||
AZStd::shared_ptr<RigidBody> AddUnitBoxToWorld(World* world, const AZ::Vector3& position);
|
||||
AZStd::shared_ptr<RigidBody> AddSphereToWorld(World* world, const AZ::Vector3& position);
|
||||
AZStd::shared_ptr<RigidBody> AddCapsuleToWorld(World* world, const AZ::Vector3& position);
|
||||
|
||||
void UpdateScene(AzPhysics::Scene* scene, float timeStep, AZ::u32 numSteps);
|
||||
float GetPositionElement(AZ::Entity* entity, int element);
|
||||
} // namespace Physics
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "PhysicsTests.h"
|
||||
#include <AzFramework/Physics/World.h>
|
||||
#include <AzFramework/Physics/RigidBody.h>
|
||||
#include <AzFramework/Physics/Shape.h>
|
||||
#include <AzFramework/Physics/SystemBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
// helper functions
|
||||
AZStd::shared_ptr<World> GenericPhysicsFixture::CreateTestWorld()
|
||||
{
|
||||
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
|
||||
{
|
||||
AzPhysics::SceneConfiguration sceneConfiguration = physicsSystem->GetDefaultSceneConfiguration();
|
||||
sceneConfiguration.m_legacyId = AZ_CRC_CE("testWorld");
|
||||
sceneConfiguration.m_legacyConfiguration.m_gravity = AZ::Vector3(0.0f, 0.0f, -10.0f);
|
||||
m_testSceneHandle = physicsSystem->AddScene(sceneConfiguration);
|
||||
m_defaultScene = physicsSystem->GetScene(m_testSceneHandle);
|
||||
return m_defaultScene->GetLegacyWorld();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GenericPhysicsFixture::DestroyTestScene()
|
||||
{
|
||||
//clean up the test scene
|
||||
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
|
||||
{
|
||||
physicsSystem->RemoveScene(m_testSceneHandle);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<RigidBodyStatic> AddStaticFloorToWorld(World* world, const AZ::Transform& transform)
|
||||
{
|
||||
WorldBodyConfiguration rigidBodySettings;
|
||||
AZStd::shared_ptr<RigidBodyStatic> floor;
|
||||
SystemRequestBus::BroadcastResult(floor, &SystemRequests::CreateStaticRigidBody, rigidBodySettings);
|
||||
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
Physics::BoxShapeConfiguration shapeConfiguration(AZ::Vector3(20.0f, 20.0f, 1.0f));
|
||||
AZStd::shared_ptr<Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
|
||||
floor->AddShape(shape);
|
||||
|
||||
world->AddBody(*floor);
|
||||
floor->SetTransform(transform);
|
||||
return floor;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<RigidBodyStatic> AddStaticUnitBoxToWorld(World* world, const AZ::Vector3& position)
|
||||
{
|
||||
WorldBodyConfiguration rigidBodySettings;
|
||||
rigidBodySettings.m_position = position;
|
||||
AZStd::shared_ptr<RigidBodyStatic> box;
|
||||
SystemRequestBus::BroadcastResult(box, &SystemRequests::CreateStaticRigidBody, rigidBodySettings);
|
||||
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
Physics::BoxShapeConfiguration shapeConfiguration;
|
||||
AZStd::shared_ptr<Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
|
||||
box->AddShape(shape);
|
||||
|
||||
world->AddBody(*box);
|
||||
return box;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<RigidBody> AddUnitBoxToWorld(World* world, const AZ::Vector3& position)
|
||||
{
|
||||
RigidBodyConfiguration rigidBodySettings;
|
||||
rigidBodySettings.m_linearDamping = 0.0f;
|
||||
AZStd::shared_ptr<RigidBody> box;
|
||||
SystemRequestBus::BroadcastResult(box, &SystemRequests::CreateRigidBody, rigidBodySettings);
|
||||
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
Physics::BoxShapeConfiguration shapeConfiguration;
|
||||
AZStd::shared_ptr<Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
|
||||
box->AddShape(shape);
|
||||
|
||||
world->AddBody(*box.get());
|
||||
box->SetTransform(AZ::Transform::CreateTranslation(position));
|
||||
return box;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<RigidBody> AddSphereToWorld(World* world, const AZ::Vector3& position)
|
||||
{
|
||||
RigidBodyConfiguration rigidBodySettings;
|
||||
rigidBodySettings.m_linearDamping = 0.0f;
|
||||
AZStd::shared_ptr<RigidBody> sphere;
|
||||
SystemRequestBus::BroadcastResult(sphere, &SystemRequests::CreateRigidBody, rigidBodySettings);
|
||||
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
Physics::SphereShapeConfiguration shapeConfiguration;
|
||||
AZStd::shared_ptr<Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
|
||||
sphere->AddShape(shape);
|
||||
|
||||
world->AddBody(*sphere.get());
|
||||
sphere->SetTransform(AZ::Transform::CreateTranslation(position));
|
||||
return sphere;
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<RigidBody> AddCapsuleToWorld(World* world, const AZ::Vector3& position)
|
||||
{
|
||||
RigidBodyConfiguration rigidBodySettings;
|
||||
AZStd::shared_ptr<RigidBody> capsule = nullptr;
|
||||
SystemRequestBus::BroadcastResult(capsule, &SystemRequests::CreateRigidBody, rigidBodySettings);
|
||||
|
||||
Physics::ColliderConfiguration colliderConfig;
|
||||
colliderConfig.m_rotation = AZ::Quaternion::CreateRotationX(AZ::Constants::HalfPi);
|
||||
Physics::CapsuleShapeConfiguration shapeConfig(2.0f, 0.5f);
|
||||
AZStd::shared_ptr<Shape> shape;
|
||||
SystemRequestBus::BroadcastResult(shape, &SystemRequests::CreateShape, colliderConfig, shapeConfig);
|
||||
capsule->AddShape(shape);
|
||||
|
||||
world->AddBody(*capsule.get());
|
||||
capsule->SetTransform(AZ::Transform::CreateTranslation(position));
|
||||
return capsule;
|
||||
}
|
||||
|
||||
void UpdateScene(AzPhysics::Scene* scene, float timeStep, AZ::u32 numSteps)
|
||||
{
|
||||
for (AZ::u32 i = 0; i < numSteps; i++)
|
||||
{
|
||||
scene->StartSimulation(timeStep);
|
||||
scene->FinishSimulation();
|
||||
}
|
||||
}
|
||||
|
||||
float GetPositionElement(AZ::Entity* entity, int element)
|
||||
{
|
||||
AZ::Transform transform = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(transform, entity->GetId(), &AZ::TransformInterface::GetWorldTM);
|
||||
return transform.GetTranslation().GetElement(element);
|
||||
}
|
||||
} // namespace Physics
|
||||
@@ -1,17 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
PhysicsTests.h
|
||||
PhysicsTests.inl
|
||||
PhysicsGenericInterfaceTests.inl
|
||||
PhysicsComponentBusTests.inl
|
||||
)
|
||||
@@ -1,243 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Asset/AssetManagerComponent.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Script/ScriptAsset.h>
|
||||
#include <AzCore/Script/ScriptSystemComponent.h>
|
||||
#include <AzCore/Script/ScriptContext.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzToolsFramework/ToolsComponents/ScriptEditorComponent.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
|
||||
#include "EntityTestbed.h"
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using namespace AZ;
|
||||
using namespace AzFramework;
|
||||
|
||||
class EntityScriptTest
|
||||
: public EntityTestbed
|
||||
{
|
||||
public:
|
||||
|
||||
ScriptContext* m_scriptContext;
|
||||
|
||||
~EntityScriptTest()
|
||||
{
|
||||
}
|
||||
|
||||
void OnDestroy() override
|
||||
{
|
||||
delete m_scriptContext;
|
||||
m_scriptContext = nullptr;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
int argc = 0;
|
||||
char* argv = nullptr;
|
||||
Run(argc, &argv);
|
||||
}
|
||||
|
||||
void OnReflect(AZ::SerializeContext& context, AZ::Entity& systemEntity) override
|
||||
{
|
||||
(void)context;
|
||||
(void)systemEntity;
|
||||
}
|
||||
|
||||
void OnSetup() override
|
||||
{
|
||||
m_scriptContext = aznew AZ::ScriptContext();
|
||||
|
||||
auto* catalogBus = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
|
||||
if (catalogBus)
|
||||
{
|
||||
// Register asset types the asset DB should query our catalog for.
|
||||
catalogBus->AddAssetType(AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
|
||||
|
||||
// Build the catalog (scan).
|
||||
catalogBus->AddExtension(".lua");
|
||||
}
|
||||
}
|
||||
|
||||
void OnEntityAdded(AZ::Entity& entity) override
|
||||
{
|
||||
// Add your components.
|
||||
entity.CreateComponent<AzToolsFramework::Components::ScriptEditorComponent>();
|
||||
entity.Activate();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(EntityScriptTest, DISABLED_Test)
|
||||
{
|
||||
run();
|
||||
}
|
||||
|
||||
class ScriptComponentTest
|
||||
: public ::testing::Test
|
||||
{
|
||||
static int mySubValue;
|
||||
static int myReloadValue;
|
||||
|
||||
public:
|
||||
void run()
|
||||
{
|
||||
{
|
||||
ComponentApplication app;
|
||||
ComponentApplication::Descriptor appDesc;
|
||||
appDesc.m_memoryBlocksByteSize = 100 * 1024 * 1024;
|
||||
//appDesc.m_recordsMode = AllocationRecords::RECORD_FULL;
|
||||
//appDesc.m_stackRecordLevels = 20;
|
||||
Entity* systemEntity = app.Create(appDesc);
|
||||
|
||||
systemEntity->CreateComponent<MemoryComponent>();
|
||||
systemEntity->CreateComponent("{CAE3A025-FAC9-4537-B39E-0A800A2326DF}"); // JobManager component
|
||||
systemEntity->CreateComponent<StreamerComponent>();
|
||||
systemEntity->CreateComponent<AssetManagerComponent>();
|
||||
systemEntity->CreateComponent("{A316662A-6C3E-43E6-BC61-4B375D0D83B4}"); // Usersettings component
|
||||
systemEntity->CreateComponent<ScriptSystemComponent>();
|
||||
|
||||
systemEntity->Init();
|
||||
systemEntity->Activate();
|
||||
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
// in the unit tests.
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
|
||||
ScriptComponent::CreateDescriptor(); // descriptor is deleted by app
|
||||
|
||||
ScriptContext* scriptContext = nullptr;
|
||||
EBUS_EVENT_RESULT(scriptContext, ScriptSystemRequestBus, GetContext, DefaultScriptContextId);
|
||||
|
||||
BehaviorContext* behaviorContext = nullptr;
|
||||
EBUS_EVENT_RESULT(behaviorContext, AZ::ComponentApplicationBus, GetBehaviorContext);
|
||||
|
||||
// make sure script instances don't can read only share data, but don't modify the source table
|
||||
{
|
||||
Data::Asset<ScriptAsset> scriptAsset = Data::AssetManager::Instance().CreateAsset<ScriptAsset>(Uuid::CreateRandom());
|
||||
AZStd::string script = "test = {\
|
||||
--[[test with no properties table as this should work too!]]\
|
||||
state = {\
|
||||
mysubstate = {\
|
||||
mysubvalue = 2,\
|
||||
},\
|
||||
myvalue = 0,\
|
||||
},\
|
||||
}\
|
||||
function test:OnActivate()\
|
||||
self.state.mysubstate.mysubvalue = 5\
|
||||
end\
|
||||
return test;";
|
||||
scriptAsset.Get()->m_scriptBuffer.insert(scriptAsset.Get()->m_scriptBuffer.begin(), script.begin(), script.end());
|
||||
EBUS_EVENT(Data::AssetManagerBus, OnAssetReady, scriptAsset);
|
||||
app.Tick();
|
||||
app.TickSystem();
|
||||
|
||||
Entity* entity1 = aznew Entity();
|
||||
entity1->CreateComponent<ScriptComponent>()->SetScript(scriptAsset);
|
||||
|
||||
entity1->Init();
|
||||
entity1->Activate();
|
||||
|
||||
Entity* entity2 = aznew Entity();
|
||||
entity2->CreateComponent<ScriptComponent>()->SetScript(scriptAsset);
|
||||
|
||||
entity2->Init();
|
||||
entity2->Activate();
|
||||
|
||||
behaviorContext->Property("globalMySubValue", BehaviorValueProperty(&mySubValue));
|
||||
scriptContext->Execute("globalMySubValue = test.state.mysubstate.mysubvalue", "Read my subvalue");
|
||||
AZ_TEST_ASSERT(mySubValue == 2); // we should not have changed test. table but the instance table of each component.
|
||||
|
||||
delete entity1;
|
||||
delete entity2;
|
||||
|
||||
scriptAsset.Reset();
|
||||
}
|
||||
|
||||
// Test script reload
|
||||
{
|
||||
behaviorContext->Property("myReloadValue", BehaviorValueProperty(&myReloadValue));
|
||||
Data::Asset<ScriptAsset> scriptAsset1 = Data::AssetManager::Instance().CreateAsset<ScriptAsset>(Uuid::CreateRandom());
|
||||
AZStd::string script1 ="local testReload = {}\
|
||||
function testReload:OnActivate()\
|
||||
myReloadValue = 1\
|
||||
end\
|
||||
function testReload:OnDeactivate()\
|
||||
myReloadValue = 0\
|
||||
end\
|
||||
return testReload;";
|
||||
|
||||
scriptAsset1.Get()->m_scriptBuffer.insert(scriptAsset1.Get()->m_scriptBuffer.begin(), script1.begin(), script1.end());
|
||||
EBUS_EVENT(Data::AssetManagerBus, OnAssetReady, scriptAsset1);
|
||||
|
||||
app.Tick();
|
||||
app.TickSystem(); // flush assets etc.
|
||||
|
||||
Entity* entity = aznew Entity();
|
||||
entity->CreateComponent<ScriptComponent>()->SetScript(scriptAsset1);
|
||||
|
||||
entity->Init();
|
||||
entity->Activate();
|
||||
|
||||
// test value, it should set during activation of the first script
|
||||
AZ_TEST_ASSERT(myReloadValue == 1);
|
||||
|
||||
AZStd::string script2 ="local testReload = {}\
|
||||
function testReload:OnActivate()\
|
||||
myReloadValue = 5\
|
||||
end\
|
||||
return testReload";
|
||||
|
||||
// modify the asset
|
||||
Data::Asset<ScriptAsset> scriptAsset2(aznew ScriptAsset(scriptAsset1.GetId()), AZ::Data::AssetLoadBehavior::Default);
|
||||
scriptAsset2.Get()->m_scriptBuffer.insert(scriptAsset2.Get()->m_scriptBuffer.begin(), script2.begin(), script2.end());
|
||||
|
||||
// When reloading script assets from files, ScriptSystemComponent would clear old script caches automatically in the
|
||||
// function `ScriptSystemComponent::LoadAssetData()`. But here we are changing script directly in memory, therefore we
|
||||
// need to clear old cache manually.
|
||||
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequestBus::Events::ClearAssetReferences, scriptAsset1.GetId());
|
||||
|
||||
// trigger reload
|
||||
Data::AssetManager::Instance().ReloadAssetFromData(scriptAsset2);
|
||||
|
||||
// ReloadAssetFromData is (now) a queued event
|
||||
// Need to tick subsystems here to receive reload event.
|
||||
app.Tick();
|
||||
app.TickSystem();
|
||||
|
||||
// test value with the reloaded value
|
||||
EXPECT_EQ(5, myReloadValue);
|
||||
|
||||
delete entity;
|
||||
|
||||
scriptAsset1.Reset();
|
||||
scriptAsset2.Reset();
|
||||
}
|
||||
|
||||
app.Destroy();
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ScriptComponentTest, ScriptComponentTestExecution)
|
||||
{
|
||||
run();
|
||||
}
|
||||
|
||||
int ScriptComponentTest::mySubValue = 0;
|
||||
int ScriptComponentTest::myReloadValue = 0;
|
||||
} // namespace UnitTest
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Asset/AssetManagerComponent.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Script/ScriptAsset.h>
|
||||
#include <AzCore/Script/ScriptSystemComponent.h>
|
||||
#include <AzCore/Script/ScriptContext.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzToolsFramework/ToolsComponents/ScriptEditorComponent.h>
|
||||
#include <AzFramework/Script/ScriptNetBindings.h>
|
||||
|
||||
#include "EntityTestbed.h"
|
||||
|
||||
extern "C" {
|
||||
# include <Lua/lualib.h>
|
||||
# include <Lua/lauxlib.h>
|
||||
}
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using namespace AZ;
|
||||
using namespace AzFramework;
|
||||
|
||||
// Global Properties used for Testing
|
||||
int mySubValue = 0;
|
||||
int myReloadValue = 0;
|
||||
|
||||
class ScriptComponentTest
|
||||
: public testing::Test
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(ScriptComponentTest, "{85CDBD49-70FF-416A-8154-B5525EDD30D4}");
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
ComponentApplication::Descriptor appDesc;
|
||||
appDesc.m_memoryBlocksByteSize = 100 * 1024 * 1024;
|
||||
//appDesc.m_recordsMode = AllocationRecords::RECORD_FULL;
|
||||
//appDesc.m_stackRecordLevels = 20;
|
||||
Entity* systemEntity = m_app.Create(appDesc);
|
||||
|
||||
systemEntity->CreateComponent<MemoryComponent>();
|
||||
systemEntity->CreateComponent("{CAE3A025-FAC9-4537-B39E-0A800A2326DF}"); // JobManager component
|
||||
systemEntity->CreateComponent<StreamerComponent>();
|
||||
systemEntity->CreateComponent<AssetManagerComponent>();
|
||||
systemEntity->CreateComponent("{A316662A-6C3E-43E6-BC61-4B375D0D83B4}"); // Usersettings component
|
||||
systemEntity->CreateComponent<ScriptSystemComponent>();
|
||||
|
||||
systemEntity->Init();
|
||||
systemEntity->Activate();
|
||||
|
||||
EBUS_EVENT_RESULT(m_scriptContext, ScriptSystemRequestBus, GetContext, DefaultScriptContextId);
|
||||
EBUS_EVENT_RESULT(m_behaviorContext, AZ::ComponentApplicationBus, GetBehaviorContext);
|
||||
EBUS_EVENT_RESULT(m_serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
|
||||
|
||||
NetBindable::Reflect(m_serializeContext);
|
||||
|
||||
AzToolsFramework::Components::ScriptEditorComponent::CreateDescriptor(); // descriptor is deleted by app
|
||||
AzToolsFramework::Components::ScriptEditorComponent::Reflect(m_serializeContext);
|
||||
|
||||
ScriptComponent::CreateDescriptor(); // descriptor is deleted by app
|
||||
ScriptComponent::Reflect(m_serializeContext);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_app.Destroy();
|
||||
}
|
||||
|
||||
Data::Asset<ScriptAsset> CreateAndLoadScriptAsset(const AZStd::string& script)
|
||||
{
|
||||
Data::Asset<ScriptAsset> scriptAsset = Data::AssetManager::Instance().CreateAsset<ScriptAsset>(Uuid::CreateRandom());
|
||||
scriptAsset.Get()->m_scriptBuffer.insert(scriptAsset.Get()->m_scriptBuffer.begin(), script.begin(), script.end());
|
||||
EBUS_EVENT(Data::AssetManagerBus, OnAssetReady, scriptAsset);
|
||||
|
||||
m_app.Tick();
|
||||
m_app.TickSystem(); // flush assets etc.
|
||||
|
||||
return scriptAsset;
|
||||
}
|
||||
|
||||
static ScriptComponent* BuildGameEntity(const Data::Asset<ScriptAsset>& scriptAsset, Entity& gameEntity)
|
||||
{
|
||||
// We first setup the ScriptEditorComponent.
|
||||
// After a script asset is loaded the ScriptEditorComponent builds the properties table.
|
||||
// BuildGameEntity() hands off the properties table to the game runtime ScriptComponent.
|
||||
Entity editorEntity;
|
||||
auto* scriptEditorComponent = editorEntity.CreateComponent<AzToolsFramework::Components::ScriptEditorComponent>();
|
||||
scriptEditorComponent->SetScript(scriptAsset);
|
||||
editorEntity.Init();
|
||||
editorEntity.Activate();
|
||||
|
||||
scriptEditorComponent->BuildGameEntity(&gameEntity);
|
||||
|
||||
auto* scriptComponent = gameEntity.FindComponent<ScriptComponent>();
|
||||
return scriptComponent;
|
||||
}
|
||||
|
||||
static void OverwriteScriptBuffer(Data::Asset<ScriptAsset> scriptAsset, AZStd::string newScript)
|
||||
{
|
||||
scriptAsset.Get()->m_scriptBuffer.insert(scriptAsset.Get()->m_scriptBuffer.begin(), newScript.begin(), newScript.end());
|
||||
}
|
||||
|
||||
ComponentApplication m_app;
|
||||
ScriptContext* m_scriptContext = nullptr;
|
||||
BehaviorContext* m_behaviorContext = nullptr;
|
||||
SerializeContext* m_serializeContext = nullptr;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(ScriptComponentTest, ScriptInstancesCanReadButDontModifySourceTable)
|
||||
{
|
||||
// make sure script instances don't can read only share data, but don't modify the source table
|
||||
const AZStd::string script = "test = {\
|
||||
--[[test with no properties table as this should work too!]]\
|
||||
state = {\
|
||||
mysubstate = {\
|
||||
mysubvalue = 2,\
|
||||
},\
|
||||
myvalue = 0,\
|
||||
},\
|
||||
}\
|
||||
function test:OnActivate()\
|
||||
self.state.mysubstate.mysubvalue = 5\
|
||||
end\
|
||||
return test;";
|
||||
|
||||
Data::Asset<ScriptAsset> scriptAsset = CreateAndLoadScriptAsset(script);
|
||||
|
||||
auto* entity1 = aznew Entity();
|
||||
entity1->CreateComponent<ScriptComponent>()->SetScript(scriptAsset);
|
||||
|
||||
entity1->Init();
|
||||
entity1->Activate();
|
||||
|
||||
auto* entity2 = aznew Entity();
|
||||
entity2->CreateComponent<ScriptComponent>()->SetScript(scriptAsset);
|
||||
|
||||
entity2->Init();
|
||||
entity2->Activate();
|
||||
|
||||
m_behaviorContext->Property("globalMySubValue", BehaviorValueProperty(&mySubValue));
|
||||
m_scriptContext->Execute("globalMySubValue = test.state.mysubstate.mysubvalue", "Read my subvalue");
|
||||
AZ_TEST_ASSERT(mySubValue == 2); // we should not have changed test. table but the instance table of each component.
|
||||
|
||||
delete entity1;
|
||||
delete entity2;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(ScriptComponentTest, ScriptReloads)
|
||||
{
|
||||
// Test script reload
|
||||
m_behaviorContext->Property("myReloadValue", BehaviorValueProperty(&myReloadValue));
|
||||
const AZStd::string script1 ="local testReload = {}\
|
||||
function testReload:OnActivate()\
|
||||
myReloadValue = 1\
|
||||
end\
|
||||
function testReload:OnDeactivate()\
|
||||
myReloadValue = 0\
|
||||
end\
|
||||
return testReload;";
|
||||
Data::Asset<ScriptAsset> scriptAsset1 = CreateAndLoadScriptAsset(script1);
|
||||
|
||||
auto* entity = aznew Entity();
|
||||
entity->CreateComponent<ScriptComponent>()->SetScript(scriptAsset1);
|
||||
|
||||
entity->Init();
|
||||
entity->Activate();
|
||||
|
||||
// test value, it should set during activation of the first script
|
||||
AZ_TEST_ASSERT(myReloadValue == 1);
|
||||
|
||||
const AZStd::string script2 ="local testReload = {}\
|
||||
function testReload:OnActivate()\
|
||||
myReloadValue = 5\
|
||||
end\
|
||||
return testReload";
|
||||
|
||||
// modify the asset
|
||||
Data::Asset<ScriptAsset> scriptAsset2(aznew ScriptAsset(scriptAsset1.GetId()), AZ::Data::AssetLoadBehavior::Default);
|
||||
OverwriteScriptBuffer(scriptAsset2, script2);
|
||||
|
||||
// When reloading script assets from files, ScriptSystemComponent would clear old script caches automatically in the
|
||||
// function `ScriptSystemComponent::LoadAssetData()`. But here we are changing script directly in memory, therefore we
|
||||
// need to clear old cache manually.
|
||||
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequestBus::Events::ClearAssetReferences, scriptAsset1.GetId());
|
||||
|
||||
// trigger reload
|
||||
Data::AssetManager::Instance().ReloadAssetFromData(scriptAsset2);
|
||||
|
||||
// ReloadAssetFromData is (now) a queued event
|
||||
// Need to tick subsystems here to receive reload event.
|
||||
m_app.Tick();
|
||||
m_app.TickSystem();
|
||||
|
||||
// test value with the reloaded value
|
||||
EXPECT_EQ(5, myReloadValue);
|
||||
|
||||
delete entity;
|
||||
}
|
||||
|
||||
TEST_F(ScriptComponentTest, LuaPropertiesAreDiscovered)
|
||||
{
|
||||
const AZStd::string script = "local test = {\
|
||||
Properties = {\
|
||||
myNum = { default = 2 },\
|
||||
},\
|
||||
}\
|
||||
function test:OnActivate()\
|
||||
self.Properties.myNum = 5\
|
||||
end\
|
||||
return test";
|
||||
|
||||
const Data::Asset<ScriptAsset> scriptAsset = CreateAndLoadScriptAsset(script);
|
||||
Entity editorEntity, gameEntity;
|
||||
auto* scriptComponent = BuildGameEntity(scriptAsset, gameEntity);
|
||||
|
||||
EXPECT_NE(scriptComponent->GetScriptProperty("myNum"), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(ScriptComponentTest, UpdateNetSynchedProperty)
|
||||
{
|
||||
// Make sure altering a netsynched property in script only affects the single entity instance
|
||||
const AZStd::string script = "local test = {\
|
||||
Properties = {\
|
||||
myNetSynchedNum = { default = 41, netSynched ={} },\
|
||||
doUpdate = { default = false },\
|
||||
},\
|
||||
}\
|
||||
function test:OnActivate()\
|
||||
self.tickBusHandler = TickBus.Connect(self, self.entityId)\
|
||||
end\
|
||||
function test:OnDeactivate()\
|
||||
self.tickBusHandler:Disconnect()\
|
||||
end\
|
||||
function test:OnTick(deltaTime, timePoint)\
|
||||
if self.Properties.doUpdate then\
|
||||
self.Properties.myNetSynchedNum = self.Properties.myNetSynchedNum+1\
|
||||
end\
|
||||
end\
|
||||
return test";
|
||||
|
||||
|
||||
const Data::Asset<ScriptAsset> scriptAsset = CreateAndLoadScriptAsset(script);
|
||||
Entity entity1, entity2;
|
||||
ScriptComponent* scriptComponentInstance1 = BuildGameEntity(scriptAsset, entity1);
|
||||
ScriptComponent* scriptComponentInstance2 = BuildGameEntity(scriptAsset, entity2);
|
||||
|
||||
// Change the value of entity1's doUpdate to true.
|
||||
// This way entity1's myNetSynchedNum should be incremented during OnTick
|
||||
auto* doUpdateScriptProperty = azrtti_cast<ScriptPropertyBoolean*>(scriptComponentInstance1->GetScriptProperty("doUpdate"));
|
||||
ASSERT_NE(doUpdateScriptProperty, nullptr);
|
||||
doUpdateScriptProperty->m_value = true;
|
||||
|
||||
entity1.Init();
|
||||
entity2.Init();
|
||||
entity1.Activate();
|
||||
entity2.Activate();
|
||||
|
||||
// Tick in order to call OnTick in our lua script.
|
||||
m_app.Tick();
|
||||
m_app.TickSystem();
|
||||
|
||||
// Ensure Entity1's myNetSynchedNum updated, but not Entity2
|
||||
auto* netSynchedProperty1 = scriptComponentInstance1->GetNetworkedScriptProperty("myNetSynchedNum");
|
||||
auto* netSynchedProperty2 = scriptComponentInstance2->GetNetworkedScriptProperty("myNetSynchedNum");
|
||||
ASSERT_NE(netSynchedProperty1, nullptr);
|
||||
ASSERT_NE(netSynchedProperty2, nullptr);
|
||||
|
||||
auto* num1 = azrtti_cast<const ScriptPropertyNumber*>(netSynchedProperty1);
|
||||
auto* num2 = azrtti_cast<const ScriptPropertyNumber*>(netSynchedProperty2);
|
||||
|
||||
ASSERT_NE(num1, nullptr);
|
||||
ASSERT_NE(num2, nullptr);
|
||||
|
||||
EXPECT_EQ(num1->m_value, 42);
|
||||
EXPECT_EQ(num2->m_value, 41);
|
||||
}
|
||||
} // namespace UnitTest
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Asset/AssetManagerComponent.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Script/ScriptAsset.h>
|
||||
#include <AzCore/Script/ScriptSystemComponent.h>
|
||||
#include <AzCore/Script/ScriptContext.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzToolsFramework/ToolsComponents/ScriptEditorComponent.h>
|
||||
|
||||
#include "EntityTestbed.h"
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
using namespace AZ;
|
||||
using namespace AzFramework;
|
||||
|
||||
class EntityScriptTest
|
||||
: public EntityTestbed
|
||||
{
|
||||
public:
|
||||
|
||||
ScriptContext* m_scriptContext;
|
||||
|
||||
~EntityScriptTest()
|
||||
{
|
||||
}
|
||||
|
||||
void OnDestroy() override
|
||||
{
|
||||
delete m_scriptContext;
|
||||
m_scriptContext = nullptr;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
int argc = 0;
|
||||
char* argv = nullptr;
|
||||
Run(argc, &argv);
|
||||
}
|
||||
|
||||
void OnReflect(AZ::SerializeContext& context, AZ::Entity& systemEntity) override
|
||||
{
|
||||
(void)context;
|
||||
(void)systemEntity;
|
||||
}
|
||||
|
||||
void OnSetup() override
|
||||
{
|
||||
m_scriptContext = aznew AZ::ScriptContext();
|
||||
|
||||
auto* catalogBus = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
|
||||
if (catalogBus)
|
||||
{
|
||||
// Register asset types the asset DB should query our catalog for.
|
||||
catalogBus->AddAssetType(AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
|
||||
|
||||
// Build the catalog (scan).
|
||||
catalogBus->AddExtension(".lua");
|
||||
}
|
||||
}
|
||||
|
||||
void OnEntityAdded(AZ::Entity& entity) override
|
||||
{
|
||||
// Add your components.
|
||||
entity.CreateComponent<AzToolsFramework::Components::ScriptEditorComponent>();
|
||||
entity.Activate();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(EntityScriptTest, DISABLED_Test)
|
||||
{
|
||||
run();
|
||||
}
|
||||
} // namespace UnitTest
|
||||
@@ -17,7 +17,6 @@ set(FILES
|
||||
BinToTextEncode.cpp
|
||||
ComponentAddRemove.cpp
|
||||
ComponentAdapterTests.cpp
|
||||
EngineTests.cpp
|
||||
EntityContext.cpp
|
||||
EntityTestbed.h
|
||||
FileFunc.cpp
|
||||
@@ -32,7 +31,8 @@ set(FILES
|
||||
OctreePerformanceTests.cpp
|
||||
OctreeTests.cpp
|
||||
Slices.cpp
|
||||
Script.cpp
|
||||
Script/ScriptComponentTests.cpp
|
||||
Script/ScriptEntityTests.cpp
|
||||
AssetCatalog.cpp
|
||||
AssetProcessorConnection.cpp
|
||||
NetBindingSystemImplTest.cpp
|
||||
|
||||
Reference in New Issue
Block a user