Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,43 @@
#
# 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.
#
ly_add_target(
NAME ScriptedEntityTweener.Static STATIC
NAMESPACE Gem
FILES_CMAKE
scriptedentitytweener_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
)
ly_add_target(
NAME ScriptedEntityTweener ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
NAMESPACE Gem
OUTPUT_NAME Gem.ScriptedEntityTweener.0d1f5f05559c4a99aaefd30633a0158e.v0.1.0
FILES_CMAKE
scriptedentitytweener_shared_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
Gem::ScriptedEntityTweener.Static
AZ::AzCore
Legacy::CryCommon
)
@@ -0,0 +1,113 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
#include <ScriptedEntityTweener/ScriptedEntityTweenerEnums.h>
namespace AZ
{
class EntityId;
struct Uuid;
}
namespace AZStd
{
class any;
}
namespace ScriptedEntityTweener
{
class ScriptedEntityTweenerRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual ~ScriptedEntityTweenerRequests() = default;
//! Animate a property or properties on component(s) on an entity
virtual void AnimateEntity(const AZ::EntityId& entityId, const AnimationParameters& params) = 0;
//! Sets optional animation parameters to be used on next AnimateEntityScript call, needed as lua implementation doesn't support > 13 args in non-debug builds
virtual void SetOptionalParams(float timeIntoAnimation,
float duration,
int easingMethod,
int easingType,
float delayTime,
int timesToPlay,
bool isFrom,
bool isPlayingBackward,
const AZ::Uuid& animationId,
int timelineId,
int onCompleteCallbackId,
int onUpdateCallbackId,
int onLoopCallbackId) = 0;
//! Script exposed version of the AnimateEntity call
virtual void AnimateEntityScript(const AZ::EntityId& entityId,
const AZStd::string& componentName,
const AZStd::string& virtualPropertyName,
const AZStd::any& paramTarget) = 0;
//! Stop all animations on an entityId, if timelineId is specified (non-zero), only stop animations associated with the timelineId
virtual void Stop(int timelineId, const AZ::EntityId& entityId) = 0;
//! Pause a specific animation, if timelineId is specified (non-zero), only pause for animations associated with the timelineId
virtual void Pause(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName) = 0;
//! Resume a specific animation, if timelineId is specified (non-zero), only resume for animations associated with the timelineId
virtual void Resume(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName) = 0;
//! Change direction an animation is playing, if timelineId (non-zero), only change for animations associated with the timelineId
virtual void SetPlayDirectionReversed(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, bool isPlayingBackward) = 0;
//! Set playback speed for a specific animation, by percentage (1.0f is default, 2.0f is 2x as fast, 0.5 is half as fast).
virtual void SetSpeed(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, float speed) = 0;
//! Set initial values for a specific animation with an animationId.
virtual void SetInitialValue(const AZ::Uuid& animationId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, const AZStd::any& initialValue) = 0;
//! Get current value
virtual AZStd::any GetVirtualPropertyValue(const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName) = 0;
virtual void Reset() = 0;
};
using ScriptedEntityTweenerBus = AZ::EBus<ScriptedEntityTweenerRequests>;
class ScriptedEntityTweenerNotifications
: public AZ::EBusTraits
{
public: // member functions
virtual ~ScriptedEntityTweenerNotifications() = default;
//! Called if lua provided a callback ID via AnimateEntityLua, and animation completed.
virtual void OnComplete(int callbackId) = 0;
//! Called if lua provided a callback ID via AnimateEntityLua, and animation updated.
virtual void OnUpdate(int callbackId, const AZStd::any& currentVal, float progressPercent) = 0;
//! Called if lua provided a callback ID via AnimateEntityLua, and animation looped.
virtual void OnLoop(int callbackId) = 0;
//! Typically called on animation finish to remove any update or loop callbacks
virtual void RemoveCallback(int callbackId) = 0;
//! Called when an animation associated with a timeline starts
virtual void OnTimelineAnimationStart(int timelineId, const AZ::Uuid& uuid, const AZStd::string& componentName, const AZStd::string& propertyName) = 0;
};
using ScriptedEntityTweenerNotificationsBus = AZ::EBus<ScriptedEntityTweenerNotifications>;
} // namespace ScriptedEntityTweener
@@ -0,0 +1,202 @@
/*
* 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 <AzCore/std/any.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_map.h>
namespace ScriptedEntityTweener
{
struct AnimationParameterAddressData
{
AZStd::string m_componentName;
AZStd::string m_virtualPropertyName;
AnimationParameterAddressData() {}
AnimationParameterAddressData(const AZStd::string& componentName, const AZStd::string virtualPropertyName)
: m_componentName(componentName), m_virtualPropertyName(virtualPropertyName)
{
}
bool operator==(const AnimationParameterAddressData& other) const
{
return m_componentName == other.m_componentName &&
m_virtualPropertyName == other.m_virtualPropertyName;
}
};
}
//! Implementation required to store custom types in unordered_map
namespace AZStd
{
template<>
struct hash<ScriptedEntityTweener::AnimationParameterAddressData>
{
using argument_type = ScriptedEntityTweener::AnimationParameterAddressData;
using result_type = AZStd::size_t;
AZ_FORCE_INLINE result_type operator()(const argument_type& ref) const
{
return (AZStd::hash<AZStd::string>()(ref.m_componentName)
^ (AZStd::hash<AZStd::string>()(ref.m_virtualPropertyName) << 1) >> 1);
}
};
}
namespace ScriptedEntityTweener
{
enum class EasingMethod
{
None = 0,
Linear = None,
Quad, //1
Cubic, //2
Quart, //3
Quint, //4
Sine, //5
Expo, //6
Circ, //7
Elastic, //8
Back, //9
Bounce //10
};
enum class EasingType
{
In = 0,
Out, //1
InOut //2
};
//Generally, animations created to animate n number of parameters a certain way, AnimationProperties defines what exactly is applied
struct AnimationProperties
{
static const float UninitializedParamFloat;
static const unsigned int InvalidCallbackId;
static const unsigned int InvalidTimelineId;
EasingMethod m_easeMethod;
EasingType m_easeType;
float m_timeIntoAnimation;
float m_timeToDelayAnim;
float m_timeDuration;
float m_amplitudeOverride;
bool m_isFrom;
bool m_isPlayingBackward;
int m_timesToPlay;
float m_playbackSpeedMultiplier;
AZ::Uuid m_animationId;
int m_timelineId;
int m_onCompleteCallbackId;
int m_onUpdateCallbackId;
int m_onLoopCallbackId;
AnimationProperties()
{
Reset();
}
void Reset()
{
m_easeMethod = EasingMethod::None;
m_easeType = EasingType::In;
m_timeToDelayAnim = 0.0f;
m_timeDuration = 0.0f;
m_amplitudeOverride = 0.0f;
m_isFrom = false;
m_isPlayingBackward = false;
m_timesToPlay = 1;
m_playbackSpeedMultiplier = 1.0f;
m_animationId = AZ::Uuid::CreateNull();
m_timelineId = InvalidTimelineId;
m_onCompleteCallbackId = InvalidCallbackId;
m_onUpdateCallbackId = InvalidCallbackId;
m_onLoopCallbackId = InvalidCallbackId;
}
};
struct AnimationParameters
{
AZ_TYPE_INFO(AnimationParameters, "{7E375768-746E-48DC-BEF4-6F40FEB534F9}");
AZ_CLASS_ALLOCATOR(AnimationParameters, AZ::SystemAllocator, 0);
AnimationParameters()
{
Reset();
}
void Reset()
{
m_animationProperties.Reset();
m_animationParameters.clear();
}
AnimationProperties m_animationProperties;
AZStd::unordered_map<AnimationParameterAddressData, AZStd::any> m_animationParameters;
};
enum class CallbackTypes
{
OnComplete,
OnUpdate,
OnLoop,
RemoveCallback
};
struct CallbackData
{
CallbackData(CallbackTypes callbackType, int callbackId)
: m_callbackType(callbackType)
, m_callbackId(callbackId)
, m_progressPercent(0)
{
}
CallbackTypes m_callbackType = CallbackTypes::OnComplete;
int m_callbackId = AnimationProperties::InvalidCallbackId;
AZStd::any m_callbackData;
float m_progressPercent;
//! Comparison operators implemented to store this data type in a set
bool operator<(const CallbackData& other) const
{
return m_callbackId < other.m_callbackId;
}
bool operator>(const CallbackData& other) const
{
return other.m_callbackId < m_callbackId;
}
bool operator==(const CallbackData& other) const
{
return m_callbackId == other.m_callbackId;
}
};
}
@@ -0,0 +1,492 @@
/*
* 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/Math/MathUtils.h>
namespace ScriptedEntityTweener
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//! A collection of common easing/tweening equations.
class EasingEquations
{
public:
template <typename T>
static T GetEasingResult(EasingMethod easeMethod, EasingType easeType, float timeActive, float duration, T valueInitial, T valueTarget)
{
switch (easeMethod)
{
case EasingMethod::Linear:
return valueInitial + (valueTarget - valueInitial) * (timeActive / duration);
case EasingMethod::Quad:
switch (easeType)
{
case EasingType::In:
return GetEasingResultCombinedIn(static_cast<float>(easeMethod), timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutQuad(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutQuad(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Cubic:
switch (easeType)
{
case EasingType::In:
return GetEasingResultCombinedIn(static_cast<float>(easeMethod), timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutCubic(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutCubic(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Quart:
switch (easeType)
{
case EasingType::In:
return GetEasingResultCombinedIn(static_cast<float>(easeMethod), timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutQuart(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutQuart(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Quint:
switch (easeType)
{
case EasingType::In:
return GetEasingResultCombinedIn(static_cast<float>(easeMethod), timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutQuint(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutQuint(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Sine:
switch (easeType)
{
case EasingType::In:
return GetEasingResultInSine(timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutSine(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutSine(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Expo:
switch (easeType)
{
case EasingType::In:
return GetEasingResultInExpo(timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutExpo(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutExpo(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Circ:
switch (easeType)
{
case EasingType::In:
return GetEasingResultInCirc(timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutCirc(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutCirc(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Elastic:
switch (easeType)
{
case EasingType::In:
return GetEasingResultInElastic(timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutElastic(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutElastic(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Back:
switch (easeType)
{
case EasingType::In:
return GetEasingResultInBack(timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutBack(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutBack(timeActive, duration, valueInitial, valueTarget);
}
break;
case EasingMethod::Bounce:
switch (easeType)
{
case EasingType::In:
return GetEasingResultInBounce(timeActive, duration, valueInitial, valueTarget);
case EasingType::InOut:
return GetEasingResultInOutBounce(timeActive, duration, valueInitial, valueTarget);
default: //EasingType::Out:
return GetEasingResultOutBounce(timeActive, duration, valueInitial, valueTarget);
}
break;
}
AZ_Warning("ScriptedEntityTweener", false, "ScriptedEntityTweenerMath::GetEasingResult - Trying to animate with an invalid easing function [%i, %i]", static_cast<int>(easeMethod), static_cast<int>(easeType));
return valueInitial;
}
//!Helper method to get EaseIn results for Quad, Cubic, Quart, and Quint since they all follow the same equation besides from how many times to multiply progressPercent.
template <typename T>
static T GetEasingResultCombinedIn(float expo, float timeActive, float duration, T valueInitial, T valueTarget)
{
float progressPercent = timeActive / duration;
return valueInitial + ((valueTarget - valueInitial) * pow(progressPercent, 1.0f + expo));
}
template <typename T>
static T GetEasingResultInSine(float timeActive, float duration, T valueInitial, T valueTarget)
{
return -(valueTarget - valueInitial) * cos(timeActive / duration * AZ::Constants::HalfPi) + (valueTarget - valueInitial) + valueInitial;
}
template <typename T>
static T GetEasingResultInExpo(float timeActive, float duration, T valueInitial, T valueTarget)
{
return (valueTarget - valueInitial) * pow(2.0f, 10.0f * (timeActive / duration - 1.0f)) + valueInitial;
}
template <typename T>
static T GetEasingResultInCirc(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive = timeActive / duration;
return -(valueTarget - valueInitial) * (sqrt(1.0f - timeActive * timeActive) - 1.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultInElastic(float timeActive, float duration, T valueInitial, T valueTarget)
{
float progressPercent = timeActive / duration;
if (progressPercent == 0.0f)
{
return valueInitial;
}
if (progressPercent == 1.0f)
{
return valueTarget;
}
T position = (valueTarget - valueInitial) * pow(2.0f, 10.0f * (progressPercent -= 1.0f));
float elasticAmplitude = 0.3f / 4.0f;
/*
if (amplitudeOverride != 0.0f)
{
elasticAmplitude = amplitudeOverride * elasticAmplitude;
}
*/
return valueInitial - (position * sin((progressPercent - elasticAmplitude) * 2.0f * AZ::Constants::Pi / 0.3f));
}
template <typename T>
static T GetEasingResultInBack(float timeActive, float duration, T valueInitial, T valueTarget)
{
float backAmplitude = 1.7337f;
float progressPercent = timeActive / duration;
/*if (amplitudeOverride != 0.0f)
{
backAmplitude = amplitudeOverride;
}
*/
return (valueTarget - valueInitial) * progressPercent * progressPercent * ((backAmplitude + 1.0f) * progressPercent - backAmplitude) + valueInitial;
}
template <typename T>
static T GetEasingResultInBounce(float timeActive, float duration, T valueInitial, T valueTarget)
{
const float bounceFullAmplitude = 7.1337f;
float progressPercent = timeActive / duration;
float bounceAmplitudeModifier = 0.0f;
if (progressPercent < (1.0f / 2.75f))
{
bounceAmplitudeModifier = 0.0f;
}
else if (progressPercent < (2.0f / 2.75f))
{
progressPercent -= (1.5f / 2.75f);
bounceAmplitudeModifier = 0.75f;
}
else if (progressPercent < (2.5f / 2.75f))
{
progressPercent -= (2.25f / 2.75f);
bounceAmplitudeModifier = 0.9375f;
}
else
{
progressPercent -= (2.625f / 2.75f);
bounceAmplitudeModifier = 0.984375f;
}
return valueInitial + (valueTarget - valueInitial) * (bounceFullAmplitude * progressPercent * progressPercent + bounceAmplitudeModifier);
}
//EASE OUT VARIANTS
template <typename T>
static T GetEasingResultOutQuad(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive = timeActive / duration;
return -(valueTarget - valueInitial) * timeActive * (timeActive - 2.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultOutCubic(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive = timeActive / duration;
timeActive--;
return (valueTarget - valueInitial) * (timeActive * timeActive * timeActive + 1.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultOutQuart(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive = timeActive / duration;
timeActive--;
return -(valueTarget - valueInitial) * (pow(timeActive, 4) - 1.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultOutQuint(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive = timeActive / duration;
timeActive--;
return (valueTarget - valueInitial) * (pow(timeActive, 5) + 1.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultOutSine(float timeActive, float duration, T valueInitial, T valueTarget)
{
return (valueTarget - valueInitial) * sin(timeActive / duration * AZ::Constants::HalfPi) + valueInitial;
}
template <typename T>
static T GetEasingResultOutExpo(float timeActive, float duration, T valueInitial, T valueTarget)
{
return (valueTarget - valueInitial) * (-pow(2.0f, -10.0f * timeActive / duration) + 1.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultOutCirc(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive = timeActive / duration;
timeActive--;
return (valueTarget - valueInitial) * sqrt(1.0f - timeActive * timeActive) + valueInitial;
}
template <typename T>
static T GetEasingResultOutElastic(float timeActive, float duration, T valueInitial, T valueTarget)
{
float progressPercent = timeActive / duration;
if (progressPercent == 0.0f)
{
return valueInitial;
}
if (progressPercent == 1.0f)
{
return valueTarget;
}
T distance = valueTarget - valueInitial;
T positionFix = distance * pow(2.0f, -10.0f * progressPercent);
float constant = 0.3f / 4.0f;
/*
if (amplitudeOverride != -1)
constant = amplitudeOverride*constant;
*/
return positionFix * sin((progressPercent - constant) * 2.0f * AZ::Constants::Pi / 0.3f) + valueTarget;
}
template <typename T>
static T GetEasingResultOutBack(float timeActive, float duration, T valueInitial, T valueTarget)
{
float progressPercent = timeActive / duration;
float constant = 1.7337f;
/*
if (amplitudeOverride != -1)
constant = amplitudeOverride;
*/
progressPercent--;
return (valueTarget - valueInitial) * (progressPercent * progressPercent * ((constant + 1.0f) * progressPercent + constant) + 1.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultOutBounce(float timeActive, float duration, T valueInitial, T valueTarget)
{
float progressPercent = timeActive / duration;
if (progressPercent < (1.0f / 2.75f))
{
return (valueTarget - valueInitial) * (7.5625f * progressPercent * progressPercent) + valueInitial;
}
else if (progressPercent < (2.0f / 2.75f))
{
progressPercent -= (1.5f / 2.75f);
return (valueTarget - valueInitial) * (7.5625f * progressPercent * progressPercent + 0.75f) + valueInitial;
}
else if (progressPercent < (2.5f / 2.75f))
{
progressPercent -= (2.25f / 2.75f);
return (valueTarget - valueInitial) * (7.5625f * progressPercent * progressPercent + 0.9375f) + valueInitial;
}
else
{
progressPercent -= (2.625f / 2.75f);
return (valueTarget - valueInitial) * (7.5625f * progressPercent * progressPercent + 0.984375f) + valueInitial;
}
}
//EASE IN OUT VARIANTS
template <typename T>
static T GetEasingResultInOutQuad(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive /= duration / 2.0f;
if (timeActive < 1.0f)
{
return (valueTarget - valueInitial) / 2.0f * timeActive * timeActive + valueInitial;
}
timeActive--;
return -(valueTarget - valueInitial) / 2.0f * (timeActive * (timeActive - 2.0f) - 1) + valueInitial;
}
template <typename T>
static T GetEasingResultInOutCubic(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive /= duration / 2.0f;
if (timeActive < 1.0f)
{
return (valueTarget - valueInitial) / 2.0f * timeActive * timeActive * timeActive + valueInitial;
}
timeActive -= 2.0f;
return (valueTarget - valueInitial) / 2.0f * (timeActive * timeActive * timeActive + 2.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultInOutQuart(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive /= duration / 2.0f;
if (timeActive < 1.0f)
{
return (valueTarget - valueInitial) / 2.0f * timeActive * timeActive * timeActive * timeActive + valueInitial;
}
timeActive -= 2.0f;
return -(valueTarget - valueInitial) / 2.0f * (timeActive * timeActive * timeActive * timeActive - 2.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultInOutQuint(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive /= duration / 2.0f;
if (timeActive < 1.0f)
{
return (valueTarget - valueInitial) / 2.0f * timeActive * timeActive * timeActive * timeActive * timeActive + valueInitial;
}
timeActive -= 2.0f;
return (valueTarget - valueInitial) / 2.0f * (timeActive * timeActive * timeActive * timeActive * timeActive + 2.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultInOutSine(float timeActive, float duration, T valueInitial, T valueTarget)
{
return -(valueTarget - valueInitial) / 2.0f * (cos(AZ::Constants::Pi * timeActive / duration) - 1.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultInOutExpo(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive /= duration / 2.0f;
if (timeActive < 1.0f)
{
return (valueTarget - valueInitial) / 2.0f * pow(2.0f, 10.0f * (timeActive - 1.0f)) + valueInitial;
}
timeActive--;
return (valueTarget - valueInitial) / 2.0f * (-pow(2.0f, -10.0f * timeActive) + 2.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultInOutCirc(float timeActive, float duration, T valueInitial, T valueTarget)
{
timeActive /= duration / 2.0f;
if (timeActive < 1.0f)
{
return -(valueTarget - valueInitial) / 2.0f * (sqrt(1.0f - timeActive * timeActive) - 1.0f) + valueInitial;
}
timeActive -= 2.0f;
return (valueTarget - valueInitial) / 2.0f * (sqrt(1.0f - timeActive * timeActive) - 1.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultInOutElastic(float timeActive, float duration, T valueInitial, T valueTarget)
{
float progressPercent = timeActive / duration;
progressPercent *= 2.0f;
if (progressPercent == 0.0f)
{
return valueInitial;
}
if (progressPercent == 2.0f)
{
return valueTarget;
}
T distance = valueTarget - valueInitial;
float constant = .3f * 1.5f;
/*
if (amplitudeOverride != -1)
constant = amplitudeOverride*constant;
*/
if (progressPercent < 1.0f)
{
T positionFix = distance * pow(2.0f, 10.0f * (progressPercent -= 1.0f));
return (positionFix * sin((progressPercent - (constant / 4.0f)) * 2.0f * AZ::Constants::Pi / constant)) * (-0.5f) + valueInitial;
}
else
{
T positionFix = distance * pow(2.0f, -10.0f * (progressPercent -= 1.0f));
return positionFix * sin((progressPercent - constant / 4.0f) * 2.0f * AZ::Constants::Pi / constant) * 0.5f + valueTarget;
}
}
template <typename T>
static T GetEasingResultInOutBack(float timeActive, float duration, T valueInitial, T valueTarget)
{
float progressPercent = timeActive / duration;
float constant = 1.7337f;
/*
if (amplitudeOverride != -1)
constant = amplitudeOverride;
*/
progressPercent *= 2.0f;
if (progressPercent < 1.0f)
{
constant = constant * 1.525f;
return (valueTarget - valueInitial) / 2.0f * (progressPercent * progressPercent * ((constant + 1.0f) * progressPercent - constant)) + valueInitial;
}
progressPercent -= 2.0f;
constant = constant * 1.525f;
return (valueTarget - valueInitial) / 2.0f * (progressPercent * progressPercent * ((constant + 1.0f) * progressPercent + constant) + 2.0f) + valueInitial;
}
template <typename T>
static T GetEasingResultInOutBounce(float timeActive, float duration, T valueInitial, T valueTarget)
{
float progressPercent = timeActive / duration;
T zero = valueInitial - valueInitial;
if (progressPercent < 0.5f)
{
return GetEasingResultInBounce(timeActive, duration / 2.0f, zero, (valueTarget - valueInitial)) * 0.5f + valueInitial;
}
else
{
return GetEasingResultOutBounce(timeActive - (duration / 2.0f), duration / 2.0f, zero, (valueTarget - valueInitial)) * 0.5f + (valueTarget - valueInitial) * 0.5f + valueInitial;
}
}
};
}
@@ -0,0 +1,73 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ScriptedEntityTweener_precompiled.h"
#include <IGem.h>
#include <ScriptedEntityTweener/ScriptedEntityTweenerBus.h>
#include "ScriptedEntityTweenerSystemComponent.h"
namespace ScriptedEntityTweener
{
class ScriptedEntityTweenerModule
: public CryHooksModule
{
public:
AZ_RTTI(ScriptedEntityTweenerModule, "{A6A93611-5E4D-4EB5-BFB9-00031F73F59B}", CryHooksModule);
ScriptedEntityTweenerModule()
: CryHooksModule()
{
// Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
m_descriptors.insert(m_descriptors.end(), {
ScriptedEntityTweenerSystemComponent::CreateDescriptor(),
});
}
/**
* Add required SystemComponents to the SystemEntity.
*/
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return AZ::ComponentTypeList{
azrtti_typeid<ScriptedEntityTweenerSystemComponent>(),
};
}
void OnSystemEvent(ESystemEvent systemEvent, UINT_PTR wparam, UINT_PTR lparam)
{
CryHooksModule::OnSystemEvent(systemEvent, wparam, lparam);
switch (systemEvent)
{
// In editor mode, ensure ending editor mode clears any in-flight animations
case ESYSTEM_EVENT_GAME_MODE_SWITCH_END:
{
bool inGame = wparam == 1;
if (!inGame)
{
EBUS_EVENT(ScriptedEntityTweenerBus, Reset);
}
} break;
default:
break;
}
}
};
}
// DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM
// The first parameter should be GemName_GemIdLower
// The second should be the fully qualified name of the class above
AZ_DECLARE_MODULE_CLASS(Gem_ScriptedEntityTweener, ScriptedEntityTweener::ScriptedEntityTweenerModule)
@@ -0,0 +1,476 @@
/*
* 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 "ScriptedEntityTweener_precompiled.h"
#include <AzCore/Math/Color.h>
#include <ScriptedEntityTweener/ScriptedEntityTweenerBus.h>
#include "ScriptedEntityTweenerSubtask.h"
AZ_PUSH_DISABLE_WARNING(4244, "-Wunknown-warning-option")
#include "ScriptedEntityTweenerMath.h"
AZ_POP_DISABLE_WARNING
namespace ScriptedEntityTweener
{
namespace SubtaskHelper
{
template <typename T>
AZ_INLINE void DoSafeSet(AZ::BehaviorEBus::VirtualProperty* prop, AZ::EntityId entityId, T& data)
{
if (!prop || !prop->m_setter)
{
return;
}
if (prop->m_setter->m_event)
{
prop->m_setter->m_event->Invoke(entityId, data);
}
else if (prop->m_setter->m_broadcast)
{
prop->m_setter->m_broadcast->Invoke(data);
}
}
template <typename T>
AZ_INLINE void DoSafeGet(AZ::BehaviorEBus::VirtualProperty* prop, AZ::EntityId entityId, T& data)
{
if (!prop || !prop->m_getter)
{
return;
}
if (prop->m_getter->m_event)
{
prop->m_getter->m_event->InvokeResult(data, entityId);
}
else if (prop->m_getter->m_broadcast)
{
prop->m_getter->m_broadcast->InvokeResult(data);
}
}
}
bool ScriptedEntityTweenerSubtask::Initialize(const AnimationParameterAddressData& animParamData, const AZStd::any& targetValue, const AnimationProperties& properties)
{
Reset();
if (CacheVirtualProperty(animParamData))
{
// Set initial value
if (GetVirtualValue(m_valueInitial))
{
if (GetValueFromAny(m_valueTarget, targetValue))
{
m_isActive = true;
m_animationProperties = properties;
if (m_animationProperties.m_isFrom)
{
EntityAnimatedValue initialValue = m_valueInitial;
m_valueInitial = m_valueTarget;
m_valueTarget = initialValue;
}
return true;
}
}
}
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::Initialize - Initialization failed for [%s, %s]", m_animParamData.m_componentName.c_str(), m_animParamData.m_virtualPropertyName.c_str());
return false;
}
void ScriptedEntityTweenerSubtask::Update(float deltaTime, AZStd::set<CallbackData>& callbacks)
{
if (m_isPaused || !m_isActive)
{
return;
}
//TODO: Use m_animationProperties.m_amplitudeOverride
float timeAnimationActive = AZ::GetClamp(m_timeSinceStart + m_animationProperties.m_timeIntoAnimation, .0f, m_animationProperties.m_timeDuration);
//If animation is meant to complete instantly, set timeToComplete and timeAnimationActive to the same non-zero value, so GetEasingResult will return m_valueTarget
if (m_animationProperties.m_timeDuration == 0)
{
m_animationProperties.m_timeDuration = timeAnimationActive = 1.0f;
}
EntityAnimatedValue currentValue;
if (m_virtualPropertyTypeId == AZ::AzTypeInfo<float>::Uuid())
{
float initialValue;
m_valueInitial.GetValue(initialValue);
float targetValue;
m_valueTarget.GetValue(targetValue);
currentValue.SetValue(EasingEquations::GetEasingResult(m_animationProperties.m_easeMethod, m_animationProperties.m_easeType, timeAnimationActive, m_animationProperties.m_timeDuration, initialValue, targetValue));
SetVirtualValue(currentValue);
}
else if (m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Vector3>::Uuid() || m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Color>::Uuid())
{
AZ::Vector3 initialValue;
m_valueInitial.GetValue(initialValue);
AZ::Vector3 targetValue;
m_valueTarget.GetValue(targetValue);
currentValue.SetValue(EasingEquations::GetEasingResult(m_animationProperties.m_easeMethod, m_animationProperties.m_easeType, timeAnimationActive, m_animationProperties.m_timeDuration, initialValue, targetValue));
SetVirtualValue(currentValue);
}
else if (m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Quaternion>::Uuid())
{
AZ::Quaternion initialValue;
m_valueInitial.GetValue(initialValue);
AZ::Quaternion targetValue;
m_valueTarget.GetValue(targetValue);
currentValue.SetValue(EasingEquations::GetEasingResult(m_animationProperties.m_easeMethod, m_animationProperties.m_easeType, timeAnimationActive, m_animationProperties.m_timeDuration, initialValue, targetValue));
SetVirtualValue(currentValue);
}
float progressPercent = timeAnimationActive / m_animationProperties.m_timeDuration;
if (m_animationProperties.m_isPlayingBackward)
{
progressPercent = 1.0f - progressPercent;
}
if (progressPercent >= 1.0f)
{
m_timesPlayed = m_timesPlayed + 1;
if (m_timesPlayed >= m_animationProperties.m_timesToPlay && m_animationProperties.m_timesToPlay != -1)
{
m_isActive = false;
if (m_animationProperties.m_onCompleteCallbackId != AnimationProperties::InvalidCallbackId)
{
callbacks.insert(CallbackData(CallbackTypes::OnComplete, m_animationProperties.m_onCompleteCallbackId));
}
if (m_animationProperties.m_onLoopCallbackId != AnimationProperties::InvalidCallbackId)
{
callbacks.insert(CallbackData(CallbackTypes::RemoveCallback, m_animationProperties.m_onLoopCallbackId));
}
if (m_animationProperties.m_onUpdateCallbackId != AnimationProperties::InvalidCallbackId)
{
callbacks.insert(CallbackData(CallbackTypes::RemoveCallback, m_animationProperties.m_onUpdateCallbackId));
}
}
else
{
m_timeSinceStart = .0f;
if (m_animationProperties.m_onLoopCallbackId != AnimationProperties::InvalidCallbackId)
{
callbacks.insert(CallbackData(CallbackTypes::OnLoop, m_animationProperties.m_onLoopCallbackId));
}
}
}
if (m_animationProperties.m_onUpdateCallbackId != AnimationProperties::InvalidCallbackId)
{
CallbackData updateCallback(CallbackTypes::OnUpdate, m_animationProperties.m_onUpdateCallbackId);
GetValueAsAny(updateCallback.m_callbackData, currentValue);
updateCallback.m_progressPercent = progressPercent;
callbacks.insert(updateCallback);
}
if (m_animationProperties.m_isPlayingBackward)
{
deltaTime *= -1.0f;
}
m_timeSinceStart += (deltaTime * m_animationProperties.m_playbackSpeedMultiplier);
}
bool ScriptedEntityTweenerSubtask::CacheVirtualProperty(const AnimationParameterAddressData& animParamData)
{
/*
Relies on some behavior context definitions for lookup
behaviorContext->EBus<UiFaderBus>("UiFaderBus")
->Event("GetFadeValue", &UiFaderBus::Events::GetFadeValue)
->Event("SetFadeValue", &UiFaderBus::Events::SetFadeValue)
->VirtualProperty("Fade", "GetFadeValue", "SetFadeValue");
behaviorContext->Class<UiFaderComponent>()->RequestBus("UiFaderBus");
behaviorContext->EBus<UiFaderNotificationBus>("UiFaderNotificationBus")
->Handler<BehaviorUiFaderNotificationBusHandler>();
*/
m_animParamData = animParamData;
m_virtualProperty = nullptr;
m_virtualPropertyTypeId = AZ::Uuid::CreateNull();
AZ::BehaviorContext* behaviorContext = nullptr;
EBUS_EVENT_RESULT(behaviorContext, AZ::ComponentApplicationBus, GetBehaviorContext);
if (!behaviorContext)
{
AZ_Error("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::CacheVirtualProperty - failed to get behavior context for caching [%s]", animParamData.m_virtualPropertyName.c_str());
return false;
}
auto findClassIter = behaviorContext->m_classes.find(animParamData.m_componentName);
if (findClassIter == behaviorContext->m_classes.end())
{
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::CacheVirtualProperty - failed to find behavior component class by component name [%s]", animParamData.m_componentName.c_str());
return false;
}
AZ::BehaviorEBus::VirtualProperty* virtualProperty = nullptr;
AZ::Uuid virtualPropertyTypeId = AZ::Uuid::CreateNull();
// Get the virtual property
AZ::BehaviorClass* behaviorClass = findClassIter->second;
for (auto reqBusName = behaviorClass->m_requestBuses.begin(); reqBusName != behaviorClass->m_requestBuses.end(); reqBusName++)
{
auto findBusIter = behaviorContext->m_ebuses.find(*reqBusName);
if (findBusIter != behaviorContext->m_ebuses.end())
{
AZ::BehaviorEBus* behaviorEbus = findBusIter->second;
auto virtualPropertyIter = behaviorEbus->m_virtualProperties.find(animParamData.m_virtualPropertyName);
if (virtualPropertyIter != behaviorEbus->m_virtualProperties.end())
{
virtualProperty = &virtualPropertyIter->second;
break;
}
}
}
AZ_Warning("ScriptedEntityTweenerSubtask", virtualProperty, "ScriptedEntityTweenerSubtask::CacheVirtualProperty - failed to find virtual property by name [%s]", animParamData.m_virtualPropertyName.c_str());
// Virtual properties with event setters/getters require a valid entityId
if (virtualProperty)
{
if (((virtualProperty->m_setter && virtualProperty->m_setter->m_event)
|| (virtualProperty->m_getter && virtualProperty->m_getter->m_event))
&& !m_entityId.IsValid())
{
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::CacheVirtualProperty - invalid entityId for virtual property's event setter/getter [%s, %s]", m_animParamData.m_componentName.c_str(), m_animParamData.m_virtualPropertyName.c_str());
virtualProperty = nullptr;
}
}
// Get the virtual property type
if (virtualProperty)
{
if (virtualProperty->m_getter->m_event)
{
virtualPropertyTypeId = virtualProperty->m_getter->m_event->GetResult()->m_typeId;
}
else if (virtualProperty->m_getter->m_broadcast)
{
virtualPropertyTypeId = virtualProperty->m_getter->m_broadcast->GetResult()->m_typeId;
}
AZ_Warning("ScriptedEntityTweenerSubtask", !virtualPropertyTypeId.IsNull(), "ScriptedEntityTweenerSubtask::CacheVirtualProperty - failed to find virtual property type Id [%s]", animParamData.m_virtualPropertyName.c_str());
}
if (virtualProperty && !virtualPropertyTypeId.IsNull())
{
m_virtualProperty = virtualProperty;
m_virtualPropertyTypeId = virtualPropertyTypeId;
return true;
}
return false;
}
bool ScriptedEntityTweenerSubtask::IsVirtualPropertyCached()
{
return m_virtualProperty && !m_virtualPropertyTypeId.IsNull();
}
bool ScriptedEntityTweenerSubtask::GetValueFromAny(EntityAnimatedValue& value, const AZStd::any& anyValue)
{
if (!IsVirtualPropertyCached())
{
return false;
}
if (m_virtualPropertyTypeId == AZ::AzTypeInfo<float>::Uuid())
{
float floatVal;
if (any_numeric_cast(&anyValue, floatVal))
{
value.SetValue(floatVal);
}
else
{
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::GetValueFromAny - numeric cast to float failed [%s]", m_animParamData.m_virtualPropertyName.c_str());
return false;
}
}
else if (m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Vector3>::Uuid() && anyValue.is<AZ::Vector3>())
{
value.SetValue(AZStd::any_cast<AZ::Vector3>(anyValue));
}
else if (m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Color>::Uuid() && anyValue.is<AZ::Color>())
{
AZ::Color color = AZStd::any_cast<AZ::Color>(anyValue);
value.SetValue(color.GetAsVector3());
}
else if (m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Quaternion>::Uuid() && anyValue.is<AZ::Quaternion>())
{
value.SetValue(AZStd::any_cast<AZ::Quaternion>(anyValue));
}
else
{
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::GetValueFromAny - Virtual property type unsupported [%s]", m_animParamData.m_virtualPropertyName.c_str());
return false;
}
return true;
}
bool ScriptedEntityTweenerSubtask::GetValueAsAny(AZStd::any& anyValue, const EntityAnimatedValue& value)
{
if (!IsVirtualPropertyCached())
{
return false;
}
if (m_virtualPropertyTypeId == AZ::AzTypeInfo<float>::Uuid())
{
float floatVal;
value.GetValue(floatVal);
anyValue = floatVal;
}
else if (m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Vector3>::Uuid())
{
AZ::Vector3 vectorValue;
value.GetValue(vectorValue);
anyValue = vectorValue;
}
else if (m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Color>::Uuid())
{
AZ::Vector3 vectorValue;
value.GetValue(vectorValue);
anyValue = AZ::Color::CreateFromVector3(vectorValue);
}
else if (m_virtualPropertyTypeId == AZ::AzTypeInfo<AZ::Quaternion>::Uuid())
{
AZ::Quaternion quatValue;
value.GetValue(quatValue);
anyValue = quatValue;
}
else
{
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::GetValueAsAny - Virtual property type unsupported [%s]", m_animParamData.m_virtualPropertyName.c_str());
return false;
}
return true;
}
bool ScriptedEntityTweenerSubtask::GetVirtualValue(EntityAnimatedValue& animatedValue)
{
if (!IsVirtualPropertyCached())
{
return false;
}
if (m_virtualPropertyTypeId == AZ::AzTypeInfo<float>::Uuid())
{
float floatValue = 0.0f;
SubtaskHelper::DoSafeGet(m_virtualProperty, m_entityId, floatValue);
animatedValue.SetValue(floatValue);
}
else if (m_virtualPropertyTypeId == AZ::Vector3::TYPEINFO_Uuid())
{
AZ::Vector3 vector3Value(AZ::Vector3::CreateZero());
SubtaskHelper::DoSafeGet(m_virtualProperty, m_entityId, vector3Value);
animatedValue.SetValue(vector3Value);
}
else if (m_virtualPropertyTypeId == AZ::Color::TYPEINFO_Uuid())
{
AZ::Color colorValue(AZ::Color::CreateZero());
SubtaskHelper::DoSafeGet(m_virtualProperty, m_entityId, colorValue);
AZ::Vector3 vector3Value = colorValue.GetAsVector3();
animatedValue.SetValue(vector3Value);
}
else if (m_virtualPropertyTypeId == AZ::Quaternion::TYPEINFO_Uuid())
{
AZ::Quaternion quaternionValue(AZ::Quaternion::CreateIdentity());
SubtaskHelper::DoSafeGet(m_virtualProperty, m_entityId, quaternionValue);
animatedValue.SetValue(quaternionValue);
}
else
{
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::GetVirtualValue - Trying to get unsupported parameter type for [%s]", m_animParamData.m_virtualPropertyName.c_str());
return false;
}
return true;
}
bool ScriptedEntityTweenerSubtask::SetVirtualValue(const EntityAnimatedValue& value)
{
if (!IsVirtualPropertyCached())
{
return false;
}
if (m_virtualPropertyTypeId == AZ::AzTypeInfo<float>::Uuid())
{
float floatValue;
value.GetValue(floatValue);
SubtaskHelper::DoSafeSet(m_virtualProperty, m_entityId, floatValue);
}
else if (m_virtualPropertyTypeId == AZ::Vector3::TYPEINFO_Uuid())
{
AZ::Vector3 vector3Value;
value.GetValue(vector3Value);
SubtaskHelper::DoSafeSet(m_virtualProperty, m_entityId, vector3Value);
}
else if (m_virtualPropertyTypeId == AZ::Color::TYPEINFO_Uuid())
{
AZ::Vector3 vector3Value;
value.GetValue(vector3Value);
AZ::Color colorValue(AZ::Color::CreateFromVector3(vector3Value));
SubtaskHelper::DoSafeSet(m_virtualProperty, m_entityId, colorValue);
}
else if (m_virtualPropertyTypeId == AZ::Quaternion::TYPEINFO_Uuid())
{
AZ::Quaternion quaternionValue;
value.GetValue(quaternionValue);
SubtaskHelper::DoSafeSet(m_virtualProperty, m_entityId, quaternionValue);
}
else
{
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::SetVirtualValue - Trying to set unsupported parameter type for [%s]", m_animParamData.m_virtualPropertyName.c_str());
return false;
}
return true;
}
bool ScriptedEntityTweenerSubtask::GetVirtualPropertyValue(AZStd::any& returnVal, const AnimationParameterAddressData& animParamData)
{
// If this is called before initialization, the virtual property needs to be cached.
// This method is available on the ScriptedEntityTweenerBus to retrieve a virtual property value on an entity
// regardless of whether a task/subtask for that entity/virtual property has been created (added to an animation).
// In that circumstance, a temproary task/subtask is created, but not initialized
if (!IsVirtualPropertyCached())
{
CacheVirtualProperty(animParamData);
}
if (IsVirtualPropertyCached())
{
EntityAnimatedValue tempVal;
if (GetVirtualValue(tempVal))
{
return GetValueAsAny(returnVal, tempVal);
}
}
AZ_Warning("ScriptedEntityTweenerSubtask", false, "ScriptedEntityTweenerSubtask::GetVirtualPropertyValue - failed for [%s, %s]", m_animParamData.m_componentName.c_str(), m_animParamData.m_virtualPropertyName.c_str());
return false;
}
}
@@ -0,0 +1,183 @@
/*
* 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 <AzCore/Math/Vector3.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/std/any.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <ScriptedEntityTweener/ScriptedEntityTweenerEnums.h>
namespace ScriptedEntityTweener
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//! Each subtask performs operations on a virtual address.
class ScriptedEntityTweenerSubtask
{
public:
ScriptedEntityTweenerSubtask(const AZ::EntityId& entityId)
: m_entityId(entityId)
{
Reset();
}
bool Initialize(const AnimationParameterAddressData& animParamData, const AZStd::any& targetValue, const AnimationProperties& properties);
//! Update virtual property based on animation properties, fill out callbacks vector with any callback information that needs to be called this update.
void Update(float deltaTime, AZStd::set<CallbackData>& callbacks);
//! True if active and animating a virtual property
bool IsActive() { return m_isActive; }
void SetPaused(int timelineId, bool isPaused)
{
if (m_animationProperties.m_timelineId == timelineId)
{
m_isPaused = isPaused;
}
}
void SetPlayDirectionReversed(int timelineId, bool isPlayingBackward)
{
if (m_animationProperties.m_timelineId == timelineId)
{
m_animationProperties.m_isPlayingBackward = isPlayingBackward;
}
}
void SetSpeed(int timelineId, float speed)
{
if (m_animationProperties.m_timelineId == timelineId)
{
m_animationProperties.m_playbackSpeedMultiplier = speed;
}
}
void SetInitialValue(const AZ::Uuid& animationId, const AZStd::any& initialValue)
{
if (m_animationProperties.m_animationId == animationId)
{
GetValueFromAny(m_valueInitial, initialValue);
}
}
const int GetTimelineId() const
{
return m_animationProperties.m_timelineId;
}
const AnimationProperties& GetAnimationProperties() const
{
return m_animationProperties;
}
bool GetVirtualPropertyValue(AZStd::any& returnVal, const AnimationParameterAddressData& animParamData);
private:
//! Animated value class that abstracts the supported types
struct EntityAnimatedValue
{
public:
EntityAnimatedValue()
: floatVal(AnimationProperties::UninitializedParamFloat), vectorVal(AZ::Vector3::CreateZero()), quatVal(AZ::Quaternion::CreateIdentity())
{
}
void GetValue(float& outVal) const
{
outVal = floatVal;
}
void GetValue(AZ::Vector3& outVal) const
{
outVal = vectorVal;
}
void GetValue(AZ::Quaternion& outVal) const
{
outVal = quatVal;
}
void SetValue(float val)
{
floatVal = val;
}
void SetValue(AZ::Vector3 val)
{
vectorVal = val;
}
void SetValue(AZ::Quaternion val)
{
quatVal = val;
}
private:
float floatVal;
AZ::Vector3 vectorVal;
AZ::Quaternion quatVal;
};
AnimationProperties m_animationProperties;
// The entity being modified
AZ::EntityId m_entityId;
// The component and property name to be modified. This is only used for displaying warning messages
AnimationParameterAddressData m_animParamData;
// Cached virtual property
AZ::BehaviorEBus::VirtualProperty* m_virtualProperty;
// Type of the virtual property
AZ::Uuid m_virtualPropertyTypeId;
bool m_isActive;
bool m_isPaused;
float m_timeSinceStart;
int m_timesPlayed;
EntityAnimatedValue m_valueInitial;
EntityAnimatedValue m_valueTarget;
void Reset()
{
m_isActive = false;
m_isPaused = false;
m_timeSinceStart = 0.0f;
m_valueInitial = EntityAnimatedValue();
m_valueTarget = EntityAnimatedValue();
m_timesPlayed = 0;
m_animationProperties.Reset();
m_animParamData = AnimationParameterAddressData();
m_virtualPropertyTypeId = AZ::Uuid::CreateNull();
m_virtualProperty = nullptr;
}
//! Cache the virtual property to be animated
bool CacheVirtualProperty(const AnimationParameterAddressData& animParamData);
//! Return whether the virtual property has been cached
bool IsVirtualPropertyCached();
//! Set value from an AZStd::any object
bool GetValueFromAny(EntityAnimatedValue& value, const AZStd::any& anyValue);
//! Set value to an AZStd::any object
bool GetValueAsAny(AZStd::any& anyValue, const EntityAnimatedValue& value);
//! Get value from the virtual address's value
bool GetVirtualValue(EntityAnimatedValue& animatedValue);
//! Set the virtual address's value
bool SetVirtualValue(const EntityAnimatedValue& value);
};
}
@@ -0,0 +1,304 @@
/*
* 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 "ScriptedEntityTweener_precompiled.h"
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <ScriptedEntityTweener/ScriptedEntityTweenerBus.h>
#include <ScriptedEntityTweener/ScriptedEntityTweenerEnums.h>
#include "ScriptedEntityTweenerSystemComponent.h"
namespace ScriptedEntityTweener
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//! ScriptedEntityTweenerNotificationBusHandler Behavior context handler class
class ScriptedEntityTweenerNotificationBusHandler :
public ScriptedEntityTweenerNotificationsBus::Handler,
public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(ScriptedEntityTweenerNotificationBusHandler, "{118D3961-17C7-46E9-B9AC-61682D57E8D3}", AZ::SystemAllocator,
OnComplete, OnUpdate, OnLoop, RemoveCallback, OnTimelineAnimationStart);
void OnComplete(int callbackId) override
{
Call(FN_OnComplete, callbackId);
}
void OnUpdate(int callbackId, const AZStd::any& currentVal, float progressPercent) override
{
Call(FN_OnUpdate, callbackId, currentVal, progressPercent);
}
void OnLoop(int callbackId) override
{
Call(FN_OnLoop, callbackId);
}
void RemoveCallback(int callbackId) override
{
Call(FN_RemoveCallback, callbackId);
}
void OnTimelineAnimationStart(int timelineId, const AZ::Uuid& uuid, const AZStd::string& componentName, const AZStd::string& propertyName)
{
Call(FN_OnTimelineAnimationStart, timelineId, uuid, componentName, propertyName);
}
};
void ScriptedEntityTweenerSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<ScriptedEntityTweenerSystemComponent, AZ::Component>()
->Version(0)
;
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->EBus<ScriptedEntityTweenerBus>("ScriptedEntityTweenerBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Event("AnimateEntity", &ScriptedEntityTweenerBus::Events::AnimateEntityScript)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::List)
->Event("SetOptionalParams", &ScriptedEntityTweenerBus::Events::SetOptionalParams)
->Event("Stop", &ScriptedEntityTweenerBus::Events::Stop)
->Event("Pause", &ScriptedEntityTweenerBus::Events::Pause)
->Event("Resume", &ScriptedEntityTweenerBus::Events::Resume)
->Event("SetPlayDirectionReversed", &ScriptedEntityTweenerBus::Events::SetPlayDirectionReversed)
->Event("SetSpeed", &ScriptedEntityTweenerBus::Events::SetSpeed)
->Event("SetInitialValue", &ScriptedEntityTweenerBus::Events::SetInitialValue)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::List)
->Event("GetVirtualPropertyValue", &ScriptedEntityTweenerBus::Events::GetVirtualPropertyValue);
behaviorContext->EBus<ScriptedEntityTweenerNotificationsBus>("ScriptedEntityTweenerNotificationBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Handler<ScriptedEntityTweenerNotificationBusHandler>();
behaviorContext->Enum<(int)ScriptedEntityTweener::EasingMethod::Linear>("ScriptedEntityTweenerEasingMethod_Linear")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Quad>("ScriptedEntityTweenerEasingMethod_Quad")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Cubic>("ScriptedEntityTweenerEasingMethod_Cubic")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Quart>("ScriptedEntityTweenerEasingMethod_Quart")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Quint>("ScriptedEntityTweenerEasingMethod_Quint")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Sine>("ScriptedEntityTweenerEasingMethod_Sine")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Expo>("ScriptedEntityTweenerEasingMethod_Expo")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Circ>("ScriptedEntityTweenerEasingMethod_Circ")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Elastic>("ScriptedEntityTweenerEasingMethod_Elastic")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Back>("ScriptedEntityTweenerEasingMethod_Back")
->Enum<(int)ScriptedEntityTweener::EasingMethod::Bounce>("ScriptedEntityTweenerEasingMethod_Bounce")
->Enum<(int)ScriptedEntityTweener::EasingType::In>("ScriptedEntityTweenerEasingType_In")
->Enum<(int)ScriptedEntityTweener::EasingType::Out>("ScriptedEntityTweenerEasingType_Out")
->Enum<(int)ScriptedEntityTweener::EasingType::InOut>("ScriptedEntityTweenerEasingType_InOut");
}
}
void ScriptedEntityTweenerSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ScriptedEntityTweenerService"));
}
void ScriptedEntityTweenerSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("ScriptedEntityTweenerService"));
}
void ScriptedEntityTweenerSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
(void)required;
}
void ScriptedEntityTweenerSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
(void)dependent;
}
void ScriptedEntityTweenerSystemComponent::Init()
{
}
void ScriptedEntityTweenerSystemComponent::Activate()
{
ScriptedEntityTweenerBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
}
void ScriptedEntityTweenerSystemComponent::Deactivate()
{
ScriptedEntityTweenerBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
}
void ScriptedEntityTweenerSystemComponent::AnimateEntity(const AZ::EntityId& entityId, const AnimationParameters& params)
{
auto animationTask = m_animationTasks.find(ScriptedEntityTweenerTask(entityId));
if (animationTask == m_animationTasks.end())
{
animationTask = m_animationTasks.insert(ScriptedEntityTweenerTask(entityId)).first;
}
animationTask->AddAnimation(params);
}
void ScriptedEntityTweenerSystemComponent::SetOptionalParams(float timeIntoAnimation, float duration, int easingMethod, int easingType, float delayTime, int timesToPlay, bool isFrom, bool isPlayingBackward, const AZ::Uuid& animationId, int timelineId, int onCompleteCallbackId, int onUpdateCallbackId, int onLoopCallbackId)
{
AnimationParameters& params = m_tempParams;
params.m_animationProperties.m_easeMethod = static_cast<EasingMethod>(easingMethod);
params.m_animationProperties.m_easeType = static_cast<EasingType>(easingType);
params.m_animationProperties.m_timeIntoAnimation = timeIntoAnimation;
params.m_animationProperties.m_timeDuration = duration;
params.m_animationProperties.m_timeToDelayAnim = delayTime;
params.m_animationProperties.m_timesToPlay = timesToPlay;
params.m_animationProperties.m_isFrom = isFrom;
params.m_animationProperties.m_isPlayingBackward = isPlayingBackward;
params.m_animationProperties.m_animationId = animationId;
params.m_animationProperties.m_timelineId = timelineId;
params.m_animationProperties.m_onCompleteCallbackId = onCompleteCallbackId;
params.m_animationProperties.m_onUpdateCallbackId = onUpdateCallbackId;
params.m_animationProperties.m_onLoopCallbackId = onLoopCallbackId;
}
void ScriptedEntityTweenerSystemComponent::AnimateEntityScript(const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, const AZStd::any& paramTarget)
{
AnimationParameters& params = m_tempParams;
AnimationParameterAddressData data(componentName, virtualPropertyName);
params.m_animationParameters.emplace(data, paramTarget);
AnimateEntity(entityId, params);
m_tempParams.Reset();
}
void ScriptedEntityTweenerSystemComponent::Stop(int timelineId, const AZ::EntityId& entityId)
{
auto animationTask = m_animationTasks.find(ScriptedEntityTweenerTask(entityId));
if (animationTask == m_animationTasks.end())
{
return;
}
animationTask->Stop(timelineId);
}
void ScriptedEntityTweenerSystemComponent::Pause(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName)
{
auto animationTask = m_animationTasks.find(ScriptedEntityTweenerTask(entityId));
if (animationTask == m_animationTasks.end())
{
return;
}
AnimationParameterAddressData data(componentName, virtualPropertyName);
animationTask->SetPaused(data, timelineId, true);
}
void ScriptedEntityTweenerSystemComponent::Resume(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName)
{
auto animationTask = m_animationTasks.find(ScriptedEntityTweenerTask(entityId));
if (animationTask == m_animationTasks.end())
{
return;
}
AnimationParameterAddressData data(componentName, virtualPropertyName);
animationTask->SetPaused(data, timelineId, false);
}
void ScriptedEntityTweenerSystemComponent::SetPlayDirectionReversed(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, bool rewind)
{
auto animationTask = m_animationTasks.find(ScriptedEntityTweenerTask(entityId));
if (animationTask == m_animationTasks.end())
{
return;
}
AnimationParameterAddressData data(componentName, virtualPropertyName);
animationTask->SetPlayDirectionReversed(data, timelineId, rewind);
}
void ScriptedEntityTweenerSystemComponent::SetSpeed(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, float speed)
{
auto animationTask = m_animationTasks.find(ScriptedEntityTweenerTask(entityId));
if (animationTask == m_animationTasks.end())
{
return;
}
AnimationParameterAddressData data(componentName, virtualPropertyName);
animationTask->SetSpeed(data, timelineId, speed);
}
void ScriptedEntityTweenerSystemComponent::SetInitialValue(const AZ::Uuid& animationId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, const AZStd::any& initialValue)
{
auto animationTask = m_animationTasks.find(ScriptedEntityTweenerTask(entityId));
if (animationTask == m_animationTasks.end())
{
return;
}
AnimationParameterAddressData data(componentName, virtualPropertyName);
animationTask->SetInitialValue(data, animationId, initialValue);
}
AZStd::any ScriptedEntityTweenerSystemComponent::GetVirtualPropertyValue(const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName)
{
AZStd::any toReturn;
AnimationParameterAddressData data(componentName, virtualPropertyName);
auto animationTask = m_animationTasks.find(ScriptedEntityTweenerTask(entityId));
if (animationTask != m_animationTasks.end())
{
animationTask->GetVirtualPropertyValue(toReturn, data);
}
else
{
ScriptedEntityTweenerTask task(entityId);
task.GetVirtualPropertyValue(toReturn, data);
}
return toReturn;
}
void ScriptedEntityTweenerSystemComponent::Reset()
{
m_animationTasks.clear();
}
void ScriptedEntityTweenerSystemComponent::OnTick(float deltaTime, AZ::ScriptTimePoint /*time*/)
{
for (auto& animTask : m_animationTasks)
{
animTask.Update(deltaTime);
}
//TODO: Possible optimization: Logic to remove "stale" animation tasks, use a "garbage collection" method to defer set removal operations (which is O(n))
for (auto it = m_animationTasks.begin(); it != m_animationTasks.end(); )
{
if (!((*it).GetIsActive()))
{
it = m_animationTasks.erase(it);
}
else
{
++it;
}
}
}
int ScriptedEntityTweenerSystemComponent::GetTickOrder()
{
return AZ::TICK_LAST;
}
}
@@ -0,0 +1,96 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/Component.h>
#include <AzCore/std/containers/set.h>
#include <ScriptedEntityTweener/ScriptedEntityTweenerBus.h>
#include "ScriptedEntityTweenerTask.h"
namespace ScriptedEntityTweener
{
class ScriptedEntityTweenerSystemComponent
: public AZ::Component
, protected ScriptedEntityTweenerBus::Handler
, protected AZ::TickBus::Handler
{
public:
AZ_COMPONENT(ScriptedEntityTweenerSystemComponent, "{6AAC4396-2FAB-4273-BA80-2D25DC91A116}", AZ::Component);
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
protected:
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// ScriptedEntityTweenerManagerBus interface implementation
void AnimateEntity(const AZ::EntityId& entityId, const AnimationParameters& params) override;
//! Sets optional animation parameters to be used on next AnimateEntityScript call, needed as lua implementation doesn't support > 13 args in non-debug builds
void SetOptionalParams(float timeIntoAnimation,
float duration,
int easingMethod,
int easingType,
float delayTime,
int timesToPlay,
bool isFrom,
bool isPlayingBackward,
const AZ::Uuid& animationId,
int timelineId,
int onCompleteCallbackId,
int onUpdateCallbackId,
int onLoopCallbackId) override;
//! Script exposed version of the AnimateEntity call
void AnimateEntityScript(const AZ::EntityId& entityId,
const AZStd::string& componentName,
const AZStd::string& virtualPropertyName,
const AZStd::any& paramTarget) override;
void Stop(int timelineId, const AZ::EntityId& entityId) override;
void Pause(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName) override;
void Resume(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName) override;
void SetPlayDirectionReversed(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, bool rewind) override;
void SetSpeed(int timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, float speed) override;
void SetInitialValue(const AZ::Uuid& timelineId, const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName, const AZStd::any& initialValue) override;
AZStd::any GetVirtualPropertyValue(const AZ::EntityId& entityId, const AZStd::string& componentName, const AZStd::string& virtualPropertyName) override;
void Reset() override;
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// AZ::TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
int GetTickOrder() override;
////////////////////////////////////////////////////////////////////////
private:
AZStd::set<ScriptedEntityTweenerTask> m_animationTasks;
//! Used by AnimateEntityScript, setup by SetOptionalParams
AnimationParameters m_tempParams;
};
}
@@ -0,0 +1,395 @@
/*
* 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 "ScriptedEntityTweener_precompiled.h"
#include <ScriptedEntityTweener/ScriptedEntityTweenerBus.h>
#include "ScriptedEntityTweenerTask.h"
namespace ScriptedEntityTweener
{
const AZStd::any ScriptedEntityTweenerTask::QueuedSubtaskInfo::m_emptyInitialValue;
const float AnimationProperties::UninitializedParamFloat = FLT_MIN;
const unsigned int AnimationProperties::InvalidCallbackId = 0;
const unsigned int AnimationProperties::InvalidTimelineId = 0;
ScriptedEntityTweenerTask::ScriptedEntityTweenerTask(AZ::EntityId id)
: m_entityId(id)
{
}
ScriptedEntityTweenerTask::~ScriptedEntityTweenerTask()
{
}
void ScriptedEntityTweenerTask::AddAnimation(const AnimationParameters& params, bool overwriteQueued)
{
float timeToDelay = params.m_animationProperties.m_timeToDelayAnim;
if (timeToDelay > .0f)
{
m_queuedSubtasks.emplace_back(QueuedSubtaskInfo(params, params.m_animationProperties.m_timeToDelayAnim));
return;
}
for (const auto& it : params.m_animationParameters)
{
const AnimationParameterAddressData& addressData = it.first;
if (IsTimelineIdValid(params.m_animationProperties.m_timelineId))
{
ScriptedEntityTweenerNotificationsBus::Broadcast(&ScriptedEntityTweenerNotificationsBus::Events::OnTimelineAnimationStart,
params.m_animationProperties.m_timelineId,
params.m_animationProperties.m_animationId,
addressData.m_componentName,
addressData.m_virtualPropertyName);
}
auto subtask = m_subtasks.find(addressData);
if (subtask == m_subtasks.end())
{
//For this property on this entity, an animation isn't already running.
ScriptedEntityTweenerSubtask subtaskToAdd(m_entityId);
if (params.m_animationProperties.m_timeDuration == 0.0f)
{
//If the animation is a set animation, immediately initialize, execute, and call callbacks related to it.
if (InitializeSubtask(subtaskToAdd, it, params))
{
AZStd::set<CallbackData> callbacks;
subtaskToAdd.Update(0, callbacks);
ExecuteCallbacks(callbacks);
}
return;
}
else
{
//Animation will play over some time, enqueue it to play as part of the update loop.
subtask = m_subtasks.emplace(addressData, subtaskToAdd).first;
}
}
else
{
//An animation already exists for this virtual property
//Cleanup any callbacks it may have registered.
ClearCallbacks(subtask->second.GetAnimationProperties());
//Overwrite any queued animations on this subtask if the animation wasn't started from the queue, as it was user specified.
if (overwriteQueued)
{
auto queuedSubtaskIter = m_queuedSubtasks.begin();
while (queuedSubtaskIter != m_queuedSubtasks.end())
{
auto& queuedAnimParams = queuedSubtaskIter->GetParameters();
//Remove each queued animation relating to this virtual address.
auto queuedAnimParamIter = queuedAnimParams.m_animationParameters.begin();
while (queuedAnimParamIter != queuedAnimParams.m_animationParameters.end())
{
const AnimationParameterAddressData& queuedAddressData = queuedAnimParamIter->first;
if (addressData == queuedAddressData)
{
queuedAnimParamIter = queuedAnimParams.m_animationParameters.erase(queuedAnimParamIter);
}
else
{
++queuedAnimParamIter;
}
}
//If queued anim subtask no longer contains any parameters to update, remove it completely.
if (queuedSubtaskIter->GetParameters().m_animationParameters.size() == 0)
{
queuedSubtaskIter = m_queuedSubtasks.erase(queuedSubtaskIter);
}
else
{
++queuedSubtaskIter;
}
}
}
}
if (!InitializeSubtask(subtask->second, it, params))
{
m_subtasks.erase(subtask);
}
}
}
void ScriptedEntityTweenerTask::Update(float deltaTime)
{
auto queuedIter = m_queuedSubtasks.begin();
while (queuedIter != m_queuedSubtasks.end())
{
if (queuedIter->UpdateUntilReady(deltaTime))
{
AddAnimation(queuedIter->GetParameters(), false);
if (queuedIter->HasInitialValue())
{
auto& queuedAnimParams = queuedIter->GetParameters();
for (const auto& it : queuedAnimParams.m_animationParameters)
{
const AnimationParameterAddressData& addressData = it.first;
const AZStd::any& initialValue = queuedIter->GetInitialValue(addressData);
if (!initialValue.empty())
{
auto subtaskPair = m_subtasks.find(addressData);
if (subtaskPair != m_subtasks.end())
{
ScriptedEntityTweenerSubtask& subtask = subtaskPair->second;
subtask.SetInitialValue(queuedIter->GetAnimationId(), initialValue);
}
}
}
}
queuedIter = m_queuedSubtasks.erase(queuedIter);
}
else
{
++queuedIter;
}
}
m_callbacks.clear();
for (auto& subtaskPair : m_subtasks)
{
ScriptedEntityTweenerSubtask& subtask = subtaskPair.second;
if (subtask.IsActive())
{
subtask.Update(deltaTime, m_callbacks);
}
}
// Aggregate all callbacks from the subtasks to execute them all at once, as multiple subtasks may reference the same callback
ExecuteCallbacks(m_callbacks);
//TODO: Possible optimization: Logic to remove "stale" animation subtasks, use a "garbage collection" method to defer set removal operations (which is O(n))
for (auto it = m_subtasks.begin(); it != m_subtasks.end(); )
{
ScriptedEntityTweenerSubtask& subtask = it->second;
if (!subtask.IsActive())
{
it = m_subtasks.erase(it);
}
else
{
++it;
}
}
}
bool ScriptedEntityTweenerTask::GetIsActive()
{
for (auto& subtaskPair : m_subtasks)
{
ScriptedEntityTweenerSubtask& subtask = subtaskPair.second;
if (subtask.IsActive())
{
return true;
}
}
return m_queuedSubtasks.size() > 0;
}
void ScriptedEntityTweenerTask::Stop(int timelineId)
{
for (auto it = m_queuedSubtasks.begin(); it != m_queuedSubtasks.end(); )
{
auto& queuedSubtask = (*it);
if (timelineId == 0 || queuedSubtask.GetTimelineId() == timelineId)
{
ClearCallbacks(queuedSubtask.GetParameters().m_animationProperties);
it = m_queuedSubtasks.erase(it);
}
else
{
++it;
}
}
for (auto it = m_subtasks.begin(); it != m_subtasks.end(); )
{
ScriptedEntityTweenerSubtask& subtask = it->second;
if (timelineId == 0 || subtask.GetTimelineId() == timelineId)
{
ClearCallbacks(subtask.GetAnimationProperties());
it = m_subtasks.erase(it);
}
else
{
++it;
}
}
}
void ScriptedEntityTweenerTask::SetPaused(const AnimationParameterAddressData& addressData, int timelineId, bool isPaused)
{
auto subtaskPair = m_subtasks.find(addressData);
if (subtaskPair != m_subtasks.end())
{
ScriptedEntityTweenerSubtask& subtask = subtaskPair->second;
subtask.SetPaused(timelineId, isPaused);
}
if (IsTimelineIdValid(timelineId))
{
for (auto& queuedSubtask : m_queuedSubtasks)
{
if (queuedSubtask.GetTimelineId() == timelineId)
{
queuedSubtask.SetPaused(isPaused);
}
}
}
}
void ScriptedEntityTweenerTask::SetPlayDirectionReversed(const AnimationParameterAddressData& addressData, int timelineId, bool isPlayingBackward)
{
auto subtaskPair = m_subtasks.find(addressData);
if (subtaskPair != m_subtasks.end())
{
ScriptedEntityTweenerSubtask& subtask = subtaskPair->second;
subtask.SetPlayDirectionReversed(timelineId, isPlayingBackward);
}
//Remove any subtask queued for this timeline id, as now that we're rewinding, they should not play.
if (IsTimelineIdValid(timelineId))
{
auto queuedIter = m_queuedSubtasks.begin();
while (queuedIter != m_queuedSubtasks.end())
{
auto& queuedSubtask = (*queuedIter);
if (queuedSubtask.GetTimelineId() == timelineId)
{
queuedIter = m_queuedSubtasks.erase(queuedIter);
}
else
{
++queuedIter;
}
}
}
}
void ScriptedEntityTweenerTask::SetSpeed(const AnimationParameterAddressData& addressData, int timelineId, float speed)
{
auto subtaskPair = m_subtasks.find(addressData);
if (subtaskPair != m_subtasks.end())
{
ScriptedEntityTweenerSubtask& subtask = subtaskPair->second;
subtask.SetSpeed(timelineId, speed);
}
if (IsTimelineIdValid(timelineId))
{
for (auto& queuedSubtask : m_queuedSubtasks)
{
if (queuedSubtask.GetTimelineId() == timelineId)
{
queuedSubtask.SetPlaybackSpeed(speed);
}
}
}
}
void ScriptedEntityTweenerTask::SetInitialValue(const AnimationParameterAddressData& addressData, const AZ::Uuid& animationId, const AZStd::any& initialValue)
{
auto subtaskPair = m_subtasks.find(addressData);
if (subtaskPair != m_subtasks.end())
{
ScriptedEntityTweenerSubtask& subtask = subtaskPair->second;
subtask.SetInitialValue(animationId, initialValue);
}
if (!animationId.IsNull())
{
for (auto& queuedSubtask : m_queuedSubtasks)
{
if (queuedSubtask.GetAnimationId() == animationId)
{
queuedSubtask.SetInitialValue(addressData, initialValue);
}
}
}
}
void ScriptedEntityTweenerTask::GetVirtualPropertyValue(AZStd::any& returnVal, const AnimationParameterAddressData& addressData)
{
auto subtaskPair = m_subtasks.find(addressData);
if (subtaskPair != m_subtasks.end())
{
ScriptedEntityTweenerSubtask& subtask = subtaskPair->second;
subtask.GetVirtualPropertyValue(returnVal, addressData);
}
else
{
ScriptedEntityTweenerSubtask tempSubtask(m_entityId);
tempSubtask.GetVirtualPropertyValue(returnVal, addressData);
}
}
bool ScriptedEntityTweenerTask::InitializeSubtask(ScriptedEntityTweenerSubtask& subtask, const AZStd::pair<AnimationParameterAddressData, AZStd::any> initData, AnimationParameters params)
{
if (!subtask.Initialize(initData.first, initData.second, params.m_animationProperties))
{
AZStd::string entityName;
if (m_entityId.IsValid())
{
AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationRequests::GetEntityName, m_entityId);
}
else
{
entityName = "InvalidEntity";
}
//AZ_Warning("ScriptedEntityTweenerTask", false, "ScriptedEntityTweenerTask::AddAnimation - Error starting set animation entity name [%s], [%s, %s]", entityName.c_str(), initData.first.m_componentName.c_str(), initData.first.m_virtualPropertyName.c_str());
return false;
}
return true;
}
void ScriptedEntityTweenerTask::ExecuteCallbacks(const AZStd::set<CallbackData>& callbacks)
{
for (const auto& callback : callbacks)
{
switch (callback.m_callbackType)
{
case CallbackTypes::OnComplete:
ScriptedEntityTweenerNotificationsBus::Broadcast(&ScriptedEntityTweenerNotificationsBus::Events::OnComplete, callback.m_callbackId);
break;
case CallbackTypes::OnUpdate:
ScriptedEntityTweenerNotificationsBus::Broadcast(&ScriptedEntityTweenerNotificationsBus::Events::OnUpdate, callback.m_callbackId, callback.m_callbackData, callback.m_progressPercent);
break;
case CallbackTypes::OnLoop:
ScriptedEntityTweenerNotificationsBus::Broadcast(&ScriptedEntityTweenerNotificationsBus::Events::OnLoop, callback.m_callbackId);
break;
case CallbackTypes::RemoveCallback:
ScriptedEntityTweenerNotificationsBus::Broadcast(&ScriptedEntityTweenerNotificationsBus::Events::RemoveCallback, callback.m_callbackId);
break;
}
}
}
void ScriptedEntityTweenerTask::ClearCallbacks(const AnimationProperties& animationProperties)
{
AZStd::vector<int> callbackIds = { animationProperties.m_onCompleteCallbackId, animationProperties.m_onLoopCallbackId, animationProperties.m_onUpdateCallbackId };
for (const auto& callbackId : callbackIds)
{
if (callbackId != AnimationProperties::InvalidCallbackId)
{
ScriptedEntityTweenerNotificationsBus::Broadcast(&ScriptedEntityTweenerNotificationsBus::Events::RemoveCallback, callbackId);
}
}
}
}
@@ -0,0 +1,164 @@
/*
* 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 <AzCore/Component/EntityId.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/list.h>
#include <ScriptedEntityTweener/ScriptedEntityTweenerEnums.h>
#include "ScriptedEntityTweenerSubtask.h"
namespace ScriptedEntityTweener
{
//! One task per entity id, contains a collection of subtasks that are unique per virtual property.
class ScriptedEntityTweenerTask
{
public: // member functions
ScriptedEntityTweenerTask(AZ::EntityId id);
~ScriptedEntityTweenerTask();
void AddAnimation(const AnimationParameters& params, bool overwriteQueued = true);
void Update(float deltaTime);
bool GetIsActive();
void Stop(int timelineId);
void SetPaused(const AnimationParameterAddressData& addressData, int timelineId, bool isPaused);
void SetPlayDirectionReversed(const AnimationParameterAddressData& addressData, int timelineId, bool isPlayingBackward);
void SetSpeed(const AnimationParameterAddressData& addressData, int timelineId, float speed);
void SetInitialValue(const AnimationParameterAddressData& addressData, const AZ::Uuid& timelineId, const AZStd::any& initialValue);
void GetVirtualPropertyValue(AZStd::any& returnVal, const AnimationParameterAddressData& addressData);
bool operator<(const ScriptedEntityTweenerTask& other) const
{
return m_entityId < other.m_entityId;
}
bool operator>(const ScriptedEntityTweenerTask& other) const
{
return other.m_entityId < m_entityId;
}
bool operator==(const ScriptedEntityTweenerTask& other) const
{
return m_entityId == other.m_entityId;
}
private: // member functions
AZ::EntityId m_entityId;
//! Unique(per address data) active subtasks being updated
AZStd::unordered_map<AnimationParameterAddressData, ScriptedEntityTweenerSubtask> m_subtasks;
class QueuedSubtaskInfo
{
public:
QueuedSubtaskInfo(AnimationParameters params, float delayTime)
: m_params(params)
, m_currentDelayTime(delayTime)
, m_isPaused(false)
{
}
bool UpdateUntilReady(float deltaTime)
{
if (!m_isPaused)
{
m_currentDelayTime -= deltaTime * m_params.m_animationProperties.m_playbackSpeedMultiplier;
if (m_currentDelayTime <= .0f)
{
m_params.m_animationProperties.m_timeToDelayAnim = .0f;
return true;
}
}
return false;
}
int GetTimelineId()
{
return m_params.m_animationProperties.m_timelineId;
}
const AZ::Uuid& GetAnimationId()
{
return m_params.m_animationProperties.m_animationId;
}
void SetPlaybackSpeed(float speed)
{
m_params.m_animationProperties.m_playbackSpeedMultiplier = speed;
}
AnimationParameters& GetParameters()
{
return m_params;
}
void SetPaused(bool isPaused)
{
m_isPaused = isPaused;
}
void SetInitialValue(const AnimationParameterAddressData& addressData, const AZStd::any initialValue)
{
m_initialValues[addressData] = initialValue;
}
bool HasInitialValue()
{
return !m_initialValues.empty();
}
const AZStd::any& GetInitialValue(const AnimationParameterAddressData& addressData)
{
auto initialValueEntry = m_initialValues.find(addressData);
if (initialValueEntry != m_initialValues.end())
{
return initialValueEntry->second;
}
return m_emptyInitialValue;
}
private:
QueuedSubtaskInfo();
float m_currentDelayTime;
bool m_isPaused;
AZStd::unordered_map<AnimationParameterAddressData, AZStd::any> m_initialValues;
AnimationParameters m_params;
static const AZStd::any m_emptyInitialValue;
};
//! List of AnimationsParameters that need to be delayed before being added to m_subtasks, possibly overriding an animation.
AZStd::list<QueuedSubtaskInfo> m_queuedSubtasks;
AZStd::set<CallbackData> m_callbacks;
bool IsTimelineIdValid(int timelineId)
{
return timelineId != AnimationProperties::InvalidTimelineId;
}
bool InitializeSubtask(ScriptedEntityTweenerSubtask& subtask, const AZStd::pair<AnimationParameterAddressData, AZStd::any> initData, AnimationParameters params);
void ExecuteCallbacks(const AZStd::set<CallbackData>& callbacks);
void ClearCallbacks(const AnimationProperties& animationProperties);
};
}
@@ -0,0 +1,13 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 "ScriptedEntityTweener_precompiled.h"
@@ -0,0 +1,13 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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
@@ -0,0 +1,22 @@
#
# 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
Source/ScriptedEntityTweener_precompiled.cpp
Source/ScriptedEntityTweener_precompiled.h
Include/ScriptedEntityTweener/ScriptedEntityTweenerBus.h
Include/ScriptedEntityTweener/ScriptedEntityTweenerEnums.h
Source/ScriptedEntityTweenerMath.h
Source/ScriptedEntityTweenerTask.cpp
Source/ScriptedEntityTweenerTask.h
Source/ScriptedEntityTweenerSubtask.cpp
Source/ScriptedEntityTweenerSubtask.h
)
@@ -0,0 +1,16 @@
#
# 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
Source/ScriptedEntityTweenerSystemComponent.cpp
Source/ScriptedEntityTweenerSystemComponent.h
Source/ScriptedEntityTweenerModule.cpp
)