427 lines
14 KiB
C#
427 lines
14 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
namespace UnityEngine.U2D
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Tangent mode for control points that defines the bezier curve.
|
|||
|
/// </summary>
|
|||
|
public enum ShapeTangentMode
|
|||
|
{
|
|||
|
/// <summary>Linear mode where tangents are zero.</summary>
|
|||
|
Linear = 0,
|
|||
|
/// <summary>Set left and right tangents so that the bezier curve is continuous.</summary>
|
|||
|
Continuous = 1,
|
|||
|
/// <summary>Set custom left and right tangents.</summary>
|
|||
|
Broken = 2,
|
|||
|
};
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Corner type to assign sprite.
|
|||
|
/// </summary>
|
|||
|
public enum CornerType
|
|||
|
{
|
|||
|
/// <summary>Outer top left Corner.</summary>
|
|||
|
OuterTopLeft,
|
|||
|
/// <summary>Outer top right Corner.</summary>
|
|||
|
OuterTopRight,
|
|||
|
/// <summary>Outer bottom left Corner.</summary>
|
|||
|
OuterBottomLeft,
|
|||
|
/// <summary>Outer bottom right Corner.</summary>
|
|||
|
OuterBottomRight,
|
|||
|
/// <summary>Inner top left Corner.</summary>
|
|||
|
InnerTopLeft,
|
|||
|
/// <summary>Inner top right Corner.</summary>
|
|||
|
InnerTopRight,
|
|||
|
/// <summary>Inner bottom left Corner.</summary>
|
|||
|
InnerBottomLeft,
|
|||
|
/// <summary>Inner bottom right Corner.</summary>
|
|||
|
InnerBottomRight,
|
|||
|
};
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Level of detail of generated SpriteShape geometry.
|
|||
|
/// </summary>
|
|||
|
public enum QualityDetail
|
|||
|
{
|
|||
|
/// <summary>Highest level of detail (16).</summary>
|
|||
|
High = 16,
|
|||
|
/// <summary>Medium level of detail (8).</summary>
|
|||
|
Mid = 8,
|
|||
|
/// <summary>Low level of detail (4).</summary>
|
|||
|
Low = 4
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Corner mode that defines how corners are handled when generating SpriteShape geometry.
|
|||
|
/// </summary>
|
|||
|
public enum Corner
|
|||
|
{
|
|||
|
/// <summary>No corners.</summary>
|
|||
|
Disable = 0,
|
|||
|
/// <summary>Automatically use respective corner sprite if 1) angle is within range 2) control point and neighbours are in linear tangent mode and their heights are same.</summary>
|
|||
|
Automatic = 1,
|
|||
|
/// <summary>The sprite at this control point is used to create a curved corner.</summary>
|
|||
|
Stretched = 2,
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Spline control point that holds information for constructing the Bezier curve and SpriteShape geometry.
|
|||
|
/// </summary>
|
|||
|
[System.Serializable]
|
|||
|
public class SplineControlPoint
|
|||
|
{
|
|||
|
/// <summary>Position of control point.</summary>
|
|||
|
public Vector3 position;
|
|||
|
/// <summary>Left tangent of control point.</summary>
|
|||
|
public Vector3 leftTangent;
|
|||
|
/// <summary>Right tangent of control point.</summary>
|
|||
|
public Vector3 rightTangent;
|
|||
|
/// <summary>Tangent mode of control point.</summary>
|
|||
|
public ShapeTangentMode mode;
|
|||
|
/// <summary>Height of control point used when generating SpriteShape geometry.</summary>
|
|||
|
public float height = 1f;
|
|||
|
/// <summary>Bevel cutoff for control Point. </summary>
|
|||
|
public float bevelCutoff;
|
|||
|
/// <summary>Bevel size. </summary>
|
|||
|
public float bevelSize;
|
|||
|
/// <summary>Sprite index used for rendering at start of this control point along the edge. </summary>
|
|||
|
public int spriteIndex;
|
|||
|
/// <summary>Enable corners of control point.</summary>
|
|||
|
public bool corner;
|
|||
|
[SerializeField]
|
|||
|
Corner m_CornerMode;
|
|||
|
|
|||
|
/// <summary>Corner mode of control point.</summary>
|
|||
|
public Corner cornerMode
|
|||
|
{
|
|||
|
get => m_CornerMode;
|
|||
|
set => m_CornerMode = value;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get hash code for this Spline control point.
|
|||
|
/// </summary>
|
|||
|
/// <returns>Hash code as int.</returns>
|
|||
|
public override int GetHashCode()
|
|||
|
{
|
|||
|
return ((int)position.x).GetHashCode() ^ ((int)position.y).GetHashCode() ^ position.GetHashCode() ^
|
|||
|
(leftTangent.GetHashCode() << 2) ^ (rightTangent.GetHashCode() >> 2) ^ ((int)mode).GetHashCode() ^
|
|||
|
height.GetHashCode() ^ spriteIndex.GetHashCode() ^ corner.GetHashCode() ^ (m_CornerMode.GetHashCode() << 2);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Angle Range defines constraints and list of sprites to be used to render edges of SpriteShape.
|
|||
|
/// </summary>
|
|||
|
[System.Serializable]
|
|||
|
public class AngleRange : ICloneable
|
|||
|
{
|
|||
|
/// <summary>Start angle of AngleRange.</summary>
|
|||
|
public float start
|
|||
|
{
|
|||
|
get { return m_Start; }
|
|||
|
set { m_Start = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>End angle of AngleRange. Angles cannot overlap with others.</summary>
|
|||
|
public float end
|
|||
|
{
|
|||
|
get { return m_End; }
|
|||
|
set { m_End = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Render order for this AngleRange.</summary>
|
|||
|
public int order
|
|||
|
{
|
|||
|
get { return m_Order; }
|
|||
|
set { m_Order = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>List of sprites that are used to render the edge. The sprite index of control point can be used to select the one to be used for rendering.</summary>
|
|||
|
public List<Sprite> sprites
|
|||
|
{
|
|||
|
get { return m_Sprites; }
|
|||
|
set { m_Sprites = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
float m_Start;
|
|||
|
[SerializeField]
|
|||
|
float m_End;
|
|||
|
[SerializeField]
|
|||
|
int m_Order;
|
|||
|
[SerializeField]
|
|||
|
List<Sprite> m_Sprites = new List<Sprite>();
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Clone object.
|
|||
|
/// </summary>
|
|||
|
/// <returns>Cloned Angle Range Object.</returns>
|
|||
|
public object Clone()
|
|||
|
{
|
|||
|
AngleRange clone = this.MemberwiseClone() as AngleRange;
|
|||
|
clone.sprites = new List<Sprite>(clone.sprites);
|
|||
|
|
|||
|
return clone;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Test for Equality.
|
|||
|
/// </summary>
|
|||
|
/// <param name="obj">Object to test against.</param>
|
|||
|
/// <returns>True if Equal.</returns>
|
|||
|
public override bool Equals(object obj)
|
|||
|
{
|
|||
|
var other = obj as AngleRange;
|
|||
|
|
|||
|
if (other == null)
|
|||
|
return false;
|
|||
|
|
|||
|
bool equals = start.Equals(other.start) && end.Equals(other.end) && order.Equals(other.order);
|
|||
|
|
|||
|
if (!equals)
|
|||
|
return false;
|
|||
|
|
|||
|
if (sprites.Count != other.sprites.Count)
|
|||
|
return false;
|
|||
|
|
|||
|
for (int i = 0; i < sprites.Count; ++i)
|
|||
|
if (sprites[i] != other.sprites[i])
|
|||
|
return false;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get hash code for this AngleRange.
|
|||
|
/// </summary>
|
|||
|
/// <returns>Hash code as int.</returns>
|
|||
|
public override int GetHashCode()
|
|||
|
{
|
|||
|
int hashCode = start.GetHashCode() ^ end.GetHashCode() ^ order.GetHashCode();
|
|||
|
|
|||
|
if (sprites != null)
|
|||
|
{
|
|||
|
for (int i = 0; i < sprites.Count; i++)
|
|||
|
{
|
|||
|
Sprite sprite = sprites[i];
|
|||
|
if (sprite)
|
|||
|
hashCode = hashCode * 16777619 ^ (sprite.GetHashCode() + i);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return hashCode;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Corner Sprite used to specify corner type and associated sprites.
|
|||
|
/// </summary>
|
|||
|
[System.Serializable]
|
|||
|
public class CornerSprite : ICloneable
|
|||
|
{
|
|||
|
|
|||
|
/// <summary>Type of corner. </summary>
|
|||
|
public CornerType cornerType
|
|||
|
{
|
|||
|
get { return m_CornerType; }
|
|||
|
set { m_CornerType = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>List of sprites associated with this corner. </summary>
|
|||
|
public List<Sprite> sprites
|
|||
|
{
|
|||
|
get { return m_Sprites; }
|
|||
|
set { m_Sprites = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
CornerType m_CornerType; ///< Set Corner type. enum { OuterTopLeft = 0, OuterTopRight = 1, OuterBottomLeft = 2, OuterBottomRight = 3, InnerTopLeft = 4, InnerTopRight = 5, InnerBottomLeft = 6, InnerBottomRight = 7 }
|
|||
|
[SerializeField]
|
|||
|
List<Sprite> m_Sprites;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Clone this object.
|
|||
|
/// </summary>
|
|||
|
/// <returns>A CornerSprite clone.</returns>
|
|||
|
public object Clone()
|
|||
|
{
|
|||
|
CornerSprite clone = this.MemberwiseClone() as CornerSprite;
|
|||
|
clone.sprites = new List<Sprite>(clone.sprites);
|
|||
|
|
|||
|
return clone;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Test for Equality.
|
|||
|
/// </summary>
|
|||
|
/// <param name="obj">Object to test against</param>
|
|||
|
/// <returns>True if objects are equal.</returns>
|
|||
|
public override bool Equals(object obj)
|
|||
|
{
|
|||
|
var other = obj as CornerSprite;
|
|||
|
|
|||
|
if (other == null)
|
|||
|
return false;
|
|||
|
|
|||
|
if (!cornerType.Equals(other.cornerType))
|
|||
|
return false;
|
|||
|
|
|||
|
if (sprites.Count != other.sprites.Count)
|
|||
|
return false;
|
|||
|
|
|||
|
for (int i = 0; i < sprites.Count; ++i)
|
|||
|
if (sprites[i] != other.sprites[i])
|
|||
|
return false;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get hash code for this CornerSprite.
|
|||
|
/// </summary>
|
|||
|
/// <returns>Hash code as int.</returns>
|
|||
|
public override int GetHashCode()
|
|||
|
{
|
|||
|
int hashCode = cornerType.GetHashCode();
|
|||
|
|
|||
|
if (sprites != null)
|
|||
|
{
|
|||
|
for (int i = 0; i < sprites.Count; i++)
|
|||
|
{
|
|||
|
Sprite sprite = sprites[i];
|
|||
|
if (sprite)
|
|||
|
{
|
|||
|
hashCode ^= (i + 1);
|
|||
|
hashCode ^= sprite.GetHashCode();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return hashCode;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// SpriteShape contains the parameters that define how SpriteShape geometry is generated from a Spline.
|
|||
|
/// </summary>
|
|||
|
[HelpURLAttribute("https://docs.unity3d.com/Packages/com.unity.2d.spriteshape@latest/index.html?subfolder=/manual/SSProfile.html")]
|
|||
|
public class SpriteShape : ScriptableObject
|
|||
|
{
|
|||
|
/// <summary>List of AngleRanges. </summary>
|
|||
|
public List<AngleRange> angleRanges
|
|||
|
{
|
|||
|
get { return m_Angles; }
|
|||
|
set { m_Angles = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Fill Texture to be used for inner geometry in case of closed shapes. The Texture wrap mode should be set to Repeat. </summary>
|
|||
|
public Texture2D fillTexture
|
|||
|
{
|
|||
|
get { return m_FillTexture; }
|
|||
|
set { m_FillTexture = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Sprites to be used for corners. </summary>
|
|||
|
public List<CornerSprite> cornerSprites
|
|||
|
{
|
|||
|
get { return m_CornerSprites; }
|
|||
|
set { m_CornerSprites = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Fill offset for the closed shape. </summary>
|
|||
|
public float fillOffset
|
|||
|
{
|
|||
|
get { return m_FillOffset; }
|
|||
|
set { m_FillOffset = value; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>Use borders of sprites when generating edge geometry. </summary>
|
|||
|
public bool useSpriteBorders
|
|||
|
{
|
|||
|
get { return m_UseSpriteBorders; }
|
|||
|
set { m_UseSpriteBorders = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
List<AngleRange> m_Angles = new List<AngleRange>();
|
|||
|
[SerializeField]
|
|||
|
Texture2D m_FillTexture;
|
|||
|
[SerializeField]
|
|||
|
List<CornerSprite> m_CornerSprites = new List<CornerSprite>();
|
|||
|
[SerializeField]
|
|||
|
float m_FillOffset;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
bool m_UseSpriteBorders = true;
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
internal static event Action<SpriteShape> onReset = null;
|
|||
|
#endif
|
|||
|
private CornerSprite GetCornerSprite(CornerType cornerType)
|
|||
|
{
|
|||
|
var cornerSprite = new CornerSprite();
|
|||
|
cornerSprite.cornerType = cornerType;
|
|||
|
cornerSprite.sprites = new List<Sprite>();
|
|||
|
cornerSprite.sprites.Insert(0, null);
|
|||
|
return cornerSprite;
|
|||
|
}
|
|||
|
|
|||
|
void ResetCornerList()
|
|||
|
{
|
|||
|
m_CornerSprites.Clear();
|
|||
|
m_CornerSprites.Insert(0, GetCornerSprite(CornerType.OuterTopLeft));
|
|||
|
m_CornerSprites.Insert(1, GetCornerSprite(CornerType.OuterTopRight));
|
|||
|
m_CornerSprites.Insert(2, GetCornerSprite(CornerType.OuterBottomLeft));
|
|||
|
m_CornerSprites.Insert(3, GetCornerSprite(CornerType.OuterBottomRight));
|
|||
|
m_CornerSprites.Insert(4, GetCornerSprite(CornerType.InnerTopLeft));
|
|||
|
m_CornerSprites.Insert(5, GetCornerSprite(CornerType.InnerTopRight));
|
|||
|
m_CornerSprites.Insert(6, GetCornerSprite(CornerType.InnerBottomLeft));
|
|||
|
m_CornerSprites.Insert(7, GetCornerSprite(CornerType.InnerBottomRight));
|
|||
|
}
|
|||
|
|
|||
|
void OnValidate()
|
|||
|
{
|
|||
|
if (m_CornerSprites.Count != 8)
|
|||
|
ResetCornerList();
|
|||
|
}
|
|||
|
|
|||
|
void Reset()
|
|||
|
{
|
|||
|
m_Angles.Clear();
|
|||
|
ResetCornerList();
|
|||
|
#if UNITY_EDITOR
|
|||
|
onReset?.Invoke(this);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
internal static int GetSpriteShapeHashCode(SpriteShape spriteShape)
|
|||
|
{
|
|||
|
// useSpriteBorders, fillOffset and fillTexture are hashChecked elsewhere.
|
|||
|
|
|||
|
unchecked
|
|||
|
{
|
|||
|
int hashCode = (int)2166136261;
|
|||
|
|
|||
|
hashCode = hashCode * 16777619 ^ spriteShape.angleRanges.Count;
|
|||
|
|
|||
|
for (int i = 0; i < spriteShape.angleRanges.Count; ++i)
|
|||
|
{
|
|||
|
hashCode = hashCode * 16777619 ^ (spriteShape.angleRanges[i].GetHashCode() + i);
|
|||
|
}
|
|||
|
|
|||
|
hashCode = hashCode * 16777619 ^ spriteShape.cornerSprites.Count;
|
|||
|
|
|||
|
for (int i = 0; i < spriteShape.cornerSprites.Count; ++i)
|
|||
|
{
|
|||
|
hashCode = hashCode * 16777619 ^ (spriteShape.cornerSprites[i].GetHashCode() + i);
|
|||
|
}
|
|||
|
|
|||
|
return hashCode;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|