//#define TOOLKIT2D_SUPPORT_ENABLED
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System.Collections;
using System.Collections.Generic;
[RequireComponent (typeof(MeshFilter), typeof(MeshRenderer))]
public abstract class JellySprite : MonoBehaviour
{
#region PUBLIC_VARIABLES
// Arrangement of the physics bodies
public enum PhysicsStyle
{
Circle,
Rectangle,
Triangle,
Grid,
Free,
Line,
}
public PhysicsStyle m_Style = PhysicsStyle.Circle;
///
/// Controls body positions in free mode
///
public List m_FreeModeBodyPositions;
public List m_FreeModeBodyRadii;
public List m_FreeModeBodyKinematic;
// Whether the chosen mass value is applied per rigid body or
// to the soft body as a whole
public enum MassStyle
{
Global,
PerBody
}
public MassStyle m_MassStyle = MassStyle.PerBody;
// Physics materials for 2D/3D modes
public PhysicsMaterial m_PhysicsMaterial;
public PhysicsMaterial2D m_PhysicsMaterial2D;
// Physics interpolation modes
public RigidbodyInterpolation m_Interpolation = RigidbodyInterpolation.None;
public RigidbodyInterpolation2D m_Interpolation2D = RigidbodyInterpolation2D.None;
// Physics collision detection modes
public CollisionDetectionMode m_CollisionDetectionMode = CollisionDetectionMode.Discrete;
public CollisionDetectionMode2D m_CollisionDetectionMode2D = CollisionDetectionMode2D.Discrete;
// If enabled, the jelly sprite will not automatically position itself at the physics
// body location, instead the user can manually position it themselves by modifying the gameobject transform
public bool m_ManualPositioning = false;
// How many vertices make up the rendered physics mesh
public int m_VertexDensity = 10;
// Radius of the rigid body colliders (given as a % of sprite size)
public float m_SphereRadius = 0.25f;
// How strongly we map reference points to mesh vertices. Higher values will make the mesh vertex distortion
// more accurately correspond to the rigid body movement, but may cause visual artefacts, especially on
// soft bodies made up of only a few rigid bodies.
public float m_DistanceExponent = 2.0f;
// How stiff the soft body physics springs are
public float m_Stiffness = 2.5f;
// The mass of the entire sprite (each of the n reference point is 1/n times this value)
public float m_Mass = 1.0f;
// Whether or not to lock rotation around the Z-axis
public bool m_LockRotation = true;
// Gravity scale (in 2D mode)
public float m_GravityScale = 1.0f;
// Use Gravity (in 3D mode)
public bool m_UseGravity = true;
// Whether child bodies should collider with one another
public bool m_CollideConnected = false;
// Whether to make the central body kinematic (ie. not move)
public bool m_CentralBodyKinematic = false;
// Drag (in 3D mode)
public float m_Drag = 0.0f;
public float m_AngularDrag = 0.05f;
// Circle-configuration only - how many rigid bodies are placed around the circle radius
public int m_RadiusPoints = 8;
// Grid-configuration only - how many rigid bodies make up the grid in each dimension
public int m_GridColumns = 4;
public int m_GridRows = 4;
// The amount by which the spring force is reduced in proportion to the movement speed. The spring will oscillate
// with a certain frequency as it attempts to reestablish the desired distance between the objects. The higher
// the damping ratio, the quicker the oscillation will die down to zero.
public float m_DampingRatio = 0.0f;
///
/// Used to scale the collider up/down without adjusting the actual sprite size
///
public Vector2 m_SoftBodyScale = Vector2.one;
///
/// Used to rotate the collider independently of the sprite
///
public float m_SoftBodyRotation = 0.0f;
///
/// Used to offset the soft bodies
///
public Vector2 m_SoftBodyOffset = Vector2.zero;
///
/// Used to offset the central soft body
///
public Vector2 m_CentralBodyOffset = Vector2.zero;
///
/// Used to scale the sprite size
///
public Vector2 m_SpriteScale = Vector2.one;
// Whether to use 2D or 3D rigid bodies/colliders
public bool m_2DMode = false;
// Controls whether bodies are attached to their neighboring bodies as well as to
// the central point
public bool m_AttachNeighbors = false;
// Flip the sprite horizontally/vertically
public bool m_FlipX = false;
public bool m_FlipY = false;
// Sprite rotation (in degrees)
public float m_SpriteRotation = 0.0f;
// Array of attach points - used to attach child objects to this jelly sprite
public int m_NumAttachPoints = 0;
public Transform[] m_AttachPoints = new Transform[0];
// Tint color
public Color m_Color = Color.white;
public List ReferencePoints { get { return m_ReferencePoints; } }
public ReferencePoint CentralPoint { get { return m_CentralPoint; } }
#endregion
#region PRIVATE_VARIABLES
// Internal rendering data
Vector3[] m_Vertices;
Vector3[] m_InitialVertexPositions;
Color[] m_Colors;
Vector2[] m_TexCoords;
int[] m_Triangles;
Mesh m_SpriteMesh;
// Physics reference points
public List m_ReferencePoints;
// Reference point->vertex weighting values
float[,] m_ReferencePointWeightings;
// Reference point->attach point weighting valuse
float[,] m_AttachPointWeightings;
// Initial attach point positions
Vector3[] m_InitialAttachPointPositions = new Vector3[0];
// Saves us checking components every frame to see if an
// attached object is actually another Jelly Sprite
bool[] m_IsAttachPointJellySprite = new bool[0];
// Parent object for rigidbodies
public GameObject m_ReferencePointParent;
// Central body point
ReferencePoint m_CentralPoint;
// List of reference point offset
Vector3[] m_ReferencePointOffsets;
// Cached transform
Transform m_Transform;
#endregion
#region PUBLIC_CLASSES
///
/// The ReferencePoint class encapsulates a rigid body (2D or 3D) and information about
/// the bodies initial position. From there, we can work out how much the body has moved
/// from its initial position and then map the movement to the visible mesh.
///
public class ReferencePoint
{
public Vector3 InitialOffset { get { return m_InitialOffset; } set { m_InitialOffset = value; } }
public Rigidbody2D Body2D { get { return m_RigidBody2D; } }
public Rigidbody Body3D { get { return m_RigidBody3D; } }
public CircleCollider2D Collider2D { get { return m_CircleCollider2D; } }
public SphereCollider Collider { get { return m_SphereCollider; } }
public bool IsDummy { get { return m_IsDummy; } }
Transform m_Transform;
Rigidbody2D m_RigidBody2D;
Rigidbody m_RigidBody3D;
CircleCollider2D m_CircleCollider2D;
SphereCollider m_SphereCollider;
Vector3 m_InitialOffset;
bool m_IsDummy = true;
///
/// ReferencePoint 2D Constructor
///
public ReferencePoint(Rigidbody2D body)
{
if (body != null)
{
m_RigidBody2D = body;
m_CircleCollider2D = body.GetComponent();
m_Transform = body.transform;
m_IsDummy = false;
}
else
{
m_IsDummy = true;
}
}
///
/// ReferencePoint 3D Constructor
///
public ReferencePoint(Rigidbody body)
{
if (body != null)
{
m_RigidBody3D = body;
m_SphereCollider = body.GetComponent();
m_Transform = body.transform;
m_IsDummy = false;
}
else
{
m_IsDummy = true;
}
}
///
/// Get the radius of the rigid body
///
public float Radius
{
get
{
if(m_CircleCollider2D != null)
{
return m_CircleCollider2D.radius;
}
else if(m_SphereCollider != null)
{
return m_SphereCollider.radius;
}
return 0.0f;
}
}
///
/// Gets the game object.
///
public GameObject GameObject
{
get
{
if(m_RigidBody2D != null)
{
return m_RigidBody2D.gameObject;
}
else if(m_RigidBody3D != null)
{
return m_RigidBody3D.gameObject;
}
return null;
}
}
///
/// Gets the transform.
///
public Transform transform { get { return m_Transform; } }
///
/// Set the kinematic flag on this object
///
public void SetKinematic(bool kinematic)
{
if(m_RigidBody2D != null)
{
m_RigidBody2D.isKinematic = kinematic;
}
else if(m_RigidBody3D != null)
{
m_RigidBody3D.isKinematic = kinematic;
}
}
}
///
/// Helper class for passing information about collisions
///
public class JellyCollision
{
public Collision Collision { get; set; }
public JellySpriteReferencePoint ReferencePoint { get; set; }
}
///
/// Helper class for passing information about 2D collisions
///
public class JellyCollision2D
{
public Collision2D Collision2D { get; set; }
public JellySpriteReferencePoint ReferencePoint { get; set; }
}
///
/// Helper class for passing information about triggers
///
public class JellyCollider
{
public Collider Collider { get; set; }
public JellySpriteReferencePoint ReferencePoint { get; set; }
}
///
/// Helper class for passing information about 2D collisions
///
public class JellyCollider2D
{
public Collider2D Collider2D { get; set; }
public JellySpriteReferencePoint ReferencePoint { get; set; }
}
#endregion
///
/// JellySprite constructor
///
void Awake()
{
m_Transform = this.transform;
}
///
/// Start this instance.
///
void Start()
{
if(m_FreeModeBodyPositions == null)
{
m_FreeModeBodyPositions = new List();
m_FreeModeBodyPositions.Add(Vector3.zero);
m_FreeModeBodyRadii = new List();
m_FreeModeBodyRadii.Add(1.0f);
m_FreeModeBodyKinematic = new List();
m_FreeModeBodyKinematic.Add(false);
}
// Maintaining support for users upgrading from 1.07 to 1.08
if(m_FreeModeBodyKinematic.Count != m_FreeModeBodyPositions.Count)
{
m_FreeModeBodyKinematic = new List();
for(int loop = 0; loop < m_FreeModeBodyPositions.Count; loop++)
{
m_FreeModeBodyKinematic.Add(false);
}
}
Bounds spriteBounds = new Bounds();
if(IsSpriteValid())
{
spriteBounds = GetSpriteBounds();
InitVertices(spriteBounds);
InitMaterial();
InitMesh();
}
else
{
MeshFilter meshFilter = GetComponent();
// If the user hasn't supplied a mesh, attempt to extract it from the meshfilter
if(Application.isPlaying && meshFilter.sharedMesh != null)
{
m_SpriteMesh = meshFilter.sharedMesh;
m_Vertices = m_SpriteMesh.vertices;
m_InitialVertexPositions = m_SpriteMesh.vertices;
m_Triangles = m_SpriteMesh.triangles;
m_TexCoords = m_SpriteMesh.uv;
m_Colors = m_SpriteMesh.colors;
spriteBounds = m_SpriteMesh.bounds;
m_SpriteScale = Vector3.one;
}
else if(Application.isPlaying)
{
Debug.LogError("Failed to initialize Jelly Sprite " + name + " - no valid sprite or mesh");
this.enabled = false;
return;
}
}
m_InitialAttachPointPositions = new Vector3[m_AttachPoints.Length];
m_IsAttachPointJellySprite = new bool[m_AttachPoints.Length];
if(Application.isPlaying)
{
Vector3 spriteAngle = m_Transform.eulerAngles;
m_Transform.eulerAngles = Vector3.zero;
#if UNITY_4_3
if(m_2DMode && !Physics2D.GetIgnoreLayerCollision(this.gameObject.layer, this.gameObject.layer))
{
Debug.LogError("Layer '" + LayerMask.LayerToName(this.gameObject.layer) + "' is set to collide with itself - soft body physics will not work as intended. Please disable collisions between this layer and itself (Edit->Project Settings->Physics 2D)");
return;
}
#endif
m_ReferencePointParent = new GameObject();
m_ReferencePointParent.name = this.name + " Reference Points";
m_ReferencePoints = new List();
switch(m_Style)
{
case PhysicsStyle.Circle:
CreateRigidBodiesCircle(spriteBounds);
break;
case PhysicsStyle.Triangle:
CreateRigidBodiesTriangle(spriteBounds);
break;
case PhysicsStyle.Rectangle:
CreateRigidBodiesRectangle(spriteBounds);
break;
case PhysicsStyle.Line:
CreateRigidBodiesLine(spriteBounds);
break;
case PhysicsStyle.Grid:
CreateRigidBodiesGrid(spriteBounds);
break;
case PhysicsStyle.Free:
CreateRigidBodiesFree(spriteBounds);
break;
}
if(m_CentralPoint != null)
{
m_CentralPoint.GameObject.name = this.name + " Central Ref Point";
}
if(m_Style != PhysicsStyle.Free)
{
UpdateRotationLock();
}
CalculateInitialOffsets();
InitMass();
CalculateWeightingValues();
SetupCollisions();
m_ReferencePointOffsets = new Vector3[m_ReferencePoints.Count];
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(!referencePoint.IsDummy)
{
Vector3 referencePointPosition = referencePoint.transform.position;
Vector3 centralPointPosition = m_Transform.position;
referencePoint.transform.position = centralPointPosition + (Quaternion.Euler(spriteAngle) * (referencePointPosition - centralPointPosition));
}
}
m_CentralPoint.transform.eulerAngles = spriteAngle;
}
}
///
/// Calculates the initial offsets of each reference point
///
void CalculateInitialOffsets()
{
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(referencePoint.GameObject && referencePoint != m_CentralPoint)
{
referencePoint.InitialOffset = m_CentralPoint.transform.InverseTransformPoint(referencePoint.transform.position);
}
}
int index = 0;
foreach(Transform attachPointTransform in m_AttachPoints)
{
JellySprite attachedJellySprite = attachPointTransform.GetComponent();
Vector3 position = m_CentralPoint.transform.InverseTransformPoint(attachPointTransform.position);
position.x /= m_Transform.localScale.x;
position.y /= m_Transform.localScale.y;
if(attachedJellySprite)
{
m_IsAttachPointJellySprite[index] = true;
m_InitialAttachPointPositions[index++] = position;
}
else
{
m_IsAttachPointJellySprite[index] = false;
m_InitialAttachPointPositions[index++] = position;
attachPointTransform.parent = m_Transform;
}
}
for(int loop = 0; loop < m_Vertices.Length; loop++)
{
m_InitialVertexPositions[loop] -= m_Transform.InverseTransformPoint(m_CentralPoint.transform.position);
m_Vertices[loop] -= m_Transform.InverseTransformPoint(m_CentralPoint.transform.position);
}
}
///
/// Raises the enable event.
///
void OnEnable()
{
if(m_ReferencePointParent)
{
m_ReferencePointParent.SetActive(true);
}
// Collisions need to be set up again each time the object is activated
SetupCollisions();
}
///
/// Raises the disable event
///
void OnDisable()
{
if (m_ReferencePointParent)
{
m_ReferencePointParent.SetActive(false);
}
}
///
/// Get the bounds of the sprite
///
protected abstract Bounds GetSpriteBounds();
///
/// Check if the sprite is valid
///
protected abstract bool IsSpriteValid();
///
/// Raises the destroy event.
///
void OnDestroy()
{
if(m_ReferencePointParent != null)
{
Destroy(m_ReferencePointParent);
}
}
///
/// Create reference points in a circular formation around the central body. Each point is linked to
/// its neighbors and to the center
///
void CreateRigidBodiesCircle(Bounds spriteBounds)
{
int numPoints = m_RadiusPoints;
float width = spriteBounds.size.x * m_SpriteScale.x;
float radius = width * 0.5f;
float sphereRadius = m_SphereRadius * m_Transform.localScale.x;
m_CentralPoint = AddReferencePoint(m_CentralBodyOffset, width * sphereRadius, m_LockRotation);
// Add nodes in a circle around the centre
for(int loop = 0; loop < numPoints; loop++)
{
// Work out the correct offset to place the node
float angle = ((Mathf.PI * 2)/numPoints) * loop;
Vector2 offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
offset *= radius;
offset.x *= m_SoftBodyScale.x;
offset.y *= m_SoftBodyScale.y;
Vector3 bodyPosition = offset * (1.0f - ((sphereRadius * width) / (m_Transform.localScale.x * offset.magnitude))) + m_SoftBodyOffset;
ReferencePoint referencePoint = AddReferencePoint(bodyPosition, width * sphereRadius, true);
AttachPoint(referencePoint, m_CentralPoint);
}
if(m_AttachNeighbors)
{
for(int loop = 2; loop < m_ReferencePoints.Count; loop++)
{
AttachPoint(m_ReferencePoints[loop], m_ReferencePoints[loop - 1]);
}
AttachPoint(m_ReferencePoints[m_ReferencePoints.Count - 1], m_ReferencePoints[1]);
}
}
///
/// Create reference points in a triangle formation. Each point is connected to the central point
/// and to its neighbors
///
void CreateRigidBodiesTriangle(Bounds spriteBounds)
{
float width = spriteBounds.size.x * m_SoftBodyScale.x * m_SpriteScale.x;
float height = spriteBounds.size.y * m_SoftBodyScale.y * m_SpriteScale.y;
float radius = spriteBounds.size.y * m_SphereRadius * m_SpriteScale.y * m_Transform.localScale.y;
float offsetFactor = 0.5f - m_SphereRadius;
m_CentralPoint = AddReferencePoint(m_CentralBodyOffset, radius, m_LockRotation);
ReferencePoint bottomLeftPoint = AddReferencePoint(new Vector2(-width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset, radius, true);
AttachPoint(bottomLeftPoint, m_CentralPoint);
ReferencePoint bottomRightPoint = AddReferencePoint(new Vector2(width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset, radius, true);
AttachPoint(bottomRightPoint, m_CentralPoint);
ReferencePoint topCentrePoint = AddReferencePoint(new Vector2(0.0f, height * offsetFactor) + m_SoftBodyOffset, radius, true);
AttachPoint(topCentrePoint, m_CentralPoint);
if(m_AttachNeighbors)
{
AttachPoint(m_ReferencePoints[1], m_ReferencePoints[2]);
AttachPoint(m_ReferencePoints[2], m_ReferencePoints[3]);
AttachPoint(m_ReferencePoints[3], m_ReferencePoints[1]);
}
}
///
/// Create reference points in a rectangular formation with each point connected to the central
/// point and to its neighbors
///
void CreateRigidBodiesRectangle(Bounds spriteBounds)
{
float width = spriteBounds.size.x * m_SoftBodyScale.x * m_SpriteScale.x;
float height = spriteBounds.size.y * m_SoftBodyScale.y * m_SpriteScale.y;
float radius = spriteBounds.size.y * m_SphereRadius * m_SpriteScale.y * m_Transform.localScale.y;
float offsetFactor = 0.5f - m_SphereRadius;
m_CentralPoint = AddReferencePoint(m_CentralBodyOffset, radius, m_LockRotation);
ReferencePoint bottomLeftPoint = AddReferencePoint(new Vector2(-width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset, radius, true);
AttachPoint(bottomLeftPoint, m_CentralPoint);
ReferencePoint bottomRightPoint = AddReferencePoint(new Vector2(width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset, radius, true);
AttachPoint(bottomRightPoint, m_CentralPoint);
ReferencePoint topRightPoint = AddReferencePoint(new Vector2(width * offsetFactor, height * offsetFactor) + m_SoftBodyOffset, radius, true);
AttachPoint(topRightPoint, m_CentralPoint);
ReferencePoint topLeftPoint = AddReferencePoint(new Vector2(-width * offsetFactor, height * offsetFactor) + m_SoftBodyOffset, radius, true);
AttachPoint(topLeftPoint, m_CentralPoint);
if(m_AttachNeighbors)
{
AttachPoint(m_ReferencePoints[1], m_ReferencePoints[2]);
AttachPoint(m_ReferencePoints[2], m_ReferencePoints[3]);
AttachPoint(m_ReferencePoints[3], m_ReferencePoints[4]);
AttachPoint(m_ReferencePoints[4], m_ReferencePoints[1]);
}
}
///
/// Create reference points in a line formation with each point connected to its neighbors
///
void CreateRigidBodiesLine(Bounds spriteBounds)
{
float width = spriteBounds.size.x * m_SoftBodyScale.x * m_SpriteScale.x;
float radius = spriteBounds.size.x * m_SphereRadius * m_SpriteScale.x * this.transform.localScale.x;
// Always create an odd number of points so that we can correctly pick the central one
int numPoints = ((m_GridColumns/2) * 2) + 1;
for(int x = 0; x < numPoints; x++)
{
Vector2 position;
position.x = (-width * 0.5f) + ((width/(float)(numPoints - 1)) * x);
position.y = 0.0f;
if(x == m_GridColumns/2)
{
AddReferencePoint(position + m_CentralBodyOffset, radius, true);
}
else
{
AddReferencePoint(position + m_SoftBodyOffset, radius, true);
}
if(x > 0)
{
AttachPoint(m_ReferencePoints[x], m_ReferencePoints[x-1]);
}
}
m_CentralPoint = m_ReferencePoints[m_GridColumns/2];
}
///
/// Create reference points in a diamond grid formation around the central body. Each point is linked to
/// its neighbors
///
void CreateRigidBodiesGrid(Bounds spriteBounds)
{
float radius = spriteBounds.size.x * m_SphereRadius * m_SpriteScale.x;
float width = (spriteBounds.size.x * m_SoftBodyScale.x * m_SpriteScale.x) - (m_SphereRadius * 4);
float height = (spriteBounds.size.y * m_SoftBodyScale.y * m_SpriteScale.y) - (m_SphereRadius * 4);
int columns = m_GridColumns;
int rows = m_GridRows;
columns = Mathf.Max(1, columns);
rows = Mathf.Max(1, rows);
for(int y = 0; y < rows; y++)
{
for(int x = 0; x < columns; x++)
{
if(y % 2 != 0 && x == columns - 1)
{
Rigidbody2D dummyBody = null;
ReferencePoint dummyPoint = new ReferencePoint(dummyBody);
m_ReferencePoints.Add(dummyPoint);
}
else
{
Vector2 position;
position.x = (-width * 0.5f) + ((width/(float)(columns - 1)) * x);
if(y % 2 != 0)
{
position.x += ((width/(float)(columns - 1)) * 0.5f);
}
position.y = (-height * 0.5f) + ((height/(float)(rows - 1)) * y);
position += m_SoftBodyOffset;
ReferencePoint refPoint = AddReferencePoint(position, radius, true);
if(x == columns/2 && y == rows/2)
{
m_CentralPoint = refPoint;
}
}
}
}
for(int y = 0; y < rows - 1; y++)
{
for(int x = 0; x < columns; x++)
{
int thisPoint = (y * columns) + x;
int nextPoint = ((y + 1) * columns) + x;
if(!m_ReferencePoints[thisPoint].IsDummy && !m_ReferencePoints[nextPoint].IsDummy)
{
AttachPoint(m_ReferencePoints[thisPoint], m_ReferencePoints[nextPoint]);
}
}
}
for(int y = 0; y < rows - 1; y++)
{
for(int x = 1; x < columns - 1; x++)
{
if(y % 2 == 0)
{
int thisPoint = (y * columns) + x;
int nextPoint = ((y + 1) * columns) + (x - 1);
if(!m_ReferencePoints[thisPoint].IsDummy && !m_ReferencePoints[nextPoint].IsDummy)
{
AttachPoint(m_ReferencePoints[thisPoint], m_ReferencePoints[nextPoint]);
}
}
}
}
for(int y = 0; y < rows - 1; y++)
{
for(int x = 0; x < columns - 1; x++)
{
if(y % 2 != 0)
{
int thisPoint = (y * columns) + x;
int nextPoint = ((y + 1) * columns) + (x + 1);
if(!m_ReferencePoints[thisPoint].IsDummy && !m_ReferencePoints[nextPoint].IsDummy)
{
AttachPoint(m_ReferencePoints[thisPoint], m_ReferencePoints[nextPoint]);
}
}
}
}
for(int y = 0; y < rows - 1; y++)
{
if(y % 2 == 0)
{
int x = columns - 1;
int thisPoint = (y * columns) + x;
int nextPoint = ((y + 1) * columns) + (x - 1);
if(!m_ReferencePoints[thisPoint].IsDummy && !m_ReferencePoints[nextPoint].IsDummy)
{
AttachPoint(m_ReferencePoints[thisPoint], m_ReferencePoints[nextPoint]);
}
}
}
}
///
/// Creates the rigid bodies in a free configuration based around a central point
///
/// .
void CreateRigidBodiesFree(Bounds spriteBounds)
{
m_CentralBodyOffset = m_FreeModeBodyPositions[0];
m_CentralPoint = AddReferencePoint(m_FreeModeBodyPositions[0], m_FreeModeBodyRadii[0], m_LockRotation);
m_CentralPoint.SetKinematic(m_FreeModeBodyKinematic[0]);
for(int loop = 1; loop < m_FreeModeBodyPositions.Count; loop++)
{
ReferencePoint referencePoint = AddReferencePoint(m_FreeModeBodyPositions[loop], m_FreeModeBodyRadii[loop], true);
AttachPoint(referencePoint, m_CentralPoint);
referencePoint.SetKinematic(m_FreeModeBodyKinematic[loop]);
}
if(m_AttachNeighbors)
{
for(int loop = 2; loop < m_ReferencePoints.Count; loop++)
{
AttachPoint(m_ReferencePoints[loop], m_ReferencePoints[loop - 1]);
}
AttachPoint(m_ReferencePoints[m_ReferencePoints.Count - 1], m_ReferencePoints[1]);
}
}
///
/// Update the sprite after changing the rotation lock flag
///
public void UpdateRotationLock()
{
if(m_CentralPoint != null)
{
if(m_2DMode)
{
Rigidbody2D centreRigidBody = m_CentralPoint.Body2D;
RigidbodyConstraints2D constraints = centreRigidBody.constraints;
if(m_LockRotation)
{
constraints |= RigidbodyConstraints2D.FreezeRotation;
}
else
{
constraints &= ~RigidbodyConstraints2D.FreezeRotation;
}
centreRigidBody.constraints = constraints;
centreRigidBody.isKinematic = m_CentralBodyKinematic;
}
else
{
Rigidbody centreRigidBody = m_CentralPoint.Body3D;
// Fix the body to the 2D plane
RigidbodyConstraints constraints = centreRigidBody.constraints;
if(m_LockRotation)
{
constraints |= RigidbodyConstraints.FreezeRotationZ;
}
else
{
constraints &= ~RigidbodyConstraints.FreezeRotationZ;
}
centreRigidBody.constraints = constraints;
centreRigidBody.isKinematic = m_CentralBodyKinematic;
}
}
}
///
/// Add a reference point - essentially just a rigid body + circle collider - at the given
/// position and with the given properties
///
ReferencePoint AddReferencePoint(Vector3 position, float radius, bool lockRotation)
{
position = Quaternion.Euler(0, 0, m_SoftBodyRotation) * position;
GameObject referencePointObject = new GameObject();
referencePointObject.name = this.name + " Ref Point " + m_ReferencePoints.Count.ToString();
referencePointObject.transform.parent = m_ReferencePointParent.transform;
referencePointObject.transform.position = m_Transform.TransformPoint(position);
referencePointObject.layer = gameObject.layer;
referencePointObject.tag = gameObject.tag;
JellySpriteReferencePoint refPointBehaviour = referencePointObject.AddComponent();
refPointBehaviour.ParentJellySprite = this.gameObject;
refPointBehaviour.Index = m_ReferencePoints.Count;
ReferencePoint referencePoint = null;
if(m_2DMode)
{
CircleCollider2D circleCollider = referencePointObject.AddComponent();
circleCollider.radius = radius;
circleCollider.sharedMaterial = m_PhysicsMaterial2D;
Rigidbody2D newRigidBody = referencePointObject.AddComponent();
if(lockRotation)
{
newRigidBody.constraints |= RigidbodyConstraints2D.FreezeRotation;
}
newRigidBody.interpolation = m_Interpolation2D;
newRigidBody.collisionDetectionMode = m_CollisionDetectionMode2D;
referencePoint = new ReferencePoint(newRigidBody);
}
else
{
SphereCollider circleCollider = referencePointObject.AddComponent();
circleCollider.radius = radius;
circleCollider.sharedMaterial = m_PhysicsMaterial;
Rigidbody newRigidBody = referencePointObject.AddComponent();
// Fix the body to the 2D plane
RigidbodyConstraints constraints = newRigidBody.constraints;
constraints |= RigidbodyConstraints.FreezePositionZ;
constraints |= RigidbodyConstraints.FreezeRotationX;
constraints |= RigidbodyConstraints.FreezeRotationY;
// Prevent rotation unless desired
constraints |= RigidbodyConstraints.FreezeRotationX;
constraints |= RigidbodyConstraints.FreezeRotationY;
if(lockRotation)
{
constraints |= RigidbodyConstraints.FreezeRotationZ;
}
newRigidBody.constraints = constraints;
newRigidBody.interpolation = m_Interpolation;
newRigidBody.collisionDetectionMode = m_CollisionDetectionMode;
referencePoint = new ReferencePoint(newRigidBody);
}
m_ReferencePoints.Add(referencePoint);
return referencePoint;
}
///
/// Attach two reference points together using a spring joint
///
void AttachPoint(ReferencePoint point1, ReferencePoint point2)
{
if(m_2DMode)
{
SpringJoint2D joint = point1.Body2D.gameObject.AddComponent();
joint.connectedBody = point2.Body2D;
joint.connectedAnchor = point1.Body2D.transform.localPosition - point2.Body2D.transform.localPosition;
joint.distance = 0.0f;
#if UNITY_5
joint.enableCollision = m_CollideConnected;
#else
joint.enableCollision = m_CollideConnected;
#endif
joint.frequency = m_Stiffness;
joint.dampingRatio = m_DampingRatio;
joint.autoConfigureDistance = false;
}
else
{
SpringJoint joint = point1.Body3D.gameObject.AddComponent();
joint.autoConfigureConnectedAnchor = false;
joint.connectedBody = point2.Body3D;
joint.connectedAnchor = point1.Body3D.transform.localPosition - point2.Body3D.transform.localPosition;
joint.minDistance = 0.0f;
joint.maxDistance = 0.0f;
joint.enableCollision = m_CollideConnected;
joint.spring = m_Stiffness;
joint.damper = m_DampingRatio;
}
}
///
/// Each vertex takes its position from the movement of the reference points, with closer reference
/// points contributing more to the final position than those far away. We can pre-calculate these weighting
/// values as they remain constant.
///
public void CalculateWeightingValues()
{
float inverseScaleX = 1.0f / m_Transform.localScale.x;
float inverseScaleY = 1.0f / m_Transform.localScale.y;
if(m_ReferencePoints != null)
{
m_ReferencePointWeightings = new float[m_Vertices.Length, m_ReferencePoints.Count];
for(int vertexIndex = 0; vertexIndex < m_Vertices.Length; vertexIndex++)
{
float distanceSum = 0.0f;
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy)
{
Vector3 offset = m_ReferencePoints[referencePointIndex].InitialOffset;
offset.x = offset.x * inverseScaleX;
offset.y = offset.y * inverseScaleY;
float distance = Vector2.Distance(offset, m_Vertices[vertexIndex]);
distance = Mathf.Pow(distance, m_DistanceExponent);
float invDistance = float.MaxValue;
if(distance > 0.0f)
{
invDistance = 1.0f/distance;
}
distanceSum += invDistance;
}
}
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy)
{
Vector3 offset = m_ReferencePoints[referencePointIndex].InitialOffset;
offset.x = offset.x * inverseScaleX;
offset.y = offset.y * inverseScaleY;
float distance = Vector2.Distance(offset, m_Vertices[vertexIndex]);
distance = Mathf.Pow(distance, m_DistanceExponent);
float invDistance = float.MaxValue;
if(distance > 0.0f)
{
invDistance = 1.0f/distance;
}
m_ReferencePointWeightings[vertexIndex, referencePointIndex] = invDistance/distanceSum;
}
}
}
}
if(m_AttachPoints != null && m_ReferencePoints != null)
{
m_AttachPointWeightings = new float[m_AttachPoints.Length, m_ReferencePoints.Count];
for(int attachPointIndex = 0; attachPointIndex < m_AttachPoints.Length; attachPointIndex++)
{
float distanceSum = 0.0f;
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy)
{
Vector3 offset = m_ReferencePoints[referencePointIndex].InitialOffset;
offset.x = offset.x * inverseScaleX;
offset.y = offset.y * inverseScaleY;
float distance = Vector2.Distance(offset, m_AttachPoints[attachPointIndex].localPosition);
distance = Mathf.Pow(distance, m_DistanceExponent);
float invDistance = float.MaxValue;
if(distance > 0.0f)
{
invDistance = 1.0f/distance;
}
distanceSum += invDistance;
}
}
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy)
{
Vector3 offset = m_ReferencePoints[referencePointIndex].InitialOffset;
offset.x = offset.x * inverseScaleX;
offset.y = offset.y * inverseScaleY;
float distance = Vector2.Distance(offset, m_AttachPoints[attachPointIndex].localPosition);
distance = Mathf.Pow(distance, m_DistanceExponent);
float invDistance = float.MaxValue;
if(distance > 0.0f)
{
invDistance = 1.0f/distance;
}
m_AttachPointWeightings[attachPointIndex, referencePointIndex] = invDistance/distanceSum;
}
}
}
}
}
///
/// Disable reference points from colliding with one another
///
void SetupCollisions()
{
if(m_ReferencePoints != null)
{
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
for(int comparisonPointIndex = 0; comparisonPointIndex < m_ReferencePoints.Count; comparisonPointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy && !m_ReferencePoints[comparisonPointIndex].IsDummy)
{
if(m_2DMode)
{
if(referencePointIndex != comparisonPointIndex)
{
#if UNITY_4_3
// No support for 2D IgnoreCollision in Unity < 4.5
if(!Physics2D.GetIgnoreLayerCollision(this.gameObject.layer, this.gameObject.layer))
{
Debug.LogError("Layer '" + LayerMask.LayerToName(this.gameObject.layer) + "' is set to collide with itself - soft body physics will not work as intended. Please disable collisions between this layer and itself (Edit->Project Settings->Physics 2D)");
return;
}
#else
Physics2D.IgnoreCollision(m_ReferencePoints[referencePointIndex].Collider2D, m_ReferencePoints[comparisonPointIndex].Collider2D);
#endif
}
}
else
{
if(referencePointIndex != comparisonPointIndex)
{
Physics.IgnoreCollision(m_ReferencePoints[referencePointIndex].Collider, m_ReferencePoints[comparisonPointIndex].Collider);
}
}
}
}
}
}
}
///
/// Initialise the render material
///
protected abstract void InitMaterial();
///
/// Check if the source sprite is rotated
///
protected abstract bool IsSourceSpriteRotated();
///
/// Initialise the grid of vertices that will be used to render this object.
///
void InitVertices(Bounds spriteBounds)
{
float width = spriteBounds.size.x * m_SpriteScale.x;
float height = spriteBounds.size.y * m_SpriteScale.y;
// Work out how many nodes we need in each direction
float nodeDistance = Mathf.Min(width, height)/m_VertexDensity;
int vertexGridWidth = Mathf.CeilToInt(width/nodeDistance);
int vertexGridHeight = Mathf.CeilToInt(height/nodeDistance);
// Set up our texture coordinates for each vertex
int numVertices = vertexGridWidth * vertexGridHeight;
m_Vertices = new Vector3[numVertices];
m_InitialVertexPositions = new Vector3[numVertices];
m_Colors = new Color[numVertices];
m_TexCoords = new Vector2[numVertices];
m_Triangles = new int[numVertices * 6];
bool rotated = IsSourceSpriteRotated();
// Work out vertex positions and texture coordinates
for(int x = 0; x < vertexGridWidth; x++)
{
for(int y = 0; y < vertexGridHeight; y++)
{
int vertexIndex = (x * vertexGridHeight) + y;
Vector2 uv = Vector2.zero;
uv.x = x/((float)vertexGridWidth - 1);
uv.y = y/((float)vertexGridHeight - 1);
if(m_FlipX)
{
uv.x = 1.0f - uv.x;
}
if(m_FlipY)
{
uv.y = 1.0f - uv.y;
}
if(rotated)
{
float temp = uv.x;
uv.x = 1.0f;
uv.y = temp;
}
m_TexCoords[vertexIndex] = uv;
m_Colors[vertexIndex] = m_Color;
Vector3 vertexPosition = Vector3.zero;
vertexPosition.x = (-width * 0.5f) + ((width/((float)vertexGridWidth - 1)) * x);
vertexPosition.y = (-height * 0.5f) + ((height/((float)vertexGridHeight - 1)) * y);
vertexPosition = Quaternion.Euler(0, 0, m_SpriteRotation) * vertexPosition;
m_Vertices[vertexIndex] = vertexPosition;
}
}
m_Vertices.CopyTo(m_InitialVertexPositions, 0);
// Generate triangle indices
int numTriangles = 0;
for(int x = 0; x < vertexGridWidth - 1; x++)
{
for(int y = 0; y < vertexGridHeight - 1; y++)
{
int p0 = (x * vertexGridHeight) + y;
int p1 = (x * vertexGridHeight) + (y + 1);
int p2 = ((x + 1) * vertexGridHeight) + (y + 1);
int p3 = ((x + 1) * vertexGridHeight) + y;
m_Triangles[numTriangles++] = p0;
m_Triangles[numTriangles++] = p1;
m_Triangles[numTriangles++] = p2;
m_Triangles[numTriangles++] = p3;
m_Triangles[numTriangles++] = p0;
m_Triangles[numTriangles++] = p2;
}
}
Vector2 minTextureCoords;
Vector2 maxTextureCoords;
GetMinMaxTextureRect(out minTextureCoords, out maxTextureCoords);
FixupTextureCoordinates(minTextureCoords, maxTextureCoords);
}
///
/// Called if you need to reinitialise the material at runtime (eg. animating the
/// sprite's texture
///
public void ReInitMaterial()
{
InitMaterial();
}
///
/// Updates the texture coords.
///
public void UpdateTextureCoords()
{
if(m_SpriteMesh)
{
Bounds spriteBounds = GetSpriteBounds();
float width = spriteBounds.size.x * m_SpriteScale.x;
float height = spriteBounds.size.y * m_SpriteScale.y;
// Work out how many nodes we need in each direction
float nodeDistance = Mathf.Min(width, height)/m_VertexDensity;
int vertexGridWidth = Mathf.CeilToInt(width/nodeDistance);
int vertexGridHeight = Mathf.CeilToInt(height/nodeDistance);
bool rotated = IsSourceSpriteRotated();
// Work out vertex positions and texture coordinates
for(int x = 0; x < vertexGridWidth; x++)
{
for(int y = 0; y < vertexGridHeight; y++)
{
int vertexIndex = (x * vertexGridHeight) + y;
Vector2 uv = Vector2.zero;
uv.x = x/((float)vertexGridWidth - 1);
uv.y = y/((float)vertexGridHeight - 1);
if(m_FlipX)
{
uv.x = 1.0f - uv.x;
}
if(m_FlipY)
{
uv.y = 1.0f - uv.y;
}
if(rotated)
{
float temp = uv.x;
uv.x = 1.0f;
uv.y = temp;
}
m_TexCoords[vertexIndex] = uv;
}
}
Vector2 minTextureCoords;
Vector2 maxTextureCoords;
GetMinMaxTextureRect(out minTextureCoords, out maxTextureCoords);
FixupTextureCoordinates(minTextureCoords, maxTextureCoords);
m_SpriteMesh.uv = m_TexCoords;
}
}
///
/// Flip the sprite horizontally
///
public void SetFlipHorizontal(bool flipHorizontal)
{
if(flipHorizontal != m_FlipX)
{
m_FlipX = flipHorizontal;
UpdateTextureCoords();
for(int loop = 0; loop < m_AttachPoints.Length; loop++)
{
Vector3 offset = m_InitialAttachPointPositions[loop];
Vector3 rotation = m_AttachPoints[loop].localEulerAngles;
offset.x *= -1.0f;
rotation.y = m_FlipX ? 180.0f : 0.0f;
m_InitialAttachPointPositions[loop] = offset;
m_AttachPoints[loop].localEulerAngles = rotation;
}
}
}
///
/// Flip the sprite vertically
///
public void SetFlipVertical(bool flipVertical)
{
if(flipVertical != m_FlipY)
{
m_FlipY = flipVertical;
UpdateTextureCoords();
for(int loop = 0; loop < m_AttachPoints.Length; loop++)
{
Vector3 offset = m_InitialAttachPointPositions[loop];
Vector3 rotation = m_AttachPoints[loop].localEulerAngles;
offset.y *= -1.0f;
rotation.x = m_FlipY ? 180.0f : 0.0f;
m_InitialAttachPointPositions[loop] = offset;
m_AttachPoints[loop].localEulerAngles = rotation;
}
}
}
///
/// Sets the position of the Jelly Sprite
///
public void SetPosition(Vector3 position, bool resetVelocity)
{
if (CentralPoint == null || CentralPoint.transform == null)
{
this.transform.position = position;
return;
}
Vector3 offset = position - CentralPoint.transform.position;
foreach(JellySprite.ReferencePoint referencePoint in ReferencePoints)
{
if(!referencePoint.IsDummy)
{
referencePoint.transform.position = referencePoint.transform.position + offset;
if(resetVelocity)
{
if(referencePoint.Body2D)
{
referencePoint.Body2D.angularVelocity = 0.0f;
referencePoint.Body2D.linearVelocity = Vector2.zero;
}
else if(referencePoint.Body3D)
{
referencePoint.Body3D.angularVelocity = Vector3.zero;
referencePoint.Body3D.linearVelocity = Vector3.zero;
}
}
}
}
}
///
/// Reset the jelly sprite bodies back to their original offsets, and places the Jelly Sprite at
/// the given positon/rotation
///
public void Reset(Vector3 position, Vector3 rotation)
{
m_CentralPoint.transform.position = position;
m_CentralPoint.transform.eulerAngles = rotation;
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy && m_ReferencePoints[referencePointIndex] != m_CentralPoint)
{
ReferencePoint referencePoint = m_ReferencePoints[referencePointIndex];
referencePoint.transform.localPosition = m_CentralPoint.transform.TransformPoint(referencePoint.InitialOffset);
if(referencePoint.Body2D)
{
referencePoint.Body2D.angularVelocity = 0.0f;
referencePoint.Body2D.linearVelocity = Vector2.zero;
}
else if(referencePoint.Body3D)
{
referencePoint.Body3D.angularVelocity = Vector3.zero;
referencePoint.Body3D.linearVelocity = Vector3.zero;
}
}
}
UpdateMesh();
UpdateAttachPoints();
}
///
/// Sets whether or not the Jelly Sprite is kinematic
///
public void SetKinematic(bool isKinematic, bool centralPointOnly)
{
foreach(JellySprite.ReferencePoint referencePoint in ReferencePoints)
{
if(!referencePoint.IsDummy)
{
if(referencePoint == m_CentralPoint || !centralPointOnly)
{
if(referencePoint.Body2D)
{
referencePoint.Body2D.isKinematic = isKinematic;
}
else if(referencePoint.Body3D)
{
referencePoint.Body3D.isKinematic = isKinematic;
}
}
}
}
}
///
/// Rotate the whole jelly sprite by the given angle
///
public void Rotate(float angleChange)
{
Vector3 eulerAngleChange = new Vector3(0.0f, 0.0f, angleChange);
// Rotate the central body by the required amount
CentralPoint.transform.localEulerAngles = CentralPoint.transform.localEulerAngles + eulerAngleChange;
// Now go through all the reference points and orbit them around the central body by the required amount
foreach(ReferencePoint referencePoint in ReferencePoints)
{
if(!referencePoint.IsDummy)
{
Vector3 referencePointPosition = referencePoint.transform.position;
Vector3 centralPointPosition = m_Transform.position;
referencePoint.transform.position = centralPointPosition + (Quaternion.Euler(eulerAngleChange) * (referencePointPosition - centralPointPosition));
}
}
}
///
/// Check if the Jelly Sprite is touching the given layer. You can specify how many physics bodies need to be touching for the
/// whole Jelly Sprite to be classes as grounded
///
public bool IsGrounded(LayerMask groundLayer, int minGroundedBodies)
{
int numGroundedBodies = 0;
foreach(JellySprite.ReferencePoint referencePoint in ReferencePoints)
{
if(!referencePoint.IsDummy)
{
if(referencePoint.Collider)
{
SphereCollider sphereCollider = referencePoint.Collider;
if(Physics.CheckSphere(sphereCollider.bounds.center + new Vector3(0, -sphereCollider.radius * 0.1f, 0), sphereCollider.radius, groundLayer))
{
numGroundedBodies++;
if(numGroundedBodies >= minGroundedBodies)
{
return true;
}
}
}
else if(referencePoint.Collider2D)
{
CircleCollider2D circleCollider = referencePoint.Collider2D;
Vector2 bodyPosition = referencePoint.transform.position;
if(Physics2D.OverlapCircle(bodyPosition + new Vector2(0, -circleCollider.radius * 0.1f), circleCollider.radius, groundLayer))
{
numGroundedBodies++;
if(numGroundedBodies >= minGroundedBodies)
{
return true;
}
}
}
}
}
return false;
}
protected abstract void GetMinMaxTextureRect(out Vector2 min, out Vector2 max);
///
/// Adjust our texture coordinates from a 0-1 scale to point at the correct offset into the
/// sprite rectangle
void FixupTextureCoordinates(Vector2 minTextureCoords, Vector2 maxTextureCoords)
{
for(int vertexIndex = 0; vertexIndex < m_Vertices.Length; vertexIndex++)
{
Vector2 spriteOffset = maxTextureCoords - minTextureCoords;
spriteOffset.Scale(m_TexCoords[vertexIndex]);
m_TexCoords[vertexIndex] = minTextureCoords + spriteOffset;
}
}
///
/// Resizes the attach point array
///
public void ResizeAttachPoints()
{
Transform[] oldAttachPoints = new Transform[m_AttachPoints.Length];
bool[] oldIsAttachPointJellySprite = new bool[m_AttachPoints.Length];
Vector3[] oldInitialAttachPointPositions = new Vector3[m_AttachPoints.Length];
m_AttachPoints.CopyTo(oldAttachPoints, 0);
m_IsAttachPointJellySprite.CopyTo(oldIsAttachPointJellySprite, 0);
m_InitialAttachPointPositions.CopyTo(oldInitialAttachPointPositions, 0);
m_AttachPoints = new Transform[m_NumAttachPoints];
m_IsAttachPointJellySprite = new bool[m_NumAttachPoints];
m_InitialAttachPointPositions = new Vector3[m_NumAttachPoints];
for(int loop = 0; loop < m_NumAttachPoints && loop < oldAttachPoints.Length; loop++)
{
m_AttachPoints[loop] = oldAttachPoints[loop];
m_IsAttachPointJellySprite[loop] = oldIsAttachPointJellySprite[loop];
m_InitialAttachPointPositions[loop] = oldInitialAttachPointPositions[loop];
}
if(m_AttachPointWeightings != null)
{
float[,] oldAttachPointWeightings = new float[m_AttachPointWeightings.GetLength(0),m_AttachPointWeightings.GetLength(1)];
for(int x = 0; x < m_AttachPointWeightings.GetLength(0); x++)
{
for(int y = 0; y < m_AttachPointWeightings.GetLength(1); y++)
{
oldAttachPointWeightings[x, y] = m_AttachPointWeightings[x, y];
}
}
m_AttachPointWeightings = new float[m_AttachPoints.Length, m_ReferencePoints.Count];
for(int x = 0; x < oldAttachPointWeightings.GetLength(0); x++)
{
for(int y = 0; y < oldAttachPointWeightings.GetLength(1); y++)
{
if(x < m_AttachPoints.Length)
{
m_AttachPointWeightings[x, y] = oldAttachPointWeightings[x, y];
}
}
}
}
}
///
/// Called when free mode is selected for the first time - copies all existing points to
/// the free mode configuration
///
public void OnCopyToFreeModeSelected()
{
if(IsSpriteValid())
{
m_FreeModeBodyPositions.Clear();
m_FreeModeBodyRadii.Clear();
m_FreeModeBodyKinematic.Clear();
Bounds spriteBounds = GetSpriteBounds();
float width = spriteBounds.size.x * m_SoftBodyScale.x * m_SpriteScale.x;
float height = spriteBounds.size.y * m_SoftBodyScale.y * m_SpriteScale.y;
switch(m_Style)
{
case PhysicsStyle.Circle:
{
width = spriteBounds.size.x * m_SpriteScale.x;
height = spriteBounds.size.y * m_SpriteScale.x;
int numPoints = m_RadiusPoints;
float radius = width * 0.5f;
float sphereRadius = m_SphereRadius * transform.localScale.x;
AddFreeModeBodyDefinition(m_CentralBodyOffset, width * sphereRadius, m_CentralBodyKinematic);
for(int loop = 0; loop < numPoints; loop++)
{
float angle = ((Mathf.PI * 2)/numPoints) * loop;
Vector2 offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
offset *= radius;
offset.x *= m_SoftBodyScale.x;
offset.y *= m_SoftBodyScale.y;
Vector3 bodyPosition = offset * (1.0f - ((sphereRadius * width) / (transform.localScale.x * offset.magnitude))) + m_SoftBodyOffset;
AddFreeModeBodyDefinition(bodyPosition, width * sphereRadius, false);
}
}
break;
case PhysicsStyle.Triangle:
{
float radius = spriteBounds.size.y * m_SphereRadius * m_SpriteScale.y * transform.localScale.y;
float offsetFactor = 0.5f - m_SphereRadius;
AddFreeModeBodyDefinition(m_CentralBodyOffset, radius, m_CentralBodyKinematic);
AddFreeModeBodyDefinition(new Vector2(-width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset, radius, false);
AddFreeModeBodyDefinition(new Vector2(width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset, radius, false);
AddFreeModeBodyDefinition(new Vector2(0.0f, height * offsetFactor) + m_SoftBodyOffset, radius, false);
}
break;
case PhysicsStyle.Rectangle:
{
float radius = spriteBounds.size.y * m_SphereRadius * m_SpriteScale.y * transform.localScale.y;
float offsetFactor = 0.5f - m_SphereRadius;
AddFreeModeBodyDefinition(m_CentralBodyOffset, radius, m_CentralBodyKinematic);
AddFreeModeBodyDefinition(new Vector2(-width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset, radius, false);
AddFreeModeBodyDefinition(new Vector2(width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset, radius, false);
AddFreeModeBodyDefinition(new Vector2(-width * offsetFactor, height * offsetFactor) + m_SoftBodyOffset, radius, false);
AddFreeModeBodyDefinition(new Vector2(width * offsetFactor, height * offsetFactor) + m_SoftBodyOffset, radius, false);
}
break;
case PhysicsStyle.Free:
break;
case PhysicsStyle.Grid:
{
width -= (m_SphereRadius * 4);
height -= (m_SphereRadius * 4);
float radius = spriteBounds.size.x * m_SphereRadius * m_SpriteScale.x * m_Transform.localScale.x;
AddFreeModeBodyDefinition(Vector2.zero, radius, m_CentralBodyKinematic);
int columns = m_GridColumns;
int rows = m_GridRows;
for(int y = 0; y < rows; y++)
{
for(int x = 0; x < (y % 2 == 0? columns : columns - 1); x++)
{
Vector2 position;
position.x = (-width * 0.5f) + ((width/(float)(columns - 1)) * x);
if(y % 2 != 0)
{
position.x += width/(float)(columns - 1) * 0.5f;
}
position.y = (-height * 0.5f) + ((height/(float)(rows - 1)) * y);
position += m_SoftBodyOffset;
AddFreeModeBodyDefinition(position, radius, false);
}
}
}
break;
}
m_Style = PhysicsStyle.Free;
m_SoftBodyOffset = Vector3.zero;
m_SoftBodyRotation = 0.0f;
m_SoftBodyScale = Vector3.one;
}
}
///
/// First time setup of the mesh
///
void InitMesh()
{
MeshFilter meshFilter = GetComponent();
m_SpriteMesh = new Mesh();
m_SpriteMesh.name = "JellySprite Mesh";
m_SpriteMesh.MarkDynamic();
meshFilter.mesh = m_SpriteMesh;
m_SpriteMesh.Clear();
m_SpriteMesh.vertices = m_Vertices;
m_SpriteMesh.uv = m_TexCoords;
m_SpriteMesh.triangles = m_Triangles;
m_SpriteMesh.colors = m_Colors;
m_SpriteMesh.RecalculateBounds();
m_SpriteMesh.RecalculateNormals();
}
///
/// Update the vertex positions of the mesh
///
void UpdateMesh()
{
// For each vertex, look at the offset values of each reference point and apply the same offset
// (scaled by the weighting value) to the vertex's position
if(Application.isPlaying)
{
// Calculate reference point offsets
bool haveAnyPointsMoved = false;
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy && m_ReferencePoints[referencePointIndex] != m_CentralPoint)
{
ReferencePoint referencePoint = m_ReferencePoints[referencePointIndex];
Vector3 offset = m_CentralPoint.transform.InverseTransformPoint(referencePoint.transform.position);
offset -= referencePoint.InitialOffset;
if(haveAnyPointsMoved || m_ReferencePointOffsets[referencePointIndex] != offset)
{
m_ReferencePointOffsets[referencePointIndex] = offset;
haveAnyPointsMoved = true;
}
}
else
{
m_ReferencePointOffsets[referencePointIndex] = Vector3.zero;
}
}
if(!haveAnyPointsMoved)
{
return;
}
int numVertices = m_Vertices.Length;
int numReferencePoints = m_ReferencePoints.Count;
int centralPointIndex = GetCentralPointIndex();
for(int vertexIndex = 0; vertexIndex < numVertices; vertexIndex++)
{
Vector3 totalOffset = Vector3.zero;
for(int referencePointIndex = 0; referencePointIndex < numReferencePoints; referencePointIndex++)
{
if(referencePointIndex != centralPointIndex && !m_ReferencePoints[referencePointIndex].IsDummy)
{
totalOffset += m_ReferencePointOffsets[referencePointIndex] * m_ReferencePointWeightings[vertexIndex, referencePointIndex];
}
}
m_Vertices[vertexIndex] = m_InitialVertexPositions[vertexIndex] + totalOffset + new Vector3(m_CentralBodyOffset.x, m_CentralBodyOffset.y, 0);
}
// Update the mesh
m_SpriteMesh.vertices = m_Vertices;
m_SpriteMesh.RecalculateBounds();
m_SpriteMesh.RecalculateNormals();
}
}
///
/// Gets the index of the central point.
///
int GetCentralPointIndex()
{
int numReferencePoints = m_ReferencePoints.Count;
for(int referencePointIndex = 0; referencePointIndex < numReferencePoints; referencePointIndex++)
{
if(m_ReferencePoints[referencePointIndex] == m_CentralPoint)
{
return referencePointIndex;
}
}
return -1;
}
///
/// Update the attach point positions
///
void UpdateAttachPoints()
{
// For each vertex, look at the offset values of each reference point and apply the same offset
// (scaled by the weighting value) to the vertex's position
if(Application.isPlaying)
{
Quaternion additionalBodyRotation = Quaternion.Euler(0, 0, m_SoftBodyRotation);
Vector3 rotatedBodyOffset = additionalBodyRotation * m_CentralBodyOffset;
int numAttachPoints = m_AttachPoints.Length;
int numReferencePoints = m_ReferencePoints.Count;
int centralPointIndex = GetCentralPointIndex();
for(int attachPointIndex = 0; attachPointIndex < numAttachPoints; attachPointIndex++)
{
Vector3 totalOffset = Vector3.zero;
for(int referencePointIndex = 0; referencePointIndex < numReferencePoints; referencePointIndex++)
{
if(referencePointIndex != centralPointIndex && !m_ReferencePoints[referencePointIndex].IsDummy)
{
ReferencePoint referencePoint = m_ReferencePoints[referencePointIndex];
Vector3 offset = m_CentralPoint.transform.InverseTransformPoint(referencePoint.transform.position);
offset -= referencePoint.InitialOffset;
totalOffset += offset * m_AttachPointWeightings[attachPointIndex, referencePointIndex];
}
}
// Attached Jelly Sprites need to behave slightly differently from regular objects - we set the central
// body to be kinematic and then adjust the position of this, which allows the Jelly Sprite to track the
// attach point position while still being able to wobble around
if(m_IsAttachPointJellySprite[attachPointIndex])
{
JellySprite attachedJellySprite = m_AttachPoints[attachPointIndex].GetComponent();
attachedJellySprite.CentralPoint.transform.parent = m_Transform;
attachedJellySprite.CentralPoint.SetKinematic(true);
attachedJellySprite.CentralPoint.transform.localPosition = m_InitialAttachPointPositions[attachPointIndex] + totalOffset + rotatedBodyOffset;
}
else
{
m_AttachPoints[attachPointIndex].transform.localPosition = m_InitialAttachPointPositions[attachPointIndex] + totalOffset + rotatedBodyOffset;
}
}
}
}
///
/// Add a force to every reference point
///
public void AddForce(Vector2 force)
{
if(m_ReferencePoints != null)
{
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(referencePoint.Body2D)
{
referencePoint.Body2D.AddForce(force);
}
if(referencePoint.Body3D)
{
referencePoint.Body3D.AddForce(force);
}
}
}
}
///
/// Add a force at a given position to every reference point
///
public void AddForceAtPosition(Vector2 force, Vector2 position)
{
if(m_ReferencePoints != null)
{
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(referencePoint.Body2D)
{
referencePoint.Body2D.AddForceAtPosition(force, position);
}
if(referencePoint.Body3D)
{
referencePoint.Body3D.AddForceAtPosition(force, position);
}
}
}
}
///
/// Called when the editor wants to update the visible mesh
///
public void RefreshMesh()
{
if(IsSpriteValid())
{
InitVertices(GetSpriteBounds());
InitMaterial();
InitMesh();
if(m_ReferencePoints != null)
{
CalculateInitialOffsets();
CalculateWeightingValues();
}
UpdateMesh();
}
}
///
/// Set up the mass of each rigidbody
///
public void InitMass()
{
if(m_ReferencePoints != null)
{
float mass = m_Mass;
// If the mass is being defined on a global scale, then for n rigid
// bodies, each one has 1/n of the total mass.
if(m_MassStyle == MassStyle.Global)
{
int numNonDummyReferencePoints = 0;
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(!referencePoint.IsDummy)
{
numNonDummyReferencePoints++;
}
}
mass /= numNonDummyReferencePoints;
}
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(!referencePoint.IsDummy)
{
if(referencePoint.Body2D)
{
referencePoint.Body2D.mass = mass;
referencePoint.Body2D.gravityScale = m_GravityScale;
referencePoint.Body2D.angularDamping = m_AngularDrag;
referencePoint.Body2D.linearDamping = m_Drag;
referencePoint.Body2D.interpolation = m_Interpolation2D;
referencePoint.Body2D.collisionDetectionMode = m_CollisionDetectionMode2D;
}
if(referencePoint.Body3D)
{
referencePoint.Body3D.mass = mass;
referencePoint.Body3D.useGravity = m_UseGravity;
referencePoint.Body3D.angularDamping = m_AngularDrag;
referencePoint.Body3D.linearDamping = m_Drag;
referencePoint.Body3D.interpolation = m_Interpolation;
referencePoint.Body3D.collisionDetectionMode = m_CollisionDetectionMode;
}
}
}
}
}
///
/// Reapply our spring/damping values to each joint
///
public void UpdateJoints()
{
if(m_ReferencePoints != null)
{
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(!referencePoint.IsDummy)
{
if(referencePoint.Body2D != null)
{
SpringJoint2D[] joints = referencePoint.Body2D.gameObject.GetComponents();
if(joints != null)
{
for(int jointIndex = 0; jointIndex < joints.Length; jointIndex++)
{
joints[jointIndex].frequency = m_Stiffness;
joints[jointIndex].dampingRatio = m_DampingRatio;
}
}
}
if(referencePoint.Body3D != null)
{
SpringJoint[] joints = referencePoint.Body3D.gameObject.GetComponents();
if(joints != null)
{
for(int jointIndex = 0; jointIndex < joints.Length; jointIndex++)
{
joints[jointIndex].spring = m_Stiffness;
joints[jointIndex].damper = m_DampingRatio;
}
}
}
}
}
}
}
///
/// Use this function to scale the Jelly Sprite at runtime. Scales the rigid bodies and
/// rendered mesh by the given amount
///
public void Scale(float scaleRatio, bool scaleAttachedObjects = true)
{
int index = 0;
Vector3[] refPointPositions = new Vector3[m_ReferencePoints.Count];
foreach (ReferencePoint refPoint in m_ReferencePoints)
{
if (refPoint.GameObject)
{
refPointPositions[index] = refPoint.transform.position;
}
index++;
}
m_Transform.localScale = m_Transform.localScale * scaleRatio;
index = 0;
foreach (ReferencePoint refPoint in m_ReferencePoints)
{
if (refPoint.GameObject)
{
if (!refPoint.IsDummy)
{
if (refPoint.Body2D)
{
CircleCollider2D circleCollider = refPoint.GameObject.GetComponent();
if (circleCollider)
{
circleCollider.radius = circleCollider.radius * scaleRatio;
}
}
else
{
SphereCollider sphereCollider = refPoint.GameObject.GetComponent();
if (sphereCollider)
{
sphereCollider.radius = sphereCollider.radius * scaleRatio;
}
}
}
refPoint.transform.position = refPointPositions[0] + ((refPointPositions[index] - refPointPositions[0]) * scaleRatio);
refPoint.InitialOffset *= scaleRatio;
if (m_2DMode)
{
SpringJoint2D[] springJoints = refPoint.GameObject.GetComponents();
for (int jointLoop = 0; jointLoop < springJoints.Length; jointLoop++)
{
springJoints[jointLoop].connectedAnchor = springJoints[jointLoop].connectedAnchor * scaleRatio;
springJoints[jointLoop].frequency *= scaleRatio;
}
}
else
{
SpringJoint[] springJoints = refPoint.GameObject.GetComponents();
for (int jointLoop = 0; jointLoop < springJoints.Length; jointLoop++)
{
springJoints[jointLoop].connectedAnchor = springJoints[jointLoop].connectedAnchor * scaleRatio;
}
}
}
index++;
}
if (!scaleAttachedObjects && scaleRatio > 0)
{
float inverseScale = 1.0f / scaleRatio;
for (int attachPointIndex = 0; attachPointIndex < m_AttachPoints.Length; attachPointIndex++)
{
m_AttachPoints[attachPointIndex].localScale *= inverseScale;
}
}
}
///
/// Attaches a new object to the Jelly Sprite at runtime
///
public void AddAttachPoint(Transform newAttachedObject)
{
m_NumAttachPoints++;
ResizeAttachPoints();
m_AttachPoints[m_NumAttachPoints - 1] = newAttachedObject;
JellySprite attachedJellySprite = newAttachedObject.GetComponent();
Vector3 position = m_CentralPoint.transform.InverseTransformPoint(newAttachedObject.position);
position.x /= m_Transform.localScale.x;
position.y /= m_Transform.localScale.y;
if(attachedJellySprite)
{
m_IsAttachPointJellySprite[m_NumAttachPoints - 1] = true;
m_InitialAttachPointPositions[m_NumAttachPoints - 1] = position;
}
else
{
m_IsAttachPointJellySprite[m_NumAttachPoints - 1] = false;
m_InitialAttachPointPositions[m_NumAttachPoints - 1] = position;
newAttachedObject.parent = m_Transform;
}
float distanceSum = 0.0f;
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy)
{
float distance = Vector2.Distance(m_ReferencePoints[referencePointIndex].InitialOffset, m_AttachPoints[m_NumAttachPoints - 1].localPosition);
distance = Mathf.Pow(distance, m_DistanceExponent);
float invDistance = float.MaxValue;
if(distance > 0.0f)
{
invDistance = 1.0f/distance;
}
distanceSum += invDistance;
}
}
for(int referencePointIndex = 0; referencePointIndex < m_ReferencePoints.Count; referencePointIndex++)
{
if(!m_ReferencePoints[referencePointIndex].IsDummy)
{
float distance = Vector2.Distance(m_ReferencePoints[referencePointIndex].InitialOffset, m_AttachPoints[m_NumAttachPoints - 1].localPosition);
distance = Mathf.Pow(distance, m_DistanceExponent);
float invDistance = float.MaxValue;
if(distance > 0.0f)
{
invDistance = 1.0f/distance;
}
m_AttachPointWeightings[m_NumAttachPoints - 1, referencePointIndex] = invDistance/distanceSum;
}
}
}
///
/// Wake up the whole body - useful for editor controls when they update a value
///
public void WakeUp()
{
if(m_ReferencePoints != null)
{
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(referencePoint.Body2D != null)
{
referencePoint.Body2D.WakeUp();
}
if(referencePoint.Body3D != null)
{
referencePoint.Body3D.WakeUp();
}
}
}
}
///
/// Update this instance.
///
void Update()
{
if(m_ReferencePoints != null)
{
#if UNITY_EDITOR
// Debug draw the joints that connect each node
if(Selection.activeGameObject == this.gameObject)
{
foreach(ReferencePoint referencePoint in m_ReferencePoints)
{
if(!referencePoint.IsDummy)
{
if(m_2DMode)
{
SpringJoint2D[] springJoints = referencePoint.Body2D.GetComponents();
for(int jointIndex = 0; jointIndex < springJoints.Length; jointIndex++)
{
Debug.DrawLine(springJoints[jointIndex].transform.position, springJoints[jointIndex].connectedBody.transform.position, Color.green);
}
}
else
{
SpringJoint[] springJoints = referencePoint.Body3D.GetComponents();
for(int jointIndex = 0; jointIndex < springJoints.Length; jointIndex++)
{
Debug.DrawLine(springJoints[jointIndex].transform.position, springJoints[jointIndex].connectedBody.transform.position, Color.green);
}
}
}
}
}
#endif
if(!m_ManualPositioning)
{
Quaternion additionalBodyRotation = Quaternion.Euler(0, 0, m_SoftBodyRotation);
Vector3 rotatedPostion = additionalBodyRotation * new Vector3(-m_CentralBodyOffset.x * m_Transform.localScale.x, -m_CentralBodyOffset.y * m_Transform.localScale.y, 0);
m_Transform.position = m_CentralPoint.transform.TransformPoint(rotatedPostion);
m_Transform.rotation = m_CentralPoint.transform.rotation;
}
// Apply our rigid body movements to the rendered mesh
UpdateMesh();
UpdateAttachPoints();
}
}
///
/// Add a position/radius pair to the free mode bodies
///
void AddFreeModeBodyDefinition(Vector2 position, float radius, bool kinematic)
{
position = Quaternion.Euler(0, 0, m_SoftBodyRotation) * position;
m_FreeModeBodyPositions.Add(position);
m_FreeModeBodyRadii.Add(radius);
m_FreeModeBodyKinematic.Add(kinematic);
}
///
/// Helper function to draw a sphere with a line connecting to it from the object's origin
///
void DrawSphereWithCentreConnection(Vector3 position, float radius, bool kinematic)
{
position = Quaternion.Euler(0, 0, m_SoftBodyRotation) * position;
Vector3 centralBodyPosition = Quaternion.Euler(0, 0, m_SoftBodyRotation) * m_CentralBodyOffset;
Vector3 worldPoint = this.transform.localToWorldMatrix.MultiplyPoint(position);
Vector3 originPoint = this.transform.localToWorldMatrix.MultiplyPoint(centralBodyPosition);
Gizmos.color = kinematic? Color.red : Color.green;
Gizmos.DrawWireSphere(worldPoint, radius);
Gizmos.color = Color.white;
Gizmos.DrawLine(worldPoint, originPoint);
}
///
/// Helper function to draw a sphere with a line connecting to it from the object's origin
///
void DrawSphereWithExplicitConnection(Vector3 position, Vector3 connectionPosition, float radius, bool kinematic)
{
position = Quaternion.Euler(0, 0, m_SoftBodyRotation) * position;
connectionPosition = Quaternion.Euler(0, 0, m_SoftBodyRotation) * connectionPosition;
Vector3 worldPoint = this.transform.localToWorldMatrix.MultiplyPoint(position);
Vector3 originPoint = this.transform.localToWorldMatrix.MultiplyPoint(connectionPosition);
Gizmos.color = kinematic? Color.red : Color.green;
Gizmos.DrawWireSphere(worldPoint, radius);
Gizmos.color = Color.white;
Gizmos.DrawLine(worldPoint, originPoint);
}
///
/// Helper function to draw a sphere with a line connecting to it from the object's origin
///
void DrawCentreConnection(Vector3 position, Vector3 centre)
{
position = Quaternion.Euler(0, 0, m_SoftBodyRotation) * position;
Vector3 centralBodyPosition = Quaternion.Euler(0, 0, m_SoftBodyRotation) * m_CentralBodyOffset;
Vector3 worldPoint = this.transform.localToWorldMatrix.MultiplyPoint(position);
Vector3 originPoint = this.transform.localToWorldMatrix.MultiplyPoint(centralBodyPosition);
Gizmos.color = Color.white;
Gizmos.DrawLine(worldPoint, originPoint);
}
///
/// Helper function to draw a sphere with a line connecting to it from the object's origin
///
void DrawCentreConnectionWithoutRotation(Vector3 position, Vector3 centre)
{
Vector3 worldPoint = this.transform.localToWorldMatrix.MultiplyPoint(position);
Vector3 originPoint = this.transform.localToWorldMatrix.MultiplyPoint(centre);
Gizmos.color = Color.white;
Gizmos.DrawLine(worldPoint, originPoint);
}
///
/// Draw the positions of the colliders when we select objects in the hierarchy
///
void OnDrawGizmosSelected ()
{
if(!Application.isPlaying && IsSpriteValid())
{
Bounds spriteBounds = GetSpriteBounds();
float width = spriteBounds.size.x * m_SoftBodyScale.x * m_SpriteScale.x;
float height = spriteBounds.size.y * m_SoftBodyScale.y * m_SpriteScale.y;
switch(m_Style)
{
case PhysicsStyle.Circle:
{
float sphereRadius = m_SphereRadius * transform.localScale.x;
width = spriteBounds.size.x * m_SpriteScale.x;
height = spriteBounds.size.y * m_SpriteScale.x;
int numPoints = m_RadiusPoints;
float radius = width * 0.5f;
Vector3 prevPosition = Vector3.zero;
Vector3 startPosition = Vector3.zero;
DrawSphereWithCentreConnection(m_CentralBodyOffset, width * sphereRadius, m_CentralBodyKinematic);
for(int loop = 0; loop < numPoints; loop++)
{
float angle = ((Mathf.PI * 2)/numPoints) * loop;
Vector2 offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
offset *= radius;
offset.x *= m_SoftBodyScale.x;
offset.y *= m_SoftBodyScale.y;
Vector3 bodyPosition = offset * (1.0f - ((sphereRadius * width) / (transform.localScale.x * offset.magnitude))) + m_SoftBodyOffset;
DrawSphereWithCentreConnection(bodyPosition, width * sphereRadius, false);
if(m_AttachNeighbors)
{
if(loop == 0)
{
startPosition = bodyPosition;
}
else
{
DrawCentreConnection(bodyPosition, prevPosition);
}
}
prevPosition = bodyPosition;
}
if(m_AttachNeighbors)
{
DrawCentreConnection(prevPosition, startPosition);
}
}
break;
case PhysicsStyle.Triangle:
{
float radius = spriteBounds.size.y * m_SphereRadius * m_SpriteScale.y * transform.localScale.y;
float offsetFactor = 0.5f - m_SphereRadius;
Vector2 point1 = new Vector2(-width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset;
Vector3 point2 = new Vector2(width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset;
Vector3 point3 = new Vector2(0.0f, height * offsetFactor) + m_SoftBodyOffset;
DrawSphereWithCentreConnection(m_CentralBodyOffset, radius, m_CentralBodyKinematic);
DrawSphereWithCentreConnection(point1, radius, false);
DrawSphereWithCentreConnection(point2, radius, false);
DrawSphereWithCentreConnection(point3, radius, false);
if(m_AttachNeighbors)
{
DrawCentreConnection(point1, point2);
DrawCentreConnection(point2, point3);
DrawCentreConnection(point3, point1);
}
}
break;
case PhysicsStyle.Rectangle:
{
float radius = spriteBounds.size.y * m_SphereRadius * m_SpriteScale.y * transform.localScale.y;
float offsetFactor = 0.5f - m_SphereRadius;
Vector2 point1 = new Vector2(-width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset;
Vector2 point2 = new Vector2(width * offsetFactor, -height * offsetFactor) + m_SoftBodyOffset;
Vector2 point3 = new Vector2(width * offsetFactor, height * offsetFactor) + m_SoftBodyOffset;
Vector2 point4 = new Vector2(-width * offsetFactor, height * offsetFactor) + m_SoftBodyOffset;
DrawSphereWithCentreConnection(m_CentralBodyOffset, radius, m_CentralBodyKinematic);
DrawSphereWithCentreConnection(point1, radius, false);
DrawSphereWithCentreConnection(point2, radius, false);
DrawSphereWithCentreConnection(point3, radius, false);
DrawSphereWithCentreConnection(point4, radius, false);
if(m_AttachNeighbors)
{
DrawCentreConnection(point1, point2);
DrawCentreConnection(point2, point3);
DrawCentreConnection(point3, point4);
DrawCentreConnection(point4, point1);
}
}
break;
case PhysicsStyle.Free:
{
if(m_FreeModeBodyPositions != null)
{
for(int loop = 1; loop < m_FreeModeBodyPositions.Count; loop++)
{
DrawCentreConnectionWithoutRotation(m_FreeModeBodyPositions[loop], m_FreeModeBodyPositions[0]);
}
}
}
break;
case PhysicsStyle.Line:
{
float radius = spriteBounds.size.x * m_SphereRadius * m_SpriteScale.x * this.transform.localScale.x;
// Always create an odd number of points so that we can correctly pick the central one
int numPoints = ((m_GridColumns/2) * 2) + 1;
Vector2 previousPosition = Vector2.zero;
for(int x = 0; x < numPoints; x++)
{
Vector2 position;
position.x = (-width * 0.5f) + ((width/(float)(numPoints - 1)) * x);
position.y = 0.0f;
bool isKinematic = false;
if(x == numPoints / 2)
{
position += m_CentralBodyOffset;
isKinematic = m_CentralBodyKinematic;
}
else
{
position += m_SoftBodyOffset;
}
if(x == 0)
{
previousPosition = position;
}
DrawSphereWithExplicitConnection(position, previousPosition, radius, isKinematic);
previousPosition = position;
}
}
break;
case PhysicsStyle.Grid:
{
width -= (m_SphereRadius * 4);
height -= (m_SphereRadius * 4);
float radius = spriteBounds.size.x * m_SphereRadius * m_SpriteScale.x * this.transform.localScale.x;
int columns = m_GridColumns;
int rows = m_GridRows;
for(int y = 0; y < rows; y++)
{
for(int x = 0; x < (y % 2 == 0? columns : columns - 1); x++)
{
Vector2 position;
position.x = (-width * 0.5f) + ((width/(float)(columns - 1)) * x);
if(y % 2 != 0)
{
position.x += width/(float)(columns - 1) * 0.5f;
}
position.y = (-height * 0.5f) + ((height/(float)(rows - 1)) * y);
position += m_SoftBodyOffset;
Vector3 worldPoint = this.transform.localToWorldMatrix.MultiplyPoint(position);
Gizmos.color = Color.green;
if(m_CentralBodyKinematic && x == columns/2 && y == rows/2)
{
Gizmos.color = Color.red;
}
Gizmos.DrawWireSphere(worldPoint, radius);
}
}
}
break;
}
}
}
}