mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-10 17:18:44 +08:00
[Unity] Added SkeletonGhost stuff
SkeletonGhost Shader contributed by Alex Dixon
This commit is contained in:
parent
45d8adb64c
commit
aef9b23a20
5
spine-unity/Assets/spine-unity/Ghost.meta
Normal file
5
spine-unity/Assets/spine-unity/Ghost.meta
Normal file
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13193c9d213765f4c85f4c1faa615711
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
5
spine-unity/Assets/spine-unity/Ghost/Shaders.meta
Normal file
5
spine-unity/Assets/spine-unity/Ghost/Shaders.meta
Normal file
@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0cee0de78cef7440ae0b5aac39ae971
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@ -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.
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3873d4699ee8a4b4da8fa6b8c229b94d
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
147
spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs
Normal file
147
spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
13
spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs.meta
Normal file
13
spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs.meta
Normal 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:
|
||||
109
spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs
Normal file
109
spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58e3a9b80754b7545a1dff4d8475b51f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
x
Reference in New Issue
Block a user