This commit is contained in:
2014-06-16 00:25:35 +04:00
committed by Ivan "KaiSD" Korystin
parent bb1b24e947
commit 441fa25a19
25 changed files with 1110 additions and 1 deletions

View File

@@ -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.

76
src/ActionsBase/Action.cs Normal file
View File

@@ -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;
}
/// <summary>
/// This method is called at the action start. Usable for instant and interval actions.
/// </summary>
public virtual Action reverse()
{
throw new Exception("Can reverse only the reversable interval actions");
}
/// <summary>
/// This method is called at the action start. Usable for instant and interval actions.
/// </summary>
public virtual void start()
{
if (target == null)
throw new Exception("Can start the action only after it's atached to the actor");
}
/// <summary>
/// This method is called at every frame update. Not usable for instant actions.
/// </summary>
public virtual void step(float dt)
{
if (target == null)
throw new Exception("Can update the action only after it's atached to the actor");
}
/// <summary>
/// This method is called after the interval action is stopped. Not usable for instant actions.
/// </summary>
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
{
}
}
}

View File

@@ -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;
}
/// <summary>
/// Step method for interval actions. Don't override this method, when inheriting, put your code in stepInterval instead.
/// </summary>
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;
}
}
/// <summary>
/// Step method for interval actions. Put your code here.
/// </summary>
public virtual void stepInterval(float dt)
{
}
/// <summary>
/// Property to get the remaining tick time after the action has ended.
/// </summary>
public override float dtr
{
get
{
return dtrdata;
}
protected set
{
dtrdata = value;
}
}
/// <summary>
/// Immediately changes the time scale for this action and all nested ones.
/// </summary>
public void setTimeScale(float ts)
{
timeScale = ts;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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]);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<MeshFilter>().mesh.vertices;
int i = 0;
while (i < verts.Length)
{
verts[i] = m.MultiplyPoint3x4(verts[i]);
i++;
}
gameObject.GetComponent<MeshFilter>().mesh.vertices = verts;
angles1prev = angles1;
angles2prev = angles2;
}
}