Add animation time control for sprites and 3D models (#5777)
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animatable capability"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animations and images"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
|
||||
@@ -53,6 +55,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Animation index")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllExpressions()["Index"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"string",
|
||||
@@ -69,6 +72,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"objectAnimationName", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Animation name")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllStrExpressions()["Name"].SetGroup("");
|
||||
|
||||
aut.AddScopedAction("PauseAnimation",
|
||||
_("Pause the animation"),
|
||||
@@ -107,6 +111,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Speed scale")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllExpressions()["SpeedScale"].SetGroup("");
|
||||
|
||||
aut.AddScopedCondition("IsAnimationPaused",
|
||||
_("Animation paused"),
|
||||
@@ -130,6 +135,31 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior")
|
||||
.MarkAsSimple();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
"ElapsedTime",
|
||||
_("Animation elapsed time"),
|
||||
_("the elapsed time from the beginning of the animation (in seconds)"),
|
||||
_("the animation elapsed time"),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior")
|
||||
.UseStandardParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Elapsed time (in seconds)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["ElapsedTime"].SetGroup("");
|
||||
|
||||
aut.AddExpression(
|
||||
"Duration",
|
||||
_("Animation duration"),
|
||||
_("Return the current animation duration (in seconds)."),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Opacity capability"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Visibility"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
|
||||
@@ -54,6 +56,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
_("Opacity (0-255)")))
|
||||
.SetFunctionName("setOpacity")
|
||||
.SetGetter("getOpacity");
|
||||
aut.GetAllExpressions()["Value"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable capability"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
|
||||
@@ -53,6 +55,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["Value"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
@@ -69,6 +72,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["X"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
@@ -85,6 +89,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["Y"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -52,6 +52,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTextContainerExtension(
|
||||
"string", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Text")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllStrExpressions()["Value"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -221,6 +221,9 @@ namespace gdjs {
|
||||
const animation = this._animations[animationIndex];
|
||||
this._currentAnimationIndex = animationIndex;
|
||||
this._renderer.playAnimation(animation.source, animation.loop);
|
||||
if (this._animationPaused) {
|
||||
this._renderer.pauseAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,12 +275,12 @@ namespace gdjs {
|
||||
|
||||
pauseAnimation() {
|
||||
this._animationPaused = true;
|
||||
return this._renderer.pauseAnimation();
|
||||
this._renderer.pauseAnimation();
|
||||
}
|
||||
|
||||
resumeAnimation() {
|
||||
this._animationPaused = false;
|
||||
return this._renderer.resumeAnimation();
|
||||
this._renderer.resumeAnimation();
|
||||
}
|
||||
|
||||
getAnimationSpeedScale() {
|
||||
@@ -288,6 +291,20 @@ namespace gdjs {
|
||||
this._animationSpeedScale = ratio;
|
||||
}
|
||||
|
||||
getAnimationElapsedTime(): float {
|
||||
return this._renderer.getAnimationElapsedTime();
|
||||
}
|
||||
|
||||
setAnimationElapsedTime(time: float): void {
|
||||
this._renderer.setAnimationElapsedTime(time);
|
||||
}
|
||||
|
||||
getAnimationDuration(): float {
|
||||
return this._renderer.getAnimationDuration(
|
||||
this._animations[this._currentAnimationIndex].source
|
||||
);
|
||||
}
|
||||
|
||||
getCenterX(): float {
|
||||
const centerPoint = this._renderer.getCenterPoint();
|
||||
return this.getWidth() * centerPoint[0];
|
||||
|
||||
@@ -348,6 +348,24 @@ namespace gdjs {
|
||||
// Make sure the first frame is displayed.
|
||||
this._animationMixer.update(0);
|
||||
}
|
||||
|
||||
getAnimationElapsedTime(): float {
|
||||
return this._action ? this._action.time : 0;
|
||||
}
|
||||
|
||||
setAnimationElapsedTime(time: float): void {
|
||||
if (this._action) {
|
||||
this._action.time = time;
|
||||
}
|
||||
}
|
||||
|
||||
getAnimationDuration(animationName: string): float {
|
||||
const clip = THREE.AnimationClip.findByName(
|
||||
this._originalModel.animations,
|
||||
animationName
|
||||
);
|
||||
return clip ? clip.duration : 0;
|
||||
}
|
||||
}
|
||||
|
||||
export const Model3DRuntimeObjectRenderer = Model3DRuntimeObject3DRenderer;
|
||||
|
||||
@@ -67,6 +67,20 @@ AnimatableExtension::AnimatableExtension() {
|
||||
conditions["AnimatableCapability::AnimatableBehavior::HasAnimationEnded"]
|
||||
.SetFunctionName("hasAnimationEnded")
|
||||
.SetIncludeFile("object-capabilities/AnimatableBehavior.js");
|
||||
|
||||
actions["AnimatableCapability::AnimatableBehavior::SetElapsedTime"]
|
||||
.SetFunctionName("setAnimationElapsedTime")
|
||||
.SetGetter("getAnimationElapsedTime")
|
||||
.SetIncludeFile("object-capabilities/AnimatableBehavior.js");
|
||||
conditions["AnimatableCapability::AnimatableBehavior::ElapsedTime"]
|
||||
.SetFunctionName("getAnimationElapsedTime")
|
||||
.SetIncludeFile("object-capabilities/AnimatableBehavior.js");
|
||||
expressions["ElapsedTime"]
|
||||
.SetFunctionName("getAnimationElapsedTime")
|
||||
.SetIncludeFile("object-capabilities/AnimatableBehavior.js");
|
||||
expressions["Duration"]
|
||||
.SetFunctionName("getAnimationDuration")
|
||||
.SetIncludeFile("object-capabilities/AnimatableBehavior.js");
|
||||
}
|
||||
|
||||
} // namespace gdjs
|
||||
|
||||
@@ -49,6 +49,22 @@ namespace gdjs {
|
||||
getAnimationSpeedScale(): float;
|
||||
|
||||
setAnimationSpeedScale(ratio: float): void;
|
||||
|
||||
/**
|
||||
* @return The elapsed time from the start of the animation in seconds.
|
||||
*/
|
||||
getAnimationElapsedTime(): float;
|
||||
|
||||
/**
|
||||
* Set the elapsed time from the start of the animation in seconds.
|
||||
* @param time The time in seconds
|
||||
*/
|
||||
setAnimationElapsedTime(time: float): void;
|
||||
|
||||
/**
|
||||
* @return The duration of the current animation in seconds.
|
||||
*/
|
||||
getAnimationDuration(): float;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,6 +144,18 @@ namespace gdjs {
|
||||
setAnimationSpeedScale(ratio: float): void {
|
||||
this.object.setAnimationSpeedScale(ratio);
|
||||
}
|
||||
|
||||
getAnimationElapsedTime(): float {
|
||||
return this.object.getAnimationElapsedTime();
|
||||
}
|
||||
|
||||
setAnimationElapsedTime(time: float): void {
|
||||
this.object.setAnimationElapsedTime(time);
|
||||
}
|
||||
|
||||
getAnimationDuration(): float {
|
||||
return this.object.getAnimationDuration();
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.registerBehavior(
|
||||
|
||||
@@ -475,40 +475,29 @@ namespace gdjs {
|
||||
const direction = this._animations[this._currentAnimation].directions[
|
||||
this._currentDirection
|
||||
];
|
||||
const oldFrame = this._currentFrame;
|
||||
const animationDuration =
|
||||
direction.frames.length * direction.timeBetweenFrames;
|
||||
const animationDuration = this.getAnimationDuration();
|
||||
if (
|
||||
!this._animationPaused &&
|
||||
(direction.loop || this._animationElapsedTime !== animationDuration) &&
|
||||
direction.timeBetweenFrames
|
||||
) {
|
||||
const elapsedTime = this.getElapsedTime() / 1000;
|
||||
this._animationElapsedTime += elapsedTime * this._animationSpeedScale;
|
||||
if (direction.loop) {
|
||||
this._animationElapsedTime = gdjs.evtTools.common.mod(
|
||||
this._animationElapsedTime,
|
||||
direction.frames.length * direction.timeBetweenFrames
|
||||
);
|
||||
} else {
|
||||
this._animationElapsedTime = gdjs.evtTools.common.clamp(
|
||||
this._animationElapsedTime,
|
||||
0,
|
||||
animationDuration
|
||||
);
|
||||
}
|
||||
this._currentFrame = Math.min(
|
||||
Math.floor(this._animationElapsedTime / direction.timeBetweenFrames),
|
||||
direction.frames.length - 1
|
||||
const animationElapsedTime =
|
||||
this._animationElapsedTime +
|
||||
(this.getElapsedTime() / 1000) * this._animationSpeedScale;
|
||||
this.setAnimationElapsedTime(
|
||||
direction.loop
|
||||
? gdjs.evtTools.common.mod(animationElapsedTime, animationDuration)
|
||||
: gdjs.evtTools.common.clamp(
|
||||
animationElapsedTime,
|
||||
0,
|
||||
animationDuration
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (oldFrame !== this._currentFrame || this._animationFrameDirty) {
|
||||
if (this._animationFrameDirty) {
|
||||
this._updateAnimationFrame();
|
||||
}
|
||||
if (oldFrame !== this._currentFrame) {
|
||||
this.invalidateHitboxes();
|
||||
}
|
||||
this._renderer.ensureUpToDate();
|
||||
}
|
||||
|
||||
@@ -755,6 +744,38 @@ namespace gdjs {
|
||||
return this._currentFrame;
|
||||
}
|
||||
|
||||
getAnimationElapsedTime(): float {
|
||||
return this._animationElapsedTime;
|
||||
}
|
||||
|
||||
setAnimationElapsedTime(time: float): void {
|
||||
const direction = this._animations[this._currentAnimation].directions[
|
||||
this._currentDirection
|
||||
];
|
||||
this._animationElapsedTime = gdjs.evtTools.common.clamp(
|
||||
time,
|
||||
0,
|
||||
this.getAnimationDuration()
|
||||
);
|
||||
|
||||
const oldFrame = this._currentFrame;
|
||||
this._currentFrame = Math.min(
|
||||
Math.floor(this._animationElapsedTime / direction.timeBetweenFrames),
|
||||
direction.frames.length - 1
|
||||
);
|
||||
if (oldFrame !== this._currentFrame) {
|
||||
this._updateAnimationFrame();
|
||||
this.invalidateHitboxes();
|
||||
}
|
||||
}
|
||||
|
||||
getAnimationDuration(): number {
|
||||
const direction = this._animations[this._currentAnimation].directions[
|
||||
this._currentDirection
|
||||
];
|
||||
return direction.frames.length * direction.timeBetweenFrames;
|
||||
}
|
||||
|
||||
getAnimationFrameCount(): number {
|
||||
if (this._currentAnimation >= this._animations.length) {
|
||||
return 0;
|
||||
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 210 B |
|
After Width: | Height: | Size: 132 B |
|
After Width: | Height: | Size: 107 B |
|
After Width: | Height: | Size: 145 B |
|
After Width: | Height: | Size: 156 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |