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