[Unity] Added SkeletonGhost stuff

SkeletonGhost Shader contributed by Alex Dixon
This commit is contained in:
Fenrisul 2015-05-17 05:20:06 -07:00
parent 45d8adb64c
commit aef9b23a20
8 changed files with 388 additions and 0 deletions

View File

@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 13193c9d213765f4c85f4c1faa615711
folderAsset: yes
DefaultImporter:
userData:

View File

@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: a0cee0de78cef7440ae0b5aac39ae971
folderAsset: yes
DefaultImporter:
userData:

View File

@ -0,0 +1,92 @@
//Shader written by Alex Dixon
Shader "Spine/SkeletonGhost"
{
Properties
{
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
_TextureFade ("Texture Fade Out", Range(0,1)) = 0
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="False" "RenderType"="Transparent"}
Fog { Mode Off }
Blend One OneMinusSrcAlpha
ZWrite Off
Cull Off
Pass
{
Tags {"LightMode" = "Always"} // This Pass tag is important or Unity may not give it the correct light information.
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//#pragma multi_compile_fwdbase // This line tells Unity to compile this pass for forward base.
#include "UnityCG.cginc"
//#include "AutoLight.cginc"
struct vertex_input
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float4 color : COLOR;
};
struct vertex_output
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
sampler2D _MainTex;
fixed4 _Color;
fixed _TextureFade;
vertex_output vert (vertex_input v)
{
vertex_output o;
o.pos = mul( UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
o.color = v.color;
return o;
}
fixed4 frag(vertex_output i) : COLOR
{
fixed4 tex = tex2D(_MainTex, i.uv);
tex = fixed4(max(_TextureFade, tex.r), max(_TextureFade, tex.g), max(_TextureFade, tex.b), tex.a);
return tex * ((i.color * _Color) * tex.a);
//float finalAlpha = tex.a * i.color.a * _Color.a;
/*
TODO: Add basic lighting stuff in later?
fixed4 c;
c.rgb = (UNITY_LIGHTMODEL_AMBIENT.rgb * tex.rgb); // Ambient term. Only do this in Forward Base. It only needs calculating once.
c.rgb += tex.rgb; // Diffuse and specular.
//Unity 4: c.rgb = (UNITY_LIGHTMODEL_AMBIENT.rgb * tex.rgb * 2); // Ambient term. Only do this in Forward Base. It only needs calculating once.
//Unity 4: c.rgb += (tex.rgb * _LightColor0.rgb * diff) * (atten * 2); // Diffuse and specular.
c.a = tex.a; // + _LightColor0.a * atten;
return c;
*/
}
ENDCG
}
}
//FallBack "Transparent/Cutout/VertexLit" // Use VertexLit's shadow caster/receiver passes.
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3873d4699ee8a4b4da8fa6b8c229b94d
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,147 @@
/*****************************************************************************
* SkeletonGhost created by Mitch Thompson
* Full irrevocable rights and permissions granted to Esoteric Software
*****************************************************************************/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[RequireComponent(typeof(SkeletonRenderer))]
public class SkeletonGhost : MonoBehaviour {
public bool ghostingEnabled = true;
public float spawnRate = 0.05f;
public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00);
[Tooltip("Remember to set color alpha to 0 if Additive is true")]
public bool additive = true;
public int maximumGhosts = 10;
public float fadeSpeed = 10;
public Shader ghostShader;
[Tooltip("0 is Color and Alpha, 1 is Alpha only.")]
[Range(0, 1)]
public float textureFade = 1;
float nextSpawnTime;
SkeletonGhostRenderer[] pool;
int poolIndex = 0;
SkeletonRenderer skeletonRenderer;
MeshRenderer meshRenderer;
MeshFilter meshFilter;
Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
void Start () {
if (ghostShader == null)
ghostShader = Shader.Find("Spine/SkeletonGhost");
skeletonRenderer = GetComponent<SkeletonRenderer>();
meshFilter = GetComponent<MeshFilter>();
meshRenderer = GetComponent<MeshRenderer>();
nextSpawnTime = Time.time + spawnRate;
pool = new SkeletonGhostRenderer[maximumGhosts];
for (int i = 0; i < maximumGhosts; i++) {
GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer));
pool[i] = go.GetComponent<SkeletonGhostRenderer>();
go.SetActive(false);
go.hideFlags = HideFlags.HideInHierarchy;
}
if (skeletonRenderer is SkeletonAnimation)
((SkeletonAnimation)skeletonRenderer).state.Event += OnEvent;
}
//SkeletonAnimation
/*
* Int Value: 0 sets ghostingEnabled to false, 1 sets ghostingEnabled to true
* Float Value: Values greater than 0 set the spawnRate equal the float value
* String Value: Pass RGBA hex color values in to set the color property. IE: "A0FF8BFF"
*/
void OnEvent (Spine.AnimationState state, int trackIndex, Spine.Event e) {
if (e.Data.Name == "Ghosting") {
ghostingEnabled = e.Int > 0;
if (e.Float > 0)
spawnRate = e.Float;
if (e.String != null) {
this.color = HexToColor(e.String);
}
}
}
//SkeletonAnimator
//SkeletonAnimator or Mecanim based animations only support toggling ghostingEnabled. Be sure not to set anything other than the Int param in Spine or String will take priority.
void Ghosting (float val) {
ghostingEnabled = val > 0;
}
void Update () {
if (!ghostingEnabled)
return;
if (Time.time >= nextSpawnTime) {
GameObject go = pool[poolIndex].gameObject;
Material[] materials = meshRenderer.sharedMaterials;
for (int i = 0; i < materials.Length; i++) {
var originalMat = materials[i];
Material ghostMat;
if (!materialTable.ContainsKey(originalMat)) {
ghostMat = new Material(originalMat);
ghostMat.shader = ghostShader;
ghostMat.color = Color.white;
if (ghostMat.HasProperty("_TextureFade"))
ghostMat.SetFloat("_TextureFade", textureFade);
materialTable.Add(originalMat, ghostMat);
} else {
ghostMat = materialTable[originalMat];
}
materials[i] = ghostMat;
}
pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingOrder - 1);
go.transform.parent = transform;
go.transform.localPosition = Vector3.zero;
go.transform.localRotation = Quaternion.identity;
go.transform.localScale = Vector3.one;
go.transform.parent = null;
poolIndex++;
if (poolIndex == pool.Length)
poolIndex = 0;
nextSpawnTime = Time.time + spawnRate;
}
}
void OnDestroy () {
for (int i = 0; i < maximumGhosts; i++) {
if (pool[i] != null)
pool[i].Cleanup();
}
foreach (var mat in materialTable.Values)
Destroy(mat);
}
//based on UnifyWiki http://wiki.unity3d.com/index.php?title=HexConverter
static Color32 HexToColor (string hex) {
if (hex.Length < 6)
return Color.magenta;
hex = hex.Replace("#", "");
byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
byte a = 0xFF;
if (hex.Length == 8)
a = byte.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
return new Color32(r, g, b, a);
}
}

