Graal Forums

Graal Forums (https://forums.graalonline.com/forums/index.php)
-   Code Gallery (https://forums.graalonline.com/forums/forumdisplay.php?f=179)
-   -   func_tween and func_easing (https://forums.graalonline.com/forums/showthread.php?t=134266548)

cbk1994 05-31-2012 04:46 AM

func_tween and func_easing
 
If you've used jQuery for simple animations, you know it's easy to make simple tweens look nice by using the variety of available easing options.

Whenever I try to do some nice effect in Graal, I end up writing the tweening code from scratch for that particular case, which is ridiculous.

Tweening is pretty easy, but to make it look nice, you need some form of easing. Animations that start and stop abruptly look bad; it's usually better for there to be some kind of slow stop, fast start, or even bouncing :).

I ported the wonderful jQuery easing plugin (BSD license) to GScript.

I present to you: func_tween and func_easing.

func_tween is just a couple functions that makes it easy to change the property of an object over time. You can use any object; images, GUIs, players, NPCs, etc.

To use it, all you do is this:

PHP Code:

temp.callback = function() {
  
player.chat "Done!";
};

this.tween(Ball"y"GraalControl.height Ball.height40temp.easingtemp.callback); 

The parameters of tween are:
  • object - the object to tween
  • property - the property to tween
  • targetValue - the target value at the end of the animation
  • frames - the number of frames the animation should last (20 frames per second)
  • easing - the name of the easing function to use (optional, defaults to "swing")
  • callback - a callback function, called when finished (optional)

You can use any easing available from the easing plugin, plus "linear" (normal, abrupt start and stop), and "swing" (the default, soft stop).

You can see all of the available easings, and try them out, on this site (probably requires a non-ancient browser).

Here's what they look like in game. The video is pretty choppy (sorry), but it looks very smooth in game.



My favorite is probably at 1:38.

Here's the two classes you'll need:

func_tween
PHP Code:

function onCreated() {
  
this.join("func_easing");
}

//#CLIENTSIDE
function tween(temp.objtemp.propertytemp.targetValuetemp.framestemp.easingtemp.callback) {
  if (
temp.easing == null) { // default easing
    
temp.easing "swing";
  }
  
  
temp.startValue temp.obj.(@ temp.property) + 0;
  
this.onTweenStep(temp.objtemp.propertytemp.startValuetemp.targetValuetemp.framestemp.framestemp.easingtemp.callback);
}

function 
onTweenStep(temp.objtemp.propertytemp.startValuetemp.targetValuetemp.framesRemainingtemp.totalFramestemp.easingtemp.callback) {
  if (
temp.framesRemaining <= 0) {
    
temp.obj.(@ temp.property) = temp.targetValue;
    
    if (
temp.callback != null) {
      
temp.callback(temp.obj);
    }
    
    return;
  }
  
  
temp.= (temp.totalFrames temp.framesRemaining);
  
temp.state temp.temp.totalFrames;
  
  
temp.pos this.ease(temp.easingtemp.statetemp.n01temp.totalFrames);
  
temp.obj.(@ temp.property) = temp.startValue + ((temp.targetValue temp.startValue) * temp.pos);
  
  
this.scheduleEvent(0.05"onTweenStep"temp.objtemp.propertytemp.startValuetemp.targetValuetemp.framesRemaining 1temp.totalFramestemp.easingtemp.callback);


func_easing
PHP Code:

/*
    This is essentially a port of the jQuery easing plugin to
    GScript
    
    jQuery easing plugin:
    http://gsgd.co.uk/sandbox/jquery/easing/
*/

//#CLIENTSIDE
function easeInQuad(xtbcd) {
  return 
* (/= d) * b;
};

function 
easeOutQuad(xtbcd) {
  return -
* (/= d) * (2) + b;
};

function 
easeInOutQuad(xtbcd) {
  if ((
/= 2) < 1) return b;
  return -
* ((--t) * (2) - 1) + b;
};

function 
easeInCubic(xtbcd) {
  return 
* (/= d) * b;
};

function 
easeOutCubic(xtbcd) {
  return 
* ((1) * 1) + b;
};

function 
easeInOutCubic(xtbcd) {
  if ((
/= 2) < 1) return b;
  return 
* ((-= 2) * 2) + b;
};

function 
easeInQuart(xtbcd) {
  return 
* (/= d) * b;
};

function 
easeOutQuart(xtbcd) {
  return -
* ((1) * 1) + b;
};

function 
easeInOutQuart(xtbcd) {
  if ((
/= 2) < 1) return b;
  return -
* ((-= 2) * 2) + b;
};

function 
easeInQuint(xtbcd) {
  return 
* (/= d) * b;
};

function 
easeOutQuint(xtbcd) {
  return 
* ((1) * 1) + b;
};

function 
easeInOutQuint(xtbcd) {
  if ((
/= 2) < 1) return b;
  return 
* ((-= 2) * 2) + b;
};

function 
easeInSine(xtbcd) {
  return -
cos(* (pi 2)) + b;
};

function 
easeOutSine(xtbcd) {
  return 
sin(* (pi 2)) + b;
};

function 
easeInOutSine(xtbcd) {
  return -
* (cos(pi d) - 1) + b;
};

function 
easeInExpo(xtbcd) {
  return (
== 0) ? ^ (10 * (1)) + b;
};

function 
easeOutExpo(xtbcd) {
  return (
== d) ? * (- (^ (-10 d)) + 1) + b;
};

function 
easeInOutExpo(xtbcd) {
  if (
== 0) return b;
  if (
== d) return c;
  if ((
/= 2) < 1) return ^ (10 * (1)) + b;
  return 
* (- (^ (-10 * --t)) + 2) + b;
};

function 
easeInCirc(xtbcd) {
  return -
* ((- (/= d) * t) ^ 0.5 1) + b;
};

function 
easeOutCirc(xtbcd) {
  return 
* (- (1) * t) ^ 0.5 b;
};

function 
easeInOutCirc(xtbcd) {
  if ((
/= 2) < 1) return -* ((t) ^ 0.5 1) + b;
  return 
* ((- (-= 2) * t) ^ 0.5 1) + b;
};

function 
easeInElastic(xtbcd) {
  
1.70158;
  
0;
  
c;
  if (
== 0) return b;
  if ((
/= d) == 1) return c;
  if (!
p.3;
  if (
abs(c)) {
    
c;
    
4;
  } else 
/ (pi) * arcsin(a);
  return -(
^ (10 * (-= 1)) * sin((s) * (pi) / p)) + b;
};

function 
easeOutElastic(xtbcd) {
  
1.70158;
  
0;
  
c;
  if (
== 0) return b;
  if ((
/= d) == 1) return c;
  if (!
p.3;
  if (
abs(c)) {
    
c;
    
4;
  } else 
/ (pi) * arcsin(a);
  return 
^ (-10 t) * sin((s) * (pi) / p) + b;
};

function 
easeInOutElastic(xtbcd) {
  
1.70158;
  
0;
  
c;
  if (
== 0) return b;
  if ((
/= 2) == 2) return c;
  if (!
p* (.3 1.5);
  if (
abs(c)) {
    
c;
    
4;
  } else 
/ (pi) * arcsin(a);
  if (
1) return -.5 * (^ (10 * (-= 1)) * sin((s) * (pi) / p)) + b;
  return 
^ (-10 * (-= 1)) * sin((s) * (pi) / p) * .5 b;
};

function 
easeInBack(xtbcds) {
  if (
s.type() == (- 1)) 1.70158;
  return 
* (/= d) * * ((1) * s) + b;
};

function 
easeOutBack(xtbcds) {
  if (
s.type() == (- 1)) 1.70158;
  return 
* ((1) * * ((1) * s) + 1) + b;
};

function 
easeInOutBack(xtbcds) {
  if (
s.type() == (- 1)) 1.70158;
  if ((
/= 2) < 1) return * (* (((*= (1.525)) + 1) * s)) + b;
  return 
* ((-= 2) * * (((*= (1.525)) + 1) * s) + 2) + b;
};

function 
easeInBounce(xtbcd) {
  return 
this.easeOutBounce(xt0cd) + b;
};

function 
easeOutBounce(xtbcd) {
  if ((
/= d) < (2.75)) {
    return 
* (7.5625 t) + b;
  } else if (
< (2.75)) {
    return 
* (7.5625 * (-= (1.5 2.75)) * .75) + b;
  } else if (
< (2.5 2.75)) {
    return 
* (7.5625 * (-= (2.25 2.75)) * .9375) + b;
  } else {
    return 
* (7.5625 * (-= (2.625 2.75)) * .984375) + b;
  }
};

function 
easeInOutBounce(xtbcd) {
  if (
2) return this.easeInBounce(x20cd) * .5 b;
  return 
this.easeOutBounce(xd0cd) * .5 .5 b;
};

// basic ones
function easeLinear(x) {
  return 
x;
}

function 
easeSwing(x) {
  return (- 
cos(pi) / 2) + 0.5;
}

// helper
function ease(temp.easingxtbcd) {
  return 
this.(@ "ease" temp.easing)(xtbcd);


Hopefully someone finds use out of this :).

Fulg0reSama 05-31-2012 05:14 AM

Insta-Impressed. (Don't care about this not being a real word)

+rep Chris.

Imperialistic 05-31-2012 05:56 AM

+rep

very impressive

Hezzy002 05-31-2012 01:23 PM

One of the features of jQuery's animation lib is daisy-chaining animations.

item.animate().animate().animate();

Then they execute in that order. You can rig that up in GS2, should look into it.

In my engine, tweening is part of the built-in scripting features. I accomplish daisy chaining by having the first call return an animation object, which is then built upon by the member functions of the animation object. That way, you can animate multiple variables concurrently and with some nice synactic sugar, too.

Chompy 05-31-2012 02:03 PM

Quote:

Originally Posted by Hezzy002 (Post 1696050)
One of the features of jQuery's animation lib is daisy-chaining animations.

item.animate().animate().animate();

Then they execute in that order. You can rig that up in GS2, should look into it.

Thumbs up for that.

Hezzy002 05-31-2012 02:25 PM

I edited my post with a way to implement it in GS2. Edit was supposed to be right after but I didn't have time, had to go off to school.

You should also rig a percentage of tweening to use as a variable, it's useful. For instance, in my implementation there's some preset types of easing, with each one being able to be modified by a scalar value. A value between -infinity and infinity. A value > 0 will make it start quickly, and end slowly. A value < 0 does the opposite, while a value of 0 will make it run at the same pace the entire time, with it all being relative to the distance from 0. Flash does the same thing. All it takes for that is a little multiplication against the value you input into the easing type function.

Emera 05-31-2012 05:01 PM

This looks great. Good job.

MattKan 05-31-2012 05:02 PM

Is there anything you can't do? +rep

salesman 06-01-2012 01:42 AM

I hate it, rep-
jk chris plz 4give

Tolnaftate2004 06-01-2012 03:34 AM

3 Attachment(s)
http://forums.graalonline.com/forums...ad.php?t=79671 :confused:

Also with regards to chaining, I've been hanging on to this because I wanted to make it more extensible (like jQuery), but I've yet to devise an elegant solution. Anyway, you can check out the attachments. It includes some tweening functionality (specifically, linear and sinusiodal). Here's an example, the Zenkou message system.

PHP Code:

// Zenkou Messaging System (Growl Edition!)
// Styled after Growl (http://growl.info)
// v0.1

function onCreated() { this.join("boilerplate"); }

//#CLIENTSIDE
const PRE "zen_messages_";

function 
getlock() {
  if (
this.lock) {
    while (!
waitfor(this.name"onMessage" this.num));
    
this.num++;
  }
  
this.lock true;
  return;
}

function 
release() {
  if (
this.lock) {
    
this.lock false;
    
this.trigger("Message" this.numNULL);
  }
  return;
}

function 
growl(stitletexticontime) {
  if (
text 0) { time texttext stitlestitle ""icon "alert"; }
  if (
icon 0)  { time iconicon texttext stitlestitle ""; }
  if (
time == 0) { time 10; }
  if (
text == "") { text stitlestitle ""; }
  
  echo(
"[" int(timevar2) @ "] " stitle); echo(text);
  
temp.lines wraptext2(2382/3" -""@verdana@@" text);
  
temp.ys = {};
  
temp.max(3218 * (temp.stitle != "") + 16 temp.lines.size());
  for (
temp.0temp.temp.lines.size(); temp.i++)
    
temp.ys.add(16 * (temp.stitle != "") + 16 temp.i);
  
  for (
temp.this.messages.size(); temp.> -1temp.i--) {
    if (
this.messages[temp.i].objecttype() == "TGraalVar")
      
this.messages.delete(temp.i);
    else 
this.messages[temp.i].duplicate().set("y""+=" @ (temp.10));
  }
  
  
temp.sw screenwidth;
  
temp.bg = $()
    .
images(9200)
    .
set("image"PRE "blackbox.png")
    .
set("layer"7)
    .
set("alpha"1)
    .
set("mode"1)
    .
set("zoom"1)
    .
set("partx", { 01020 })
    .
set("party", { 000101010202020 })
    .
set("partw"10)
    .
set("parth" 10)
    .
set("stretchx", { 128})
    .
set("stretchy", { 111temp.h/10-1temp.h/10-1temp.h/10-111})
    .
set("x", { temp.sw 303temp.sw 158temp.sw 13 })
    .
set("y", { 4444+temp.h/24+temp.h/24+temp.h/24+temp.h4+temp.h4+temp.})
  ;
  if ( 
stitle != "" ) {
    
temp.title = $()
      .
images(1200)
      .
set("text"stitle)
      .
set("zoom"0.7)
      .
set("font""verdana")
      .
set("style""b")
      .
set("layer"8)
      .
set("x"temp.sw 251)
      .
set("y"4)
    ;
  }
  
temp.desc = $()
    .
images(temp.lines.size(), 200)
    .
set("text"temp.lines)
    .
set("zoom"2/3)
    .
set("font""verdana")
    .
set("layer"8)
    .
set("x"temp.sw 251)
    .
set("y"temp.ys)
  ;
  
temp.icon = $()
    .
images(1200)
    .
set("image"PRE "icon_" icon ".png")
    .
set("layer"8)
    .
set("mode"1)
    .
set("x"temp.sw 294)
    .
set("y"temp.h/9)
  ;
  
  (
temp.merged = $());
  
temp.merged
    
.merge({ temp.bgtemp.titletemp.desctemp.icon })
    .
wait(time)
    .
call(
      
this.clear_out = function (args) {
        
temp.args[0];
        
temp.args[1];
        
temp.on false;
        for (
temp.this.messages.size()-1temp.> -1temp.i--) {
          
temp.msg this.messages[temp.i];
          if (
temp.msg.objecttype() == "TGraalVar") continue;
          if (
temp.msg == temp.qtemp.on true;
          elseif (
temp.on) {
            
temp.msg
              
.duplicate()
              .
tween("y""-=" @ (temp.10), 1)
              .
wait(1)
            ;
          }
        }
      }, { 
temp.mergedtemp.}
    )
    .
tween("alpha"01)
    .
wait(1)
    .
end()
  ;
  
  
this.messages.add(temp.merged);
}

public function 
addmsg(titledescicondur) {
  
getlock();
  
growl(titledescicondur);
  
release();


Of course I was aiming for something a little more sophisticated than a series of chained set()s for image manipulation when I started writing it but meh.

cbk1994 06-01-2012 04:05 AM

Quote:

Originally Posted by Tolnaftate2004 (Post 1696091)

Hah, totally forgot about that. I even replied in the thread. :blush:

xXziroXx 04-21-2013 05:25 PM

Hey Chris, I've been using this to tween the x & y of GUI controls successfully a few times, but it doesn't seem to like tweening the width/height of them at all. The GUI control disappears, and reappears with the target width/height on the last frame.

Is it your code not supporting it or is it just another beautiful day in GScript? :p If there's anything you could on your end to "fix" it, I'd much appreciate it :)

cbk1994 04-22-2013 01:46 AM

Quote:

Originally Posted by xXziroXx (Post 1716733)
Hey Chris, I've been using this to tween the x & y of GUI controls successfully a few times, but it doesn't seem to like tweening the width/height of them at all. The GUI control disappears, and reappears with the target width/height on the last frame.

Is it your code not supporting it or is it just another beautiful day in GScript? :p If there's anything you could on your end to "fix" it, I'd much appreciate it :)

I don't think I ever tried it in that manner but I don't see why it wouldn't work. If it doesn't, my guess would be it's one of the many quirks of Graal. I don't have access to any server to test on now, though.

xXziroXx 04-22-2013 02:40 AM

Quote:

Originally Posted by cbk1994 (Post 1716770)
I don't have access to any server to test on now, though.

I'd be more than happy to give you whatever rights you need on Maloria if you need a server to test code on. It's classic enabled, so shouldn't be any problems with that. If it's something you'd be interested in, let me know!

fowlplay4 04-22-2013 03:54 AM

Try tweening another variable and then setting the height/width to that variable.


All times are GMT +2. The time now is 01:19 PM.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Copyright (C) 1998-2019 Toonslab All Rights Reserved.