1. svn -> git
This commit is contained in:
5
Assets/Reference/Destructible2D/Required/Editor.meta
Normal file
5
Assets/Reference/Destructible2D/Required/Editor.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62daea4a87c9d8041a7149462a18c08b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_Anchor))]
|
||||
public class D2D_Anchor_Editor : D2D_Editor<D2D_Anchor>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
DrawDefault("Radius");
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86957fea163f95e48aa5258ea8b9d704
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_AutoPolygonCollider))]
|
||||
public class D2D_AutoPolygonCollider_Editor : D2D_Collider_Editor<D2D_AutoPolygonCollider>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
base.OnInspector();
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36ed20a27ed98c445add19717f9592a5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,52 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_Breakable))]
|
||||
public class D2D_Breakable_Editor : D2D_Editor<D2D_Breakable>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
if (Any(t => t.GetComponent<D2D_DestructibleSprite>().AllowSplit == false))
|
||||
{
|
||||
if (HelpButton("Breakable sprites require Allow Split", MessageType.Error, "Set Allow Split", 50.0f) == true)
|
||||
{
|
||||
Each(t => t.GetComponent<D2D_DestructibleSprite>().AllowSplit = true);
|
||||
}
|
||||
}
|
||||
|
||||
if (Any(t => t.GetComponent<Rigidbody2D>().isKinematic == false))
|
||||
{
|
||||
if (HelpButton("Breakable sprites require Is Kinematic", MessageType.Error, "Set Is Kinematic", 50.0f) == true)
|
||||
{
|
||||
Each(t => t.GetComponent<Rigidbody2D>().isKinematic = true);
|
||||
}
|
||||
}
|
||||
|
||||
DrawDefault("ChangeColliderType");
|
||||
|
||||
if (Any(t => t.ChangeColliderType == true))
|
||||
{
|
||||
DrawDefault("NewColliderType");
|
||||
|
||||
if (Any(t => t.NewColliderType == D2D_SpriteColliderType.Edge))
|
||||
{
|
||||
if (HelpButton("Dynamic edge colliders may not work well", MessageType.Warning, "Change", 50.0f) == true)
|
||||
{
|
||||
Each(t => {if (t.NewColliderType == D2D_SpriteColliderType.Edge) t.NewColliderType = D2D_SpriteColliderType.AutoPolygon; });
|
||||
}
|
||||
}
|
||||
|
||||
if (Any(t => t.NewColliderType == t.GetComponent<D2D_DestructibleSprite>().ColliderType))
|
||||
{
|
||||
EditorGUILayout.HelpBox("This is the same Collider Type as the source.", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
BeginError(Any(t => t.Anchors.Count == 0));
|
||||
{
|
||||
DrawDefault("Anchors");
|
||||
}
|
||||
EndError();
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b7b26c214aacad41b43412fc62d9828
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
public abstract class D2D_Collider_Editor<T> : D2D_Editor<T>
|
||||
where T : D2D_Collider
|
||||
{
|
||||
private static bool hide;
|
||||
|
||||
protected override void OnInspector()
|
||||
{
|
||||
DrawHide();
|
||||
DrawDefault("IsTrigger");
|
||||
DrawDefault("Material");
|
||||
|
||||
foreach (var t in Targets)
|
||||
{
|
||||
t.UpdateColliderSettings();
|
||||
t.SetHideFlags(hide);
|
||||
}
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
}
|
||||
|
||||
protected void BeginChangeCheck()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
}
|
||||
|
||||
protected void EndChangeCheck()
|
||||
{
|
||||
if (EditorGUI.EndChangeCheck() == true)
|
||||
{
|
||||
foreach (var t in Targets)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
t.MarkAsDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawHide()
|
||||
{
|
||||
var position = D2D_Helper.Reserve();
|
||||
|
||||
if (D2D_Helper.BaseRectSet == true)
|
||||
{
|
||||
position.x = D2D_Helper.BaseRect.x;
|
||||
position.width = D2D_Helper.BaseRect.width;
|
||||
}
|
||||
|
||||
hide = EditorGUI.Toggle(position, "Hide", hide);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b443d662ea688434b9e17d1d8e6f1456
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,26 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_DamageableSprite))]
|
||||
public class D2D_DamageableSprite_Editor : D2D_Editor<D2D_DamageableSprite>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
DrawDefault("Damage");
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
DrawDefault("AllowDestruction");
|
||||
|
||||
if (Any(t => t.AllowDestruction == true))
|
||||
{
|
||||
DrawDefault("DamageLimit");
|
||||
DrawDefault("ReplaceWith");
|
||||
}
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
DrawDefault("DamageLevels");
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be46c5e59ac012d4cb2d95c64da82a8d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,90 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_DestructibleSprite))]
|
||||
public class D2D_DestructibleSprite_Editor : D2D_Editor<D2D_DestructibleSprite>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
DrawAlphaTex();
|
||||
DrawDefault("DensityTex");
|
||||
|
||||
BeginError(Any(t => t.SourceMaterial == null));
|
||||
{
|
||||
DrawDefault("SourceMaterial");
|
||||
}
|
||||
EndError();
|
||||
|
||||
if (Any(t => t.SourceMaterial != null && AssetDatabase.Contains(t.SourceMaterial) == false))
|
||||
{
|
||||
EditorGUILayout.HelpBox("This material isn't an asset, so you won't be able to store this Destructible Sprite in a prefab.", MessageType.Warning);
|
||||
}
|
||||
|
||||
if (Any(t => t.SourceMaterial != null && t.SourceMaterial.HasProperty("_AlphaTex") == false))
|
||||
{
|
||||
EditorGUILayout.HelpBox("This material lacks the _AlphaTex property, so it will not render correctly.", MessageType.Error);
|
||||
}
|
||||
|
||||
DrawDefault("Sharpness");
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
DrawDefault("ColliderType");
|
||||
|
||||
if (Any(t => t.ColliderType == D2D_SpriteColliderType.AutoPolygon && t.AlphaTex != null && t.AlphaTex.width * t.AlphaTex.height > 400 * 400))
|
||||
{
|
||||
if (HelpButton("Your Alpha Tex is quite large, and it may run slowly with this Collider Type. Consider lowering its resolution.", MessageType.Warning, "Halve Alpha Tex", 50.0f) == true)
|
||||
{
|
||||
Each(t => t.HalveAlphaTex());
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
DrawDefault("AllowSplit");
|
||||
|
||||
if (Any(t => t.AllowSplit == true && t.AlphaTex != null && t.AlphaTex.width * t.AlphaTex.height > 400 * 400))
|
||||
{
|
||||
if (HelpButton("Your Alpha Tex is quite large, and it may run slowly with Allow Split. Consider lowering its resolution.", MessageType.Warning, "Halve Alpha Tex", 50.0f) == true)
|
||||
{
|
||||
Each(t => t.HalveAlphaTex());
|
||||
}
|
||||
}
|
||||
|
||||
if (Any(t => t.AllowSplit == true))
|
||||
{
|
||||
DrawDefault("SplitMinPixels");
|
||||
DrawDefault("SplitThreshold");
|
||||
DrawDefault("SplitOrder");
|
||||
}
|
||||
|
||||
if (Targets.Length == 1 && AssetDatabase.Contains(Target) == false)
|
||||
{
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
{
|
||||
EditorGUILayout.IntField("Solid Pixel Count", Target.SolidPixelCount);
|
||||
EditorGUILayout.IntField("Original Solid Pixel Count", Target.OriginalSolidPixelCount);
|
||||
EditorGUILayout.Slider("Solid Pixel Ratio", Target.SolidPixelRatio, 0.0f, 1.0f);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawAlphaTex()
|
||||
{
|
||||
var newAlphaTex = (Texture2D)EditorGUI.ObjectField(D2D_Helper.Reserve(), "Alpha Tex", Target.AlphaTex, typeof(Texture2D), false);
|
||||
|
||||
if (newAlphaTex != Target.AlphaTex)
|
||||
{
|
||||
D2D_Helper.Record(Targets, "Replace Destructible Sprite Alpha");
|
||||
|
||||
foreach (var t in Targets)
|
||||
{
|
||||
t.ReplaceAlphaWith(newAlphaTex); t.RecalculateOriginalSolidPixelCount(); D2D_Helper.SetDirty(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c466f1ced95651f40819391968a6cd22
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_DynamicMass))]
|
||||
public class D2D_DynamicMass_Editor : D2D_Editor<D2D_DynamicMass>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
DrawDefault("MassPerPixel");
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 971a59b36bd50894ab5197ed429afc3b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_EdgeColliders))]
|
||||
public class D2D_EdgeColliders_Editor : D2D_Collider_Editor<D2D_EdgeColliders>
|
||||
{
|
||||
private static bool hideColliders;
|
||||
|
||||
protected override void OnInspector()
|
||||
{
|
||||
base.OnInspector();
|
||||
|
||||
BeginChangeCheck();
|
||||
{
|
||||
DrawDefault("CellSize");
|
||||
DrawDefault("Tolerance");
|
||||
}
|
||||
EndChangeCheck();
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc588e4e2ce390b4eab278efa7470f5e
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
141
Assets/Reference/Destructible2D/Required/Editor/D2D_Editor.cs
Normal file
141
Assets/Reference/Destructible2D/Required/Editor/D2D_Editor.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
|
||||
public abstract class D2D_Editor<T> : Editor
|
||||
where T : MonoBehaviour
|
||||
{
|
||||
protected T Target;
|
||||
|
||||
protected T[] Targets;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
D2D_Helper.BaseRect = D2D_Helper.Reserve(0.0f);
|
||||
D2D_Helper.BaseRectSet = true;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
serializedObject.UpdateIfDirtyOrScript();
|
||||
|
||||
Target = (T)target;
|
||||
Targets = targets.Select(t => (T)t).ToArray();
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
OnInspector();
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (EditorGUI.EndChangeCheck() == true)
|
||||
{
|
||||
GUI.changed = true; Repaint();
|
||||
|
||||
foreach (var t in Targets)
|
||||
{
|
||||
D2D_Helper.SetDirty(t);
|
||||
}
|
||||
}
|
||||
|
||||
D2D_Helper.BaseRectSet = false;
|
||||
}
|
||||
|
||||
public virtual void OnSceneGUI()
|
||||
{
|
||||
Target = (T)target;
|
||||
|
||||
OnScene();
|
||||
|
||||
if (GUI.changed == true)
|
||||
{
|
||||
D2D_Helper.SetDirty(target);
|
||||
}
|
||||
}
|
||||
|
||||
protected void Each(System.Action<T> update)
|
||||
{
|
||||
foreach (var t in Targets)
|
||||
{
|
||||
update(t);
|
||||
}
|
||||
}
|
||||
|
||||
protected bool Any(System.Func<T, bool> check)
|
||||
{
|
||||
foreach (var t in Targets)
|
||||
{
|
||||
if (check(t) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool All(System.Func<T, bool> check)
|
||||
{
|
||||
foreach (var t in Targets)
|
||||
{
|
||||
if (check(t) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool Button(string text)
|
||||
{
|
||||
var rect = D2D_Helper.Reserve();
|
||||
|
||||
return GUI.Button(rect, text);
|
||||
}
|
||||
|
||||
protected bool HelpButton(string helpText, UnityEditor.MessageType type, string buttonText, float buttonWidth)
|
||||
{
|
||||
var clicked = false;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
{
|
||||
EditorGUILayout.HelpBox(helpText, type);
|
||||
|
||||
var style = new GUIStyle(GUI.skin.button); style.wordWrap = true;
|
||||
|
||||
clicked = GUILayout.Button(buttonText, style, GUILayout.ExpandHeight(true), GUILayout.Width(buttonWidth));
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
protected void BeginError(bool error = true)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(error == true ? D2D_Helper.Error : D2D_Helper.NoError);
|
||||
}
|
||||
|
||||
protected void EndError()
|
||||
{
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
protected void DrawDefault(string proeprtyPath)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(D2D_Helper.NoError);
|
||||
{
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty(proeprtyPath), true);
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
protected virtual void OnInspector()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnScene()
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dca63b284a7b1ee4f8fbe4edc4d10b49
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_ExplosionForce))]
|
||||
public class D2D_ExplosionForce_Editor : D2D_Editor<D2D_ExplosionForce>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
DrawDefault("Layers");
|
||||
DrawDefault("Radius");
|
||||
DrawDefault("Force");
|
||||
DrawDefault("Samples");
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
DrawDefault("HasExploded");
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe7add65c216c6c4399e6d3e58403285
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_ExplosionStamp))]
|
||||
public class D2D_ExplosionStamp_Editor : D2D_Editor<D2D_ExplosionStamp>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
DrawDefault("Layers");
|
||||
DrawDefault("StampTex");
|
||||
DrawDefault("Hardness");
|
||||
DrawDefault("Size");
|
||||
DrawDefault("AngleOffset");
|
||||
DrawDefault("AngleRandomness");
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
DrawDefault("HasExploded");
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26e615c1ebb07f141a540f956b424657
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_Fixture))]
|
||||
public class D2D_Fixture_Editor : D2D_Editor<D2D_Fixture>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
if (Any(t => D2D_Helper.GetComponentUpwards<D2D_DestructibleSprite>(t.transform) == null))
|
||||
{
|
||||
EditorGUILayout.HelpBox("A parent of this GameObject should have the D2D_DestructibleSprite component", MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("This GameObject will get destroyed when the fixture point is removed.", MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 629bcc221d923574e85dda07e518b213
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_PolygonColliders))]
|
||||
public class D2D_PolygonColliders_Editor : D2D_Collider_Editor<D2D_PolygonColliders>
|
||||
{
|
||||
private static bool hideColliders;
|
||||
|
||||
protected override void OnInspector()
|
||||
{
|
||||
base.OnInspector();
|
||||
|
||||
BeginChangeCheck();
|
||||
{
|
||||
DrawDefault("CellSize");
|
||||
DrawDefault("Tolerance");
|
||||
}
|
||||
EndChangeCheck();
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b2b6e48f46b12348a8bed882d67e93c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(D2D_RuntimeSprite))]
|
||||
public class D2D_RuntimeSprite_Editor : D2D_Editor<D2D_RuntimeSprite>
|
||||
{
|
||||
protected override void OnInspector()
|
||||
{
|
||||
EditorGUILayout.HelpBox("This component will clean up the runtime generated Sprite used to make this GameObject.", MessageType.Info);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcefc71840b1c414ea79d9c96e4d8b62
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
5
Assets/Reference/Destructible2D/Required/Library.meta
Normal file
5
Assets/Reference/Destructible2D/Required/Library.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4801e87083d3df640b53d19c3688abd6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@@ -0,0 +1,443 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static class D2D_EdgeCalculator
|
||||
{
|
||||
enum Edge
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Bottom,
|
||||
Top
|
||||
}
|
||||
|
||||
class Point
|
||||
{
|
||||
public bool Used;
|
||||
|
||||
public Point Other;
|
||||
|
||||
public Vector2 Position;
|
||||
|
||||
public Edge Inner;
|
||||
|
||||
public Edge Outer;
|
||||
}
|
||||
|
||||
class Cell
|
||||
{
|
||||
public int X;
|
||||
|
||||
public int Y;
|
||||
|
||||
public int PointIndex;
|
||||
|
||||
public int PointCount;
|
||||
}
|
||||
|
||||
private static int xMin;
|
||||
|
||||
private static int xMax;
|
||||
|
||||
private static int yMin;
|
||||
|
||||
private static int yMax;
|
||||
|
||||
private static int width;
|
||||
|
||||
private static int height;
|
||||
|
||||
private static int cellCount;
|
||||
|
||||
private static List<Cell> cells = new List<Cell>();
|
||||
|
||||
private static List<EdgeCollider2D> generatedEdgeColliders = new List<EdgeCollider2D>();
|
||||
|
||||
private static List<Vector2> generatedPoints = new List<Vector2>();
|
||||
|
||||
private static int cellPointCount;
|
||||
|
||||
private static List<Point> cellPoints = new List<Point>();
|
||||
|
||||
private static LinkedList<Point> tracedEdges = new LinkedList<Point>();
|
||||
|
||||
private static Vector2 translation;
|
||||
|
||||
private static Color[] pixels;
|
||||
|
||||
private static int pixelsX;
|
||||
|
||||
private static int pixelsY;
|
||||
|
||||
private static int pixelsWidth;
|
||||
|
||||
private static int pixelsHeight;
|
||||
|
||||
private static void GetPixels(Texture2D alphaTex, int l, int b, int r, int t)
|
||||
{
|
||||
l = Mathf.Max(l, 0);
|
||||
b = Mathf.Max(b, 0);
|
||||
r = Mathf.Min(r, alphaTex.width);
|
||||
t = Mathf.Min(t, alphaTex.height);
|
||||
|
||||
pixelsX = l;
|
||||
pixelsY = b;
|
||||
pixelsWidth = r - l;
|
||||
pixelsHeight = t - b;
|
||||
pixels = alphaTex.GetPixels(pixelsX, pixelsY, pixelsWidth, pixelsHeight);
|
||||
}
|
||||
|
||||
public static List<EdgeCollider2D> Generate(GameObject gameObject, Texture2D alphaTex, int newXMin, int newXMax, int newYMin, int newYMax, float tolerance)
|
||||
{
|
||||
cellCount = 0;
|
||||
cellPointCount = 0;
|
||||
|
||||
generatedEdgeColliders.Clear();
|
||||
|
||||
if (gameObject != null && alphaTex != null)
|
||||
{
|
||||
xMin = newXMin; if (xMin == 0) xMin -= 1; // Expand left?
|
||||
yMin = newYMin; if (yMin == 0) yMin -= 1; // Expand bottom?
|
||||
xMax = newXMax;
|
||||
yMax = newYMax;
|
||||
width = xMax - xMin;
|
||||
height = yMax - yMin;
|
||||
|
||||
GetPixels(alphaTex, newXMin - 1, newYMin - 1, newXMax + 1, newYMax + 1);
|
||||
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
if (cells.Capacity < width * height)
|
||||
{
|
||||
cells.Capacity = width * height;
|
||||
}
|
||||
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
var cell = GetNextCell();
|
||||
var bl = GetAlphaOrDefault(xMin + x , yMin + y );
|
||||
var br = GetAlphaOrDefault(xMin + x + 1, yMin + y );
|
||||
var tl = GetAlphaOrDefault(xMin + x , yMin + y + 1);
|
||||
var tr = GetAlphaOrDefault(xMin + x + 1, yMin + y + 1);
|
||||
|
||||
cell.X = x;
|
||||
cell.Y = y;
|
||||
|
||||
translation.x = xMin + x + 0.5f;
|
||||
translation.y = yMin + y + 0.5f;
|
||||
|
||||
CalculateCell(cell, bl, br, tl, tr, 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < cellCount; i++)
|
||||
{
|
||||
var cell = cells[i];
|
||||
|
||||
for (var j = 0; j < cell.PointCount; j++)
|
||||
{
|
||||
var point = cellPoints[cell.PointIndex + j];
|
||||
|
||||
if (point.Used == false)
|
||||
{
|
||||
TraceEdges(cell, point);
|
||||
|
||||
OptimizeEdges(tolerance);
|
||||
|
||||
CompileTracedEdges(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
var border = gameObject.AddComponent<EdgeCollider2D>(); generatedEdgeColliders.Add(border);
|
||||
|
||||
border.points = new Vector2[] {
|
||||
new Vector2(xMin, yMin),
|
||||
new Vector2(xMax, yMin),
|
||||
new Vector2(xMax, yMax),
|
||||
new Vector2(xMin, yMax),
|
||||
new Vector2(xMin, yMin) };*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return generatedEdgeColliders;
|
||||
}
|
||||
|
||||
private static Cell GetNextCell()
|
||||
{
|
||||
var cell = default(Cell);
|
||||
|
||||
if (cellCount >= cells.Count)
|
||||
{
|
||||
cell = new Cell(); cells.Add(cell);
|
||||
}
|
||||
else
|
||||
{
|
||||
cell = cells[cellCount];
|
||||
}
|
||||
|
||||
cellCount += 1;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
private static Point GetNextCellPoint()
|
||||
{
|
||||
var point = default(Point);
|
||||
|
||||
if (cellPointCount >= cellPoints.Count)
|
||||
{
|
||||
point = new Point(); cellPoints.Add(point);
|
||||
}
|
||||
else
|
||||
{
|
||||
point = cellPoints[cellPointCount];
|
||||
point.Used = false;
|
||||
}
|
||||
|
||||
cellPointCount += 1;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
private static void CompileTracedEdges(GameObject gameObject)
|
||||
{
|
||||
var edgeCollider2D = gameObject.AddComponent<EdgeCollider2D>(); generatedEdgeColliders.Add(edgeCollider2D);
|
||||
|
||||
generatedPoints.Clear();
|
||||
|
||||
foreach (var point in tracedEdges)
|
||||
{
|
||||
generatedPoints.Add(point.Position);
|
||||
}
|
||||
|
||||
edgeCollider2D.points = generatedPoints.ToArray();
|
||||
}
|
||||
|
||||
private static void CalculateCell(Cell cell, float bl, float br, float tl, float tr, float threshold)
|
||||
{
|
||||
var index = cellPointCount;
|
||||
var count = 0;
|
||||
var useBl = bl >= threshold;
|
||||
var useBr = br >= threshold;
|
||||
var useTl = tl >= threshold;
|
||||
var useTr = tr >= threshold;
|
||||
|
||||
// Top
|
||||
if (useTl ^ useTr)
|
||||
{
|
||||
var point = GetNextCellPoint(); count += 1;
|
||||
|
||||
point.Position = Transform((tl - threshold) / (tl - tr), 1.0f);
|
||||
point.Inner = Edge.Top;
|
||||
point.Outer = Edge.Bottom;
|
||||
}
|
||||
|
||||
// Right
|
||||
if (useTr ^ useBr)
|
||||
{
|
||||
var point = GetNextCellPoint(); count += 1;
|
||||
|
||||
point.Position = Transform(1.0f, (br - threshold) / (br - tr));
|
||||
point.Inner = Edge.Right;
|
||||
point.Outer = Edge.Left;
|
||||
}
|
||||
|
||||
// Bottom
|
||||
if (useBl ^ useBr)
|
||||
{
|
||||
var point = GetNextCellPoint(); count += 1;
|
||||
|
||||
point.Position = Transform((bl - threshold) / (bl - br), 0.0f);
|
||||
point.Inner = Edge.Bottom;
|
||||
point.Outer = Edge.Top;
|
||||
}
|
||||
|
||||
// Left
|
||||
if (useTl ^ useBl)
|
||||
{
|
||||
var point = GetNextCellPoint(); count += 1;
|
||||
|
||||
point.Position = Transform(0.0f, (bl - threshold) / (bl - tl));
|
||||
point.Inner = Edge.Left;
|
||||
point.Outer = Edge.Right;
|
||||
}
|
||||
|
||||
// Pair up points
|
||||
switch (count)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
cellPoints[index + 0].Other = cellPoints[index + 1];
|
||||
cellPoints[index + 1].Other = cellPoints[index + 0];
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
if (useTl == true && useBr == true)
|
||||
{
|
||||
cellPoints[index + 0].Other = cellPoints[index + 3];
|
||||
cellPoints[index + 1].Other = cellPoints[index + 2];
|
||||
cellPoints[index + 2].Other = cellPoints[index + 1];
|
||||
cellPoints[index + 3].Other = cellPoints[index + 0];
|
||||
}
|
||||
else
|
||||
{
|
||||
cellPoints[index + 0].Other = cellPoints[index + 1];
|
||||
cellPoints[index + 1].Other = cellPoints[index + 0];
|
||||
cellPoints[index + 2].Other = cellPoints[index + 3];
|
||||
cellPoints[index + 3].Other = cellPoints[index + 2];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cell.PointIndex = index;
|
||||
cell.PointCount = count;
|
||||
}
|
||||
|
||||
private static void TraceEdges(Cell cell, Point point)
|
||||
{
|
||||
tracedEdges.Clear();
|
||||
|
||||
TraceEdge(cell, point, false);
|
||||
TraceEdge(cell, point.Other, true);
|
||||
}
|
||||
|
||||
private static void TraceEdge(Cell cell, Point point, bool last)
|
||||
{
|
||||
point.Used = true;
|
||||
|
||||
if (last == true)
|
||||
{
|
||||
tracedEdges.AddLast(point);
|
||||
}
|
||||
else
|
||||
{
|
||||
tracedEdges.AddFirst(point);
|
||||
}
|
||||
|
||||
switch (point.Inner)
|
||||
{
|
||||
case Edge.Left:
|
||||
{
|
||||
cell = GetCell(cell.X - 1, cell.Y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Edge.Right:
|
||||
{
|
||||
cell = GetCell(cell.X + 1, cell.Y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Edge.Bottom:
|
||||
{
|
||||
cell = GetCell(cell.X, cell.Y - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case Edge.Top:
|
||||
{
|
||||
cell = GetCell(cell.X, cell.Y + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (cell != null)
|
||||
{
|
||||
for (var i = 0; i < cell.PointCount; i++)
|
||||
{
|
||||
var outerPoint = cellPoints[cell.PointIndex + i];
|
||||
|
||||
if (outerPoint.Used == false && outerPoint.Inner == point.Outer)
|
||||
{
|
||||
outerPoint.Used = true;
|
||||
|
||||
TraceEdge(cell, outerPoint.Other, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OptimizeEdges(float tolerance)
|
||||
{
|
||||
if (tolerance > 0.0f && tracedEdges.Count > 2)
|
||||
{
|
||||
var a = tracedEdges.First;
|
||||
var b = a.Next;
|
||||
var c = b.Next;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var ab = Vector3.Normalize(b.Value.Position - a.Value.Position);
|
||||
var bc = Vector3.Normalize(c.Value.Position - b.Value.Position);
|
||||
var abc = Vector3.Dot(ab, bc);
|
||||
|
||||
if (abc > (1.0f - tolerance))
|
||||
{
|
||||
tracedEdges.Remove(b);
|
||||
|
||||
if (c != tracedEdges.Last)
|
||||
{
|
||||
b = c;
|
||||
c = c.Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c != tracedEdges.Last)
|
||||
{
|
||||
a = b;
|
||||
b = c;
|
||||
c = c.Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float GetAlphaOrDefault(int x, int y)
|
||||
{
|
||||
x -= pixelsX;
|
||||
y -= pixelsY;
|
||||
|
||||
if (x >= 0 && y >= 0 && x < pixelsWidth && y < pixelsHeight)
|
||||
{
|
||||
return pixels[x + y * pixelsWidth].a;
|
||||
}
|
||||
|
||||
return default(float);
|
||||
}
|
||||
|
||||
private static Cell GetCell(int x, int y)
|
||||
{
|
||||
if (x >= 0 && y >= 0 && x < width && y < height)
|
||||
{
|
||||
return cells[x + y * width];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Vector2 Transform(float x, float y)
|
||||
{
|
||||
x = x + translation.x;
|
||||
y = y + translation.y;
|
||||
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ff43b95fef4b8240b7c27f680217911
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
512
Assets/Reference/Destructible2D/Required/Library/D2D_Helper.cs
Normal file
512
Assets/Reference/Destructible2D/Required/Library/D2D_Helper.cs
Normal file
@@ -0,0 +1,512 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static partial class D2D_Helper
|
||||
{
|
||||
public static int MeshVertexLimit = 65000;
|
||||
|
||||
public static void GetPixelsBilinear(Texture2D t, int x, int y)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void Swap<T>(ref T a, ref T b)
|
||||
{
|
||||
var c = b;
|
||||
|
||||
b = a;
|
||||
a = c;
|
||||
}
|
||||
|
||||
public static float Atan2(Vector2 xy)
|
||||
{
|
||||
return Mathf.Atan2(xy.x, xy.y);
|
||||
}
|
||||
|
||||
public static void ResizeArrayTo<T>(List<T> array, int size, System.Func<int, T> newT, System.Action<T> removeT)
|
||||
{
|
||||
if (array != null)
|
||||
{
|
||||
while (array.Count < size)
|
||||
{
|
||||
array.Add(newT != null ? newT(array.Count) : default(T));
|
||||
}
|
||||
|
||||
while (array.Count > size)
|
||||
{
|
||||
if (removeT != null)
|
||||
{
|
||||
removeT(array[array.Count - 1]);
|
||||
}
|
||||
|
||||
array.RemoveAt(array.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HideFlags oldHideFlags;
|
||||
|
||||
public static void BeginStealthSet(Object o)
|
||||
{
|
||||
if (o != null)
|
||||
{
|
||||
oldHideFlags = o.hideFlags;
|
||||
|
||||
o.hideFlags = HideFlags.DontSave;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndStealthSet(Object o)
|
||||
{
|
||||
if (o != null)
|
||||
{
|
||||
o.hideFlags = oldHideFlags;
|
||||
}
|
||||
}
|
||||
|
||||
public static void StealthSet(MeshFilter mf, Mesh m)
|
||||
{
|
||||
if (mf != null && mf.sharedMesh != m)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var hf = mf.hideFlags;
|
||||
|
||||
mf.hideFlags = HideFlags.DontSave;
|
||||
mf.sharedMesh = m;
|
||||
mf.hideFlags = hf;
|
||||
#else
|
||||
mf.sharedMesh = m;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static void StealthSet(MeshRenderer mr, Material m)
|
||||
{
|
||||
if (mr != null && mr.sharedMaterial != m)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var hf = mr.hideFlags;
|
||||
|
||||
mr.hideFlags = HideFlags.DontSave;
|
||||
mr.sharedMaterial = m;
|
||||
mr.hideFlags = hf;
|
||||
#else
|
||||
mr.sharedMaterial = m;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetPosition(Transform t, Vector3 v)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying == false && t.position == v) return;
|
||||
#endif
|
||||
t.position = v;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Destroy(Object o)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying == false)
|
||||
{
|
||||
Object.DestroyImmediate(o, true); return;
|
||||
}
|
||||
#endif
|
||||
|
||||
Object.Destroy(o);
|
||||
}
|
||||
|
||||
public static void DestroyManaged(System.Action DestroyAction)
|
||||
{
|
||||
if (DestroyAction != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var isPlaying = Application.isPlaying;
|
||||
|
||||
UnityEditor.EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (Application.isPlaying == isPlaying)
|
||||
{
|
||||
DestroyAction();
|
||||
}
|
||||
};
|
||||
#else
|
||||
DestroyAction();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetParent(Transform t, Transform newParent, bool keepLocalTransform = true)
|
||||
{
|
||||
if (t != null && t.parent != newParent)
|
||||
{
|
||||
if (keepLocalTransform == true)
|
||||
{
|
||||
var oldLocalPosition = t.localPosition;
|
||||
var oldLocalRotation = t.localRotation;
|
||||
var oldLocalScale = t.localScale;
|
||||
|
||||
t.parent = newParent;
|
||||
t.localPosition = oldLocalPosition;
|
||||
t.localRotation = oldLocalRotation;
|
||||
t.localScale = oldLocalScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
t.parent = newParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetLocalPosition(Transform t, float x, float y, float z)
|
||||
{
|
||||
SetLocalPosition(t, new Vector3(x, y, z));
|
||||
}
|
||||
|
||||
public static void SetLocalPosition(Transform t, Vector3 v)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying == false && t.localPosition == v) return;
|
||||
#endif
|
||||
t.localPosition = v;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetLocalScale(Transform t, float v)
|
||||
{
|
||||
SetLocalScale(t, new Vector3(v, v, v));
|
||||
}
|
||||
|
||||
public static void SetLocalScale(Transform t, Vector3 v)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying == false && t.localScale == v) return;
|
||||
#endif
|
||||
if (t.localScale == v) return;
|
||||
|
||||
t.localScale = v;
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetComponentUpwards<T>(Transform transform, bool skipFirst = false)
|
||||
where T : Component
|
||||
{
|
||||
if (transform != null)
|
||||
{
|
||||
if (skipFirst == true)
|
||||
{
|
||||
transform = transform.parent;
|
||||
}
|
||||
|
||||
while (transform != null)
|
||||
{
|
||||
var component = transform.GetComponent<T>();
|
||||
|
||||
if (component != null) return component;
|
||||
|
||||
transform = transform.parent;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static T GetOrAddComponent<T>(GameObject gameObject)
|
||||
where T : Component
|
||||
{
|
||||
if (gameObject != null)
|
||||
{
|
||||
var component = gameObject.GetComponent<T>();
|
||||
|
||||
if (component == null) component = gameObject.AddComponent<T>();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static GameObject CloneGameObject(GameObject source, Transform parent, bool keepName = false)
|
||||
{
|
||||
if (source != null)
|
||||
{
|
||||
var clone = (GameObject)GameObject.Instantiate(source); if (clone == null) throw new System.NullReferenceException();
|
||||
|
||||
if (parent != null) SetParent(clone.transform, parent, true);
|
||||
if (keepName == true) clone.name = source.name;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public static GameObject CloneGameObject(GameObject source, Transform parent, Vector3 xyz, Quaternion rot, bool keepName = false)
|
||||
{
|
||||
if (source != null)
|
||||
{
|
||||
var clone = (GameObject)GameObject.Instantiate(source, xyz, rot); if (clone == null) throw new System.NullReferenceException();
|
||||
|
||||
if (parent != null) SetParent(clone.transform, parent, true);
|
||||
if (keepName == true) clone.name = source.name;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public static GameObject CreateGameObject(string name = "", Transform parent = null)
|
||||
{
|
||||
return CreateGameObject(name, parent, Vector3.zero, Quaternion.identity, Vector3.one);
|
||||
}
|
||||
|
||||
public static GameObject CreateGameObject(string name, Transform parent, Vector3 position, Quaternion identity, Vector3 scale)
|
||||
{
|
||||
var gameObject = new GameObject(name);
|
||||
|
||||
gameObject.transform.parent = parent;
|
||||
gameObject.transform.localPosition = Vector3.zero;
|
||||
gameObject.transform.localRotation = Quaternion.identity;
|
||||
gameObject.transform.localScale = Vector3.one;
|
||||
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
public static T Clone<T>(T o, bool keepName = true)
|
||||
where T : Object
|
||||
{
|
||||
if (o != null)
|
||||
{
|
||||
var c = (T)Object.Instantiate(o);
|
||||
|
||||
if (c != null && keepName == true) c.name = o.name;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool Enabled(Behaviour b)
|
||||
{
|
||||
return b != null && b.enabled == true && b.gameObject.activeInHierarchy == true;
|
||||
}
|
||||
|
||||
public static float Divide(float a, float b)
|
||||
{
|
||||
return Zero(b) == false ? a / b : 0.0f;
|
||||
}
|
||||
|
||||
public static Vector2 Divide(float xA, float yA, float xB, float yB)
|
||||
{
|
||||
return new Vector2(Divide(xA, xB), Divide(yA, yB));
|
||||
}
|
||||
|
||||
public static float Reciprocal(float v)
|
||||
{
|
||||
return Zero(v) == false ? 1.0f / v : 0.0f;
|
||||
}
|
||||
|
||||
public static Vector2 Reciprocal(Vector2 v)
|
||||
{
|
||||
return new Vector2(Reciprocal(v.x), Reciprocal(v.y));
|
||||
}
|
||||
|
||||
public static Vector2 Reciprocal(float x, float y)
|
||||
{
|
||||
return new Vector2(Reciprocal(x), Reciprocal(y));
|
||||
}
|
||||
|
||||
public static Vector3 Reciprocal(Vector3 v)
|
||||
{
|
||||
return new Vector3(Reciprocal(v.x), Reciprocal(v.y), Reciprocal(v.z));
|
||||
}
|
||||
|
||||
public static Vector3 Reciprocal(float x, float y, float z)
|
||||
{
|
||||
return new Vector3(Reciprocal(x), Reciprocal(y), Reciprocal(z));
|
||||
}
|
||||
|
||||
public static bool Zero(float v)
|
||||
{
|
||||
return Mathf.Approximately(v, 0.0f);
|
||||
}
|
||||
|
||||
public static Matrix4x4 RotationMatrix(Quaternion q)
|
||||
{
|
||||
var matrix = Matrix4x4.TRS(Vector3.zero, q, Vector3.one);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static Matrix4x4 TranslationMatrix(Vector3 xyz)
|
||||
{
|
||||
return TranslationMatrix(xyz.x, xyz.y, xyz.z);
|
||||
}
|
||||
|
||||
public static Matrix4x4 TranslationMatrix(float x, float y, float z)
|
||||
{
|
||||
var matrix = Matrix4x4.identity;
|
||||
|
||||
matrix.m03 = x;
|
||||
matrix.m13 = y;
|
||||
matrix.m23 = z;
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static Matrix4x4 ScalingMatrix(float xyz)
|
||||
{
|
||||
return ScalingMatrix(xyz, xyz, xyz);
|
||||
}
|
||||
|
||||
public static Matrix4x4 ScalingMatrix(Vector3 xyz)
|
||||
{
|
||||
return ScalingMatrix(xyz.x, xyz.y, xyz.z);
|
||||
}
|
||||
|
||||
public static Matrix4x4 ScalingMatrix(float x, float y, float z)
|
||||
{
|
||||
var matrix = Matrix4x4.identity;
|
||||
|
||||
matrix.m00 = x;
|
||||
matrix.m11 = y;
|
||||
matrix.m22 = z;
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static float DampenFactor(float dampening, float elapsed)
|
||||
{
|
||||
return 1.0f - Mathf.Pow((float)System.Math.E, - dampening * elapsed);
|
||||
}
|
||||
|
||||
public static Quaternion Dampen(Quaternion current, Quaternion target, float dampening, float elapsed, float minStep = 0.0f)
|
||||
{
|
||||
var factor = DampenFactor(dampening, elapsed);
|
||||
var maxDelta = Quaternion.Angle(current, target) * factor + minStep * elapsed;
|
||||
|
||||
return MoveTowards(current, target, maxDelta);
|
||||
}
|
||||
|
||||
public static float Dampen(float current, float target, float dampening, float elapsed, float minStep = 0.0f)
|
||||
{
|
||||
var factor = DampenFactor(dampening, elapsed);
|
||||
var maxDelta = Mathf.Abs(target - current) * factor + minStep * elapsed;
|
||||
|
||||
return MoveTowards(current, target, maxDelta);
|
||||
}
|
||||
|
||||
public static Vector3 Dampen3(Vector3 current, Vector3 target, float dampening, float elapsed, float minStep = 0.0f)
|
||||
{
|
||||
var factor = DampenFactor(dampening, elapsed);
|
||||
var maxDelta = Mathf.Abs((target - current).magnitude) * factor + minStep * elapsed;
|
||||
|
||||
return Vector3.MoveTowards(current, target, maxDelta);
|
||||
}
|
||||
|
||||
public static Quaternion MoveTowards(Quaternion current, Quaternion target, float maxDelta)
|
||||
{
|
||||
var delta = Quaternion.Angle(current, target);
|
||||
|
||||
return Quaternion.Slerp(current, target, Divide(maxDelta, delta));
|
||||
}
|
||||
|
||||
public static float MoveTowards(float current, float target, float maxDelta)
|
||||
{
|
||||
if (target > current)
|
||||
{
|
||||
current = System.Math.Min(target, current + maxDelta);
|
||||
}
|
||||
else
|
||||
{
|
||||
current = System.Math.Max(target, current - maxDelta);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
public static Vector3 ClosestPointToLineSegment(Vector3 a, Vector3 b, Vector3 point)
|
||||
{
|
||||
var l = (b - a).magnitude;
|
||||
var d = (b - a).normalized;
|
||||
|
||||
return a + Mathf.Clamp(Vector3.Dot(point - a, d), 0.0f, l) * d;
|
||||
}
|
||||
|
||||
public static Vector3 ClosestPointToTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 p)
|
||||
{
|
||||
var r = Quaternion.Inverse(Quaternion.LookRotation(-Vector3.Cross(a - b, a - c)));
|
||||
var ra = r * a;
|
||||
var rb = r * b;
|
||||
var rc = r * c;
|
||||
var rp = r * p;
|
||||
|
||||
var a2 = D2D_Helper.VectorXY(ra);
|
||||
var b2 = D2D_Helper.VectorXY(rb);
|
||||
var c2 = D2D_Helper.VectorXY(rc);
|
||||
var p2 = D2D_Helper.VectorXY(rp);
|
||||
|
||||
if (PointLeftOfLine(a2, b2, p2) == true)
|
||||
{
|
||||
return ClosestPointToLineSegment(a, b, p);
|
||||
}
|
||||
|
||||
if (PointLeftOfLine(b2, c2, p2) == true)
|
||||
{
|
||||
return ClosestPointToLineSegment(b, c, p);
|
||||
}
|
||||
|
||||
if (PointLeftOfLine(c2, a2, p2) == true)
|
||||
{
|
||||
return ClosestPointToLineSegment(c, a, p);
|
||||
}
|
||||
|
||||
var barycentric = GetBarycentric(a2, b2, c2, p2);
|
||||
|
||||
return barycentric.x * a + barycentric.y * b + barycentric.z * c;
|
||||
}
|
||||
|
||||
public static Vector3 GetBarycentric(Vector2 a, Vector2 b, Vector2 c, Vector2 p)
|
||||
{
|
||||
var barycentric = Vector3.zero;
|
||||
var v0 = b - a;
|
||||
var v1 = c - a;
|
||||
var v2 = p - a;
|
||||
var d00 = Vector2.Dot(v0, v0);
|
||||
var d01 = Vector2.Dot(v0, v1);
|
||||
var d11 = Vector2.Dot(v1, v1);
|
||||
var d20 = Vector2.Dot(v2, v0);
|
||||
var d21 = Vector2.Dot(v2, v1);
|
||||
var denom = D2D_Helper.Reciprocal(d00 * d11 - d01 * d01);
|
||||
|
||||
barycentric.y = (d11 * d20 - d01 * d21) * denom;
|
||||
barycentric.z = (d00 * d21 - d01 * d20) * denom;
|
||||
barycentric.x = 1.0f - barycentric.y - barycentric.z;
|
||||
|
||||
return barycentric;
|
||||
}
|
||||
|
||||
public static bool PointLeftOfLine(Vector2 a, Vector2 b, Vector2 p) // NOTE: CCW
|
||||
{
|
||||
return ((b.x - a.x) * (p.y - a.y) - (p.x - a.x) * (b.y - a.y)) >= 0.0f;
|
||||
}
|
||||
|
||||
public static bool PointRightOfLine(Vector2 a, Vector2 b, Vector2 p) // NOTE: CCW
|
||||
{
|
||||
return ((b.x - a.x) * (p.y - a.y) - (p.x - a.x) * (b.y - a.y)) <= 0.0f;
|
||||
}
|
||||
|
||||
public static Vector2 VectorXY(Vector3 xyz)
|
||||
{
|
||||
return new Vector2(xyz.x, xyz.y);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a0dca602cf01b8499a666502d6b1c77
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,214 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static partial class D2D_Helper
|
||||
{
|
||||
public static bool BaseRectSet;
|
||||
|
||||
public static Rect BaseRect;
|
||||
|
||||
private static GUIStyle none;
|
||||
|
||||
private static GUIStyle error;
|
||||
|
||||
private static GUIStyle noError;
|
||||
|
||||
public static GUIStyle None
|
||||
{
|
||||
get
|
||||
{
|
||||
if (none == null)
|
||||
{
|
||||
none = new GUIStyle();
|
||||
}
|
||||
|
||||
return none;
|
||||
}
|
||||
}
|
||||
|
||||
public static GUIStyle Error
|
||||
{
|
||||
get
|
||||
{
|
||||
if (error == null)
|
||||
{
|
||||
error = new GUIStyle();
|
||||
error.border = new RectOffset(3, 3, 3, 3);
|
||||
error.normal = new GUIStyleState();
|
||||
error.normal.background = CreateTempTexture(12, 12, "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAALElEQVQIHWP4z8CgC8SHgfg/lNZlQBIACYIlGEEMBjTABOQfQRM7AlKGYSYAoOwcvDRV9/MAAAAASUVORK5CYII=");
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
public static GUIStyle NoError
|
||||
{
|
||||
get
|
||||
{
|
||||
if (noError == null)
|
||||
{
|
||||
noError = new GUIStyle();
|
||||
noError.border = new RectOffset(3, 3, 3, 3);
|
||||
noError.normal = new GUIStyleState();
|
||||
}
|
||||
|
||||
return noError;
|
||||
}
|
||||
}
|
||||
|
||||
public static Texture2D CreateTempTexture(int width, int height, string encoded)
|
||||
{
|
||||
var texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
|
||||
|
||||
texture.hideFlags = HideFlags.HideAndDontSave;
|
||||
texture.LoadImage(System.Convert.FromBase64String(encoded));
|
||||
texture.Apply();
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public static Rect Reserve(float height = 16.0f)
|
||||
{
|
||||
var rect = default(Rect);
|
||||
|
||||
EditorGUILayout.BeginVertical(NoError);
|
||||
{
|
||||
rect = EditorGUILayout.BeginVertical();
|
||||
{
|
||||
EditorGUILayout.LabelField(string.Empty, GUILayout.Height(height));
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
if (BaseRectSet == true)
|
||||
{
|
||||
rect.xMin = BaseRect.xMin;
|
||||
rect.xMax = BaseRect.xMax;
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
public static void Record<T>(T t, string desc)
|
||||
where T : Object
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
Undo.RecordObject(t, desc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Record<T>(T[] ts, string desc)
|
||||
where T : Object
|
||||
{
|
||||
if (ts != null)
|
||||
{
|
||||
Undo.RecordObjects(ts, desc);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetDirty<T>(T t)
|
||||
where T : Object
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
EditorUtility.SetDirty(t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetDirty<T>(T[] ts)
|
||||
where T : Object
|
||||
{
|
||||
foreach (var t in ts)
|
||||
{
|
||||
SetDirty(t);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<T> LoadAllAssets<T>(string pattern) // e.g. "*.prefab"
|
||||
where T : Object
|
||||
{
|
||||
var assets = new List<T>();
|
||||
var basePath = Application.dataPath;
|
||||
var files = new List<string>(); GetFilesRecursive(files, basePath, pattern);
|
||||
var sub = basePath.Length - "Assets".Length;
|
||||
|
||||
for (var i = 0; i < files.Count; i++)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Loading Assets", "", (float)files.Count / (float)i);
|
||||
|
||||
var file = files[i];
|
||||
var path = file.Substring(sub);
|
||||
var asset = LoadAsset<T>(path);
|
||||
|
||||
if (asset != null)
|
||||
{
|
||||
assets.Add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
return assets;
|
||||
}
|
||||
|
||||
public static T LoadAsset<T>(string path)
|
||||
where T : Object
|
||||
{
|
||||
return (T)AssetDatabase.LoadAssetAtPath(path, typeof(T));
|
||||
}
|
||||
|
||||
private static void GetFilesRecursive(List<string> files, string path, string pattern)
|
||||
{
|
||||
files.AddRange(System.IO.Directory.GetFiles(path, pattern));
|
||||
|
||||
var directories = System.IO.Directory.GetDirectories(path);
|
||||
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
GetFilesRecursive(files, directory, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetAssetImporter<T>(Object asset)
|
||||
where T : AssetImporter
|
||||
{
|
||||
return GetAssetImporter<T>((AssetDatabase.GetAssetPath(asset)));
|
||||
}
|
||||
|
||||
public static T GetAssetImporter<T>(string path)
|
||||
where T : AssetImporter
|
||||
{
|
||||
return (T)AssetImporter.GetAtPath(path);
|
||||
}
|
||||
|
||||
public static void ReimportAsset(Object asset)
|
||||
{
|
||||
ReimportAsset(AssetDatabase.GetAssetPath(asset));
|
||||
}
|
||||
|
||||
public static void ReimportAsset(string path)
|
||||
{
|
||||
AssetDatabase.ImportAsset(path);
|
||||
}
|
||||
|
||||
public static void MakeTextureReadable(Texture2D texture)
|
||||
{
|
||||
if (texture != null)
|
||||
{
|
||||
var importer = D2D_Helper.GetAssetImporter<UnityEditor.TextureImporter>(texture);
|
||||
|
||||
if (importer != null && importer.isReadable == false)
|
||||
{
|
||||
importer.isReadable = true;
|
||||
|
||||
D2D_Helper.ReimportAsset(importer.assetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 958e55e465f5714428368c324eca0449
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
278
Assets/Reference/Destructible2D/Required/Library/D2D_Pixels.cs
Normal file
278
Assets/Reference/Destructible2D/Required/Library/D2D_Pixels.cs
Normal file
@@ -0,0 +1,278 @@
|
||||
using UnityEngine;
|
||||
|
||||
[System.Serializable]
|
||||
public class D2D_Pixels
|
||||
{
|
||||
[SerializeField]
|
||||
private int width;
|
||||
|
||||
[SerializeField]
|
||||
private int height;
|
||||
|
||||
[SerializeField]
|
||||
private Color32[] pixels;
|
||||
|
||||
public int Width
|
||||
{
|
||||
get
|
||||
{
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
get
|
||||
{
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
public Color32[] Pixels
|
||||
{
|
||||
get
|
||||
{
|
||||
return pixels;
|
||||
}
|
||||
}
|
||||
|
||||
public D2D_Pixels()
|
||||
{
|
||||
}
|
||||
|
||||
public D2D_Pixels(Texture texture) : this((Texture2D)texture)
|
||||
{
|
||||
}
|
||||
|
||||
public D2D_Pixels(Sprite sprite)
|
||||
{
|
||||
if (sprite == null) throw new System.ArgumentNullException();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
D2D_Helper.MakeTextureReadable(sprite.texture);
|
||||
#endif
|
||||
|
||||
var rect = sprite.textureRect;
|
||||
var sourceWidth = sprite.texture.width;
|
||||
var sourcePixels = sprite.texture.GetPixels32();
|
||||
var sourceOffset = sourceWidth * (int)rect.yMin + (int)rect.xMin;
|
||||
var targetOffset = 0;
|
||||
|
||||
width = (int)rect.width;
|
||||
height = (int)rect.height;
|
||||
pixels = new Color32[width * height];
|
||||
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
pixels[targetOffset + x] = sourcePixels[sourceOffset + x];
|
||||
}
|
||||
|
||||
sourceOffset += sourceWidth;
|
||||
targetOffset += width;
|
||||
}
|
||||
}
|
||||
|
||||
public D2D_Pixels(Texture2D texture)
|
||||
{
|
||||
if (texture == null) throw new System.ArgumentNullException();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
D2D_Helper.MakeTextureReadable(texture);
|
||||
#endif
|
||||
|
||||
width = texture.width;
|
||||
height = texture.height;
|
||||
pixels = texture.GetPixels32();
|
||||
}
|
||||
|
||||
public D2D_Pixels(int newWidth, int newHeight)
|
||||
{
|
||||
if (newWidth < 0) throw new System.ArgumentOutOfRangeException();
|
||||
if (newHeight < 0) throw new System.ArgumentOutOfRangeException();
|
||||
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
pixels = new Color32[newWidth * newHeight];
|
||||
}
|
||||
|
||||
public D2D_Pixels(int newWidth, int newHeight, Color32[] newPixels)
|
||||
{
|
||||
if (newWidth < 0) throw new System.ArgumentOutOfRangeException();
|
||||
|
||||
if (newHeight < 0) throw new System.ArgumentOutOfRangeException();
|
||||
|
||||
if (newPixels == null) throw new System.ArgumentNullException();
|
||||
|
||||
if (newWidth * newHeight != newPixels.Length) throw new System.ArgumentOutOfRangeException();
|
||||
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
pixels = newPixels;
|
||||
}
|
||||
|
||||
public Color32 GetPixel(int x, int y)
|
||||
{
|
||||
return pixels[x + width * y];
|
||||
}
|
||||
|
||||
public Color32 GetPixelTransparent(int x, int y)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= width || y >= height) return new Color32(0, 0, 0, 0);
|
||||
|
||||
return pixels[x + width * y];
|
||||
}
|
||||
|
||||
public Color32 GetPixelClamp(int x, int y)
|
||||
{
|
||||
if (x < 0) x = 0; else if (x >= width ) x = width -1;
|
||||
if (y < 0) y = 0; else if (y >= height) y = height -1;
|
||||
|
||||
return pixels[x + width * y];
|
||||
}
|
||||
|
||||
public Color32 GetPixelRepeat(int x, int y)
|
||||
{
|
||||
x = x >= 0 ? x % width : width + (x % width );
|
||||
y = y >= 0 ? y % height : height + (y % height);
|
||||
|
||||
return pixels[x + width * y];
|
||||
}
|
||||
|
||||
public Color32 GetPixelBilinear(float u, float v)
|
||||
{
|
||||
u = u * (width - 1);
|
||||
v = v * (height - 1);
|
||||
|
||||
var x = Mathf.FloorToInt(u);
|
||||
var y = Mathf.FloorToInt(v);
|
||||
var s = u - x;
|
||||
var t = v - y;
|
||||
|
||||
var bl = GetPixelClamp(x , y );
|
||||
var br = GetPixelClamp(x + 1, y );
|
||||
var tl = GetPixelClamp(x , y + 1);
|
||||
var tr = GetPixelClamp(x + 1, y + 1);
|
||||
|
||||
var bb = Color32.Lerp(bl, br, s);
|
||||
var tt = Color32.Lerp(tl, tr, s);
|
||||
|
||||
return Color32.Lerp(bb, tt, t);
|
||||
}
|
||||
|
||||
public void SetPixel(int x, int y, Color32 colour)
|
||||
{
|
||||
pixels[x + width * y] = colour;
|
||||
}
|
||||
|
||||
public void SetPixelClamp(int x, int y, Color32 colour)
|
||||
{
|
||||
if (x < 0) x = 0; else if (x >= width ) x = width -1;
|
||||
if (y < 0) y = 0; else if (y >= height) y = height -1;
|
||||
|
||||
pixels[x + width * y] = colour;
|
||||
}
|
||||
|
||||
public void SetPixels(int x, int y, D2D_Pixels s)
|
||||
{
|
||||
for (var sy = 0; sy < s.height; sy++)
|
||||
{
|
||||
for (var sx = 0; sx < s.width; sx++)
|
||||
{
|
||||
SetPixel(x + sx, y + sy, s.GetPixel(sx, sy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPixelsClamp(int x, int y, D2D_Pixels s)
|
||||
{
|
||||
for (var sy = 0; sy < s.height; sy++)
|
||||
{
|
||||
for (var sx = 0; sx < s.width; sx++)
|
||||
{
|
||||
SetPixelClamp(x + sx, y + sy, s.GetPixelClamp(sx, sy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public D2D_Pixels GetResized(int newWidth, int newHeight)
|
||||
{
|
||||
var o = new D2D_Pixels(newWidth, newHeight);
|
||||
var w = (float)(newWidth - 1);
|
||||
var h = (float)(newHeight - 1);
|
||||
|
||||
for (var y = 0; y < newHeight; y++)
|
||||
{
|
||||
for (var x = 0; x < newWidth; x++)
|
||||
{
|
||||
var pixel = GetPixelBilinear((float)x / w, (float)y / h);
|
||||
|
||||
o.SetPixel(x, y, pixel);
|
||||
}
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
public D2D_Pixels GetBlurredAlpha()
|
||||
{
|
||||
var o = new D2D_Pixels(width, height);
|
||||
|
||||
// Horizontal
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
var a = GetPixelTransparent(x - 1, y);
|
||||
var b = GetPixelTransparent(x , y);
|
||||
var c = GetPixelTransparent(x + 1, y);
|
||||
var t = (int)a.a + (int)b.a + (int)c.a;
|
||||
|
||||
b.a = (byte)(t / 3);
|
||||
|
||||
o.SetPixel(x, y, b);
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
var a = GetPixelTransparent(x, y - 1);
|
||||
var b = GetPixelTransparent(x, y );
|
||||
var c = GetPixelTransparent(x, y + 1);
|
||||
var t = (int)a.a + (int)b.a + (int)c.a;
|
||||
|
||||
b.a = (byte)(t / 3);
|
||||
|
||||
o.SetPixel(x, y, b);
|
||||
}
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
public Texture2D Apply(TextureFormat format, bool mipmap = false, bool linear = false)
|
||||
{
|
||||
var texture = new Texture2D(width, height, format, mipmap, linear);
|
||||
|
||||
texture.SetPixels32(pixels);
|
||||
texture.Apply();
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public byte[] ApplyAlpha()
|
||||
{
|
||||
var alphas = new byte[pixels.Length];
|
||||
|
||||
for (var i = 0; i < pixels.Length; i++)
|
||||
{
|
||||
alphas[i] = pixels[i].a;
|
||||
}
|
||||
|
||||
return alphas;
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b391339ee853d4438312b6530a2dc37
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,445 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static class D2D_PolygonCalculator
|
||||
{
|
||||
enum Edge
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Bottom,
|
||||
Top
|
||||
}
|
||||
|
||||
class Point
|
||||
{
|
||||
public bool Used;
|
||||
|
||||
public Point Other;
|
||||
|
||||
public Vector2 Position;
|
||||
|
||||
public Edge Inner;
|
||||
|
||||
public Edge Outer;
|
||||
}
|
||||
|
||||
class Cell
|
||||
{
|
||||
public int X;
|
||||
|
||||
public int Y;
|
||||
|
||||
public int PointIndex;
|
||||
|
||||
public int PointCount;
|
||||
}
|
||||
|
||||
private static int xMin;
|
||||
|
||||
private static int xMax;
|
||||
|
||||
private static int yMin;
|
||||
|
||||
private static int yMax;
|
||||
|
||||
private static int width;
|
||||
|
||||
private static int height;
|
||||
|
||||
private static int cellCount;
|
||||
|
||||
private static List<Cell> cells = new List<Cell>();
|
||||
|
||||
private static List<Vector2> generatedPoints = new List<Vector2>();
|
||||
|
||||
private static int cellPointCount;
|
||||
|
||||
private static List<Point> cellPoints = new List<Point>();
|
||||
|
||||
private static LinkedList<Point> tracedEdges = new LinkedList<Point>();
|
||||
|
||||
private static Vector2 translation;
|
||||
|
||||
private static Color[] pixels;
|
||||
|
||||
private static int pixelsX;
|
||||
|
||||
private static int pixelsY;
|
||||
|
||||
private static int pixelsWidth;
|
||||
|
||||
private static int pixelsHeight;
|
||||
|
||||
public static PolygonCollider2D Generate(GameObject gameObject, PolygonCollider2D polygonCollider, Texture2D alphaTex, int newXMin, int newXMax, int newYMin, int newYMax, float tolerance)
|
||||
{
|
||||
cellCount = 0;
|
||||
cellPointCount = 0;
|
||||
|
||||
if (gameObject != null && alphaTex != null)
|
||||
{
|
||||
if (polygonCollider == null)
|
||||
{
|
||||
polygonCollider = gameObject.AddComponent<PolygonCollider2D>();
|
||||
}
|
||||
|
||||
xMin = newXMin - 1;
|
||||
yMin = newYMin - 1;
|
||||
xMax = newXMax + 1;
|
||||
yMax = newYMax + 1;
|
||||
width = xMax - xMin;
|
||||
height = yMax - yMin;
|
||||
|
||||
GetPixels(alphaTex, newXMin, newYMin, newXMax, newYMax);
|
||||
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
if (cells.Capacity < width * height)
|
||||
{
|
||||
cells.Capacity = width * height;
|
||||
}
|
||||
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
var cell = GetNextCell();
|
||||
var bl = GetAlphaOrDefault(xMin + x , yMin + y );
|
||||
var br = GetAlphaOrDefault(xMin + x + 1, yMin + y );
|
||||
var tl = GetAlphaOrDefault(xMin + x , yMin + y + 1);
|
||||
var tr = GetAlphaOrDefault(xMin + x + 1, yMin + y + 1);
|
||||
|
||||
cell.X = x;
|
||||
cell.Y = y;
|
||||
|
||||
translation.x = xMin + x + 0.5f;
|
||||
translation.y = yMin + y + 0.5f;
|
||||
|
||||
CalculateCell(cell, bl, br, tl, tr, 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
var pathCount = 0;
|
||||
|
||||
for (var i = 0; i < cellCount; i++)
|
||||
{
|
||||
var cell = cells[i];
|
||||
|
||||
for (var j = 0; j < cell.PointCount; j++)
|
||||
{
|
||||
var point = cellPoints[cell.PointIndex + j];
|
||||
|
||||
if (point.Used == false)
|
||||
{
|
||||
TraceEdges(cell, point);
|
||||
|
||||
OptimizeEdges(tolerance);
|
||||
|
||||
CompileTracedEdges(polygonCollider, pathCount); pathCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (polygonCollider.pathCount > pathCount)
|
||||
{
|
||||
polygonCollider.pathCount = pathCount;
|
||||
}
|
||||
}
|
||||
|
||||
return polygonCollider;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void GetPixels(Texture2D alphaTex, int l, int b, int r, int t)
|
||||
{
|
||||
l = Mathf.Max(l, 0);
|
||||
b = Mathf.Max(b, 0);
|
||||
r = Mathf.Min(r, alphaTex.width);
|
||||
t = Mathf.Min(t, alphaTex.height);
|
||||
|
||||
pixelsX = l;
|
||||
pixelsY = b;
|
||||
pixelsWidth = r - l;
|
||||
pixelsHeight = t - b;
|
||||
pixels = alphaTex.GetPixels(pixelsX, pixelsY, pixelsWidth, pixelsHeight);
|
||||
}
|
||||
|
||||
private static void CompileTracedEdges(PolygonCollider2D polygonCollider, int pathCount)
|
||||
{
|
||||
generatedPoints.Clear();
|
||||
|
||||
foreach (var point in tracedEdges)
|
||||
{
|
||||
generatedPoints.Add(point.Position);
|
||||
}
|
||||
|
||||
if (polygonCollider.pathCount <= pathCount)
|
||||
{
|
||||
polygonCollider.pathCount += 1;
|
||||
}
|
||||
|
||||
polygonCollider.SetPath(pathCount, generatedPoints.ToArray());
|
||||
}
|
||||
|
||||
private static void CalculateCell(Cell cell, float bl, float br, float tl, float tr, float threshold)
|
||||
{
|
||||
var count = 0;
|
||||
var index = cellPointCount;
|
||||
var useBl = bl >= threshold;
|
||||
var useBr = br >= threshold;
|
||||
var useTl = tl >= threshold;
|
||||
var useTr = tr >= threshold;
|
||||
|
||||
// Top
|
||||
if (useTl ^ useTr)
|
||||
{
|
||||
var point = GetNextCellPoint(); count += 1;
|
||||
|
||||
point.Position = Transform((tl - threshold) / (tl - tr), 1.0f);
|
||||
point.Inner = Edge.Top;
|
||||
point.Outer = Edge.Bottom;
|
||||
}
|
||||
|
||||
// Right
|
||||
if (useTr ^ useBr)
|
||||
{
|
||||
var point = GetNextCellPoint(); count += 1;
|
||||
|
||||
point.Position = Transform(1.0f, (br - threshold) / (br - tr));
|
||||
point.Inner = Edge.Right;
|
||||
point.Outer = Edge.Left;
|
||||
}
|
||||
|
||||
// Bottom
|
||||
if (useBl ^ useBr)
|
||||
{
|
||||
var point = GetNextCellPoint(); count += 1;
|
||||
|
||||
point.Position = Transform((bl - threshold) / (bl - br), 0.0f);
|
||||
point.Inner = Edge.Bottom;
|
||||
point.Outer = Edge.Top;
|
||||
}
|
||||
|
||||
// Left
|
||||
if (useTl ^ useBl)
|
||||
{
|
||||
var point = GetNextCellPoint(); count += 1;
|
||||
|
||||
point.Position = Transform(0.0f, (bl - threshold) / (bl - tl));
|
||||
point.Inner = Edge.Left;
|
||||
point.Outer = Edge.Right;
|
||||
}
|
||||
|
||||
// Pair up points
|
||||
switch (count)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
cellPoints[index + 0].Other = cellPoints[index + 1];
|
||||
cellPoints[index + 1].Other = cellPoints[index + 0];
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
if (useTl == true && useBl == true)
|
||||
{
|
||||
cellPoints[index + 0].Other = cellPoints[index + 1];
|
||||
cellPoints[index + 1].Other = cellPoints[index + 0];
|
||||
cellPoints[index + 2].Other = cellPoints[index + 3];
|
||||
cellPoints[index + 3].Other = cellPoints[index + 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
cellPoints[index + 0].Other = cellPoints[index + 3];
|
||||
cellPoints[index + 1].Other = cellPoints[index + 2];
|
||||
cellPoints[index + 2].Other = cellPoints[index + 1];
|
||||
cellPoints[index + 3].Other = cellPoints[index + 0];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cell.PointIndex = index;
|
||||
cell.PointCount = count;
|
||||
}
|
||||
|
||||
private static Cell GetNextCell()
|
||||
{
|
||||
var cell = default(Cell);
|
||||
|
||||
if (cellCount >= cells.Count)
|
||||
{
|
||||
cell = new Cell(); cells.Add(cell);
|
||||
}
|
||||
else
|
||||
{
|
||||
cell = cells[cellCount];
|
||||
}
|
||||
|
||||
cellCount += 1;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
private static Point GetNextCellPoint()
|
||||
{
|
||||
var point = default(Point);
|
||||
|
||||
if (cellPointCount >= cellPoints.Count)
|
||||
{
|
||||
point = new Point(); cellPoints.Add(point);
|
||||
}
|
||||
else
|
||||
{
|
||||
point = cellPoints[cellPointCount];
|
||||
point.Used = false;
|
||||
}
|
||||
|
||||
cellPointCount += 1;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
private static void TraceEdges(Cell cell, Point point)
|
||||
{
|
||||
tracedEdges.Clear();
|
||||
|
||||
TraceEdge(cell, point, false);
|
||||
TraceEdge(cell, point.Other, true);
|
||||
}
|
||||
|
||||
private static void TraceEdge(Cell cell, Point point, bool last)
|
||||
{
|
||||
point.Used = true;
|
||||
|
||||
if (last == true)
|
||||
{
|
||||
tracedEdges.AddLast(point);
|
||||
}
|
||||
else
|
||||
{
|
||||
tracedEdges.AddFirst(point);
|
||||
}
|
||||
|
||||
switch (point.Inner)
|
||||
{
|
||||
case Edge.Left:
|
||||
{
|
||||
cell = GetCell(cell.X - 1, cell.Y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Edge.Right:
|
||||
{
|
||||
cell = GetCell(cell.X + 1, cell.Y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Edge.Bottom:
|
||||
{
|
||||
cell = GetCell(cell.X, cell.Y - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case Edge.Top:
|
||||
{
|
||||
cell = GetCell(cell.X, cell.Y + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (cell != null)
|
||||
{
|
||||
for (var i = 0; i < cell.PointCount; i++)
|
||||
{
|
||||
var outerPoint = cellPoints[cell.PointIndex + i];
|
||||
|
||||
if (outerPoint.Used == false && outerPoint.Inner == point.Outer)
|
||||
{
|
||||
outerPoint.Used = true;
|
||||
|
||||
TraceEdge(cell, outerPoint.Other, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OptimizeEdges(float tolerance)
|
||||
{
|
||||
if (tolerance > 0.0f && tracedEdges.Count > 2)
|
||||
{
|
||||
var a = tracedEdges.First;
|
||||
var b = a.Next;
|
||||
var c = b.Next;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var ab = Vector3.Normalize(b.Value.Position - a.Value.Position);
|
||||
var bc = Vector3.Normalize(c.Value.Position - b.Value.Position);
|
||||
var abc = Vector3.Dot(ab, bc);
|
||||
|
||||
if (abc > (1.0f - tolerance))
|
||||
{
|
||||
tracedEdges.Remove(b);
|
||||
|
||||
if (c != tracedEdges.Last)
|
||||
{
|
||||
b = c;
|
||||
c = c.Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c != tracedEdges.Last)
|
||||
{
|
||||
a = b;
|
||||
b = c;
|
||||
c = c.Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float GetAlphaOrDefault(int x, int y)
|
||||
{
|
||||
x -= pixelsX;
|
||||
y -= pixelsY;
|
||||
|
||||
if (x >= 0 && y >= 0 && x < pixelsWidth && y < pixelsHeight)
|
||||
{
|
||||
return pixels[x + y * pixelsWidth].a;
|
||||
}
|
||||
|
||||
return default(float);
|
||||
}
|
||||
|
||||
private static Cell GetCell(int x, int y)
|
||||
{
|
||||
if (x >= 0 && y >= 0 && x < width && y < height)
|
||||
{
|
||||
return cells[x + y * width];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Vector2 Transform(float x, float y)
|
||||
{
|
||||
x = x + translation.x;
|
||||
y = y + translation.y;
|
||||
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fa7253934c605b4396e5c5715ab962d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,38 @@
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
public class D2D_PopupAttribute : PropertyAttribute
|
||||
{
|
||||
public GUIContent[] Names;
|
||||
|
||||
public int[] Values;
|
||||
|
||||
public D2D_PopupAttribute(params int[] newValues)
|
||||
{
|
||||
Names = newValues.Select(v => new GUIContent(v.ToString())).ToArray();
|
||||
|
||||
Values = newValues.Select(v => v).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(D2D_PopupAttribute))]
|
||||
public class D2D_PopupDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (D2D_Helper.BaseRectSet == true)
|
||||
{
|
||||
position.x = D2D_Helper.BaseRect.x;
|
||||
position.width = D2D_Helper.BaseRect.width;
|
||||
}
|
||||
|
||||
var Attribute = (D2D_PopupAttribute)attribute;
|
||||
|
||||
EditorGUI.IntPopup(position, property, Attribute.Names, Attribute.Values, label);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4152e5044d65f84a8deb3eb6face827
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,37 @@
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
public class D2D_RangeAttribute : PropertyAttribute
|
||||
{
|
||||
public float Min;
|
||||
|
||||
public float Max;
|
||||
|
||||
public D2D_RangeAttribute(float newMin, float newMax)
|
||||
{
|
||||
Min = newMin;
|
||||
Max = newMax;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(D2D_RangeAttribute))]
|
||||
public class D2D_RangeDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (D2D_Helper.BaseRectSet == true)
|
||||
{
|
||||
position.x = D2D_Helper.BaseRect.x;
|
||||
position.width = D2D_Helper.BaseRect.width;
|
||||
}
|
||||
|
||||
var Attribute = (D2D_RangeAttribute)attribute;
|
||||
|
||||
EditorGUI.showMixedValue = property.hasMultipleDifferentValues;
|
||||
EditorGUI.Slider(position, property, Attribute.Min, Attribute.Max, label);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8eebccf5a9710945bf59a7f22418110
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,263 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static class D2D_SplitCalculator
|
||||
{
|
||||
class Fill
|
||||
{
|
||||
public List<int> Indices = new List<int>();
|
||||
|
||||
public List<Color32> Colours = new List<Color32>();
|
||||
|
||||
public int Count;
|
||||
|
||||
public int XMin;
|
||||
|
||||
public int XMax;
|
||||
|
||||
public int YMin;
|
||||
|
||||
public int YMax;
|
||||
|
||||
public bool Valid;
|
||||
}
|
||||
|
||||
class Spread
|
||||
{
|
||||
public int i;
|
||||
public int x;
|
||||
public int y;
|
||||
}
|
||||
|
||||
private static D2D_Destructible target;
|
||||
|
||||
private static List<bool> cells = new List<bool>();
|
||||
|
||||
private static List<Fill> fills = new List<Fill>();
|
||||
|
||||
private static List<Spread> spreads = new List<Spread>();
|
||||
|
||||
private static int spreadCount;
|
||||
|
||||
private static Fill currentFill;
|
||||
|
||||
private static Color32[] pixels;
|
||||
|
||||
private static Texture2D tex;
|
||||
|
||||
private static int width;
|
||||
|
||||
private static int height;
|
||||
|
||||
private static int total;
|
||||
|
||||
public static bool Generate(D2D_Destructible destructible, D2D_SpriteSplitOrder splitOrder)
|
||||
{
|
||||
cells.Clear();
|
||||
|
||||
if (destructible != null && destructible.AlphaTex != null)
|
||||
{
|
||||
target = destructible;
|
||||
tex = target.AlphaTex;
|
||||
width = tex.width;
|
||||
height = tex.height;
|
||||
total = width * height;
|
||||
pixels = tex.GetPixels32();
|
||||
|
||||
if (cells.Capacity < total)
|
||||
{
|
||||
cells.Capacity = total;
|
||||
}
|
||||
|
||||
var threshold = (byte)(target.SplitThreshold * 255.0f);
|
||||
|
||||
for (var i = 0; i < total; i++)
|
||||
{
|
||||
cells.Add(pixels[i].a >= threshold);
|
||||
}
|
||||
|
||||
fills.Clear();
|
||||
|
||||
var validFillCount = 0;
|
||||
|
||||
for (var i = 0; i < total; i++)
|
||||
{
|
||||
if (cells[i] == true)
|
||||
{
|
||||
currentFill = new Fill(); fills.Add(currentFill);
|
||||
|
||||
currentFill.XMin = currentFill.XMax = i % width;
|
||||
currentFill.YMin = currentFill.YMax = i / width;
|
||||
|
||||
BeginFloodFill(i, currentFill.XMin, currentFill.YMin);
|
||||
|
||||
// Skip the first floodfill
|
||||
if (currentFill.Count >= target.SplitMinPixels)
|
||||
{
|
||||
currentFill.Valid = true; validFillCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Can we split?
|
||||
if (validFillCount > 1)
|
||||
{
|
||||
var firstSet = false;
|
||||
|
||||
switch (splitOrder)
|
||||
{
|
||||
case D2D_SpriteSplitOrder.KeepLargest: fills.Sort((a, b) => b.Count.CompareTo(a.Count)); break;
|
||||
case D2D_SpriteSplitOrder.KeepSmallest: fills.Sort((a, b) => a.Count.CompareTo(b.Count)); break;
|
||||
}
|
||||
|
||||
foreach (var fill in fills)
|
||||
{
|
||||
if (fill.Valid == true)
|
||||
{
|
||||
if (firstSet == false)
|
||||
{
|
||||
firstSet = true;
|
||||
|
||||
Split(destructible, fill, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var clonedGameObject = D2D_Helper.CloneGameObject(destructible.gameObject, destructible.transform.parent);
|
||||
var clonedDestructible = clonedGameObject.GetComponent<D2D_Destructible>();
|
||||
|
||||
Split(clonedDestructible, fill, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (validFillCount == 0)
|
||||
{
|
||||
D2D_Helper.Destroy(destructible.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void Split(D2D_Destructible destructible, Fill fill, bool isClone)
|
||||
{
|
||||
var clear = new Color32(0, 0, 0, 0);
|
||||
|
||||
for (var i = 0; i < total; i++)
|
||||
{
|
||||
pixels[i] = clear;
|
||||
}
|
||||
|
||||
for (var i = 0; i < fill.Count; i++)
|
||||
{
|
||||
pixels[fill.Indices[i]] = fill.Colours[i];
|
||||
}
|
||||
|
||||
destructible.ReplaceAlphaWith(width, height, pixels);
|
||||
|
||||
// Split notification
|
||||
destructible.BroadcastMessage("OnSpriteSplit", isClone, SendMessageOptions.DontRequireReceiver);
|
||||
}
|
||||
|
||||
private static void BeginFloodFill(int i, int x, int y)
|
||||
{
|
||||
var oldSpreadCount = spreadCount = 0;
|
||||
|
||||
SpreadTo(i, x, y);
|
||||
|
||||
// Non-recursive floodfill
|
||||
while (spreadCount != oldSpreadCount)
|
||||
{
|
||||
var start = oldSpreadCount;
|
||||
var end = oldSpreadCount = spreadCount;
|
||||
|
||||
for (var j = start; j < end; j++)
|
||||
{
|
||||
var spread = spreads[j];
|
||||
|
||||
FloodFill(spread.i, spread.x, spread.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SpreadTo(int i, int x, int y)
|
||||
{
|
||||
cells[i] = false;
|
||||
|
||||
var spread = default(Spread);
|
||||
|
||||
if (spreadCount >= spreads.Count)
|
||||
{
|
||||
spread = new Spread(); spreads.Add(spread);
|
||||
}
|
||||
else
|
||||
{
|
||||
spread = spreads[spreadCount];
|
||||
}
|
||||
|
||||
spread.i = i;
|
||||
spread.x = x;
|
||||
spread.y = y;
|
||||
|
||||
spreadCount += 1;
|
||||
}
|
||||
|
||||
private static void FloodFill(int i, int x, int y)
|
||||
{
|
||||
currentFill.Count += 1;
|
||||
currentFill.Indices.Add(i);
|
||||
currentFill.Colours.Add(pixels[i]);
|
||||
|
||||
currentFill.XMin = Mathf.Min(currentFill.XMin, x);
|
||||
currentFill.XMax = Mathf.Max(currentFill.XMax, x);
|
||||
currentFill.YMin = Mathf.Min(currentFill.YMin, y);
|
||||
currentFill.YMax = Mathf.Max(currentFill.YMax, y);
|
||||
|
||||
// Left
|
||||
if (x > 0)
|
||||
{
|
||||
var n = i - 1;
|
||||
|
||||
if (cells[n] == true)
|
||||
{
|
||||
SpreadTo(n, x - 1, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Right
|
||||
if (x < width - 1)
|
||||
{
|
||||
var n = i + 1;
|
||||
|
||||
if (cells[n] == true)
|
||||
{
|
||||
SpreadTo(n, x + 1, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom
|
||||
if (y > 0)
|
||||
{
|
||||
var n = i - width;
|
||||
|
||||
if (cells[n] == true)
|
||||
{
|
||||
SpreadTo(n, x, y - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Top
|
||||
if (y < height - 1)
|
||||
{
|
||||
var n = i + width;
|
||||
|
||||
if (cells[n] == true)
|
||||
{
|
||||
SpreadTo(n, x, y + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c281e527c4903c14eabbb78f1ff2b654
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
5
Assets/Reference/Destructible2D/Required/Materials.meta
Normal file
5
Assets/Reference/Destructible2D/Required/Materials.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd1fa9c068dbba244b366aec4f5f2c9f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e9718397ce67bc4687d8c9f08dc7814
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@@ -0,0 +1,69 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 3
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 0}
|
||||
m_Name: Sprites-Default (Destructible 2D)
|
||||
m_Shader: {fileID: 4800000, guid: 9c897ee6dc2bb7d40a4ad66067efa576, type: 3}
|
||||
m_ShaderKeywords: []
|
||||
m_CustomRenderQueue: -1
|
||||
m_SavedProperties:
|
||||
serializedVersion: 2
|
||||
m_TexEnvs:
|
||||
data:
|
||||
first:
|
||||
name: _MainTex
|
||||
second:
|
||||
m_Texture: {fileID: 2800000, guid: f75c046c76364fe4691555d1d7365051, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
data:
|
||||
first:
|
||||
name: _AlphaTex
|
||||
second:
|
||||
m_Texture: {fileID: 2800000, guid: 8964e942e781b9c4191e0ca6a68d9430, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
data:
|
||||
first:
|
||||
name: _AlphaMap
|
||||
second:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
data:
|
||||
first:
|
||||
name: _AT
|
||||
second:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
data:
|
||||
first:
|
||||
name: _Cutoff
|
||||
second: .439999998
|
||||
data:
|
||||
first:
|
||||
name: PixelSnap
|
||||
second: 0
|
||||
data:
|
||||
first:
|
||||
name: _Sharpness
|
||||
second: 1
|
||||
data:
|
||||
first:
|
||||
name: _AlphaThreshold
|
||||
second: 0
|
||||
data:
|
||||
first:
|
||||
name: _AlphaContrast
|
||||
second: 1
|
||||
m_Colors:
|
||||
data:
|
||||
first:
|
||||
name: _Color
|
||||
second: {r: 1, g: 1, b: 1, a: 1}
|
@@ -0,0 +1,4 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f935b17ca3c4d84fbcb73997407334c
|
||||
NativeFormatImporter:
|
||||
userData:
|
5
Assets/Reference/Destructible2D/Required/Player.meta
Normal file
5
Assets/Reference/Destructible2D/Required/Player.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e4fd912fb4728c4d8734215230ed42d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
|
||||
[AddComponentMenu("Destructible 2D/D2D Anchor")]
|
||||
public class D2D_Anchor : MonoBehaviour
|
||||
{
|
||||
public float Radius = 1.0f;
|
||||
|
||||
public float ScaledRadius
|
||||
{
|
||||
get
|
||||
{
|
||||
return Radius * Mathf.Max(transform.lossyScale.x, Mathf.Max(transform.lossyScale.y, transform.lossyScale.z));
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected virtual void OnDrawGizmosSelected()
|
||||
{
|
||||
var c = transform.position;
|
||||
var r = ScaledRadius;
|
||||
var s = Mathf.PI * 2.0f / 36.0f;
|
||||
|
||||
for (var i = 0; i < 36; i++)
|
||||
{
|
||||
var a = i * s;
|
||||
var b = a + s;
|
||||
|
||||
Gizmos.DrawLine(c + new Vector3(Mathf.Sin(a), Mathf.Cos(a), 0.0f) * r, c + new Vector3(Mathf.Sin(b), Mathf.Cos(b), 0.0f) * r);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 264ae736e9dab8a46a43cd3488c6230e
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,122 @@
|
||||
using UnityEngine;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Destructible 2D/D2D Auto Polygon Collider")]
|
||||
public class D2D_AutoPolygonCollider : D2D_Collider
|
||||
{
|
||||
[SerializeField]
|
||||
private PolygonCollider2D polygonCollider2D;
|
||||
|
||||
public void RebuildCollider(Texture2D alphaTex)
|
||||
{
|
||||
DestroyPolygonCollider2D();
|
||||
|
||||
if (alphaTex != null)
|
||||
{
|
||||
if (polygonCollider2D == null)
|
||||
{
|
||||
var spriteRenderer = D2D_Helper.GetOrAddComponent<SpriteRenderer>(gameObject);
|
||||
var sprite = Sprite.Create(alphaTex, new Rect(0, 0, alphaTex.width, alphaTex.height), Vector2.zero, 1.0f, 0, SpriteMeshType.FullRect);
|
||||
|
||||
spriteRenderer.sprite = sprite;
|
||||
|
||||
polygonCollider2D = gameObject.AddComponent<PolygonCollider2D>();
|
||||
|
||||
// Disable the collider if it couldn't form any triangles
|
||||
polygonCollider2D.enabled = IsDefaultPolygonCollider2D(polygonCollider2D) == false;
|
||||
|
||||
UpdateColliderSettings();
|
||||
|
||||
D2D_Helper.Destroy(sprite);
|
||||
D2D_Helper.Destroy(spriteRenderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateColliderSettings()
|
||||
{
|
||||
if (polygonCollider2D != null)
|
||||
{
|
||||
polygonCollider2D.isTrigger = IsTrigger;
|
||||
polygonCollider2D.sharedMaterial = Material;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
D2D_Helper.DestroyManaged(DestroyPolygonCollider2D);
|
||||
}
|
||||
|
||||
protected override void RebuildAll()
|
||||
{
|
||||
var destructible = D2D_Helper.GetComponentUpwards<D2D_Destructible>(transform);
|
||||
|
||||
if (destructible != null)
|
||||
{
|
||||
RebuildCollider(destructible.AlphaTex);
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyPolygonCollider2D()
|
||||
{
|
||||
if (polygonCollider2D != null)
|
||||
{
|
||||
D2D_Helper.Destroy(polygonCollider2D);
|
||||
|
||||
polygonCollider2D = null;
|
||||
}
|
||||
}
|
||||
|
||||
// The default collider is a pentagon, but its position and size changes based on the sprite
|
||||
private static bool IsDefaultPolygonCollider2D(PolygonCollider2D polygonCollider2D)
|
||||
{
|
||||
if (polygonCollider2D == null) return false;
|
||||
|
||||
if (polygonCollider2D.GetTotalPointCount() != 5) return false;
|
||||
|
||||
var points = polygonCollider2D.points;
|
||||
var spacing = Vector2.Distance(points[0], points[4]);
|
||||
|
||||
// Same spacing?
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var spacing2 = Vector2.Distance(points[i], points[i + 1]);
|
||||
|
||||
if (Mathf.Approximately(spacing, spacing2) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var midpoint = (points[0] + points[1] + points[2] + points[3] + points[4]) * 0.2f;
|
||||
var radius = Vector2.Distance(points[0], midpoint);
|
||||
|
||||
// Same radius?
|
||||
for (var i = 1; i < 5; i++)
|
||||
{
|
||||
var radius2 = Vector2.Distance(points[i], midpoint);
|
||||
|
||||
if (Mathf.Approximately(radius, radius2) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Must be a pentagon then!
|
||||
return true;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void SetHideFlags(HideFlags hideFlags)
|
||||
{
|
||||
if (polygonCollider2D != null)
|
||||
{
|
||||
polygonCollider2D.hideFlags = hideFlags;
|
||||
}
|
||||
}
|
||||
#else
|
||||
protected override void SetHideFlags(HideFlags hideFlags)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0379d2d63d6fc2a48b08b1495ea22549
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[RequireComponent(typeof(D2D_DestructibleSprite))]
|
||||
[RequireComponent(typeof(Rigidbody2D))]
|
||||
[AddComponentMenu("Destructible 2D/D2D Breakable")]
|
||||
public class D2D_Breakable : MonoBehaviour
|
||||
{
|
||||
public bool ChangeColliderType;
|
||||
|
||||
public D2D_SpriteColliderType NewColliderType = D2D_SpriteColliderType.AutoPolygon;
|
||||
|
||||
public List<D2D_Anchor> Anchors = new List<D2D_Anchor>();
|
||||
|
||||
protected virtual void OnAlphaTexModified()
|
||||
{
|
||||
var destructibleSprite = GetComponent<D2D_DestructibleSprite>();
|
||||
var rb2d = GetComponent<Rigidbody2D>();
|
||||
var anchored = false;
|
||||
|
||||
// Find which anchors we're connected to
|
||||
foreach (var anchor in Anchors)
|
||||
{
|
||||
var collider2Ds = Physics2D.OverlapCircleAll(anchor.transform.position, anchor.ScaledRadius);
|
||||
|
||||
foreach (var collider2D in collider2Ds)
|
||||
{
|
||||
if (collider2D.attachedRigidbody == rb2d)
|
||||
{
|
||||
anchored = true; goto ExitLoops;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExitLoops:
|
||||
|
||||
// Broken off anchors?
|
||||
if (anchored == false)
|
||||
{
|
||||
// Enable physics
|
||||
GetComponent<Rigidbody2D>().isKinematic = false;
|
||||
|
||||
// Change collider?
|
||||
if (ChangeColliderType == true && destructibleSprite.ColliderType != NewColliderType)
|
||||
{
|
||||
destructibleSprite.ColliderType = NewColliderType;
|
||||
|
||||
destructibleSprite.RebuildColliders();
|
||||
}
|
||||
|
||||
// Now that it's broken, we no longer need this
|
||||
D2D_Helper.Destroy(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf10e60ef78d24d4ca2bdc58163c7d41
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,52 @@
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class D2D_Collider : MonoBehaviour
|
||||
{
|
||||
public bool IsTrigger;
|
||||
|
||||
public PhysicsMaterial2D Material;
|
||||
|
||||
private bool dirty;
|
||||
|
||||
public void MarkAsDirty()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
D2D_Helper.SetDirty(this);
|
||||
#endif
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public abstract void UpdateColliderSettings();
|
||||
|
||||
protected abstract void SetHideFlags(HideFlags hideFlags);
|
||||
|
||||
protected abstract void RebuildAll();
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (dirty == true)
|
||||
{
|
||||
dirty = false;
|
||||
|
||||
RebuildAll();
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public void SetHideFlags(bool hide)
|
||||
{
|
||||
var hideFlags = HideFlags.NotEditable;
|
||||
|
||||
if (hide == true)
|
||||
{
|
||||
hideFlags |= HideFlags.HideInInspector;
|
||||
}
|
||||
|
||||
SetHideFlags(hideFlags);
|
||||
}
|
||||
#else
|
||||
public void SetHideFlags(bool hide)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7375669b0754bd044a108513917998d3
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,155 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Destructible 2D/D2D Damageable Sprite")]
|
||||
[RequireComponent(typeof(SpriteRenderer))]
|
||||
public class D2D_DamageableSprite : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public class DamageLevel
|
||||
{
|
||||
public Sprite Sprite;
|
||||
|
||||
public float DamageRequired;
|
||||
}
|
||||
|
||||
public float Damage;
|
||||
|
||||
public bool AllowDestruction;
|
||||
|
||||
public float DamageLimit;
|
||||
|
||||
public GameObject ReplaceWith;
|
||||
|
||||
public List<DamageLevel> DamageLevels = new List<DamageLevel>();
|
||||
|
||||
private SpriteRenderer spriteRenderer;
|
||||
|
||||
public DamageLevel AddDamageLevel(Sprite sprite = null, float damageRequired = 0.0f)
|
||||
{
|
||||
if (DamageLevels == null)
|
||||
{
|
||||
DamageLevels = new List<DamageLevel>();
|
||||
}
|
||||
|
||||
var newDamageLevel = new DamageLevel(); DamageLevels.Add(newDamageLevel);
|
||||
|
||||
newDamageLevel.Sprite = sprite;
|
||||
newDamageLevel.DamageRequired = damageRequired;
|
||||
|
||||
return newDamageLevel;
|
||||
}
|
||||
|
||||
public void InflictDamage(float amount)
|
||||
{
|
||||
Damage += amount;
|
||||
|
||||
UpdateSprite();
|
||||
}
|
||||
|
||||
public void UpdateSprite()
|
||||
{
|
||||
var bestDamageLevel = default(DamageLevel);
|
||||
|
||||
if (DamageLevels != null)
|
||||
{
|
||||
foreach (var damageLevel in DamageLevels)
|
||||
{
|
||||
if (damageLevel != null)
|
||||
{
|
||||
if (bestDamageLevel == null || Damage >= damageLevel.DamageRequired)
|
||||
{
|
||||
// Skip if this is below the best
|
||||
if (bestDamageLevel != null && damageLevel.DamageRequired < bestDamageLevel.DamageRequired)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bestDamageLevel = damageLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace sprite?
|
||||
if (bestDamageLevel != null)
|
||||
{
|
||||
if (spriteRenderer == null) spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
if (spriteRenderer.sprite != bestDamageLevel.Sprite)
|
||||
{
|
||||
if (bestDamageLevel.Sprite != null)
|
||||
{
|
||||
spriteRenderer.sprite = bestDamageLevel.Sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteRenderer.sprite = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateDestruction();
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
UpdateDamageLevels();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected virtual void Reset()
|
||||
{
|
||||
UpdateDamageLevels();
|
||||
}
|
||||
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
UpdateSprite();
|
||||
}
|
||||
#endif
|
||||
|
||||
private void UpdateDamageLevels()
|
||||
{
|
||||
if (DamageLevels == null)
|
||||
{
|
||||
DamageLevels = new List<DamageLevel>();
|
||||
}
|
||||
|
||||
// Copy default damage level from SpriteRenderer?
|
||||
if (DamageLevels.Count == 0)
|
||||
{
|
||||
if (spriteRenderer == null) spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
if (spriteRenderer.sprite != null)
|
||||
{
|
||||
var newDamageLevel = new DamageLevel(); DamageLevels.Add(newDamageLevel);
|
||||
|
||||
newDamageLevel.Sprite = spriteRenderer.sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDestruction()
|
||||
{
|
||||
if (AllowDestruction == true)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (Damage >= DamageLimit)
|
||||
{
|
||||
if (ReplaceWith != null)
|
||||
{
|
||||
D2D_Helper.CloneGameObject(ReplaceWith, transform.parent, transform.position, transform.rotation);
|
||||
}
|
||||
|
||||
D2D_Helper.Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f6d4628b68d59b4993e66e09e35af00
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,524 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public enum D2D_SpriteSplitOrder
|
||||
{
|
||||
Default,
|
||||
KeepLargest,
|
||||
KeepSmallest
|
||||
}
|
||||
|
||||
public abstract class D2D_Destructible : MonoBehaviour
|
||||
{
|
||||
public static List<D2D_Destructible> Destructibles = new List<D2D_Destructible>();
|
||||
|
||||
public static List<D2D_Destructible> DestructiblesCopy = new List<D2D_Destructible>();
|
||||
|
||||
public Texture2D DensityTex;
|
||||
|
||||
public bool AllowSplit;
|
||||
|
||||
public int SplitMinPixels = 20;
|
||||
|
||||
[D2D_RangeAttribute(0.0f, 1.0f)]
|
||||
public float SplitThreshold = 0.25f;
|
||||
|
||||
public D2D_SpriteSplitOrder SplitOrder = D2D_SpriteSplitOrder.Default;
|
||||
|
||||
public int OriginalSolidPixelCount;
|
||||
|
||||
[System.NonSerialized]
|
||||
private Texture2D alphaTex;
|
||||
|
||||
[SerializeField]
|
||||
private float alphaScaleX;
|
||||
|
||||
[SerializeField]
|
||||
private float alphaScaleY;
|
||||
|
||||
[SerializeField]
|
||||
private byte[] alphaTexData;
|
||||
|
||||
[SerializeField]
|
||||
private int alphaTexWidth;
|
||||
|
||||
[SerializeField]
|
||||
private int alphaTexHeight;
|
||||
|
||||
[SerializeField]
|
||||
private int cachedSolidPixelCount = -1;
|
||||
|
||||
private bool dirty;
|
||||
|
||||
public Texture2D AlphaTex
|
||||
{
|
||||
get
|
||||
{
|
||||
DeserializeAlphaTex();
|
||||
|
||||
return alphaTex;
|
||||
}
|
||||
}
|
||||
|
||||
public int SolidPixelCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (cachedSolidPixelCount == -1)
|
||||
{
|
||||
cachedSolidPixelCount = 0;
|
||||
|
||||
DeserializeAlphaTex();
|
||||
|
||||
if (alphaTex != null && alphaTex.width > 0 && alphaTex.height > 0)
|
||||
{
|
||||
/*
|
||||
for (var y = 0; y < alphaTex.height; y++)
|
||||
{
|
||||
for (var x = 0; x < alphaTex.width; x++)
|
||||
{
|
||||
if (alphaTex.GetPixel(x, y).a >= 0.5f)
|
||||
{
|
||||
cachedSolidPixelCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
// This is faster?
|
||||
var pixels = alphaTex.GetPixels32();
|
||||
|
||||
for (var i = pixels.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (pixels[i].a >= 128)
|
||||
{
|
||||
cachedSolidPixelCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cachedSolidPixelCount;
|
||||
}
|
||||
}
|
||||
|
||||
public float SolidPixelRatio
|
||||
{
|
||||
get
|
||||
{
|
||||
return D2D_Helper.Divide(SolidPixelCount, OriginalSolidPixelCount);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Matrix4x4 WorldToPixelMatrix
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public void ResizeAlphaTex(int newWidth, int newHeight)
|
||||
{
|
||||
DeserializeAlphaTex();
|
||||
|
||||
if (alphaTex != null)
|
||||
{
|
||||
ReplaceAlphaWith(new D2D_Pixels(alphaTex).GetResized(newWidth, newHeight));
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Halve Alpha Tex")]
|
||||
public void HalveAlphaTex()
|
||||
{
|
||||
DeserializeAlphaTex();
|
||||
|
||||
if (alphaTex != null)
|
||||
{
|
||||
ResizeAlphaTex(alphaTex.width / 2, alphaTex.height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Blur Alpha Tex")]
|
||||
public void BlurAlphaTex()
|
||||
{
|
||||
DeserializeAlphaTex();
|
||||
|
||||
if (alphaTex != null)
|
||||
{
|
||||
ReplaceAlphaWith(new D2D_Pixels(alphaTex).GetBlurredAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Recalculate Original Solid Pixel Count")]
|
||||
public void RecalculateOriginalSolidPixelCount()
|
||||
{
|
||||
OriginalSolidPixelCount = SolidPixelCount;
|
||||
}
|
||||
|
||||
public void ReplaceAlphaWith(Sprite newSprite)
|
||||
{
|
||||
ReplaceAlphaWith(newSprite != null ? new D2D_Pixels(newSprite) : null);
|
||||
}
|
||||
|
||||
public void ReplaceAlphaWith(Texture2D newTexture)
|
||||
{
|
||||
ReplaceAlphaWith(newTexture != null ? new D2D_Pixels(newTexture) : null);
|
||||
}
|
||||
|
||||
public void ReplaceAlphaWith(int newWidth, int newHeight, Color32[] newPixels)
|
||||
{
|
||||
ReplaceAlphaWith(new D2D_Pixels(newWidth, newHeight, newPixels));
|
||||
}
|
||||
|
||||
public void ReplaceAlphaWith(D2D_Pixels newPixels)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (alphaTex != null)
|
||||
{
|
||||
D2D_Helper.Record(this, "Modify Alpha Tex");
|
||||
}
|
||||
#endif
|
||||
if (newPixels != null)
|
||||
{
|
||||
// Remake texture?
|
||||
//if (alphaTex == null || alphaTex.width != newPixels.Width || alphaTex.height != newPixels.Height)
|
||||
{
|
||||
DestroyAlphaTex();
|
||||
|
||||
alphaTexData = newPixels.ApplyAlpha();
|
||||
alphaTexWidth = newPixels.Width;
|
||||
alphaTexHeight = newPixels.Height;
|
||||
alphaScaleX = D2D_Helper.Reciprocal(alphaTexWidth - 1);
|
||||
alphaScaleY = D2D_Helper.Reciprocal(alphaTexHeight - 1);
|
||||
}
|
||||
// Copy settings over?
|
||||
//else
|
||||
{
|
||||
// alphaTex.SetPixels32(newPixels.Pixels);
|
||||
// alphaTex.Apply();
|
||||
}
|
||||
|
||||
UpdateRect(0, alphaTexWidth, 0, alphaTexHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyAlphaTex();
|
||||
|
||||
UpdateRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
MarkAsDirty();
|
||||
|
||||
NotifyChanges();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
D2D_Helper.SetDirty(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Matrix4x4 CalculateStampMatrix(Vector2 position, Vector2 size, float angle)
|
||||
{
|
||||
var t = D2D_Helper.TranslationMatrix(position.x, position.y, 0.0f);
|
||||
var r = D2D_Helper.RotationMatrix(Quaternion.Euler(0.0f, 0.0f, angle));
|
||||
var s = D2D_Helper.ScalingMatrix(size.x, size.y, 1.0f);
|
||||
var o = D2D_Helper.TranslationMatrix(-0.5f, -0.5f, 0.0f); // Centre stamp
|
||||
|
||||
return t * r * s * o;
|
||||
}
|
||||
|
||||
public static void SliceAll(Vector2 startPos, Vector2 endPos, float thickness, Texture2D stampTex, float hardness, int layerMask = -1)
|
||||
{
|
||||
if (stampTex != null)
|
||||
{
|
||||
var mid = (startPos + endPos) / 2.0f;
|
||||
var vec = endPos - startPos;
|
||||
var size = new Vector2(thickness, vec.magnitude);
|
||||
var angle = D2D_Helper.Atan2(vec) * -Mathf.Rad2Deg;
|
||||
|
||||
StampAll(CalculateStampMatrix(mid, size, angle), stampTex, hardness, layerMask);
|
||||
}
|
||||
}
|
||||
|
||||
public static void StampAll(Vector2 position, Vector2 size, float angle, Texture2D stampTex, float hardness, int layerMask = -1)
|
||||
{
|
||||
if (stampTex != null)
|
||||
{
|
||||
StampAll(CalculateStampMatrix(position, size, angle), stampTex, hardness, layerMask);
|
||||
}
|
||||
}
|
||||
|
||||
public static void StampAll(Matrix4x4 matrix, Texture2D stampTex, float hardness, int layerMask = -1)
|
||||
{
|
||||
if (stampTex != null)
|
||||
{
|
||||
// The original list may change during the stamping, so make a clone
|
||||
DestructiblesCopy.Clear();
|
||||
DestructiblesCopy.AddRange(Destructibles);
|
||||
|
||||
foreach (var destructible in DestructiblesCopy)
|
||||
{
|
||||
if (destructible != null)
|
||||
{
|
||||
var mask = 1 << destructible.gameObject.layer;
|
||||
|
||||
if ((layerMask & mask) != 0)
|
||||
{
|
||||
destructible.Stamp(matrix, stampTex, hardness);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float GetAlphaAll(Vector2 position, int layerMask = -1)
|
||||
{
|
||||
var alpha = default(float);
|
||||
|
||||
GetAlphaAll(position, ref alpha);
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
public bool GetAlphaAll(Vector2 position, ref float alpha, int layerMask = -1)
|
||||
{
|
||||
foreach (var destructible in Destructibles)
|
||||
{
|
||||
if (destructible != null)
|
||||
{
|
||||
var mask = 1 << destructible.gameObject.layer;
|
||||
|
||||
if ((layerMask & mask) != 0)
|
||||
{
|
||||
if (destructible.GetAlpha(position, ref alpha) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public float GetAlpha(Vector2 position)
|
||||
{
|
||||
var alpha = default(float);
|
||||
|
||||
GetAlpha(position, ref alpha);
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
public bool GetAlpha(Vector2 position, ref float alpha)
|
||||
{
|
||||
if (alphaTexData != null && alphaTexData.Length > 0)
|
||||
{
|
||||
var point = WorldToPixelMatrix.MultiplyPoint(position);
|
||||
var x = Mathf.FloorToInt(point.x);
|
||||
var y = Mathf.FloorToInt(point.y);
|
||||
|
||||
if (x >= 0 && x < alphaTexWidth)
|
||||
{
|
||||
if (y >= 0 && y < alphaTexHeight)
|
||||
{
|
||||
alpha = alphaTexData[x + y * alphaTexWidth]; return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Stamp(Vector2 position, Vector2 size, float angle, Texture2D stampTex, float hardness)
|
||||
{
|
||||
DeserializeAlphaTex();
|
||||
|
||||
if (alphaTex != null && stampTex != null)
|
||||
{
|
||||
Stamp(CalculateStampMatrix(position, size, angle), stampTex, hardness);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stamp(Matrix4x4 stampMatrix, Texture2D stampTex, float hardness)
|
||||
{
|
||||
if (alphaTex != null && stampTex != null)
|
||||
{
|
||||
var stampToPixelMatrix = WorldToPixelMatrix * stampMatrix;
|
||||
var pixelToStampMatrix = stampToPixelMatrix.inverse;
|
||||
#if UNITY_EDITOR
|
||||
D2D_Helper.MakeTextureReadable(stampTex);
|
||||
D2D_Helper.MakeTextureReadable(DensityTex);
|
||||
#endif
|
||||
// Project corners of stamp
|
||||
// TODO: account for non-orthogonal matrices?
|
||||
var bl = stampToPixelMatrix.MultiplyPoint(new Vector3(0.0f, 0.0f, 0.0f));
|
||||
var br = stampToPixelMatrix.MultiplyPoint(new Vector3(1.0f, 0.0f, 0.0f));
|
||||
var tl = stampToPixelMatrix.MultiplyPoint(new Vector3(0.0f, 1.0f, 0.0f));
|
||||
var tr = stampToPixelMatrix.MultiplyPoint(new Vector3(1.0f, 1.0f, 0.0f));
|
||||
|
||||
// Find AABB of stamp
|
||||
var xMin = Mathf.FloorToInt(Mathf.Min(Mathf.Min(bl.x, br.x), Mathf.Min(tl.x, tr.x)));
|
||||
var xMax = Mathf.FloorToInt(Mathf.Max(Mathf.Max(bl.x, br.x), Mathf.Max(tl.x, tr.x)));
|
||||
var yMin = Mathf.FloorToInt(Mathf.Min(Mathf.Min(bl.y, br.y), Mathf.Min(tl.y, tr.y)));
|
||||
var yMax = Mathf.FloorToInt(Mathf.Max(Mathf.Max(bl.y, br.y), Mathf.Max(tl.y, tr.y)));
|
||||
|
||||
xMin = Mathf.Clamp(xMin, 0, alphaTexWidth - 1);
|
||||
xMax = Mathf.Clamp(xMax, 0, alphaTexWidth - 1);
|
||||
yMin = Mathf.Clamp(yMin, 0, alphaTexHeight - 1);
|
||||
yMax = Mathf.Clamp(yMax, 0, alphaTexHeight - 1);
|
||||
|
||||
if (xMax > xMin && yMax > yMin)
|
||||
{
|
||||
MarkAsDirty();
|
||||
|
||||
for (var y = yMin; y <= yMax; y++)
|
||||
{
|
||||
for (var x = xMin; x <= xMax; x++)
|
||||
{
|
||||
var uv = pixelToStampMatrix.MultiplyPoint(new Vector3(x, y, 0.0f));
|
||||
|
||||
// Is this pixel within the stamp?
|
||||
if (uv.x >= 0.0f && uv.y >= 0.0f && uv.x < 1.0f && uv.y < 1.0f)
|
||||
{
|
||||
var stamp = stampTex.GetPixel(Mathf.FloorToInt(uv.x * stampTex.width), Mathf.FloorToInt(uv.y * stampTex.height));
|
||||
|
||||
FastPaint(x, y, stamp.a * hardness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (alphaTex != null)
|
||||
{
|
||||
alphaTex.Apply();
|
||||
}
|
||||
|
||||
var split = false;
|
||||
|
||||
if (AllowSplit == true)
|
||||
{
|
||||
split = D2D_SplitCalculator.Generate(this, SplitOrder);
|
||||
}
|
||||
|
||||
if (split == false)
|
||||
{
|
||||
UpdateRect(xMin, xMax, yMin, yMax);
|
||||
|
||||
NotifyChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FastPaint(int x, int y, float opacity)
|
||||
{
|
||||
var index = x + y * alphaTexWidth;
|
||||
var alpha = alphaTexData[index];
|
||||
|
||||
if (DensityTex != null)
|
||||
{
|
||||
var u = x * alphaScaleX * DensityTex.width;
|
||||
var v = y * alphaScaleY * DensityTex.height;
|
||||
var density = DensityTex.GetPixel(Mathf.FloorToInt(u), Mathf.FloorToInt(v));
|
||||
|
||||
opacity *= 1.0f - density.a;
|
||||
}
|
||||
|
||||
var a = (int)(opacity * 255.0f);
|
||||
|
||||
alpha = (byte)Mathf.Clamp((int)alpha - a, 0, 255);
|
||||
|
||||
alphaTexData[index] = alpha;
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
Destructibles.Add(this);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (UnityEditor.AssetDatabase.Contains(this) == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
CacheTexures();
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
Destructibles.Remove(this);
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
DestroyAlphaTex();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
D2D_Helper.MakeTextureReadable(DensityTex);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected abstract void UpdateRect(int xMin, int xMax, int yMin, int yMax);
|
||||
|
||||
private void DeserializeAlphaTex()
|
||||
{
|
||||
if ((dirty == true || alphaTex == null) && alphaTexData != null && alphaTexData.Length > 0 && alphaTexWidth > 0 && alphaTexHeight > 0)
|
||||
{
|
||||
dirty = false;
|
||||
|
||||
var pixels = new Color32[alphaTexData.Length];
|
||||
|
||||
for (var i = pixels.Length - 1; i >= 0; i--)
|
||||
{
|
||||
pixels[i].a = alphaTexData[i];
|
||||
}
|
||||
|
||||
alphaTex = new Texture2D(alphaTexWidth, alphaTexHeight, TextureFormat.Alpha8, false);
|
||||
alphaTex.hideFlags = HideFlags.DontSave;
|
||||
alphaTex.wrapMode = TextureWrapMode.Clamp;
|
||||
alphaTex.SetPixels32(pixels);
|
||||
alphaTex.Apply();
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyAlphaTex()
|
||||
{
|
||||
if (alphaTex != null)
|
||||
{
|
||||
D2D_Helper.Destroy(alphaTex);
|
||||
|
||||
alphaTex = null;
|
||||
}
|
||||
|
||||
alphaTexData = null;
|
||||
alphaTexWidth = 0;
|
||||
alphaTexHeight = 0;
|
||||
}
|
||||
|
||||
private void CacheTexures()
|
||||
{
|
||||
if (DensityTex != null)
|
||||
{
|
||||
if (DensityTex.width > 0 && DensityTex.height > 0)
|
||||
{
|
||||
DensityTex.GetPixel(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MarkAsDirty()
|
||||
{
|
||||
dirty = true;
|
||||
cachedSolidPixelCount = -1;
|
||||
}
|
||||
|
||||
private void NotifyChanges()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
BroadcastMessage("OnAlphaTexModified", SendMessageOptions.DontRequireReceiver);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69ac85ffa12a392489dd6515db8561f8
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,489 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public enum D2D_SpriteColliderType
|
||||
{
|
||||
None,
|
||||
Edge,
|
||||
Polygon,
|
||||
AutoPolygon
|
||||
}
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Destructible 2D/D2D Destructible Sprite")]
|
||||
[RequireComponent(typeof(SpriteRenderer))]
|
||||
public class D2D_DestructibleSprite : D2D_Destructible
|
||||
{
|
||||
public static List<D2D_DestructibleSprite> DestructibleSprites = new List<D2D_DestructibleSprite>();
|
||||
|
||||
public Material SourceMaterial;
|
||||
|
||||
[D2D_RangeAttribute(1.0f, 10.0f)]
|
||||
public float Sharpness = 1.0f;
|
||||
|
||||
public D2D_SpriteColliderType ColliderType = D2D_SpriteColliderType.None;
|
||||
|
||||
[SerializeField]
|
||||
private SpriteRenderer spriteRenderer;
|
||||
|
||||
[SerializeField]
|
||||
private D2D_EdgeColliders edgeColliders;
|
||||
|
||||
[SerializeField]
|
||||
private D2D_PolygonColliders polygonColliders;
|
||||
|
||||
[SerializeField]
|
||||
private D2D_AutoPolygonCollider autoPolygonCollider;
|
||||
|
||||
[SerializeField]
|
||||
private float expectedPixelsToUnits;
|
||||
|
||||
[SerializeField]
|
||||
private Vector2 expectedPivot;
|
||||
|
||||
[SerializeField]
|
||||
private Material clonedMaterial;
|
||||
|
||||
public override Matrix4x4 WorldToPixelMatrix
|
||||
{
|
||||
get
|
||||
{
|
||||
if (spriteRenderer == null) spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
if (AlphaTex != null)
|
||||
{
|
||||
var sprite = spriteRenderer.sprite;
|
||||
|
||||
if (sprite != null)
|
||||
{
|
||||
var scale = CalculateAlphaTexScale(sprite);
|
||||
var offset = CalculateAlphaTexOffset(sprite);
|
||||
var s = D2D_Helper.ScalingMatrix(D2D_Helper.Reciprocal(scale));
|
||||
var t = D2D_Helper.TranslationMatrix(-offset);
|
||||
|
||||
return s * t * transform.worldToLocalMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
return transform.worldToLocalMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Halve Alpha Tex And Split Min Pixels")]
|
||||
public void HalveAlphaTexAndSplitMinPixels()
|
||||
{
|
||||
if (AlphaTex != null)
|
||||
{
|
||||
HalveAlphaTex();
|
||||
|
||||
SplitMinPixels = Mathf.Max(1, SplitMinPixels / 2);
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Blur Alpha Tex And Double Sharpness")]
|
||||
public void BlurAlphaTexAndDoubleSharpness()
|
||||
{
|
||||
if (AlphaTex != null)
|
||||
{
|
||||
BlurAlphaTex();
|
||||
|
||||
Sharpness *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildColliders()
|
||||
{
|
||||
if (AlphaTex != null && AlphaTex.width > 0 && AlphaTex.height > 0)
|
||||
{
|
||||
RebuildColliders(0, AlphaTex.width, 0, AlphaTex.height);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
// Has this been cloned?
|
||||
if (clonedMaterial != null)
|
||||
{
|
||||
foreach (var destructibleSprite in DestructibleSprites)
|
||||
{
|
||||
if (destructibleSprite != null && destructibleSprite.clonedMaterial == clonedMaterial)
|
||||
{
|
||||
OnDuplicate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DestructibleSprites.Add(this);
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
DestructibleSprites.Remove(this);
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (spriteRenderer == null) spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
// Copy alpha from main tex
|
||||
if (AlphaTex == null && spriteRenderer.sprite != null)
|
||||
{
|
||||
ReplaceAlphaWith(spriteRenderer.sprite);
|
||||
|
||||
RecalculateOriginalSolidPixelCount();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
D2D_Helper.MakeTextureReadable(DensityTex);
|
||||
#endif
|
||||
|
||||
UpdateColliders();
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
DestroyAutoPolygonCollider();
|
||||
DestroyPolygonColliders();
|
||||
DestroyEdgeColliders();
|
||||
DestroyMaterial();
|
||||
}
|
||||
|
||||
protected virtual void OnWillRenderObject()
|
||||
{
|
||||
if (spriteRenderer == null) spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
UpdateSourceMaterial();
|
||||
|
||||
DestroyMaterialIfSettingsDiffer();
|
||||
|
||||
var sprite = spriteRenderer.sprite;
|
||||
|
||||
if (SourceMaterial != null && sprite != null)
|
||||
{
|
||||
// Clone new material?
|
||||
if (clonedMaterial == null)
|
||||
{
|
||||
clonedMaterial = D2D_Helper.Clone(SourceMaterial, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
clonedMaterial.CopyPropertiesFromMaterial(SourceMaterial);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
clonedMaterial.hideFlags = HideFlags.DontSave;
|
||||
#endif
|
||||
|
||||
clonedMaterial.SetTexture("_MainTex", sprite.texture);
|
||||
clonedMaterial.SetTexture("_AlphaTex", AlphaTex);
|
||||
clonedMaterial.SetVector("_AlphaScale", CalculateAlphaScale(sprite));
|
||||
clonedMaterial.SetVector("_AlphaOffset", CalculateAlphaOffset(sprite));
|
||||
clonedMaterial.SetFloat("_Sharpness", Sharpness);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
clonedMaterial.hideFlags = HideFlags.None;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (spriteRenderer.sharedMaterial != clonedMaterial)
|
||||
{
|
||||
spriteRenderer.sharedMaterial = clonedMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateRect(int xMin, int xMax, int yMin, int yMax)
|
||||
{
|
||||
RebuildColliders(xMin, xMax, yMin, yMax);
|
||||
}
|
||||
|
||||
protected virtual void OnDuplicate()
|
||||
{
|
||||
if (spriteRenderer == null) spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
|
||||
if (clonedMaterial == spriteRenderer.sharedMaterial)
|
||||
{
|
||||
clonedMaterial = D2D_Helper.Clone(clonedMaterial);
|
||||
|
||||
spriteRenderer.sharedMaterial = clonedMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 CalculateAlphaScale(Sprite sprite)
|
||||
{
|
||||
var texture = sprite.texture;
|
||||
var textureRect = sprite.textureRect;
|
||||
var scaleX = texture.width / textureRect.width;
|
||||
var scaleY = texture.height / textureRect.height;
|
||||
|
||||
return new Vector2(scaleX, scaleY);
|
||||
}
|
||||
|
||||
private Vector2 CalculateAlphaOffset(Sprite sprite)
|
||||
{
|
||||
var texture = sprite.texture;
|
||||
var textureRect = sprite.textureRect;
|
||||
var offsetX = textureRect.x / texture.width;
|
||||
var offsetY = textureRect.y / texture.height;
|
||||
|
||||
return new Vector2(offsetX, offsetY);
|
||||
}
|
||||
|
||||
private void UpdateSourceMaterial()
|
||||
{
|
||||
// Do we need to set a source material?
|
||||
if (SourceMaterial == null)
|
||||
{
|
||||
if (spriteRenderer.sharedMaterial != null)
|
||||
{
|
||||
SourceMaterial = spriteRenderer.sharedMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
SourceMaterial = Resources.Load<Material>("Sprites-Default (Destructible 2D)");
|
||||
}
|
||||
}
|
||||
|
||||
// Replace Sprites-Default with Sprites-Default (Destructible 2D)?
|
||||
if (SourceMaterial != null && SourceMaterial.HasProperty("_AlphaTex") == false)
|
||||
{
|
||||
if (SourceMaterial.name == "Sprites-Default")
|
||||
{
|
||||
SourceMaterial = Resources.Load<Material>("Sprites-Default (Destructible 2D)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildColliders(int xMin, int xMax, int yMin, int yMax)
|
||||
{
|
||||
switch (ColliderType)
|
||||
{
|
||||
case D2D_SpriteColliderType.Edge:
|
||||
{
|
||||
if (edgeColliders != null)
|
||||
{
|
||||
edgeColliders.RebuildColliders(AlphaTex, xMin, xMax, yMin, yMax);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case D2D_SpriteColliderType.Polygon:
|
||||
{
|
||||
if (polygonColliders != null)
|
||||
{
|
||||
polygonColliders.RebuildColliders(AlphaTex, xMin, xMax, yMin, yMax);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case D2D_SpriteColliderType.AutoPolygon:
|
||||
{
|
||||
if (autoPolygonCollider != null)
|
||||
{
|
||||
autoPolygonCollider.RebuildCollider(AlphaTex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateColliders()
|
||||
{
|
||||
if (ColliderType != D2D_SpriteColliderType.None)
|
||||
{
|
||||
var colliderTransform = default(Transform);
|
||||
|
||||
switch (ColliderType)
|
||||
{
|
||||
case D2D_SpriteColliderType.Edge:
|
||||
{
|
||||
DestroyAutoPolygonCollider();
|
||||
DestroyPolygonColliders();
|
||||
|
||||
if (edgeColliders == null)
|
||||
{
|
||||
edgeColliders = D2D_Helper.CreateGameObject("Edge Colliders", transform).AddComponent<D2D_EdgeColliders>();
|
||||
edgeColliders.RebuildAllColliders(AlphaTex);
|
||||
}
|
||||
|
||||
colliderTransform = edgeColliders.transform;
|
||||
}
|
||||
break;
|
||||
|
||||
case D2D_SpriteColliderType.Polygon:
|
||||
{
|
||||
DestroyAutoPolygonCollider();
|
||||
DestroyEdgeColliders();
|
||||
|
||||
if (polygonColliders == null)
|
||||
{
|
||||
polygonColliders = D2D_Helper.CreateGameObject("Polygon Colliders", transform).AddComponent<D2D_PolygonColliders>();
|
||||
polygonColliders.RebuildAllColliders(AlphaTex);
|
||||
}
|
||||
|
||||
colliderTransform = polygonColliders.transform;
|
||||
}
|
||||
break;
|
||||
|
||||
case D2D_SpriteColliderType.AutoPolygon:
|
||||
{
|
||||
DestroyPolygonColliders();
|
||||
DestroyEdgeColliders();
|
||||
|
||||
if (autoPolygonCollider == null)
|
||||
{
|
||||
autoPolygonCollider = D2D_Helper.CreateGameObject("Auto Polygon Collider", transform).AddComponent<D2D_AutoPolygonCollider>();
|
||||
autoPolygonCollider.RebuildCollider(AlphaTex);
|
||||
}
|
||||
|
||||
colliderTransform = autoPolygonCollider.transform;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (colliderTransform != null)
|
||||
{
|
||||
var colliderScale = Vector3.one;
|
||||
var colliderOffset = Vector3.zero;
|
||||
|
||||
if (spriteRenderer != null && AlphaTex != null)
|
||||
{
|
||||
var sprite = spriteRenderer.sprite;
|
||||
|
||||
// Magic to align the colliders with the sprite renderer
|
||||
if (sprite != null)
|
||||
{
|
||||
colliderScale = CalculateAlphaTexScale(sprite);
|
||||
colliderOffset = CalculateAlphaTexOffset(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
if (colliderTransform.localPosition != colliderOffset)
|
||||
{
|
||||
colliderTransform.localPosition = colliderOffset;
|
||||
}
|
||||
|
||||
if (colliderTransform.localScale != colliderScale)
|
||||
{
|
||||
colliderTransform.localScale = colliderScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyAutoPolygonCollider();
|
||||
DestroyPolygonColliders();
|
||||
DestroyEdgeColliders();
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 CalculateAlphaTexScale(Sprite sprite)
|
||||
{
|
||||
var scale = Vector3.one;
|
||||
|
||||
scale.x = D2D_Helper.Divide(sprite.bounds.size.x, sprite.rect.width) * D2D_Helper.Divide(sprite.textureRect.width, AlphaTex.width);
|
||||
scale.y = D2D_Helper.Divide(sprite.bounds.size.y, sprite.rect.height) * D2D_Helper.Divide(sprite.textureRect.height, AlphaTex.height);
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
private Vector3 CalculateAlphaTexOffset(Sprite sprite)
|
||||
{
|
||||
var offset = Vector3.one;
|
||||
|
||||
offset.x = sprite.bounds.min.x + sprite.bounds.size.x * D2D_Helper.Divide(sprite.textureRectOffset.x, sprite.rect.width);
|
||||
offset.y = sprite.bounds.min.y + sprite.bounds.size.y * D2D_Helper.Divide(sprite.textureRectOffset.y, sprite.rect.height);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
private void DestroyMaterialIfSettingsDiffer()
|
||||
{
|
||||
if (clonedMaterial != null)
|
||||
{
|
||||
if (SourceMaterial == null)
|
||||
{
|
||||
DestroyMaterial(); return;
|
||||
}
|
||||
|
||||
if (clonedMaterial.shader != SourceMaterial.shader)
|
||||
{
|
||||
DestroyMaterial(); return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyEdgeColliders()
|
||||
{
|
||||
if (edgeColliders != null)
|
||||
{
|
||||
D2D_Helper.Destroy(edgeColliders.gameObject);
|
||||
|
||||
edgeColliders = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyPolygonColliders()
|
||||
{
|
||||
if (polygonColliders != null)
|
||||
{
|
||||
D2D_Helper.Destroy(polygonColliders.gameObject);
|
||||
|
||||
polygonColliders = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyAutoPolygonCollider()
|
||||
{
|
||||
if (autoPolygonCollider != null)
|
||||
{
|
||||
D2D_Helper.Destroy(autoPolygonCollider.gameObject);
|
||||
|
||||
autoPolygonCollider = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyMaterial()
|
||||
{
|
||||
D2D_Helper.Destroy(clonedMaterial);
|
||||
|
||||
clonedMaterial = null;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.MenuItem("CONTEXT/SpriteRenderer/Make Destructible", true)]
|
||||
private static bool MakeDestructibleValidate(UnityEditor.MenuCommand mc)
|
||||
{
|
||||
if (mc != null && mc.context != null)
|
||||
{
|
||||
var spriteRenderer = mc.context as SpriteRenderer;
|
||||
|
||||
if (spriteRenderer != null)
|
||||
{
|
||||
return spriteRenderer.GetComponent<D2D_DestructibleSprite>() == null;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[UnityEditor.MenuItem("CONTEXT/SpriteRenderer/Make Destructible", false)]
|
||||
private static void MakeDestructible(UnityEditor.MenuCommand mc)
|
||||
{
|
||||
if (mc != null && mc.context != null)
|
||||
{
|
||||
var spriteRenderer = mc.context as SpriteRenderer;
|
||||
|
||||
if (spriteRenderer != null && spriteRenderer.GetComponent<D2D_DestructibleSprite>() == null)
|
||||
{
|
||||
UnityEditor.Undo.AddComponent<D2D_DestructibleSprite>(spriteRenderer.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6902fd9fba33c0143ad93ba6ee754d73
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,34 @@
|
||||
using UnityEngine;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[RequireComponent(typeof(D2D_DestructibleSprite))]
|
||||
[RequireComponent(typeof(Rigidbody2D))]
|
||||
[AddComponentMenu("Destructible 2D/D2D Dynamic Mass")]
|
||||
public class D2D_DynamicMass : MonoBehaviour
|
||||
{
|
||||
public float MassPerPixel = 0.01f;
|
||||
|
||||
private D2D_DestructibleSprite destructibleSprite;
|
||||
|
||||
protected virtual void FixedUpdate()
|
||||
{
|
||||
UpdateMass();
|
||||
}
|
||||
|
||||
protected virtual void LateUpdate()
|
||||
{
|
||||
UpdateMass();
|
||||
}
|
||||
|
||||
private void UpdateMass()
|
||||
{
|
||||
if (destructibleSprite == null) destructibleSprite = GetComponent<D2D_DestructibleSprite>();
|
||||
|
||||
var newMass = destructibleSprite.SolidPixelCount * MassPerPixel;
|
||||
|
||||
if (GetComponent<Rigidbody2D>().mass != newMass)
|
||||
{
|
||||
GetComponent<Rigidbody2D>().mass = newMass;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd1445ce7be05b54b9509f4324e485e4
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,216 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Destructible 2D/D2D Edge Colliders")]
|
||||
public class D2D_EdgeColliders : D2D_Collider
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Cell
|
||||
{
|
||||
public List<EdgeCollider2D> EdgeCollider2Ds = new List<EdgeCollider2D>();
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
foreach (var edgeCollider2D in EdgeCollider2Ds)
|
||||
{
|
||||
D2D_Helper.Destroy(edgeCollider2D);
|
||||
}
|
||||
|
||||
EdgeCollider2Ds.Clear();
|
||||
}
|
||||
|
||||
public void ReplaceColliders(List<EdgeCollider2D> newEdgeCollider2Ds)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
EdgeCollider2Ds.AddRange(newEdgeCollider2Ds);
|
||||
}
|
||||
|
||||
public void UpdateColliderSettings(bool isTrigger, PhysicsMaterial2D material)
|
||||
{
|
||||
foreach (var edgeCollider2D in EdgeCollider2Ds)
|
||||
{
|
||||
if (edgeCollider2D != null)
|
||||
{
|
||||
edgeCollider2D.isTrigger = isTrigger;
|
||||
edgeCollider2D.sharedMaterial = material;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[D2D_PopupAttribute(8, 16, 32, 64, 128, 256)]
|
||||
public int CellSize = 64;
|
||||
|
||||
[D2D_RangeAttribute(0.0f, 0.3f)]
|
||||
public float Tolerance = 0.01f;
|
||||
|
||||
[SerializeField]
|
||||
private List<Cell> cells = new List<Cell>();
|
||||
|
||||
[SerializeField]
|
||||
private int cellsX;
|
||||
|
||||
[SerializeField]
|
||||
private int cellsY;
|
||||
|
||||
[SerializeField]
|
||||
private int cellsXY;
|
||||
|
||||
[SerializeField]
|
||||
private int width;
|
||||
|
||||
[SerializeField]
|
||||
private int height;
|
||||
|
||||
public void RebuildAllColliders(Texture2D alphaTex)
|
||||
{
|
||||
if (alphaTex != null)
|
||||
{
|
||||
RebuildColliders(alphaTex, 0, alphaTex.width, 0, alphaTex.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyAllCells();
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildColliders(Texture2D alphaTex, int xMin, int xMax, int yMin, int yMax)
|
||||
{
|
||||
UpdateColliders(alphaTex);
|
||||
|
||||
if (cells.Count > 0)
|
||||
{
|
||||
//var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
xMin = Mathf.Clamp(xMin - 1, 0, alphaTex.width - 1);
|
||||
yMin = Mathf.Clamp(yMin - 1, 0, alphaTex.height - 1);
|
||||
|
||||
var cellXMin = xMin / CellSize;
|
||||
var cellYMin = yMin / CellSize;
|
||||
var cellXMax = (xMax + CellSize - 1) / CellSize;
|
||||
var cellYMax = (yMax + CellSize - 1) / CellSize;
|
||||
|
||||
for (var cellY = cellYMin; cellY <= cellYMax; cellY++)
|
||||
{
|
||||
for (var cellX = cellXMin; cellX <= cellXMax; cellX++)
|
||||
{
|
||||
if (cellX >= 0 && cellX < cellsX && cellY >= 0 && cellY < cellsY)
|
||||
{
|
||||
xMin = CellSize * cellX;
|
||||
yMin = CellSize * cellY;
|
||||
xMax = Mathf.Min(CellSize + xMin, alphaTex.width);
|
||||
yMax = Mathf.Min(CellSize + yMin, alphaTex.height);
|
||||
|
||||
var cell = cells[cellX + cellY * cellsX];
|
||||
var newEdgeCollider2Ds = D2D_EdgeCalculator.Generate(gameObject, alphaTex, xMin, xMax, yMin, yMax, Tolerance);
|
||||
|
||||
cell.ReplaceColliders(newEdgeCollider2Ds);
|
||||
cell.UpdateColliderSettings(IsTrigger, Material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//stopwatch.Stop(); Debug.Log("RebuildColliders took " + stopwatch.ElapsedMilliseconds + " ms");
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateColliderSettings()
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
if (cell != null)
|
||||
{
|
||||
cell.UpdateColliderSettings(IsTrigger, Material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateColliders(Texture2D alphaTex)
|
||||
{
|
||||
if (alphaTex != null && alphaTex.width > 0 && alphaTex.height > 0 && CellSize > 0 && Tolerance >= 0.0f)
|
||||
{
|
||||
cellsX = (alphaTex.width + CellSize - 1) / CellSize;
|
||||
cellsY = (alphaTex.height + CellSize - 1) / CellSize;
|
||||
cellsXY = cellsX * cellsY;
|
||||
|
||||
if (cells.Count > 0)
|
||||
{
|
||||
if (cells.Count != cellsXY)
|
||||
{
|
||||
DestroyAllCells();
|
||||
}
|
||||
|
||||
if (alphaTex.width != width || alphaTex.height != height)
|
||||
{
|
||||
DestroyAllCells();
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild all cells?
|
||||
if (cells.Count == 0 && cellsXY > 0)
|
||||
{
|
||||
width = alphaTex.width;
|
||||
height = alphaTex.height;
|
||||
|
||||
for (var i = 0; i < cellsXY; i++)
|
||||
{
|
||||
cells.Add(new Cell());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyAllCells();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
D2D_Helper.DestroyManaged(DestroyAllCells);
|
||||
}
|
||||
|
||||
protected override void RebuildAll()
|
||||
{
|
||||
var destructible = D2D_Helper.GetComponentUpwards<D2D_Destructible>(transform);
|
||||
|
||||
if (destructible != null)
|
||||
{
|
||||
RebuildAllColliders(destructible.AlphaTex);
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyAllCells()
|
||||
{
|
||||
if (cells.Count > 0)
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
cell.Destroy();
|
||||
}
|
||||
|
||||
cells.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void SetHideFlags(HideFlags hideFlags)
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
foreach (var edgeCollider2D in cell.EdgeCollider2Ds)
|
||||
{
|
||||
if (edgeCollider2D != null)
|
||||
{
|
||||
edgeCollider2D.hideFlags = hideFlags;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
protected override void SetHideFlags(HideFlags hideFlags)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df82a8b85d0c7a241a904864275dc63c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
|
||||
[AddComponentMenu("Destructible 2D/D2D Explosion Damage")]
|
||||
public class D2D_ExplosionDamage : MonoBehaviour
|
||||
{
|
||||
public LayerMask Layers = -1;
|
||||
|
||||
public float Radius = 1.0f;
|
||||
|
||||
public float Damage = 10.0f;
|
||||
|
||||
public int Samples = 32;
|
||||
|
||||
public bool HasExploded;
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (HasExploded == false)
|
||||
{
|
||||
HasExploded = true;
|
||||
|
||||
Explode();
|
||||
}
|
||||
}
|
||||
|
||||
public void Explode()
|
||||
{
|
||||
if (Samples > 0)
|
||||
{
|
||||
var origin = transform.position;
|
||||
var step = 360.0f / Samples;
|
||||
var scaledDamage = Damage / Samples;
|
||||
|
||||
for (var i = 0; i < Samples; i++)
|
||||
{
|
||||
var angle = i * step;
|
||||
var direction = new Vector2(Mathf.Sin(angle), Mathf.Cos(angle));
|
||||
var hit = Physics2D.Raycast(origin, direction, Radius);
|
||||
var collider = hit.collider;
|
||||
|
||||
if (collider != null && collider.isTrigger == false)
|
||||
{
|
||||
var mask = 1 << collider.gameObject.layer;
|
||||
|
||||
if ((mask & Layers.value) != 0)
|
||||
{
|
||||
var damageableSprite = D2D_Helper.GetComponentUpwards<D2D_DamageableSprite>(collider.transform);
|
||||
|
||||
if (damageableSprite != null)
|
||||
{
|
||||
damageableSprite.InflictDamage(scaledDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 856de3ca546c49448b24dcb5151b45fb
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
|
||||
[AddComponentMenu("Destructible 2D/D2D Explosion Force")]
|
||||
public class D2D_ExplosionForce : MonoBehaviour
|
||||
{
|
||||
public LayerMask Layers = -1;
|
||||
|
||||
public float Radius = 1.0f;
|
||||
|
||||
public float Force = 1.0f;
|
||||
|
||||
public int Samples = 32;
|
||||
|
||||
public bool HasExploded;
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (HasExploded == false)
|
||||
{
|
||||
HasExploded = true;
|
||||
|
||||
Explode();
|
||||
}
|
||||
}
|
||||
|
||||
public void Explode()
|
||||
{
|
||||
if (Samples > 0)
|
||||
{
|
||||
var origin = transform.position;
|
||||
var step = 360.0f / Samples;
|
||||
var scaledForce = Force / Samples;
|
||||
|
||||
for (var i = 0; i < Samples; i++)
|
||||
{
|
||||
var angle = i * step;
|
||||
var direction = new Vector2(Mathf.Sin(angle), Mathf.Cos(angle));
|
||||
var hit = Physics2D.Raycast(origin, direction, Radius);
|
||||
var collider = hit.collider;
|
||||
|
||||
if (collider != null && collider.isTrigger == false && collider.attachedRigidbody != null)
|
||||
{
|
||||
var mask = 1 << collider.gameObject.layer;
|
||||
|
||||
if ((mask & Layers.value) != 0)
|
||||
{
|
||||
var force = direction * scaledForce * (1.0f - hit.fraction);
|
||||
|
||||
hit.collider.attachedRigidbody.AddForceAtPosition(force, origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30cd04bd1fefc174b811f74241da78b1
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,36 @@
|
||||
using UnityEngine;
|
||||
|
||||
[AddComponentMenu("Destructible 2D/D2D Explosion Stamp")]
|
||||
public class D2D_ExplosionStamp : MonoBehaviour
|
||||
{
|
||||
public LayerMask Layers = -1;
|
||||
|
||||
public Texture2D StampTex;
|
||||
|
||||
public float Hardness = 1.0f;
|
||||
|
||||
public Vector2 Size = Vector2.one;
|
||||
|
||||
public float AngleOffset;
|
||||
|
||||
public float AngleRandomness;
|
||||
|
||||
public bool HasExploded;
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (HasExploded == false)
|
||||
{
|
||||
HasExploded = true;
|
||||
|
||||
Explode();
|
||||
}
|
||||
}
|
||||
|
||||
public void Explode()
|
||||
{
|
||||
var angle = transform.rotation.eulerAngles.z + AngleOffset + Random.Range(-0.5f, 0.5f) * AngleRandomness;
|
||||
|
||||
D2D_Destructible.StampAll(transform.position, Size, angle, StampTex, Hardness, Layers);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bbea72cf307518469cf0d6eb4291f0d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
116
Assets/Reference/Destructible2D/Required/Player/D2D_Fixture.cs
Normal file
116
Assets/Reference/Destructible2D/Required/Player/D2D_Fixture.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[AddComponentMenu("Destructible 2D/D2D Fixture")]
|
||||
public class D2D_Fixture : MonoBehaviour
|
||||
{
|
||||
public static List<D2D_Fixture> Fixtures = new List<D2D_Fixture>();
|
||||
|
||||
[D2D_RangeAttribute(0.01f, 1.0f)]
|
||||
public float Threshold = 0.5f;
|
||||
|
||||
private D2D_Destructible destructible;
|
||||
|
||||
private bool dirty = true;
|
||||
|
||||
[SerializeField]
|
||||
private int fixtureID;
|
||||
|
||||
private static int nextFixtureID = 1;
|
||||
|
||||
protected virtual void OnSpriteSplit(bool isClone)
|
||||
{
|
||||
// Assign a fixtureID to the parent, this will be copied to the clones
|
||||
if (isClone == false)
|
||||
{
|
||||
if (nextFixtureID > 1000000)
|
||||
{
|
||||
nextFixtureID = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextFixtureID += 1;
|
||||
}
|
||||
|
||||
fixtureID = nextFixtureID;
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
protected virtual void OnAlphaTexModified()
|
||||
{
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
Fixtures.Add(this);
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
Fixtures.Remove(this);
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (dirty == true)
|
||||
{
|
||||
dirty = false;
|
||||
destructible = D2D_Helper.GetComponentUpwards<D2D_Destructible>(transform);
|
||||
|
||||
if (destructible != null)
|
||||
{
|
||||
var alpha = destructible.GetAlpha(transform.position);
|
||||
|
||||
// Break fixture?
|
||||
if (alpha < Threshold)
|
||||
{
|
||||
DestroyFixture();
|
||||
}
|
||||
// Break others?
|
||||
else if (fixtureID > 0)
|
||||
{
|
||||
for (var i = Fixtures.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var fixture = Fixtures[i];
|
||||
|
||||
if (fixture != null && fixture != this && fixture.fixtureID == fixtureID)
|
||||
{
|
||||
fixture.DestroyFixture();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static D2D_Fixture FindFixture(string name, Transform transform)
|
||||
{
|
||||
if (transform != null)
|
||||
{
|
||||
var destructible = transform.GetComponentInParent<D2D_Destructible>();
|
||||
|
||||
if (destructible != null)
|
||||
{
|
||||
var fixtures = destructible.GetComponentsInChildren<D2D_Fixture>();
|
||||
|
||||
foreach (var fixture in fixtures)
|
||||
{
|
||||
if (fixture.name == name)
|
||||
{
|
||||
return fixture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void DestroyFixture()
|
||||
{
|
||||
D2D_Helper.Destroy(gameObject);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bbefba9614e86b48a23d7d563b245a9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,217 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Destructible 2D/D2D Polygon Colliders")]
|
||||
public class D2D_PolygonColliders : D2D_Collider
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Cell
|
||||
{
|
||||
public PolygonCollider2D PolygonCollider2D;
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
if (PolygonCollider2D != null)
|
||||
{
|
||||
D2D_Helper.Destroy(PolygonCollider2D);
|
||||
|
||||
PolygonCollider2D = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReplaceCollider(PolygonCollider2D newPolygonCollider2D)
|
||||
{
|
||||
if (PolygonCollider2D != newPolygonCollider2D)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
PolygonCollider2D = newPolygonCollider2D;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateColliderSettings(bool isTrigger, PhysicsMaterial2D material)
|
||||
{
|
||||
if (PolygonCollider2D != null)
|
||||
{
|
||||
PolygonCollider2D.isTrigger = isTrigger;
|
||||
PolygonCollider2D.sharedMaterial = material;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[D2D_PopupAttribute(8, 16, 32, 64, 128, 256)]
|
||||
public int CellSize = 64;
|
||||
|
||||
[D2D_RangeAttribute(0.0f, 0.6f)]
|
||||
public float Tolerance = 0.1f;
|
||||
|
||||
[SerializeField]
|
||||
private List<Cell> cells = new List<Cell>();
|
||||
|
||||
[SerializeField]
|
||||
private int cellsX;
|
||||
|
||||
[SerializeField]
|
||||
private int cellsY;
|
||||
|
||||
[SerializeField]
|
||||
private int cellsXY;
|
||||
|
||||
[SerializeField]
|
||||
private int width;
|
||||
|
||||
[SerializeField]
|
||||
private int height;
|
||||
|
||||
public void RebuildAllColliders(Texture2D alphaTex)
|
||||
{
|
||||
if (alphaTex != null)
|
||||
{
|
||||
RebuildColliders(alphaTex, 0, alphaTex.width, 0, alphaTex.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyAllCells();
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildColliders(Texture2D alphaTex, int xMin, int xMax, int yMin, int yMax)
|
||||
{
|
||||
UpdateColliders(alphaTex);
|
||||
|
||||
if (cells.Count > 0)
|
||||
{
|
||||
//var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
xMin = Mathf.Clamp(xMin - 1, 0, alphaTex.width - 1);
|
||||
yMin = Mathf.Clamp(yMin - 1, 0, alphaTex.height - 1);
|
||||
|
||||
var cellXMin = xMin / CellSize;
|
||||
var cellYMin = yMin / CellSize;
|
||||
var cellXMax = (xMax + CellSize - 1) / CellSize;
|
||||
var cellYMax = (yMax + CellSize - 1) / CellSize;
|
||||
|
||||
for (var cellY = cellYMin; cellY <= cellYMax; cellY++)
|
||||
{
|
||||
for (var cellX = cellXMin; cellX <= cellXMax; cellX++)
|
||||
{
|
||||
if (cellX >= 0 && cellX < cellsX && cellY >= 0 && cellY < cellsY)
|
||||
{
|
||||
xMin = CellSize * cellX;
|
||||
yMin = CellSize * cellY;
|
||||
xMax = Mathf.Min(CellSize + xMin, alphaTex.width);
|
||||
yMax = Mathf.Min(CellSize + yMin, alphaTex.height);
|
||||
|
||||
var cell = cells[cellX + cellY * cellsX];
|
||||
var newPolygonCollider2D = D2D_PolygonCalculator.Generate(gameObject, cell.PolygonCollider2D, alphaTex, xMin, xMax, yMin, yMax, Tolerance);
|
||||
|
||||
cell.ReplaceCollider(newPolygonCollider2D);
|
||||
cell.UpdateColliderSettings(IsTrigger, Material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//stopwatch.Stop(); Debug.Log("RebuildColliders took " + stopwatch.ElapsedMilliseconds + " ms");
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateColliderSettings()
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
if (cell != null)
|
||||
{
|
||||
cell.UpdateColliderSettings(IsTrigger, Material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateColliders(Texture2D alphaTex)
|
||||
{
|
||||
if (alphaTex != null && alphaTex.width > 0 && alphaTex.height > 0 && CellSize > 0 && Tolerance >= 0.0f)
|
||||
{
|
||||
cellsX = (alphaTex.width + CellSize - 1) / CellSize;
|
||||
cellsY = (alphaTex.height + CellSize - 1) / CellSize;
|
||||
cellsXY = cellsX * cellsY;
|
||||
|
||||
if (cells.Count > 0)
|
||||
{
|
||||
if (cells.Count != cellsXY)
|
||||
{
|
||||
DestroyAllCells();
|
||||
}
|
||||
|
||||
if (alphaTex.width != width || alphaTex.height != height)
|
||||
{
|
||||
DestroyAllCells();
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild all cells?
|
||||
if (cells.Count == 0 && cellsXY > 0)
|
||||
{
|
||||
width = alphaTex.width;
|
||||
height = alphaTex.height;
|
||||
|
||||
for (var i = 0; i < cellsXY; i++)
|
||||
{
|
||||
cells.Add(new Cell());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyAllCells();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
D2D_Helper.DestroyManaged(DestroyAllCells);
|
||||
}
|
||||
|
||||
protected override void RebuildAll()
|
||||
{
|
||||
var destructible = D2D_Helper.GetComponentUpwards<D2D_Destructible>(transform);
|
||||
|
||||
if (destructible != null)
|
||||
{
|
||||
RebuildAllColliders(destructible.AlphaTex);
|
||||
}
|
||||
}
|
||||
|
||||
private void DestroyAllCells()
|
||||
{
|
||||
if (cells.Count > 0)
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
cell.Destroy();
|
||||
}
|
||||
|
||||
cells.Clear();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
D2D_Helper.SetDirty(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void SetHideFlags(HideFlags hideFlags)
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
if (cell.PolygonCollider2D != null)
|
||||
{
|
||||
cell.PolygonCollider2D.hideFlags = hideFlags;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
protected override void SetHideFlags(HideFlags hideFlags)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70f8987444db4274e808235343cdada3
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class D2D_RuntimeSprite : MonoBehaviour
|
||||
{
|
||||
public Sprite Sprite;
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
D2D_Helper.Destroy(Sprite);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4fbb12d5c9357249a0d858dfe2a30ac
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
5
Assets/Reference/Destructible2D/Required/Shaders.meta
Normal file
5
Assets/Reference/Destructible2D/Required/Shaders.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4d054acb729ecc46b348952ed00df4c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
@@ -0,0 +1,90 @@
|
||||
Shader "Sprites/Default (Destructible 2D)"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_MainTex ("Sprite Texture", 2D) = "white" {}
|
||||
_AlphaTex ("Alpha Tex", 2D) = "white" {}
|
||||
_AlphaScale ("Alpha Scale", Vector) = (1,1,0,0)
|
||||
_AlphaOffset ("Alpha Offset", Vector) = (0,0,0,0)
|
||||
_Sharpness ("Sharpness", Float) = 1.0
|
||||
_Color ("Tint", Color) = (1,1,1,1)
|
||||
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags
|
||||
{
|
||||
"Queue"="Transparent"
|
||||
"IgnoreProjector"="True"
|
||||
"RenderType"="Transparent"
|
||||
"PreviewType"="Plane"
|
||||
"CanUseSpriteAtlas"="True"
|
||||
}
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
Fog { Mode Off }
|
||||
Blend One OneMinusSrcAlpha
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex Vert
|
||||
#pragma fragment Frag
|
||||
#pragma multi_compile DUMMY PIXELSNAP_ON
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
sampler2D _MainTex;
|
||||
|
||||
sampler2D _AlphaTex;
|
||||
|
||||
float _Sharpness;
|
||||
|
||||
float4 _Color;
|
||||
|
||||
float2 _AlphaScale;
|
||||
|
||||
float2 _AlphaOffset;
|
||||
|
||||
struct a2v
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
void Vert(a2v i, out v2f o)
|
||||
{
|
||||
o.vertex = mul(UNITY_MATRIX_MVP, i.vertex);
|
||||
o.color = i.color * _Color;
|
||||
o.texcoord = i.texcoord;
|
||||
#if PIXELSNAP_ON
|
||||
o.vertex = UnityPixelSnap(o.vertex);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Frag(v2f i, out float4 o:COLOR0)
|
||||
{
|
||||
float4 mainTex = tex2D(_MainTex, i.texcoord);
|
||||
float4 alphaTex = tex2D(_AlphaTex, (i.texcoord - _AlphaOffset) * _AlphaScale);
|
||||
|
||||
o.rgba = mainTex * i.color;
|
||||
|
||||
o.a *= saturate(0.5f + (alphaTex.a - 0.5f) * _Sharpness);
|
||||
|
||||
o.rgb *= o.a;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c897ee6dc2bb7d40a4ad66067efa576
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
Reference in New Issue
Block a user