Tween engine

Share your ZGE-development tips and techniques here!

Moderator: Moderators

Post Reply
User avatar
Ats
Posts: 713
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Tween engine

Post by Ats »

I think ZGE was missing some tweening examples. So I wrote one, based on the Pico-8 Tween engine by egordorichev. I've put almost everything in a model so it can be used and removed easily. But maybe there a better way to do that.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" CameraPosition="0 0 40" FileVersion="2">
  <OnLoaded>
    <ZLibrary Comment="Tween Functions">
      <Source>
<![CDATA[//
// Inspired by https://www.lexaloffle.com/bbs/?pid=52681&tid=31274

const float back = 1.70158;

float tween_easing(float t, string type)
{
  float s = back;
  switch (type)
  {
    case "linear":

      return t;
      break;

    case "quad_out":

      return -1*t*(t-2);
      break;

    case "quad_in":

      return t*t;
      break;

    case "quad_in_out":

      t = t*2;
      if (t<1) return 0.5*t*t;
    	return -0.5*((t-1)*(t-3)-1);
      break;

    case "back_in":

      return t*t*((s+1)*t-s);
      break;

    case "back_out":

      t-=1;
      return t*t*((s+1)*t+s)+1;
      break;

    case "back_in_out":

      s = back;
      t *= 2;
      if (t<1)
      {
        s *= 1.525;
        return 0.5*(t*t*((s+1)*t-s));
      }
      t -= 2;
      s *= 1.525;
      return 0.5*(t*t*((s+1)*t+s)+2);
      break;
  }

  return t; // return linear if error in type string
}

void tween_init(model t, model m, string ease, float start, float end, float duration, float delay)
{
  t.TweenModel = m;          // Attached model to the tween
  t.TweenEase = ease;        // See list of easings in Tween Definitions
  t.TweenStart = start;      // Start value
  t.TweenDiff = end - start; // End value
  t.TweenTime = duration;    // Duration of the tween (in seconds)
  t.TweenDelay = delay;      // Delay before starting the tween (in seconds)
  t.TweenProgress = 0;
}]]>
      </Source>
    </ZLibrary>
    <ZExpression Comment="Tween Definitions">
      <Expression>
<![CDATA[//
TweenNames[0] = "linear";
TweenNames[1] = "quad_in";
TweenNames[2] = "quad_out";
TweenNames[3] = "quad_in_out";
TweenNames[4] = "back_in";
TweenNames[5] = "back_out";
TweenNames[6] = "back_in_out";]]>
      </Expression>
    </ZExpression>
    <ZExpression>
      <Expression>
