添加插件

This commit is contained in:
2025-11-10 00:08:26 +08:00
parent 4059c207c0
commit 76f80db694
2814 changed files with 436400 additions and 178 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c75d46b0788adf543b0088b6f6275521
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,266 @@
using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace FIMSpace.FTail
{
/// <summary>
/// FCr: Part of Tail Animator Skinning Static Meshes API
/// Methods used by skinner to create skinned mesh renderers from static meshes
/// </summary>
public static class FTail_Skinning
{
/// <summary>
/// Calculating base vertices datas for provided bones setup
/// </summary>
/// <param name="baseMesh"> Mesh to be weighted </param>
/// <param name="bonesCoords"> Required local positions and rotations for bones </param>
/// <param name="spreadOffset"> Origin weighting offset which can be helpful in some cases, it can be Vector3.zero in most cases </param>
/// <param name="weightBoneLimit"> To how many bones vertex can be weighted to create smooth weight effect </param>
/// <param name="spreadValue"> Smoothing weights on the edges of bones if lower then more smooth but don't oversmooth it </param>
/// <param name="spreadPower"> Making smoothing more sharp on edges </param>
public static FTail_SkinningVertexData[] CalculateVertexWeightingData(Mesh baseMesh, Transform[] bonesCoords, Vector3 spreadOffset, int weightBoneLimit = 2, float spreadValue = 0.8f, float spreadPower = 0.185f)
{
Vector3[] pos = new Vector3[bonesCoords.Length];
Quaternion[] rot = new Quaternion[bonesCoords.Length];
//for (int i = 0; i < bonesCoords.Length; i++)
//{
// pos[i] = bonesCoords[i].position;
// rot[i] = bonesCoords[i].rotation;
//}
// We must reset bones structure to identity space
for (int i = 0; i < bonesCoords.Length; i++)
{
// Transforming from world to local space coords
pos[i] = bonesCoords[0].parent.InverseTransformPoint(bonesCoords[i].position);
rot[i] = FEngineering.QToLocal(bonesCoords[0].parent.rotation, bonesCoords[i].rotation);
}
return CalculateVertexWeightingData(baseMesh, pos, rot, spreadOffset, weightBoneLimit, spreadValue, spreadPower);
}
/// <summary>
/// Calculating base vertices datas for provided bones setup
/// </summary>
/// <param name="baseMesh"> Mesh to be weighted </param>
/// <param name="bonesPos"> Mesh local space positions for bones </param>
/// <param name="bonesRot"> Mesh local space rotations for bones </param>
/// <param name="bonesCoords"> Required local positions and rotations for bones </param>
/// <param name="spreadOffset"> Origin weighting offset which can be helpful in some cases, it can be Vector3.zero in most cases </param>
/// <param name="weightBoneLimit"> To how many bones vertex can be weighted to create smooth weight effect </param>
/// <param name="spreadValue"> Smoothing weights on the edges of bones if lower then more smooth but don't oversmooth it </param>
/// <param name="spreadPower"> Making smoothing more sharp on edges </param>
public static FTail_SkinningVertexData[] CalculateVertexWeightingData(Mesh baseMesh, Vector3[] bonesPos, Quaternion[] bonesRot, Vector3 spreadOffset, int weightBoneLimit = 2, float spreadValue = 0.8f, float spreadPower = 0.185f)
{
if (weightBoneLimit < 1) weightBoneLimit = 1;
if (weightBoneLimit > 2) weightBoneLimit = 2; // Limiting for now
#region Editor progress dialogs
#if UNITY_EDITOR
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
#endif
#endregion
int vertCount = baseMesh.vertexCount;
FTail_SkinningVertexData[] vertexDatas = new FTail_SkinningVertexData[vertCount];
// Computing helper segments for weighting bones
Vector3[] boneAreas = new Vector3[bonesPos.Length];
for (int i = 0; i < bonesPos.Length - 1; i++)
{
// Direction vector towards further bone
boneAreas[i] = bonesPos[i + 1] - bonesPos[i]; //bonesCoords[i + 1].localPosition - bonesCoords[i].localPosition;
}
if (boneAreas.Length > 1) boneAreas[boneAreas.Length - 1] = boneAreas[boneAreas.Length - 2];
#if UNITY_EDITOR
try
{
for (int i = 0; i < vertCount; i++)
{
vertexDatas[i] = new FTail_SkinningVertexData(baseMesh.vertices[i]);
vertexDatas[i].CalculateVertexParameters(bonesPos, bonesRot, boneAreas, weightBoneLimit, spreadValue, spreadOffset, spreadPower);
#region Editor progress dialogs
if (!Application.isPlaying)
// Displaying progress bar when iteration takes too much time
if (watch.ElapsedMilliseconds > 1500)
if (i % 10 == 0)
EditorUtility.DisplayProgressBar("Analizing mesh vertices...", "Analizing Vertices (" + i + "/" + vertCount + ")", ((float)i / (float)vertCount));
#endregion
}
#region Editor progress dialogs
if (!Application.isPlaying)
EditorUtility.ClearProgressBar();
#endregion
}
catch (System.Exception exc)
{
Debug.LogError(exc);
#region Editor progress dialogs
if (!Application.isPlaying)
EditorUtility.ClearProgressBar();
#endregion
}
#else
for (int i = 0; i < vertCount; i++)
{
vertexDatas[i] = new FTail_SkinningVertexData(baseMesh.vertices[i]);
vertexDatas[i].CalculateVertexParameters(bonesPos, bonesRot, boneAreas, weightBoneLimit, spreadValue, spreadOffset, spreadPower);
}
#endif
return vertexDatas;
}
/// <summary>
/// Skinning target mesh with helper vertex datas which you can get with CalculateVertexWeightingData() method
/// Using transforms as guidement for bones positions and rotations
/// </summary>
/// <returns> Skinned mesh can't be returned as 'Mesh' type because skinned mesh is mesh + bones transforms etc. </returns>
public static SkinnedMeshRenderer SkinMesh(Mesh baseMesh, Transform skinParent, Transform[] bonesStructure, FTail_SkinningVertexData[] vertData)
{
Vector3[] pos = new Vector3[bonesStructure.Length];
Quaternion[] rot = new Quaternion[bonesStructure.Length];
// We must reset bones structure to identity space
for (int i = 0; i < bonesStructure.Length; i++)
{
// Transforming from world to local space coords
pos[i] = skinParent.InverseTransformPoint(bonesStructure[i].position);
rot[i] = FEngineering.QToLocal(skinParent.rotation, bonesStructure[i].rotation);
}
SkinnedMeshRenderer skin = SkinMesh(baseMesh, pos, rot, vertData);
return skin;
}
/// <summary>
/// Skinning target mesh with helper vertex datas which you can
/// </summary>
/// <param name="baseMesh"> Base static mesh to be skinned </param>
/// <param name="bonesPositions"> Bones positions in mesh local space </param>
/// <param name="bonesRotations"> Bones rotations in mesh local space </param>
/// <param name="vertData"> Get it with CalculateVertexWeightingData() method </param>
/// <returns> Skinned mesh can't be returned as 'Mesh' type because skinned mesh is mesh + bones transforms etc. </returns>
public static SkinnedMeshRenderer SkinMesh(Mesh baseMesh, Vector3[] bonesPositions, Quaternion[] bonesRotations, FTail_SkinningVertexData[] vertData)
{
if (bonesPositions == null) return null;
if (bonesRotations == null) return null;
if (baseMesh == null) return null;
if (vertData == null) return null;
// Creating copy of target mesh and refreshing it
Mesh newMesh = GameObject.Instantiate(baseMesh);
newMesh.name = baseMesh.name + " [FSKINNED]";
// Preparing new object which will have skinned mesh renderer with new mesh and bones in it
GameObject newSkinObject = new GameObject(baseMesh.name + " [FSKINNED]");
Transform newParent = newSkinObject.transform;
// Preparing skin
SkinnedMeshRenderer skin = newParent.gameObject.AddComponent<SkinnedMeshRenderer>();
// Preparing bones for weighting
Transform[] bones = new Transform[bonesPositions.Length];
Matrix4x4[] bindPoses = new Matrix4x4[bonesPositions.Length];
string nameString;
if (baseMesh.name.Length < 6) nameString = baseMesh.name; else nameString = baseMesh.name.Substring(0, 5);
for (int i = 0; i < bonesPositions.Length; i++)
{
bones[i] = new GameObject("BoneF-" + nameString + "[" + i + "]").transform;
if (i == 0) bones[i].SetParent(newParent, true); else bones[i].SetParent(bones[i - 1], true);
bones[i].transform.position = bonesPositions[i];
bones[i].transform.rotation = bonesRotations[i];
bindPoses[i] = bones[i].worldToLocalMatrix * newParent.localToWorldMatrix;
}
BoneWeight[] weights = new BoneWeight[newMesh.vertexCount];
for (int v = 0; v < weights.Length; v++) weights[v] = new BoneWeight();
// Calculating and applying weights for verices
for (int i = 0; i < vertData.Length; i++)
{
for (int w = 0; w < vertData[i].weights.Length; w++)
{
weights[i] = SetWeightIndex(weights[i], w, vertData[i].bonesIndexes[w]);
weights[i] = SetWeightToBone(weights[i], w, vertData[i].weights[w]);
}
}
newMesh.bindposes = bindPoses;
newMesh.boneWeights = weights;
List<Vector3> normals = new List<Vector3>();
List < Vector4> tangents = new List<Vector4>();
baseMesh.GetNormals(normals);
baseMesh.GetTangents(tangents);
newMesh.SetNormals(normals);
newMesh.SetTangents(tangents);
newMesh.bounds = baseMesh.bounds;
// Applying generated mesh to skin controller
skin.sharedMesh = newMesh;
skin.rootBone = bones[0];
skin.bones = bones;
return skin;
}
/// <summary>
/// Method which is setting certain weight variable from BoneWeight struct
/// </summary>
public static BoneWeight SetWeightIndex(BoneWeight weight, int bone = 0, int index = 0)
{
switch (bone)
{
case 1: weight.boneIndex1 = index; break;
case 2: weight.boneIndex2 = index; break;
case 3: weight.boneIndex3 = index; break;
default: weight.boneIndex0 = index; break;
}
return weight;
}
/// <summary>
/// Method which is setting certain weight variable from BoneWeight struct
/// </summary>
public static BoneWeight SetWeightToBone(BoneWeight weight, int bone = 0, float value = 1f)
{
switch (bone)
{
case 1: weight.weight1 = value; break;
case 2: weight.weight2 = value; break;
case 3: weight.weight3 = value; break;
default: weight.weight0 = value; break;
}
return weight;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6dbb5fa7a5ec9084ba3564faf70c8899
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: f1f543bbb761e234cbb384a09c3482cc, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,231 @@
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FTail
{
/// <summary>
/// FCr: Part of Tail Animator Skinning Static Meshes API
/// Simple helper class to store vertices parameters in reference to bones
/// </summary>
[System.Serializable]
public class FTail_SkinningVertexData
{
public Vector3 position;
//public Transform[] bones;
/// <summary> Indexes for helpers in visualization </summary>
public int[] bonesIndexes;
public int allMeshBonesCount;
// Assigned during custom weight calculations
public float[] weights;
public FTail_SkinningVertexData(Vector3 pos) { position = pos; }
/// <summary>
/// Distance to bone area for weighting
/// </summary>
public float DistanceToLine(Vector3 pos, Vector3 lineStart, Vector3 lineEnd)
{
Vector3 dirVector1 = pos - lineStart;
Vector3 dirVector2 = (lineEnd - lineStart).normalized;
float distance = Vector3.Distance(lineStart, lineEnd);
float dot = Vector3.Dot(dirVector2, dirVector1);
if (dot <= 0) return Vector3.Distance(pos, lineStart);
if (dot >= distance) return Vector3.Distance(pos, lineEnd);
Vector3 dotVector = dirVector2 * dot;
Vector3 closestPoint = lineStart + dotVector;
return Vector3.Distance(pos, closestPoint);
}
/// <summary>
/// Calculating vertex's distances to 4 nearest bones (4 bone weights is maximum count in Unity)
/// for further custom weight calculations
/// </summary>
public void CalculateVertexParameters(Vector3[] bonesPos, Quaternion[] bonesRot, Vector3[] boneAreas, int maxWeightedBones, float spread, Vector3 spreadOffset, float spreadPower = 1f)
{
allMeshBonesCount = bonesPos.Length;
// Using Vector2 for simple two float values in one variable, x = bone index y = distance of vertex to this bone, later we will sort list using distances
List<Vector2> calculatedDistances = new List<Vector2>();
// Check later if we don't need to transpone points to model space scale
for (int i = 0; i < bonesPos.Length; i++)
{
Vector3 boneEnd;
if (i != bonesPos.Length - 1)
boneEnd = Vector3.Lerp(bonesPos[i], bonesPos[i + 1], 0.9f);
else
boneEnd = Vector3.Lerp(bonesPos[i], bonesPos[i] + (bonesPos[i] - bonesPos[i - 1]), 0.9f);
boneEnd += bonesRot[i] * spreadOffset;
float distance = DistanceToLine(position, bonesPos[i], boneEnd);
// Making bone offset to behave like bone area
calculatedDistances.Add(new Vector2(i, distance));
}
// Sorting by nearest all bones
calculatedDistances.Sort((a, b) => a.y.CompareTo(b.y));
// Limiting vertex weight up to 4 bones
int maxBones = (int)Mathf.Min(maxWeightedBones, bonesPos.Length);
// Assigning max 4 nearest bones and their distances to this vertex
bonesIndexes = new int[maxBones];
float[] nearestDistances = new float[maxBones];
for (int i = 0; i < maxBones; i++)
{
bonesIndexes[i] = (int)calculatedDistances[i].x;
nearestDistances[i] = calculatedDistances[i].y;
}
// Basing on spread value we spreading weight to nearest bones
// Calculating percentage distances to bones
float[] boneWeightsForVertex = new float[maxBones];
AutoSetBoneWeights(boneWeightsForVertex, nearestDistances, spread, spreadPower, boneAreas);
float weightLeft = 1f; // Must amount of weight which needs to be assigned
weights = new float[maxBones]; // New weight parameters
// Applying weights to each bone assigned to vertex
for (int i = 0; i < maxBones; i++)
{
if (spread == 0) if (i > 0) break;
if (weightLeft <= 0f) // No more weight to apply
{
weights[i] = 0f;
continue;
}
float targetWeight = boneWeightsForVertex[i];
weightLeft -= targetWeight;
if (weightLeft <= 0f) targetWeight += weightLeft; else { if (i == maxBones - 1) targetWeight += weightLeft; } // Using weight amount which is left to assign
weights[i] = targetWeight;
}
}
public float[] debugDists;
public float[] debugDistWeights;
public float[] debugWeights;
/// <summary>
/// Spreading weights over bones for current vertex
/// </summary>
public void AutoSetBoneWeights(float[] weightForBone, float[] distToBone, float spread, float spreadPower, Vector3[] boneAreas)
{
int bonesC = weightForBone.Length;
float[] boneLengths = new float[bonesC]; for (int i = 0; i < boneLengths.Length; i++) boneLengths[i] = boneAreas[i].magnitude;
float[] normalizedDistanceWeights = new float[bonesC];
for (int i = 0; i < weightForBone.Length; i++) weightForBone[i] = 0f;
float normalizeDistance = 0f;
for (int i = 0; i < bonesC; i++) normalizeDistance += distToBone[i];
for (int i = 0; i < bonesC; i++) normalizedDistanceWeights[i] = 1f - distToBone[i] / normalizeDistance; // Reversing weight power - nearest (smallest distance) must have biggest weight value
debugDists = distToBone;
if (bonesC == 1 || spread == 0f) // Simpliest ONE BONE -------------------------------------------------------------
{
// [0] - nearest bone
weightForBone[0] = 1f; // Just one weight - spread does not change anything
}
else if (bonesC == 2) // Simple TWO BONES -------------------------------------------------------------
{
float normalizer = 1f;
weightForBone[0] = 1f;
// distToBone[0] is zero, max spread distance is length of bone / 3
float distRange = Mathf.InverseLerp(distToBone[0] + (boneLengths[0] / 1.25f) * spread, distToBone[0], distToBone[1]);
debugDists[0] = distRange;
// 0 -> full nearest bone weight
// 1 -> half nearest half second bone weight
float value = DistributionIn(Mathf.Lerp(0f, 1f, distRange), Mathf.Lerp(1.5f, 16f, spreadPower));
weightForBone[1] = value;
normalizer += value;
debugDistWeights = new float[weightForBone.Length];
weightForBone.CopyTo(debugDistWeights, 0);
for (int i = 0; i < bonesC; i++) weightForBone[i] /= normalizer;
debugWeights = weightForBone;
}
else // Complex > TWO BONES -------------------------------------------------------------
{
float reffVal = boneLengths[0] / 10f;
float refLength = boneLengths[0] / 2f;
float normalizer = 0f;
for (int i = 0; i < bonesC; i++)
{
float weight = Mathf.InverseLerp(0f, reffVal + refLength * (spread), distToBone[i]);
float value = Mathf.Lerp(1f, 0f, weight);
if (i == 0) if (value == 0f) value = 1f;
weightForBone[i] = value;
normalizer += value;
}
debugDistWeights = new float[weightForBone.Length];
weightForBone.CopyTo(debugDistWeights, 0);
for (int i = 0; i < bonesC; i++) weightForBone[i] /= normalizer;
debugWeights = weightForBone;
}
}
/// <summary>
/// Easing weight distribution
/// </summary>
public static float DistributionIn(float k, float power)
{ return Mathf.Pow(k, power + 1f); }
/// <summary>
/// Returning helper color for bone
/// </summary>
public static Color GetBoneIndicatorColor(int boneIndex, int bonesCount, float s = 0.9f, float v = 0.9f)
{
float h = ((float)(boneIndex) * 1.125f) / bonesCount;
h += 0.125f * boneIndex;
h += 0.3f;
h %= 1f;
return Color.HSVToRGB(h, s, v);
}
/// <summary>
/// Returns average color value for weight idicator for this vertex
/// </summary>
public Color GetWeightColor()
{
Color lerped = GetBoneIndicatorColor(bonesIndexes[0], allMeshBonesCount, 1f, 1f);
for (int i = 1; i < bonesIndexes.Length; i++)
lerped = Color.Lerp(lerped, GetBoneIndicatorColor(bonesIndexes[i], allMeshBonesCount, 1f, 1f), weights[i]);
return lerped;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2595a070e1f36f49862752d4a32580f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: f1f543bbb761e234cbb384a09c3482cc, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,454 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace FIMSpace.FTail
{
/// <summary>
/// FCr: Implementation of Tail Animator Skinning Static Meshes API
/// Class to use only in editor, it creates bones with preview static mesh then skin it to skinned mesh renderer
/// </summary>
[ExecuteInEditMode]
[AddComponentMenu("FImpossible Creations/Tail Animator Utilities/Editor Tail Skinner")]
public class FTail_Editor_Skinner : MonoBehaviour
{
#region Inspector Variables
[FPD_Header("SKIN STATIC MESHES INSIDE UNITY", 3, 8, 8)]
[BackgroundColor(0.75f, 0.75f, 1.0f, 0.7f)]
public int AutoMarkersCount = 8;
public float DistanceValue = 0.3f;
public Vector3 positionOffset = new Vector3(0, 0f);
public Vector2 startDirection = new Vector2(-90, 0f);
public Vector2 rotationOffset = new Vector2(0f, 0f);
[Range(0f, 5f)]
public float HelpScaleValue = 1f;
[BackgroundColor(0.85f, 0.85f, 1.0f, 0.85f)]
public AnimationCurve DistancesFaloff = AnimationCurve.Linear(0f, 1f, 1f, 1f);
public AnimationCurve RotationsFaloff = AnimationCurve.Linear(0f, 1f, 1f, 1f);
[BackgroundColor(0.5f, 1f, 0.5f, 0.8f)]
[Space(10f, order = 0)]
[Header("Left empty if you don't use custom markers", order = 1)]
[Space(-7f, order = 2)]
[Header("Moving custom markers will not trigger realtime update", order = 3)]
public Transform[] CustomBoneMarkers;
[Space(7f, order = 0)]
[FPD_Header("Weights Spread Settings", 7, 4, 4)]
[Space(3f, order = 2)]
[Range(0f, 1f)]
public float SpreadValue = 0.8f;
[Range(0f, 1f)]
public float SpreadPower = .185f;
[Tooltip("Offsetting spreading area, For example 0,0,1 and recommended values from 0 to 2 not bigger")]
public Vector3 SpreadOffset = Vector3.zero;
[Range(1, 2)]
public int LimitBoneWeightCount = 2;
[BackgroundColor(0.4f, 0.8f, 0.8f, 0.8f)]
[FPD_Header("Additional Variables", 7, 4, 4)]
[Range(0f, 5f)]
public float GizmoSize = 0.1f;
[Range(0f, 1f)]
public float GizmoAlpha = .65f;
[BackgroundColor()]
[Tooltip("If your model have many vertices, turn it only when neccesary")]
public bool RealtimeUpdate = true;
public bool ShowPreview = true;
public bool DebugMode = false;
#endregion
#region Private variables
/// <summary> Base Mesh </summary>
private Mesh baseMesh;
[HideInInspector]
public List<Color32> baseVertexColor;
private MeshRenderer meshRenderer;
/// <summary> Fake bones list before creating true skeleton for mesh </summary>
private Transform[] ghostBones;
/// <summary> Vertex datas used for setting weights precisely</summary>
private FTail_SkinningVertexData[] vertexDatas;
/// <summary> Generated marker points for automatic bone points </summary>
internal Transform[] autoMarkers;
// Hide in inspector because when variables are private, they're resetted to null every time code compiles
/// <summary> Because we can't destroy gameObjects in OnValidate, we do something similar to object pools </summary>
[HideInInspector]
public List<Transform> allMarkersTransforms = new List<Transform>();
/// <summary> Transform with components helping drawing how weights are spread on model </summary>
[HideInInspector]
public Transform weightPreviewTransform;
[HideInInspector]
public bool popupShown = false;
internal bool initValues = false;
#endregion
/// <summary>
/// When something changes in inspector, let's recalculate parameters
/// </summary>
private void OnValidate()
{
if (!initValues)
{
MeshRenderer m = GetComponent<MeshRenderer>();
if (m) DistanceValue = m.bounds.extents.magnitude / 7f;
initValues = true;
}
if (AutoMarkersCount < 2) AutoMarkersCount = 2;
if (!GetBaseMesh()) return;
if (CustomBoneMarkers == null) CustomBoneMarkers = new Transform[0]; // Prevent error log when adding component
// Use only custom markers if they're assigned
if (CustomBoneMarkers.Length > 0)
ghostBones = CustomBoneMarkers;
else // Use auto markers
{
CalculateAutoMarkers();
ghostBones = autoMarkers;
}
if (RealtimeUpdate)
{
vertexDatas = FTail_Skinning.CalculateVertexWeightingData(
GetBaseMesh(), ghostBones, SpreadOffset, LimitBoneWeightCount, SpreadValue, SpreadPower);
UpdatePreviewMesh();
}
}
/// <summary>
/// Drawing helper stuff
/// </summary>
private void OnDrawGizmos()
{
if (CustomBoneMarkers == null)
CustomBoneMarkers = new Transform[0];
if (ghostBones[0] == null)
CalculateAutoMarkers();
if (CustomBoneMarkers.Length < 1)
DrawMarkers(autoMarkers);
else
DrawMarkers(CustomBoneMarkers);
}
/// <summary>
/// Calculating auto markers transforms
/// </summary>
private void CalculateAutoMarkers()
{
#region Creation of markers' transforms
if (autoMarkers == null) autoMarkers = new Transform[0];
if (allMarkersTransforms.Count < AutoMarkersCount)
{
for (int i = autoMarkers.Length; i < AutoMarkersCount; i++)
{
GameObject newMarker = new GameObject(name + "-SkinMarker " + i);
newMarker.transform.SetParent(transform, true);
allMarkersTransforms.Add(newMarker.transform);
}
}
if (autoMarkers.Length != AutoMarkersCount)
{
autoMarkers = new Transform[AutoMarkersCount];
for (int i = 0; i < AutoMarkersCount; i++)
{
autoMarkers[i] = allMarkersTransforms[i];
}
}
#endregion
autoMarkers[0].position = transform.position + positionOffset;
autoMarkers[0].rotation = Quaternion.Euler(startDirection + rotationOffset);
float step = 1f / (float)AutoMarkersCount;
for (int i = 1; i < AutoMarkersCount; i++)
{
float forwardMultiplier = DistanceValue;
forwardMultiplier *= DistancesFaloff.Evaluate(i * step);
forwardMultiplier *= HelpScaleValue;
Vector3 targetPosition = autoMarkers[i - 1].position + autoMarkers[i - 1].rotation * Vector3.forward * forwardMultiplier;
Vector3 newRot = startDirection + rotationOffset * (i + 1) * RotationsFaloff.Evaluate(i * step);
autoMarkers[i].position = targetPosition;
autoMarkers[i].rotation = Quaternion.Euler(newRot);
}
}
/// <summary>
/// Getting base mesh variable, depends if it's skinned mesh or static mesh
/// </summary>
private Mesh GetBaseMesh()
{
if (baseMesh == null)
{
meshRenderer = GetComponent<MeshRenderer>();
MeshFilter meshFilter = GetComponent<MeshFilter>();
if (meshFilter) baseMesh = meshFilter.sharedMesh;
}
else return baseMesh;
if (!baseMesh)
{
if (!popupShown)
{
EditorUtility.DisplayDialog("Tail Skinner Error", "[Tail Skinner] No base mesh! (mesh filter and mesh renderer)", "Ok");
popupShown = true;
}
Debug.LogError("No BaseMesh!");
}
if (baseMesh)
{
if (baseVertexColor == null) baseVertexColor = new List<Color32>();
if (baseVertexColor.Count != baseMesh.vertexCount)
{
baseVertexColor.Clear();
baseMesh.GetColors(baseVertexColor);
}
}
return baseMesh;
}
/// <summary>
/// Skinning mesh to new skinned mesh renderer with choosed weight markers settings
/// </summary>
public void SkinMesh(bool addTailAnimator, Vector3 newObjectOffset)
{
// Remembering data and preparing objects with components
List<Color32> baseVertColors = new List<Color32>();
GetBaseMesh().GetColors(baseVertColors);
// Doing skinning
vertexDatas = FTail_Skinning.CalculateVertexWeightingData(
GetBaseMesh(), ghostBones, SpreadOffset, LimitBoneWeightCount, SpreadValue, SpreadPower);
SkinnedMeshRenderer newSkinnedMesh = FTail_Skinning.SkinMesh(baseMesh, transform, ghostBones, vertexDatas);
if (newSkinnedMesh == null)
{ Debug.LogError("[Tail Animator Skinning] Creating skinned mesh failed!"); return; }
// Skin renderer quality
switch (LimitBoneWeightCount)
{
case 1: newSkinnedMesh.quality = SkinQuality.Bone1; break;
case 2: newSkinnedMesh.quality = SkinQuality.Bone2; break;
case 4: newSkinnedMesh.quality = SkinQuality.Bone4; break;
default: newSkinnedMesh.quality = SkinQuality.Auto; break;
}
// Filling new mesh with materials
MeshRenderer meshRend = GetComponent<MeshRenderer>();
if (meshRend)
{
newSkinnedMesh.materials = meshRend.sharedMaterials;
newSkinnedMesh.sharedMaterials = meshRend.sharedMaterials;
}
// Adding tail animator
if (addTailAnimator)
{
TailAnimator2 t = newSkinnedMesh.bones[0].gameObject.AddComponent<TailAnimator2>();
t.StartBone = newSkinnedMesh.bones[0];
t.EndBone = newSkinnedMesh.bones[newSkinnedMesh.bones.Length - 1];
}
// Setting new object position to be next to current model
newSkinnedMesh.transform.position = transform.position + new Vector3(1f, 1f, 1f);
// Create asset for new model so it not disappear when we create prefab from this gameObject
string newMeshPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(baseMesh));
AssetDatabase.CreateAsset(newSkinnedMesh.sharedMesh, newMeshPath + "/" + newSkinnedMesh.name + ".mesh");
AssetDatabase.SaveAssets();
Debug.Log("New skinned mesh '" + newSkinnedMesh.name + ".mesh" + "' saved under path: '" + newMeshPath + "'");
}
/// <summary>
/// Make sure everything which was created by this script is destroyed
/// </summary>
private void OnDestroy()
{
for (int i = 0; i < allMarkersTransforms.Count; i++) if (allMarkersTransforms[i] != null) DestroyImmediate(allMarkersTransforms[i].gameObject);
if (weightPreviewTransform != null) DestroyImmediate(weightPreviewTransform.gameObject);
if (baseMesh == null) return;
meshRenderer.enabled = true;
}
/// <summary>
/// Drawing markers to be visible in editor window to help place bones correctly
/// </summary>
public void DrawMarkers(Transform[] markers)
{
if (markers == null) return;
for (int i = 0; i < markers.Length; i++)
{
Gizmos.color = ChangeColorAlpha(GetBoneIndicatorColor(i, markers.Length), GizmoAlpha);
Vector3 targetPosition = markers[i].position;
Gizmos.DrawWireSphere(targetPosition, GizmoSize);
Gizmos.color = ChangeColorAlpha(GetBoneIndicatorColor(i, markers.Length, 1f, 1f), GizmoAlpha * 0.8f);
Gizmos.DrawSphere(targetPosition, GizmoSize * 0.7f);
Gizmos.DrawRay(targetPosition, markers[i].up * GizmoSize * 1.1f);
Gizmos.DrawRay(targetPosition, -markers[i].up * GizmoSize * 1.1f);
Gizmos.DrawRay(targetPosition, markers[i].right * GizmoSize * 1.1f);
Gizmos.DrawRay(targetPosition, -markers[i].right * GizmoSize * 1.1f);
Vector3 targetPoint;
if (i < markers.Length - 1) targetPoint = markers[i + 1].position;
else
targetPoint = markers[i].position + (markers[i].position - markers[i - 1].position);
Gizmos.DrawLine(targetPosition + markers[i].up * GizmoSize * 1.1f, targetPoint);
Gizmos.DrawLine(targetPosition - markers[i].up * GizmoSize * 1.1f, targetPoint);
Gizmos.DrawLine(targetPosition + markers[i].right * GizmoSize * 1.1f, targetPoint);
Gizmos.DrawLine(targetPosition - markers[i].right * GizmoSize * 1.1f, targetPoint);
}
}
/// <summary>
/// Updating preview mesh to view weights correctly
/// </summary>
private void UpdatePreviewMesh()
{
#region Creation of new preview mesh when needed
if (weightPreviewTransform == null)
{
weightPreviewTransform = new GameObject(name + "[preview mesh]").transform;
weightPreviewTransform.SetParent(transform);
weightPreviewTransform.localPosition = Vector3.zero;
weightPreviewTransform.localRotation = Quaternion.identity;
weightPreviewTransform.localScale = Vector3.one;
weightPreviewTransform.gameObject.AddComponent<MeshFilter>().mesh = baseMesh;
Material[] newMaterials = new Material[meshRenderer.sharedMaterials.Length];
for (int i = 0; i < newMaterials.Length; i++) newMaterials[i] = new Material(Shader.Find("Particles/FVertexLit Blended"));
weightPreviewTransform.gameObject.AddComponent<MeshRenderer>().materials = newMaterials;
}
#endregion
if (ShowPreview)
{
meshRenderer.enabled = false;
weightPreviewTransform.gameObject.SetActive(true);
List<Color> vColors = new List<Color>();
for (int i = 0; i < vertexDatas.Length; i++) vColors.Add(vertexDatas[i].GetWeightColor());
baseMesh.SetColors(vColors);
weightPreviewTransform.gameObject.GetComponent<MeshFilter>().mesh = baseMesh;
}
else
{
meshRenderer.enabled = true;
if (baseVertexColor != null) if (baseMesh) if (baseMesh.vertexCount == baseVertexColor.Count) baseMesh.SetColors(baseVertexColor);
weightPreviewTransform.gameObject.SetActive(false);
}
}
private void OnDrawGizmosSelected()
{
if (!DebugMode) return;
if (vertexDatas == null) return;
if (vertexDatas.Length == 0) return;
if (vertexDatas[0].bonesIndexes == null) return;
for (int i = 0; i < vertexDatas.Length; i++)
{
Handles.color = GetBoneIndicatorColor(vertexDatas[i].bonesIndexes[0], vertexDatas[i].bonesIndexes.Length) * new Color(1f, 1f, 1f, GizmoAlpha);
Gizmos.color = Handles.color;
Handles.Label(transform.TransformPoint(vertexDatas[i].position), "[" + i + "]");
//Handles.Label(transform.TransformPoint(vertexDatas[i].position), "[" + i + "]\n" + Math.Round(vertexDatas[i].debugDists[0], 3) + "\n" + Math.Round(vertexDatas[i].debugDists[1], 3));
Gizmos.DrawSphere(transform.TransformPoint(vertexDatas[i].position), 0.125f * GizmoSize);
}
}
/// <summary>
/// Returning helper color for bone
/// </summary>
public static Color GetBoneIndicatorColor(int boneIndex, int bonesCount, float s = 0.9f, float v = 0.9f)
{
float h = ((float)(boneIndex) * 1.125f) / bonesCount;
h += 0.125f * boneIndex;
h += 0.3f;
h %= 1f;
return Color.HSVToRGB(h, s, v);
}
public static Color ChangeColorAlpha(Color color, float alpha)
{ return new Color(color.r, color.g, color.b, alpha); }
}
/// <summary>
/// FM: Editor class component to enchance controll over component from inspector window
/// </summary>
[UnityEditor.CustomEditor(typeof(FTail_Editor_Skinner))]
public class FTail_Editor_SkinnerEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
FTail_Editor_Skinner targetScript = (FTail_Editor_Skinner)target;
DrawDefaultInspector();
GUILayout.Space(10f);
if (GUILayout.Button("Skin It")) targetScript.SkinMesh(false, Vector3.right);
if (GUILayout.Button("Skin and add Tail Animator")) targetScript.SkinMesh(true, Vector3.right);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 64150bf558082b8468d3f05b3e9d3b5a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 7958d01c62579034fa1ffbb911dd6b59, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,301 @@
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.FTail
{
/// <summary>
/// FC: Experimental class under developement
/// </summary>
[AddComponentMenu("FImpossible Creations/Tail Animator Utilities/Tail Animator Wind")]
public class TailAnimatorWind : MonoBehaviour, UnityEngine.EventSystems.IDropHandler, IFHierarchyIcon
{
#region Hierarchy Icon
public string EditorIconPath { get { return "Tail Animator/TailAnimatorWindIconSmall"; } }
public void OnDrop(UnityEngine.EventSystems.PointerEventData data) { }
#endregion
#region Singleton
public static TailAnimatorWind Instance { get; private set; } // { get { if (!_instance) GenerateWindComponentInstance(); return _instance; } }
//private static TailAnimatorWind _instance;
private void Awake()
{
if (!Application.isPlaying) return;
Instance = this;
if (persistThroughAllScenes) DontDestroyOnLoad(gameObject);
}
public void OnValidate()
{
Instance = this;
}
//private static void GenerateWindComponentInstance()
//{
// GameObject windObj = new GameObject("Tail Animator Wind");
// _instance = windObj.AddComponent<TailAnimatorWind>();
//}
/// <summary>
/// Generating wind component if needed and adding tail animator component to wind affected components list
/// </summary>
//public static void Refresh(TailAnimator2 tail)
//{
// if (!_instance) GenerateWindComponentInstance();
// if (_instance.WindAffected == null) _instance.WindAffected = new List<TailAnimator2>();
// if (tail != null) if (!_instance.WindAffected.Contains(tail)) _instance.WindAffected.Add(tail);
//}
#endregion
[Header("In playmode you will find this object in DontDestroyOnLoad")]
[FPD_Header("Main Wind Setings", 2, 4)]
public float power = 1f;
public float additionalTurbulence = 1f;
public float additionalTurbSpeed = 1f;
[Space(7)]
public WindZone SyncWithUnityWindZone;
public float UnityWindZonePowerMul = 2f;
public float UnityWindZoneTurbMul = 1f;
[Header("Overriding wind if value below different than 0,0,0")]
public Vector3 overrideWind = Vector3.zero;
[FPD_Header("Procedural Wind Settings (if not syncing and not overriding)", 6, 4)]
[Range(0.1f, 1f)]
public float rapidness = 0.95f;
[FPD_Suffix(0, 360, FPD_SuffixAttribute.SuffixMode.FromMinToMaxRounded, "°")]
public float changesPower = 90f;
[Header("Extra")]
[Range(0f, 10f)] public float turbulenceSpeed = 1f;
[FPD_Header("World Position Turbulence", 6, 4)]
[Tooltip("Increase to make objects next to each other wave in slightly different way")]
public float worldTurb = 1f;
[Tooltip("If higher no performance cost, it is just a number")]
public float worldTurbScale = 512;
public float worldTurbSpeed = 5f;
[FPD_Header("Tail Compoenents Related", 6, 4)]
[Tooltip("When tail is longer then power of wind should be higher")]
public bool powerDependOnTailLength = true;
[Tooltip("Don't destroy on load")]
public bool persistThroughAllScenes = false;
//[Tooltip("Finding all TailAnimato2 compoents at start")]
//public bool collectFromSceneAtStart = false;
//public List<TailAnimator2> WindAffected;
private Vector3 targetWind = Vector3.zero;
private Vector3 smoothWind = Vector3.zero;
private Vector3 windVeloHelper = Vector3.zero;
private Quaternion windOrientation = Quaternion.identity;
private Quaternion smoothWindOrient = Quaternion.identity;
private Quaternion smoothWindOrientHelper = Quaternion.identity;
private float[] randNumbers;
private float[] randTimes;
private float[] randSpeeds;
private int frameOffset = 2;
void Update()
{
if (frameOffset > 0) { frameOffset--; return; }
//if (collectFromSceneAtStart)
//{
// collectFromSceneAtStart = false;
// GetTailAnimatorsFromScene();
//}
ComputeWind();
#region Hidden backup
//TailAnimator2 t;
//for (int i = 0; i < WindAffected.Count; i++)
//{
// t = WindAffected[i];
// if (!t.UseWind) continue;
// if (t.WindEffectPower <= 0f) continue;
// if (t.TailSegments.Count <= 0) continue;
// float lengthRatio = 1f;
// if (powerDependOnTailLength)
// {
// lengthRatio = (t._TC_TailLength * t.TailSegments[0].transform.lossyScale.z) / 5f;
// if (t.TailSegments.Count > 3) lengthRatio *= Mathf.Lerp(0.7f, 3f, t.TailSegments.Count / 14f);
// }
// if (t.WindWorldNoisePower > 0f)
// {
// float wTurb = worldTurbSpeed;
// if (SyncWithUnityWindZone) wTurb *= SyncWithUnityWindZone.windTurbulence * UnityWindZoneTurbMul;
// float worldPosTurbulence = (.5f + Mathf.Sin(Time.time * wTurb + t.TailSegments[0].ProceduralPosition.x * worldTurbScale) / 2f) + (.5f + Mathf.Cos(Time.time * wTurb + t.TailSegments[0].ProceduralPosition.z * worldTurbScale) / 2f);
// lengthRatio += worldPosTurbulence * worldTurb * t.WindWorldNoisePower;
// }
// lengthRatio *= t.WindEffectPower;
// if (t.WindTurbulencePower > 0f)
// t.WindEffect = new Vector3(targetWind.x * lengthRatio + finalAddTurbulence.x * t.WindTurbulencePower, targetWind.y * lengthRatio + finalAddTurbulence.y * t.WindTurbulencePower, targetWind.z * lengthRatio + finalAddTurbulence.z * t.WindTurbulencePower);
// else
// t.WindEffect = new Vector3(targetWind.x * lengthRatio, targetWind.y * lengthRatio, targetWind.z * lengthRatio);
//}
#endregion
}
public static void Refresh()
{
if (Instance == null)
{
UnityEngine.Debug.Log("[Tail Animator Wind] No Tail Animator Wind component on the scene!");
UnityEngine.Debug.LogWarning("[Tail Animator Wind] No Tail Animator Wind component on the scene!");
}
}
public void AffectTailWithWind(TailAnimator2 t)
{
if (!t.UseWind) return;
if (t.WindEffectPower <= 0f) return;
if (t.TailSegments.Count <= 0) return;
float lengthRatio = 1f;
if (powerDependOnTailLength)
{
lengthRatio = (t._TC_TailLength * t.TailSegments[0].transform.lossyScale.z) / 5f;
if (t.TailSegments.Count > 3) lengthRatio *= Mathf.Lerp(0.7f, 3f, t.TailSegments.Count / 14f);
}
if (t.WindWorldNoisePower > 0f)
{
float wTurb = worldTurbSpeed;
if (SyncWithUnityWindZone) wTurb *= SyncWithUnityWindZone.windTurbulence * UnityWindZoneTurbMul;
float worldPosTurbulence = (.5f + Mathf.Sin(Time.time * wTurb + t.TailSegments[0].ProceduralPosition.x * worldTurbScale) / 2f) + (.5f + Mathf.Cos(Time.time * wTurb + t.TailSegments[0].ProceduralPosition.z * worldTurbScale) / 2f);
lengthRatio += worldPosTurbulence * worldTurb * t.WindWorldNoisePower;
}
lengthRatio *= t.WindEffectPower;
if (t.WindTurbulencePower > 0f)
t.WindEffect = new Vector3(targetWind.x * lengthRatio + finalAddTurbulence.x * t.WindTurbulencePower, targetWind.y * lengthRatio + finalAddTurbulence.y * t.WindTurbulencePower, targetWind.z * lengthRatio + finalAddTurbulence.z * t.WindTurbulencePower);
else
t.WindEffect = new Vector3(targetWind.x * lengthRatio, targetWind.y * lengthRatio, targetWind.z * lengthRatio);
}
private void Start()
{
int numCount = 10;
randNumbers = new float[numCount];
randTimes = new float[numCount];
randSpeeds = new float[numCount];
for (int i = 0; i < 10; i++)
{
randNumbers[i] = Random.Range(-1000f, 1000f);
randTimes[i] = Random.Range(-1000f, 1000f);
randSpeeds[i] = Random.Range(0.18f, 0.7f);
}
}
void ComputeWind()
{
Vector3 newWind;
if (SyncWithUnityWindZone)
{
newWind = SyncWithUnityWindZone.transform.forward * SyncWithUnityWindZone.windMain * UnityWindZonePowerMul;
transform.rotation = SyncWithUnityWindZone.transform.rotation;
}
else
{
if (overrideWind != Vector3.zero) newWind = overrideWind;
else // Procedural wind
{
for (int i = 0; i < 4; i++)
randTimes[i] += Time.deltaTime * randSpeeds[i] * turbulenceSpeed;
Quaternion windDir = windOrientation;
float x = -1f + Mathf.PerlinNoise(randTimes[0], 256f + randTimes[1]) * 2f;
float y = -1f + Mathf.PerlinNoise(-randTimes[1], 55f + randTimes[2]) * 2f;
float z = -1f + Mathf.PerlinNoise(-randTimes[3], 55f + randTimes[0]) * 2f;
windDir *= Quaternion.Euler(new Vector3(0, y, 0) * changesPower);
windDir = Quaternion.Euler(x * (changesPower / 6f), windDir.eulerAngles.y, z * (changesPower / 6f));
smoothWindOrient = FEngineering.SmoothDampRotation(smoothWindOrient, windDir, ref smoothWindOrientHelper, 1f - rapidness, Time.deltaTime);
transform.rotation = smoothWindOrient;
newWind = smoothWindOrient * Vector3.forward;
}
}
// Additional turbulence
smoothAddTurbulence = Vector3.SmoothDamp(smoothAddTurbulence, GetAddTurbulence() * additionalTurbulence, ref addTurbHelper, 0.05f, Mathf.Infinity, Time.deltaTime);
// Smooth out
smoothWind = Vector3.SmoothDamp(smoothWind, newWind, ref windVeloHelper, 0.1f, Mathf.Infinity, Time.deltaTime);
for (int i = 7; i < 10; i++)
randTimes[i] += Time.deltaTime * randSpeeds[i] * turbulenceSpeed;
float turbulencedPower = power * 0.015f;
turbulencedPower *= 0.5f + Mathf.PerlinNoise(randTimes[7] * 2f, 25 + randTimes[8] * 0.5f);
finalAddTurbulence = smoothAddTurbulence * turbulencedPower;
targetWind = smoothWind * turbulencedPower;
}
Vector3 finalAddTurbulence = Vector3.zero;
Vector3 addTurbHelper = Vector3.zero;
private Vector3 GetAddTurbulence()
{
float turb = additionalTurbSpeed;
if (SyncWithUnityWindZone) turb *= (SyncWithUnityWindZone.windTurbulence * UnityWindZoneTurbMul);
for (int i = 4; i < 7; i++)
randTimes[i] += Time.deltaTime * randSpeeds[i] * turb;
float x = -1f + Mathf.PerlinNoise(randTimes[4] + 7.123f, -2.324f + Time.time * 0.24f) * 2f;
float y = -1f + Mathf.PerlinNoise(randTimes[5] - 4.7523f, -25.324f + Time.time * 0.54f) * 2f;
float z = -1f + Mathf.PerlinNoise(randTimes[6] + 1.123f, -63.324f + Time.time * -0.49f) * 2f;
return new Vector3(x, y, z);
}
Vector3 smoothAddTurbulence = Vector3.zero;
/// <summary>
/// Collecting tail animator components from scene (WARNING: Don't execute it every frame)
/// </summary>
//public void GetTailAnimatorsFromScene()
//{
// TailAnimator2[] tails = FindObjectsOfType<TailAnimator2>();
// for (int i = 0; i < tails.Length; i++)
// {
// if (!WindAffected.Contains(tails[i])) WindAffected.Add(tails[i]);
// }
//}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca32c544193be8047ad129a8e7f52f78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 73e848e8b105ad4419739438d540ff07, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,173 @@
using UnityEngine;
#if UNITY_2019_4_OR_NEWER
using UnityEngine.Tilemaps;
#endif
namespace FIMSpace.FTail
{
/// <summary>
/// FM: Simple class sending collision events to main script
/// </summary>
[AddComponentMenu("FImpossible Creations/Hidden/Tail Collision Helper")]
public class TailCollisionHelper : MonoBehaviour
{
public TailAnimator2 ParentTail;
public Collider TailCollider;
public Collider2D TailCollider2D;
public int Index;
internal Rigidbody RigBody { get; private set; }
internal Rigidbody2D RigBody2D { get; private set; }
Transform previousCollision;
internal TailCollisionHelper Init(bool addRigidbody = true, float mass = 1f, bool kinematic = false)
{
if (TailCollider2D == null)
{
if (addRigidbody)
{
Rigidbody rig = GetComponent<Rigidbody>();
if (!rig) rig = gameObject.AddComponent<Rigidbody>();
rig.interpolation = RigidbodyInterpolation.Interpolate;
rig.useGravity = false;
rig.isKinematic = kinematic;
rig.constraints = RigidbodyConstraints.FreezeAll;
rig.mass = mass;
RigBody = rig;
}
else
{
RigBody = GetComponent<Rigidbody>();
if (RigBody) RigBody.mass = mass;
}
}
else
{
if (addRigidbody)
{
Rigidbody2D rig = GetComponent<Rigidbody2D>();
if (!rig) rig = gameObject.AddComponent<Rigidbody2D>();
rig.interpolation = RigidbodyInterpolation2D.Interpolate;
rig.gravityScale = 0f;
rig.isKinematic = kinematic;
rig.constraints = RigidbodyConstraints2D.FreezeAll;
rig.mass = mass;
RigBody2D = rig;
}
else
{
RigBody2D = GetComponent<Rigidbody2D>();
if (RigBody2D) RigBody2D.mass = mass;
}
}
return this;
}
void OnCollisionEnter(Collision collision)
{
if (ParentTail == null)
{
GameObject.Destroy(this);
return;
}
TailCollisionHelper helper = collision.transform.GetComponent<TailCollisionHelper>();
if (helper)
{
if (ParentTail.CollideWithOtherTails == false) return;
if (helper.ParentTail == ParentTail) return;
}
if (ParentTail._TransformsGhostChain.Contains(collision.transform)) return;
if (ParentTail.IgnoredColliders.Contains(collision.collider)) return;
ParentTail.CollisionDetection(Index, collision);
previousCollision = collision.transform;
}
void OnCollisionExit(Collision collision)
{
if (collision.transform == previousCollision)
{
ParentTail.ExitCollision(Index);
previousCollision = null;
}
}
void OnTriggerEnter(Collider other)
{
if (other.isTrigger) return;
if (ParentTail.IgnoreMeshColliders)
if (other is MeshCollider) return;
if (other is CharacterController) return;
if (ParentTail._TransformsGhostChain.Contains(other.transform)) return;
if (ParentTail.IgnoredColliders.Contains(other)) return;
if (ParentTail.CollideWithOtherTails == false)
{
TailCollisionHelper helper = other.transform.GetComponent<TailCollisionHelper>();
if (helper) return;
//if (ParentTail.CollideWithOtherTails == false) return;
//if (helper.ParentTail == ParentTail) return;
}
ParentTail.AddCollider(other);
}
void OnTriggerExit(Collider other)
{
if (ParentTail.IncludedColliders.Contains(other))
{
if (!ParentTail.DynamicAlwaysInclude.Contains(other))
ParentTail.IncludedColliders.Remove(other);
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.isTrigger) return;
if (other is CompositeCollider2D) return;
#if UNITY_2019_4_OR_NEWER
if (other is TilemapCollider2D) return;
#endif
if (other is EdgeCollider2D) return;
if (ParentTail._TransformsGhostChain.Contains(other.transform)) return;
if (ParentTail.IgnoredColliders2D.Contains(other)) return;
//TailCollisionHelper helper = other.transform.GetComponent<TailCollisionHelper>();
//if (helper)
//{
// if (ParentTail.CollideWithOtherTails == false) return;
// if (helper.ParentTail == ParentTail) return;
//}
if (ParentTail.CollideWithOtherTails == false)
{
TailCollisionHelper helper = other.transform.GetComponent<TailCollisionHelper>();
if (helper) return;
}
ParentTail.AddCollider(other);
}
void OnTriggerExit2D(Collider2D other)
{
if (ParentTail.IncludedColliders2D.Contains(other))
{
if (!ParentTail.DynamicAlwaysInclude.Contains(other))
ParentTail.IncludedColliders2D.Remove(other);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea3bd5313553529418ec44a77ec6f94f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: a05a4f2884a2af44eaeedea1ea5ca396, type: 3}
userData:
assetBundleName:
assetBundleVariant: