diff --git a/README.md b/README.md
index 5c1e61f..9ffd371 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,62 @@
coa4u
=====
+## Cocos2d-like Actions for Unity3d
-Cocos2d Actions for Unity3d
+Unity3d is a very good game engine. It's almost perfect for quick prototyping.
+After switching from Cocos2D to Unity3d, i'm still missing just one cocos's feature - actions.
+It's a great combination of simplicity and flexibility, and i haven't found the suitable replacement for it.
+Since actions are quite simple, i implemented them myself.
+
+### Included actions (ready and WIP)
+
+Base actions
+- [x] Sequence
+- [x] Parallel
+- [x] Repeat and Loop *implemented in one action*
+- [ ] Reverse
+
+Interval actions
+- [x] MoveTo
+- [x] MoveBy
+- [x] RotateTo
+- [x] RotateBy
+- [x] ScaleTo
+- [x] ScaleBy
+- [x] TintBy
+- [x] Delay and RandomDelay *implemented in one action*
+- [ ] TintTo
+- [ ] FadeOut
+- [ ] FadeIn
+- [ ] FadeTo
+- [ ] JumpTo
+- [ ] JumpBy
+- [ ] Bezier
+- [ ] Blink
+
+Instant actions
+- [x] Place *renamed to SetPlace*
+- [x] CallFunc *renamed to SendMessage*
+- [x] Hide
+- [x] Show
+- [x] ToggleVisibility
+
+### Some additional actions
+
+Interval actions
+- [ ] SkewBy
+- [ ] SkewTo
+
+Instant actions
+- [x] SetRotation
+- [x] SetTint
+
+### Future plans
+After completing these actions, i'm going to add some more to the list.
+
+## License
+Just like Cocos2D, this code licensed under the [MIT License](http://en.wikipedia.org/wiki/MIT_License)
+
+## Help and donations
+I'm not doing it to make profit, but if you want, [you can send me a couple of bucks via PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z64675TKXFRFU).
+
+Also, if you'll write some action based on mine, feel free to send it to me, if you want me to add it to the library. I'll put your name on this page.
\ No newline at end of file
diff --git a/src/ActionsBase/Action.cs b/src/ActionsBase/Action.cs
new file mode 100644
index 0000000..5fa089f
--- /dev/null
+++ b/src/ActionsBase/Action.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class Action
+{
+ protected Actor target;
+ public float duration = 0;
+
+ public Action()
+ {
+ }
+
+ public virtual Action clone()
+ {
+ return new Action();
+ }
+
+ public virtual bool isRunning()
+ {
+ return false;
+ }
+
+ ///
+ /// This method is called at the action start. Usable for instant and interval actions.
+ ///
+ public virtual Action reverse()
+ {
+ throw new Exception("Can reverse only the reversable interval actions");
+ }
+
+ ///
+ /// This method is called at the action start. Usable for instant and interval actions.
+ ///
+ public virtual void start()
+ {
+ if (target == null)
+ throw new Exception("Can start the action only after it's atached to the actor");
+ }
+
+ ///
+ /// This method is called at every frame update. Not usable for instant actions.
+ ///
+ public virtual void step(float dt)
+ {
+ if (target == null)
+ throw new Exception("Can update the action only after it's atached to the actor");
+ }
+
+ ///
+ /// This method is called after the interval action is stopped. Not usable for instant actions.
+ ///
+ public virtual void stop()
+ {
+ if (target == null)
+ throw new Exception("Can stop the action only after it's atached to the actor");
+ }
+
+ public void setActor(Actor actor)
+ {
+ target = actor;
+ }
+
+ public virtual float dtr
+ {
+ get
+ {
+ return 0;
+ }
+
+ protected set
+ {
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/ActionsBase/ActionInterval.cs b/src/ActionsBase/ActionInterval.cs
new file mode 100644
index 0000000..ad6c901
--- /dev/null
+++ b/src/ActionsBase/ActionInterval.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionInterval : Action
+{
+ protected float timer;
+ protected float timeScale;
+ protected bool running;
+ private float dtrdata;
+
+ public ActionInterval(float tgtDuration)
+ : base()
+ {
+ duration = tgtDuration;
+ timeScale = 1F;
+ dtr = 0;
+ }
+
+ public override Action clone()
+ {
+ return new ActionInterval(duration);
+ }
+
+ public override Action reverse()
+ {
+ throw new Exception("Can reverse only the reversable interval actions");
+ }
+
+ public override bool isRunning()
+ {
+ return running;
+ }
+
+ public override void start()
+ {
+ base.start();
+ running = true;
+ timer = 0F;
+ }
+
+ public override void stop()
+ {
+ base.stop();
+ running = false;
+ }
+
+ ///
+ /// Step method for interval actions. Don't override this method, when inheriting, put your code in stepInterval instead.
+ ///
+ public override void step(float dt)
+ {
+ dt *= timeScale;
+ base.step(dt);
+ if (timer + dt > duration)
+ {
+ float odt = dt;
+ dt = duration - timer;
+ timer += odt;
+ }
+ else
+ {
+ timer += dt;
+ }
+ stepInterval(dt);
+ if (timer > duration)
+ {
+ stop();
+ dtr = timer - duration;
+ }
+ }
+
+ ///
+ /// Step method for interval actions. Put your code here.
+ ///
+ public virtual void stepInterval(float dt)
+ {
+ }
+
+ ///
+ /// Property to get the remaining tick time after the action has ended.
+ ///
+ public override float dtr
+ {
+ get
+ {
+ return dtrdata;
+ }
+
+ protected set
+ {
+ dtrdata = value;
+ }
+ }
+
+ ///
+ /// Immediately changes the time scale for this action and all nested ones.
+ ///
+ public void setTimeScale(float ts)
+ {
+ timeScale = ts;
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsBase/ActionParallel.cs b/src/ActionsBase/ActionParallel.cs
new file mode 100644
index 0000000..d038b4d
--- /dev/null
+++ b/src/ActionsBase/ActionParallel.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionParallel : ActionInterval
+{
+ protected Action[] actions;
+
+ public ActionParallel(Action action1, Action action2)
+ : base(0)
+ {
+ actions = new Action[] {action1, action2};
+ }
+
+ public ActionParallel(Action[] actionsList)
+ : base(0)
+ {
+ actions = actionsList;
+
+ }
+
+ public override Action clone()
+ {
+ Action[] aList = new Action[actions.Length];
+ for (int i = 0; i < actions.Length; i++)
+ {
+ aList[i] = actions[i].clone();
+ }
+ return new ActionSequence(aList);
+ }
+
+ public override Action reverse()
+ {
+ Action[] aList = new Action[actions.Length];
+ for (int i = 0; i < actions.Length; i++)
+ {
+ aList[i] = actions[i].reverse();
+ }
+ return new ActionSequence(aList);
+ }
+
+ public override void start()
+ {
+ base.start();
+ for (int i = 0; i < actions.Length; i++)
+ {
+ actions[i].setActor(target);
+ actions[i].start();
+ }
+ }
+
+ public override void step(float dt)
+ {
+ dt *= timeScale;
+ for (int i = 0; i < actions.Length; i++)
+ {
+ if (actions[i].isRunning())
+ actions[i].step(dt);
+ }
+ bool canStopNow = true;
+ float dtrdata = 0;
+ for (int i = 0; i < actions.Length; i++)
+ {
+ if (actions[i].isRunning())
+ {
+ canStopNow = false;
+ dtrdata = Math.Max(actions[i].dtr, dtrdata);
+ }
+ }
+ if (canStopNow)
+ {
+ stop();
+ dtr = dtrdata;
+ }
+ }
+
+ public override void stop()
+ {
+ base.stop();
+ for (int i = 0; i < actions.Length; i++)
+ {
+ actions[i].stop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsBase/ActionRepeat.cs b/src/ActionsBase/ActionRepeat.cs
new file mode 100644
index 0000000..91e9cee
--- /dev/null
+++ b/src/ActionsBase/ActionRepeat.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionRepeat : ActionInterval
+{
+ protected ActionInterval action;
+ protected int count;
+ protected int counter;
+ protected bool forever;
+
+ public ActionRepeat(ActionInterval tgtAction, int tgtCount)
+ : base(0)
+ {
+ action = tgtAction;
+ count = tgtCount;
+ counter = 0;
+ forever = false;
+ }
+
+ public ActionRepeat(ActionInterval tgtAction)
+ : base(0)
+ {
+ action = tgtAction;
+ count = 0;
+ counter = 0;
+ forever = true;
+ }
+
+ public override Action clone()
+ {
+ return new ActionRepeat((ActionInterval) action.clone(), count);
+ }
+
+ public override Action reverse()
+ {
+ return new ActionRepeat((ActionInterval) action.reverse(), count);
+ }
+
+ public override void start()
+ {
+ base.start();
+ action.setActor(target);
+ action.start();
+ counter = 0;
+ }
+
+ public override void step(float dt)
+ {
+ dt *= timeScale;
+ if (action.isRunning())
+ {
+ action.step(dt);
+ }
+ if (!action.isRunning() && (forever || counter < count - 1))
+ {
+ float dtrdata = action.dtr;
+ action.start();
+ if (dtrdata > 0)
+ action.step(dtrdata);
+ counter += 1;
+ }
+ else if (!action.isRunning() && counter >= count - 1)
+ {
+ dtr = action.dtr;
+ stop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsBase/ActionSequence.cs b/src/ActionsBase/ActionSequence.cs
new file mode 100644
index 0000000..a105ad9
--- /dev/null
+++ b/src/ActionsBase/ActionSequence.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionSequence : ActionInterval
+{
+ protected Action[] actions;
+ protected int index;
+
+ public ActionSequence(Action action1, Action action2)
+ : base(0)
+ {
+ actions = new Action[] {action1, action2};
+ }
+
+ public ActionSequence(Action[] actionsList)
+ : base(0)
+ {
+ actions = actionsList;
+
+ }
+
+ public override Action clone()
+ {
+ Action[] aList = new Action[actions.Length];
+ for (int i = 0; i < actions.Length; i++)
+ {
+ aList[i] = actions[i].clone();
+ }
+ return new ActionSequence(aList);
+ }
+
+ public override Action reverse()
+ {
+ Action[] aList = new Action[actions.Length];
+ for (int i = 0; i < actions.Length; i++)
+ {
+ aList[actions.Length - 1 - i] = actions[i].reverse();
+ }
+ return new ActionSequence(aList);
+ }
+
+ public override void start()
+ {
+ base.start();
+ index = 0;
+ actions[0].setActor(target);
+ actions[0].start();
+ while (!actions[index].isRunning() && index < actions.Length)
+ {
+ index += 1;
+ actions[index].setActor(target);
+ actions[index].start();
+ }
+ }
+
+ public override void step(float dt)
+ {
+ dt *= timeScale;
+ float dtrdata = 0;
+ if (actions[index].isRunning())
+ {
+ actions[index].step(dt);
+ if (!actions[index].isRunning())
+ dtrdata = actions[index].dtr;
+ }
+ while (!actions[index].isRunning() && index < actions.Length - 1)
+ {
+ index += 1;
+ actions[index].setActor(target);
+ actions[index].start();
+ if (actions[index].isRunning() && dtrdata > 0)
+ actions[index].step(dtrdata);
+ }
+ if (!actions[index].isRunning())
+ {
+ stop();
+ dtr = dtrdata;
+ }
+ }
+
+ public override void stop()
+ {
+ base.stop();
+ for (int i = 0; i < actions.Length; i++)
+ {
+ actions[i].stop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInstant/ActionHide.cs b/src/ActionsInstant/ActionHide.cs
new file mode 100644
index 0000000..ad683f4
--- /dev/null
+++ b/src/ActionsInstant/ActionHide.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionHide : Action
+{
+
+ public ActionHide()
+ : base()
+ {
+ }
+
+ public override Action clone()
+ {
+ return new ActionHide();
+ }
+
+ public override void start()
+ {
+ base.start();
+ target.gameObject.renderer.enabled = false;
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInstant/ActionLog.cs b/src/ActionsInstant/ActionLog.cs
new file mode 100644
index 0000000..8c31115
--- /dev/null
+++ b/src/ActionsInstant/ActionLog.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionLog : Action
+{
+ string message;
+
+ public ActionLog(string tgtMessage)
+ : base()
+ {
+ message = tgtMessage;
+ }
+
+ public override void start()
+ {
+ base.start();
+ Debug.Log(message);
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInstant/ActionSendMessage.cs b/src/ActionsInstant/ActionSendMessage.cs
new file mode 100644
index 0000000..a31dbff
--- /dev/null
+++ b/src/ActionsInstant/ActionSendMessage.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionSendMessage : Action
+{
+ protected string message;
+ protected object param;
+ protected SendMessageOptions options = SendMessageOptions.DontRequireReceiver;
+
+ public ActionSendMessage(string tgtMessage)
+ : base()
+ {
+ message = tgtMessage;
+ }
+
+ public ActionSendMessage(string tgtMessage, object tgtParam)
+ : base()
+ {
+ message = tgtMessage;
+ param = tgtParam;
+ }
+
+ public ActionSendMessage(string tgtMessage, object tgtParam, SendMessageOptions tgtOptions)
+ : base()
+ {
+ message = tgtMessage;
+ param = tgtParam;
+ options = tgtOptions;
+ }
+
+ public override void start()
+ {
+ base.start();
+ if (param != null)
+ {
+ target.SendMessage(message, param);
+ }
+ else
+ {
+ target.SendMessage(message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInstant/ActionSetPlace.cs b/src/ActionsInstant/ActionSetPlace.cs
new file mode 100644
index 0000000..10062f2
--- /dev/null
+++ b/src/ActionsInstant/ActionSetPlace.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionSetPlace : Action
+{
+ protected Vector3 place;
+
+ public ActionSetPlace(Vector3 tgtPlace)
+ : base()
+ {
+ place = tgtPlace;
+ }
+
+ public override Action clone()
+ {
+ return new ActionSetPlace(place);
+ }
+
+ public override void start()
+ {
+ base.start();
+ target.gameObject.transform.position = place;
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInstant/ActionSetRotation.cs b/src/ActionsInstant/ActionSetRotation.cs
new file mode 100644
index 0000000..bcf62f2
--- /dev/null
+++ b/src/ActionsInstant/ActionSetRotation.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionSetRotation : Action
+{
+ protected Vector3 value;
+
+ public ActionSetRotation(Vector3 tgtValue)
+ : base()
+ {
+ value = tgtValue;
+ }
+
+ public override Action clone()
+ {
+ return new ActionSetRotation(value);
+ }
+
+ public override void start()
+ {
+ base.start();
+ Vector3 path = new Vector3();
+ for (int i = 0; i < 3; i++)
+ {
+ path[i] = value[i] - target.gameObject.transform.rotation.eulerAngles[i];
+ }
+ target.gameObject.transform.Rotate(Vector3.up, path.y);
+ target.gameObject.transform.Rotate(Vector3.right, path.x);
+ target.gameObject.transform.Rotate(Vector3.forward, path.z);
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInstant/ActionSetTint.cs b/src/ActionsInstant/ActionSetTint.cs
new file mode 100644
index 0000000..70dba09
--- /dev/null
+++ b/src/ActionsInstant/ActionSetTint.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionSetTint : Action
+{
+ public Vector4 color;
+ protected const float coeff = 1F / 255F;
+
+ public ActionSetTint(Vector4 tgtColor)
+ : base()
+ {
+ color = tgtColor * coeff;
+ }
+
+ public override void start()
+ {
+ base.start();
+ target.gameObject.renderer.material.color = new Color(color[0], color[1], color[2], color[3]);
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInstant/ActionShow.cs b/src/ActionsInstant/ActionShow.cs
new file mode 100644
index 0000000..e47b567
--- /dev/null
+++ b/src/ActionsInstant/ActionShow.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionShow : Action
+{
+
+ public ActionShow()
+ : base()
+ {
+ }
+
+ public override Action clone()
+ {
+ return new ActionShow();
+ }
+
+ public override void start()
+ {
+ base.start();
+ target.gameObject.renderer.enabled = true;
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInstant/ActionToggleVisibility.cs b/src/ActionsInstant/ActionToggleVisibility.cs
new file mode 100644
index 0000000..799f04e
--- /dev/null
+++ b/src/ActionsInstant/ActionToggleVisibility.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionToggleVisibility : Action
+{
+
+ public ActionToggleVisibility()
+ : base()
+ {
+ }
+
+ public override void start()
+ {
+ base.start();
+ target.gameObject.renderer.enabled = !target.gameObject.renderer.enabled;
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInterval/ActionDelay.cs b/src/ActionsInterval/ActionDelay.cs
new file mode 100644
index 0000000..296450d
--- /dev/null
+++ b/src/ActionsInterval/ActionDelay.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionDelay : ActionInterval
+{
+ protected float durationMin;
+ protected float durationMax;
+
+ public ActionDelay(float tgtDuration)
+ : base(tgtDuration)
+ {
+ durationMin = tgtDuration;
+ durationMax = tgtDuration;
+ }
+
+ public ActionDelay(float tgtDuration, float tgtDurationMax)
+ : base(tgtDuration)
+ {
+ durationMin = tgtDuration;
+ durationMax = tgtDurationMax;
+ }
+
+ public override void start()
+ {
+ base.start();
+ if (durationMax != null)
+ {
+ duration = UnityEngine.Random.Range(durationMin, durationMax);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInterval/ActionMoveBy.cs b/src/ActionsInterval/ActionMoveBy.cs
new file mode 100644
index 0000000..cfe19b1
--- /dev/null
+++ b/src/ActionsInterval/ActionMoveBy.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionMoveBy : ActionInterval
+{
+ protected Vector3 delta;
+
+ public ActionMoveBy(Vector3 tgtDelta, float tgtDuration)
+ : base(tgtDuration)
+ {
+ delta = tgtDelta;
+ }
+
+ public override Action clone()
+ {
+ return new ActionMoveBy(delta, duration);
+ }
+
+ public override Action reverse()
+ {
+ return new ActionMoveBy(delta * -1F, duration);
+ }
+
+ public override void stepInterval(float dt)
+ {
+ float d = dt / duration;
+ Vector3 tgt = delta * d;
+ target.gameObject.transform.Translate(tgt, Space.World);
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInterval/ActionMoveTo.cs b/src/ActionsInterval/ActionMoveTo.cs
new file mode 100644
index 0000000..954cd31
--- /dev/null
+++ b/src/ActionsInterval/ActionMoveTo.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionMoveTo : ActionInterval
+{
+ protected Vector3 value;
+ protected Vector3 path;
+
+ public ActionMoveTo(Vector3 tgtValue, float tgtDuration)
+ : base(tgtDuration)
+ {
+ value = tgtValue;
+ }
+
+
+ public override Action clone()
+ {
+ return new ActionMoveBy(value, duration);
+ }
+
+ public override void start()
+ {
+ base.start();
+ path = value - target.gameObject.transform.position;
+ }
+
+ public override void stepInterval(float dt)
+ {
+ float d = dt / duration;
+ Vector3 tgt = path * d;
+ target.gameObject.transform.Translate(tgt, Space.World);
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInterval/ActionRotateBy.cs b/src/ActionsInterval/ActionRotateBy.cs
new file mode 100644
index 0000000..5d299f4
--- /dev/null
+++ b/src/ActionsInterval/ActionRotateBy.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionRotateBy : ActionInterval
+{
+ protected Vector3 delta;
+
+ public ActionRotateBy(Vector3 tgtDelta, float tgtDuration)
+ : base(tgtDuration)
+ {
+ delta = tgtDelta;
+ }
+
+ public override Action clone()
+ {
+ return new ActionRotateBy(delta, duration);
+ }
+
+ public override Action reverse()
+ {
+ return new ActionRotateBy(delta * -1F, duration);
+ }
+
+ public override void stepInterval(float dt)
+ {
+ float d = dt / duration;
+ Vector3 tgt = delta * d;
+ target.gameObject.transform.Rotate(Vector3.up, tgt.y);
+ target.gameObject.transform.Rotate(Vector3.right, tgt.x);
+ target.gameObject.transform.Rotate(Vector3.forward, tgt.z);
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInterval/ActionRotateTo.cs b/src/ActionsInterval/ActionRotateTo.cs
new file mode 100644
index 0000000..446c2de
--- /dev/null
+++ b/src/ActionsInterval/ActionRotateTo.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionRotateTo : ActionInterval
+{
+ protected Vector3 value;
+ protected Vector3 path;
+
+ public ActionRotateTo(Vector3 tgtValue, float tgtDuration)
+ : base(tgtDuration)
+ {
+ value = tgtValue;
+ }
+
+ public override Action clone()
+ {
+ return new ActionRotateTo(value, duration);
+ }
+
+ public override void start()
+ {
+ base.start();
+ path = new Vector3();
+ for (int i = 0; i < 3; i++)
+ {
+ float t = value[i];
+ float f = target.gameObject.transform.rotation.eulerAngles[i];
+ if (Math.Abs(t - f) < Math.Abs(t + 360 - f))
+ path[i] = t - f;
+ else
+ path[i] = t + 360 - f;
+ }
+ }
+ public override void stepInterval(float dt)
+ {
+ float d = dt / duration;
+ Vector3 tgt = path * d;
+ target.gameObject.transform.Rotate(Vector3.up, tgt.y);
+ target.gameObject.transform.Rotate(Vector3.right, tgt.x);
+ target.gameObject.transform.Rotate(Vector3.forward, tgt.z);
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInterval/ActionScaleBy.cs b/src/ActionsInterval/ActionScaleBy.cs
new file mode 100644
index 0000000..7270e01
--- /dev/null
+++ b/src/ActionsInterval/ActionScaleBy.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionScaleBy : ActionInterval
+{
+ protected Vector3 delta;
+ protected Vector3 path;
+
+ public ActionScaleBy(Vector3 tgtDelta, float tgtDuration)
+ : base(tgtDuration)
+ {
+ delta = tgtDelta;
+ }
+
+ public override Action clone()
+ {
+ return new ActionScaleBy(delta, duration);
+ }
+
+ public override Action reverse()
+ {
+ return new ActionScaleBy(delta * -1F, duration);
+ }
+
+ public override void start()
+ {
+ base.start();
+ Vector3 scale = target.gameObject.transform.localScale;
+ scale.x *= delta.x;
+ scale.y *= delta.y;
+ scale.z *= delta.z;
+ path = scale - target.gameObject.transform.localScale;
+ }
+
+ public override void stepInterval(float dt)
+ {
+ float d = dt / duration;
+ Vector3 tgt = path * d;
+ target.gameObject.transform.localScale += tgt;
+ }
+
+ public override void stop()
+ {
+ base.stop();
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInterval/ActionScaleTo.cs b/src/ActionsInterval/ActionScaleTo.cs
new file mode 100644
index 0000000..29696d3
--- /dev/null
+++ b/src/ActionsInterval/ActionScaleTo.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionScaleTo : ActionInterval
+{
+ protected Vector3 value;
+ protected Vector3 path;
+
+ public ActionScaleTo(Vector3 tgtValue, float tgtDuration)
+ : base(tgtDuration)
+ {
+ value = tgtValue;
+ }
+
+ public override Action clone()
+ {
+ return new ActionScaleTo(value, duration);
+ }
+
+ public override void start()
+ {
+ base.start();
+ path = value - target.gameObject.transform.localScale;
+ }
+
+ public override void stepInterval(float dt)
+ {
+ float d = dt / duration;
+ Vector3 tgt = path * d;
+ target.gameObject.transform.localScale += tgt;
+ }
+
+ public override void stop()
+ {
+ base.stop();
+ }
+}
\ No newline at end of file
diff --git a/src/ActionsInterval/ActionTintBy.cs b/src/ActionsInterval/ActionTintBy.cs
new file mode 100644
index 0000000..cdc2eec
--- /dev/null
+++ b/src/ActionsInterval/ActionTintBy.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+class ActionTintBy : ActionInterval
+{
+ public Vector4 color;
+ protected const float coeff = 1F / 255F;
+
+ public ActionTintBy(Vector4 tgtColor, float tgtDuration)
+ : base(tgtDuration)
+ {
+ color = tgtColor * coeff;
+ }
+
+ public override Action clone()
+ {
+ return new ActionTintBy(color / coeff, duration);
+ }
+
+ public override Action reverse()
+ {
+ return new ActionTintBy(-color / coeff, duration);
+ }
+
+ public override void stepInterval(float dt)
+ {
+ float d = dt / duration;
+ Vector4 tgt = color * d;
+ Color tgtColor = target.gameObject.renderer.material.color;
+ tgtColor[0] += tgt[0];
+ tgtColor[1] += tgt[1];
+ tgtColor[2] += tgt[2];
+ tgtColor[3] += tgt[3];
+ target.gameObject.renderer.material.color = tgtColor;
+ }
+}
\ No newline at end of file
diff --git a/src/Examples/SampleActions.cs b/src/Examples/SampleActions.cs
new file mode 100644
index 0000000..6e9f6d5
--- /dev/null
+++ b/src/Examples/SampleActions.cs
@@ -0,0 +1,35 @@
+using UnityEngine;
+using System.Collections;
+
+public class SampleActions : MonoBehaviour
+{
+ public void Start()
+ {
+ Action seq = new ActionRepeat (new ActionSequence(new Action[]
+ {
+ new ActionTintBy(new Vector4(-50,50,-50,-50),1),
+ new ActionParallel(new Action[] {
+ new ActionMoveBy(new Vector3(10, 0, 0), 1),
+ new ActionMoveBy(new Vector3(0, 10, 0), 1),
+ // new ActionRotateBy(new Vector3(90, 0, 0), 1)
+ }),
+ new ActionScaleBy(new Vector3(2, 2, 1), 1),
+ new ActionScaleBy(new Vector3(0.5F, 0.5F, 2), 1),
+ new ActionDelay(1),
+ new ActionRepeat(new ActionSequence(new Action[] {
+ new ActionHide(),
+ new ActionDelay(0F, 0.2F),
+ new ActionShow(),
+ new ActionDelay(0F, 0.2F),
+ }), 5),
+ new ActionDelay(1),
+ new ActionMoveBy(new Vector3(-10, 0, 0), 1),
+ new ActionDelay(0.5F),
+ new ActionMoveTo(new Vector3(0, 0, 10), 1),
+ new ActionScaleTo(new Vector3(2, 2, 2), 1),
+ new ActionRotateTo(new Vector3(0, 0, 0), 1),
+ new ActionTintBy(new Vector4(50,-50,50,50),3)
+ }), 5);
+ gameObject.SendMessage("AttachAction", seq);
+ }
+}
diff --git a/src/UnityComponents/Actor.cs b/src/UnityComponents/Actor.cs
new file mode 100644
index 0000000..3692882
--- /dev/null
+++ b/src/UnityComponents/Actor.cs
@@ -0,0 +1,47 @@
+using UnityEngine;
+using System.Collections;
+
+public class Actor : MonoBehaviour
+{
+ protected Action action;
+ private bool paused = false;
+
+ void Update()
+ {
+ if (paused || action == null)
+ return;
+ if (action.isRunning())
+ action.step(Time.deltaTime);
+ }
+
+ public void AttachAction(Action tgtAction)
+ {
+ action = tgtAction;
+ action.setActor(this);
+ action.start();
+ }
+
+ public void StopAction()
+ {
+ action.stop();
+ action = null;
+ }
+
+ public void PauseAction()
+ {
+ paused = true;
+ }
+
+ public void UnpauseAction()
+ {
+ paused = false;
+ }
+
+ public void SetTimeScale(float ts)
+ {
+ if (action is ActionInterval)
+ {
+ ((ActionInterval)action).setTimeScale(ts);
+ }
+ }
+}
diff --git a/src/UnityComponents/SkewModifier.cs b/src/UnityComponents/SkewModifier.cs
new file mode 100644
index 0000000..2348d1b
--- /dev/null
+++ b/src/UnityComponents/SkewModifier.cs
@@ -0,0 +1,45 @@
+using UnityEngine;
+using System.Collections;
+
+[System.Serializable]
+public class SkewModifier : MonoBehaviour
+{
+ public Vector3 angles1;
+ public Vector3 angles2;
+ protected Vector3 angles1prev = Vector3.zero;
+ protected Vector3 angles2prev = Vector3.zero;
+ protected const float coeff = Mathf.PI/180;
+
+ void Update()
+ {
+ if (angles1 != angles1prev || angles2 != angles2prev)
+ updateMesh();
+ }
+
+ protected void updateMesh()
+ {
+ Matrix4x4 m = Matrix4x4.zero;
+ m[0, 0] = 1;
+ m[1, 1] = 1;
+ m[2, 2] = 1;
+ m[3, 3] = 1;
+ m[0, 1] = Mathf.Tan(angles1.x * coeff) - Mathf.Tan(angles1prev.x * coeff); // skewing in xy
+ m[0, 2] = Mathf.Tan(angles2.x * coeff) - Mathf.Tan(angles2prev.x * coeff); // skewing in xz
+ m[1, 0] = Mathf.Tan(angles1.y * coeff) - Mathf.Tan(angles1prev.y * coeff); // skewing in yx
+ m[1, 2] = Mathf.Tan(angles2.y * coeff) - Mathf.Tan(angles2prev.y * coeff); // skewing in yz
+ m[2, 0] = Mathf.Tan(angles1.z * coeff) - Mathf.Tan(angles1prev.z * coeff); // skewing in zx
+ m[2, 1] = Mathf.Tan(angles2.z * coeff) - Mathf.Tan(angles2prev.z * coeff); // skewing in zy
+
+ Vector3[] verts = gameObject.GetComponent().mesh.vertices;
+ int i = 0;
+ while (i < verts.Length)
+ {
+ verts[i] = m.MultiplyPoint3x4(verts[i]);
+ i++;
+ }
+
+ gameObject.GetComponent().mesh.vertices = verts;
+ angles1prev = angles1;
+ angles2prev = angles2;
+ }
+}