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,749 @@
----------------------------------------------------------------------------------------------------
--
-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
-- its licensors.
--
-- For complete copyright and license terms please see the LICENSE at the root of this
-- distribution (the "License"). All use of this software is governed by the License,
-- or, if provided, by the license below or the license accompanying this file. Do not
-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--
--
----------------------------------------------------------------------------------------------------
if g_timelineCounter == nil then
g_timelineCounter = 0
g_animationCallbackCounter = 0
g_animationCallbacks = {}
end
local ScriptedEntityTweener = {}
function ScriptedEntityTweener:OnActivate()
if self.tweenerNotificationHandler == nil then
self.animationParameterShortcuts =
{
--UI Related
["opacity"] = {"UiFaderComponent", "Fade" },
["imgColor"] = {"UiImageComponent", "Color" },
["imgAlpha"] = {"UiImageComponent", "Alpha" },
["imgFill"] = {"UiImageComponent", "FillAmount" },
["imgFillAngle"] = {"UiImageComponent", "RadialFillStartAngle" },
["layoutMinWidth"] = {"UiLayoutCellComponent", "MinWidth" },
["layoutMinHeight"] = {"UiLayoutCellComponent", "MinHeight" },
["layoutTargetWidth"] = {"UiLayoutCellComponent", "TargetWidth" },
["layoutTargetHeight"] = {"UiLayoutCellComponent", "TargetHeight" },
["layoutExtraWidthRatio"] = {"UiLayoutCellComponent", "ExtraWidthRatio" },
["layoutExtraHeightRatio"] = {"UiLayoutCellComponent", "ExtraHeightRatio" },
["layoutColumnPadding"] = {"UiLayoutColumnComponent", "Padding" },
["layoutColumnSpacing"] = {"UiLayoutColumnComponent", "Spacing" },
["layoutRowPadding"] = {"UiLayoutRowComponent", "Padding" },
["layoutRowSpacing"] = {"UiLayoutRowComponent", "Spacing" },
["scrollHandleSize"] = {"UiScrollBarComponent", "HandleSize" },
["scrollHandleMinPixelSize"] = {"UiScrollBarComponent", "MinHandlePixelSize" },
["scrollValue"] = {"UiScrollBarComponent", "Value" },
["sliderValue"] = {"UiSliderComponent", "Value" },
["sliderMinValue"] = {"UiSliderComponent", "MinValue" },
["sliderMaxValue"] = {"UiSliderComponent", "MaxValue" },
["sliderStepValue"] = {"UiSliderComponent", "StepValue" },
["textSize"] = {"UiTextComponent", "FontSize" },
["textColor"] = {"UiTextComponent", "Color" },
["textCharacterSpace"] = {"UiTextComponent", "CharacterSpacing" },
["textSpacing"] = {"UiTextComponent", "LineSpacing" },
["textInputSelectionColor"] = {"UiTextInputComponent", "TextSelectionColor" },
["textInputCursorColor"] = {"UiTextInputComponent", "TextCursorColor" },
["textInputCursorBlinkInterval"] = {"UiTextInputComponent", "CursorBlinkInterval" },
["textInputMaxStringLength"] = {"UiTextInputComponent", "MaxStringLength" },
["tooltipDelayTime"] = {"UiTooltipDisplayComponent", "DelayTime" },
["tooltipDisplayTime"] = {"UiTooltipDisplayComponent", "DisplayTime" },
["scaleX"] = {"UiTransform2dComponent", "ScaleX" },
["scaleY"] = {"UiTransform2dComponent", "ScaleY" },
["pivotX"] = {"UiTransform2dComponent", "PivotX" },
["pivotY"] = {"UiTransform2dComponent", "PivotY" },
["x"] = {"UiTransform2dComponent", "LocalPositionX" },
["y"] = {"UiTransform2dComponent", "LocalPositionY" },
["rotation"] = {"UiTransform2dComponent", "Rotation" },
["w"] = {"UiTransform2dComponent", "LocalWidth" },
["h"] = {"UiTransform2dComponent", "LocalHeight" },
--3d transform
["3dposition"] = {"TransformComponent", "Position" },
["3drotation"] = {"TransformComponent", "Rotation" },
["3dscale"] = {"TransformComponent", "Scale" },
--Camera
["camFov"] = {"CameraComponent", "FieldOfView" },
["camNear"] = {"CameraComponent", "NearClipDistance" },
["camFar"] = {"CameraComponent", "FarClipDistance" },
--[[
--Some available virtual properties without shortcuts
--Lights
[""] = {"LightComponent", "Visible" },
[""] = {"LightComponent", "Color" },
[""] = {"LightComponent", "DiffuseMultiplier" },
[""] = {"LightComponent", "SpecularMultiplier" },
[""] = {"LightComponent", "Ambient" },
[""] = {"LightComponent", "PointMaxDistance" },
[""] = {"LightComponent", "PointAttenuationBulbSize" },
[""] = {"LightComponent", "AreaMaxDistance" },
[""] = {"LightComponent", "AreaWidth" },
[""] = {"LightComponent", "AreaHeight" },
[""] = {"LightComponent", "AreaFOV" },
[""] = {"LightComponent", "ProjectorMaxDistance" },
[""] = {"LightComponent", "ProjectorAttenuationBulbSize" },
[""] = {"LightComponent", "ProjectorFOV" },
[""] = {"LightComponent", "ProjectorNearPlane" },
[""] = {"LightComponent", "ProbeAreaDimensions" },
[""] = {"LightComponent", "ProbeSortPriority" },
[""] = {"LightComponent", "ProbeBoxProjected" },
[""] = {"LightComponent", "ProbeBoxHeight" },
[""] = {"LightComponent", "ProbeBoxLength" },
[""] = {"LightComponent", "ProbeBoxWidth" },
[""] = {"LightComponent", "ProbeAttenuationFalloff" },
--Particles
[""] = {"ParticleComponent", "Visible" },
[""] = {"ParticleComponent", "Enable" },
[""] = {"ParticleComponent", "ColorTint" },
[""] = {"ParticleComponent", "CountScale" },
[""] = {"ParticleComponent", "TimeScale" },
[""] = {"ParticleComponent", "SpeedScale" },
[""] = {"ParticleComponent", "GlobalSizeScale" },
[""] = {"ParticleComponent", "ParticleSizeScaleX" },
[""] = {"ParticleComponent", "ParticleSizeScaleY" },
--Static mesh
["meshVisibility"] = {"StaticMeshComponent", "Visibility" },
--]]
--Add any more custom shortcuts here, [string] = { ComponentName, VirtualProperty }
}
self.animationEaseMethodShortcuts =
{
["Linear"] = ScriptedEntityTweenerEasingMethod_Linear,
["Quad"] = ScriptedEntityTweenerEasingMethod_Quad,
["Cubic"] = ScriptedEntityTweenerEasingMethod_Cubic,
["Quart"] = ScriptedEntityTweenerEasingMethod_Quart,
["Quint"] = ScriptedEntityTweenerEasingMethod_Quint,
["Sine"] = ScriptedEntityTweenerEasingMethod_Sine,
["Expo"] = ScriptedEntityTweenerEasingMethod_Expo,
["Circ"] = ScriptedEntityTweenerEasingMethod_Circ,
["Elastic"] = ScriptedEntityTweenerEasingMethod_Elastic,
["Back"] = ScriptedEntityTweenerEasingMethod_Back,
["Bounce"] = ScriptedEntityTweenerEasingMethod_Bounce,
}
self.animationEaseTypeShortcuts =
{
[1] = {"InOut", ScriptedEntityTweenerEasingType_InOut},
[2] = {"In", ScriptedEntityTweenerEasingType_In},
[3] = {"Out", ScriptedEntityTweenerEasingType_Out},
}
self.tweenerNotificationHandler = ScriptedEntityTweenerNotificationBus.Connect(self)
self.tickBusHandler = TickBus.Connect(self)
self.activationCount = 0
self.timelineRefs = {}
end
self.activationCount = self.activationCount + 1
end
function ScriptedEntityTweener:OnDeactivate()
if self.tweenerNotificationHandler then
self.activationCount = self.activationCount - 1
if self.activationCount == 0 then
self.tweenerNotificationHandler:Disconnect()
self.tweenerNotificationHandler = nil
self.tickBusHandler:Disconnect()
self.tickBusHandler = nil
end
end
end
function ScriptedEntityTweener:Stop(id)
ScriptedEntityTweenerBus.Broadcast.Stop(0, id)
end
function ScriptedEntityTweener:Set(id, args)
self:StartAnimation(id, 0, args)
end
function ScriptedEntityTweener:Play(id, duration, args, toArgs)
self:StartAnimation(id, duration, args, toArgs)
end
function ScriptedEntityTweener:StartAnimation(id, duration, args, toArgs)
if self.animationParameterShortcuts == nil then
Debug.Log("ScriptedEntityTweener: Make sure to call OnActivate() and OnDeactivate() for this table when requiring this file")
return
end
if type(id) == "table" and duration == nil and args == nil and toArgs == nil then
--Legacy support for old API, where it was just one table passed in containing args and duration
args = id
else
args.id = id
args.duration = duration
end
if self:ValidateAnimationParameters(args) == false then
return
end
if toArgs then
--Optional toArgs, if specified, will make args behave as a Set call, before starting the animation specified in toArgs
self:Set(id, args)
self:Play(id, duration, toArgs)
return
end
local easeMethod = args.easeMethod
local easeType = args.easeType
if args.ease then
--Parse shortcut ease parameter
if args.easeMethod then
Debug.Log("ScriptedEntityTweener: Warning, easeMethod will be overriden by ease parameter")
end
if args.easeType then
Debug.Log("ScriptedEntityTweener: Warning, easeType will be overriden by ease parameter")
end
for easeName, easeValue in pairs(self.animationEaseMethodShortcuts) do
if string.match(args.ease, easeName) then
easeMethod = easeValue
break
end
end
for i,easeTypeData in ipairs(self.animationEaseTypeShortcuts) do
local easeStr = easeTypeData[1]
--if ease contains this easeStr, use this type
if string.match(args.ease, easeStr) then
easeType = easeTypeData[2]
break
end
end
end
local optionalParams =
{
timeIntoTween = args.timeIntoTween or 0, --Example: If timeIntoTween = 0.5 and duration = 1, the animation will instantly set the animation to 50% complete.
duration = args.duration or 0,
easeMethod = easeMethod or ScriptedEntityTweenerEasingMethod_Linear,
easeType = easeType or ScriptedEntityTweenerEasingType_Out,
delay = args.delay or 0.0,
timesToPlay = args.timesToPlay or 1,
isFrom = args.isFrom,
isPlayingBackward = args.isPlayingBackward,
uuid = args.uuid or Uuid.CreateNull()
}
self:ConvertShortcutsToVirtualProperties(args)
local virtualPropertiesToUse = args.virtualProperties
if args.timelineParams ~= nil then
optionalParams.delay = optionalParams.delay + args.timelineParams.initialStartTime
optionalParams.timeIntoTween = optionalParams.timeIntoTween + args.timelineParams.timeIntoTween
optionalParams.timelineId = args.timelineParams.timelineId
optionalParams.uuid = args.timelineParams.uuidOverride
if args.timelineParams.durationOverride ~= nil then
optionalParams.duration = args.timelineParams.durationOverride
end
if args.timelineParams.seekDelayOverride ~= nil then
optionalParams.delay = args.timelineParams.seekDelayOverride
end
if args.timelineParams.reversePlaybackOverride then
if optionalParams.isPlayingBackward == nil then
optionalParams.isPlayingBackward = false
end
optionalParams.isPlayingBackward = not optionalParams.isPlayingBackward
end
end
--Create animation callbacks with an id, id sent to C so C can notify this system to trigger the callback.
if args.onComplete ~= nil then
optionalParams.onComplete = self:CreateCallback(args.onComplete)
end
if args.onUpdate ~= nil then
optionalParams.onUpdate = self:CreateCallback(args.onUpdate)
end
if args.onLoop ~= nil then
optionalParams.onLoop = self:CreateCallback(args.onLoop)
end
for componentData, paramTarget in pairs(virtualPropertiesToUse) do
ScriptedEntityTweenerBus.Broadcast.SetOptionalParams(
optionalParams.timeIntoTween,
optionalParams.duration,
optionalParams.easeMethod,
optionalParams.easeType,
optionalParams.delay,
optionalParams.timesToPlay,
optionalParams.isFrom == true,
optionalParams.isPlayingBackward == true,
optionalParams.uuid,
optionalParams.timelineId or 0,
optionalParams.onComplete or 0,
optionalParams.onUpdate or 0,
optionalParams.onLoop or 0)
ScriptedEntityTweenerBus.Broadcast.AnimateEntity(
args.id,
componentData[1],
componentData[2],
paramTarget)
end
end
function ScriptedEntityTweener:ValidateAnimationParameters(args)
--check for required params
if args == nil then
Debug.Log("ScriptedEntityTweener: animation with invalid args, args == nil")
return false
end
if args.id == nil then
Debug.Log("ScriptedEntityTweener: animation with no id specified " .. args)
return false
end
if not args.id:IsValid() then
--Fail silently
return
end
return true
end
function ScriptedEntityTweener:ConvertShortcutsToVirtualProperties(args)
for shortcutName, componentData in pairs(self.animationParameterShortcuts) do
local paramTarget = args[shortcutName]
if paramTarget ~= nil then
if args.virtualProperties == nil then
args.virtualProperties = {}
end
local virtualPropertyKey = {componentData[1], componentData[2]}
args.virtualProperties[virtualPropertyKey] = paramTarget
args[shortcutName] = nil
end
end
end
--Callback related
function ScriptedEntityTweener:CreateCallback(fnCallback)
--todo: better uuid generation
g_animationCallbackCounter = g_animationCallbackCounter + 1
g_animationCallbacks[g_animationCallbackCounter] = fnCallback
return g_animationCallbackCounter
end
function ScriptedEntityTweener:RemoveCallback(callbackId)
g_animationCallbacks[callbackId] = nil
end
function ScriptedEntityTweener:CallCallback(callbackId)
local callbackFn = g_animationCallbacks[callbackId]
if callbackFn ~= nil then
callbackFn()
end
end
function ScriptedEntityTweener:OnComplete(callbackId)
self:CallCallback(callbackId)
self:RemoveCallback(callbackId)
end
function ScriptedEntityTweener:OnUpdate(callbackId, currentValue, progressPercent)
local callbackFn = g_animationCallbacks[callbackId]
if callbackFn ~= nil then
callbackFn(currentValue, progressPercent)
end
end
function ScriptedEntityTweener:OnLoop(callbackId)
self:CallCallback(callbackId)
end
--Timeline related
function ScriptedEntityTweener:OnTick(deltaTime, timePoint)
for timelineId, timeline in pairs(self.timelineRefs) do
if (timeline.isPaused == false and timeline.playingSpeed ~= 0) then
timeline.currentSeekTime = timeline.currentSeekTime + deltaTime * timeline.playingSpeed
if (timeline.playingSpeed > 0 and timeline.currentSeekTime >= timeline.duration) then
timeline.currentSeekTime = timeline.duration
timeline.playingSpeed = 0
elseif (timeline.playingSpeed < 0 and timeline.currentSeekTime <= 0) then
timeline.currentSeekTime = 0
timeline.playingSpeed = 0
end
end
end
end
function ScriptedEntityTweener:GetUniqueTimelineId()
g_timelineCounter = g_timelineCounter + 1
return g_timelineCounter
end
function ScriptedEntityTweener:TimelineCreate()
local timeline = {}
timeline.animations = {}
timeline.labels = {}
timeline.duration = 0
timeline.isPaused = false
timeline.timelineId = self:GetUniqueTimelineId()
timeline.currentSeekTime = 0
timeline.playingSpeed = 0 -- 0 when not playing, >0 when playing forward, <0 when playing backwards
--Gets the duration of a specific animation
timeline.GetDurationOfAnim = function(timelineSelf, animParams)
return ((animParams.duration or 0) - (animParams.timeIntoTween or 0)) + (animParams.delay or 0)
end
--Timeline configuration functions
timeline.Add = function(timelineSelf, id, duration, animParams, timelineParams)
--Timeline is a collection of animations
--Operations like play, pause, reverse, etc
-- Any animation is automatically added to the end of the animation.
-- Optional parameters:
-- timelineParams.offset will add it to the end +time specified, negative offsets allowed.
-- timelineParams.initialStartTime will set the timeline to start playing at a certain time within the timeline
-- timelineParams.label will specify the timelime to start playing at the label.
if timelineSelf == nil then
Debug.Log("ScriptedEntityTweener:TimelineAdd no timeline")
return
end
if type(id) == "table" and (duration == nil or type(duration) == "table") and animParams == nil and timelineParams == nil then
--Legacy support for old API, where the animParams table already contained id and duration
animParams = id
timelineParams = duration
else
animParams.id = id
animParams.duration = duration
end
if self:ValidateAnimationParameters(animParams) == false then
Debug.Log("ScriptedEntityTweener:TimelineAdd invalid animation parameters for timline uuid " .. animParams)
return
end
local optionalParams =
{
offset = 0,
}
if timelineParams ~= nil then
if timelineParams.label then
optionalParams.initialStartTime = timelineSelf.labels[timelineParams.label]
else
optionalParams.initialStartTime = timelineParams.initialStartTime
end
optionalParams.offset = timelineParams.offset or 0
end
animParams.timelineParams = {}
--initialStartTime is either specified from user as a number or a label, otherwise append animation to end of timeline
animParams.timelineParams.initialStartTime = optionalParams.initialStartTime or timelineSelf.duration
--user can also offset the time the animation is appended to.
animParams.timelineParams.initialStartTime = animParams.timelineParams.initialStartTime + optionalParams.offset
--additional time into tween specified from timeline, to allow timeline to seek into the middle of an animation
animParams.timelineParams.timeIntoTween = 0
--additional delay and duration overrides to allow seeking
animParams.timelineParams.seekDelayOverride = nil
animParams.timelineParams.durationOverride = nil
animParams.timelineParams.timelineId = timelineSelf.timelineId
animParams.timelineParams.uuidOverride = Uuid.Create()
self:ConvertShortcutsToVirtualProperties(animParams)
animParams.timelineParams.initialValues = {}
for componentData, paramTarget in pairs(animParams.virtualProperties) do
--Cache initial values for backwards playback purposes.
animParams.timelineParams.initialValues[componentData] = ScriptedEntityTweenerBus.Broadcast.GetVirtualPropertyValue(animParams.id, componentData[1], componentData[2])
end
timelineSelf.animations[#timelineSelf.animations + 1] = animParams
local animEndTime = animParams.timelineParams.initialStartTime + timelineSelf:GetDurationOfAnim(animParams)
if animEndTime > timelineSelf.duration then
timelineSelf.duration = animEndTime
end
table.sort(timelineSelf.animations,
function(first, second)
return first.timelineParams.initialStartTime < second.timelineParams.initialStartTime
end
)
end
timeline.AddLabel = function(timelineSelf, labelId, labelTime)
if labelId == nil then
Debug.Log("Warning: TimelineLabel: labelId is nil")
return
end
if labelTime == nil then
Debug.Log("TimelineLabel: label " .. labelId .. " doesn't have a labelTime")
return
end
if timelineSelf.labels[labelId] ~= nil then
Debug.Log("Warning: TimelineLabel: label " .. labelId .. " already exists")
end
timelineSelf.labels[labelId] = labelTime
end
--Timeline control functions
timeline.Play = function(timelineSelf, labelOrTime)
timelineSelf:Resume()
local startTime = 0
if labelOrTime ~= nil then
local typeInfo = type(labelOrTime)
if typeInfo == "string" then
startTime = timelineSelf.labels[labelOrTime] or 0
elseif typeInfo == "number" then
startTime = labelOrTime
end
end
timelineSelf:Seek(startTime)
end
timeline.Stop = function(timelineSelf)
timelineSelf.playingSpeed = 0
timelineSelf.isPaused = false
for i=1, #timelineSelf.animations do
local animParams = timelineSelf.animations[i]
ScriptedEntityTweenerBus.Broadcast.Stop(timelineSelf.timelineId, animParams.id)
end
end
timeline.Pause = function(timelineSelf)
if timelineSelf.isPaused == false then
timelineSelf.isPaused = true
for i=1, #timelineSelf.animations do
local animParams = timelineSelf.animations[i]
for componentData, paramTarget in pairs(animParams.virtualProperties) do
ScriptedEntityTweenerBus.Broadcast.Pause(timelineSelf.timelineId, animParams.id, componentData[1], componentData[2])
end
end
end
end
timeline.Resume = function(timelineSelf)
if timelineSelf.isPaused == true then
timelineSelf.isPaused = false
for i=1, #timelineSelf.animations do
local animParams = timelineSelf.animations[i]
for componentData, paramTarget in pairs(animParams.virtualProperties) do
ScriptedEntityTweenerBus.Broadcast.Resume(timelineSelf.timelineId, animParams.id, componentData[1], componentData[2])
end
end
end
end
timeline.ResetRuntimeVars = function(timelineSelf, animParams)
--Reset all timeline params that might have been set by a previous Seek or Reverse call
animParams.timelineParams.timeIntoTween = 0
animParams.timelineParams.reversePlaybackOverride = nil
animParams.timelineParams.seekDelayOverride = nil
animParams.timelineParams.durationOverride = nil
end
timeline.Seek = function(timelineSelf, seekTime)
local typeInfo = type(seekTime)
if typeInfo == "string" then
seekTime = timelineSelf.labels[seekTime] or 0
end
if seekTime < 0 then
seekTime = 0
elseif seekTime > timelineSelf.duration then
seekTime = timelineSelf.duration
end
timelineSelf:Stop()
timelineSelf.isPaused = false
timelineSelf.playingSpeed = 1
--Go through each animation in the timeline and configure them to play as appropriate
timelineSelf.currentSeekTime = seekTime
local runningDuration = 0
for i=1, #timelineSelf.animations do
local animParams = timelineSelf.animations[i]
local prevCompletionState = runningDuration < seekTime
local animEndTime = animParams.timelineParams.initialStartTime + timelineSelf:GetDurationOfAnim(animParams)
if animEndTime > runningDuration then
runningDuration = animEndTime
end
local currentCompletionState = runningDuration < seekTime
timelineSelf:ResetRuntimeVars(animParams)
if runningDuration <= seekTime then
--Current animation already complete
animParams.timelineParams.seekDelayOverride = 0
animParams.timelineParams.durationOverride = 0
elseif prevCompletionState ~= currentCompletionState then
--Current animation is partially complete
local diff = runningDuration - seekTime
animParams.timelineParams.timeIntoTween = timelineSelf:GetDurationOfAnim(animParams) - diff
animParams.timelineParams.seekDelayOverride = 0
elseif runningDuration > seekTime then
--Current animation still needs to be played
animParams.timelineParams.seekDelayOverride = (animParams.delay or 0) + (animParams.timelineParams.initialStartTime - seekTime)
end
end
for i=1, #timelineSelf.animations do
local animParams = timelineSelf.animations[i]
self:StartAnimation(animParams)
end
end
timeline.PlayBackwards = function(timelineSelf, specificSeekTime)
local seekTime = specificSeekTime
if seekTime == nil then
seekTime = timelineSelf.currentSeekTime
end
if seekTime < 0 then
seekTime = 0
elseif seekTime > timelineSelf.duration then
seekTime = timelineSelf.duration
end
-- Check if timeline needs to seek first
local seekNeeded = false
if seekTime > timelineSelf.currentSeekTime then
-- Only animations that have already played or are currently playing can be played backwards
seekNeeded = true
elseif (seekTime > 0 and seekTime < timelineSelf.duration) then
if (timelineSelf.playingSpeed == 0 or seekTime ~= timelineSelf.currentSeekTime) then
-- In order for playback to work correctly, an animation that is partially completed needs to
-- already be added to the tweener, and that animation needs to start playing from the same time
-- into the animation
seekNeeded = true
end
end
if seekNeeded == true then
timelineSelf:Seek(seekTime)
else
timelineSelf.currentSeekTime = seekTime
end
if timelineSelf.isPaused then
timelineSelf:Resume()
end
timelineSelf.playingSpeed = -1
local animsToPlay = {}
local runningDuration = 0
for i=1, #timelineSelf.animations do
local animParams = timelineSelf.animations[i]
local animEndTime = animParams.timelineParams.initialStartTime + timelineSelf:GetDurationOfAnim(animParams)
if animEndTime > runningDuration then
runningDuration = animEndTime
end
timelineSelf:ResetRuntimeVars(animParams)
if runningDuration <= seekTime then
--Current animation has already played, queue it to play again backwards
animParams.timelineParams.timeIntoTween = timelineSelf:GetDurationOfAnim(animParams)
animParams.timelineParams.seekDelayOverride = seekTime - runningDuration
animParams.timelineParams.reversePlaybackOverride = true
animsToPlay[#animsToPlay + 1] = animParams
else
--Current animation is either partially complete or hasn't been played
for componentData, paramTarget in pairs(animParams.virtualProperties) do
ScriptedEntityTweenerBus.Broadcast.SetPlayDirectionReversed(timelineSelf.timelineId, animParams.id, componentData[1], componentData[2], true)
end
end
end
table.sort(animsToPlay,
function(first, second)
return first.timelineParams.initialStartTime < second.timelineParams.initialStartTime
end
)
for i=1,#animsToPlay do
local animParams = animsToPlay[i]
--Copy last initial values as start animation will overwrite with current initial values
local initialValues = {}
for componentData, paramInitial in pairs(animParams.timelineParams.initialValues) do
initialValues[componentData] = paramInitial
end
self:StartAnimation(animParams)
--Set the newly started animation's initial start position from cached start position.
for componentData, paramInitial in pairs(initialValues) do
ScriptedEntityTweenerBus.Broadcast.SetInitialValue(animParams.timelineParams.uuidOverride, animParams.id, componentData[1], componentData[2], paramInitial)
end
end
end
timeline.SetSpeed = function(timelineSelf, multiplier)
if multiplier > 0 then
if timelineSelf.playingSpeed > 0 then
timelineSelf.playingSpeed = multiplier
elseif timelineSelf.playingSpeed < 0 then
timelineSelf.playingSpeed = -multiplier
end
for i=1, #timelineSelf.animations do
local animParams = timelineSelf.animations[i]
for componentData, paramTarget in pairs(animParams.virtualProperties) do
ScriptedEntityTweenerBus.Broadcast.SetSpeed(timelineSelf.timelineId, animParams.id, componentData[1], componentData[2], multiplier)
end
end
else
Debug.Log("ScriptedEntityTweener: Warning, SetSpeed multiplier must be greater than 0")
end
end
--SetCompletion - Seek to animation percentage rather than specific time or label
timeline.SetCompletion = function(timelineSelf, percentage)
timelineSelf:Seek(timelineSelf.duration * percentage)
end
timeline.GetCurrentSeekTime = function(timelineSelf)
return timelineSelf.currentSeekTime
end
timeline.GetDuration = function(timelineSelf)
return timelineSelf.duration
end
self.timelineRefs[timeline.timelineId] = timeline
return timeline
end
function ScriptedEntityTweener:TimelineDestroy(timeline)
--If a timeline is created, it should eventually be destroyed, otherwise they'll never be removed from self.timelineRef's table and garbage collected.
self.timelineRefs[timeline.timelineId] = nil
end
function ScriptedEntityTweener:OnTimelineAnimationStart(timelineId, animUuid, addressComponentName, addressPropertyName)
--Update cached initial value whenever a timeline animation starts, this data is needed if the user ever wants to play the animation backwards
local timeline = self.timelineRefs[timelineId]
if timeline == nil then
return
end
for i=1,#timeline.animations do
local animParams = timeline.animations[i]
if animParams.timelineParams.uuidOverride == animUuid then
for componentData, paramTarget in pairs(animParams.virtualProperties) do
if componentData[1] == addressComponentName and componentData[2] == addressPropertyName then
if animParams.duration == 0 or animParams.isFrom == true then
animParams.timelineParams.initialValues[componentData] = paramTarget
else
animParams.timelineParams.initialValues[componentData] = ScriptedEntityTweenerBus.Broadcast.GetVirtualPropertyValue(animParams.id, componentData[1], componentData[2])
end
break
end
end
end
end
end
return ScriptedEntityTweener
+12
View File
@@ -0,0 +1,12 @@
#
# 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.
#
add_subdirectory(Code)
@@ -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
)
+11
View File
@@ -0,0 +1,11 @@
{
"GemFormatVersion": 3,
"Uuid": "0d1f5f05559c4a99aaefd30633a0158e",
"Name": "ScriptedEntityTweener",
"DisplayName": "Scripted Entity Tweener",
"Version": "0.1.0",
"LinkType": "Dynamic",
"Summary": "Entity Animator can be used to transform entity properties in Lumberyard",
"Tags": ["UI"],
"IconPath": "preview.png"
}
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6d6204c6730e5675791765ca194e9b1cbec282208e280507de830afc2805e5fa
size 41127