<![CDATA[App.CameraPosition.Z = 40;

model m, t;
for (int i=0; i<TweenNames.SizeDim1; i++)
{
  m = createModel(Ball);
  m.Position.X = -12 + i * 4;
  m.Position.Y = -10;

  t = createModel(Tween);
  tween_init(t, m, TweenNames[i], m.Position.Y, 10, 1, 1);
}]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <Content>
    <Mesh Name="SphereMesh">
      <Producers>
        <MeshSphere/>
      </Producers>
    </Mesh>
    <Model Name="Ball">
      <OnRender>
        <RenderMesh Mesh="SphereMesh"/>
      </OnRender>
    </Model>
    <Model Name="Tween">
      <Definitions>
        <Variable Name="TweenEase" Type="2"/>
        <Variable Name="TweenDelay"/>
        <Variable Name="TweenTime"/>
        <Variable Name="TweenStart"/>
        <Variable Name="TweenProgress"/>
        <Variable Name="TweenDiff"/>
        <Variable Name="TweenModel" Type="3"/>
      </Definitions>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[//
float i;

if (CurrentModel.TweenDelay > 0)
{
  CurrentModel.TweenDelay -= App.DeltaTime;
  i = CurrentModel.TweenStart;
}
else
{
  CurrentModel.TweenProgress += App.DeltaTime / CurrentModel.TweenTime;
  i = CurrentModel.TweenStart + CurrentModel.tweenDiff * tween_easing(clamp(CurrentModel.TweenProgress, 0, 1), CurrentModel.TweenEase);
}

// See how can I pass some parameters to move the model on another axis, or rotate it or return some floats to the TweenModel
CurrentModel.TweenModel.Position.Y = i;

if (CurrentModel.TweenProgress >= 1)
{
  // Do something on tween end. That's the part where I would like to pass a function
  model t = createModel(Tween);
  tween_init(t, CurrentModel.TweenModel, CurrentModel.TweenEase, CurrentModel.TweenModel.Position.Y, CurrentModel.TweenModel.Position.Y *= -1, 1, 1);

  @RemoveModel();
}]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
    </Model>
    <Array Name="TweenNames" Type="2" SizeDim1="7"/>
  </Content>
</ZApplication>
While i'ts working, I'm still searching a clever way to use the tween on a peculiar position axis, or rotation...
And my second concern is: is it possible to pass a function so it can be called when the tween ends? You'll see that in Tween (Model) OnUpdate.
Thanks !
Last edited by Ats on Tue Aug 07, 2018 9:57 am, edited 1 time in total.
User avatar
Kjell
Posts: 1911
Joined: Sat Feb 23, 2008 11:15 pm

Re: Tween engine

Post by Kjell »

Hi Ats,

Looks familiar ... :wink:

K
User avatar
Ats
Posts: 713
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Tween engine

Post by Ats »

lol :D
I didn't find that in the forum. Thanks !
User avatar
Ats
Posts: 713
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Tween engine

Post by Ats »

I'm trying to make a portable tween engine, based on the Pico-8 Tween engine by egordorichev. I've put almost everything in a model so it can be used and removed easily. But maybe there a better way to do that.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" CameraPosition="0 0 40" FileVersion="2">
  <OnLoaded>
    <ZLibrary Comment="Tween Functions">
      <Source>
<![CDATA[//
// Inspired by https://www.lexaloffle.com/bbs/?pid=52681&tid=31274

const float back = 1.70158;

float tween_easing(float t, string type)
{
  float s = back;
  switch (type)
  {
    case "linear":

      return t;
      break;

    case "quad_out":

      return -1*t*(t-2);
      break;

    case "quad_in":

      return t*t;
      break;

    case "quad_in_out":

      t = t*2;
      if (t<1) return 0.5*t*t;
    	return -0.5*((t-1)*(t-3)-1);
      break;

    case "back_in":

      return t*t*((s+1)*t-s);
      break;

    case "back_out":

      t-=1;
      return t*t*((s+1)*t+s)+1;
      break;

    case "back_in_out":

      s = back;
      t *= 2;
      if (t<1)
      {
        s *= 1.525;
        return 0.5*(t*t*((s+1)*t-s));
      }
      t -= 2;
      s *= 1.525;
      return 0.5*(t*t*((s+1)*t+s)+2);
      break;
  }

  return t; // return linear if error in type string
}

void tween_init(model t, model m, vec3 pos, vec3 rot, string ease, float start, float end, float duration, float delay)
{
  t.TweenModel = m;          // Attached model to the tween
  t.TweenPosition = pos;     // vector3 to activate position axis (ex: (1,0,1) to move on Position.X and Z)
  t.TweenRotation = rot;     // vector3 to activate rotation axis (ex: (0,1,0) to move on Rotation.Y)
  t.TweenEase = ease;        // See list of easings in Tween Definitions
  t.TweenStart = start;      // Start value
  t.TweenDiff = end - start; // End value
  t.TweenTime = duration;    // Duration of the tween (in seconds)
  t.TweenDelay = delay;      // Delay before starting the tween (in seconds)
  t.TweenProgress = 0;
}]]>
      </Source>
    </ZLibrary>
    <ZExpression Comment="Tween Definitions">
      <Expression>
<![CDATA[//
TweenNames[0] = "linear";
TweenNames[1] = "quad_in";
TweenNames[2] = "quad_out";
TweenNames[3] = "quad_in_out";
TweenNames[4] = "back_in";
TweenNames[5] = "back_out";
TweenNames[6] = "back_in_out";]]>
      </Expression>
    </ZExpression>
    <ZExpression>
      <Expression>
<![CDATA[App.CameraPosition.Z = 40;

model m, t;
for (int i=0; i<TweenNames.SizeDim1; i++)
{
  m = createModel(Ball);
  m.Position.X = -12 + i * 4;
  m.Position.Y = -10;

  t = createModel(Tween);
  tween_init(t, m, vector3(0,1,0), vector3(1,0,0), TweenNames[i], m.Position.Y, 10, 1, 1);
}]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <Content>
    <Mesh Name="StuffMesh">
      <Producers>
        <MeshBox/>
      </Producers>
    </Mesh>
    <Model Name="Ball">
      <OnRender>
        <RenderMesh Mesh="StuffMesh"/>
      </OnRender>
    </Model>
    <Model Name="Tween">
      <Definitions>
        <Variable Name="TweenEase" Type="2"/>
        <Variable Name="TweenDelay"/>
        <Variable Name="TweenTime"/>
        <Variable Name="TweenStart"/>
        <Variable Name="TweenProgress"/>
        <Variable Name="TweenDiff"/>
        <Variable Name="TweenModel" Type="3"/>
        <Variable Name="TweenPosition" Type="7"/>
        <Variable Name="TweenRotation" Type="7"/>
      </Definitions>
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[//
float i;

if (CurrentModel.TweenDelay > 0)
{
  CurrentModel.TweenDelay -= App.DeltaTime;
  i = CurrentModel.TweenStart;
}
else
{
  CurrentModel.TweenProgress += App.DeltaTime / CurrentModel.TweenTime;
  i = CurrentModel.TweenStart + CurrentModel.tweenDiff * tween_easing(clamp(CurrentModel.TweenProgress, 0, 1), CurrentModel.TweenEase);
}

// Is it possible to pass a parameter pointer in order to get rid of those two vector3?
if (CurrentModel.TweenPosition.X) CurrentModel.TweenModel.Position.X = i;
if (CurrentModel.TweenPosition.Y) CurrentModel.TweenModel.Position.Y = i;
if (CurrentModel.TweenPosition.Z) CurrentModel.TweenModel.Position.Z = i;
if (CurrentModel.TweenRotation.X) CurrentModel.TweenModel.Rotation.X = i;
if (CurrentModel.TweenRotation.Y) CurrentModel.TweenModel.Rotation.Y = i;
if (CurrentModel.TweenRotation.Z) CurrentModel.TweenModel.Rotation.Z = i;

if (CurrentModel.TweenProgress >= 1)
{
  // Do something on tween end. That's the part where I would like to pass a function
  model t = createModel(Tween);
  tween_init(t, CurrentModel.TweenModel, vector3(0,1,0), vector3(1,0,0), CurrentModel.TweenEase, CurrentModel.TweenModel.Position.Y, CurrentModel.TweenModel.Position.Y *= -1, 1, 1);

  @RemoveModel();
}]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
    </Model>
    <Array Name="TweenNames" Type="2" SizeDim1="7"/>
  </Content>
</ZApplication>

While i'ts working, I'm still searching a clever way to use the tween on a peculiar position axis, or rotation...

Edit: @Kjell, I've added two arguments to pass around in the tween_init. Two vector3 to choose which Position or Rotation Axis to move on. But that would be better if I could pass the parameter pointer instead of the parameter value. Just like in HaxeFlixel.


And my second concern is: is it possible to pass a function so it can be called when the tween ends? You'll see that in Tween (Model) OnUpdate.

Edit: The answer is no, so I'll stop with that and move to something else...
User avatar
Kjell
Posts: 1911
Joined: Sat Feb 23, 2008 11:15 pm

Re: Tween engine

Post by Kjell »

Hi Ats,
Ats wrote:I'm trying to make a portable tween engine, based on the Pico-8 Tween engine by egordorichev. I've put almost everything in a model so it can be used and removed easily.
I'm a bit puzzled as to why you'd want to wrap this in a model .. but maybe that's just me? Anyway, don't use strings in a switch-statement unless you absolutely have to. Instead, you can define a bunch of constant integers in your library ( if you want descriptive function-calls ) like this ..

Code: Select all

const int EASE_LINEAR = 0,
          EASE_QUAD_IN = 1,
          EASE_QUAD_OUT = 2,
          EASE_QUAD_INOUT = 3,
          EASE_BACK_IN = 4,
          EASE_BACK_OUT = 5,
          EASE_BACK_INOUT = 6;
Ats wrote:I've added two arguments to pass around in the tween_init. Two vector3 to choose which Position or Rotation Axis to move on. But that would be better if I could pass the parameter pointer instead of the parameter value.
Vector arguments are automatically passed as reference ( which are basically safe pointers ) as demonstrated in the following example.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZLibrary>
      <Source>
<![CDATA[//

void move(vec3 v)
{
  v.x += App.DeltaTime;
}]]>
      </Source>
    </ZLibrary>
    <SpawnModel Model="Foo"/>
  </OnLoaded>
  <Content>
    <Model Name="Foo">
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[//

move(CurrentModel.Position);]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <RenderSprite/>
      </OnRender>
    </Model>
  </Content>
</ZApplication>
K
Post Reply