Browse Source

initial

master
25 changed files with 1110 additions and 1 deletions
  1. +59
    -1
      README.md
  2. +76
    -0
      src/ActionsBase/Action.cs
  3. +103
    -0
      src/ActionsBase/ActionInterval.cs
  4. +85
    -0
      src/ActionsBase/ActionParallel.cs
  5. +69
    -0
      src/ActionsBase/ActionRepeat.cs
  6. +90
    -0
      src/ActionsBase/ActionSequence.cs
  7. +23
    -0
      src/ActionsInstant/ActionHide.cs
  8. +20
    -0
      src/ActionsInstant/ActionLog.cs
  9. +44
    -0
      src/ActionsInstant/ActionSendMessage.cs
  10. +25
    -0
      src/ActionsInstant/ActionSetPlace.cs
  11. +32
    -0
      src/ActionsInstant/ActionSetRotation.cs
  12. +21
    -0
      src/ActionsInstant/ActionSetTint.cs
  13. +23
    -0
      src/ActionsInstant/ActionShow.cs
  14. +18
    -0
      src/ActionsInstant/ActionToggleVisibility.cs
  15. +32
    -0
      src/ActionsInterval/ActionDelay.cs
  16. +31
    -0
      src/ActionsInterval/ActionMoveBy.cs
  17. +34
    -0
      src/ActionsInterval/ActionMoveTo.cs
  18. +33
    -0
      src/ActionsInterval/ActionRotateBy.cs
  19. +43
    -0
      src/ActionsInterval/ActionRotateTo.cs
  20. +47
    -0
      src/ActionsInterval/ActionScaleBy.cs
  21. +38
    -0
      src/ActionsInterval/ActionScaleTo.cs
  22. +37
    -0
      src/ActionsInterval/ActionTintBy.cs
  23. +35
    -0
      src/Examples/SampleActions.cs
  24. +47
    -0
      src/UnityComponents/Actor.cs
  25. +45
    -0
      src/UnityComponents/SkewModifier.cs

+ 59
- 1
README.md View File

@@ -1,4 +1,62 @@
coa4u 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
- 0
src/ActionsBase/Action.cs 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
{
}
}

}

+ 103
- 0
src/ActionsBase/ActionInterval.cs 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;
}
}

+ 85
- 0
src/ActionsBase/ActionParallel.cs 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();
}
}
}

+ 69
- 0
src/ActionsBase/ActionRepeat.cs 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();
}
}
}

+ 90
- 0
src/ActionsBase/ActionSequence.cs 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();
}
}
}

+ 23
- 0
src/ActionsInstant/ActionHide.cs 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;
}
}

+ 20
- 0
src/ActionsInstant/ActionLog.cs 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);
}
}

+ 44
- 0
src/ActionsInstant/ActionSendMessage.cs 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);
}
}
}

+ 25
- 0
src/ActionsInstant/ActionSetPlace.cs 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;
}
}

+ 32
- 0
src/ActionsInstant/ActionSetRotation.cs 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);
}
}

+ 21
- 0
src/ActionsInstant/ActionSetTint.cs 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]);
}
}

+ 23
- 0
src/ActionsInstant/ActionShow.cs 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;
}
}

+ 18
- 0
src/ActionsInstant/ActionToggleVisibility.cs 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;
}
}

+ 32
- 0
src/ActionsInterval/ActionDelay.cs 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);
}
}
}

+ 31
- 0
src/ActionsInterval/ActionMoveBy.cs 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);
}
}

+ 34
- 0
src/ActionsInterval/ActionMoveTo.cs 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);
}
}

+ 33
- 0
src/ActionsInterval/ActionRotateBy.cs 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);
}
}

+ 43
- 0
src/ActionsInterval/ActionRotateTo.cs 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);
}
}

+ 47
- 0
src/ActionsInterval/ActionScaleBy.cs 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();
}
}

+ 38
- 0
src/ActionsInterval/ActionScaleTo.cs 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();
}
}

+ 37
- 0
src/ActionsInterval/ActionTintBy.cs 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;
}
}

+ 35
- 0
src/Examples/SampleActions.cs 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);
}
}

+ 47
- 0
src/UnityComponents/Actor.cs 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);
}
}
}

+ 45
- 0
src/UnityComponents/SkewModifier.cs 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;
}
}