View File

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 02f2fa991881c6d419500ccc40ad443f
timeCreated: 1431858330
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences:
- ghostShader: {fileID: 4800000, guid: 3873d4699ee8a4b4da8fa6b8c229b94d, type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,109 @@
/*****************************************************************************
* SkeletonGhostRenderer created by Mitch Thompson
* Full irrevocable rights and permissions granted to Esoteric Software
*****************************************************************************/
using UnityEngine;
using System.Collections;
public class SkeletonGhostRenderer : MonoBehaviour {
public float fadeSpeed = 10;
Color32[] colors;
Color32 black = new Color32(0, 0, 0, 0);
MeshFilter meshFilter;
MeshRenderer meshRenderer;
void Awake () {
meshRenderer = gameObject.AddComponent<MeshRenderer>();
meshFilter = gameObject.AddComponent<MeshFilter>();
}
public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingOrder) {
StopAllCoroutines();
gameObject.SetActive(true);
meshRenderer.sharedMaterials = materials;
meshRenderer.sortingOrder = sortingOrder;
meshFilter.sharedMesh = (Mesh)Instantiate(mesh);
colors = meshFilter.sharedMesh.colors32;
if ((color.a + color.r + color.g + color.b) > 0) {
for (int i = 0; i < colors.Length; i++) {
colors[i] = color;
}
}
fadeSpeed = speed;
if (additive)
StartCoroutine(FadeAdditive());
else
StartCoroutine(Fade());
}
IEnumerator Fade () {
Color32 c;
for (int t = 0; t < 500; t++) {
bool breakout = true;
for (int i = 0; i < colors.Length; i++) {
c = colors[i];
if (c.a > 0)
breakout = false;
colors[i] = Color32.Lerp(c, black, Time.deltaTime * fadeSpeed);
}
meshFilter.sharedMesh.colors32 = colors;
if (breakout)
break;
yield return null;
}
Destroy(meshFilter.sharedMesh);
gameObject.SetActive(false);
}
IEnumerator FadeAdditive () {
Color32 c;
Color32 black = this.black;
for (int t = 0; t < 500; t++) {
bool breakout = true;
for (int i = 0; i < colors.Length; i++) {
c = colors[i];
black.a = c.a;
if (c.r > 0 || c.g > 0 || c.b > 0)
breakout = false;
colors[i] = Color32.Lerp(c, black, Time.deltaTime * fadeSpeed);
}
meshFilter.sharedMesh.colors32 = colors;
if (breakout)
break;
yield return null;
}
Destroy(meshFilter.sharedMesh);
gameObject.SetActive(false);
}
public void Cleanup () {
if (meshFilter != null && meshFilter.sharedMesh != null)
Destroy(meshFilter.sharedMesh);
Destroy(gameObject);
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 58e3a9b80754b7545a1dff4d8475b51f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: