Game Development Community

Motion AfterImage Effect??

by Kevin Epps · in Torque Game Builder · 03/09/2008 (11:49 am) · 10 replies

I don't really know what anyone else would call this to do a proper search, but I'm trying to figure out how achieve an afterimage motion effect.

Meaning, I want copies of a sprite to trail the main sprite and fade out. I really hope you know what I'm talking about because I don't know how to put it into words without just calling it a motion afterimage effect.

Does anyone know how to achieve this in TGB? Maybe use particles for this or something?

#1
03/09/2008 (2:49 pm)
Could you not create a custom sprite animation for the effect? Might be the easiest way - cant think of any way of doing this real time unless you can play around with the mounting.
#2
03/09/2008 (4:03 pm)
You'd just have to create copies of the static sprite that just have the same dimensions and picture. Set them at a lower alpha then fade them out and delete them. If the object is moving, the images will be copied in a row and have different blend alphas then fade out.
#3
03/09/2008 (4:48 pm)
Here's some quick code for an after image effect for an animated sprite:

// after image motion blur effect
function t2dAnimatedSprite::doAfterImageEffect(%this)
{
   %this.createAfterImageClone();
   %this.schedule(50, "createAfterImageClone");
   %this.schedule(100, "createAfterImageClone");
   %this.schedule(150, "createAfterImageClone");
   %this.schedule(200, "createAfterImageClone");
   %this.schedule(250, "createAfterImageClone");
   %this.schedule(300, "createAfterImageClone");
   %this.schedule(350, "createAfterImageClone");
   %this.schedule(400, "createAfterImageClone");
   %this.schedule(450, "createAfterImageClone");
   %this.schedule(500, "createAfterImageClone");
}

function t2dAnimatedSprite::createAfterImageClone(%this)
{
   %clone = new t2dAnimatedSprite()
   {
      scenegraph = %this.getSceneGraph();
      class = "afterImageObject";
      size = %this.getSize();
      position = %this.getPosition();
      layer = (%this.getLayer() + 1);
      flipX = %this.FlipX;
      rotation = %this.getRotation();
      animationName = %this.getAnimation();
   };
   %clone.setFrameChangeCallback(true);
   %clone.setAnimationFrame(%this.getAnimationFrame());
   %clone.fadeOut(0.14, 50, 0);
   %clone.schedule(1500, "safeDelete");
}

function afterImageObject::onFrameChange(%this, %frameIndex)
{
   %this.setAnimationFrame(%frameIndex - 1);  
}

You can easily adapt this for different objects, but I'll be posting a much more refined method later. (After I write it.)
#4
03/09/2008 (5:00 pm)
Oh yeah, this function would probably be helpful:

//This function makes the object gradually disappear.
function t2dSceneObject::fadeOut(%this, %increment, %speed, %goal)
{
    if(isEventPending(%this.fadeInSchedule))
        cancel(%this.fadeInSchedule);
    
    %curAlpha = %this.getBlendAlpha();
    
    %this.setBlendAlpha(%curAlpha - %increment);
    
    if(%this.getBlendAlpha() >= %goal)
        %this.fadeOutSchedule = %this.schedule(%speed, "fadeOut", %increment, %speed, %goal);
    
    else
    {
        %this.setBlendAlpha(%goal);
        cancel(%this.fadeOutSchedule);
    }
}
#5
03/09/2008 (7:55 pm)
Thanks so much, Kevin!
#6
03/11/2008 (8:02 pm)
@Kevin

