Plastic Gem #4: Animated Guis
by Anthony Rosenbaum · 06/12/2008 (6:43 am) · 38 comments
Download Code File

Plastic Gem #4: Animated GUI Controls
Difficulty: Hard
For a list of gems see the Gem A Day page.
Hello again from Plastic Games. This resource will allow you to animate your GUI s in a variety of ways. You can animate a GUI's position, extent, clipping area, alpha and even color! However the alpha and color aspects have to be incorporated into the children GUI you wish you use. Lucky for you we decided to include our GuiPlasticBitmapCtrl. The GuiPlasticBitmapCtrl will also allow you to run bitmap animation. Essentially you will have the ability to make a nice 'flashy' interface without the hassle of incorporating Flash.
You must follow the instructions in Gem #3: Ease to add the Ease class before you can add the GUI changes. You can elect to follow the instructions in Gem #2: Animation Threads first. If you do then you can follow all instructions from Gem #3. If you do not care about animation threads in 3D objects you can skip that part and simply ignore any mention of threads when you follow the Gem #3 on ease.
To get you psyched check out this YouTube Video
'
All the code presented here was developed by Paul Dana, Lead programmer at Plastic Games.
This code can be implemented in TGEA 1.7 and TGE 1.5.2
1)We need to change mBounds from a private to a protected variable
At top of GuiControl Class change this
To this
2) Provide a lot of variables for animation.
Guicontrol.h after
static GuiControl *smCurResponder;
after
enum vertSizingOptions
{
vertResizeBottom = 0, ///< fixed on the top and in height
vertResizeHeight, ///< fixed on the top and bottom
vertResizeTop, ///< fixed in height and on the bottom
vertResizeCenter,
vertResizeRelative ///< resize relative
};
3) Declare an interface to animate with.
after
F32 getFloatVariable(); ///< Returns value of control's bound variable as a float
Now edit the file guicontrol.cc
GuiControl::GuiControl()
After
mTipHoverTime = 1000;
(or mCanSaveFieldDictionary = false; in TGEA)
5) Time to expose a new group and fields to the editor.
in void GuiControl::initPersistFields()
after endGroup("ToolTip");
6) Here is some support functions for the clip animation.
after renderTooltip()
7) This next function allows us to incorporate animation into child controls so replace the existing function with this one.
replace
void GuiControl::renderChildControls()
with TGE VERSION ONLY ( TGEA BELOW)
8) We are going to comment out the old getCenter() Interface for the GUI control, you can delete it if you like but I am of the opinion to keep the old until you know for sure you'll never use it again, your call. We will be providing a new getCenter() in a little bit.
comment out
ConsoleMethod( GuiControl, getCenter,
Like this
at the end of the file
Specifically
Con::executef(this,2,"onAnimationStarted"); // TGE
// Con::executef(this,"onAnimationStarted"); // TGEA
ALSO Comment out the old ConsoleMethod GetRoot
There you have it now you can do some amazing things with GUI. Here is the necessary script code to get it working.
EXAMPLE APPLICATION
10) Here is an example of how you might incorporate the alpha and color into an existing control, the GuiMLTextColor.
guiMLTextCtrl.cc
TGE ONLY
in void GuiMLTextCtrl::drawAtomText(
after
dglDrawTextN(font, drawPoint + atom->style->shadowOffset, tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL);
}
TGEA ONLY
in void GuiMLTextCtrl::drawAtomText(
after
if(atom->style->shadowOffset.x || atom->style->shadowOffset.y)
{
ColorI shadowColor = atom->style->shadowColor;
shadowColor.alpha = (S32)(mAlpha * shadowColor.alpha);
drawer->setBitmapModulation(shadowColor);
drawer->drawTextN(font, drawPoint + atom->style->shadowOffset,
tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL);
}
11) Finally add the GuiPlasticBitmapCtrl.h and GuiPlasticBitmapCtrl.cc into the gui/shiney folder. And Compile
This control can load single images or a dml then play thu the dml as an animation.
The GuiPlasticBitmapCtrl also has callbacks for mouse and animation events.
You'll also find 2 additional fields in the GuiPlasticBitmapCtrl.
Another great feature with the GuiPlasticBitmapCtrl is that if you make your images colored white you can blend and animate color with the animateColor and setColor functions.
PlasticBitmap Example TGE. This will make a PlasticBitmap on the current GUI name pg. I put some functionality into its mouseCallbacks but I also included some code so you can see how sweet this stuff can be.
12) place the star folder within you game's folder as a sibling of the data/server/client
13) In console (~) exec("YOURGAMENAME/star/star.cs");
14) Press the Star to start the PlasticBitmap Spinning and animating color.
15) pg.animateAlpha(0, 0, 500); watch the bitmap disappear;
16) pg.animateAlpha(1, 0, 500); watch the bitmap reappear
17) pg.animateUnitClipping(1, 1, 1, 1, 500, 500); wipes texture to bottom right
18) pg.animateUnitClipping(1, 1, 1, 1, 500, 500); restore clip area
19) pg.animateCenter(500, 200 , 0, 1500, "0 2"); //notice the ease in out on the move
20) pg.animateCenter(500,500 , 0, 1500, "2 10"); //notice the bounce
The Next Gem
That's all for the animated GUI resource. This resource is so large there really wasn't enough time or space to fully cover all the applications of this code. Later gems will give more applications for GUI resources.
The next gem will cover the isJugg variable and the AudioManager which allow us to have one script code base that works between TGE and TGEA. It also introduces the Plastic folder, a folder for useful code that you might share between games.

