Game Development Community

FxSceneObject2D::startLinearTween()

by Chris Cockcroft · in Torque Game Builder · 04/01/2005 (11:52 am) · 6 replies

I'm nervous about being the first one to post in here. I'm a C++ virgin, so be gentle :)

After a lot of messing around, I was finally able to add tweening support the fxSceneObject2D class. It's not perfect, by any means, but I think it gives a good basis for some useful functionality. I find it particulary useful for any non-interactive elements like titles and HUD's.

Please note that my C++ is very limited, so I may be using the incorrect terms. Please feel free to correct me :)

Step 1

The first step before making these adjustments is to back up your engine folder, of course :)

Step 2

Add the following variable declarations to fxSceneObject2D.h:

// Tweening
    F32							mTweenDuration;
    bool						mIsTweening;
    fxVector2D					mTargetPosition;
    fxVector2D					mStartPosition;
    F32							mCurrentTweenTime;


Same with the function declarations:

// Tweening
	inline void setTargetPosition( fxVector2D targetPosition ) { mTargetPosition = targetPosition; }
	inline fxVector2D getTargetPosition() { return mTargetPosition; }
	inline void setStartPosition( fxVector2D startPosition ) { mStartPosition = startPosition; }
	inline fxVector2D getStartPosition() { return mStartPosition; }
	inline void setIsTweening( bool value ) { mIsTweening = value; }
	inline bool getIsTweening() { return mIsTweening; }
	inline void setTweenDuration( F32 tweenDuration ) { mTweenDuration = tweenDuration; }
	inline F32 getTweenDuration() { return mTweenDuration; }
	inline void setCurrentTweenTime( F32 currentTweenTime ) { mCurrentTweenTime = currentTweenTime; }
	inline F32 getCurrentTweenTime() { return mCurrentTweenTime; }
	
	void startLinearTween( fxVector2D targetPosition, F32 duration);
	void updateTween( F32 delta );
	F32 tween( F32 current, F32 target, F32 delta );
	F32 linearTween( F32 current, F32 target, F32 delta );
	void completeTween();



// continued

#1
04/01/2005 (11:52 am)
Step 3

Now, to fxSceneObject2d.cc, add the following:

//This is the console method that allows for the call from TorqueScript.

ConsoleMethod(fxSceneObject2D, startLinearTween, void, 4, 4, "(x/y, duration) - Moves object using linear ramp.")
{
	
	if ( fxSceneObject2D::getStringElementCount(argv[2]) < 2 )
	{
		Con::warnf("fxSceneObject2D::startLinearTween - Invalid number of parameters!");
		return;
	}
	
	object->startLinearTween( fxVector2D(dAtof(fxSceneObject2D::getStringElement(argv[2], 0)),
	                                     dAtof(fxSceneObject2D::getStringElement(argv[2], 1))),
	                           
	                           
	                           dAtof(argv[3]));

}

// this is the corresponding function for the above method
// It prepares our object for the tween

void fxSceneObject2D::startLinearTween( fxVector2D targetPosition, F32 duration)
{
	
	// set our starting position
	setStartPosition( getPosition() );
	
	// check to see if we are already at our target position
	if ( targetPosition.mX == mStartPosition.mX &&
		 targetPosition.mY == mStartPosition.mY)
	{
		// if we are already there, no need to tween
		Con::warnf("fxSceneObject2D::startLinearTween - Object is already at target location");
		return;
	} 
	else if ( duration <= 0.0 )
	{
		Con::warnf("fxSceneObject2D::startLinearTween - Cannot have negative or zero duration");
		return;
	}
	else	
	{
		// get set up for our tween
		// convert the target position according to the objects current position
		// Our target position is actually the change in x and y of our object
		
		targetPosition.mX = targetPosition.mX - mStartPosition.mX;
		targetPosition.mY = targetPosition.mY - mStartPosition.mY;
		
		// set the converted target position
		setTargetPosition( targetPosition );
		
		// set the duration of our tween (in seconds)
		setTweenDuration( duration );
		
		// reset our counter
		setCurrentTweenTime( 0.0 );	
		
		// let everyone know we are tweening
		setIsTweening( true );	
	}
}

// this is function called by fxSceneObject2D's prepareUpdate() function
// which (obviously) updates the tween

void fxSceneObject2D::updateTween( F32 delta )
{
	
	// if our timeStep >= duration, we're finished
	if ( delta >= 1.0f )
	{
		completeTween();
		return;
	}
	
	// Calculate our tween amounts for x & y and add them to our starting position
	fxVector2D newPosition = fxVector2D( linearTween( getPosition().mX, mTargetPosition.mX, delta ) + mStartPosition.mX,
										 linearTween( getPosition().mY, mTargetPosition.mY, delta ) + mStartPosition.mY);
										 
	// set our objects new position									 
	setPosition( newPosition );
}

// I borrowed this from Melv's camera interpolation code found in fxSceneWindow2D

F32 fxSceneObject2D::linearTween( F32 current, F32 target, F32 delta )
{
	
	if ( delta <= 0.0f )
		return current;
	else if ( delta >= 1.0f ) 
		return target;
	
	return ( current * ( 1.0f - delta ) ) + ( target * delta );

}

// called when the duration of the tween has been reached or exceeded
// this code ensures that the object stops at the exact location specified

void fxSceneObject2D::completeTween()
{
	setPosition( fxVector2D( getTargetPosition().mX + getStartPosition().mX, getTargetPosition().mY + getStartPosition().mY ) );
	setIsTweening( false );
}

//This is the console method that allows for the call from TorqueScript.

ConsoleMethod(fxSceneObject2D, stopLinearTween, void, 2, 2, "Ends the tween")
{
	
	object->setIsTweening( false );

}

The last thing to add to make this all work is some code that goes in fxSceneObject2D's prepareUpdate() function, found around line 3519 of fxSceneObject2D.cc:

if ( mIsTweening == true )
	{
		mCurrentTweenTime += elapsedTime / mTweenDuration;
		updateTween( mCurrentTweenTime );
	}

//continued
#2
04/01/2005 (11:53 am)
Step 4

Now recompile and try it out. Create an object (like a static sprite, or even one of Greg Lincoln's fxTextObject2d objects) and then add this script:

function fxSceneWindow2D::onMouseDown(%this, %modifier, %worldPosition, %mouseClicks) {
	
	// assuming your object is called $test :P
	$test.startLinearTween(%worldPosition, 0.7);
	
}

Now run your game and click anywhere in the screen. Your object will move towards the cursor at a velocity that will get it there in 0.7 seconds. Dandy :)


So that's it. It's not perfect by any means, and there could be a lot more console methods (like one to check if an object is currently tweening). But it's a start. I'd love to add some more complicated motion curves to this as well. I think adding wave motion and object shake could also be done in a very similar way. And perhaps something that forces the object to move at the same velocity (ignoring the time variable). Looking at it now, there's too many things I could add :P

And again, let me know if there are any errors. I'm more familiar with Obj-C, and this is my first attempt at coding anything in C++. Let me know what you think :)

Chris
#3
04/01/2005 (2:50 pm)
Wow. I don't have time to try this out but big pat on the back for you Chris for being the first to post up some new engine functionality code!
#4
04/01/2005 (6:24 pm)
Thanks for the code Chris. Your code works fine (and I do appreciate it) but I'm not sure that I like linear tweening built into the fxSceneObject2D class. I still appreciate you releasing this because between your code and the Font add-on, I think I have an idea of how I'm going to attempt my own tweening classes.

In case you (or anyone else) cares, what I'm going to try is this: deriving a generic tween class from the fxSceneObject class. From the generic tween class, we can then derive more complicated tweening classes such as bezier tweening and sinusoidal movement. In order to use one of these paths, you would create an invisible path object and temporarily mount the object you desired moved onto this path object. The path object then ferries your object to the end of the path at which point you can dismount it.

By the way, your C++ code looks fine. I really do appreciate people releasing snippets. Like most people at this point, I really don't know what I'm doing and let's face it, Torque is just plain menacing the first time you look at its innards. Seeing things like this gives me confidence to try my own hacking.

P.S. I realize that Melv is tenatively working on tweening, but lets see if we can beat him:)
#5
04/01/2005 (7:51 pm)
I'm not sure why (yet), but this doesn't seem to work?
It freaks out and goes off to a corner and then starts to come back and ends up in the right place.

Example:
S__F.
(S = Start, _ = space F = Finish)
Instead of going S->->F, it goes:
S F
* &
* &
* &
*(etc)
(* happens first, and then &, but then it ends up in the right position, but takes totally the wrong path)

I even tried it on a blank mod with a 2dsprite object and it still flips out, but ends up in the right place.

I copy and pasted the code, so I don't think there are any typos. Any ideas would could be causing this weird behavour?
#6
04/02/2005 (5:38 pm)
Here's the fix that seems to work ok for me.
In F32 fxSceneObject2D::linearTween( F32 current, F32 target, F32 delta ) {

replace return ( current / ( 1.0f - delta ) ) + ( target * delta ); with

if (target == 0) {
return 0;
} else {
return (target * delta);
}

When linearTween returns it adds the startposition to that and moves by that much, which messes causes it to freak out if target is 0.

It could be that i'm just doing it the wrong way, but the code above seems to work ok for me.