Merged dev
Signed-off-by: AMZN-Olex <5432499+AMZN-Olex@users.noreply.github.com>
This commit is contained in:
@@ -25,6 +25,9 @@ ly_add_target(
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
AZ::AzNetworking
|
||||
PRIVATE
|
||||
Gem::EMotionFXStaticLib
|
||||
Gem::PhysX.Static
|
||||
AUTOGEN_RULES
|
||||
*.AutoPackets.xml,AutoPackets_Header.jinja,$path/$fileprefix.AutoPackets.h
|
||||
*.AutoPackets.xml,AutoPackets_Inline.jinja,$path/$fileprefix.AutoPackets.inl
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Multiplayer
|
||||
using EntityMigrationStartEvent = AZ::Event<ClientInputId>;
|
||||
using EntityMigrationEndEvent = AZ::Event<>;
|
||||
using EntityServerMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, HostId, AzNetworking::ConnectionId>;
|
||||
using EntityPreRenderEvent = AZ::Event<float, float>;
|
||||
using EntityPreRenderEvent = AZ::Event<float>;
|
||||
using EntityCorrectionEvent = AZ::Event<>;
|
||||
|
||||
//! @class NetBindComponent
|
||||
@@ -118,7 +118,7 @@ namespace Multiplayer
|
||||
void NotifyMigrationStart(ClientInputId migratedInputId);
|
||||
void NotifyMigrationEnd();
|
||||
void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId);
|
||||
void NotifyPreRender(float deltaTime, float blendFactor);
|
||||
void NotifyPreRender(float deltaTime);
|
||||
void NotifyCorrection();
|
||||
|
||||
void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/AutoGen/NetworkCharacterComponent.AutoComponent.h>
|
||||
#include <PhysX/CharacterGameplayBus.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
class Character;
|
||||
}
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! NetworkCharacterComponent
|
||||
//! Provides multiplayer support for game-play player characters.
|
||||
class NetworkCharacterComponent
|
||||
: public NetworkCharacterComponentBase
|
||||
, private PhysX::CharacterGameplayRequestBus::Handler
|
||||
{
|
||||
friend class NetworkCharacterComponentController;
|
||||
|
||||
public:
|
||||
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkCharacterComponent, s_networkCharacterComponentConcreteUuid, Multiplayer::NetworkCharacterComponentBase)
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
NetworkCharacterComponent();
|
||||
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
|
||||
}
|
||||
|
||||
// AZ::Component
|
||||
void OnInit() override {}
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
private:
|
||||
void OnTranslationChangedEvent(const AZ::Vector3& translation);
|
||||
void OnSyncRewind();
|
||||
|
||||
// CharacterGameplayRequestBus
|
||||
bool IsOnGround() const override;
|
||||
float GetGravityMultiplier() const override { return {}; }
|
||||
void SetGravityMultiplier([[maybe_unused]] float gravityMultiplier) override {}
|
||||
AZ::Vector3 GetFallingVelocity() const override { return {}; }
|
||||
void SetFallingVelocity([[maybe_unused]] const AZ::Vector3& fallingVelocity) override {}
|
||||
|
||||
Physics::Character* m_physicsCharacter = nullptr;
|
||||
Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler = Multiplayer::EntitySyncRewindEvent::Handler([this]() { OnSyncRewind(); });
|
||||
AZ::Event<AZ::Vector3>::Handler m_translationEventHandler;
|
||||
};
|
||||
|
||||
//! NetworkCharacterComponentController
|
||||
//! This is the network controller for NetworkCharacterComponent.
|
||||
//! Class provides the ability to move characters in physical space while keeping the network in-sync.
|
||||
class NetworkCharacterComponentController
|
||||
: public NetworkCharacterComponentControllerBase
|
||||
{
|
||||
public:
|
||||
NetworkCharacterComponentController(NetworkCharacterComponent& parent);
|
||||
|
||||
// NetworkCharacterComponentControllerBase
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
//! TryMoveWithVelocity
|
||||
//! Will move this character entity kinematically through physical world while also ensuring the network stays in-sync.
|
||||
//! Velocity will be applied over delta-time to determine the movement amount.
|
||||
//! Returns this entity's world-space position after the move.
|
||||
AZ::Vector3 TryMoveWithVelocity(const AZ::Vector3& velocity, float deltaTime);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Integration/ActorComponentBus.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
class CharacterRequests;
|
||||
class CharacterHitDetectionConfiguration;
|
||||
}
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
class NetworkHitVolumesComponent
|
||||
: public NetworkHitVolumesComponentBase
|
||||
, private EMotionFX::Integration::ActorComponentNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
struct AnimatedHitVolume final
|
||||
{
|
||||
AnimatedHitVolume
|
||||
(
|
||||
AzNetworking::ConnectionId connectionId,
|
||||
Physics::CharacterRequests* character,
|
||||
const char* hitVolumeName,
|
||||
const Physics::ColliderConfiguration* colliderConfig,
|
||||
const Physics::ShapeConfiguration* shapeConfig,
|
||||
const uint32_t jointIndex
|
||||
);
|
||||
|
||||
~AnimatedHitVolume() = default;
|
||||
|
||||
void UpdateTransform(const AZ::Transform& transform);
|
||||
void SyncToCurrentTransform();
|
||||
|
||||
Multiplayer::RewindableObject<AZ::Transform, Multiplayer::RewindHistorySize> m_transform;
|
||||
AZStd::shared_ptr<Physics::Shape> m_physicsShape;
|
||||
|
||||
// Cached so we don't have to do subsequent lookups by name
|
||||
const Physics::ColliderConfiguration* m_colliderConfig = nullptr;
|
||||
const Physics::ShapeConfiguration* m_shapeConfig = nullptr;
|
||||
AZ::Transform m_colliderOffSetTransform;
|
||||
const AZ::u32 m_jointIndex = 0;
|
||||
};
|
||||
|
||||
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHitVolumesComponent, s_networkHitVolumesComponentConcreteUuid, Multiplayer::NetworkHitVolumesComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
NetworkHitVolumesComponent();
|
||||
|
||||
void OnInit() override;
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
private:
|
||||
void OnPreRender(float deltaTime);
|
||||
void OnTransformUpdate(const AZ::Transform& transform);
|
||||
void OnSyncRewind();
|
||||
|
||||
void CreateHitVolumes();
|
||||
void DestroyHitVolumes();
|
||||
|
||||
//! ActorComponentNotificationBus::Handler
|
||||
//! @{
|
||||
void OnActorInstanceCreated(EMotionFX::ActorInstance* actorInstance) override;
|
||||
void OnActorInstanceDestroyed(EMotionFX::ActorInstance* actorInstance) override;
|
||||
//! @}
|
||||
|
||||
Physics::CharacterRequests* m_physicsCharacter = nullptr;
|
||||
EMotionFX::Integration::ActorComponentRequests* m_actorComponent = nullptr;
|
||||
const Physics::CharacterColliderConfiguration* m_hitDetectionConfig = nullptr;
|
||||
|
||||
AZStd::vector<AnimatedHitVolume> m_animatedHitVolumes;
|
||||
|
||||
Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler;
|
||||
Multiplayer::EntityPreRenderEvent::Handler m_preRenderHandler;
|
||||
AZ::TransformChangedEvent::Handler m_transformChangedHandler;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
|
||||
namespace Physics
|
||||
{
|
||||
class RigidBodyRequests;
|
||||
}
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! Bus for requests to the network rigid body component.
|
||||
class NetworkRigidBodyRequests : public AZ::ComponentBus
|
||||
{
|
||||
};
|
||||
using NetworkRigidBodyRequestBus = AZ::EBus<NetworkRigidBodyRequests>;
|
||||
|
||||
class NetworkRigidBodyComponent final
|
||||
: public NetworkRigidBodyComponentBase
|
||||
, private NetworkRigidBodyRequestBus::Handler
|
||||
{
|
||||
friend class NetworkRigidBodyComponentController;
|
||||
|
||||
public:
|
||||
AZ_MULTIPLAYER_COMPONENT(
|
||||
Multiplayer::NetworkRigidBodyComponent, s_networkRigidBodyComponentConcreteUuid, Multiplayer::NetworkRigidBodyComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
|
||||
|
||||
NetworkRigidBodyComponent();
|
||||
|
||||
void OnInit() override;
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
private:
|
||||
void OnTransformUpdate(const AZ::Transform& worldTm);
|
||||
void OnSyncRewind();
|
||||
|
||||
Multiplayer::EntitySyncRewindEvent::Handler m_syncRewindHandler;
|
||||
AZ::TransformChangedEvent::Handler m_transformChangedHandler;
|
||||
Physics::RigidBodyRequests* m_physicsRigidBodyComponent = nullptr;
|
||||
Multiplayer::RewindableObject<AZ::Transform, Multiplayer::RewindHistorySize> m_transform;
|
||||
};
|
||||
|
||||
class NetworkRigidBodyComponentController
|
||||
: public NetworkRigidBodyComponentControllerBase
|
||||
{
|
||||
public:
|
||||
NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent);
|
||||
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
void HandleSendApplyImpulse(AzNetworking::IConnection* invokingConnection, const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) override;
|
||||
};
|
||||
} // namespace Multiplayer
|
||||
@@ -29,26 +29,9 @@ namespace Multiplayer
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
private:
|
||||
void OnPreRender(float deltaTime, float blendFactor);
|
||||
void OnPreRender(float deltaTime);
|
||||
void OnCorrection();
|
||||
|
||||
void OnRotationChangedEvent(const AZ::Quaternion& rotation);
|
||||
void OnTranslationChangedEvent(const AZ::Vector3& translation);
|
||||
void OnScaleChangedEvent(float scale);
|
||||
void OnResetCountChangedEvent();
|
||||
void OnParentIdChangedEvent(NetEntityId newParent);
|
||||
|
||||
void UpdateTargetHostFrameId();
|
||||
|
||||
AZ::Transform m_previousTransform = AZ::Transform::CreateIdentity();
|
||||
AZ::Transform m_targetTransform = AZ::Transform::CreateIdentity();
|
||||
|
||||
AZ::Event<AZ::Quaternion>::Handler m_rotationEventHandler;
|
||||
AZ::Event<AZ::Vector3>::Handler m_translationEventHandler;
|
||||
AZ::Event<float>::Handler m_scaleEventHandler;
|
||||
AZ::Event<uint8_t>::Handler m_resetCountEventHandler;
|
||||
AZ::Event<NetEntityId>::Handler m_parentIdChangedEventHandler;
|
||||
|
||||
EntityPreRenderEvent::Handler m_entityPreRenderEventHandler;
|
||||
EntityCorrectionEvent::Handler m_entityCorrectionEventHandler;
|
||||
|
||||
|
||||
@@ -193,15 +193,13 @@ namespace Multiplayer
|
||||
m_previousHostFrameId = time->GetHostFrameId();
|
||||
m_previousHostTimeMs = time->GetHostTimeMs();
|
||||
m_previousRewindConnectionId = time->GetRewindingConnectionId();
|
||||
time->AlterTime(frameId, timeMs, connectionId);
|
||||
m_previousBlendFactor = time->GetHostBlendFactor();
|
||||
time->AlterBlendFactor(blendFactor);
|
||||
time->AlterTime(frameId, timeMs, blendFactor, connectionId);
|
||||
}
|
||||
inline ~ScopedAlterTime()
|
||||
{
|
||||
INetworkTime* time = GetNetworkTime();
|
||||
time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousRewindConnectionId);
|
||||
time->AlterBlendFactor(m_previousBlendFactor);
|
||||
time->AlterTime(m_previousHostFrameId, m_previousHostTimeMs, m_previousBlendFactor, m_previousRewindConnectionId);
|
||||
}
|
||||
private:
|
||||
HostFrameId m_previousHostFrameId = InvalidHostFrameId;
|
||||
|
||||
@@ -52,12 +52,6 @@ namespace Multiplayer
|
||||
//! @return the ConnectionId of the connection requesting the rewind operation
|
||||
virtual AzNetworking::ConnectionId GetRewindingConnectionId() const = 0;
|
||||
|
||||
//! Get the controlling connection that may be currently altering global game time.
|
||||
//! Note this abstraction is required at a relatively high level to allow for 'don't rewind the shooter' semantics
|
||||
//! @param rewindConnectionId if this parameter matches the current rewindConnectionId, it will return the unaltered hostFrameId
|
||||
//! @return the HostFrameId taking into account the provided rewinding connectionId
|
||||
virtual HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const = 0;
|
||||
|
||||
//! Forcibly sets the current network time to the provided frameId and game time in milliseconds.
|
||||
//! @param frameId the new HostFrameId to use
|
||||
//! @param timeMs the new HostTimeMs to use
|
||||
@@ -66,12 +60,9 @@ namespace Multiplayer
|
||||
//! Alters the current HostFrameId and binds that alteration to the provided ConnectionId.
|
||||
//! @param frameId the new HostFrameId to use
|
||||
//! @param timeMs the new HostTimeMs to use
|
||||
//! @param blendFactor the factor used to blend between values at the current and previous HostFrameId
|
||||
//! @param rewindConnectionId the rewinding ConnectionId
|
||||
virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) = 0;
|
||||
|
||||
//! Alters the current Host blend factor. Used to drive interpolation in rewound states.
|
||||
//! @param blendFactor the blend factor to use
|
||||
virtual void AlterBlendFactor(float blendFactor) = 0;
|
||||
virtual void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId) = 0;
|
||||
|
||||
//! Syncs all entities contained within a volume to the current rewind state.
|
||||
//! @param rewindVolume the volume to rewind entities within (needed for physics entities)
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Multiplayer
|
||||
//! @return value in const base type form
|
||||
const BASE_TYPE& Get() const;
|
||||
|
||||
//! Const base type retriever for one host frame behind Get(). Only intended for use in SyncRewind contexts.
|
||||
//! Const base type retriever for one host frame behind Get() when contextually appropriate, otherwise identical to Get().
|
||||
//! @return value in const base type form
|
||||
const BASE_TYPE& GetPrevious() const;
|
||||
|
||||
@@ -86,9 +86,13 @@ namespace Multiplayer
|
||||
private:
|
||||
|
||||
//! Returns what the appropriate current time is for this rewindable property.
|
||||
//! @return the appropriate current time is for this rewindable property
|
||||
//! @return the appropriate current time for this rewindable property
|
||||
HostFrameId GetCurrentTimeForProperty() const;
|
||||
|
||||
//! Returns what the appropriate previous time is for this rewindable property.
|
||||
//! @return the appropriate previous time for this rewindable property
|
||||
HostFrameId GetPreviousTimeForProperty() const;
|
||||
|
||||
//! Updates the latest value for this object instance, if frameTime represents a current or future time.
|
||||
//! Any attempts to set old values on the object will fail
|
||||
//! @param value the new value to set in the object history
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Multiplayer
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline const BASE_TYPE& RewindableObject<BASE_TYPE, REWIND_SIZE>::GetPrevious() const
|
||||
{
|
||||
return GetValueForTime(GetCurrentTimeForProperty() - HostFrameId(1));
|
||||
return GetValueForTime(GetPreviousTimeForProperty());
|
||||
}
|
||||
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
@@ -118,7 +118,22 @@ namespace Multiplayer
|
||||
inline HostFrameId RewindableObject<BASE_TYPE, REWIND_SIZE>::GetCurrentTimeForProperty() const
|
||||
{
|
||||
INetworkTime* networkTime = Multiplayer::GetNetworkTime();
|
||||
return networkTime->GetHostFrameIdForRewindingConnection(m_owningConnectionId);
|
||||
if (networkTime->IsTimeRewound() && (m_owningConnectionId == networkTime->GetRewindingConnectionId()))
|
||||
{
|
||||
return networkTime->GetUnalteredHostFrameId();
|
||||
}
|
||||
return networkTime->GetHostFrameId();
|
||||
}
|
||||
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
inline HostFrameId RewindableObject<BASE_TYPE, REWIND_SIZE>::GetPreviousTimeForProperty() const
|
||||
{
|
||||
INetworkTime* networkTime = Multiplayer::GetNetworkTime();
|
||||
if (networkTime->IsTimeRewound() && (m_owningConnectionId == networkTime->GetRewindingConnectionId()))
|
||||
{
|
||||
return networkTime->GetUnalteredHostFrameId();
|
||||
}
|
||||
return networkTime->GetHostFrameId() - HostFrameId(1);
|
||||
}
|
||||
|
||||
template <typename BASE_TYPE, AZStd::size_t REWIND_SIZE>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{% set Namespace = dataFiles[0].attrib['Namespace'] %}
|
||||
{% for Component in dataFiles %}
|
||||
{% if Component.attrib['Namespace'] != Namespace %}
|
||||
#error "mismatched component namespaces detected in declared multiplayer components, expected {{ Namespace }} but found {{ Component.attrib['Namespace'] }}"
|
||||
#error "mismatched component namespaces detected in declared multiplayer components, expected {{ Namespace }} but {{ Component.attrib['Name'] }} is using {{ Component.attrib['Namespace'] }} namespace."
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
namespace {{ Namespace }}
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
{% macro DeclareNetworkPropertyGetter(Property) %}
|
||||
{% set PropertyName = UpperFirst(Property.attrib['Name']) %}
|
||||
{% if Property.attrib['Container'] == 'Array' %}
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Array() const;
|
||||
{% else %}
|
||||
{% else %}
|
||||
const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Array() const;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}(int32_t index) const;
|
||||
{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
|
||||
void {{ PropertyName }}AddEvent(AZ::Event<int32_t, {{ Property.attrib['Type'] }}>::Handler& handler);
|
||||
{% endif %}
|
||||
{% elif Property.attrib['Container'] == 'Vector' %}
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Vector() const;
|
||||
{% else %}
|
||||
{% else %}
|
||||
const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& Get{{ PropertyName }}Vector() const;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}(int32_t index) const;
|
||||
const {{ Property.attrib['Type'] }}& {{ PropertyName }}GetBack() const;
|
||||
uint32_t {{ PropertyName }}GetSize() const;
|
||||
@@ -31,6 +31,9 @@ void {{ PropertyName }}SizeChangedAddEvent(AZ::Event<uint32_t>::Handler& handler
|
||||
{% endif %}
|
||||
{% else %}
|
||||
const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}() const;
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
const {{ Property.attrib['Type'] }}& Get{{ PropertyName }}Previous() const;
|
||||
{% endif %}
|
||||
{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
|
||||
void {{ PropertyName }}AddEvent(AZ::Event<{{ Property.attrib['Type'] }}>::Handler& handler);
|
||||
{% endif %}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
{% macro LowerFirst(text) %}{{ text[0] | lower}}{{ text[1:] }}{% endmacro %}
|
||||
{% macro DefineNetworkPropertyGet(ClassName, Property, Prefix = '') %}
|
||||
{% if Property.attrib['Container'] == 'Array' %}
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
const RewindableArray<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const
|
||||
{% else %}
|
||||
{% else %}
|
||||
const AZStd::array<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Array() const
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{
|
||||
return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }};
|
||||
}
|
||||
@@ -25,11 +25,11 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Even
|
||||
|
||||
{% endif %}
|
||||
{% elif Property.attrib['Container'] == 'Vector' %}
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
const RewindableFixedVector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const
|
||||
{% else %}
|
||||
{% else %}
|
||||
const AZStd::fixed_vector<{{ Property.attrib['Type'] }}, {{ Property.attrib['Count'] }}>& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Vector() const
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{
|
||||
return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }};
|
||||
}
|
||||
@@ -68,7 +68,12 @@ const {{ Property.attrib['Type'] }}& {{ ClassName }}::Get{{ UpperFirst(Property.
|
||||
{
|
||||
return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }};
|
||||
}
|
||||
|
||||
{% if Property.attrib['IsRewindable']|booleanTrue %}
|
||||
const {{ Property.attrib['Type'] }}& {{ ClassName }}::Get{{ UpperFirst(Property.attrib['Name']) }}Previous() const
|
||||
{
|
||||
return {{ Prefix }}m_{{ LowerFirst(Property.attrib['Name']) }}.GetPrevious();
|
||||
}
|
||||
{% endif %}
|
||||
{% if Property.attrib['GenerateEventBindings']|booleanTrue %}
|
||||
void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}AddEvent(AZ::Event<{{ Property.attrib['Type'] }}>::Handler& handler)
|
||||
{
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
<Packet Name="EntityUpdates" Desc="A packet that contains multiple entity updates">
|
||||
<Member Type="AZ::TimeMs" Name="hostTimeMs" Init="AZ::TimeMs{ 0 }" />
|
||||
<Member Type="Multiplayer::HostFrameId" Name="hostFrameId" Init="Multiplayer::InvalidHostFrameId" />
|
||||
<Member Type="Multiplayer::NetworkEntityUpdateMessage" Name="entityMessages" Container="Vector" Count="Multiplayer::MaxAggregateEntityMessages" SuppressFromInitializerList="true" />
|
||||
<Member Type="Multiplayer::NetworkEntityUpdateMessage" Name="entityMessages" Container="Vector" Count="Multiplayer::MaxAggregateEntityMessages" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="EntityRpcs" Desc="A packet that contains multiple entity rpcs">
|
||||
<Member Type="Multiplayer::NetworkEntityRpcMessage" Name="entityRpcs" Container="Vector" Count="Multiplayer::MaxAggregateRpcMessages" SuppressFromInitializerList="true" />
|
||||
<Member Type="Multiplayer::NetworkEntityRpcMessage" Name="entityRpcs" Container="Vector" Count="Multiplayer::MaxAggregateRpcMessages" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="ClientMigration" Desc="Tell a client to migrate to a new server">
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<Component
|
||||
Name="NetworkCharacterComponent"
|
||||
Namespace="Multiplayer"
|
||||
OverrideComponent="true"
|
||||
OverrideController="true"
|
||||
OverrideInclude="Multiplayer/Components/NetworkCharacterComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
</Component>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<Component
|
||||
Name="NetworkHitVolumesComponent"
|
||||
Namespace="Multiplayer"
|
||||
OverrideComponent="true"
|
||||
OverrideController="false"
|
||||
OverrideInclude="Multiplayer/Components/NetworkHitVolumesComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<ComponentRelation Constraint="Weak" HasController="false" Name="TransformComponent" Namespace="AzFramework" Include="AzFramework/Components/TransformComponent.h" />
|
||||
</Component>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<Component
|
||||
Name="NetworkRigidBodyComponent"
|
||||
Namespace="Multiplayer"
|
||||
OverrideComponent="true"
|
||||
OverrideController="true"
|
||||
OverrideInclude="Multiplayer/Components/NetworkRigidBodyComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
|
||||
<RemoteProcedure Name="SendApplyImpulse" InvokeFrom="Server" HandleOn="Authority" IsPublic="true" IsReliable="true" GenerateEventBindings="false" Description="Applies an impulse">
|
||||
<Param Type="AZ::Vector3" Name="impulse" />
|
||||
<Param Type="AZ::Vector3" Name="worldPoint" />
|
||||
</RemoteProcedure>
|
||||
|
||||
</Component>
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
<Include File="Multiplayer/MultiplayerTypes.h"/>
|
||||
|
||||
<NetworkProperty Type="AZ::Quaternion" Name="rotation" Init="AZ::Quaternion::CreateIdentity()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
<NetworkProperty Type="AZ::Quaternion" Name="rotation" Init="AZ::Quaternion::CreateIdentity()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
|
||||
<NetworkProperty Type="AZ::Vector3" Name="translation" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
<NetworkProperty Type="float" Name="scale" Init="1.0f" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
<NetworkProperty Type="float" Name="scale" Init="1.0f" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
|
||||
<NetworkProperty Type="uint8_t" Name="resetCount" Init="0" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
<NetworkProperty Type="NetEntityId" Name="parentEntityId" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
<NetworkProperty Type="int32_t" Name="parentAttachmentBoneId" Init="-1" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" IsPredictable="true" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
|
||||
@@ -185,12 +185,9 @@ namespace Multiplayer
|
||||
// Discard move input events, client may be speed hacking
|
||||
if (m_clientBankedTime < sv_MaxBankTimeWindowSec)
|
||||
{
|
||||
// Client blends from previous frame to target so here we subtract blend factor to get to that state
|
||||
const float blendFactor = AZStd::min(AZStd::max(0.f, input.GetHostBlendFactor()), 1.0f);
|
||||
const AZ::TimeMs blendMs = AZ::TimeMs(static_cast<float>(static_cast<AZ::TimeMs>(cl_InputRateMs)) * (1.0f - blendFactor));
|
||||
m_clientBankedTime = AZStd::min(m_clientBankedTime + clientInputRateSec, (double)sv_MaxBankTimeWindowSec); // clamp to boundary
|
||||
{
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs() - blendMs, input.GetHostBlendFactor(), invokingConnection->GetConnectionId());
|
||||
ScopedAlterTime scopedTime(input.GetHostFrameId(), input.GetHostTimeMs(), input.GetHostBlendFactor(), invokingConnection->GetConnectionId());
|
||||
GetNetBindComponent()->ProcessInput(input, static_cast<float>(clientInputRateSec));
|
||||
}
|
||||
|
||||
@@ -436,10 +433,13 @@ namespace Multiplayer
|
||||
|
||||
NetworkInputArray inputArray(GetEntityHandle());
|
||||
NetworkInput& input = inputArray[0];
|
||||
const float blendFactor = AZStd::min(AZStd::max(0.f, multiplayer->GetCurrentBlendFactor()), 1.0f);
|
||||
const AZ::TimeMs blendMs = AZ::TimeMs(static_cast<float>(static_cast<AZ::TimeMs>(cl_InputRateMs)) * (1.0f - blendFactor));
|
||||
|
||||
input.SetClientInputId(m_clientInputId);
|
||||
input.SetHostFrameId(networkTime->GetHostFrameId());
|
||||
input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs());
|
||||
// Account for the client blending from previous frame to current
|
||||
input.SetHostTimeMs(multiplayer->GetCurrentHostTimeMs() - blendMs);
|
||||
input.SetHostBlendFactor(multiplayer->GetCurrentBlendFactor());
|
||||
|
||||
// Allow components to form the input for this frame
|
||||
|
||||
@@ -405,9 +405,9 @@ namespace Multiplayer
|
||||
m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId);
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyPreRender(float deltaTime, float blendFactor)
|
||||
void NetBindComponent::NotifyPreRender(float deltaTime)
|
||||
{
|
||||
m_entityPreRenderEvent.Signal(deltaTime, blendFactor);
|
||||
m_entityPreRenderEvent.Signal(deltaTime);
|
||||
}
|
||||
|
||||
void NetBindComponent::NotifyCorrection()
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Multiplayer/Components/NetworkCharacterComponent.h>
|
||||
#include <Multiplayer/Components/NetworkRigidBodyComponent.h>
|
||||
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
|
||||
#include <AzFramework/Physics/CharacterBus.h>
|
||||
#include <AzFramework/Physics/Character.h>
|
||||
#include <Multiplayer/Components/NetworkTransformComponent.h>
|
||||
#include <Multiplayer/NetworkTime/INetworkTime.h>
|
||||
#include <PhysXCharacters/API/CharacterController.h>
|
||||
#include <PhysX/PhysXLocks.h>
|
||||
#include <PhysX/Utils.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
|
||||
bool CollisionLayerBasedControllerFilter(const physx::PxController& controllerA, const physx::PxController& controllerB)
|
||||
{
|
||||
PHYSX_SCENE_READ_LOCK(controllerA.getActor()->getScene());
|
||||
physx::PxRigidDynamic* actorA = controllerA.getActor();
|
||||
physx::PxRigidDynamic* actorB = controllerB.getActor();
|
||||
|
||||
if (actorA && actorA->getNbShapes() > 0 && actorB && actorB->getNbShapes() > 0)
|
||||
{
|
||||
physx::PxShape* shapeA = nullptr;
|
||||
actorA->getShapes(&shapeA, 1, 0);
|
||||
physx::PxFilterData filterDataA = shapeA->getSimulationFilterData();
|
||||
physx::PxShape* shapeB = nullptr;
|
||||
actorB->getShapes(&shapeB, 1, 0);
|
||||
physx::PxFilterData filterDataB = shapeB->getSimulationFilterData();
|
||||
return PhysX::Utils::Collision::ShouldCollide(filterDataA, filterDataB);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
physx::PxQueryHitType::Enum CollisionLayerBasedObjectPreFilter(
|
||||
const physx::PxFilterData& filterData,
|
||||
const physx::PxShape* shape,
|
||||
const physx::PxRigidActor* actor,
|
||||
[[maybe_unused]] physx::PxHitFlags& queryFlags)
|
||||
{
|
||||
// non-kinematic dynamic bodies should not impede the movement of the character
|
||||
if (actor->getConcreteType() == physx::PxConcreteType::eRIGID_DYNAMIC)
|
||||
{
|
||||
const physx::PxRigidDynamic* rigidDynamic = static_cast<const physx::PxRigidDynamic*>(actor);
|
||||
|
||||
bool isKinematic = (rigidDynamic->getRigidBodyFlags() & physx::PxRigidBodyFlag::eKINEMATIC);
|
||||
if (isKinematic)
|
||||
{
|
||||
const PhysX::ActorData* actorData = PhysX::Utils::GetUserData(rigidDynamic);
|
||||
if (actorData)
|
||||
{
|
||||
const AZ::EntityId entityId = actorData->GetEntityId();
|
||||
|
||||
if (Multiplayer::NetworkRigidBodyRequestBus::FindFirstHandler(entityId) != nullptr)
|
||||
{
|
||||
// Network rigid bodies are kinematic on the client but dynamic on the server,
|
||||
// hence filtering treats these actors as dynamic to support client prediction and avoid desyncs
|
||||
isKinematic = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isKinematic)
|
||||
{
|
||||
return physx::PxQueryHitType::eNONE;
|
||||
}
|
||||
}
|
||||
|
||||
// all other cases should be determined by collision filters
|
||||
if (PhysX::Utils::Collision::ShouldCollide(filterData, shape->getSimulationFilterData()))
|
||||
{
|
||||
return physx::PxQueryHitType::eBLOCK;
|
||||
}
|
||||
|
||||
return physx::PxQueryHitType::eNONE;
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::NetworkCharacterComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<NetworkCharacterComponent, NetworkCharacterComponentBase>()
|
||||
->Version(1);
|
||||
}
|
||||
NetworkCharacterComponentBase::Reflect(context);
|
||||
}
|
||||
|
||||
NetworkCharacterComponent::NetworkCharacterComponent()
|
||||
: m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); })
|
||||
{
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
Physics::CharacterRequests* characterRequests = Physics::CharacterRequestBus::FindFirstHandler(GetEntityId());
|
||||
m_physicsCharacter = (characterRequests != nullptr) ? characterRequests->GetCharacter() : nullptr;
|
||||
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
|
||||
|
||||
if (m_physicsCharacter)
|
||||
{
|
||||
auto controller = static_cast<PhysX::CharacterController*>(m_physicsCharacter);
|
||||
controller->SetFilterFlags(physx::PxQueryFlag::eSTATIC | physx::PxQueryFlag::eDYNAMIC | physx::PxQueryFlag::ePREFILTER);
|
||||
if (auto callbackManager = controller->GetCallbackManager())
|
||||
{
|
||||
callbackManager->SetControllerFilter(CollisionLayerBasedControllerFilter);
|
||||
callbackManager->SetObjectPreFilter(CollisionLayerBasedObjectPreFilter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!HasController())
|
||||
{
|
||||
GetNetworkTransformComponent()->TranslationAddEvent(m_translationEventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::OnTranslationChangedEvent([[maybe_unused]] const AZ::Vector3& translation)
|
||||
{
|
||||
OnSyncRewind();
|
||||
}
|
||||
|
||||
void NetworkCharacterComponent::OnSyncRewind()
|
||||
{
|
||||
if (m_physicsCharacter == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const AZ::Vector3 currPosition = m_physicsCharacter->GetBasePosition();
|
||||
if (!currPosition.IsClose(GetNetworkTransformComponent()->GetTranslation()))
|
||||
{
|
||||
uint32_t frameId = static_cast<uint32_t>(Multiplayer::GetNetworkTime()->GetHostFrameId());
|
||||
m_physicsCharacter->SetFrameId(frameId);
|
||||
//m_physicsCharacter->SetBasePosition(GetNetworkTransformComponent()->GetTranslation());
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkCharacterComponent::IsOnGround() const
|
||||
{
|
||||
auto pxController = static_cast<physx::PxController*>(m_physicsCharacter->GetNativePointer());
|
||||
if (!pxController)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
physx::PxControllerState state;
|
||||
pxController->getState(state);
|
||||
return state.touchedActor != nullptr || (state.collisionFlags & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) != 0;
|
||||
}
|
||||
|
||||
NetworkCharacterComponentController::NetworkCharacterComponentController(NetworkCharacterComponent& parent)
|
||||
: NetworkCharacterComponentControllerBase(parent)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkCharacterComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkCharacterComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
AZ::Vector3 NetworkCharacterComponentController::TryMoveWithVelocity(const AZ::Vector3& velocity, [[maybe_unused]] float deltaTime)
|
||||
{
|
||||
// Ensure any entities that we might interact with are properly synchronized to their rewind state
|
||||
if (IsAuthority())
|
||||
{
|
||||
const AZ::Aabb entityStartBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityLocalBoundsUnion(GetEntity()->GetId());
|
||||
const AZ::Aabb entityFinalBounds = entityStartBounds.GetTranslated(velocity);
|
||||
AZ::Aabb entitySweptBounds = entityStartBounds;
|
||||
entitySweptBounds.AddAabb(entityFinalBounds);
|
||||
Multiplayer::GetNetworkTime()->SyncEntitiesToRewindState(entitySweptBounds);
|
||||
}
|
||||
|
||||
if ((GetParent().m_physicsCharacter == nullptr) || (velocity.GetLengthSq() <= 0.0f))
|
||||
{
|
||||
return GetEntity()->GetTransform()->GetWorldTranslation();
|
||||
}
|
||||
GetParent().m_physicsCharacter->AddVelocity(velocity);
|
||||
GetParent().m_physicsCharacter->ApplyRequestedVelocity(deltaTime);
|
||||
GetEntity()->GetTransform()->SetWorldTranslation(GetParent().m_physicsCharacter->GetBasePosition());
|
||||
AZLOG
|
||||
(
|
||||
NET_Movement,
|
||||
"Moved to position %f x %f x %f",
|
||||
GetParent().m_physicsCharacter->GetBasePosition().GetX(),
|
||||
GetParent().m_physicsCharacter->GetBasePosition().GetY(),
|
||||
GetParent().m_physicsCharacter->GetBasePosition().GetZ()
|
||||
);
|
||||
return GetEntity()->GetTransform()->GetWorldTranslation();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Multiplayer/Components/NetworkHitVolumesComponent.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Physics/Common/PhysicsTypes.h>
|
||||
#include <AzFramework/Physics/CharacterBus.h>
|
||||
#include <AzFramework/Physics/Character.h>
|
||||
#include <AzFramework/Physics/SystemBus.h>
|
||||
#include <MCore/Source/AzCoreConversions.h>
|
||||
#include <Integration/ActorComponentBus.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
AZ_CVAR(bool, bg_DrawArticulatedHitVolumes, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables debug draw of articulated hit volumes");
|
||||
AZ_CVAR(float, bg_DrawDebugHitVolumeLifetime, 0.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The lifetime for hit volume draw-debug shapes");
|
||||
|
||||
AZ_CVAR(float, bg_RewindPositionTolerance, 0.0001f, nullptr, AZ::ConsoleFunctorFlags::Null, "Don't sync the physx entity if the square of delta position is less than this value");
|
||||
AZ_CVAR(float, bg_RewindOrientationTolerance, 0.001f, nullptr, AZ::ConsoleFunctorFlags::Null, "Don't sync the physx entity if the square of delta orientation is less than this value");
|
||||
|
||||
NetworkHitVolumesComponent::AnimatedHitVolume::AnimatedHitVolume
|
||||
(
|
||||
AzNetworking::ConnectionId connectionId,
|
||||
Physics::CharacterRequests* character,
|
||||
const char* hitVolumeName,
|
||||
const Physics::ColliderConfiguration* colliderConfig,
|
||||
const Physics::ShapeConfiguration* shapeConfig,
|
||||
const uint32_t jointIndex
|
||||
)
|
||||
: m_colliderConfig(colliderConfig)
|
||||
, m_shapeConfig(shapeConfig)
|
||||
, m_jointIndex(jointIndex)
|
||||
{
|
||||
m_transform.SetOwningConnectionId(connectionId);
|
||||
|
||||
m_colliderOffSetTransform = AZ::Transform::CreateFromQuaternionAndTranslation(m_colliderConfig->m_rotation, m_colliderConfig->m_position);
|
||||
|
||||
if (m_colliderConfig->m_isExclusive)
|
||||
{
|
||||
Physics::SystemRequestBus::BroadcastResult(m_physicsShape, &Physics::SystemRequests::CreateShape, *m_colliderConfig, *m_shapeConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
Physics::ColliderConfiguration colliderConfiguration = *m_colliderConfig;
|
||||
colliderConfiguration.m_isExclusive = true;
|
||||
colliderConfiguration.m_isSimulated = false;
|
||||
colliderConfiguration.m_isInSceneQueries = true;
|
||||
Physics::SystemRequestBus::BroadcastResult(m_physicsShape, &Physics::SystemRequests::CreateShape, colliderConfiguration, *m_shapeConfig);
|
||||
}
|
||||
|
||||
if (m_physicsShape)
|
||||
{
|
||||
m_physicsShape->SetName(hitVolumeName);
|
||||
character->GetCharacter()->AttachShape(m_physicsShape);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::AnimatedHitVolume::UpdateTransform(const AZ::Transform& transform)
|
||||
{
|
||||
m_transform = transform;
|
||||
m_physicsShape->SetLocalPose(transform.GetTranslation(), transform.GetRotation());
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::AnimatedHitVolume::SyncToCurrentTransform()
|
||||
{
|
||||
AZ::Transform rewoundTransform;
|
||||
const AZ::Transform& targetTransform = m_transform.Get();
|
||||
const float blendFactor = Multiplayer::GetNetworkTime()->GetHostBlendFactor();
|
||||
if (blendFactor < 1.f)
|
||||
{
|
||||
// If a blend factor was supplied, interpolate the transform appropriately
|
||||
const AZ::Transform& previousTransform = m_transform.GetPrevious();
|
||||
rewoundTransform.SetRotation(previousTransform.GetRotation().Slerp(targetTransform.GetRotation(), blendFactor));
|
||||
rewoundTransform.SetTranslation(previousTransform.GetTranslation().Lerp(targetTransform.GetTranslation(), blendFactor));
|
||||
rewoundTransform.SetUniformScale(AZ::Lerp(previousTransform.GetUniformScale(), targetTransform.GetUniformScale(), blendFactor));
|
||||
}
|
||||
else
|
||||
{
|
||||
rewoundTransform = m_transform.Get();
|
||||
}
|
||||
|
||||
const AZ::Transform physicsTransform = AZ::Transform::CreateFromQuaternionAndTranslation(m_physicsShape->GetLocalPose().second, m_physicsShape->GetLocalPose().first);
|
||||
|
||||
// Don't call SetLocalPose unless the transforms are actually different
|
||||
const AZ::Vector3 positionDelta = physicsTransform.GetTranslation() - rewoundTransform.GetTranslation();
|
||||
const AZ::Quaternion orientationDelta = physicsTransform.GetRotation() - rewoundTransform.GetRotation();
|
||||
|
||||
if ((positionDelta.GetLengthSq() >= bg_RewindPositionTolerance) || (orientationDelta.GetLengthSq() >= bg_RewindOrientationTolerance))
|
||||
{
|
||||
m_physicsShape->SetLocalPose(rewoundTransform.GetTranslation(), rewoundTransform.GetRotation());
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::NetworkHitVolumesComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<NetworkHitVolumesComponent, NetworkHitVolumesComponentBase>()
|
||||
->Version(1);
|
||||
}
|
||||
NetworkHitVolumesComponentBase::Reflect(context);
|
||||
}
|
||||
|
||||
NetworkHitVolumesComponent::NetworkHitVolumesComponent()
|
||||
: m_syncRewindHandler([this]() { OnSyncRewind(); })
|
||||
, m_preRenderHandler([this](float deltaTime) { OnPreRender(deltaTime); })
|
||||
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform& worldTm) { OnTransformUpdate(worldTm); })
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::OnInit()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
EMotionFX::Integration::ActorComponentNotificationBus::Handler::BusConnect(GetEntityId());
|
||||
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
|
||||
m_physicsCharacter = Physics::CharacterRequestBus::FindFirstHandler(GetEntityId());
|
||||
GetTransformComponent()->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
OnTransformUpdate(GetTransformComponent()->GetWorldTM());
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
DestroyHitVolumes();
|
||||
EMotionFX::Integration::ActorComponentNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::OnPreRender([[maybe_unused]] float deltaTime)
|
||||
{
|
||||
if (m_animatedHitVolumes.size() <= 0)
|
||||
{
|
||||
CreateHitVolumes();
|
||||
}
|
||||
|
||||
AZ::Vector3 position, scale;
|
||||
AZ::Quaternion rotation;
|
||||
for (AnimatedHitVolume& hitVolume : m_animatedHitVolumes)
|
||||
{
|
||||
m_actorComponent->GetJointTransformComponents(hitVolume.m_jointIndex, EMotionFX::Integration::Space::ModelSpace, position, rotation, scale);
|
||||
hitVolume.UpdateTransform(AZ::Transform::CreateFromQuaternionAndTranslation(rotation, position) * hitVolume.m_colliderOffSetTransform);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::OnTransformUpdate([[maybe_unused]] const AZ::Transform& transform)
|
||||
{
|
||||
OnSyncRewind();
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::OnSyncRewind()
|
||||
{
|
||||
if (m_physicsCharacter && m_physicsCharacter->GetCharacter())
|
||||
{
|
||||
uint32_t frameId = static_cast<uint32_t>(Multiplayer::GetNetworkTime()->GetHostFrameId());
|
||||
m_physicsCharacter->GetCharacter()->SetFrameId(frameId);
|
||||
}
|
||||
|
||||
for (AnimatedHitVolume& hitVolume : m_animatedHitVolumes)
|
||||
{
|
||||
hitVolume.SyncToCurrentTransform();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::CreateHitVolumes()
|
||||
{
|
||||
if (m_physicsCharacter == nullptr || m_actorComponent == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Physics::AnimationConfiguration* physicsConfig = m_actorComponent->GetPhysicsConfig();
|
||||
if (physicsConfig == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_hitDetectionConfig = &physicsConfig->m_hitDetectionConfig;
|
||||
const AzNetworking::ConnectionId owningConnectionId = GetNetBindComponent()->GetOwningConnectionId();
|
||||
|
||||
m_animatedHitVolumes.reserve(m_hitDetectionConfig->m_nodes.size());
|
||||
for (const Physics::CharacterColliderNodeConfiguration& nodeConfig : m_hitDetectionConfig->m_nodes)
|
||||
{
|
||||
const AZStd::size_t jointIndex = m_actorComponent->GetJointIndexByName(nodeConfig.m_name.c_str());
|
||||
if (jointIndex == EMotionFX::Integration::ActorComponentRequests::s_invalidJointIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const AzPhysics::ShapeColliderPair& coliderPair : nodeConfig.m_shapes)
|
||||
{
|
||||
const Physics::ColliderConfiguration* colliderConfig = coliderPair.first.get();
|
||||
Physics::ShapeConfiguration* shapeConfig = coliderPair.second.get();
|
||||
m_animatedHitVolumes.emplace_back(owningConnectionId, m_physicsCharacter, nodeConfig.m_name.c_str(), colliderConfig, shapeConfig, aznumeric_cast<uint32_t>(jointIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::DestroyHitVolumes()
|
||||
{
|
||||
m_animatedHitVolumes.clear();
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::OnActorInstanceCreated([[maybe_unused]] EMotionFX::ActorInstance* actorInstance)
|
||||
{
|
||||
m_actorComponent = EMotionFX::Integration::ActorComponentRequestBus::FindFirstHandler(GetEntity()->GetId());
|
||||
}
|
||||
|
||||
void NetworkHitVolumesComponent::OnActorInstanceDestroyed([[maybe_unused]] EMotionFX::ActorInstance* actorInstance)
|
||||
{
|
||||
m_actorComponent = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Multiplayer/Components/NetworkRigidBodyComponent.h>
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Physics/RigidBodyBus.h>
|
||||
#include <AzFramework/Physics/SimulatedBodies/RigidBody.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
AZ_CVAR_EXTERNED(float, bg_RewindPositionTolerance);
|
||||
AZ_CVAR_EXTERNED(float, bg_RewindOrientationTolerance);
|
||||
|
||||
void NetworkRigidBodyComponent::NetworkRigidBodyComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<NetworkRigidBodyComponent, NetworkRigidBodyComponentBase>()->Version(1);
|
||||
}
|
||||
NetworkRigidBodyComponentBase::Reflect(context);
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkRigidBodyService"));
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC_CE("PhysXRigidBodyService"));
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
{
|
||||
dependent.push_back(AZ_CRC_CE("TransformService"));
|
||||
dependent.push_back(AZ_CRC_CE("PhysXRigidBodyService"));
|
||||
}
|
||||
|
||||
NetworkRigidBodyComponent::NetworkRigidBodyComponent()
|
||||
: m_syncRewindHandler([this](){ OnSyncRewind(); })
|
||||
, m_transformChangedHandler([this]([[maybe_unused]] const AZ::Transform& localTm, const AZ::Transform& worldTm){ OnTransformUpdate(worldTm); })
|
||||
{
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
NetworkRigidBodyRequestBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
|
||||
GetEntity()->FindComponent<AzFramework::TransformComponent>()->BindTransformChangedEventHandler(m_transformChangedHandler);
|
||||
|
||||
m_physicsRigidBodyComponent =
|
||||
Physics::RigidBodyRequestBus::FindFirstHandler(GetEntity()->GetId());
|
||||
AZ_Assert(m_physicsRigidBodyComponent, "PhysX Rigid Body Component is required on entity %s", GetEntity()->GetName().c_str());
|
||||
|
||||
if (!HasController())
|
||||
{
|
||||
m_physicsRigidBodyComponent->SetKinematic(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
NetworkRigidBodyRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::OnTransformUpdate(const AZ::Transform& worldTm)
|
||||
{
|
||||
m_transform = worldTm;
|
||||
|
||||
if (!HasController())
|
||||
{
|
||||
m_physicsRigidBodyComponent->SetKinematicTarget(worldTm);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponent::OnSyncRewind()
|
||||
{
|
||||
uint32_t frameId = static_cast<uint32_t>(Multiplayer::GetNetworkTime()->GetHostFrameId());
|
||||
|
||||
AzPhysics::RigidBody* rigidBody = m_physicsRigidBodyComponent->GetRigidBody();
|
||||
rigidBody->SetFrameId(frameId);
|
||||
|
||||
AZ::Transform rewoundTransform;
|
||||
const AZ::Transform& targetTransform = m_transform.Get();
|
||||
const float blendFactor = Multiplayer::GetNetworkTime()->GetHostBlendFactor();
|
||||
if (blendFactor < 1.f)
|
||||
{
|
||||
// If a blend factor was supplied, interpolate the transform appropriately
|
||||
const AZ::Transform& previousTransform = m_transform.GetPrevious();
|
||||
rewoundTransform.SetRotation(previousTransform.GetRotation().Slerp(targetTransform.GetRotation(), blendFactor));
|
||||
rewoundTransform.SetTranslation(previousTransform.GetTranslation().Lerp(targetTransform.GetTranslation(), blendFactor));
|
||||
rewoundTransform.SetUniformScale(AZ::Lerp(previousTransform.GetUniformScale(), targetTransform.GetUniformScale(), blendFactor));
|
||||
}
|
||||
else
|
||||
{
|
||||
rewoundTransform = m_transform.Get();
|
||||
}
|
||||
const AZ::Transform& physicsTransform = rigidBody->GetTransform();
|
||||
|
||||
// Don't call SetLocalPose unless the transforms are actually different
|
||||
const AZ::Vector3 positionDelta = physicsTransform.GetTranslation() - rewoundTransform.GetTranslation();
|
||||
const AZ::Quaternion orientationDelta = physicsTransform.GetRotation() - rewoundTransform.GetRotation();
|
||||
|
||||
if ((positionDelta.GetLengthSq() >= bg_RewindPositionTolerance) ||
|
||||
(orientationDelta.GetLengthSq() >= bg_RewindOrientationTolerance))
|
||||
{
|
||||
rigidBody->SetTransform(rewoundTransform);
|
||||
}
|
||||
}
|
||||
|
||||
NetworkRigidBodyComponentController::NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent)
|
||||
: NetworkRigidBodyComponentControllerBase(parent)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkRigidBodyComponentController::HandleSendApplyImpulse
|
||||
(
|
||||
[[maybe_unused]] AzNetworking::IConnection* invokingConnection,
|
||||
const AZ::Vector3& impulse,
|
||||
const AZ::Vector3& worldPoint
|
||||
)
|
||||
{
|
||||
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
|
||||
rigidBody->ApplyLinearImpulseAtWorldPoint(impulse, worldPoint);
|
||||
}
|
||||
} // namespace Multiplayer
|
||||
@@ -26,12 +26,7 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
NetworkTransformComponent::NetworkTransformComponent()
|
||||
: m_rotationEventHandler([this](const AZ::Quaternion& rotation) { OnRotationChangedEvent(rotation); })
|
||||
, m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); })
|
||||
, m_scaleEventHandler([this](float scale) { OnScaleChangedEvent(scale); })
|
||||
, m_resetCountEventHandler([this](const uint8_t&) { OnResetCountChangedEvent(); })
|
||||
, m_parentIdChangedEventHandler([this](NetEntityId newParent) { OnParentIdChangedEvent(newParent); })
|
||||
, m_entityPreRenderEventHandler([this](float deltaTime, float blendFactor) { OnPreRender(deltaTime, blendFactor); })
|
||||
: m_entityPreRenderEventHandler([this](float deltaTime) { OnPreRender(deltaTime); })
|
||||
, m_entityCorrectionEventHandler([this]() { OnCorrection(); })
|
||||
{
|
||||
;
|
||||
@@ -44,19 +39,8 @@ namespace Multiplayer
|
||||
|
||||
void NetworkTransformComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
RotationAddEvent(m_rotationEventHandler);
|
||||
TranslationAddEvent(m_translationEventHandler);
|
||||
ScaleAddEvent(m_scaleEventHandler);
|
||||
ResetCountAddEvent(m_resetCountEventHandler);
|
||||
ParentEntityIdAddEvent(m_parentIdChangedEventHandler);
|
||||
if (GetNetBindComponent())
|
||||
{
|
||||
GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler);
|
||||
GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler);
|
||||
}
|
||||
|
||||
// When coming into relevance, reset all blending factors so we don't interpolate to our start position
|
||||
OnResetCountChangedEvent();
|
||||
GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler);
|
||||
GetNetBindComponent()->AddEntityCorrectionEventHandler(m_entityCorrectionEventHandler);
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
@@ -64,90 +48,31 @@ namespace Multiplayer
|
||||
;
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnRotationChangedEvent(const AZ::Quaternion& rotation)
|
||||
{
|
||||
m_previousTransform.SetRotation(m_targetTransform.GetRotation());
|
||||
m_targetTransform.SetRotation(rotation);
|
||||
UpdateTargetHostFrameId();
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnTranslationChangedEvent(const AZ::Vector3& translation)
|
||||
{
|
||||
m_previousTransform.SetTranslation(m_targetTransform.GetTranslation());
|
||||
m_targetTransform.SetTranslation(translation);
|
||||
UpdateTargetHostFrameId();
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnScaleChangedEvent(float scale)
|
||||
{
|
||||
m_previousTransform.SetUniformScale(m_targetTransform.GetUniformScale());
|
||||
m_targetTransform.SetUniformScale(scale);
|
||||
UpdateTargetHostFrameId();
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnResetCountChangedEvent()
|
||||
{
|
||||
OnParentIdChangedEvent(GetParentEntityId());
|
||||
|
||||
m_targetTransform.SetRotation(GetRotation());
|
||||
m_targetTransform.SetTranslation(GetTranslation());
|
||||
m_targetTransform.SetUniformScale(GetScale());
|
||||
m_previousTransform = m_targetTransform;
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnParentIdChangedEvent([[maybe_unused]] NetEntityId newParent)
|
||||
{
|
||||
if (newParent == InvalidNetEntityId)
|
||||
{
|
||||
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
|
||||
{
|
||||
if (transformComponent->GetParentId() != AZ::EntityId())
|
||||
{
|
||||
transformComponent->SetParent(AZ::EntityId());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const ConstNetworkEntityHandle rootHandle = GetNetworkEntityManager()->GetEntity(newParent);
|
||||
if (rootHandle.Exists())
|
||||
{
|
||||
const AZ::EntityId parentEntityId = rootHandle.GetEntity()->GetId();
|
||||
if (AzFramework::TransformComponent* transformComponent = GetEntity()->FindComponent<AzFramework::TransformComponent>())
|
||||
{
|
||||
if (transformComponent->GetParentId() != parentEntityId)
|
||||
{
|
||||
transformComponent->SetParent(parentEntityId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::UpdateTargetHostFrameId()
|
||||
{
|
||||
const HostFrameId currentHostFrameId = Multiplayer::GetNetworkTime()->GetHostFrameId();
|
||||
if (currentHostFrameId > m_targetHostFrameId)
|
||||
{
|
||||
m_targetHostFrameId = currentHostFrameId;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime, float blendFactor)
|
||||
void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime)
|
||||
{
|
||||
if (!HasController())
|
||||
{
|
||||
AZ::Transform blendTransform;
|
||||
if (Multiplayer::GetNetworkTime() && Multiplayer::GetNetworkTime()->GetHostFrameId() > m_targetHostFrameId)
|
||||
blendTransform.SetRotation(GetRotation());
|
||||
blendTransform.SetTranslation(GetTranslation());
|
||||
blendTransform.SetUniformScale(GetScale());
|
||||
|
||||
const float blendFactor = GetMultiplayer()->GetCurrentBlendFactor();
|
||||
if (!AZ::IsClose(blendFactor, 1.0f))
|
||||
{
|
||||
m_previousTransform = m_targetTransform;
|
||||
blendTransform = m_targetTransform;
|
||||
}
|
||||
else
|
||||
{
|
||||
blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor));
|
||||
blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor));
|
||||
blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor));
|
||||
AZ::Transform blendTransformPrevious;
|
||||
blendTransformPrevious.SetRotation(GetRotationPrevious());
|
||||
blendTransformPrevious.SetTranslation(GetTranslationPrevious());
|
||||
blendTransformPrevious.SetUniformScale(GetScalePrevious());
|
||||
|
||||
if (!blendTransform.IsClose(blendTransformPrevious))
|
||||
{
|
||||
blendTransform.SetRotation(blendTransformPrevious.GetRotation().Slerp(blendTransform.GetRotation(), blendFactor));
|
||||
blendTransform.SetTranslation(
|
||||
blendTransformPrevious.GetTranslation().Lerp(blendTransform.GetTranslation(), blendFactor));
|
||||
blendTransform.SetUniformScale(
|
||||
AZ::Lerp(blendTransformPrevious.GetUniformScale(), blendTransform.GetUniformScale(), blendFactor));
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetTransformComponent()->GetWorldTM().IsClose(blendTransform))
|
||||
@@ -160,12 +85,15 @@ namespace Multiplayer
|
||||
void NetworkTransformComponent::OnCorrection()
|
||||
{
|
||||
// Snap to latest
|
||||
OnResetCountChangedEvent();
|
||||
AZ::Transform targetTransform;
|
||||
targetTransform.SetRotation(GetRotation());
|
||||
targetTransform.SetTranslation(GetTranslation());
|
||||
targetTransform.SetUniformScale(GetScale());
|
||||
|
||||
// Hard set the entities transform
|
||||
if (!GetTransformComponent()->GetWorldTM().IsClose(m_targetTransform))
|
||||
if (!GetTransformComponent()->GetWorldTM().IsClose(targetTransform))
|
||||
{
|
||||
GetTransformComponent()->SetWorldTM(m_targetTransform);
|
||||
GetTransformComponent()->SetWorldTM(targetTransform);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -922,7 +922,7 @@ namespace Multiplayer
|
||||
|
||||
for (NetBindComponent* netBindComponent : gatheredEntities)
|
||||
{
|
||||
netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor);
|
||||
netBindComponent->NotifyPreRender(deltaTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -934,7 +934,7 @@ namespace Multiplayer
|
||||
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
|
||||
if (netBindComponent != nullptr)
|
||||
{
|
||||
netBindComponent->NotifyPreRender(deltaTime, m_renderBlendFactor);
|
||||
netBindComponent->NotifyPreRender(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +65,6 @@ namespace Multiplayer
|
||||
return m_rewindingConnectionId;
|
||||
}
|
||||
|
||||
HostFrameId NetworkTime::GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const
|
||||
{
|
||||
return (IsTimeRewound() && (rewindConnectionId == m_rewindingConnectionId)) ? m_unalteredFrameId : m_hostFrameId;
|
||||
}
|
||||
|
||||
void NetworkTime::ForceSetTime(HostFrameId frameId, AZ::TimeMs timeMs)
|
||||
{
|
||||
AZ_Assert(!IsTimeRewound(), "Forcibly setting network time is unsupported under a rewound time scope");
|
||||
@@ -79,16 +74,12 @@ namespace Multiplayer
|
||||
m_rewindingConnectionId = AzNetworking::InvalidConnectionId;
|
||||
}
|
||||
|
||||
void NetworkTime::AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId)
|
||||
void NetworkTime::AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId)
|
||||
{
|
||||
m_hostFrameId = frameId;
|
||||
m_hostTimeMs = timeMs;
|
||||
m_rewindingConnectionId = rewindConnectionId;
|
||||
}
|
||||
|
||||
void NetworkTime::AlterBlendFactor(float blendFactor)
|
||||
{
|
||||
m_hostBlendFactor = blendFactor;
|
||||
m_rewindingConnectionId = rewindConnectionId;
|
||||
}
|
||||
|
||||
void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume)
|
||||
@@ -121,8 +112,15 @@ namespace Multiplayer
|
||||
|
||||
if (networkTransform != nullptr)
|
||||
{
|
||||
// We're not presently factoring in interpolated position here
|
||||
const AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); // Get the rewound position
|
||||
// Get the rewound position for target host frame ID plus the one preceding it for potential lerp
|
||||
AZ::Vector3 rewindCenter = networkTransform->GetTranslation();
|
||||
const AZ::Vector3 rewindCenterPrevious = networkTransform->GetTranslationPrevious();
|
||||
const float blendFactor = GetNetworkTime()->GetHostBlendFactor();
|
||||
if (!AZ::IsClose(blendFactor, 1.0f) && !rewindCenter.IsClose(rewindCenterPrevious))
|
||||
{
|
||||
// If we have a blend factor, lerp the translation for accuracy
|
||||
rewindCenter = rewindCenterPrevious.Lerp(rewindCenter, blendFactor);
|
||||
}
|
||||
const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
|
||||
const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
|
||||
|
||||
|
||||
@@ -32,10 +32,8 @@ namespace Multiplayer
|
||||
AZ::TimeMs GetHostTimeMs() const override;
|
||||
float GetHostBlendFactor() const override;
|
||||
AzNetworking::ConnectionId GetRewindingConnectionId() const override;
|
||||
HostFrameId GetHostFrameIdForRewindingConnection(AzNetworking::ConnectionId rewindConnectionId) const override;
|
||||
void ForceSetTime(HostFrameId frameId, AZ::TimeMs timeMs) override;
|
||||
void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, AzNetworking::ConnectionId rewindConnectionId) override;
|
||||
void AlterBlendFactor(float blendFactor) override;
|
||||
void AlterTime(HostFrameId frameId, AZ::TimeMs timeMs, float blendFactor, AzNetworking::ConnectionId rewindConnectionId) override;
|
||||
void SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) override;
|
||||
void ClearRewoundEntities() override;
|
||||
//! @}
|
||||
|
||||
@@ -57,6 +57,35 @@ namespace UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RewindableObjectTests, CurrentPreviousTests)
|
||||
{
|
||||
Multiplayer::RewindableObject<uint32_t, RewindableBufferFrames> test(0);
|
||||
|
||||
for (uint32_t i = 0; i < RewindableBufferFrames; ++i)
|
||||
{
|
||||
test = i;
|
||||
EXPECT_EQ(i, test);
|
||||
Multiplayer::GetNetworkTime()->IncrementHostFrameId();
|
||||
}
|
||||
|
||||
{
|
||||
// Test that Get/GetPrevious return different value when not on the owning connection
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::InvalidConnectionId);
|
||||
EXPECT_EQ(RewindableBufferFrames - 1, test.Get());
|
||||
EXPECT_EQ(RewindableBufferFrames - 2, test.GetPrevious());
|
||||
}
|
||||
|
||||
// Test that Get/GetPrevious return the unaltered frame on the owning conection
|
||||
Multiplayer::GetNetworkTime()->AlterTime(static_cast<Multiplayer::HostFrameId>(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::ConnectionId(0));
|
||||
{
|
||||
Multiplayer::ScopedAlterTime time(static_cast<Multiplayer::HostFrameId>(RewindableBufferFrames - 1), AZ::TimeMs{ 0 }, 1.f, AzNetworking::ConnectionId(0));
|
||||
test.SetOwningConnectionId(AzNetworking::ConnectionId(0));
|
||||
EXPECT_EQ(RewindableBufferFrames - 1, test.Get());
|
||||
EXPECT_EQ(RewindableBufferFrames - 1, test.GetPrevious());
|
||||
}
|
||||
Multiplayer::GetNetworkTime()->AlterTime(static_cast<Multiplayer::HostFrameId>(RewindableBufferFrames), AZ::TimeMs(0), 1.f, AzNetworking::InvalidConnectionId);
|
||||
}
|
||||
|
||||
TEST_F(RewindableObjectTests, OverflowTests)
|
||||
{
|
||||
Multiplayer::RewindableObject<uint32_t, RewindableBufferFrames> test(0);
|
||||
|
||||
@@ -21,6 +21,9 @@ set(FILES
|
||||
Include/Multiplayer/Components/NetworkHierarchyChildComponent.h
|
||||
Include/Multiplayer/Components/NetworkHierarchyRootComponent.h
|
||||
Include/Multiplayer/Components/NetworkHierarchyBus.h
|
||||
Include/Multiplayer/Components/NetworkCharacterComponent.h
|
||||
Include/Multiplayer/Components/NetworkHitVolumesComponent.h
|
||||
Include/Multiplayer/Components/NetworkRigidBodyComponent.h
|
||||
Include/Multiplayer/Components/NetworkTransformComponent.h
|
||||
Include/Multiplayer/ConnectionData/IConnectionData.h
|
||||
Include/Multiplayer/EntityDomains/IEntityDomain.h
|
||||
@@ -56,6 +59,9 @@ set(FILES
|
||||
Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml
|
||||
Source/AutoGen/Multiplayer.AutoPackets.xml
|
||||
Source/AutoGen/MultiplayerEditor.AutoPackets.xml
|
||||
Source/AutoGen/NetworkCharacterComponent.AutoComponent.xml
|
||||
Source/AutoGen/NetworkHitVolumesComponent.AutoComponent.xml
|
||||
Source/AutoGen/NetworkRigidBodyComponent.AutoComponent.xml
|
||||
Source/AutoGen/NetworkTransformComponent.AutoComponent.xml
|
||||
Source/AutoGen/NetworkHierarchyChildComponent.AutoComponent.xml
|
||||
Source/AutoGen/NetworkHierarchyRootComponent.AutoComponent.xml
|
||||
@@ -66,6 +72,9 @@ set(FILES
|
||||
Source/Components/NetBindComponent.cpp
|
||||
Source/Components/NetworkHierarchyChildComponent.cpp
|
||||
Source/Components/NetworkHierarchyRootComponent.cpp
|
||||
Source/Components/NetworkCharacterComponent.cpp
|
||||
Source/Components/NetworkHitVolumesComponent.cpp
|
||||
Source/Components/NetworkRigidBodyComponent.cpp
|
||||
Source/Components/NetworkTransformComponent.cpp
|
||||
Source/ConnectionData/ClientToServerConnectionData.cpp
|
||||
Source/ConnectionData/ClientToServerConnectionData.h
|
||||
|
||||
Reference in New Issue
Block a user