添加插件
This commit is contained in:
@@ -19,6 +19,8 @@ namespace FIMSpace.Basics
|
||||
/// <summary> Just multiplier for rotation </summary>
|
||||
public float MouseSensitivity = 5f;
|
||||
|
||||
public bool NeedRMB = true;
|
||||
|
||||
/// <summary> Variables controlling turbo speed on shift key </summary>
|
||||
private float turboModeMultiply = 5f;
|
||||
|
||||
@@ -45,6 +47,7 @@ namespace FIMSpace.Basics
|
||||
|
||||
void Update()
|
||||
{
|
||||
|
||||
// Detecting key movement factors
|
||||
float f = Input.GetAxis("Vertical");
|
||||
float r = Input.GetAxis("Horizontal");
|
||||
@@ -65,11 +68,14 @@ namespace FIMSpace.Basics
|
||||
forward *= turbo;
|
||||
right *= turbo;
|
||||
|
||||
// Movement to sides with pressed rmb
|
||||
if (Input.GetMouseButton(1))
|
||||
if (Cursor.lockState != CursorLockMode.None)
|
||||
{
|
||||
rotation.x -= (Input.GetAxis("Mouse Y") * 1f * MouseSensitivity);
|
||||
rotation.y += (Input.GetAxis("Mouse X") * 1f * MouseSensitivity);
|
||||
// Rotation with pressed rmb
|
||||
if (Input.GetMouseButton(1) || !NeedRMB )
|
||||
{
|
||||
rotation.x -= (Input.GetAxis("Mouse Y") * 1f * MouseSensitivity);
|
||||
rotation.y += (Input.GetAxis("Mouse X") * 1f * MouseSensitivity);
|
||||
}
|
||||
}
|
||||
|
||||
// Lerping speed variables for smooth effect
|
||||
@@ -112,8 +118,11 @@ namespace FIMSpace.Basics
|
||||
}
|
||||
else
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
if (NeedRMB)
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6c164d759783a64cab9addd022955d6
|
||||
folderAsset: yes
|
||||
timeCreated: 1562097466
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
// Made with Amplify Shader Editor
|
||||
// Available at the Unity Asset Store - http://u3d.as/y3X
|
||||
Shader "FImpossible/Debug Vertex Color"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
_SpecColor("Specular Color",Color)=(1,1,1,1)
|
||||
[HideInInspector] __dirty( "", Int ) = 1
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+0" }
|
||||
Cull Back
|
||||
CGPROGRAM
|
||||
#pragma target 3.0
|
||||
#pragma surface surf BlinnPhong keepalpha addshadow fullforwardshadows
|
||||
struct Input
|
||||
{
|
||||
float4 vertexColor : COLOR;
|
||||
};
|
||||
|
||||
void surf( Input i , inout SurfaceOutput o )
|
||||
{
|
||||
o.Albedo = i.vertexColor.rgb;
|
||||
o.Alpha = 1;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
}
|
||||
Fallback "Diffuse"
|
||||
CustomEditor "ASEMaterialInspector"
|
||||
}
|
||||
/*ASEBEGIN
|
||||
Version=17700
|
||||
196;400;1164;522;-1073.19;205.4677;1.316292;True;False
|
||||
Node;AmplifyShaderEditor.VertexColorNode;134;1778.882,152.0806;Inherit;False;0;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
|
||||
Node;AmplifyShaderEditor.StandardSurfaceOutputNode;0;2007.661,79.56698;Float;False;True;-1;2;ASEMaterialInspector;0;0;BlinnPhong;FImpossible/Debug Vertex Color;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;Back;0;False;-1;0;False;-1;False;0;False;-1;0;False;-1;False;0;Opaque;0.5;True;True;0;False;Opaque;;Geometry;All;14;all;True;True;True;True;0;False;-1;False;0;False;-1;255;False;-1;255;False;-1;0;False;-1;0;False;-1;0;False;-1;0;False;-1;0;False;-1;0;False;-1;0;False;-1;0;False;-1;False;2;16.8;10;25;False;0.5;True;0;0;False;-1;0;False;-1;0;0;False;-1;0;False;-1;0;False;-1;0;False;-1;0;False;0;0,0,0,0;VertexOffset;True;False;Cylindrical;False;Relative;0;;-1;-1;-1;-1;0;False;0;0;False;-1;0;0;False;-1;0;0;0;False;0.1;False;-1;0;False;-1;15;0;FLOAT3;0,0,0;False;1;FLOAT3;0,0,0;False;2;FLOAT3;0,0,0;False;3;FLOAT;0;False;4;FLOAT;0;False;6;FLOAT3;0,0,0;False;7;FLOAT3;0,0,0;False;8;FLOAT;0;False;9;FLOAT;0;False;10;FLOAT;0;False;13;FLOAT3;0,0,0;False;11;FLOAT3;0,0,0;False;12;FLOAT3;0,0,0;False;14;FLOAT4;0,0,0,0;False;15;FLOAT3;0,0,0;False;0
|
||||
WireConnection;0;0;134;0
|
||||
ASEEND*/
|
||||
//CHKSM=EDA1B2F3D976C2A3E62B0FF40BFD5E6D54D79AB5
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eed2fa022e0342748a216ab9e75c992b
|
||||
timeCreated: 1565040098
|
||||
licenseType: Store
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
|
||||
|
||||
Shader "Particles/FVertexLit Blended"
|
||||
{
|
||||
Properties {
|
||||
_EmisColor ("Emissive Color", Color) = (.2,.2,.2,0)
|
||||
_MainTex ("Particle Texture", 2D) = "white" {}
|
||||
}
|
||||
|
||||
SubShader {
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
|
||||
Tags { "LightMode" = "Vertex" }
|
||||
Cull Off
|
||||
Lighting On
|
||||
Material { Emission [_EmisColor] }
|
||||
ColorMaterial AmbientAndDiffuse
|
||||
ZWrite Off
|
||||
ColorMask RGB
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
Pass {
|
||||
SetTexture [_MainTex] { combine primary * texture }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29b94f23dc02e254dacf2ecc893790d3
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67fc378029898f8418cf4c8b4c7043bb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,510 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.FTools
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// FC: Class for processing IK logics for multiple bones inverse kinematics
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class FIK_CCDProcessor : FIK_ProcessorBase
|
||||
{
|
||||
#region CCDIK Processor
|
||||
|
||||
public CCDIKBone[] IKBones; // { get; private set; }
|
||||
public CCDIKBone StartIKBone { get { return IKBones[0]; } }
|
||||
public CCDIKBone EndIKBone { get { return IKBones[IKBones.Length - 1]; } }
|
||||
|
||||
public bool ContinousSolving = true;
|
||||
[Range( 0f, 1f )]
|
||||
public float SyncWithAnimator = 1f;
|
||||
[Range( 1, 12 )]
|
||||
public int ReactionQuality = 2;
|
||||
[Range( 0f, 1f )]
|
||||
public float Smoothing = 0f;
|
||||
[Range( 0f, 1.5f )]
|
||||
public float StretchToTarget = 0f;
|
||||
public AnimationCurve StretchCurve = AnimationCurve.EaseInOut( 0f, 0f, 1f, 1f );
|
||||
|
||||
public bool Use2D = false;
|
||||
public bool Invert = false;
|
||||
|
||||
public float ActiveLength { get; private set; }
|
||||
|
||||
/// <summary> Assigning bones for IK processor with CCD IK logics (unlimited bone count) </summary>
|
||||
public FIK_CCDProcessor( Transform[] bonesChain )
|
||||
{
|
||||
IKBones = new CCDIKBone[bonesChain.Length];
|
||||
Bones = new CCDIKBone[IKBones.Length];
|
||||
|
||||
for( int i = 0; i < bonesChain.Length; i++ )
|
||||
{
|
||||
IKBones[i] = new CCDIKBone( bonesChain[i] );
|
||||
Bones[i] = IKBones[i];
|
||||
}
|
||||
|
||||
IKTargetPosition = EndBone.transform.position; IKTargetRotation = EndBone.transform.rotation;
|
||||
}
|
||||
|
||||
|
||||
public override void Init( Transform root )
|
||||
{
|
||||
if( Initialized ) return;
|
||||
|
||||
fullLength = 0f;
|
||||
for( int i = 0; i < Bones.Length; i++ )
|
||||
{
|
||||
CCDIKBone b = IKBones[i];
|
||||
CCDIKBone child = null, parent = null;
|
||||
|
||||
if( i > 0 ) parent = IKBones[i - 1];
|
||||
if( i < Bones.Length - 1 )
|
||||
{
|
||||
child = IKBones[i + 1];
|
||||
}
|
||||
|
||||
|
||||
if( i < Bones.Length - 1 )
|
||||
{
|
||||
IKBones[i].Init( child, parent );
|
||||
fullLength += b.BoneLength;
|
||||
b.ForwardOrientation = Quaternion.Inverse( b.transform.rotation ) * ( IKBones[i + 1].transform.position - b.transform.position );
|
||||
}
|
||||
else
|
||||
{
|
||||
IKBones[i].Init( child, parent );
|
||||
b.ForwardOrientation = Quaternion.Inverse( b.transform.rotation ) * ( IKBones[IKBones.Length - 1].transform.position - IKBones[0].transform.position );
|
||||
}
|
||||
}
|
||||
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
|
||||
#region Methods
|
||||
|
||||
|
||||
/// <summary> Updating processor with n-bones oriented inverse kinematics </summary>
|
||||
public override void Update()
|
||||
{
|
||||
if( !Initialized ) return;
|
||||
if( IKWeight <= 0f ) return;
|
||||
CCDIKBone wb = IKBones[0];
|
||||
|
||||
// Restoring previous IK progress for continous solving
|
||||
if( ContinousSolving )
|
||||
{
|
||||
while( wb != null )
|
||||
{
|
||||
wb.LastKeyLocalRotation = wb.transform.localRotation;
|
||||
wb.transform.localPosition = wb.LastIKLocPosition;
|
||||
wb.transform.localRotation = wb.LastIKLocRotation;
|
||||
wb = wb.IKChild;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( SyncWithAnimator > 0f )
|
||||
// Memory for animator syncing
|
||||
while( wb != null )
|
||||
{
|
||||
wb.LastKeyLocalRotation = wb.transform.localRotation;
|
||||
wb = wb.IKChild;
|
||||
}
|
||||
}
|
||||
|
||||
if( ReactionQuality < 0 ) ReactionQuality = 1;
|
||||
|
||||
Vector3 goalPivotOffset = Vector3.zero;
|
||||
if( ReactionQuality > 1 ) goalPivotOffset = GetGoalPivotOffset();
|
||||
|
||||
for( int itr = 0; itr < ReactionQuality; itr++ )
|
||||
{
|
||||
// Restrictions for multiple interations
|
||||
if( itr >= 1 )
|
||||
if( goalPivotOffset.sqrMagnitude == 0 )
|
||||
if( Smoothing > 0 )
|
||||
if( GetVelocityDifference() < Smoothing * Smoothing ) break;
|
||||
|
||||
LastLocalDirection = RefreshLocalDirection();
|
||||
|
||||
Vector3 ikGoal = IKTargetPosition + goalPivotOffset;
|
||||
|
||||
// Going in iterations in reversed way, from pre end child to root parent
|
||||
wb = IKBones[IKBones.Length - 2];
|
||||
|
||||
if( !Use2D ) // Full 3D space rotations calculations
|
||||
{
|
||||
if( !Invert )
|
||||
{
|
||||
while( wb != null )
|
||||
{
|
||||
float weight = wb.MotionWeight * IKWeight;
|
||||
|
||||
if( weight > 0f )
|
||||
{
|
||||
Quaternion targetRotation = Quaternion.FromToRotation( Bones[Bones.Length - 1].transform.position - wb.transform.position /*fromThisToEndChildBone*/, ikGoal - wb.transform.position /*fromThisToIKGoal*/) * wb.transform.rotation;
|
||||
if( weight < 1f ) wb.transform.rotation = Quaternion.Lerp( wb.transform.rotation, targetRotation, weight );
|
||||
else wb.transform.rotation = targetRotation;
|
||||
}
|
||||
|
||||
wb.AngleLimiting();
|
||||
wb = wb.IKParent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while( wb != null )
|
||||
{
|
||||
wb.AngleLimiting();
|
||||
wb = wb.IKParent;
|
||||
}
|
||||
|
||||
wb = IKBones[0];
|
||||
|
||||
while( wb != null )
|
||||
{
|
||||
float weight = wb.MotionWeight * IKWeight;
|
||||
|
||||
if( weight > 0f )
|
||||
{
|
||||
Quaternion targetRotation = Quaternion.FromToRotation( Bones[Bones.Length - 1].transform.position - wb.transform.position /*fromThisToEndChildBone*/, ikGoal - wb.transform.position /*fromThisToIKGoal*/) * wb.transform.rotation;
|
||||
if( weight < 1f ) wb.transform.rotation = Quaternion.Lerp( wb.transform.rotation, targetRotation, weight );
|
||||
else wb.transform.rotation = targetRotation;
|
||||
}
|
||||
|
||||
wb = wb.IKChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !Invert )
|
||||
{
|
||||
// Going in while() loop is 2x faster than for(i;i;i;) when there is more iterations
|
||||
while( wb != null )
|
||||
{
|
||||
float weight = wb.MotionWeight * IKWeight;
|
||||
|
||||
if( weight > 0f )
|
||||
{
|
||||
Vector3 fromThisToEndChildBone = Bones[Bones.Length - 1].transform.position - wb.transform.position;
|
||||
Vector3 fromThisToIKGoal = ikGoal - wb.transform.position;
|
||||
wb.transform.rotation = Quaternion.AngleAxis( Mathf.DeltaAngle( Mathf.Atan2( fromThisToEndChildBone.x, fromThisToEndChildBone.y ) * Mathf.Rad2Deg /* Angle to last bone */, Mathf.Atan2( fromThisToIKGoal.x, fromThisToIKGoal.y ) * Mathf.Rad2Deg /* Angle to goal position */) * weight, Vector3.back ) * wb.transform.rotation;
|
||||
}
|
||||
|
||||
wb.AngleLimiting();
|
||||
wb = wb.IKParent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while( wb != null )
|
||||
{
|
||||
wb.AngleLimiting();
|
||||
wb = wb.IKParent;
|
||||
}
|
||||
|
||||
wb = IKBones[0];
|
||||
|
||||
while( wb != null )
|
||||
{
|
||||
float weight = wb.MotionWeight * IKWeight;
|
||||
|
||||
if( weight > 0f )
|
||||
{
|
||||
Vector3 fromThisToEndChildBone = Bones[Bones.Length - 1].transform.position - wb.transform.position;
|
||||
Vector3 fromThisToIKGoal = ikGoal - wb.transform.position;
|
||||
wb.transform.rotation = Quaternion.AngleAxis( Mathf.DeltaAngle( Mathf.Atan2( fromThisToEndChildBone.x, fromThisToEndChildBone.y ) * Mathf.Rad2Deg /* Angle to last bone */, Mathf.Atan2( fromThisToIKGoal.x, fromThisToIKGoal.y ) * Mathf.Rad2Deg /* Angle to goal position */) * weight, Vector3.back ) * wb.transform.rotation;
|
||||
}
|
||||
|
||||
wb = wb.IKChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LastLocalDirection = RefreshLocalDirection();
|
||||
|
||||
// Support for stretching
|
||||
if( StretchToTarget > 0f )
|
||||
{
|
||||
float remainingDist = ( IKTargetPosition - EndIKBone.transform.position ).magnitude;
|
||||
|
||||
ActiveLength = Mathf.Epsilon;
|
||||
wb = IKBones[0];
|
||||
int ind = 0;
|
||||
float boneMul = Mathf.Max( 1f, StretchToTarget );
|
||||
|
||||
while( wb.IKChild != null )
|
||||
{
|
||||
if( remainingDist <= 0f )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Vector3 toTarget = ( IKTargetPosition - wb.transform.position );
|
||||
Vector3 toTargetN = toTarget.normalized;
|
||||
|
||||
Vector3 prePos = wb.transform.position;
|
||||
Vector3 preChildPos = wb.IKChild.transform.position;
|
||||
Vector3 toNext = preChildPos - prePos;
|
||||
Vector3 norm = toNext.normalized;
|
||||
|
||||
float dot = Vector3.Dot( norm, toTargetN );
|
||||
|
||||
if( dot > 0f )
|
||||
{
|
||||
float moveBy = wb.BoneLength * boneMul * dot;
|
||||
if( moveBy > remainingDist ) moveBy = remainingDist;
|
||||
|
||||
Vector3 newChildPos = preChildPos + norm * ( moveBy );
|
||||
wb.IKChild.transform.position = Vector3.Lerp( preChildPos, newChildPos, StretchToTarget );
|
||||
wb.transform.rotation = wb.transform.rotation * Quaternion.FromToRotation( preChildPos - prePos, wb.Child.transform.position - wb.transform.position );
|
||||
|
||||
remainingDist -= Vector3.Distance( preChildPos, newChildPos );
|
||||
}
|
||||
|
||||
wb = wb.IKChild;
|
||||
ind += 1;
|
||||
}
|
||||
|
||||
//if (stretch > 1f) for (int i = 1; i < IKBones.Length; ++i) IKBones[i].transform.position += (toGoal.normalized) * ((IKBones[i - 1].BoneLength ) * StretchCurve.Evaluate(-(1f - stretch)));
|
||||
}
|
||||
|
||||
|
||||
wb = IKBones[0];
|
||||
while( wb != null )
|
||||
{
|
||||
// Storing final rotations for animator offset
|
||||
wb.LastIKLocRotation = wb.transform.localRotation;
|
||||
wb.LastIKLocPosition = wb.transform.localPosition;
|
||||
|
||||
// Offset based rotation sync with animator
|
||||
Quaternion ikDiff = wb.LastIKLocRotation * Quaternion.Inverse( wb.InitialLocalRotation );
|
||||
wb.transform.localRotation = Quaternion.Lerp( wb.LastIKLocRotation, ikDiff * wb.LastKeyLocalRotation, SyncWithAnimator );
|
||||
|
||||
if( IKWeight < 1f )
|
||||
wb.transform.localRotation = Quaternion.Lerp( wb.LastKeyLocalRotation, wb.transform.localRotation, IKWeight );
|
||||
|
||||
wb = wb.IKChild;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected Vector3 GetGoalPivotOffset()
|
||||
{
|
||||
if( !GoalPivotOffsetDetected() ) return Vector3.zero;
|
||||
|
||||
Vector3 IKDirection = ( IKTargetPosition - IKBones[0].transform.position ).normalized;
|
||||
Vector3 secondaryDirection = new Vector3( IKDirection.y, IKDirection.z, IKDirection.x );
|
||||
|
||||
if( IKBones[IKBones.Length - 2].AngleLimit < 180 || IKBones[IKBones.Length - 2].TwistAngleLimit < 180 )
|
||||
secondaryDirection = IKBones[IKBones.Length - 2].transform.rotation * IKBones[IKBones.Length - 2].ForwardOrientation;
|
||||
|
||||
return Vector3.Cross( IKDirection, secondaryDirection ) * IKBones[IKBones.Length - 2].BoneLength * 0.5f;
|
||||
}
|
||||
|
||||
private bool GoalPivotOffsetDetected()
|
||||
{
|
||||
if( !Initialized ) return false;
|
||||
|
||||
Vector3 toLastDirection = Bones[Bones.Length - 1].transform.position - Bones[0].transform.position;
|
||||
Vector3 toGoalDirection = IKTargetPosition - Bones[0].transform.position;
|
||||
|
||||
float toLastMagn = toLastDirection.magnitude;
|
||||
float toGoalMagn = toGoalDirection.magnitude;
|
||||
|
||||
if( toGoalMagn == 0 ) return false;
|
||||
if( toLastMagn == 0 ) return false;
|
||||
if( toLastMagn < toGoalMagn ) return false;
|
||||
if( toLastMagn < fullLength - ( Bones[Bones.Length - 2].BoneLength * 0.1f ) ) return false;
|
||||
if( toGoalMagn > toLastMagn ) return false;
|
||||
|
||||
float dot = Vector3.Dot( toLastDirection / toLastMagn, toGoalDirection / toGoalMagn );
|
||||
if( dot < 0.999f ) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3 RefreshLocalDirection()
|
||||
{
|
||||
LocalDirection = Bones[0].transform.InverseTransformDirection( Bones[Bones.Length - 1].transform.position - Bones[0].transform.position );
|
||||
return LocalDirection;
|
||||
}
|
||||
|
||||
float GetVelocityDifference()
|
||||
{ return Vector3.SqrMagnitude( LocalDirection - LastLocalDirection ); }
|
||||
|
||||
|
||||
|
||||
/// <summary> Limiting angle for all IK bones </summary>
|
||||
public void AutoLimitAngle( float angleLimit = 60f, float twistAngleLimit = 50f )
|
||||
{
|
||||
if( IKBones == null ) return;
|
||||
|
||||
float step = 1f / (float)IKBones.Length;
|
||||
|
||||
if( Invert )
|
||||
{
|
||||
for( int i = 0; i < IKBones.Length; i++ )
|
||||
{
|
||||
IKBones[i].AngleLimit = angleLimit * Mathf.Min( 1f, ( 1f - ( ( i + 1 ) * step ) ) * 3f );
|
||||
IKBones[i].TwistAngleLimit = twistAngleLimit * Mathf.Min( 1f, ( 1f - ( ( i + 1 ) * step ) ) * 4.5f );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for( int i = 0; i < IKBones.Length; i++ )
|
||||
{
|
||||
IKBones[i].AngleLimit = angleLimit * Mathf.Min( 1f, ( i + 1 ) * step * 3f );
|
||||
IKBones[i].TwistAngleLimit = twistAngleLimit * Mathf.Min( 1f, ( i + 1 ) * step * 4.5f );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Spreading weight over IK bones automatically </summary>
|
||||
public void AutoWeightBones( float baseValue = 1f )
|
||||
{
|
||||
float step = baseValue / (float)( Bones.Length * 1.3f );
|
||||
|
||||
if( Invert )
|
||||
{
|
||||
for( int i = 0; i < Bones.Length; i++ )
|
||||
{
|
||||
Bones[i].MotionWeight = 1f - ( baseValue - step * i );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for( int i = 0; i < Bones.Length; i++ )
|
||||
{
|
||||
Bones[i].MotionWeight = baseValue - step * i;
|
||||
//Bones[i].MotionWeight *= Mathf.Min(1f, (i + 1) * step1 * 3f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Spreading weight over IK bones with curve (Clamped01) </summary>
|
||||
public void AutoWeightBones( AnimationCurve weightCurve )
|
||||
{
|
||||
if( Invert )
|
||||
{
|
||||
for( int i = 0; i < Bones.Length; i++ )
|
||||
Bones[i].MotionWeight = Mathf.Clamp( 1f - weightCurve.Evaluate( (float)i / (float)Bones.Length ), 0f, 1f );
|
||||
return;
|
||||
}
|
||||
|
||||
for( int i = 0; i < Bones.Length; i++ )
|
||||
Bones[i].MotionWeight = Mathf.Clamp( weightCurve.Evaluate( (float)i / (float)Bones.Length ), 0f, 1f );
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
[System.Serializable]
|
||||
public class CCDIKBone : FIK_IKBoneBase
|
||||
{
|
||||
public CCDIKBone IKParent { get; private set; }
|
||||
public CCDIKBone IKChild { get; private set; }
|
||||
|
||||
[Range( 0f, 180f )] public float AngleLimit = 45f;
|
||||
[Range( 0f, 180f )] public float TwistAngleLimit = 5f;
|
||||
|
||||
/// <summary> Defined at Init() of CCD IK processor </summary>
|
||||
public Vector3 ForwardOrientation;
|
||||
public float FrameWorldLength = 1f;
|
||||
|
||||
public Vector2 HingeLimits = Vector2.zero;
|
||||
public Quaternion PreviousHingeRotation;
|
||||
public float PreviousHingeAngle;
|
||||
|
||||
public Vector3 LastIKLocPosition;
|
||||
public Quaternion LastIKLocRotation;
|
||||
|
||||
public CCDIKBone( Transform t ) : base( t ) { }
|
||||
|
||||
public void Init( CCDIKBone child, CCDIKBone parent )
|
||||
{
|
||||
LastIKLocPosition = transform.localPosition;
|
||||
IKParent = parent;
|
||||
if( child != null ) SetChild( child );
|
||||
IKChild = child;
|
||||
}
|
||||
|
||||
public override void SetChild( FIK_IKBoneBase child )
|
||||
{
|
||||
base.SetChild( child );
|
||||
}
|
||||
|
||||
#region CCD IK Methods
|
||||
|
||||
public void AngleLimiting()
|
||||
{
|
||||
Quaternion localRotation = Quaternion.Inverse( LastKeyLocalRotation ) * transform.localRotation;
|
||||
Quaternion limitedRotation = localRotation;
|
||||
|
||||
if( FEngineering.VIsZero( HingeLimits ) )
|
||||
{
|
||||
if( AngleLimit < 180 ) limitedRotation = LimitSpherical( limitedRotation );
|
||||
if( TwistAngleLimit < 180 ) limitedRotation = LimitZ( limitedRotation );
|
||||
}
|
||||
else limitedRotation = LimitHinge( limitedRotation );
|
||||
|
||||
if( FEngineering.QIsSame( limitedRotation, localRotation ) ) return;
|
||||
|
||||
transform.localRotation = LastKeyLocalRotation * limitedRotation;
|
||||
}
|
||||
|
||||
private Quaternion LimitSpherical( Quaternion rotation )
|
||||
{
|
||||
if( FEngineering.QIsZero( rotation ) ) return rotation;
|
||||
Vector3 currentForward = rotation * ForwardOrientation;
|
||||
Quaternion limitAngle = Quaternion.RotateTowards( Quaternion.identity, Quaternion.FromToRotation( ForwardOrientation, currentForward ), AngleLimit );
|
||||
return Quaternion.FromToRotation( currentForward, limitAngle * ForwardOrientation ) * rotation;
|
||||
}
|
||||
|
||||
private Quaternion LimitZ( Quaternion currentRotation )
|
||||
{
|
||||
Vector3 orthoOrientation = new Vector3( ForwardOrientation.y, ForwardOrientation.z, ForwardOrientation.x );
|
||||
Vector3 normal = currentRotation * ForwardOrientation;
|
||||
Vector3 tangent = orthoOrientation;
|
||||
Vector3.OrthoNormalize( ref normal, ref tangent );
|
||||
|
||||
orthoOrientation = currentRotation * orthoOrientation;
|
||||
Vector3.OrthoNormalize( ref normal, ref orthoOrientation );
|
||||
|
||||
Quaternion limitRot = Quaternion.FromToRotation( orthoOrientation, tangent ) * currentRotation;
|
||||
if( TwistAngleLimit <= 0 ) return limitRot;
|
||||
|
||||
return Quaternion.RotateTowards( limitRot, currentRotation, TwistAngleLimit );
|
||||
}
|
||||
|
||||
private Quaternion LimitHinge( Quaternion rotation )
|
||||
{
|
||||
Quaternion addRotation = ( Quaternion.FromToRotation( rotation * ForwardOrientation, ForwardOrientation ) * rotation ) * Quaternion.Inverse( PreviousHingeRotation );
|
||||
float addAngle = Quaternion.Angle( Quaternion.identity, addRotation );
|
||||
|
||||
Vector3 orthoOrientation = new Vector3( ForwardOrientation.z, ForwardOrientation.x, ForwardOrientation.y );
|
||||
Vector3 cross = Vector3.Cross( orthoOrientation, ForwardOrientation );
|
||||
if( Vector3.Dot( addRotation * orthoOrientation, cross ) > 0f ) addAngle = -addAngle;
|
||||
|
||||
PreviousHingeAngle = Mathf.Clamp( PreviousHingeAngle + addAngle, HingeLimits.x, HingeLimits.y );
|
||||
PreviousHingeRotation = Quaternion.AngleAxis( PreviousHingeAngle, ForwardOrientation );
|
||||
return PreviousHingeRotation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dad66f3b80f9f2a4fa589a5b9ab4da09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbcd6612c7be2d247a034546e811150a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,316 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace.Basics
|
||||
{
|
||||
/// <summary>
|
||||
/// FM: Basic class for holding animation clips as hashes inside dictionary and with some other handy methods
|
||||
/// Add your animation clips with fAnimationClip.AddClip(animator, Idle) then use it by fAnimationClip["Idle"]
|
||||
/// </summary>
|
||||
public class FAnimationClips : Dictionary<string, int>
|
||||
{
|
||||
/// <summary> Reference to Unity's Animator component </summary>
|
||||
public readonly Animator Animator;
|
||||
|
||||
/// <summary> Lastest crossfaded from code animation clip name </summary>
|
||||
public string CurrentAnimation { get; private set; }
|
||||
/// <summary> Previously crossfaded from code animation clip name </summary>
|
||||
public string PreviousAnimation { get; private set; }
|
||||
|
||||
public int Layer = 0;
|
||||
|
||||
/// <summary>
|
||||
/// ADD ANIMATION CLIPS USING - AddClip() - method !!!
|
||||
/// </summary>
|
||||
public FAnimationClips(Animator animator)
|
||||
{
|
||||
this.Animator = animator;
|
||||
CurrentAnimation = "";
|
||||
PreviousAnimation = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checking if inside animator clipName state exist and with 'exactClipName' false it will check all variants of clipName, uppercase / lowercase etc.
|
||||
/// </summary>
|
||||
public void Add(string clipName, bool exactClipName = false)
|
||||
{
|
||||
AddClip(clipName, exactClipName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checking if inside animator clipName state exist and with 'exactClipName' false it will check all variants of clipName, uppercase / lowercase etc.
|
||||
/// </summary>
|
||||
public void AddClip(string clipName, bool exactClipName = false)
|
||||
{
|
||||
AddClip(Animator, clipName, exactClipName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checking if inside animator clipName state exist and with 'exactClipName' false it will check all variants of clipName, uppercase / lowercase etc.
|
||||
/// </summary>
|
||||
public void AddClip(Animator animator, string clipName, bool exactClipName = false)
|
||||
{
|
||||
if (!animator)
|
||||
{
|
||||
Debug.LogError("No animator!");
|
||||
return;
|
||||
}
|
||||
|
||||
string existing = "";
|
||||
|
||||
if (!exactClipName) // Checking if animation state exists with different variants for clipName word
|
||||
{
|
||||
if (FAnimatorMethods.StateExists(animator, clipName, Layer)) existing = clipName;
|
||||
else
|
||||
if (FAnimatorMethods.StateExists(animator, FStringMethods.CapitalizeFirstLetter(clipName))) existing = FStringMethods.CapitalizeFirstLetter(clipName);
|
||||
else
|
||||
if (FAnimatorMethods.StateExists(animator, clipName.ToLower(), Layer)) existing = clipName.ToLower();
|
||||
else
|
||||
if (FAnimatorMethods.StateExists(animator, clipName.ToUpper(), Layer)) existing = clipName.ToUpper();
|
||||
}
|
||||
else // Checking if state with provided exact name exists inside animator
|
||||
{
|
||||
if (FAnimatorMethods.StateExists(animator, clipName, Layer)) existing = clipName;
|
||||
}
|
||||
|
||||
if (existing == "")
|
||||
{
|
||||
Debug.LogWarning("Clip with name " + clipName + " not exists in animator from game object " + animator.gameObject.name);
|
||||
}
|
||||
else // Adding clip hash to dictionary if it exists inside animator
|
||||
{
|
||||
if (!ContainsKey(clipName))
|
||||
Add(clipName, Animator.StringToHash(existing));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transitioning to choosed animation by dictionary
|
||||
/// </summary>
|
||||
public void CrossFadeInFixedTime(string clip, float transitionTime = 0.25f, float timeOffset = 0f, bool startOver = false)
|
||||
{
|
||||
if (this.ContainsKey(clip))
|
||||
{
|
||||
RefreshClipMemory(clip);
|
||||
|
||||
if (startOver)
|
||||
Animator.CrossFadeInFixedTime(this[clip], transitionTime, Layer, timeOffset);
|
||||
else
|
||||
if (!IsPlaying(clip))
|
||||
Animator.CrossFadeInFixedTime(this[clip], transitionTime, Layer, timeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transitioning to choosed animation by dictionary
|
||||
/// </summary>
|
||||
public void CrossFade(string clip, float transitionTime = 0.25f, float timeOffset = 0f, bool startOver = false)
|
||||
{
|
||||
if (this.ContainsKey(clip))
|
||||
{
|
||||
RefreshClipMemory(clip);
|
||||
|
||||
if (startOver)
|
||||
Animator.CrossFade(this[clip], transitionTime, Layer, timeOffset);
|
||||
else
|
||||
if (!IsPlaying(clip))
|
||||
Animator.CrossFade(this[clip], transitionTime, Layer, timeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Storing lately and currently played clip in variables
|
||||
/// </summary>
|
||||
private void RefreshClipMemory(string name)
|
||||
{
|
||||
if (name != CurrentAnimation)
|
||||
{
|
||||
PreviousAnimation = CurrentAnimation;
|
||||
CurrentAnimation = name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing float parameter value smoothly (when speed value about 10, 60 is instant)
|
||||
/// </summary>
|
||||
public void SetFloat(string parameter, float value = 0f, float deltaSpeed = 60f)
|
||||
{
|
||||
float newValue = Animator.GetFloat(parameter);
|
||||
if (deltaSpeed >= 60f) newValue = value; else newValue = FLogicMethods.FLerp(newValue, value, Time.deltaTime * deltaSpeed);
|
||||
Animator.SetFloat(parameter, newValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing float parameter value smoothly (when speed value about 10, 60 is instant)
|
||||
/// </summary>
|
||||
public void SetFloatUnscaledDelta(string parameter, float value = 0f, float deltaSpeed = 60f)
|
||||
{
|
||||
float newValue = Animator.GetFloat(parameter);
|
||||
if (deltaSpeed >= 60f) newValue = value; else newValue = FLogicMethods.FLerp(newValue, value, Time.unscaledDeltaTime * deltaSpeed);
|
||||
Animator.SetFloat(parameter, newValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If animator is truly playing clip with given string id (added by you using clips.AddClip(name) )
|
||||
/// </summary>
|
||||
internal bool IsPlaying(string clip)
|
||||
{
|
||||
AnimatorStateInfo info;
|
||||
if (Animator.IsInTransition(Layer))
|
||||
{
|
||||
info = Animator.GetNextAnimatorStateInfo(Layer);
|
||||
if (info.shortNameHash == this[clip]) return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
info = Animator.GetCurrentAnimatorStateInfo(Layer);
|
||||
if (info.shortNameHash == this[clip]) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class FAnimator
|
||||
{
|
||||
/// <summary> Reference to Unity's Animator component </summary>
|
||||
public readonly Animator Animator;
|
||||
|
||||
/// <summary> Lastest crossfaded from code animation clip name </summary>
|
||||
public string CurrentAnimation { get; private set; }
|
||||
/// <summary> Previously crossfaded from code animation clip name </summary>
|
||||
public string PreviousAnimation { get; private set; }
|
||||
|
||||
public int Layer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// ADD ANIMATION CLIPS USING - AddClip() - method !!!
|
||||
/// </summary>
|
||||
public FAnimator(Animator animator, int layer = 0)
|
||||
{
|
||||
this.Animator = animator;
|
||||
CurrentAnimation = "";
|
||||
PreviousAnimation = "";
|
||||
Layer = layer;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checking if inside animator clipName state exist and with 'exactClipName' false it will check all variants of clipName, uppercase / lowercase etc.
|
||||
/// </summary>
|
||||
public bool ContainsClip(string clipName, bool exactClipName = false)
|
||||
{
|
||||
if (!Animator)
|
||||
{
|
||||
Debug.LogError("No animator!");
|
||||
return false;
|
||||
}
|
||||
|
||||
string existing = "";
|
||||
|
||||
if (!exactClipName) // Checking if animation state exists with different variants for clipName word
|
||||
{
|
||||
if (FAnimatorMethods.StateExists(Animator, clipName, Layer)) existing = clipName;
|
||||
else
|
||||
if (FAnimatorMethods.StateExists(Animator, FStringMethods.CapitalizeFirstLetter(clipName))) existing = FStringMethods.CapitalizeFirstLetter(clipName);
|
||||
else
|
||||
if (FAnimatorMethods.StateExists(Animator, clipName.ToLower(), Layer)) existing = clipName.ToLower();
|
||||
else
|
||||
if (FAnimatorMethods.StateExists(Animator, clipName.ToUpper(), Layer)) existing = clipName.ToUpper();
|
||||
}
|
||||
else // Checking if state with provided exact name exists inside animator
|
||||
{
|
||||
if (FAnimatorMethods.StateExists(Animator, clipName, Layer)) existing = clipName;
|
||||
}
|
||||
|
||||
if (existing == "")
|
||||
{
|
||||
Debug.LogWarning("Clip with name " + clipName + " not exists in animator from game object " + Animator.gameObject.name);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transitioning to choosed animation by dictionary
|
||||
/// </summary>
|
||||
public void CrossFadeInFixedTime(string clip, float transitionTime = 0.25f, float timeOffset = 0f, bool startOver = false)
|
||||
{
|
||||
RefreshClipMemory(clip);
|
||||
|
||||
if (startOver)
|
||||
Animator.CrossFadeInFixedTime(clip, transitionTime, Layer, timeOffset);
|
||||
else
|
||||
if (!IsPlaying(clip))
|
||||
Animator.CrossFadeInFixedTime(clip, transitionTime, Layer, timeOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transitioning to choosed animation by dictionary
|
||||
/// </summary>
|
||||
public void CrossFade(string clip, float transitionTime = 0.25f, float timeOffset = 0f, bool startOver = false)
|
||||
{
|
||||
RefreshClipMemory(clip);
|
||||
|
||||
if (startOver)
|
||||
Animator.CrossFade(clip, transitionTime, Layer, timeOffset);
|
||||
else
|
||||
if (!IsPlaying(clip))
|
||||
Animator.CrossFade(clip, transitionTime, Layer, timeOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Storing lately and currently played clip in variables
|
||||
/// </summary>
|
||||
private void RefreshClipMemory(string name)
|
||||
{
|
||||
if (name != CurrentAnimation)
|
||||
{
|
||||
PreviousAnimation = CurrentAnimation;
|
||||
CurrentAnimation = name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing float parameter value smoothly (when speed value about 10, 60 is instant)
|
||||
/// </summary>
|
||||
public void SetFloat(string parameter, float value = 0f, float deltaSpeed = 60f)
|
||||
{
|
||||
float newValue = Animator.GetFloat(parameter);
|
||||
if (deltaSpeed >= 60f) newValue = value; else newValue = FLogicMethods.FLerp(newValue, value, Time.deltaTime * deltaSpeed);
|
||||
Animator.SetFloat(parameter, newValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing float parameter value smoothly (when speed value about 10, 60 is instant)
|
||||
/// </summary>
|
||||
public void SetFloatUnscaledDelta(string parameter, float value = 0f, float deltaSpeed = 60f)
|
||||
{
|
||||
float newValue = Animator.GetFloat(parameter);
|
||||
if (deltaSpeed >= 60f) newValue = value; else newValue = FLogicMethods.FLerp(newValue, value, Time.unscaledDeltaTime * deltaSpeed);
|
||||
Animator.SetFloat(parameter, newValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If animator is truly playing clip with given string id (added by you using clips.AddClip(name) )
|
||||
/// </summary>
|
||||
internal bool IsPlaying(string clip)
|
||||
{
|
||||
AnimatorStateInfo info;
|
||||
if (Animator.IsInTransition(Layer))
|
||||
{
|
||||
info = Animator.GetNextAnimatorStateInfo(Layer);
|
||||
if (info.shortNameHash == Animator.StringToHash(clip) ) return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
info = Animator.GetCurrentAnimatorStateInfo(Layer);
|
||||
if (info.shortNameHash == Animator.StringToHash(clip)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7502876f3a7fc4648ac29443a7d6817a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
/// <summary>
|
||||
/// FMoeglich: Class with methods which can be helpful when using unity Animator class
|
||||
/// </summary>
|
||||
public static class FAnimatorMethods
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Sets animator's float value with lerp
|
||||
/// </summary>
|
||||
public static void LerpFloatValue(this Animator animator, string name = "RunWalk", float value = 0f, float deltaSpeed = 8f)
|
||||
{
|
||||
float newValue = animator.GetFloat(name);
|
||||
newValue = Mathf.Lerp(newValue, value, Time.deltaTime * deltaSpeed);
|
||||
animator.SetFloat(name, newValue);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Function called to detect if no-looped animation finish
|
||||
/// </summary>
|
||||
public static bool CheckAnimationEnd(this Animator animator, int layer = 0, bool reverse = false, bool checkAnimLoop = true)
|
||||
{
|
||||
AnimatorStateInfo info = animator.GetCurrentAnimatorStateInfo(layer);
|
||||
|
||||
if (!animator.IsInTransition(layer))
|
||||
{
|
||||
if (checkAnimLoop)
|
||||
{
|
||||
if (info.loop == false)
|
||||
{
|
||||
if (!reverse)
|
||||
if (info.normalizedTime > 0.98f) return true;
|
||||
else
|
||||
if (info.normalizedTime < 0.02f) return true;
|
||||
}
|
||||
}
|
||||
else /* Same operation as above but without checking if animation is looped in the source */
|
||||
{
|
||||
if (!reverse)
|
||||
if (info.normalizedTime > 0.98f) return true;
|
||||
else
|
||||
if (info.normalizedTime < 0.02f) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resetting all additional layers' weights to zero (lerp but reaching value)
|
||||
/// </summary>
|
||||
public static void ResetLayersWeights(this Animator animator, float speed = 10f)
|
||||
{
|
||||
for (int i = 1; i < animator.layerCount; i++)
|
||||
{
|
||||
animator.SetLayerWeight(i, animator.GetLayerWeight(i).Lerp(0f, Time.deltaTime * speed));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Transitioning value of animator layer's weight to target with smooth effect
|
||||
/// </summary>
|
||||
public static void LerpLayerWeight(this Animator animator, int layer = 0, float newValue = 1f, float speed = 8f)
|
||||
{
|
||||
float newWeight = animator.GetLayerWeight(layer);
|
||||
newWeight.Lerp(newValue, Time.deltaTime * speed);
|
||||
|
||||
if (newValue == 1f) if (newWeight > 0.999f) newWeight = 1f;
|
||||
if (newValue == 0f) if (newWeight < 0.01f) newWeight = 0f;
|
||||
|
||||
animator.SetLayerWeight(layer, newWeight);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returning true if state exist
|
||||
/// </summary>
|
||||
public static bool StateExists(this Animator animator, string clipName, int layer = 0)
|
||||
{
|
||||
int animHash = Animator.StringToHash(clipName);
|
||||
return animator.HasState(layer, animHash);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e38c053ed5086f49ab2f804edeed7c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
/// <summary>
|
||||
/// FMoeglich: Class with methods which can be helpful in using Unity Console.
|
||||
/// Recommending to use some console extensions like Console Enchanced or other.
|
||||
/// </summary>
|
||||
public static class FDebug
|
||||
{
|
||||
public static void Log(string log)
|
||||
{
|
||||
Debug.Log("LOG: " + log);
|
||||
}
|
||||
|
||||
public static void Log(string log, string category)
|
||||
{
|
||||
Debug.Log(MarkerColor("#1A6600") + "[" + category + "]" + EndColorMarker() + " " + log);
|
||||
}
|
||||
|
||||
public static void LogRed(string log)
|
||||
{
|
||||
Debug.Log(MarkerColor("red") + log + EndColorMarker());
|
||||
}
|
||||
|
||||
public static void LogOrange(string log)
|
||||
{
|
||||
Debug.Log(MarkerColor("#D1681D") + log + EndColorMarker());
|
||||
}
|
||||
|
||||
public static void LogYellow(string log)
|
||||
{
|
||||
Debug.Log(MarkerColor("#E0D300") + log + EndColorMarker());
|
||||
}
|
||||
|
||||
private static readonly System.Diagnostics.Stopwatch _debugWatch = new System.Diagnostics.Stopwatch();
|
||||
public static void StartMeasure()
|
||||
{
|
||||
_debugWatch.Reset();
|
||||
_debugWatch.Start();
|
||||
}
|
||||
|
||||
public static void PauseMeasure()
|
||||
{
|
||||
_debugWatch.Stop();
|
||||
}
|
||||
|
||||
public static void ResumeMeasure()
|
||||
{
|
||||
_debugWatch.Start();
|
||||
}
|
||||
|
||||
public static void EndMeasureAndLog(string v)
|
||||
{
|
||||
_debugWatch.Stop();
|
||||
_LastMeasureMilliseconds = _debugWatch.ElapsedMilliseconds;
|
||||
_LastMeasureTicks = _debugWatch.ElapsedTicks;
|
||||
UnityEngine.Debug.Log("Measure " + v + ": " + _debugWatch.ElapsedTicks + " ticks " + _debugWatch.ElapsedMilliseconds + "ms");
|
||||
}
|
||||
|
||||
public static long EndMeasureAndGetTicks()
|
||||
{
|
||||
_debugWatch.Stop();
|
||||
_LastMeasureMilliseconds = _debugWatch.ElapsedMilliseconds;
|
||||
_LastMeasureTicks = _debugWatch.ElapsedTicks;
|
||||
return _debugWatch.ElapsedTicks;
|
||||
}
|
||||
|
||||
public static long _LastMeasureMilliseconds = 0;
|
||||
public static long _LastMeasureTicks = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Rich text marker for color
|
||||
/// </summary>
|
||||
public static string MarkerColor(string color)
|
||||
{
|
||||
return "<color='" + color + "'>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// close rich text marker for color
|
||||
/// </summary>
|
||||
public static string EndColorMarker()
|
||||
{
|
||||
return "</color>";
|
||||
}
|
||||
|
||||
|
||||
public static void DrawBounds2D(this Bounds b, Color c, float y = 0f, float scale = 1f, float duration = 1.1f)
|
||||
{
|
||||
Vector3 fr1 = new Vector3(b.max.x, y, b.max.z) * scale;
|
||||
Vector3 br1 = new Vector3(b.max.x, y, b.min.z) * scale;
|
||||
Vector3 bl1 = new Vector3(b.min.x, y, b.min.z) * scale;
|
||||
Vector3 fl1 = new Vector3(b.min.x, y, b.max.z) * scale;
|
||||
Debug.DrawLine(fr1, br1, c, duration);
|
||||
Debug.DrawLine(br1, bl1, c, duration);
|
||||
Debug.DrawLine(br1, bl1, c, duration);
|
||||
Debug.DrawLine(bl1, fl1, c, duration);
|
||||
Debug.DrawLine(fl1, fr1, c, duration);
|
||||
}
|
||||
|
||||
public static void DrawBounds3D(this Bounds b, Color c, float scale = 1f, float time = 1.01f)
|
||||
{
|
||||
Vector3 fr1 = new Vector3(b.max.x, b.min.y, b.max.z) * scale;
|
||||
Vector3 br1 = new Vector3(b.max.x, b.min.y, b.min.z) * scale;
|
||||
Vector3 bl1 = new Vector3(b.min.x, b.min.y, b.min.z) * scale;
|
||||
Vector3 fl1 = new Vector3(b.min.x, b.min.y, b.max.z) * scale;
|
||||
Debug.DrawLine(fr1, br1, c, time);
|
||||
Debug.DrawLine(br1, bl1, c, time);
|
||||
Debug.DrawLine(br1, bl1, c, time);
|
||||
Debug.DrawLine(bl1, fl1, c, time);
|
||||
Debug.DrawLine(fl1, fr1, c, time);
|
||||
|
||||
Vector3 fr = new Vector3(b.max.x, b.max.y, b.max.z) * scale;
|
||||
Vector3 br = new Vector3(b.max.x, b.max.y, b.min.z) * scale;
|
||||
Vector3 bl = new Vector3(b.min.x, b.max.y, b.min.z) * scale;
|
||||
Vector3 fl = new Vector3(b.min.x, b.max.y, b.max.z) * scale;
|
||||
Debug.DrawLine(fr, br, c, time);
|
||||
Debug.DrawLine(br, bl, c, time);
|
||||
Debug.DrawLine(br, bl, c, time);
|
||||
Debug.DrawLine(bl, fl, c, time);
|
||||
Debug.DrawLine(fl, fr, c, time);
|
||||
|
||||
Debug.DrawLine(fr1, fr1, c,time);
|
||||
Debug.DrawLine(br, br1, c, time);
|
||||
Debug.DrawLine(bl1, bl, c, time);
|
||||
Debug.DrawLine(fl1, fl, c, time);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ddaf37d5aa608345adae3324d191ed6
|
||||
timeCreated: 1528881308
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,178 @@
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
/// <summary>
|
||||
/// FM: Class which contains many helpful methods which operates
|
||||
/// </summary>
|
||||
public static class FStringMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// Converting int to strig
|
||||
/// </summary>
|
||||
/// <param name="value">value to show</param>
|
||||
/// <param name="signs">How many signs, value = 8, signs = 3 -> 008</param>
|
||||
public static string IntToString(this int value, int signs)
|
||||
{
|
||||
string output = value.ToString();
|
||||
|
||||
int missingZeros = signs - output.Length;
|
||||
|
||||
if (missingZeros > 0)
|
||||
{
|
||||
string missing = "0";
|
||||
|
||||
for (int i = 1; i < missingZeros; i++)
|
||||
{
|
||||
missing += 0;
|
||||
}
|
||||
|
||||
output = missing + output;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing first letter to uppercase and rest to lowercase
|
||||
/// </summary>
|
||||
public static string CapitalizeOnlyFirstLetter(this string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return text;
|
||||
|
||||
return text[0].ToString().ToUpper() + (text.Length > 1 ? text.Substring(1) : "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chanigng first letter to uppercase and rest without changes
|
||||
/// </summary>
|
||||
public static string CapitalizeFirstLetter(this string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return text;
|
||||
|
||||
return text[0].ToString().ToUpper() + text.Substring(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changing space signs to underline signs
|
||||
/// </summary>
|
||||
public static string ReplaceSpacesWithUnderline(this string text)
|
||||
{
|
||||
if (text.Contains(" "))
|
||||
{
|
||||
text = text.Replace(" ", "_");
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returning string from end to the separator, for GetEndOfStringFromSeparator("ask/ddd/aaa", new char[] { '\\', '/' }) will return "aaa"
|
||||
/// </summary>
|
||||
/// <param name="source"> Base string, from which you need only part of it </param>
|
||||
/// <param name="separators"> separators array to define, when stop checking new char[] { '\\', '/' } </param>
|
||||
/// <param name="which"> how many occurences of separator should happen </param>
|
||||
/// <param name="fromEnd"> start checking from end or from start of base string </param>
|
||||
/// <returns></returns>
|
||||
public static string GetEndOfStringFromSeparator(this string source, char[] separators, int which = 1, bool fromEnd = false)
|
||||
{
|
||||
bool separated = false;
|
||||
int counter = 0;
|
||||
int steps = 0;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (i = source.Length - 1; i >= 0; i--)
|
||||
{
|
||||
steps++;
|
||||
|
||||
for (int c = 0; c < separators.Length; c++)
|
||||
{
|
||||
if (source[i] == separators[c])
|
||||
{
|
||||
counter++;
|
||||
|
||||
if (counter == which)
|
||||
{
|
||||
i++;
|
||||
|
||||
separated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (separated) break;
|
||||
}
|
||||
|
||||
if (separated)
|
||||
{
|
||||
if (!fromEnd)
|
||||
{
|
||||
return source.Substring(0, source.Length - (steps));
|
||||
}
|
||||
else
|
||||
{
|
||||
return source.Substring(i, source.Length - i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as above GetEndOfStringFromSeparator() but here you define separators as strings, so for GetEndOfStringFromStringSeparator("ask/ddd/aaa", new string[] { "ask" }) will return "/ddd/aaa"
|
||||
/// </summary>
|
||||
public static string GetEndOfStringFromStringSeparator(this string source, string[] separators, int which = 1, bool rest = false)
|
||||
{
|
||||
bool separated = false;
|
||||
int counter = 0;
|
||||
int steps = 0;
|
||||
|
||||
int i = 0;
|
||||
for (i = 0; i < source.Length; i++)
|
||||
{
|
||||
steps++;
|
||||
|
||||
for (int c = 0; c < separators.Length; c++)
|
||||
{
|
||||
if (i + separators[c].Length > source.Length) break;
|
||||
|
||||
if (source.Substring(i, separators[c].Length) == separators[c])
|
||||
{
|
||||
counter++;
|
||||
|
||||
if (counter == which)
|
||||
{
|
||||
i++;
|
||||
|
||||
i += separators[c].Length - 1;
|
||||
|
||||
separated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (separated) break;
|
||||
}
|
||||
|
||||
if (separated)
|
||||
{
|
||||
if (rest)
|
||||
{
|
||||
return source.Substring(0, source.Length - (steps));
|
||||
}
|
||||
else
|
||||
{
|
||||
return source.Substring(i, source.Length - i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 624de49f8d4dd534ba80e893e1269f46
|
||||
timeCreated: 1508272082
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,230 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// FM: Class containing few handy easing functions
|
||||
/// </summary>
|
||||
public static class FEasing
|
||||
{
|
||||
#region Example of how to use easings
|
||||
|
||||
/// <summary>
|
||||
/// Example how to use easing function
|
||||
/// </summary>
|
||||
//static System.Collections.IEnumerator Example()
|
||||
//{
|
||||
// float timeInSecs = 3f;
|
||||
// float elapsed = 0f;
|
||||
|
||||
// Vector3 from = new Vector3(-1f, 2f, 1f);
|
||||
// Vector3 to = new Vector3(1f, 0f, 3f);
|
||||
|
||||
// // Simple example with hard defined easing function
|
||||
// while (elapsed < timeInSecs)
|
||||
// {
|
||||
// float easeValue = EaseInOutCubic(0f, 1f, elapsed / timeInSecs);
|
||||
|
||||
// Vector3 easedTargetVector = Vector3.Lerp(from, to, easeValue);
|
||||
|
||||
// yield return null;
|
||||
// }
|
||||
|
||||
// // Example with easing function to choose
|
||||
|
||||
// EFease easeType = EFease.EaseInOutElastic;
|
||||
|
||||
// float extraValue = 1.1f;
|
||||
|
||||
// while (elapsed < timeInSecs)
|
||||
// {
|
||||
// float easeValue = GetEasingFunction(easeType)(0f, 1f, elapsed / timeInSecs, extraValue);
|
||||
|
||||
// Vector3 easedTargetVector = Vector3.Lerp(from, to, easeValue);
|
||||
|
||||
// yield return null;
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
public enum EFease
|
||||
{
|
||||
EaseInCubic,
|
||||
EaseOutCubic,
|
||||
EaseInOutCubic,
|
||||
|
||||
EaseInOutElastic,
|
||||
EaseInElastic,
|
||||
EaseOutElastic,
|
||||
|
||||
EaseInExpo,
|
||||
EaseOutExpo,
|
||||
EaseInOutExpo,
|
||||
|
||||
Linear,
|
||||
}
|
||||
|
||||
#region Easing Methods
|
||||
|
||||
/// <summary>
|
||||
/// Cubic smooth ease, ignore argument is for delegate to work with extra arguments
|
||||
/// </summary>
|
||||
public static float EaseInCubic(float start, float end, float value, float ignore = 1f)
|
||||
{
|
||||
end -= start;
|
||||
return end * value * value * value + start;
|
||||
}
|
||||
|
||||
public static float EaseOutCubic(float start, float end, float value, float ignore = 1f)
|
||||
{
|
||||
value -= 1;
|
||||
end -= start;
|
||||
return end * (value * value * value + 1) + start;
|
||||
}
|
||||
|
||||
|
||||
public static float EaseInOutCubic(float start, float end, float value, float ignore = 1f)
|
||||
{
|
||||
value /= .5f;
|
||||
end -= start;
|
||||
|
||||
if (value < 1) return end * 0.5f * value * value * value + start;
|
||||
|
||||
value -= 2;
|
||||
|
||||
return end * 0.5f * (value * value * value + 2) + start;
|
||||
}
|
||||
|
||||
|
||||
public static float EaseOutElastic(float start, float end, float value, float rangeMul = 1f)
|
||||
{
|
||||
end -= start;
|
||||
|
||||
float d = 1f;
|
||||
float p = d * .3f * rangeMul;
|
||||
float s;
|
||||
float a = 0;
|
||||
|
||||
if (value == 0) return start;
|
||||
|
||||
if ((value /= d) == 1) return start + end;
|
||||
|
||||
if (a == 0f || a < Mathf.Abs(end))
|
||||
{
|
||||
a = end;
|
||||
s = p * 0.25f * rangeMul;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
|
||||
}
|
||||
|
||||
return (a * Mathf.Pow(2, -10 * value * rangeMul) * Mathf.Sin((value * d - s) * (2 * Mathf.PI) / p) + end + start);
|
||||
}
|
||||
|
||||
|
||||
public static float EaseInElastic(float start, float end, float value, float rangeMul = 1f)
|
||||
{
|
||||
end -= start;
|
||||
|
||||
float d = 1f;
|
||||
float p = d * .3f * rangeMul;
|
||||
float s;
|
||||
float a = 0;
|
||||
|
||||
if (value == 0) return start;
|
||||
|
||||
if ((value /= d) == 1) return start + end;
|
||||
|
||||
if (a == 0f || a < Mathf.Abs(end))
|
||||
{
|
||||
a = end;
|
||||
s = (p / 4) * rangeMul;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
|
||||
}
|
||||
|
||||
return -(a * Mathf.Pow(2, 10 * rangeMul * (value -= 1)) * Mathf.Sin((value * d - s) * (2 * Mathf.PI) / p)) + start;
|
||||
}
|
||||
|
||||
|
||||
public static float EaseInOutElastic(float start, float end, float value, float rangeMul = 1f)
|
||||
{
|
||||
end -= start;
|
||||
|
||||
float d = 1f;
|
||||
float p = d * .3f * rangeMul;
|
||||
float s;
|
||||
float a = 0;
|
||||
|
||||
if (value == 0) return start;
|
||||
|
||||
if ((value /= d * 0.5f) == 2) return start + end;
|
||||
|
||||
if (a == 0f || a < Mathf.Abs(end))
|
||||
{
|
||||
a = end;
|
||||
s = p / 4 * rangeMul;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
|
||||
}
|
||||
|
||||
if (value < 1) return -0.5f * (a * Mathf.Pow(2, 10 * (value -= 1)) * Mathf.Sin((value * d - s) * (2 * Mathf.PI) / p)) + start;
|
||||
return a * Mathf.Pow(2, -10 * rangeMul * (value -= 1)) * Mathf.Sin((value * d - s) * (2 * Mathf.PI) / p) * 0.5f + end + start;
|
||||
}
|
||||
|
||||
|
||||
public static float EaseInExpo(float start, float end, float value, float ignore = 1f)
|
||||
{
|
||||
end -= start;
|
||||
return end * Mathf.Pow(2, 10 * (value - 1)) + start;
|
||||
}
|
||||
|
||||
public static float EaseOutExpo(float start, float end, float value, float ignore = 1f)
|
||||
{
|
||||
end -= start;
|
||||
return end * (-Mathf.Pow(2, -10 * value) + 1) + start;
|
||||
}
|
||||
|
||||
public static float EaseInOutExpo(float start, float end, float value, float ignore = 1f)
|
||||
{
|
||||
value /= .5f;
|
||||
end -= start;
|
||||
if (value < 1) return end * 0.5f * Mathf.Pow(2, 10 * (value - 1)) + start;
|
||||
value--;
|
||||
return end * 0.5f * (-Mathf.Pow(2, -10 * value) + 2) + start;
|
||||
}
|
||||
|
||||
public static float Linear(float start, float end, float value, float ignore = 1f)
|
||||
{
|
||||
return Mathf.Lerp(start, end, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public delegate float Function(float s, float e, float v, float extraParameter = 1f);
|
||||
|
||||
public static Function GetEasingFunction(EFease easingFunction)
|
||||
{
|
||||
if (easingFunction == EFease.EaseInCubic) return EaseInCubic;
|
||||
if (easingFunction == EFease.EaseOutCubic) return EaseOutCubic;
|
||||
if (easingFunction == EFease.EaseInOutCubic) return EaseInOutCubic;
|
||||
|
||||
if (easingFunction == EFease.EaseInElastic) return EaseInElastic;
|
||||
if (easingFunction == EFease.EaseOutElastic) return EaseOutElastic;
|
||||
if (easingFunction == EFease.EaseInOutElastic) return EaseInOutElastic;
|
||||
|
||||
if (easingFunction == EFease.EaseInExpo) return EaseInExpo;
|
||||
if (easingFunction == EFease.EaseOutExpo) return EaseOutExpo;
|
||||
if (easingFunction == EFease.EaseInOutExpo) return EaseInOutExpo;
|
||||
|
||||
if (easingFunction == EFease.Linear) return Linear;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc641929bb5b9dc468f81c4d80c0cda0
|
||||
timeCreated: 1607337550
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dda743092a136d0499b79fcc38fbc515
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,155 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
/// <summary> V1.3.5
|
||||
/// FM: Base class to hold calculations on colliders for fimpossible packages
|
||||
/// </summary>
|
||||
public abstract class FImp_ColliderData_Base
|
||||
{
|
||||
public Transform Transform { get; protected set; }
|
||||
public Collider Collider { get; protected set; }
|
||||
public Collider2D Collider2D { get; protected set; }
|
||||
public bool Is2D = false;
|
||||
|
||||
public bool IsStatic { get; private set; }
|
||||
public enum EFColliderType { Box, Sphere, Capsule, Mesh, Terrain }
|
||||
public EFColliderType ColliderType { get; protected set; }
|
||||
|
||||
protected Vector3 previousPosition = Vector3.zero;
|
||||
protected Quaternion previousRotation = Quaternion.identity;
|
||||
protected Vector3 previousScale = Vector3.one;
|
||||
|
||||
/// <summary>
|
||||
/// Generating class for given collider
|
||||
/// </summary>
|
||||
public static FImp_ColliderData_Base GetColliderDataFor(Collider collider)
|
||||
{
|
||||
SphereCollider s = collider as SphereCollider;
|
||||
|
||||
if (s)
|
||||
return new FImp_ColliderData_Sphere(s);
|
||||
else
|
||||
{
|
||||
CapsuleCollider c = collider as CapsuleCollider;
|
||||
if (c)
|
||||
return new FImp_ColliderData_Capsule(c);
|
||||
else
|
||||
{
|
||||
BoxCollider b = collider as BoxCollider;
|
||||
if (b)
|
||||
return new FImp_ColliderData_Box(b);
|
||||
else
|
||||
{
|
||||
MeshCollider m = collider as MeshCollider;
|
||||
if (m)
|
||||
return new FImp_ColliderData_Mesh(m);
|
||||
else
|
||||
{
|
||||
TerrainCollider t = collider as TerrainCollider;
|
||||
if (t)
|
||||
return new FImp_ColliderData_Terrain(t);
|
||||
else
|
||||
{
|
||||
CharacterController ch = collider as CharacterController;
|
||||
if (ch)
|
||||
return new FImp_ColliderData_CharacterCapsule(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generating class for given collider
|
||||
/// </summary>
|
||||
public static FImp_ColliderData_Base GetColliderDataFor(Collider2D collider)
|
||||
{
|
||||
CircleCollider2D s = collider as CircleCollider2D;
|
||||
|
||||
if (s)
|
||||
return new FImp_ColliderData_Sphere(s);
|
||||
else
|
||||
{
|
||||
CapsuleCollider2D c = collider as CapsuleCollider2D;
|
||||
if (c)
|
||||
return new FImp_ColliderData_Capsule(c);
|
||||
else
|
||||
{
|
||||
BoxCollider2D b = collider as BoxCollider2D;
|
||||
if (b)
|
||||
return new FImp_ColliderData_Box(b);
|
||||
else
|
||||
{
|
||||
PolygonCollider2D m = collider as PolygonCollider2D;
|
||||
if (m)
|
||||
return new FImp_ColliderData_Mesh(m);
|
||||
//else
|
||||
//{
|
||||
// EdgeCollider2D e = collider as EdgeCollider2D;
|
||||
// if (e)
|
||||
// return new FImp_ColliderData_Mesh(e);
|
||||
// else
|
||||
// {
|
||||
// TilemapCollider2D t = collider as TilemapCollider2D;
|
||||
// if (t)
|
||||
// return new FImp_ColliderData_Mesh(t);
|
||||
// else
|
||||
// {
|
||||
// CompositeCollider2D cps = collider as CompositeCollider2D;
|
||||
// if (cps)
|
||||
// return new FImp_ColliderData_Mesh(cps);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// When collider moves / rotates / scales this method should be called
|
||||
/// </summary>
|
||||
public virtual void RefreshColliderData()
|
||||
{
|
||||
if (Transform.gameObject.isStatic) IsStatic = true; else IsStatic = false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Detecting if given point (sphere) is inside collider or colliding with (for mesh collider)
|
||||
/// and projecting it onto collider's surface
|
||||
/// </summary>
|
||||
/// <param name="point"> Position of colliding sphere which will be pushed out </param>
|
||||
/// <param name="pointRadius"> Radius of colliding sphere </param>
|
||||
/// <param name="pointOffset"> Offset in position of colliding sphere </param>
|
||||
/// <returns></returns>
|
||||
public virtual bool PushIfInside(ref Vector3 point, float pointRadius, Vector3 pointOffset)
|
||||
{
|
||||
if ( Collider as SphereCollider )
|
||||
Debug.Log("It shouldn't appear");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If not implemented 3D algorithm will be applied
|
||||
/// </summary>
|
||||
public virtual bool PushIfInside2D(ref Vector3 point, float pointRadius, Vector3 pointOffset)
|
||||
{
|
||||
return PushIfInside(ref point, pointRadius, pointOffset);
|
||||
}
|
||||
|
||||
|
||||
public static bool VIsSame(Vector3 vec1, Vector3 vec2)
|
||||
{
|
||||
if (vec1.x != vec2.x) return false; if (vec1.y != vec2.y) return false; if (vec1.z != vec2.z) return false; return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2eae3be7652c13f4badbace952b305ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 4ba1f7a7d53fd274c98bdce0c9242640, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,719 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
public class FImp_ColliderData_Box : FImp_ColliderData_Base
|
||||
{
|
||||
public BoxCollider Box { get; private set; }
|
||||
public BoxCollider2D Box2D { get; private set; }
|
||||
|
||||
private Vector3 boxCenter;
|
||||
|
||||
private Vector3 right;
|
||||
private Vector3 up;
|
||||
private Vector3 forward;
|
||||
|
||||
private Vector3 rightN;
|
||||
private Vector3 upN;
|
||||
private Vector3 forwardN;
|
||||
|
||||
private Vector3 scales;
|
||||
|
||||
// For 3D
|
||||
public FImp_ColliderData_Box(BoxCollider collider)
|
||||
{
|
||||
Is2D = false;
|
||||
Collider = collider;
|
||||
Transform = collider.transform;
|
||||
Box = collider;
|
||||
ColliderType = EFColliderType.Box;
|
||||
RefreshColliderData();
|
||||
previousPosition = Transform.position + Vector3.forward * Mathf.Epsilon;
|
||||
}
|
||||
|
||||
// For 2D
|
||||
public FImp_ColliderData_Box(BoxCollider2D collider2D)
|
||||
{
|
||||
Is2D = true;
|
||||
Collider2D = collider2D;
|
||||
Transform = collider2D.transform;
|
||||
Box2D = collider2D;
|
||||
ColliderType = EFColliderType.Box;
|
||||
RefreshColliderData();
|
||||
previousPosition = Transform.position + Vector3.forward * Mathf.Epsilon;
|
||||
}
|
||||
|
||||
|
||||
#region Refreshing Data
|
||||
|
||||
|
||||
public override void RefreshColliderData()
|
||||
{
|
||||
if (IsStatic) return; // No need to refresh collider data if it is static
|
||||
|
||||
if (Collider2D == null) // 3D Refresh
|
||||
{
|
||||
bool diff = false;
|
||||
|
||||
if (!FEngineering.VIsSame(Transform.position, previousPosition)) diff = true;
|
||||
else
|
||||
if (!FEngineering.QIsSame(Transform.rotation, previousRotation)) diff = true;
|
||||
|
||||
if (diff)
|
||||
{
|
||||
right = Box.transform.TransformVector((Vector3.right / 2f) * Box.size.x);
|
||||
up = Box.transform.TransformVector((Vector3.up / 2f) * Box.size.y);
|
||||
forward = Box.transform.TransformVector((Vector3.forward / 2f) * Box.size.z);
|
||||
|
||||
rightN = right.normalized;
|
||||
upN = up.normalized;
|
||||
forwardN = forward.normalized;
|
||||
|
||||
boxCenter = GetBoxCenter(Box);
|
||||
|
||||
scales = Vector3.Scale(Box.size, Box.transform.lossyScale);
|
||||
scales.Normalize();
|
||||
}
|
||||
}
|
||||
else // 2D Refresh
|
||||
{
|
||||
bool diff = false;
|
||||
|
||||
if (Vector2.Distance(Transform.position, previousPosition) > Mathf.Epsilon) { diff = true; }
|
||||
else
|
||||
if (!FEngineering.QIsSame(Transform.rotation, previousRotation)) { diff = true; }
|
||||
|
||||
if (diff)
|
||||
{
|
||||
right = Box2D.transform.TransformVector((Vector3.right / 2f) * Box2D.size.x);
|
||||
up = Box2D.transform.TransformVector((Vector3.up / 2f) * Box2D.size.y);
|
||||
|
||||
rightN = right.normalized;
|
||||
upN = up.normalized;
|
||||
|
||||
boxCenter = GetBoxCenter(Box2D);
|
||||
boxCenter.z = 0f;
|
||||
|
||||
Vector3 scale = Transform.lossyScale; scale.z = 1f;
|
||||
scales = Vector3.Scale(Box2D.size, scale);
|
||||
scales.Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
base.RefreshColliderData();
|
||||
|
||||
previousPosition = Transform.position;
|
||||
previousRotation = Transform.rotation;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public override bool PushIfInside(ref Vector3 segmentPosition, float segmentRadius, Vector3 segmentOffset)
|
||||
{
|
||||
int inOrInt = 0;
|
||||
Vector3 interPlane = Vector3.zero;
|
||||
Vector3 segmentOffsetted = segmentPosition + segmentOffset;
|
||||
float planeDistance = PlaneDistance(boxCenter + up, upN, segmentOffsetted);
|
||||
if (SphereInsidePlane(planeDistance, segmentRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, segmentRadius)) { inOrInt++; interPlane = up; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter - up, -upN, segmentOffsetted);
|
||||
if (SphereInsidePlane(planeDistance, segmentRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, segmentRadius)) { inOrInt++; interPlane = -up; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter - right, -rightN, segmentOffsetted);
|
||||
if (SphereInsidePlane(planeDistance, segmentRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, segmentRadius)) { inOrInt++; interPlane = -right; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter + right, rightN, segmentOffsetted);
|
||||
if (SphereInsidePlane(planeDistance, segmentRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, segmentRadius)) { inOrInt++; interPlane = right; }
|
||||
|
||||
bool insideOrIntersects = false;
|
||||
|
||||
if (Collider2D == null)
|
||||
{
|
||||
planeDistance = PlaneDistance(boxCenter + forward, forwardN, segmentOffsetted);
|
||||
if (SphereInsidePlane(planeDistance, segmentRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, segmentRadius)) { inOrInt++; interPlane = forward; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter - forward, -forwardN, segmentOffsetted);
|
||||
if (SphereInsidePlane(planeDistance, segmentRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, segmentRadius)) { inOrInt++; interPlane = -forward; }
|
||||
|
||||
if (inOrInt == 6) insideOrIntersects = true;
|
||||
}
|
||||
else if (inOrInt == 4) insideOrIntersects = true;
|
||||
|
||||
if (insideOrIntersects)
|
||||
{
|
||||
bool inside = false;
|
||||
//Vector3 rayDirection;
|
||||
|
||||
if (interPlane.sqrMagnitude == 0f) // sphere is inside the box
|
||||
{
|
||||
//if ( Collider2D == null)
|
||||
// interPlane = -GetTargetPlaneNormal(Box, segmentOffsetted, right, up, forward, scales);
|
||||
//else
|
||||
// interPlane = -GetTargetPlaneNormal(Box2D, segmentOffsetted, right, up, scales);
|
||||
inside = true;
|
||||
//rayDirection = (interPlane).normalized; // poprawić przy przeskalowanych boxach
|
||||
}
|
||||
else // sphere is intersecting box
|
||||
{
|
||||
//rayDirection = (segmentOffsetted - boxCenter).normalized;
|
||||
if (Collider2D == null)
|
||||
{ if (IsInsideBoxCollider(Box, segmentOffsetted)) inside = true; }
|
||||
else if (IsInsideBoxCollider(Box2D, segmentOffsetted)) inside = true;
|
||||
}
|
||||
|
||||
Vector3 pointOnPlane = GetNearestPoint(segmentOffsetted);
|
||||
Vector3 toNormal = pointOnPlane - segmentOffsetted;
|
||||
|
||||
if (inside) toNormal += toNormal.normalized * segmentRadius; else toNormal -= toNormal.normalized * segmentRadius;
|
||||
//Debug.DrawRay(pointOnPlane, toNormal);
|
||||
|
||||
if (inside)
|
||||
{
|
||||
segmentPosition = segmentPosition + toNormal;
|
||||
}
|
||||
else
|
||||
if (toNormal.sqrMagnitude > 0) segmentPosition = segmentPosition + toNormal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static void PushOutFromBoxCollider(BoxCollider box, Collision collision, float segmentColliderRadius, ref Vector3 segmentPosition, bool is2D = false)
|
||||
{
|
||||
Vector3 right = box.transform.TransformVector((Vector3.right / 2f) * box.size.x + box.center.x * Vector3.right);
|
||||
Vector3 up = box.transform.TransformVector((Vector3.up / 2f) * box.size.y + box.center.y * Vector3.up);
|
||||
Vector3 forward = box.transform.TransformVector((Vector3.forward / 2f) * box.size.z + box.center.z * Vector3.forward);
|
||||
|
||||
Vector3 scales = Vector3.Scale(box.size, box.transform.lossyScale);
|
||||
scales.Normalize();
|
||||
|
||||
PushOutFromBoxCollider(box, collision, segmentColliderRadius, ref segmentPosition, right, up, forward, scales, is2D);
|
||||
}
|
||||
|
||||
public static void PushOutFromBoxCollider(BoxCollider box, float segmentColliderRadius, ref Vector3 segmentPosition, bool is2D = false)
|
||||
{
|
||||
Vector3 right = box.transform.TransformVector((Vector3.right / 2f) * box.size.x + box.center.x * Vector3.right);
|
||||
Vector3 up = box.transform.TransformVector((Vector3.up / 2f) * box.size.y + box.center.y * Vector3.up);
|
||||
Vector3 forward = box.transform.TransformVector((Vector3.forward / 2f) * box.size.z + box.center.z * Vector3.forward);
|
||||
|
||||
Vector3 scales = Vector3.Scale(box.size, box.transform.lossyScale);
|
||||
scales.Normalize();
|
||||
|
||||
Vector3 boxCenter = GetBoxCenter(box);
|
||||
|
||||
float pointRadius = segmentColliderRadius;
|
||||
Vector3 upN = up.normalized; Vector3 rightN = right.normalized; Vector3 forwardN = forward.normalized;
|
||||
|
||||
int inOrInt = 0;
|
||||
Vector3 interPlane = Vector3.zero;
|
||||
float planeDistance = PlaneDistance(boxCenter + up, upN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = up; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter - up, -upN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = -up; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter - right, -rightN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = -right; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter + right, rightN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = right; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter + forward, forwardN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = forward; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter - forward, -forwardN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = -forward; }
|
||||
|
||||
// Collision occured - sphere intersecting box shape volume or is inside of it
|
||||
if (inOrInt == 6)
|
||||
{
|
||||
bool inside = false;
|
||||
//Vector3 rayDirection;
|
||||
|
||||
if (interPlane.sqrMagnitude == 0f) // sphere is inside the box
|
||||
{
|
||||
//interPlane = -GetTargetPlaneNormal(box, segmentPosition, right, up, forward, scales, is2D);
|
||||
inside = true;
|
||||
//rayDirection = (interPlane).normalized; // poprawić przy przeskalowanych boxach
|
||||
}
|
||||
else // sphere is intersecting box
|
||||
{
|
||||
//rayDirection = (segmentPosition - boxCenter).normalized;
|
||||
if (IsInsideBoxCollider(box, segmentPosition)) inside = true;
|
||||
}
|
||||
|
||||
Vector3 pointOnPlane = GetNearestPoint(segmentPosition, boxCenter, right, up, forward, is2D);
|
||||
|
||||
Vector3 toNormal = pointOnPlane - segmentPosition;
|
||||
if (inside) toNormal += toNormal.normalized * pointRadius * 1.01f; else toNormal -= toNormal.normalized * pointRadius * 1.01f;
|
||||
|
||||
if (inside)
|
||||
{
|
||||
segmentPosition = segmentPosition + toNormal;
|
||||
}
|
||||
else
|
||||
if (toNormal.sqrMagnitude > 0)
|
||||
{
|
||||
segmentPosition = segmentPosition + toNormal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void PushOutFromBoxCollider(BoxCollider box, Collision collision, float segmentColliderRadius, ref Vector3 pos, Vector3 right, Vector3 up, Vector3 forward, Vector3 scales, bool is2D = false)
|
||||
{
|
||||
Vector3 collisionPoint = collision.contacts[0].point;
|
||||
Vector3 pushNormal = pos - collisionPoint;
|
||||
Vector3 boxCenter = GetBoxCenter(box);
|
||||
if (pushNormal.sqrMagnitude == 0f) pushNormal = pos - boxCenter;
|
||||
|
||||
float insideMul = 1f;
|
||||
if (IsInsideBoxCollider(box, pos))
|
||||
{
|
||||
// Finding intersection point on the box from the inside
|
||||
float castFactor = GetBoxAverageScale(box);
|
||||
Vector3 fittingNormal = GetTargetPlaneNormal(box, pos, right, up, forward, scales);
|
||||
Vector3 fittingNormalNorm = fittingNormal.normalized;
|
||||
|
||||
RaycastHit info;
|
||||
// Doing cheap boxCollider's raycast from outside to hit surface
|
||||
if (box.Raycast(new Ray(pos - fittingNormalNorm * castFactor * 3f, fittingNormalNorm), out info, castFactor * 4))
|
||||
{
|
||||
collisionPoint = info.point;
|
||||
}
|
||||
else
|
||||
collisionPoint = GetIntersectOnBoxFromInside(box, boxCenter, pos, fittingNormal);
|
||||
|
||||
pushNormal = collisionPoint - pos;
|
||||
insideMul = 100f;
|
||||
}
|
||||
|
||||
Vector3 toNormal = pos - ((pushNormal / insideMul + pushNormal.normalized * 1.15f) / 2f) * (segmentColliderRadius);
|
||||
toNormal = collisionPoint - toNormal;
|
||||
|
||||
float pushMagn = toNormal.sqrMagnitude;
|
||||
if (pushMagn > 0 && pushMagn < segmentColliderRadius * segmentColliderRadius * insideMul) pos = pos + toNormal;
|
||||
}
|
||||
|
||||
#region Push out from box 2D
|
||||
|
||||
public static void PushOutFromBoxCollider(BoxCollider2D box2D, float segmentColliderRadius, ref Vector3 segmentPosition)
|
||||
{
|
||||
Vector2 right = box2D.transform.TransformVector((Vector3.right / 2f) * box2D.size.x + box2D.offset.x * Vector3.right);
|
||||
Vector2 up = box2D.transform.TransformVector((Vector3.up / 2f) * box2D.size.y + box2D.offset.y * Vector3.up);
|
||||
|
||||
Vector3 scale2D = box2D.transform.lossyScale; scale2D.z = 1f;
|
||||
Vector2 scales = Vector3.Scale(box2D.size, scale2D);
|
||||
scales.Normalize();
|
||||
|
||||
Vector2 boxCenter = GetBoxCenter(box2D);
|
||||
|
||||
float pointRadius = segmentColliderRadius;
|
||||
Vector2 upN = up.normalized; Vector2 rightN = right.normalized;
|
||||
|
||||
int inOrInt = 0;
|
||||
Vector3 interPlane = Vector3.zero;
|
||||
float planeDistance = PlaneDistance(boxCenter + up, upN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = up; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter - up, -upN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = -up; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter - right, -rightN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = -right; }
|
||||
|
||||
planeDistance = PlaneDistance(boxCenter + right, rightN, segmentPosition);
|
||||
if (SphereInsidePlane(planeDistance, pointRadius)) inOrInt++; else if (SphereIntersectsPlane(planeDistance, pointRadius)) { inOrInt++; interPlane = right; }
|
||||
|
||||
// Collision occured - sphere intersecting box shape volume or is inside of it
|
||||
if (inOrInt == 4)
|
||||
{
|
||||
bool inside = false;
|
||||
|
||||
if (interPlane.sqrMagnitude == 0f) // sphere is inside the box
|
||||
{
|
||||
//interPlane = -GetTargetPlaneNormal(box2D, segmentPosition, right, up, scales);
|
||||
inside = true;
|
||||
}
|
||||
else // sphere is intersecting box
|
||||
{
|
||||
if (IsInsideBoxCollider(box2D, segmentPosition)) inside = true;
|
||||
}
|
||||
|
||||
Vector3 pointOnPlane = GetNearestPoint2D(segmentPosition, boxCenter, right, up);
|
||||
|
||||
Vector3 toNormal = pointOnPlane - segmentPosition;
|
||||
if (inside) toNormal += toNormal.normalized * pointRadius * 1.01f; else toNormal -= toNormal.normalized * pointRadius * 1.01f;
|
||||
|
||||
if (inside)
|
||||
segmentPosition = segmentPosition + toNormal;
|
||||
else
|
||||
if (toNormal.sqrMagnitude > 0) segmentPosition = segmentPosition + toNormal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Box Calculations Helpers
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Getting nearest plane normal fitting to given point position
|
||||
/// </summary>
|
||||
private Vector3 GetNearestPoint(Vector3 point)
|
||||
{
|
||||
Vector3 pointOnBox = point;
|
||||
|
||||
Vector3 distancesPositive = Vector3.one;
|
||||
distancesPositive.x = PlaneDistance(boxCenter + right, rightN, point);
|
||||
distancesPositive.y = PlaneDistance(boxCenter + up, upN, point);
|
||||
if (Collider2D == null) distancesPositive.z = PlaneDistance(boxCenter + forward, forwardN, point);
|
||||
|
||||
Vector3 distancesNegative = Vector3.one;
|
||||
distancesNegative.x = PlaneDistance(boxCenter - right, -rightN, point);
|
||||
distancesNegative.y = PlaneDistance(boxCenter - up, -upN, point);
|
||||
if (Collider2D == null) distancesNegative.z = PlaneDistance(boxCenter - forward, -forwardN, point);
|
||||
|
||||
float nearestX, nearestY, nearestZ;
|
||||
float negX = 1f, negY = 1f, negZ = 1f;
|
||||
|
||||
if (distancesPositive.x > distancesNegative.x) { nearestX = distancesPositive.x; negX = -1f; } else { nearestX = distancesNegative.x; negX = 1f; }
|
||||
if (distancesPositive.y > distancesNegative.y) { nearestY = distancesPositive.y; negY = -1f; } else { nearestY = distancesNegative.y; negY = 1f; }
|
||||
|
||||
if (Collider2D == null)
|
||||
{
|
||||
if (distancesPositive.z > distancesNegative.z) { nearestZ = distancesPositive.z; negZ = -1f; } else { nearestZ = distancesNegative.z; negZ = 1f; }
|
||||
if (nearestX > nearestZ)
|
||||
{
|
||||
if (nearestX > nearestY) { pointOnBox = ProjectPointOnPlane(right * negX, point, nearestX); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nearestZ > nearestY) { pointOnBox = ProjectPointOnPlane(forward * negZ, point, nearestZ); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nearestX > nearestY) { pointOnBox = ProjectPointOnPlane(right * negX, point, nearestX); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
|
||||
|
||||
return pointOnBox;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting nearest plane normal fitting to given point position
|
||||
/// </summary>
|
||||
private static Vector3 GetNearestPoint(Vector3 point, Vector3 boxCenter, Vector3 right, Vector3 up, Vector3 forward, bool is2D = false)
|
||||
{
|
||||
Vector3 pointOnBox = point;
|
||||
|
||||
Vector3 distancesPositive = Vector3.one;
|
||||
distancesPositive.x = PlaneDistance(boxCenter + right, right.normalized, point);
|
||||
distancesPositive.y = PlaneDistance(boxCenter + up, up.normalized, point);
|
||||
if (is2D == false) distancesPositive.z = PlaneDistance(boxCenter + forward, forward.normalized, point);
|
||||
|
||||
Vector3 distancesNegative = Vector3.one;
|
||||
distancesNegative.x = PlaneDistance(boxCenter - right, -right.normalized, point);
|
||||
distancesNegative.y = PlaneDistance(boxCenter - up, -up.normalized, point);
|
||||
if (is2D == false) distancesNegative.z = PlaneDistance(boxCenter - forward, -forward.normalized, point);
|
||||
|
||||
float nearestX, nearestY, nearestZ;
|
||||
float negX = 1f, negY = 1f, negZ = 1f;
|
||||
|
||||
if (distancesPositive.x > distancesNegative.x) { nearestX = distancesPositive.x; negX = -1f; } else { nearestX = distancesNegative.x; negX = 1f; }
|
||||
if (distancesPositive.y > distancesNegative.y) { nearestY = distancesPositive.y; negY = -1f; } else { nearestY = distancesNegative.y; negY = 1f; }
|
||||
|
||||
if (is2D == false)
|
||||
{
|
||||
if (distancesPositive.z > distancesNegative.z) { nearestZ = distancesPositive.z; negZ = -1f; } else { nearestZ = distancesNegative.z; negZ = 1f; }
|
||||
|
||||
if (nearestX > nearestZ)
|
||||
{
|
||||
if (nearestX > nearestY) { pointOnBox = ProjectPointOnPlane(right * negX, point, nearestX); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nearestZ > nearestY) { pointOnBox = ProjectPointOnPlane(forward * negZ, point, nearestZ); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nearestX > nearestY) { pointOnBox = ProjectPointOnPlane(right * negX, point, nearestX); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
|
||||
return pointOnBox;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting nearest plane normal fitting to given point position
|
||||
/// </summary>
|
||||
private static Vector3 GetNearestPoint2D(Vector2 point, Vector2 boxCenter, Vector2 right, Vector2 up)
|
||||
{
|
||||
Vector3 pointOnBox = point;
|
||||
|
||||
Vector3 distancesPositive = Vector3.one;
|
||||
distancesPositive.x = PlaneDistance(boxCenter + right, right.normalized, point);
|
||||
distancesPositive.y = PlaneDistance(boxCenter + up, up.normalized, point);
|
||||
|
||||
Vector3 distancesNegative = Vector3.one;
|
||||
distancesNegative.x = PlaneDistance(boxCenter - right, -right.normalized, point);
|
||||
distancesNegative.y = PlaneDistance(boxCenter - up, -up.normalized, point);
|
||||
|
||||
float nearestX, nearestY;
|
||||
float negX = 1f, negY = 1f;
|
||||
|
||||
if (distancesPositive.x > distancesNegative.x) { nearestX = distancesPositive.x; negX = -1f; } else { nearestX = distancesNegative.x; negX = 1f; }
|
||||
if (distancesPositive.y > distancesNegative.y) { nearestY = distancesPositive.y; negY = -1f; } else { nearestY = distancesNegative.y; negY = 1f; }
|
||||
|
||||
if (nearestX > nearestY) { pointOnBox = ProjectPointOnPlane(right * negX, point, nearestX); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
|
||||
return pointOnBox;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Getting nearest plane point on box collider
|
||||
/// </summary>
|
||||
public static Vector3 GetNearestPointOnBox(BoxCollider boxCollider, Vector3 point, bool is2D = false)
|
||||
{
|
||||
Vector3 right = boxCollider.transform.TransformVector(Vector3.right / 2f);
|
||||
Vector3 up = boxCollider.transform.TransformVector(Vector3.up / 2f);
|
||||
Vector3 forward = Vector3.forward; if (is2D == false) forward = boxCollider.transform.TransformVector(Vector3.forward / 2f);
|
||||
|
||||
Vector3 pointOnBox = point;
|
||||
Vector3 center = GetBoxCenter(boxCollider);
|
||||
|
||||
Vector3 rightN = right.normalized;
|
||||
Vector3 upN = up.normalized;
|
||||
Vector3 forwardN = forward.normalized;
|
||||
|
||||
Vector3 distancesPositive = Vector3.one;
|
||||
distancesPositive.x = PlaneDistance(center + right, rightN, point);
|
||||
distancesPositive.y = PlaneDistance(center + up, upN, point);
|
||||
if (is2D == false) distancesPositive.z = PlaneDistance(center + forward, forwardN, point);
|
||||
|
||||
Vector3 distancesNegative = Vector3.one;
|
||||
distancesNegative.x = PlaneDistance(center - right, -rightN, point);
|
||||
distancesNegative.y = PlaneDistance(center - up, -upN, point);
|
||||
if (is2D == false) distancesNegative.z = PlaneDistance(center - forward, -forwardN, point);
|
||||
|
||||
float nearestX, nearestY, nearestZ;
|
||||
float negX = 1f, negY = 1f, negZ = 1f;
|
||||
|
||||
if (distancesPositive.x > distancesNegative.x) { nearestX = distancesPositive.x; negX = -1f; } else { nearestX = distancesNegative.x; negX = 1f; }
|
||||
if (distancesPositive.y > distancesNegative.y) { nearestY = distancesPositive.y; negY = -1f; } else { nearestY = distancesNegative.y; negY = 1f; }
|
||||
|
||||
if (is2D == false)
|
||||
{
|
||||
if (distancesPositive.z > distancesNegative.z) { nearestZ = distancesPositive.z; negZ = -1f; } else { nearestZ = distancesNegative.z; negZ = 1f; }
|
||||
|
||||
if (nearestX > nearestZ)
|
||||
{
|
||||
if (nearestX > nearestY) { pointOnBox = ProjectPointOnPlane(right * negX, point, nearestX); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nearestZ > nearestY) { pointOnBox = ProjectPointOnPlane(forward * negZ, point, nearestZ); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nearestX > nearestY) { pointOnBox = ProjectPointOnPlane(right * negX, point, nearestX); }
|
||||
else
|
||||
pointOnBox = ProjectPointOnPlane(up * negY, point, nearestY);
|
||||
}
|
||||
|
||||
|
||||
return pointOnBox;
|
||||
}
|
||||
|
||||
|
||||
private static float PlaneDistance(Vector3 planeCenter, Vector3 planeNormal, Vector3 point)
|
||||
{
|
||||
return Vector3.Dot(point - planeCenter, planeNormal);
|
||||
}
|
||||
|
||||
private static Vector3 ProjectPointOnPlane(Vector3 planeNormal, Vector3 point, float distance)
|
||||
{
|
||||
Vector3 translationVector = planeNormal.normalized * distance;
|
||||
return point + translationVector;
|
||||
}
|
||||
|
||||
private static bool SphereInsidePlane(float planeDistance, float pointRadius) { return -planeDistance > pointRadius; }
|
||||
private static bool SphereOutsidePlane(float planeDistance, float pointRadius) { return planeDistance > pointRadius; }
|
||||
private static bool SphereIntersectsPlane(float planeDistance, float pointRadius) { return Mathf.Abs(planeDistance) <= pointRadius; }
|
||||
|
||||
|
||||
public static bool IsInsideBoxCollider(BoxCollider collider, Vector3 point, bool is2D = false)
|
||||
{
|
||||
point = collider.transform.InverseTransformPoint(point) - collider.center;
|
||||
|
||||
float xExtend = (collider.size.x * 0.5f);
|
||||
float yExtend = (collider.size.y * 0.5f);
|
||||
float zExtend = (collider.size.z * 0.5f);
|
||||
return (point.x < xExtend && point.x > -xExtend && point.y < yExtend && point.y > -yExtend && point.z < zExtend && point.z > -zExtend);
|
||||
}
|
||||
|
||||
// 2D Version
|
||||
public static bool IsInsideBoxCollider(BoxCollider2D collider, Vector3 point)
|
||||
{
|
||||
point = (Vector2)collider.transform.InverseTransformPoint(point) - collider.offset;
|
||||
|
||||
float xExtend = (collider.size.x * 0.5f);
|
||||
float yExtend = (collider.size.y * 0.5f);
|
||||
|
||||
return (point.x < xExtend && point.x > -xExtend && point.y < yExtend && point.y > -yExtend);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Getting average scale of box's dimensions
|
||||
/// </summary>
|
||||
protected static float GetBoxAverageScale(BoxCollider box)
|
||||
{
|
||||
Vector3 scales = box.transform.lossyScale;
|
||||
scales = Vector3.Scale(scales, box.size);
|
||||
return (scales.x + scales.y + scales.z) / 3f;
|
||||
}
|
||||
|
||||
protected static Vector3 GetBoxCenter(BoxCollider box)
|
||||
{
|
||||
return box.transform.position + box.transform.TransformVector(box.center);
|
||||
}
|
||||
|
||||
protected static Vector3 GetBoxCenter(BoxCollider2D box)
|
||||
{
|
||||
return box.transform.position + box.transform.TransformVector(box.offset);
|
||||
}
|
||||
|
||||
protected static Vector3 GetTargetPlaneNormal(BoxCollider boxCollider, Vector3 point, bool is2D = false)
|
||||
{
|
||||
Vector3 right = boxCollider.transform.TransformVector((Vector3.right / 2f) * boxCollider.size.x);
|
||||
Vector3 up = boxCollider.transform.TransformVector((Vector3.up / 2f) * boxCollider.size.y);
|
||||
Vector3 forward = Vector3.forward; if (is2D == false) forward = boxCollider.transform.TransformVector((Vector3.forward / 2f) * boxCollider.size.z);
|
||||
|
||||
Vector3 scales = Vector3.Scale(boxCollider.size, boxCollider.transform.lossyScale);
|
||||
scales.Normalize();
|
||||
|
||||
return GetTargetPlaneNormal(boxCollider, point, right, up, forward, scales, is2D);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting nearest plane normal fitting to given point position
|
||||
/// </summary>
|
||||
protected static Vector3 GetTargetPlaneNormal(BoxCollider boxCollider, Vector3 point, Vector3 right, Vector3 up, Vector3 forward, Vector3 scales, bool is2D = false)
|
||||
{
|
||||
Vector3 rayDirection = (GetBoxCenter(boxCollider) - point).normalized;
|
||||
|
||||
// Finding proper box's plane
|
||||
Vector3 dots;
|
||||
dots.x = Vector3.Dot(rayDirection, right.normalized);
|
||||
dots.y = Vector3.Dot(rayDirection, up.normalized);
|
||||
dots.x = dots.x * scales.y * scales.z;
|
||||
dots.y = dots.y * scales.x * scales.z;
|
||||
|
||||
if (is2D == false)
|
||||
{
|
||||
dots.z = Vector3.Dot(rayDirection, forward.normalized);
|
||||
dots.z = dots.z * scales.y * scales.x;
|
||||
}
|
||||
else dots.z = 0;
|
||||
|
||||
dots.Normalize();
|
||||
|
||||
Vector3 dotsAbs = dots;
|
||||
if (dots.x < 0) dotsAbs.x = -dots.x;
|
||||
if (dots.y < 0) dotsAbs.y = -dots.y;
|
||||
if (dots.z < 0) dotsAbs.z = -dots.z;
|
||||
|
||||
Vector3 planeNormal;
|
||||
if (dotsAbs.x > dotsAbs.y)
|
||||
{
|
||||
if (dotsAbs.x > dotsAbs.z || is2D) planeNormal = right * Mathf.Sign(dots.x); else planeNormal = forward * Mathf.Sign(dots.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dotsAbs.y > dotsAbs.z || is2D) planeNormal = up * Mathf.Sign(dots.y); else planeNormal = forward * Mathf.Sign(dots.z);
|
||||
}
|
||||
|
||||
return planeNormal;
|
||||
}
|
||||
|
||||
|
||||
// 2D Version
|
||||
protected static Vector3 GetTargetPlaneNormal(BoxCollider2D boxCollider, Vector2 point, Vector2 right, Vector2 up, Vector2 scales)
|
||||
{
|
||||
Vector2 rayDirection = ((Vector2)GetBoxCenter(boxCollider) - point).normalized;
|
||||
|
||||
// Finding proper box's plane
|
||||
Vector2 dots;
|
||||
dots.x = Vector3.Dot(rayDirection, right.normalized);
|
||||
dots.y = Vector3.Dot(rayDirection, up.normalized);
|
||||
dots.x = dots.x * scales.y;
|
||||
dots.y = dots.y * scales.x;
|
||||
|
||||
dots.Normalize();
|
||||
|
||||
Vector2 dotsAbs = dots;
|
||||
if (dots.x < 0) dotsAbs.x = -dots.x;
|
||||
if (dots.y < 0) dotsAbs.y = -dots.y;
|
||||
|
||||
Vector3 planeNormal;
|
||||
if (dotsAbs.x > dotsAbs.y) planeNormal = right * Mathf.Sign(dots.x);
|
||||
else
|
||||
planeNormal = up * Mathf.Sign(dots.y);
|
||||
|
||||
return planeNormal;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Calculating cheap ray on box plane to detect position from inside
|
||||
/// </summary>
|
||||
protected static Vector3 GetIntersectOnBoxFromInside(BoxCollider boxCollider, Vector3 from, Vector3 to, Vector3 planeNormal)
|
||||
{
|
||||
Vector3 rayDirection = (to - from);
|
||||
|
||||
// Creating box's plane and casting cheap ray on it to detect intersection position
|
||||
Plane plane = new Plane(-planeNormal, GetBoxCenter(boxCollider) + planeNormal);
|
||||
Vector3 intersectionPoint = to;
|
||||
|
||||
float enter = 0f;
|
||||
Ray ray = new Ray(from, rayDirection);
|
||||
if (plane.Raycast(ray, out enter)) intersectionPoint = ray.GetPoint(enter);
|
||||
|
||||
return intersectionPoint;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d88eaa8955b2dd4fa35f5005531a210
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 4ba1f7a7d53fd274c98bdce0c9242640, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,241 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
public class FImp_ColliderData_Capsule : FImp_ColliderData_Base
|
||||
{
|
||||
public CapsuleCollider Capsule { get; private set; }
|
||||
public CapsuleCollider2D Capsule2D { get; private set; }
|
||||
private Vector3 Top;
|
||||
private Vector3 Bottom;
|
||||
private Vector3 Direction;
|
||||
private float radius;
|
||||
private float scaleFactor;
|
||||
private float preRadius;
|
||||
|
||||
public FImp_ColliderData_Capsule(CapsuleCollider collider)
|
||||
{
|
||||
Is2D = false;
|
||||
Transform = collider.transform;
|
||||
Collider = collider;
|
||||
Transform = collider.transform;
|
||||
Capsule = collider;
|
||||
ColliderType = EFColliderType.Capsule;
|
||||
CalculateCapsuleParameters(Capsule, ref Direction, ref radius, ref scaleFactor);
|
||||
RefreshColliderData();
|
||||
}
|
||||
|
||||
public FImp_ColliderData_Capsule(CapsuleCollider2D collider)
|
||||
{
|
||||
Is2D = true;
|
||||
Transform = collider.transform;
|
||||
Collider2D = collider;
|
||||
Transform = collider.transform;
|
||||
Capsule2D = collider;
|
||||
ColliderType = EFColliderType.Capsule;
|
||||
CalculateCapsuleParameters(Capsule2D, ref Direction, ref radius, ref scaleFactor);
|
||||
RefreshColliderData();
|
||||
}
|
||||
|
||||
public override void RefreshColliderData()
|
||||
{
|
||||
if (IsStatic) return; // No need to refresh collider data if it is static
|
||||
|
||||
bool diff = false;
|
||||
|
||||
if (!FEngineering.VIsSame(previousPosition, Transform.position)) diff = true;
|
||||
else
|
||||
if (!FEngineering.QIsSame(Transform.rotation, previousRotation)) diff = true;
|
||||
else
|
||||
{
|
||||
if (Is2D == false)
|
||||
{
|
||||
if (preRadius != Capsule.radius || !FEngineering.VIsSame(previousScale, Transform.lossyScale))
|
||||
CalculateCapsuleParameters(Capsule, ref Direction, ref radius, ref scaleFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preRadius != GetCapsule2DRadius(Capsule2D) || !FEngineering.VIsSame(previousScale, Transform.lossyScale))
|
||||
CalculateCapsuleParameters(Capsule2D, ref Direction, ref radius, ref scaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
if (diff)
|
||||
{
|
||||
if (Is2D == false)
|
||||
GetCapsuleHeadsPositions(Capsule, ref Top, ref Bottom, Direction, radius, scaleFactor);
|
||||
else
|
||||
GetCapsuleHeadsPositions(Capsule2D, ref Top, ref Bottom, Direction, radius, scaleFactor);
|
||||
}
|
||||
|
||||
base.RefreshColliderData();
|
||||
|
||||
previousPosition = Transform.position;
|
||||
previousRotation = Transform.rotation;
|
||||
previousScale = Transform.lossyScale;
|
||||
|
||||
if (Is2D == false) preRadius = Capsule.radius; else preRadius = GetCapsule2DRadius(Capsule2D);
|
||||
}
|
||||
|
||||
public override bool PushIfInside(ref Vector3 point, float pointRadius, Vector3 pointOffset)
|
||||
{
|
||||
return PushOutFromCapsuleCollider(pointRadius, ref point, Top, Bottom, radius, pointOffset, Is2D);
|
||||
}
|
||||
|
||||
|
||||
public static bool PushOutFromCapsuleCollider(CapsuleCollider capsule, float segmentColliderRadius, ref Vector3 pos, Vector3 segmentOffset)
|
||||
{
|
||||
Vector3 direction = Vector3.zero; float capsuleRadius = capsule.radius, scalerFactor = 1f;
|
||||
CalculateCapsuleParameters(capsule, ref direction, ref capsuleRadius, ref scalerFactor);
|
||||
Vector3 up = Vector3.zero, bottom = Vector3.zero;
|
||||
GetCapsuleHeadsPositions(capsule, ref up, ref bottom, direction, capsuleRadius, scalerFactor);
|
||||
return PushOutFromCapsuleCollider(segmentColliderRadius, ref pos, up, bottom, capsuleRadius, segmentOffset);
|
||||
}
|
||||
|
||||
public static bool PushOutFromCapsuleCollider(float segmentColliderRadius, ref Vector3 segmentPos, Vector3 capSphereCenter1, Vector3 capSphereCenter2, float capsuleRadius, Vector3 segmentOffset, bool is2D = false)
|
||||
{
|
||||
float radius = capsuleRadius + segmentColliderRadius;
|
||||
Vector3 capsuleUp = capSphereCenter2 - capSphereCenter1;
|
||||
Vector3 fromCenter = (segmentPos + segmentOffset) - capSphereCenter1;
|
||||
|
||||
if (is2D)
|
||||
{
|
||||
capsuleUp.z = 0;
|
||||
fromCenter.z = 0;
|
||||
}
|
||||
|
||||
float orientationDot = Vector3.Dot(fromCenter, capsuleUp);
|
||||
|
||||
if (orientationDot <= 0) // Main Sphere Cap
|
||||
{
|
||||
float sphereRefDistMagn = fromCenter.sqrMagnitude;
|
||||
|
||||
if (sphereRefDistMagn > 0 && sphereRefDistMagn < radius * radius)
|
||||
{
|
||||
segmentPos = capSphereCenter1 - segmentOffset + fromCenter * (radius / Mathf.Sqrt(sphereRefDistMagn));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float upRefMagn = capsuleUp.sqrMagnitude;
|
||||
if (orientationDot >= upRefMagn) // Counter Sphere Cap
|
||||
{
|
||||
fromCenter = (segmentPos + segmentOffset) - capSphereCenter2;
|
||||
float sphereRefDistMagn = fromCenter.sqrMagnitude;
|
||||
|
||||
if (sphereRefDistMagn > 0 && sphereRefDistMagn < radius * radius)
|
||||
{
|
||||
segmentPos = capSphereCenter2 - segmentOffset + fromCenter * (radius / Mathf.Sqrt(sphereRefDistMagn));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (upRefMagn > 0) // Cylinder Volume
|
||||
{
|
||||
fromCenter -= capsuleUp * (orientationDot / upRefMagn);
|
||||
float sphericalRefDistMagn = fromCenter.sqrMagnitude;
|
||||
|
||||
if (sphericalRefDistMagn > 0 && sphericalRefDistMagn < radius * radius)
|
||||
{
|
||||
float projectedDistance = Mathf.Sqrt(sphericalRefDistMagn);
|
||||
segmentPos += fromCenter * ((radius - projectedDistance) / projectedDistance);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Capsule Calculations Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Calculating capsule's centers of up and down sphere which are fitting unity capsule collider with all collider's transformations
|
||||
/// </summary>
|
||||
protected static void CalculateCapsuleParameters(CapsuleCollider capsule, ref Vector3 direction, ref float trueRadius, ref float scalerFactor)
|
||||
{
|
||||
Transform cTransform = capsule.transform;
|
||||
|
||||
float radiusScaler;
|
||||
|
||||
if (capsule.direction == 1)
|
||||
{ /* Y */
|
||||
direction = Vector3.up; scalerFactor = cTransform.lossyScale.y;
|
||||
radiusScaler = cTransform.lossyScale.x > cTransform.lossyScale.z ? cTransform.lossyScale.x : cTransform.lossyScale.z;
|
||||
}
|
||||
else if (capsule.direction == 0)
|
||||
{ /* X */
|
||||
direction = Vector3.right; scalerFactor = cTransform.lossyScale.x;
|
||||
radiusScaler = cTransform.lossyScale.y > cTransform.lossyScale.z ? cTransform.lossyScale.y : cTransform.lossyScale.z;
|
||||
}
|
||||
else
|
||||
{ /* Z */
|
||||
direction = Vector3.forward; scalerFactor = cTransform.lossyScale.z;
|
||||
radiusScaler = cTransform.lossyScale.y > cTransform.lossyScale.x ? cTransform.lossyScale.y : cTransform.lossyScale.x;
|
||||
}
|
||||
|
||||
trueRadius = capsule.radius * radiusScaler;
|
||||
}
|
||||
|
||||
private static float GetCapsule2DRadius(CapsuleCollider2D capsule)
|
||||
{
|
||||
if (capsule.direction == CapsuleDirection2D.Vertical)
|
||||
return capsule.size.x / 2f;
|
||||
else
|
||||
return capsule.size.y / 2f;
|
||||
}
|
||||
|
||||
private static float GetCapsule2DHeight(CapsuleCollider2D capsule)
|
||||
{
|
||||
if (capsule.direction == CapsuleDirection2D.Vertical)
|
||||
return capsule.size.y / 2f;
|
||||
else
|
||||
return capsule.size.x / 2f;
|
||||
}
|
||||
|
||||
protected static void CalculateCapsuleParameters(CapsuleCollider2D capsule, ref Vector3 direction, ref float trueRadius, ref float scalerFactor)
|
||||
{
|
||||
Transform cTransform = capsule.transform;
|
||||
|
||||
float radiusScaler;
|
||||
|
||||
if (capsule.direction == CapsuleDirection2D.Vertical)
|
||||
{ /* Y */
|
||||
direction = Vector3.up; scalerFactor = cTransform.lossyScale.y;
|
||||
radiusScaler = cTransform.lossyScale.x > cTransform.lossyScale.z ? cTransform.lossyScale.x : cTransform.lossyScale.z;
|
||||
trueRadius = (capsule.size.x / 2f) * radiusScaler;
|
||||
}
|
||||
else if (capsule.direction == CapsuleDirection2D.Horizontal)
|
||||
{ /* X */
|
||||
direction = Vector3.right; scalerFactor = cTransform.lossyScale.x;
|
||||
radiusScaler = cTransform.lossyScale.y > cTransform.lossyScale.z ? cTransform.lossyScale.y : cTransform.lossyScale.z;
|
||||
trueRadius = (capsule.size.y / 2f) * radiusScaler;
|
||||
}
|
||||
}
|
||||
|
||||
protected static void GetCapsuleHeadsPositions(CapsuleCollider capsule, ref Vector3 upper, ref Vector3 bottom, Vector3 direction, float radius, float scalerFactor)
|
||||
{
|
||||
Vector3 upCapCenter = direction * ((capsule.height / 2) * scalerFactor - radius); // Local Space Position
|
||||
upper = capsule.transform.position + capsule.transform.TransformDirection(upCapCenter) + capsule.transform.TransformVector(capsule.center); // World Space
|
||||
|
||||
Vector3 downCapCenter = -direction * ((capsule.height / 2) * scalerFactor - radius);
|
||||
bottom = capsule.transform.position + capsule.transform.TransformDirection(downCapCenter) + capsule.transform.TransformVector(capsule.center);
|
||||
}
|
||||
|
||||
protected static void GetCapsuleHeadsPositions(CapsuleCollider2D capsule, ref Vector3 upper, ref Vector3 bottom, Vector3 direction, float radius, float scalerFactor)
|
||||
{
|
||||
Vector3 upCapCenter = direction * (GetCapsule2DHeight(capsule) * scalerFactor - radius); // Local Space Position
|
||||
upper = capsule.transform.position + capsule.transform.TransformDirection(upCapCenter) + capsule.transform.TransformVector(capsule.offset); // World Space
|
||||
upper.z = 0f;
|
||||
|
||||
Vector3 downCapCenter = -direction * (GetCapsule2DHeight(capsule) * scalerFactor - radius);
|
||||
bottom = capsule.transform.position + capsule.transform.TransformDirection(downCapCenter) + capsule.transform.TransformVector(capsule.offset);
|
||||
bottom.z = 0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ffcc9ee773aad94b9214d78942d8fd4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 4ba1f7a7d53fd274c98bdce0c9242640, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,141 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
public class FImp_ColliderData_CharacterCapsule : FImp_ColliderData_Base
|
||||
{
|
||||
public CharacterController Capsule { get; private set; }
|
||||
private Vector3 Top;
|
||||
private Vector3 Bottom;
|
||||
private Vector3 Direction;
|
||||
private float radius;
|
||||
private float scaleFactor;
|
||||
private float preRadius;
|
||||
|
||||
public FImp_ColliderData_CharacterCapsule(CharacterController collider)
|
||||
{
|
||||
Is2D = false;
|
||||
Transform = collider.transform;
|
||||
Collider = collider;
|
||||
Transform = collider.transform;
|
||||
Capsule = collider;
|
||||
ColliderType = EFColliderType.Capsule;
|
||||
CalculateCapsuleParameters(Capsule, ref Direction, ref radius, ref scaleFactor);
|
||||
RefreshColliderData();
|
||||
}
|
||||
|
||||
public override void RefreshColliderData()
|
||||
{
|
||||
if (IsStatic) return; // No need to refresh collider data if it is static
|
||||
|
||||
bool diff = false;
|
||||
|
||||
if (!FEngineering.VIsSame(previousPosition, Transform.position)) diff = true;
|
||||
else
|
||||
if (!FEngineering.QIsSame(Transform.rotation, previousRotation)) diff = true;
|
||||
else
|
||||
{
|
||||
if (preRadius != Capsule.radius || !FEngineering.VIsSame(previousScale, Transform.lossyScale))
|
||||
CalculateCapsuleParameters(Capsule, ref Direction, ref radius, ref scaleFactor);
|
||||
}
|
||||
|
||||
if (diff)
|
||||
{
|
||||
GetCapsuleHeadsPositions(Capsule, ref Top, ref Bottom, Direction, radius, scaleFactor);
|
||||
}
|
||||
|
||||
base.RefreshColliderData();
|
||||
|
||||
previousPosition = Transform.position;
|
||||
previousRotation = Transform.rotation;
|
||||
previousScale = Transform.lossyScale;
|
||||
|
||||
preRadius = Capsule.radius;
|
||||
}
|
||||
|
||||
public override bool PushIfInside(ref Vector3 point, float pointRadius, Vector3 pointOffset)
|
||||
{
|
||||
return PushOutFromCapsuleCollider(pointRadius, ref point, Top, Bottom, radius, pointOffset, false);
|
||||
}
|
||||
|
||||
public static bool PushOutFromCapsuleCollider(float segmentColliderRadius, ref Vector3 segmentPos, Vector3 capSphereCenter1, Vector3 capSphereCenter2, float capsuleRadius, Vector3 segmentOffset, bool is2D = false)
|
||||
{
|
||||
float radius = capsuleRadius + segmentColliderRadius;
|
||||
Vector3 capsuleUp = capSphereCenter2 - capSphereCenter1;
|
||||
Vector3 fromCenter = (segmentPos + segmentOffset) - capSphereCenter1;
|
||||
|
||||
float orientationDot = Vector3.Dot(fromCenter, capsuleUp);
|
||||
|
||||
if (orientationDot <= 0) // Main Sphere Cap
|
||||
{
|
||||
float sphereRefDistMagn = fromCenter.sqrMagnitude;
|
||||
|
||||
if (sphereRefDistMagn > 0 && sphereRefDistMagn < radius * radius)
|
||||
{
|
||||
segmentPos = capSphereCenter1 - segmentOffset + fromCenter * (radius / Mathf.Sqrt(sphereRefDistMagn));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float upRefMagn = capsuleUp.sqrMagnitude;
|
||||
if (orientationDot >= upRefMagn) // Counter Sphere Cap
|
||||
{
|
||||
fromCenter = (segmentPos + segmentOffset) - capSphereCenter2;
|
||||
float sphereRefDistMagn = fromCenter.sqrMagnitude;
|
||||
|
||||
if (sphereRefDistMagn > 0 && sphereRefDistMagn < radius * radius)
|
||||
{
|
||||
segmentPos = capSphereCenter2 - segmentOffset + fromCenter * (radius / Mathf.Sqrt(sphereRefDistMagn));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (upRefMagn > 0) // Cylinder Volume
|
||||
{
|
||||
fromCenter -= capsuleUp * (orientationDot / upRefMagn);
|
||||
float sphericalRefDistMagn = fromCenter.sqrMagnitude;
|
||||
|
||||
if (sphericalRefDistMagn > 0 && sphericalRefDistMagn < radius * radius)
|
||||
{
|
||||
float projectedDistance = Mathf.Sqrt(sphericalRefDistMagn);
|
||||
segmentPos += fromCenter * ((radius - projectedDistance) / projectedDistance);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Capsule Calculations Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Calculating capsule's centers of up and down sphere which are fitting unity capsule collider with all collider's transformations
|
||||
/// </summary>
|
||||
protected static void CalculateCapsuleParameters(CharacterController capsule, ref Vector3 direction, ref float trueRadius, ref float scalerFactor)
|
||||
{
|
||||
Transform cTransform = capsule.transform;
|
||||
|
||||
float radiusScaler;
|
||||
|
||||
direction = Vector3.up; scalerFactor = cTransform.lossyScale.y;
|
||||
radiusScaler = cTransform.lossyScale.x > cTransform.lossyScale.z ? cTransform.lossyScale.x : cTransform.lossyScale.z;
|
||||
|
||||
trueRadius = capsule.radius * radiusScaler;
|
||||
}
|
||||
|
||||
protected static void GetCapsuleHeadsPositions(CharacterController capsule, ref Vector3 upper, ref Vector3 bottom, Vector3 direction, float radius, float scalerFactor)
|
||||
{
|
||||
Vector3 upCapCenter = direction * ((capsule.height / 2) * scalerFactor - radius); // Local Space Position
|
||||
upper = capsule.transform.position + capsule.transform.TransformDirection(upCapCenter) + capsule.transform.TransformVector(capsule.center); // World Space
|
||||
|
||||
Vector3 downCapCenter = -direction * ((capsule.height / 2) * scalerFactor - radius);
|
||||
bottom = capsule.transform.position + capsule.transform.TransformDirection(downCapCenter) + capsule.transform.TransformVector(capsule.center);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c29c49f32c3d59546973844bb3f495d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 4ba1f7a7d53fd274c98bdce0c9242640, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,235 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
public class FImp_ColliderData_Mesh : FImp_ColliderData_Base
|
||||
{
|
||||
public MeshCollider Mesh { get; private set; }
|
||||
public PolygonCollider2D Poly2D { get; private set; }
|
||||
private ContactFilter2D filter;
|
||||
|
||||
public FImp_ColliderData_Mesh(MeshCollider collider)
|
||||
{
|
||||
Is2D = false;
|
||||
Transform = collider.transform;
|
||||
Collider = collider;
|
||||
Mesh = collider;
|
||||
ColliderType = EFColliderType.Mesh;
|
||||
}
|
||||
|
||||
public FImp_ColliderData_Mesh(PolygonCollider2D collider)
|
||||
{
|
||||
Is2D = true;
|
||||
Transform = collider.transform;
|
||||
Poly2D = collider;
|
||||
Collider2D = collider;
|
||||
ColliderType = EFColliderType.Mesh;
|
||||
filter = new ContactFilter2D();
|
||||
filter.useTriggers = false;
|
||||
filter.useDepth = false;
|
||||
r = new RaycastHit2D[1];
|
||||
}
|
||||
|
||||
private RaycastHit2D[] r;
|
||||
public override bool PushIfInside(ref Vector3 segmentPosition, float segmentRadius, Vector3 segmentOffset)
|
||||
{
|
||||
if (Is2D == false)
|
||||
{
|
||||
if (Mesh.convex)
|
||||
{
|
||||
Vector3 closest;
|
||||
Vector3 positionOffsetted = segmentPosition + segmentOffset;
|
||||
float castMul = 1f;
|
||||
|
||||
closest = Physics.ClosestPoint(positionOffsetted, Mesh, Mesh.transform.position, Mesh.transform.rotation);
|
||||
if (Vector3.Distance(closest, positionOffsetted) > segmentRadius * 1.01f) return false;
|
||||
|
||||
Vector3 dir = (closest - positionOffsetted);
|
||||
if (dir == Vector3.zero) return false;
|
||||
|
||||
RaycastHit meshHit;
|
||||
Mesh.Raycast(new Ray(positionOffsetted, dir.normalized), out meshHit, segmentRadius * castMul);
|
||||
|
||||
if (meshHit.transform)
|
||||
{
|
||||
segmentPosition = meshHit.point + meshHit.normal * segmentRadius;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 closest;
|
||||
float plus = 0f;
|
||||
|
||||
Vector3 positionOffsetted = segmentPosition + segmentOffset;
|
||||
|
||||
closest = Mesh.ClosestPointOnBounds(positionOffsetted);
|
||||
plus = (closest - Mesh.transform.position).magnitude;
|
||||
|
||||
bool inside = false;
|
||||
float insideMul = 1f;
|
||||
|
||||
if (closest == positionOffsetted)
|
||||
{
|
||||
inside = true;
|
||||
insideMul = 7f;
|
||||
closest = Mesh.transform.position;
|
||||
}
|
||||
|
||||
Vector3 targeting = closest - positionOffsetted;
|
||||
Vector3 rayDirection = targeting.normalized;
|
||||
Vector3 rayOrigin = positionOffsetted - rayDirection * (segmentRadius * 2f + Mesh.bounds.extents.magnitude);
|
||||
|
||||
float rayDistance = targeting.magnitude + segmentRadius * 2f + plus + Mesh.bounds.extents.magnitude;
|
||||
|
||||
if ((positionOffsetted - closest).magnitude < segmentRadius * insideMul)
|
||||
{
|
||||
Ray ray = new Ray(rayOrigin, rayDirection);
|
||||
|
||||
RaycastHit hit;
|
||||
if (Mesh.Raycast(ray, out hit, rayDistance))
|
||||
{
|
||||
float hitToPointDist = (positionOffsetted - hit.point).magnitude;
|
||||
|
||||
if (hitToPointDist < segmentRadius * insideMul)
|
||||
{
|
||||
|
||||
Vector3 toNormal = hit.point - positionOffsetted;
|
||||
Vector3 pushNormal;
|
||||
|
||||
if (inside) pushNormal = toNormal + toNormal.normalized * segmentRadius; else pushNormal = toNormal - toNormal.normalized * segmentRadius;
|
||||
|
||||
float dot = Vector3.Dot((hit.point - positionOffsetted).normalized, rayDirection);
|
||||
if (inside && dot > 0f) pushNormal = toNormal - toNormal.normalized * segmentRadius;
|
||||
|
||||
segmentPosition = segmentPosition + pushNormal;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
Vector2 positionOffsetted = segmentPosition + segmentOffset;
|
||||
Vector2 closest;
|
||||
|
||||
if (Poly2D.OverlapPoint(positionOffsetted))
|
||||
{
|
||||
// Collider inside polygon collider!
|
||||
Vector3 indir = Poly2D.bounds.center - (Vector3)positionOffsetted; indir.z = 0f;
|
||||
Ray r = new Ray(Poly2D.bounds.center - indir * Poly2D.bounds.max.magnitude, indir);
|
||||
float dist = 0f;
|
||||
Poly2D.bounds.IntersectRay(r, out dist); // We've got partially correct point
|
||||
if (dist > 0f)
|
||||
closest = Poly2D.ClosestPoint(r.GetPoint(dist));
|
||||
else
|
||||
closest = Poly2D.ClosestPoint(positionOffsetted);
|
||||
}
|
||||
else
|
||||
closest = Poly2D.ClosestPoint(positionOffsetted);
|
||||
|
||||
Vector2 dir = (closest - positionOffsetted).normalized;
|
||||
int hits = Physics2D.Raycast(positionOffsetted, dir, filter, r, segmentRadius);
|
||||
|
||||
if (hits > 0)
|
||||
{
|
||||
if (r[0].transform == Transform)
|
||||
{
|
||||
segmentPosition = closest + r[0].normal * segmentRadius;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void PushOutFromMeshCollider(MeshCollider mesh, Collision collision, float segmentColliderRadius, ref Vector3 pos)
|
||||
{
|
||||
Vector3 collisionPoint = collision.contacts[0].point;
|
||||
Vector3 pushNormal = collision.contacts[0].normal;
|
||||
|
||||
RaycastHit info;
|
||||
// Doing cheap mesh raycast from outside to hit surface
|
||||
if (mesh.Raycast(new Ray(pos + pushNormal * segmentColliderRadius * 2f, -pushNormal), out info, segmentColliderRadius * 5))
|
||||
{
|
||||
pushNormal = info.point - pos;
|
||||
float pushMagn = pushNormal.sqrMagnitude;
|
||||
if (pushMagn > 0 && pushMagn < segmentColliderRadius * segmentColliderRadius) pos = info.point - pushNormal * (segmentColliderRadius / Mathf.Sqrt(pushMagn)) * 0.9f;
|
||||
}
|
||||
else
|
||||
{
|
||||
pushNormal = collisionPoint - pos;
|
||||
float pushMagn = pushNormal.sqrMagnitude;
|
||||
if (pushMagn > 0 && pushMagn < segmentColliderRadius * segmentColliderRadius) pos = collisionPoint - pushNormal * (segmentColliderRadius / Mathf.Sqrt(pushMagn)) * 0.9f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void PushOutFromMesh(MeshCollider mesh, Collision collision, float pointRadius, ref Vector3 point)
|
||||
{
|
||||
Vector3 closest;
|
||||
float plus = 0f;
|
||||
|
||||
closest = mesh.ClosestPointOnBounds(point);
|
||||
plus = (closest - mesh.transform.position).magnitude;
|
||||
|
||||
bool inside = false;
|
||||
float insideMul = 1f;
|
||||
|
||||
if (closest == point)
|
||||
{
|
||||
inside = true;
|
||||
insideMul = 7f;
|
||||
closest = mesh.transform.position;
|
||||
}
|
||||
|
||||
Vector3 targeting = closest - point;
|
||||
Vector3 rayDirection = targeting.normalized;
|
||||
Vector3 rayOrigin = point - rayDirection * (pointRadius * 2f + mesh.bounds.extents.magnitude);
|
||||
|
||||
float rayDistance = targeting.magnitude + pointRadius * 2f + plus + mesh.bounds.extents.magnitude;
|
||||
|
||||
if ((point - closest).magnitude < pointRadius * insideMul)
|
||||
{
|
||||
Vector3 collisionPoint;
|
||||
|
||||
if (!inside)
|
||||
collisionPoint = collision.contacts[0].point;
|
||||
else
|
||||
{
|
||||
Ray ray = new Ray(rayOrigin, rayDirection);
|
||||
RaycastHit hit;
|
||||
if (mesh.Raycast(ray, out hit, rayDistance)) collisionPoint = hit.point; else collisionPoint = collision.contacts[0].point;
|
||||
}
|
||||
|
||||
float hitToPointDist = (point - collisionPoint).magnitude;
|
||||
|
||||
if (hitToPointDist < pointRadius * insideMul)
|
||||
{
|
||||
Vector3 toNormal = collisionPoint - point;
|
||||
Vector3 pushNormal;
|
||||
|
||||
if (inside) pushNormal = toNormal + toNormal.normalized * pointRadius; else pushNormal = toNormal - toNormal.normalized * pointRadius;
|
||||
|
||||
float dot = Vector3.Dot((collisionPoint - point).normalized, rayDirection);
|
||||
if (inside && dot > 0f) pushNormal = toNormal - toNormal.normalized * pointRadius;
|
||||
|
||||
point = point + pushNormal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bde60ae57f06f434ebd27fc9589b8ff7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 4ba1f7a7d53fd274c98bdce0c9242640, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,138 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
public class FImp_ColliderData_Sphere : FImp_ColliderData_Base
|
||||
{
|
||||
public SphereCollider Sphere { get; private set; }
|
||||
public CircleCollider2D Sphere2D { get; private set; }
|
||||
private float SphereRadius;
|
||||
|
||||
public FImp_ColliderData_Sphere(SphereCollider collider)
|
||||
{
|
||||
Is2D = false;
|
||||
Transform = collider.transform;
|
||||
Collider = collider;
|
||||
Sphere = collider;
|
||||
ColliderType = EFColliderType.Sphere;
|
||||
RefreshColliderData();
|
||||
}
|
||||
|
||||
public FImp_ColliderData_Sphere(CircleCollider2D collider)
|
||||
{
|
||||
Is2D = true;
|
||||
Transform = collider.transform;
|
||||
Collider2D = collider;
|
||||
Sphere2D = collider;
|
||||
ColliderType = EFColliderType.Sphere;
|
||||
RefreshColliderData();
|
||||
}
|
||||
|
||||
public override void RefreshColliderData()
|
||||
{
|
||||
if (IsStatic) return; // No need to refresh collider data if it is static
|
||||
|
||||
if (Sphere2D == null)
|
||||
{
|
||||
SphereRadius = CalculateTrueRadiusOfSphereCollider(Sphere.transform, Sphere.radius);
|
||||
base.RefreshColliderData();
|
||||
}
|
||||
else
|
||||
{
|
||||
SphereRadius = CalculateTrueRadiusOfSphereCollider(Sphere2D.transform, Sphere2D.radius);
|
||||
base.RefreshColliderData();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool PushIfInside(ref Vector3 point, float pointRadius, Vector3 pointOffset)
|
||||
{
|
||||
if ( Is2D == false)
|
||||
return PushOutFromSphereCollider(Sphere, pointRadius, ref point, SphereRadius, pointOffset);
|
||||
else
|
||||
return PushOutFromSphereCollider(Sphere2D, pointRadius, ref point, SphereRadius, pointOffset);
|
||||
}
|
||||
|
||||
|
||||
public static bool PushOutFromSphereCollider(SphereCollider sphere, float segmentColliderRadius, ref Vector3 segmentPos, Vector3 segmentOffset)
|
||||
{
|
||||
return PushOutFromSphereCollider(sphere, segmentColliderRadius, ref segmentPos, CalculateTrueRadiusOfSphereCollider(sphere), segmentOffset);
|
||||
}
|
||||
|
||||
|
||||
public static bool PushOutFromSphereCollider(SphereCollider sphere, float segmentColliderRadius, ref Vector3 segmentPos, float collidingSphereRadius, Vector3 segmentOffset)
|
||||
{
|
||||
Vector3 sphereCenter = sphere.transform.position + sphere.transform.TransformVector(sphere.center);
|
||||
float radius = collidingSphereRadius + segmentColliderRadius;
|
||||
|
||||
Vector3 pushNormal = (segmentPos + segmentOffset) - sphereCenter;
|
||||
float squaredPushMagn = pushNormal.sqrMagnitude;
|
||||
|
||||
if (squaredPushMagn > 0 && squaredPushMagn < radius * radius)
|
||||
{
|
||||
segmentPos = sphereCenter - segmentOffset + pushNormal * (radius / Mathf.Sqrt(squaredPushMagn));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool PushOutFromSphereCollider(CircleCollider2D sphere, float segmentColliderRadius, ref Vector3 segmentPos, float collidingSphereRadius, Vector3 segmentOffset)
|
||||
{
|
||||
Vector3 sphereCenter = sphere.transform.position + sphere.transform.TransformVector(sphere.offset);
|
||||
sphereCenter.z = 0f;
|
||||
float radius = collidingSphereRadius + segmentColliderRadius;
|
||||
|
||||
Vector3 pos2D = segmentPos; pos2D.z = 0f;
|
||||
Vector3 pushNormal = (pos2D + segmentOffset) - sphereCenter;
|
||||
float squaredPushMagn = pushNormal.sqrMagnitude;
|
||||
|
||||
if (squaredPushMagn > 0 && squaredPushMagn < radius * radius)
|
||||
{
|
||||
segmentPos = sphereCenter - segmentOffset + pushNormal * (radius / Mathf.Sqrt(squaredPushMagn));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Sphere Calculation Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Calculating radius of sphere collider including sphere collider's transform scalling
|
||||
/// </summary>
|
||||
public static float CalculateTrueRadiusOfSphereCollider(SphereCollider sphere)
|
||||
{
|
||||
return CalculateTrueRadiusOfSphereCollider(sphere.transform, sphere.radius);
|
||||
}
|
||||
|
||||
public static float CalculateTrueRadiusOfSphereCollider(CircleCollider2D sphere)
|
||||
{
|
||||
return CalculateTrueRadiusOfSphereCollider(sphere.transform, sphere.radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculating radius of sphere collider including sphere collider's transform scalling
|
||||
/// </summary>
|
||||
public static float CalculateTrueRadiusOfSphereCollider(Transform transform, float componentRadius)
|
||||
{
|
||||
float radius = componentRadius;
|
||||
|
||||
if (transform.lossyScale.x > transform.lossyScale.y)
|
||||
{
|
||||
if (transform.lossyScale.x > transform.lossyScale.z) radius *= transform.lossyScale.x;
|
||||
else
|
||||
radius *= transform.lossyScale.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transform.lossyScale.y > transform.lossyScale.z) radius *= transform.lossyScale.y;
|
||||
else
|
||||
radius *= transform.lossyScale.z;
|
||||
}
|
||||
|
||||
return radius;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bba586e4c6fd54349ad44a2c387d49bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 4ba1f7a7d53fd274c98bdce0c9242640, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,94 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FIMSpace
|
||||
{
|
||||
public class FImp_ColliderData_Terrain : FImp_ColliderData_Base
|
||||
{
|
||||
public TerrainCollider TerrCollider { get; private set; }
|
||||
public Terrain TerrainComponent { get; private set; }
|
||||
|
||||
public FImp_ColliderData_Terrain(TerrainCollider collider)
|
||||
{
|
||||
Collider = collider;
|
||||
Transform = collider.transform;
|
||||
TerrCollider = collider;
|
||||
ColliderType = EFColliderType.Terrain;
|
||||
TerrainComponent = collider.GetComponent<Terrain>();
|
||||
}
|
||||
|
||||
public override bool PushIfInside(ref Vector3 segmentPosition, float segmentRadius, Vector3 segmentOffset)
|
||||
{
|
||||
// Checking if segment is inside box shape of terrain
|
||||
if (segmentPosition.x + segmentRadius < TerrainComponent.GetPosition().x - segmentRadius || segmentPosition.x > TerrainComponent.GetPosition().x + TerrainComponent.terrainData.size.x ||
|
||||
segmentPosition.z + segmentRadius < TerrainComponent.GetPosition().z - segmentRadius || segmentPosition.z > TerrainComponent.GetPosition().z + TerrainComponent.terrainData.size.z)
|
||||
return false;
|
||||
|
||||
Vector3 offsettedPosition = segmentPosition + segmentOffset;
|
||||
Vector3 terrPoint = offsettedPosition;
|
||||
terrPoint.y = TerrCollider.transform.position.y + TerrainComponent.SampleHeight(offsettedPosition);
|
||||
|
||||
|
||||
float hitToPointDist = (offsettedPosition - terrPoint).magnitude;
|
||||
float underMul = 1f;
|
||||
|
||||
if (offsettedPosition.y < terrPoint.y)
|
||||
{
|
||||
underMul = 4f;
|
||||
}
|
||||
else
|
||||
if (offsettedPosition.y + segmentRadius * 2f < terrPoint.y)
|
||||
{
|
||||
underMul = 8f;
|
||||
}
|
||||
|
||||
if (hitToPointDist < segmentRadius * underMul)
|
||||
{
|
||||
Vector3 toNormal = terrPoint - offsettedPosition;
|
||||
|
||||
Vector3 pushNormal;
|
||||
if (underMul > 1f) pushNormal = toNormal + toNormal.normalized * segmentRadius; else pushNormal = toNormal - toNormal.normalized * segmentRadius;
|
||||
segmentPosition = segmentPosition + pushNormal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void PushOutFromTerrain(TerrainCollider terrainCollider, float segmentRadius, ref Vector3 point)
|
||||
{
|
||||
Terrain terrain = terrainCollider.GetComponent<Terrain>();
|
||||
|
||||
Vector3 rayOrigin = point;
|
||||
rayOrigin.y = terrainCollider.transform.position.y + terrain.SampleHeight(point) + segmentRadius;
|
||||
|
||||
Ray ray = new Ray(rayOrigin, Vector3.down);
|
||||
|
||||
RaycastHit hit;
|
||||
if (terrainCollider.Raycast(ray, out hit, segmentRadius * 2f))
|
||||
{
|
||||
float hitToPointDist = (point - hit.point).magnitude;
|
||||
|
||||
float underMul = 1f;
|
||||
if (hit.point.y > point.y + segmentRadius * 0.9f)
|
||||
{
|
||||
underMul = 8f;
|
||||
}
|
||||
else
|
||||
if (hit.point.y > point.y)
|
||||
{
|
||||
underMul = 4f;
|
||||
}
|
||||
|
||||
if (hitToPointDist < segmentRadius * underMul)
|
||||
{
|
||||
Vector3 toNormal = hit.point - point;
|
||||
Vector3 pushNormal;
|
||||
|
||||
if (underMul > 1f) pushNormal = toNormal + toNormal.normalized * segmentRadius; else pushNormal = toNormal - toNormal.normalized * segmentRadius;
|
||||
point = point + pushNormal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f194fa206da32744ab55e530d336095a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 4ba1f7a7d53fd274c98bdce0c9242640, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user