I meant to tell you that you code worked like a charm!!
#7
03/12/2008 (1:33 pm)
No problem Kevin. I'm still going to post some more refined code, but I've been pretty busy. ;)
#8
03/21/2008 (4:25 pm)
Finally got around to working on the after image effect some more. Now you can pass in parameters that distinctly change how the effect plays out. Recommended defaults:
object.doSimpleAfterImageEffect(100, 1500, false, true, 0.14, 50, 0);
//**simple after image motion blur effect for animated sprites**
//frequency - how often to make after image clones (copies of object that fade out)
//duration - how long the effect will last
//owner visible - if true, the owner stays visible during the entire effect
//                if false, the owner disappears during the effect, and reappears
//                at the end.
//still frames - if true, then the copies are forced to render the same exact frame
//               as the owner had when the particular copy was made. Bit of a hacked
//               solution. Changes the frame back to previous one on an onFrameChange
//               callback.
//increment, speed, goal - all of these parameters are used soly for the fadeOut
//                         function. Increment is the number by which to reduce
//                         the alpha each speed interval which is in milliseconds.
//                         Goal tells the function when to stop fading.
function t2dAnimatedSprite::doSimpleAfterImageEffect(%this, %frequency, %duration, %ownerVisible, %stillFrames, %increment, %speed, %goal)
{
   for(%i = 0; %i < %duration; %i += %frequency)
   {
      %this.schedule(%i, "createAfterImageClone", %stillFrames, %increment, %speed, %goal);
   }
   
   if(!%ownerVisible)
   {
      %this.visible = false;
      %this.schedule(%duration, "setVisible", true);
   }
}

//used by the after image effects to produce copy images.
function t2dAnimatedSprite::createAfterImageClone(%this, %stillFrames, %increment, %speed, %goal)
{
   %clone = new t2dAnimatedSprite()
   {
      scenegraph = %this.getSceneGraph();
      class = "afterImageObject";
      size = %this.getSize();
      position = %this.getPosition();
      layer = (%this.getLayer() + 1);
      flipX = %this.FlipX;
      rotation = %this.getRotation();
      animationName = %this.getAnimation();
   };
   %clone.setAnimationFrame(%this.getAnimationFrame());
   %clone.fadeOut(%increment, %speed, %goal);
   //the next line assumes that your goal is 0. It just automatically computes a safe waiting
   //time for the deletion of the clone. Use the commented line after it to manually control
   //the deletion. Make sure to comment the automated line.
   %clone.schedule(((((1 - %increment) * %speed)/43) * 1500), "safeDelete");
   //%clone.schedule([int in milliseconds] "safeDelete");
   
   if(%stillFrames)
      %clone.setFrameChangeCallback(true);
}

//hacked solution to freeze an animation
function afterImageObject::onFrameChange(%this, %frameIndex)
{
   %this.setAnimationFrame(%frameIndex - 1);  
}

function t2dStaticSprite::doSimpleAfterImageEffect(%this, %frequency, %duration, %ownerVisible, %increment, %speed, %goal)
{
   for(%i = 0; %i < %duration; %i += %frequency)
   {
      %this.schedule(%i, "createAfterImageClone", %increment, %speed, %goal);
   }
   
   if(!%ownerVisible)
   {
      %this.visible = false;
      %this.schedule(%duration, "setVisible", true);
   }
}

function t2dStaticSprite::createAfterImageClone(%this, %increment, %speed, %goal)
{
   %clone = new t2dStaticSprite()
   {
      scenegraph = %this.getSceneGraph();
      class = "afterImageObject";
      size = %this.getSize();
      position = %this.getPosition();
      layer = (%this.getLayer() + 1);
      flipX = %this.FlipX;
      rotation = %this.getRotation();
      imageMap = %this.getImageMap();
   };
   %clone.fadeOut(%increment, %speed, %goal);
   //the next line assumes that your goal is 0. It just automatically computes a safe waiting
   //time for the deletion of the clone. Use the commented line after it to manually control
   //the deletion. Make sure to comment the automated line.
   %clone.schedule(((((1 - %increment) * %speed)/43) * 1500), "safeDelete");
   //%clone.schedule([int in milliseconds] "safeDelete");
}
#9
03/29/2008 (1:13 pm)
Now to make this into a behavior :)
#10
03/29/2008 (6:39 pm)
Cool, Matt!

I'm just curious as how you are utilizing it in a behavior?