Sprite property interpolation code
by Jason McIntosh · in Torque Game Builder · 04/28/2005 (9:36 pm) · 5 replies
Well, I'm in need of animatable properties, but they aren't ready yet, so I created a stop-gap in TorqueScript for my current game.
Here's the interpolation code (crude linear), a "sizer" object which will scale an image on both dimensions within a given timeframe (milliseconds), and a "fader" object which does the same, except for alpha blending values. These will optionally "ping-pong" their effects so there's a nice pulsing effect. This also can be extended to do rotations, probably translations too. And it's framerate independent.
You don't have to understand the code to use it. I'll give an example at the end. :)
(continued...)
(Edit: I added code for fading.)
Here's the interpolation code (crude linear), a "sizer" object which will scale an image on both dimensions within a given timeframe (milliseconds), and a "fader" object which does the same, except for alpha blending values. These will optionally "ping-pong" their effects so there's a nice pulsing effect. This also can be extended to do rotations, probably translations too. And it's framerate independent.
You don't have to understand the code to use it. I'll give an example at the end. :)
// timeStart = SimTime when interpolating began
// timeTotal = how long interpolation should take (milliseconds)
// interval = startValue - endValue: for example, 1.0 (end) - 0.0 (start) = 1.0 (interval)
function interpUp( %timeStart, %timeTotal, %interval )
{
%now = getSimTime();
%total = %now - %timeStart;
if ( %total < %timeTotal )
{
%percent = %total / %timeTotal;
return %interval * %percent;
}
else
{
return %interval;
}
}
// timeStart = SimTime when interpolating began
// timeTotal = how long interpolation should take (milliseconds)
// interval = startValue - endValue: for example, 1.0 (start) - 0.0 (end) = 1.0 (interval)
function interpDown( %timeStart, %timeTotal, %interval )
{
%now = getSimTime();
%total = %now - %timeStart;
if ( %total < %timeTotal )
{
%percent = %total / %timeTotal;
return %interval - (%interval * %percent);
}
else
{
return 0;
}
}(continued...)
(Edit: I added code for fading.)
About the author
#2
That's it.
04/28/2005 (9:37 pm)
Example usage:%image = new fxStaticSprite2D() { scenegraph = t2dSceneGraph; };
....etc....
// Scale from fullscreen to small, then back, over and over
%sizeomatic = new ScriptObject( sizer ) { image = %image; };
%sizeomatic.repeat = true;
%sizeomatic.setX( 100, 25 ); // x start, x end
%sizeomatic.setY( 75, 18 ); // y start, y end
%sizeomatic.sizeDown( 4000 ); // do it in 4 secondsThat's it.
#3
(continued...)
04/28/2005 (10:24 pm)
Here's code for fading an image.function fader::onAdd( %this )
{
%this.fading = false;
%this.repeat = false; // Set to true to "ping-pong" the fade.
%this.setAlphaRange( 0, 255 ); // Provide common default.
}
function fader::onRemove( %this )
{
if ( %this.fading == true )
cancel( %this.nextUpdate );
%this.image.setBlendColour( %this.alpha ); // Reset to original alpha
}
// Set the alpha start and end values, from 0 - 255 for each. 0 = totally invisible.
function fader::setAlphaRange( %this, %min, %max )
{
%this.alpha = %this.image.getBlendColour(); // Cache original alpha value
if ( %min < 0 )
%min = 0;
if ( %max > 255 )
%max = 255;
%this.min = %min;
%this.max = %max;
if ( %this.max > %this.min )
%this.interval = %this.max - %this.min;
else
%this.interval = %this.min - %this.max;
}
function fader::fadeIn( %this, %fadeTime )
{
if ( %this.fading == false )
cancel( %this.nextUpdate ); // Interrupt if in progress
if ( %this.min > %this.max )
{
// Swap in case we're reversing direction.
%t = %this.min;
%this.min = %this.max;
%this.max = %t;
}
%this.fadeTime = %fadeTime;
%this.fading = true;
%this.fadeStart = getSimTime();
%this.lastUpdate = %this.fadeStart;
%this.nextUpdate = %this.schedule( 20, "fadeInUpdate" );
%this.image.setBlendColour( "255 255 255" SPC %this.min );
}
function fader::fadeOut( %this, %fadeTime )
{
if ( %this.fading == false )
cancel( %this.nextUpdate ); // Interrupt if in progress
if ( %this.min > %this.max )
{
// Swap in case we're reversing direction.
%t = %this.min;
%this.min = %this.max;
%this.max = %t;
}
%this.fadeTime = %fadeTime;
%this.fading = true;
%this.fadeStart = getSimTime();
%this.lastUpdate = %this.fadeStart;
%this.nextUpdate = %this.schedule( 20, "fadeOutUpdate" );
%this.image.setBlendColour( "255 255 255" SPC %this.max );
}
function fader::fadeInUpdate( %this )
{
%alpha = interpUp( %this.fadeStart, %this.fadeTime, %this.interval );
if ( (%alpha + %this.min) < %this.max )
{
// Hasn't reached max alpha.
%this.nextUpdate = %this.schedule( 20, "fadeInUpdate" );
}
else
{
// Got to max alpha, done.
%this.fading = false;
if ( %this.repeat == true )
%this.fadeOut( %this.fadeTime ); // Start opposite fading.
}
%this.image.setBlendColour( "255 255 255" SPC (%this.min + %alpha) );
}
function fader::fadeOutUpdate( %this )
{
%alpha = interpDown( %this.fadeStart, %this.fadeTime, %this.interval );
if ( (%alpha + %this.min) > %this.min )
{
%this.nextUpdate = %this.schedule( 20, "fadeOutUpdate" );
}
else
{
%this.fading = false;
if ( %this.repeat == true )
%this.fadeIn( %this.fadeTime );
}
%this.image.setBlendColour( "255 255 255" SPC (%this.min + %alpha) );
}
// Proxy function to start a fade with a schedule()
function startFadeIn( %fader, %fadeTime )
{
%fader.fadeIn( %fadeTime );
}
function startFadeOut( %fader, %fadeTime )
{
%fader.fadeOut( %fadeTime );
}(continued...)
#4
The alpha min/max defaults to 0/255 (totally invisible/totally visible), so you can leave that part out if the default is ok.
Pretty easy. :)
04/28/2005 (10:28 pm)
Example usage:%image = new fxStaticSprite2D() { scenegraph = t2dSceneGraph; };
(...etc...)
%fadeomatic = new ScriptObject( fader ) { image = %image; };
%fadeomatic.min = 0; // alpha min, 0 = totally invisible
%fadeomatic.max = 128; // 50% visible
%fadeomatic.repeat = true; // ping-pong the fade
%fadeomatic.fadeOut( 3000 ); // fade out in 3 secondsThe alpha min/max defaults to 0/255 (totally invisible/totally visible), so you can leave that part out if the default is ok.
Pretty easy. :)
#5
04/29/2005 (9:11 am)
Will definitely come in handy Jason! Thanks!:)
Torque Owner Jason McIntosh
function sizer::onAdd( %this ) { %this.sizing = false; %this.repeat = false; // set to true to ping-pong the sizing. } function sizer::onRemove( %this ) { if ( %this.sizing == true ) cancel( %this.nextUpdate ); %this.image.setSize( %this.sizeXY ); // Set back to original size } // Values to size to, eg: 5, 10 -> sizes the object on the X component. function sizer::setX( %this, %min, %max ) { %this.sizeXY = %this.image.getSize(); // Cache original size %this.xMin = %min; %this.xMax = %max; if ( %this.xMax > %this.xMin ) %this.xInterval = %this.xMax - %this.xMin; else %this.xInterval = %this.xMin - %this.xMax; } // Values to size to, eg: 5, 10 -> sizes the object on the Y component. function sizer::setY( %this, %min, %max ) { %this.yMin = %min; %this.yMax = %max; if ( %this.yMax > %this.yMin ) %this.yInterval = %this.yMax - %this.yMin; else %this.yInterval = %this.yMin - %this.yMax; } function sizer::sizeUp( %this, %sizeTime ) { if ( %this.sizing == true ) cancel( %this.nextUpdate ); // Interrupt if in progress if ( %this.xMin > %this.xMax ) { // Swap in case we're reversing direction. %t = %this.xMin; %this.xMin = %this.xMax; %this.xMax = %t; } if ( %this.yMin > %this.yMax ) { // Swap in case we're reversing direction. %t = %this.yMin; %this.yMin = %this.yMax; %this.yMax = %t; } %this.sizeTime = %sizeTime; %this.sizing = true; %this.sizeStart = getSimTime(); %this.lastUpdate = %this.sizeStart; %this.nextUpdate = %this.schedule( 20, "sizeUpUpdate" ); %this.image.setSize( %this.xMin SPC %this.yMin ); } function sizer::sizeDown( %this, %sizeTime ) { if ( %this.sizing == true ) cancel( %this.nextUpdate ); // Interrupt if in progress if ( %this.xMin > %this.xMax ) { // Swap in case we're reversing direction. %t = %this.xMin; %this.xMin = %this.xMax; %this.xMax = %t; } if ( %this.yMin > %this.yMax ) { // Swap in case we're reversing direction. %t = %this.yMin; %this.yMin = %this.yMax; %this.yMax = %t; } %this.sizeTime = %sizeTime; %this.sizing = true; %this.sizeStart = getSimTime(); %this.lastUpdate = %this.sizeStart; %this.nextUpdate = %this.schedule( 20, "sizeDownUpdate" ); %this.image.setSize( %this.xMax SPC %this.yMax ); } function sizer::sizeUpUpdate( %this ) { %x = interpUp( %this.sizeStart, %this.sizeTime, %this.xInterval ); %y = interpUp( %this.sizeStart, %this.sizeTime, %this.yInterval ); // echo( "sizeUpUpdate x = " @ %x+%this.xMin @ " (" @ %this.xMax @ "), y = " @ %y+%this.yMin @ " (" @ %this.yMax @ ")" ); if ( (%x + %this.xMin) < %this.xMax || (%y + %this.yMin) < %this.yMax ) { // Hasn't reached max on both components yet. %this.nextUpdate = %this.schedule( 20, "sizeUpUpdate" ); } else { // Got to max on x and y components, done. %this.sizing = false; if ( %this.repeat == true ) %this.sizeDown( %this.sizeTime ); // Start opposite sizing. } %this.image.setSize( (%this.xMin + %x) SPC (%this.yMin + %y) ); } function sizer::sizeDownUpdate( %this ) { %x = interpDown( %this.sizeStart, %this.sizeTime, %this.xInterval ); %y = interpDown( %this.sizeStart, %this.sizeTime, %this.yInterval ); // echo( "sizeDownUpdate x = " @ %x+%this.xMin @ " (" @ %this.xMin @ "), y = " @ %y+%this.yMin @ " (" @ %this.yMin @ ")" ); if ( (%x + %this.xMin) > %this.xMin || (%y + %this.yMin) > %this.yMin ) { %this.nextUpdate = %this.schedule( 20, "sizeDownUpdate" ); } else { %this.sizing = false; if ( %this.repeat == true ) %this.sizeUp( %this.sizeTime ); } %this.image.setSize( (%this.xMin + %x) SPC (%this.yMin + %y) ); } // Proxy function to start a size with a schedule() function startSizeUp( %sizer, %sizeTime ) { %sizer.sizeUp( %sizeTime ); } function startSizeDown( %sizer, %sizeTime ) { %sizer.sizeDown( %sizeTime ); }(continued...)