Plastic Gem #4: Animated GUI Controls
Difficulty: Hard
For a list of gems see the Gem A Day page.
Hello again from Plastic Games. This resource will allow you to animate your GUI s in a variety of ways. You can animate a GUI's position, extent, clipping area, alpha and even color! However the alpha and color aspects have to be incorporated into the children GUI you wish you use. Lucky for you we decided to include our GuiPlasticBitmapCtrl. The GuiPlasticBitmapCtrl will also allow you to run bitmap animation. Essentially you will have the ability to make a nice 'flashy' interface without the hassle of incorporating Flash.
You must follow the instructions in Gem #3: Ease to add the Ease class before you can add the GUI changes. You can elect to follow the instructions in Gem #2: Animation Threads first. If you do then you can follow all instructions from Gem #3. If you do not care about animation threads in 3D objects you can skip that part and simply ignore any mention of threads when you follow the Gem #3 on ease.
To get you psyched check out this YouTube Video
'
All the code presented here was developed by Paul Dana, Lead programmer at Plastic Games.
This code can be implemented in TGEA 1.7 and TGE 1.5.2
1)We need to change mBounds from a private to a protected variable
At top of GuiControl Class change this
private: typedef SimGroup Parent; StringTableEntry mScriptFile; ///< The script file this GUI was created from SimGroup *mAddGroup; ///< The internal name of a SimGroup child of the global GuiGroup in which to organize this gui on creation RectI mBounds; ///< The internal bounds of this control
To this
private: typedef SimGroup Parent; StringTableEntry mScriptFile; ///< The script file this GUI was created from SimGroup *mAddGroup; ///< The internal name of a SimGroup child of the global GuiGroup in which to organize this gui on creation // > pg animation protected: RectI mBounds; ///< The internal bounds of this control // < animation
2) Provide a lot of variables for animation.
Guicontrol.h after
static GuiControl *smCurResponder;
// > pg animation
bool mPassThru; // pass mouse events through this control...
Point2F mCenter; // only updated when asked for it...always call getCenter()
// NOTE: ADDED Robert Brenner's very fine EASE code to mMath.h as mEase.h
struct animation
{
const static S32 maxDims = 4; // max # of dims allowed...
bool enabled;
EaseF ease;
F32 startTime; // careful! this IS a F32 but it represents MILLISECONDS here
F32 duration;
F32 currValue[maxDims];
F32 startValue[maxDims];
F32 deltaValue[maxDims];
// when doing a simple animation interpStart would always be 0.0
// and interpEnd would always be 1.0...however when doing a Key Point List
// these values will represent which SEGMENT of the overal KPL is equivalent
// to the current interpolation underway
// TODO: remove this?
F32 interpStart;
F32 interpEnd;
bool kplEnabled; // true if we are interpolating along a list of keypoints
F32 kplStartTime; // start time of entire Key Point List (in milliseconds)
F32 kplDuration; // DURATION in milliseconds for entire animation
EaseF kplEase; // ease to use for kpl
// the input from user in the KPL is seconds...we normalize from that to 0..1
Vector<Point4F>keyPointList; // the KPL: list of key points (values are x,y,z, and w = normalized time, ie last w is always 1.0)
S32 kplIndex; // current INDEX in a KPL playback...
S32 kplNumDimensions; // how many dimensions in input...set in kplReadInput()
void kplReadInput(SimObject *input, S32 numDims);
void kplSetup(SimObject *input, S32 numDims, F32 startDelay, F32 duration, EaseF ease); // -1 duration means get from input.duration
bool kplSetupNext(); // setup next sgement in KPL, return false if there is no next...
animation();
~animation();
void cancel() { enabled= false; }
void setup(F32 startDelay, F32 duration, EaseF ease);
void setup(F32 startDelay, F32 dur, EaseF ease, F32 start, F32 end);
void setup(F32 dur, F32 start, F32 end);
void setupF(F32 oldVal, F32 newVal, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
void setup2I(S32 oldX, S32 oldY, S32 newX, S32 newY, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
void setup2F(F32 oldX, F32 oldY, F32 newX, F32 newY, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
void setup3F(F32 oldX, F32 oldY, F32 oldZ, F32 newX, F32 newY, F32 newZ, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
void setup4F(F32 oldX, F32 oldY, F32 oldZ, F32 oldW, F32 newX, F32 newY, F32 newZ, F32 newW, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
// start end values actaully currentl not used
// TODO: remove start/end?
void setupF(F32 oldVal, F32 newVal, F32 startDelay, F32 duration, EaseF ease)
{
setupF(oldVal,newVal,startDelay,duration,ease,0.0f,1.0f);
}
void setup2I(S32 oldX, S32 oldY, S32 newX, S32 newY, F32 startDelay, F32 duration, EaseF ease)
{
setup2I(oldX,oldY,newX,newY,startDelay,duration,ease,0.0f,1.0f);
}
void setup2F(F32 oldX, F32 oldY, F32 newX, F32 newY, F32 startDelay, F32 duration, EaseF ease)
{
setup2F(oldX,oldY,newX,newY,startDelay,duration,ease,0.0f,1.0f);
}
void setup3F(F32 oldX, F32 oldY, F32 oldZ, F32 newX, F32 newY, F32 newZ, F32 startDelay, F32 duration, EaseF ease)
{
setup3F(oldX,oldY,oldZ,newX,newY,newZ,startDelay,duration,ease,0.0f,1.0f);
}
void setup4F(F32 oldX, F32 oldY, F32 oldZ, F32 oldW, F32 newX, F32 newY, F32 newZ, F32 newW, F32 startDelay, F32 duration, EaseF ease)
{
setup4F(oldX,oldY,oldZ,oldW,newX,newY,newZ,newW,startDelay,duration,ease,0.0f,1.0f);
}
bool interp(int numDims);
void getFloat(F32 &f)
{
f = currValue[0];
}
void getColor(ColorI &c)
{
c.red = (S32)currValue[0];
c.green = (S32)currValue[1];
c.blue = (S32)currValue[2];
}
// FIX THESE...g
void getRectF(RectF &r)
{
r.point.x = currValue[0];
r.point.y = currValue[1];
r.extent.x = currValue[2];
r.extent.y = currValue[3];
}
void getRectI(RectI &r)
{
r.point.x = (S32)currValue[0];
r.point.y = (S32)currValue[1];
r.extent.x = (S32)currValue[2];
r.extent.y = (S32)currValue[3];
}
void getPoint4F(Point4F &p)
{
p.x = currValue[0];
p.y = currValue[1];
p.z = currValue[2];
p.w = currValue[3];
}
void getPoint3F(Point3F &p)
{
p.x = currValue[0];
p.y = currValue[1];
p.z = currValue[1];
}
void getPoint2F(Point2F &p)
{
p.x = currValue[0];
p.y = currValue[1];
}
void getPoint2I(Point2I &p)
{
p.x = (S32)currValue[0];
p.y = (S32)currValue[1];
}
};
// < pg animationafter
enum vertSizingOptions
{
vertResizeBottom = 0, ///< fixed on the top and in height
vertResizeHeight, ///< fixed on the top and bottom
vertResizeTop, ///< fixed in height and on the bottom
vertResizeCenter,
vertResizeRelative ///< resize relative
};
protected:
// > pg guicolor
F32 mAlpha;
ColorI mColor;
RectF mUnitClip; // clip rect in unit space...ie 0 0 1 1 means draw all
bool mAnimating; // track when any animation is taking place...
animation mAlphaAnim;
animation mColorAnim;
animation mUnitClipAnim;
animation mPositionAnim; // EITHER use position & extent...
animation mExtentAnim;
animation mBoundsAnim; // ... OR use bounds
// < pg guicolor3) Declare an interface to animate with.
after
F32 getFloatVariable(); ///< Returns value of control's bound variable as a float
public:
// > pg animation
void setPassThru(bool passThru);
bool getPassThru() { return mPassThru; }
const Point2F& getCenter() {
mCenter.x = ((F32)mBounds.extent.x) / 2.0F + mBounds.point.x;
mCenter.y = ((F32)mBounds.extent.y) / 2.0F + mBounds.point.y;
return mCenter;
}
const F32 getHalfWidth() { return (F32)mBounds.extent.x/2.0F; }
const F32 getHalfHeight() { return (F32)mBounds.extent.y/2.0F; }
void setCenter(const Point2F ¢er);
void setCenterAndSize(const Point2F ¢er, const Point2I &extent);
void setAlpha(F32 a);
F32 getAlpha() {return mAlpha;}
void setColor(ColorI color);
ColorI getColor() {return mColor;}
void setUnitClip(RectF unitClip);
RectF getUnitClip() {return mUnitClip;}
void updateAnimationState();
void cancelAllAnimations();
void updateAnimation();
// position now cets the controls CENTER...
void animatePositionKeyPointList(SimObject *input, F32 startDelay, F32 duration, EaseF ease);
void animatePosition(Point2F newPos, F32 startDelay, F32 duration, EaseF ease);
void animateExtent(Point2I newExt, F32 startDelay, F32 duration, EaseF ease);
void animateAlpha(F32 newAlpha, F32 startDelay, F32 duration, EaseF ease);
void animateColor(ColorI newColor, F32 startDelay, F32 duration, EaseF ease);
void animateUnitClip(RectF newClip, F32 startDelay, F32 duration, EaseF ease);
void animateBounds(RectF newBounds, F32 startDelay, F32 duration, EaseF ease);
void updateColorAnimation();
void updatePositionAnimation();
void updateExtentAnimation();
void updateAlphaAnimation();
void updateUnitClipAnimation();
void updateBoundsAnimation();
bool isAnimating() { return mAnimating;}
// < pg animation4) Let's initialize all those new variables. Now edit the file guicontrol.cc
GuiControl::GuiControl()
After
mTipHoverTime = 1000;
(or mCanSaveFieldDictionary = false; in TGEA)
// > pg animation mPassThru = false; mAlpha = 1.0; mColor.set(255,255,255); mUnitClip.point.x = mUnitClip.point.y = 0; mUnitClip.extent.x = mUnitClip.extent.y = 1; mAnimating = false; mPositionAnim.enabled = false; mExtentAnim.enabled = false; mBoundsAnim.enabled = false; mAlphaAnim.enabled = false; mColorAnim.enabled = false; mUnitClipAnim.enabled = false; // < pg animation
5) Time to expose a new group and fields to the editor.
in void GuiControl::initPersistFields()
after endGroup("ToolTip");
// > pg Animation
addGroup("Animation");
addField("alpha", TypeF32, Offset(mAlpha, GuiControl));
addField("color", TypeColorI, Offset(mColor, GuiControl));
addField("unitClip", TypeRectF, Offset(mUnitClip, GuiControl));
endGroup("Animation");
// < pg Animation6) Here is some support functions for the clip animation.
after renderTooltip()
// > pg animation
// re express a rect relative to current & unit
static RectI getRealClip(const RectF &unitClip, const Point2I &point, const Point2I &extent)
{
RectI clip(point.x+unitClip.point.x*extent.x,
point.y+unitClip.point.y*extent.y,
unitClip.extent.x*extent.x,
unitClip.extent.y*extent.y);
return clip;
}
// < pg animation7) This next function allows us to incorporate animation into child controls so replace the existing function with this one.
replace
void GuiControl::renderChildControls()
with TGE VERSION ONLY ( TGEA BELOW)
void GuiControl::renderChildControls(Point2I offset, const RectI &updateRect)
{
// > pg animation
// offset is the upper-left corner of this control in screen coordinates
// updateRect is the intersection rectangle in screen coords of the control
// hierarchy. This can be set as the clip rectangle in most cases.
// move this into loop...
//RectI clipRect = updateRect;
iterator i;
for(i = begin(); i != end(); i++)
{
GuiControl *ctrl = static_cast<GuiControl *>(*i);
if (ctrl->mVisible)
{
ctrl->updateAnimation();
Point2I childPosition;
RectI childClip;
RectI clipRect;
bool draw;
childPosition = offset + ctrl->getPosition();
if (ctrl->mUnitClip.point.x == 0 && ctrl->mUnitClip.point.y == 0 &&
mUnitClip.extent.x == 1 && ctrl->mUnitClip.extent.y == 1)
{
// no unit clipping...
draw = true;
clipRect = updateRect;
}
else
{
// unit clipping...
RectI realClip = getRealClip(ctrl->mUnitClip,childPosition,ctrl->getExtent());
clipRect = updateRect;
draw = clipRect.intersect(realClip);
}
childClip.point = childPosition;
childClip.extent = ctrl->getExtent();
if (draw && childClip.intersect(clipRect))
{
dglSetClipRect(childClip);
glDisable(GL_CULL_FACE);
ctrl->onRender(childPosition, childClip);
}
}
}
// < pg animation
}THIS IS TGEA VERSIONvoid GuiControl::renderChildControls(Point2I offset, const RectI &updateRect)
{
// > pg animation
// offset is the upper-left corner of this control in screen coordinates
// updateRect is the intersection rectangle in screen coords of the control
// hierarchy. This can be set as the clip rectangle in most cases.
// move this into loop...
//RectI clipRect = updateRect;
iterator i;
for(i = begin(); i != end(); i++)
{
GuiControl *ctrl = static_cast<GuiControl *>(*i);
if (ctrl->mVisible)
{
ctrl->updateAnimation();
Point2I childPosition;
RectI childClip;
RectI clipRect;
bool draw;
childPosition = offset + ctrl->getPosition();
if (ctrl->mUnitClip.point.x == 0 && ctrl->mUnitClip.point.y == 0 &&
mUnitClip.extent.x == 1 && ctrl->mUnitClip.extent.y == 1)
{
// no unit clipping...
draw = true;
clipRect = updateRect;
}
else
{
// unit clipping...
RectI realClip = getRealClip(ctrl->mUnitClip,childPosition,ctrl->getExtent());
clipRect = updateRect;
draw = clipRect.intersect(realClip);
}
childClip.point = childPosition;
childClip.extent = ctrl->getExtent();
if (draw && childClip.intersect(clipRect))
{
GFX->setClipRect( childClip );
GFX->setStateBlock(mDefaultGuiSB); // <-----NEW
ctrl->onRender(childPosition, childClip);
}
}
}
// < pg animation
}8) We are going to comment out the old getCenter() Interface for the GUI control, you can delete it if you like but I am of the opinion to keep the old until you know for sure you'll never use it again, your call. We will be providing a new getCenter() in a little bit.
comment out
ConsoleMethod( GuiControl, getCenter,
Like this
// > pg animation
/*
ConsoleMethod( GuiControl, getCenter, const char*, 2, 2, " returns center of control, as space seperated ints")
{
char *retBuffer = Con::getReturnBuffer(64);
const Point2I pos = object->getPosition();
const Point2I ext = object->getExtent();
Point2I center(pos.x + ext.x/2, pos.y + ext.y/2);
dSprintf(retBuffer, 64, "%d %d", center.x, center.y);
return retBuffer;
}
*/
// < pg animation9) Now let's define all those interfaces we declared. at the end of the file
// > pg Animation
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
void GuiControl::cancelAllAnimations()
{
mColorAnim.cancel();
mPositionAnim.cancel();
mExtentAnim.cancel();
mBoundsAnim.cancel();
mAlphaAnim.cancel();
mUnitClipAnim.cancel();
updateAnimationState();
}
void GuiControl::updateAnimation()
{
updatePositionAnimation();
updateExtentAnimation();
updateBoundsAnimation();
updateAlphaAnimation();
updateColorAnimation();
updateUnitClipAnimation();
updateAnimationState();
// test if we can infact stop causing updates if not animating...
//if (mAnimating)
setUpdate();
}
void GuiControl::updateAnimationState()
{
bool actuallyAnimating =
mColorAnim.enabled ||
mPositionAnim.enabled ||
mExtentAnim.enabled ||
mUnitClipAnim.enabled ||
mBoundsAnim.enabled ||
mAlphaAnim.enabled ;
// if we have changed state...do callback
if (actuallyAnimating != mAnimating)
{
if (actuallyAnimating)
Con::executef(this,2,"onAnimationStarted"); // TGE
// Con::executef(this,"onAnimationStarted"); // TGEA
else
Con::executef(this,2,"onAnimationStopped"); //TGE
//// Con::executef(this," onAnimationStopped "); // TGEA
mAnimating = actuallyAnimating;
}
}
// CTOR
GuiControl::animation::animation()
{
enabled = false;
kplEnabled = false;
}
// DTOR
GuiControl::animation::~animation()
{
}
// Setup a key point list...
void GuiControl::animation::kplSetup(SimObject *input, S32 numDims, F32 startDelay, F32 duration, EaseF ease)
{
// read input into keypoint list and set kplDuration
kplReadInput(input,numDims);
// override duration?
if (duration < -1)
kplDuration = duration; // already in milliseconds...
// ok we MUST have at least two keypoints or there is nothing to do...
// and we only handle dimensions 1..3
if (keyPointList.size() < 2 || kplNumDimensions < 0 || kplNumDimensions > 3)
{
keyPointList.clear();
kplEnabled = false;
return;
}
// OK apply the ease to the time values themselves...
// NOT supporing any extrapolation, so we clamp
int i;
for (i=0; i<keyPointList.size(); i++)
keyPointList[i].w = ease.getUnitValue(keyPointList[i].w,true);
// but no matter what...first timeoint is zero and last is one...
// this should be true anyway after the above, but let's force it
keyPointList[0].w = 0.0f;
keyPointList[keyPointList.size()-1].w = 1.0f;
// ok setup for first segment....
kplEnabled = true;
kplEase = ease;
// ok setup the first segment...
kplIndex = 0; // we always interp from kplIndex-1 to kplIndex, start at
// zero and the "next" call below will advance...
kplStartTime = Platform::getVirtualMilliseconds() + startDelay;
// ok setup next, meaning first...
kplSetupNext();
}
bool GuiControl::animation::kplSetupNext()
{
// goto next segment...
kplIndex++;
// are we out of segments? return false if so
if (kplIndex >= keyPointList.size())
{
kplEnabled = false;
}
else
{
// figure out the duration and start/end of first bit...
F32 start = keyPointList[kplIndex-1].w;
F32 end = keyPointList[kplIndex].w;
F32 duration = (end - start) * kplDuration;
F32 startDelay = 0.0; // start time is corrected below...
// setup data for first segment..
if (kplNumDimensions == 1)
setupF(keyPointList[kplIndex-1].x, keyPointList[kplIndex].x,startDelay,duration,kplEase,start,end);
else if (kplNumDimensions == 2)
setup2F(keyPointList[kplIndex-1].x, keyPointList[kplIndex-1].y, keyPointList[kplIndex].x,keyPointList[kplIndex].y,startDelay,duration,kplEase,start,end);
else
setup3F(keyPointList[kplIndex-1].x, keyPointList[kplIndex-1].y, keyPointList[kplIndex-1].z, keyPointList[kplIndex].x,keyPointList[kplIndex].y,keyPointList[kplIndex].z,startDelay,duration,kplEase,start,end);
// correct start time to be more accurate and to reflect the start delay...
startTime = kplStartTime + (start * kplDuration);
}
return kplEnabled;
}
void GuiControl::animation::kplReadInput(SimObject *input, S32 numDims)
{
kplNumDimensions = numDims;
keyPointList.clear();
AbstractClassRep::FieldList list = input->getFieldList();
// keypoint list is in seconds but we renormalize...
// assume a duration of 1.0 unless specified in time[size-1] or as input.duration
kplDuration = 1.0f;
// let's see if there is a .duration override
F32 overrideDuration = -1.0f;
const char *durValue = input->getDataField(StringTable->insert("duration"),NULL);
if (durValue != NULL && durValue[0] != '')
overrideDuration = dAtof(durValue);
// should we be reading time values (or generating them?)
const char *firstTime = input->getDataField("time","0");
bool doReadTime = (firstTime != NULL && firstTime[0] != '');
// read the times and keypoints from the given input object...
int i;
for (i=0; i<10000; i++)
{
// read values till we hit the end...
static char indexBuf[128];
dSprintf(indexBuf,128,"%d",i);
const char *keyValue = input->getDataField("value",indexBuf);
if (keyValue == NULL || keyValue[0] == '')
break;
Point4F value(0,0,0,0);
if (kplNumDimensions == 1)
dSscanf(keyValue,"%g",&value.x);
else if (kplNumDimensions == 2)
dSscanf(keyValue,"%g %g",&value.x,&value.y);
else
dSscanf(keyValue,"%g %g %g",&value.x,&value.y,&value.z);
if (doReadTime)
{
const char *keyTime = input->getDataField("time",indexBuf);
if (keyTime == NULL || keyTime[0] == '')
{
// if we ever get a missing piece...we drop back to
// as if we had none...
Con::errorf("GuiControl::animation::kplReadInput(): missing time[%d], ignoring all time[] values",i);
doReadTime = false;
}
else
{
value.w = dAtof(keyTime);
}
}
keyPointList.push_back(value);
}
// we need 2 or more
S32 size = keyPointList.size();
if (size < 2)
{
Con::errorf("GuiControl::animation::kplReadInput(): less than 2 points in KeyPointList");
keyPointList.clear();
return;
}
// if we read given times...then we must renormalize based on that...
if (doReadTime)
{
F32 max = keyPointList[size-1].w;
for (i=0; i<size; i++)
keyPointList[i].w /= max;
// duration comes from time[size-1]
kplDuration = max;
}
else
{
// if we did not read time then fill in default values...
F32 max = size-1;
for (i=0; i<size; i++)
keyPointList[i].w = (F32)(i) / max;
// duration stays at default of 1.0...unless overridden
}
// did we have an override? ok then use that...
if (overrideDuration > 0)
kplDuration = overrideDuration;
// ok we want this in milliseconds, not seconds
kplDuration *= 1000;
}
// NOTE: in the re-worked animation system, we animate by "center" (not by "position" meaning upper left)
void GuiControl::animation::setup(F32 startDelay, F32 dur, EaseF easeData, F32 start, F32 end)
{
enabled = true;
ease = easeData;
startTime = Platform::getVirtualMilliseconds() + startDelay;
duration = dur;
interpStart = start;
interpEnd = end;
}
void GuiControl::animation::setup(F32 dur, F32 start, F32 end)
{
startTime = Platform::getVirtualMilliseconds();
duration = dur;
interpStart = start;
interpEnd = end;
}
void GuiControl::animation::setup(F32 startDelay, F32 dur, EaseF easeData)
{
setup(startDelay,dur,easeData,0.0f,1.0f);
}
void GuiControl::animation::setupF(F32 oldVal, F32 newVal, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
setup(startDelay, duration, ease,start,end);
currValue[0] = startValue[0] = oldVal;
deltaValue[0] = newVal - oldVal;
}
void GuiControl::animation::setup2F(F32 oldX, F32 oldY, F32 newX, F32 newY, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
setup(startDelay, duration, ease,start,end);
currValue[0] = startValue[0] = oldX;
currValue[1] = startValue[1] = oldY;
deltaValue[0] = newX - oldX;
deltaValue[1] = newY - oldY;
}
void GuiControl::animation::setup2I(S32 oldX, S32 oldY, S32 newX, S32 newY, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
setup(startDelay, duration, ease, start,end);
currValue[0] = startValue[0] = (F32)oldX;
currValue[1] = startValue[1] = (F32)oldY;
deltaValue[0] = (F32)newX - oldX;
deltaValue[1] = (F32)newY - oldY;
}
void GuiControl::animation::setup3F(F32 oldX, F32 oldY, F32 oldZ, F32 newX, F32 newY, F32 newZ, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
setup(startDelay, duration, ease,start, end);
currValue[0] = startValue[0] = oldX;
currValue[1] = startValue[1] = oldY;
currValue[2] = startValue[2] = oldZ;
deltaValue[0] = newX - oldX;
deltaValue[1] = newY - oldY;
deltaValue[2] = newZ - oldZ;
}
void GuiControl::animation::setup4F(F32 oldX, F32 oldY, F32 oldZ, F32 oldW, F32 newX, F32 newY, F32 newZ, F32 newW, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
setup(startDelay, duration, ease,start, end);
currValue[0] = startValue[0] = oldX;
currValue[1] = startValue[1] = oldY;
currValue[2] = startValue[2] = oldZ;
currValue[3] = startValue[3] = oldW;
deltaValue[0] = newX - oldX;
deltaValue[1] = newY - oldY;
deltaValue[2] = newZ - oldZ;
deltaValue[3] = newW - oldW;
}
bool GuiControl::animation::interp(int numDims)
{
if (!enabled)
return false;
// interp the given # of dimensions...
U32 now = Platform::getVirtualMilliseconds();
// wait for scheduled start time
if(now < startTime)
return false;
// was kpl enabled when we came in
bool doKPL = kplEnabled;
// calculate elapsed time and distance traveled
F32 elapsedTime = now - startTime;
// watch for end of animation
if(elapsedTime >= duration)
{
if (kplEnabled)
{
// ok we need to move to the next segment
if (kplSetupNext())
{
// ok we have setup the next segment...move on...
// we do this recursively incase we have to even skip
// past a few segments to find the right one...
return interp(numDims);
}
else
{
// there is no next...kpl is already disabled just
// finish out this last segment by dropping through...
enabled = false;
elapsedTime = duration;
}
}
else
{
enabled = false;
elapsedTime = duration;
}
}
// do the interpolation
if (doKPL)
{
// for each individual segment...we always do linear interpolation
// the ease has been calculated accross the whole segment...
int i;
for (i=0; i<numDims; i++)
currValue[i] = mLinearTween(elapsedTime,startValue[i], deltaValue[i], duration);
}
else
{
int i;
for (i=0; i<numDims; i++)
currValue[i] = ease.getValue(elapsedTime,startValue[i], deltaValue[i], duration);
}
// we DID interp something...
return true;
}
void GuiControl::setCenter(const Point2F ¢er)
{
Point2I newPos((S32)(center.x - getHalfWidth()),(S32)(center.y - getHalfHeight()));
resize(newPos, mBounds.extent);
}
void GuiControl::setCenterAndSize(const Point2F ¢er, const Point2I &extent)
{
Point2I newPos((S32)(center.x - (F32)extent.x/2.0f),(S32)(center.y - (F32)extent.y/2.0f));
resize(newPos, extent);
}
void GuiControl::animatePosition(Point2F newPos, F32 startDelay, F32 duration, EaseF ease)
{
Point2F center = getCenter();
mPositionAnim.setup2F(center.x, center.y, newPos.x,newPos.y,startDelay,duration,ease);
}
void GuiControl::animatePositionKeyPointList(SimObject *input, F32 startDelay, F32 duration, EaseF ease )
{
mPositionAnim.kplSetup(input,2,startDelay,duration,ease);
}
void GuiControl::animateExtent(Point2I newExt, F32 startDelay, F32 duration, EaseF ease)
{
mExtentAnim.setup2I(mBounds.extent.x, mBounds.extent.y, newExt.x,newExt.y,startDelay,duration,ease);
}
void GuiControl::setAlpha(F32 newAlpha)
{
mAlpha = newAlpha;
setUpdate();
mAlphaAnim.enabled = false;
// setting alpha AUTOMATICALLY set's alpha of children so that
// setting alpha is analogous to setting position in that it
// propogates down to the children.
iterator i;
for(i = begin(); i != end(); i++)
{
GuiControl *ctrl = static_cast<GuiControl *>(*i);
ctrl->setAlpha(newAlpha);
}
}
void GuiControl::setColor(ColorI color)
{
// setting the color simply sets the color...it does not affect
// the children's color, but it might cancel any animation in progress...
mColor = color;
setUpdate();
mColorAnim.enabled = false;
}
void GuiControl::animateAlpha(F32 newAlpha, F32 startDelay, F32 duration, EaseF ease)
{
mAlphaAnim.setupF(mAlpha, newAlpha, startDelay,duration,ease);
// update children
iterator i;
for(i = begin(); i != end(); i++)
{
GuiControl *ctrl = static_cast<GuiControl *>(*i);
ctrl->animateAlpha(newAlpha, startDelay, duration,ease);
}
}
void GuiControl::animateColor(ColorI newColor, F32 startDelay, F32 duration, EaseF ease)
{
mColorAnim.setup3F(mColor.red,mColor.green,mColor.blue,newColor.red,newColor.green,newColor.blue,startDelay,duration,ease);
}
void GuiControl::updateColorAnimation()
{
if (!mColorAnim.interp(3))
return;
ColorI newColor;
mColorAnim.getColor(newColor);
// apply changes
mColor = newColor;
}
void GuiControl::animateUnitClip(RectF newClip, F32 startDelay, F32 duration, EaseF ease)
{
mUnitClipAnim.setup4F(
mUnitClip.point.x,mUnitClip.point.y,mUnitClip.extent.x,mUnitClip.extent.y,
newClip.point.x,newClip.point.y,newClip.extent.x,newClip.extent.y,
startDelay,duration,ease);
}
void GuiControl::updateUnitClipAnimation()
{
if (!mUnitClipAnim.interp(4))
return;
RectF newClip;
mUnitClipAnim.getRectF(newClip);
// apply changes
mUnitClip = newClip;
}
void GuiControl::setUnitClip(RectF clip)
{
// setting the clip simply sets the clip...it does not affect
// the children's clip, but it might cancel any animation in progress...
mUnitClip = clip;
setUpdate();
mUnitClipAnim.enabled = false;
}
void GuiControl::animateBounds(RectF newBounds, F32 startDelay, F32 duration, EaseF ease)
{
mBoundsAnim.setup4F(
mBounds.point.x,mBounds.point.y,mBounds.extent.x,mBounds.extent.y,
newBounds.point.x,newBounds.point.y,newBounds.extent.x,newBounds.extent.y,
startDelay,duration,ease);
}
void GuiControl::updateBoundsAnimation()
{
if (!mBoundsAnim.interp(4))
return;
RectF newBounds;
mBoundsAnim.getRectF(newBounds);
// apply changes
Point2I pos(newBounds.point.x,newBounds.point.y);
Point2I extent(newBounds.extent.x,newBounds.extent.y);
resize(pos, extent);
}
void GuiControl::updatePositionAnimation()
{
if (!mPositionAnim.interp(2))
return;
Point2F newCenter;
mPositionAnim.getPoint2F(newCenter);
setCenter(newCenter);
}
void GuiControl::updateExtentAnimation()
{
if (!mExtentAnim.interp(2))
return;
Point2I newExt;
mExtentAnim.getPoint2I(newExt);
// apply changes
setCenterAndSize(getCenter(), newExt);
}
void GuiControl::updateAlphaAnimation()
{
if (!mAlphaAnim.interp(1))
return;
F32 newAlpha;
mAlphaAnim.getFloat(newAlpha);
// apply changes
mAlpha = newAlpha;
}
void GuiControl::setPassThru(bool passThru)
{
mPassThru = passThru;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
ConsoleMethod(GuiControl, getPassThru, bool, 2, 2, "ctrl.getPassThru()")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
return ctrl->getPassThru();
}
ConsoleMethod(GuiControl, setPassThru, void, 3, 3, "ctrl.setPassThru(passThru)")
{
argc;
GuiControl* ctrl = static_cast<GuiControl*>(object);
ctrl->setPassThru(dAtob(argv[2]));
}
ConsoleMethod(GuiControl, getCenter, const char *, 2, 2, "ctrl.getCenter()")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
char *retBuffer = Con::getReturnBuffer(64);
const Point2F &pos = ctrl->getCenter();
dSprintf(retBuffer, 64, "%f %f", pos.x, pos.y);
return retBuffer;
}
ConsoleMethod(GuiControl, setColor, void, 5, 5, "ctrl.setColor(r,g,b)")
{
argc;
GuiControl* ctrl = static_cast<GuiControl*>(object);
ColorI color(dAtof(argv[2]),dAtof(argv[3]),dAtof(argv[4]));
ctrl->setColor(color);
}
ConsoleMethod(GuiControl, setUnitClip, void, 6, 6, "ctrl.setUnitClip(x,y,w,h)")
{
argc;
GuiControl* ctrl = static_cast<GuiControl*>(object);
RectF clip(dAtof(argv[2]),dAtof(argv[3]),dAtof(argv[4]),dAtof(argv[5]));
ctrl->setUnitClip(clip);
}
ConsoleMethod(GuiControl, setAlpha, void, 3, 3, "ctrl.setAlpha(value)")
{
argc;
GuiControl* ctrl = static_cast<GuiControl*>(object);
ctrl->setAlpha(dAtof(argv[2]));
}
ConsoleMethod(GuiControl, getAlpha, F32, 2, 2, "ctrl.getAlpha()")
{
argc;
GuiControl* ctrl = static_cast<GuiControl*>(object);
return ctrl->getAlpha();
}
ConsoleMethod(GuiControl, isAnimating, bool, 2, 2, "ctrl.isAnimating()")
{
argc;
GuiControl* ctrl = static_cast<GuiControl*>(object);
return ctrl->isAnimating();
}
// internally this animation refers to "position", but logically what is
// happening is we are animating the CENTER...therefore it is renamed
// when exposed to script
ConsoleMethod(GuiControl, animateCenter, void, 6,7,"ctrl.animateCenter(x, y, delay, duration [,ease])")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
Point2F newPos(dAtof(argv[2]), dAtof(argv[3]));
EaseF ease;
if (argc > 6)
ease.set(argv[6]);
ctrl->animatePosition(newPos, dAtof(argv[4]), dAtof(argv[5]), ease);
}
ConsoleMethod(GuiControl, animateExtent, void, 6,7,"ctrl.animateExtent(w, h, delay, duration [,ease]))")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
Point2I newExt(dAtoi(argv[2]), dAtoi(argv[3]));
EaseF ease;
if (argc > 6)
ease.set(argv[6]);
ctrl->animateExtent(newExt, dAtof(argv[4]), dAtof(argv[5]), ease);
}
ConsoleMethod(GuiControl, animateAlpha, void, 5,6,"ctrl.animateAlpha(alpha, delay, duration [,ease]))")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
EaseF ease;
if (argc > 5)
ease.set(argv[5]);
ctrl->animateAlpha(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]), ease);
}
ConsoleMethod(GuiControl, animateColor, void, 7,8,"ctrl.animateColor(r, g, b, delay, duration [,ease]))")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
ColorI newColor(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]));
EaseF ease;
if (argc > 7)
ease.set(argv[7]);
ctrl->animateColor(newColor, dAtof(argv[5]), dAtof(argv[6]), ease);
}
ConsoleMethod(GuiControl, animateUnitClipping, void, 8,9,"ctrl.animateUnitClipping(x, y, w, h, delay, duration [,ease]))")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
RectF newClip(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]), dAtof(argv[5]));
EaseF ease;
if (argc > 8)
ease.set(argv[8]);
ctrl->animateUnitClip(newClip, dAtof(argv[6]), dAtof(argv[7]), ease);
}
ConsoleMethod(GuiControl, animateBounds, void, 8,9,"ctrl.animateBounds(x, y, w, h, delay, duration [,ease]))")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
RectF newBounds(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]), dAtof(argv[5]));
EaseF ease;
if (argc > 8)
ease.set(argv[8]);
ctrl->animateBounds(newBounds, dAtof(argv[6]), dAtof(argv[7]), ease);
}
ConsoleMethod(GuiControl, cancelAllAnimations, void, 2,2,"ctrl.cancelAllAnimations()")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
ctrl->cancelAllAnimations();
}
// new experimental method to animate the center along a keypoint list...
ConsoleMethod(GuiControl, animateCenterKeyPointList, void, 4,6,"ctrl.animateCenterKeyPointList(keyobj, delay [,duration] [,ease])")
{
GuiControl *ctrl = static_cast<GuiControl*>(object);
F32 delay = dAtof(argv[3]);
EaseF ease;
F32 duration = -1.0f;
if (argc > 4)
duration = dAtof(argv[4]);
if (argc > 5)
ease.set(argv[5]);
SimObject *obj;
if (!Sim::findObject(argv[2],obj))
{
Con::errorf("GuiControl.animateCenterKeyPointList() - cannot find key object: %s",argv[2]);
return;
}
ctrl->animatePositionKeyPointList(obj, delay, duration, ease);
}
ConsoleMethod( GuiControl, getRoot, S32, 2, 2, "()"){
GuiCanvas* canvas = object->getRoot();
return canvas ? canvas->getId() : -1;
}
// < pg AnimationNOTE watch out for the Con::execute functions the TGE version take an additional parameter. I have commented out a TGEA equivalent below the TGE versionSpecifically
Con::executef(this,2,"onAnimationStarted"); // TGE
// Con::executef(this,"onAnimationStarted"); // TGEA
ALSO Comment out the old ConsoleMethod GetRoot
There you have it now you can do some amazing things with GUI. Here is the necessary script code to get it working.
%ctrl.animateColor(%r, %g, %b, %delay, %duration, %ease); // animate to a new color %ctrl.animateAlpha(%alpha, %delay, %duration, %ease); // animate to new alpha
%ctrl.animateExtent(%w, %h, %delay, %duration, %ease); // animate to new extent while maintaining top/left position
%ctrl.animateCenter(%x, %y, %delay, %duration, %ease); // animate center of GUI to a new position
%ctrl.animateBounds(%x, %y, %w, %h, %delay, %duration, %ease); // animates to a new recatangle ( equivallent of animateCenter + animateExtent)
%ctrl.animateUnitClipping(%x, %y, %w, %h, %delay, %duration, %ease); // animates what clip area gets rendered, great for wipes.
%ctrl.cancelAllAnimations(); // stop all animations
%ctrl.getCenter();
%ctrl.getGlobalCenter(); // get center related to root GUI
%ctrl.setAlpha(%alpha);
%ctrl.getAlpha();
%ctrl.setColor(%r, %g, %b);
%ctrl.getColor();
%ctrl.getRoot();
%ctrl.setUnitClip(x,y,w,h); // default 0, 0, 1, 1
%ctrl.isAnimating(); // determines if an animation is in process
%ctrl.getPassThru();
%ctrl.setPassThru( %bool ); // allow mouse click to pass to parent
%ctrl.animateCenterKeyPointList(%keyobj, %delay [,%duration] [,%ease]);
// Takes a script object which has value[] and a time[] arrays to be used for animating . Duration and Ease can be added to the scriptObject or passed in optionally as additional parameters. If time[0] is not specified it will animate each value equally based on the duration.
new ScriptObject(TestKeyPoints)
{
duration = "5.0"; // seconds
ease = "1 3 "; // TypeEaseF ease for whole path...
value[0] = "999 100"; //x y center ot GUI
value[1] = "950 135";
value[2] = "900 195";
value[3] = "998 248";
value[4] = "945 320";
value[5] = "880 417";
value[6] = "821 501";
value[7] = "920 540";
value[8] = "1004 588";
time[0] = "0"; //in seconds from start of animation
time [1] = "2";
time [2] = "3";
time [3] = "7";
time [4] = "8";
time [5] = "13";
time [6] = "15";
time [7] = "16";
time [8] = "20";
};
%ctrl.animateCenterKeyPointList(TestKeyPoints,0);EXAMPLE APPLICATION
10) Here is an example of how you might incorporate the alpha and color into an existing control, the GuiMLTextColor.
guiMLTextCtrl.cc
TGE ONLY
in void GuiMLTextCtrl::drawAtomText(
after
dglDrawTextN(font, drawPoint + atom->style->shadowOffset, tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL);
}
// > pg guicolor
color.alpha = color.alpha * mAlpha;
F32 red = F32(color.red) * mColor.red / 255.0f;
F32 green = F32(color.green) * mColor.green / 255.0f;
F32 blue = F32(color.blue) * mColor.blue / 255.0f;
ColorI blendedColor(red,green,blue,color.alpha);
dglSetBitmapModulation(blendedColor);
// < pg guicolorTGEA ONLY
in void GuiMLTextCtrl::drawAtomText(
after
if(atom->style->shadowOffset.x || atom->style->shadowOffset.y)
{
ColorI shadowColor = atom->style->shadowColor;
shadowColor.alpha = (S32)(mAlpha * shadowColor.alpha);
drawer->setBitmapModulation(shadowColor);
drawer->drawTextN(font, drawPoint + atom->style->shadowOffset,
tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL);
}
// > pg guicolor
color.alpha = color.alpha * mAlpha;
F32 red = F32(color.red) * mColor.red / 255.0f;
F32 green = F32(color.green) * mColor.green / 255.0f;
F32 blue = F32(color.blue) * mColor.blue / 255.0f;
ColorI blendedColor(red,green,blue,color.alpha);
drawer->setBitmapModulation(blendedColor);
// < pg guicolor11) Finally add the GuiPlasticBitmapCtrl.h and GuiPlasticBitmapCtrl.cc into the gui/shiney folder. And Compile
This control can load single images or a dml then play thu the dml as an animation.
%ctrl.setDml(%dml); // pass a dml for animation or a filename for a single bitmap %ctrl.setFrame(%num); // sets the current frame of the animation %ctrl.setFPS(F32); // sets the interval between frames %ctrl.setValue(xAxis, yAxis); // set X/Y wrap offset %ctrl.play([stopAtEnd]); // plays animation, bool can be set to play once thru then stop %ctrl.stop(); // stop animation %ctrl.rewind(); // rewind animation %ctrl.isPlaying();// determines if dml animation is playing %ctrl.getFrames(); // gets current frame %ctrl.getTotalFrames();// get total frames per animation you can have up to 50 frames.
The GuiPlasticBitmapCtrl also has callbacks for mouse and animation events.
%ctrl.onStoppedAtEnd(); // called at end of animation if bool is set in .play() %ctrl.onMouseLeave(); %ctrl.onMouseEnter(); %ctrl.onMouseDragged(); %ctrl.onMouseMove(); %ctrl.onMouseDown(); %ctrl.onMouseUp();
You'll also find 2 additional fields in the GuiPlasticBitmapCtrl.
%ctrl.rotation %ctrl.delta rotationRotation will allow you rotate the GUI element X degrees from the normal. DeltaRotation will automatically update the rotation of the GUI element by X degrees/second.
Another great feature with the GuiPlasticBitmapCtrl is that if you make your images colored white you can blend and animate color with the animateColor and setColor functions.
PlasticBitmap Example TGE. This will make a PlasticBitmap on the current GUI name pg. I put some functionality into its mouseCallbacks but I also included some code so you can see how sweet this stuff can be.
12) place the star folder within you game's folder as a sibling of the data/server/client
13) In console (~) exec("YOURGAMENAME/star/star.cs");
14) Press the Star to start the PlasticBitmap Spinning and animating color.
15) pg.animateAlpha(0, 0, 500); watch the bitmap disappear;
16) pg.animateAlpha(1, 0, 500); watch the bitmap reappear
17) pg.animateUnitClipping(1, 1, 1, 1, 500, 500); wipes texture to bottom right
18) pg.animateUnitClipping(1, 1, 1, 1, 500, 500); restore clip area
19) pg.animateCenter(500, 200 , 0, 1500, "0 2"); //notice the ease in out on the move
20) pg.animateCenter(500,500 , 0, 1500, "2 10"); //notice the bounce
The Next Gem
That's all for the animated GUI resource. This resource is so large there really wasn't enough time or space to fully cover all the applications of this code. Later gems will give more applications for GUI resources.
The next gem will cover the isJugg variable and the AudioManager which allow us to have one script code base that works between TGE and TGEA. It also introduces the Plastic folder, a folder for useful code that you might share between games.
About the author
#2
06/12/2008 (9:34 am)
Really nice work. Thanks for sharing.
#3
06/13/2008 (8:35 am)
It's nice to see people helping out people while so many are focused on making a quick buck and keeping their methods a secret. Thanks for supporting the community in this way. :)
#4
mb - hey if I can figure out how to make a quick buck - or even a moderately quick buck - you will see a Paul shaped hole in the air where I used to be while I pursue that opportunity. :-) In fact we may try to package some of our more thrilling gems as "code packs" to be sold at the GG store.
Thank you for your kind words. They are certainly nice to hear. The reason we are even bothering with these gems (which do take quite a bit of effort to write - even when the tech they are based on is already written) is that the Torque community has done a lot for us over the years and we feel a need to pay that debt back. If anyone feels indebted to us in any way for these gems then the best way to "pay us back" would be to post resources of your own when you are able. Pay it forward.
06/16/2008 (7:00 am)
thanks all.mb - hey if I can figure out how to make a quick buck - or even a moderately quick buck - you will see a Paul shaped hole in the air where I used to be while I pursue that opportunity. :-) In fact we may try to package some of our more thrilling gems as "code packs" to be sold at the GG store.
Thank you for your kind words. They are certainly nice to hear. The reason we are even bothering with these gems (which do take quite a bit of effort to write - even when the tech they are based on is already written) is that the Torque community has done a lot for us over the years and we feel a need to pay that debt back. If anyone feels indebted to us in any way for these gems then the best way to "pay us back" would be to post resources of your own when you are able. Pay it forward.
#6
07/29/2008 (12:11 am)
I'm using this all over the place to make an interactive GUI, it is pretty fantastic and easily one of the best resources i've seen on GG. EXACTLY what I was looking for
#7
Now if only I knew how to mask gui controls! :)
Paul, Anthony and Plastic Games; thank you very much!
08/04/2008 (1:56 pm)
This is an awesome resource! It has at least doubled the usability of the existing GUI. Now if only I knew how to mask gui controls! :)
Paul, Anthony and Plastic Games; thank you very much!
#8
08/05/2008 (8:34 pm)
Vihn Tran - we are glad you like it. Did you also merge in the Ease gem? You are absolutely gonna love our next gem. We are posting Gem #35 soon on the Plastic Tweaker. The Tweaker makes working with animated GUIs even better. Later gems will talk about special enhancements that add features to the tweaker, such as an "Ease View Control" that lets you view your ease as a curve as you tweak it. Also a "Point Picker" and a "Rectangle Picker" for choosing locations and areas on screen. Good stuff!
#9
After implementing, I decided see how well the guiPlasticBitmapCtrl would work when several different controls were used in concert with one another and I hit a snag that I'm hoping has a simple fix.
I built a GUI that has the shape of a manilla folder. The GUI comprises of two pieces - the main piece being the 'folder' portion and a smaller piece that makes up the Tab of the folder. The idea is that starting out, the GUI will be hidden except for the Tab. Clicking on the Tab animates the GUI and 'pulls' the folder out, exposing it. The Folder portion of the GUI is the parent with the Tab portion being a child element.
Now, I have a main intro screen that is built as a GuiChunkedBitmapCtrl. When I add my Folder GUI to the main Canvas (Canvas.getContent.add(FolderGUI)), everything works like a champ!
However, when I add my folder to my PlayGui (Which is a GameTSCtrl), it looks as though the 'hot spots' of all the elements of the Folder GUI either get shifted or clipped.
To test this, I used the onMouseEnter and onMouseLeave callbacks to animate the colors of each element so I could test the extents of the skewing and it appears as though some sort of 'hot spot' clipping is happening, even though all the graphics appear in their normal places at their normal scales. I thought perhaps that the applyFilterToChildren field of GameTSCtrl might be the culprit but setting this to false does not seem to work.
Any ideas as to what might be happening?
Thanks again to Paul and Plastic Games!
08/15/2008 (11:32 pm)
This was a hugely beneficial resource to post, Paul!After implementing, I decided see how well the guiPlasticBitmapCtrl would work when several different controls were used in concert with one another and I hit a snag that I'm hoping has a simple fix.
I built a GUI that has the shape of a manilla folder. The GUI comprises of two pieces - the main piece being the 'folder' portion and a smaller piece that makes up the Tab of the folder. The idea is that starting out, the GUI will be hidden except for the Tab. Clicking on the Tab animates the GUI and 'pulls' the folder out, exposing it. The Folder portion of the GUI is the parent with the Tab portion being a child element.
Now, I have a main intro screen that is built as a GuiChunkedBitmapCtrl. When I add my Folder GUI to the main Canvas (Canvas.getContent.add(FolderGUI)), everything works like a champ!
However, when I add my folder to my PlayGui (Which is a GameTSCtrl), it looks as though the 'hot spots' of all the elements of the Folder GUI either get shifted or clipped.
To test this, I used the onMouseEnter and onMouseLeave callbacks to animate the colors of each element so I could test the extents of the skewing and it appears as though some sort of 'hot spot' clipping is happening, even though all the graphics appear in their normal places at their normal scales. I thought perhaps that the applyFilterToChildren field of GameTSCtrl might be the culprit but setting this to false does not seem to work.
Any ideas as to what might be happening?
Thanks again to Paul and Plastic Games!
#10
08/17/2008 (2:39 am)
The class definition it guicontrols.h (TGE 1.5.2) has mBounds as a public member, not private or protected. Anyone else notice this? I suppose this shouldn't affect the rest of the changes.
#11
@Joseph R: I did notice this. I have not had any issues with it so far and it looks like this change was made in the 1.5.2 baseline so I just left it as is. :)
08/18/2008 (8:38 am)
I figured out what the issue was. Turns out there was another invisible GUI element whose area was overlapping my Folder GUI. So, all is well that ends well! So, if for some reason you find areas on your GUI's that don't seem to work, check to make sure you don't have any overlapping GUI extents. :)@Joseph R: I did notice this. I have not had any issues with it so far and it looks like this change was made in the 1.5.2 baseline so I just left it as is. :)
#12
Thank you, Paul and Anthony!
08/28/2008 (3:09 am)
Outstanding! A painless merge. This will really open up some new possibilities for GUIs! I plan to whip up a few GUI-based minigames with this puppy... Thank you, Paul and Anthony!
#13
TIA!
08/28/2008 (9:01 pm)
Quick question... how can I use the GuiPlasticBitmapCtrl to display a bitmap without modifying the color? I've got a color bitmap that I don't want to alter, but the control is overriding the bitmap's RGB. I only want to animate its position.TIA!
#14
08/29/2008 (12:40 am)
Maybe you could use a plain GuiBitmapCtrl for that. You can still animate that if I'm right.
#15
I figured it out -- just set the Color field's alpha to 0. So, in the object definition, color="255 255 255 0".
I'm considering getting a tattoo on my forehead that reads "Insert Brain Here". Whaddya think?
I didn't realize the animation methods could be applied to the other GUI classes, though! I'll have to check that out. Bouncing splash screens might be pretty cool.
By the way, I whipped up a classic sliding 15-puzzle in about 10 minutes with this resource. Freakin' awesome. Next challenge: drag-and-drop jigsaw puzzles...
08/29/2008 (1:59 am)
Hi Konrad,I figured it out -- just set the Color field's alpha to 0. So, in the object definition, color="255 255 255 0".
I'm considering getting a tattoo on my forehead that reads "Insert Brain Here". Whaddya think?
I didn't realize the animation methods could be applied to the other GUI classes, though! I'll have to check that out. Bouncing splash screens might be pretty cool.
By the way, I whipped up a classic sliding 15-puzzle in about 10 minutes with this resource. Freakin' awesome. Next challenge: drag-and-drop jigsaw puzzles...
#16
That's a really cool idea! I've so far treated this resource as eyecandy, but it's a huge leap in minigames!
08/29/2008 (2:29 am)
Hi John,That's a really cool idea! I've so far treated this resource as eyecandy, but it's a huge leap in minigames!
#17
I implemented everything, however, when i do animateAlpha, nothing happens. It just stays opaque.
Any ideas what am i missing (using tge 1.5.2, btw)
Regards
10/24/2008 (3:59 am)
Hi, I implemented everything, however, when i do animateAlpha, nothing happens. It just stays opaque.
Any ideas what am i missing (using tge 1.5.2, btw)
Regards
#19
1>c:\torque\tgea_1_8_0\engine\source\gui\core\guicontrol.cpp(185) : error C2039: 'setCullMode' : is not a member of 'GFXDevice'
After I added the GuiPlasticBitmapCtrl.h and GuiPlasticBitmapCtrl.cc, the same kind of problems happened:
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(179) : error C2661: 'GFXTexHandle::set' : no overloaded function takes 2 arguments
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(240) : warning C4832: token '.' is illegal after UDT 'ResourceManager'
1> c:\torque\tgea_1_8_0\engine\source\core\resourcemanager.h(20) : see declaration of 'ResourceManager'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(240) : error C2039: 'openStream' : is not a member of 'ResourceManager'
1> c:\torque\tgea_1_8_0\engine\source\core\resourcemanager.h(20) : see declaration of 'ResourceManager'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(250) : error C2143: syntax error : missing ';' before '->'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(250) : error C2143: syntax error : missing ';' before '->'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(387) : error C2039: 'setBaseRenderState' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(436) : error C2039: 'setCullMode' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(437) : error C2039: 'setLightingEnable' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(438) : error C2039: 'setAlphaBlendEnable' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(439) : error C2039: 'setSrcBlend' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(440) : error C2039: 'setDestBlend' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(441) : error C2039: 'setTextureStageColorOp' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(442) : error C2039: 'setTextureStageColorOp' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(448) : error C2039: 'setAlphaBlendEnable' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\torque\tgea_1_8_0\engine\source\gui\core\guicontrol.cpp(185) : error C2039: 'setCullMode' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
can anybody tell me how to solve these problems? thanks
01/03/2009 (11:41 pm)
I just have one error after building, it is:1>c:\torque\tgea_1_8_0\engine\source\gui\core\guicontrol.cpp(185) : error C2039: 'setCullMode' : is not a member of 'GFXDevice'
After I added the GuiPlasticBitmapCtrl.h and GuiPlasticBitmapCtrl.cc, the same kind of problems happened:
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(179) : error C2661: 'GFXTexHandle::set' : no overloaded function takes 2 arguments
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(240) : warning C4832: token '.' is illegal after UDT 'ResourceManager'
1> c:\torque\tgea_1_8_0\engine\source\core\resourcemanager.h(20) : see declaration of 'ResourceManager'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(240) : error C2039: 'openStream' : is not a member of 'ResourceManager'
1> c:\torque\tgea_1_8_0\engine\source\core\resourcemanager.h(20) : see declaration of 'ResourceManager'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(250) : error C2143: syntax error : missing ';' before '->'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(250) : error C2143: syntax error : missing ';' before '->'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(387) : error C2039: 'setBaseRenderState' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(436) : error C2039: 'setCullMode' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(437) : error C2039: 'setLightingEnable' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(438) : error C2039: 'setAlphaBlendEnable' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(439) : error C2039: 'setSrcBlend' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(440) : error C2039: 'setDestBlend' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(441) : error C2039: 'setTextureStageColorOp' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(442) : error C2039: 'setTextureStageColorOp' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\users\iml\desktop\pg04_gui\pg04_gui\tgea\guiplasticbitmapctrl.cc(448) : error C2039: 'setAlphaBlendEnable' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
1>c:\torque\tgea_1_8_0\engine\source\gui\core\guicontrol.cpp(185) : error C2039: 'setCullMode' : is not a member of 'GFXDevice'
1> c:\torque\tgea_1_8_0\engine\source\gfx\gfxdevice.h(163) : see declaration of 'GFXDevice'
can anybody tell me how to solve these problems? thanks
#20
@Wenyu
I have looked a bit closer at this. It looks like it might be that TGEA 1.8 has changed their APIs slightly. This means our code does not work as listed in the resource against TGEA 1.8. I have not confirmed this, but I suspect this is the problem. WE developed this resource against an earlier TGEA.
If this is true then there are likely quite a few people who are having this problem and possibly we can all help each other out here. Plastic Games is about to start on some contract work that will be using TGEA. We might port our technology to the TGEA 1.8 codebase, which will force us to deal with these same issues. I do not know how long after that we can work the changes into the Gems, so if there are any other community members who are following our Gems and are solving these issues for 1.8, please share! We will be happy to incorporate any updates into the .zip files for the Gem.
01/05/2009 (9:46 am)
@Jendrick - is it possible you did not correctly merge something alpha-related? Much of the animation work is done at the GuiControl level, but ALPHA is handled by each Gui control that extends GuiControl. In otherwords, unfortunately, to get ALPHA support in a particular control, like the ML text control say, you have to have merged in the appropriate code for that control. I hope that makes some sense.@Wenyu
I have looked a bit closer at this. It looks like it might be that TGEA 1.8 has changed their APIs slightly. This means our code does not work as listed in the resource against TGEA 1.8. I have not confirmed this, but I suspect this is the problem. WE developed this resource against an earlier TGEA.
If this is true then there are likely quite a few people who are having this problem and possibly we can all help each other out here. Plastic Games is about to start on some contract work that will be using TGEA. We might port our technology to the TGEA 1.8 codebase, which will force us to deal with these same issues. I do not know how long after that we can work the changes into the Gems, so if there are any other community members who are following our Gems and are solving these issues for 1.8, please share! We will be happy to incorporate any updates into the .zip files for the Gem.
Torque Owner Kyle Cook