Thursday, 23 April 2015

Unity 5: Creating a shield

8 hours Code/Asset

While not perfect this works as a functioning shield that creates a dynamic mesh. All you need is to place this onto a child empty object then place a material on it. Then modify it to the right size.


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[ExecuteInEditMode]
[RequireComponent(typeof(MeshRenderer))]
public class CircleMesh : MonoBehaviour
{

    public int elements = 60;
    public float startAngle = 0f;
    private float internalStartAngle = -1f;
    public float endAngle = 360f;
    private float internalEndAngle = -1f;
    public float innerRadius = 50f;
    private float savedInnerRadius = 50f;
    public float circleWidth = 10f;
    private float savedCircleWidth = 10f;
    private float circleRadius = 360f;

    public bool addCaps = false;

    private Vector3[] allVertices = new Vector3[0];
    private Vector2[] allUVs = new Vector2[0];
    private int[] allTriangles = new int[0];

    public Vector2 uv1 = new Vector2(0f, 0f);
    public Vector2 uv2 = new Vector2(0f, 1f);

    public Mesh mesh = null;
    public bool createNewMeshInAwake = true;

    public bool fullCircle = true;
    private bool savedFullCircle = false;

    public int count = 0;
    private int savedCount = -1;

    private bool busy = false;

    void Awake()
    {
        if (createNewMeshInAwake)
        {
            MeshFilter mF = transform.GetComponent<MeshFilter>();
            if (mF) mF.sharedMesh = null;
            mesh = null;

            RecalculateMesh();
        }
    }

    void Update()
    {
        if (startAngle != internalStartAngle || endAngle != internalEndAngle || innerRadius != savedInnerRadius || circleWidth != savedCircleWidth || mesh == null)
        {
            RecalculateMesh(uv1, uv2, (mesh == null) ? true : false);
            transform.rotation = Quaternion.Euler(90, 0, (endAngle / 2) - (circleRadius / elements /2));
        }

    }

    public void ForceRefreshMesh()
    {
        StartCoroutine(ForceRefreshMeshNow());
    }
    IEnumerator ForceRefreshMeshNow()
    {
        while (busy) yield return null;
        RecalculateMesh(uv1, uv2, true);
    }

    void RecalculateMesh()
    {
        RecalculateMesh(uv1, uv2, false);
    }
    void RecalculateMesh(Vector2 uvOne, Vector2 uvTwo, bool forceRefresh)
    {

        if (busy) return;

        busy = true;
        float degreeStep = 360f / elements;

        internalStartAngle = startAngle;
        internalEndAngle = endAngle;

        if (internalEndAngle > 360f) internalEndAngle = 360f;
        if (internalStartAngle < 0f) internalStartAngle = 0f;

        if (internalStartAngle > 0f || internalEndAngle < 360f) fullCircle = false;
        else fullCircle = true;

        count = 0;
        float deg = 0f;
        for (int c = 0; c < elements; c++)
        {
            if (deg >= internalStartAngle && deg <= internalEndAngle) count++;
            deg += degreeStep;
        }

        if (count < 2)
        {
            GetComponent<Renderer>().enabled = false;
            busy = false;
            return;
        }
        else GetComponent<Renderer>().enabled = true;

        if (!forceRefresh && count == savedCount && fullCircle == savedFullCircle && innerRadius == savedInnerRadius && circleWidth == savedCircleWidth)
        {
            busy = false;
            return;
        }
        savedCount = count;
        savedFullCircle = fullCircle;
        savedInnerRadius = innerRadius;
        savedCircleWidth = circleWidth;

        if (addCaps && !fullCircle) count += 2;

        allVertices = new Vector3[count * 2];
        allUVs = new Vector2[count * 2];
        int numTris = count * 6;
        if (!fullCircle) numTris -= 6;
        allTriangles = new int[numTris];

        if (addCaps && !fullCircle) count -= 2;

        if (!gameObject.GetComponent("MeshFilter")) gameObject.AddComponent<MeshFilter>();
        if (!gameObject.GetComponent("MeshRenderer")) gameObject.AddComponent<MeshRenderer>();
        if (!mesh) mesh = GetComponent<MeshFilter>().sharedMesh;
        if (!mesh)
        {
            mesh = new Mesh();
            mesh.name = "Circle Mesh for " + gameObject.name;
            GetComponent<MeshFilter>().sharedMesh = mesh;
        }
        mesh.Clear();

        Quaternion quat = Quaternion.identity;

        deg = 0f;
        while (deg < internalStartAngle) deg += degreeStep;
        for (int i = 0; i < count * 2; i += 2)
        {
            quat = Quaternion.AngleAxis(deg, -Vector3.forward);

            allVertices[i] = quat * new Vector3(0f, innerRadius, 0f);
            allVertices[i + 1] = quat * new Vector3(0f, innerRadius + circleWidth, 0f);

            allUVs[i] = uvOne;
            allUVs[i + 1] = uvTwo;

          
            int nextDown = i + 2;
            int nextUp = i + 3;
            if (i + 2 >= count * 2)
            {
                nextUp = 1; nextDown = 0;
            }
            if (i + 2 >= count * 2 && !fullCircle) break;

            allTriangles[(i * 3)] = i;
            allTriangles[(i * 3) + 1] = i + 1;
            allTriangles[(i * 3) + 2] = nextUp;
            allTriangles[(i * 3) + 3] = i;
            allTriangles[(i * 3) + 4] = nextUp;
            allTriangles[(i * 3) + 5] = nextDown;

            deg += degreeStep;
        }

        if (addCaps && !fullCircle)
        {
            float capAngleOffset = Mathf.Lerp(2f, 30f, circleWidth / innerRadius);

            quat = Quaternion.AngleAxis(internalStartAngle - capAngleOffset, -Vector3.forward);
           
            allVertices[count * 2] = quat * new Vector3(0f, innerRadius, 0f);

            allVertices[count * 2 + 1] = quat * new Vector3(0f, innerRadius + circleWidth, 0f);

            allUVs[count * 2] = uvOne + new Vector2(1f, 0f);
            allUVs[count * 2 + 1] = uvTwo + new Vector2(1f, 0f);

            allTriangles[(count * 2 * 3)] = count * 2;
            allTriangles[(count * 2 * 3) + 1] = count * 2 + 1;
            allTriangles[(count * 2 * 3) + 2] = 0;
            allTriangles[(count * 2 * 3) + 3] = count * 2 + 1;
            allTriangles[(count * 2 * 3) + 4] = 1;
            allTriangles[(count * 2 * 3) + 5] = 0;

            quat = Quaternion.AngleAxis(internalEndAngle + capAngleOffset, -Vector3.forward);
           
            allVertices[count * 2 + 2] = quat * new Vector3(0f, innerRadius, 0f);

            allVertices[count * 2 + 3] = quat * new Vector3(0f, innerRadius + circleWidth, 0f);

            allUVs[count * 2 + 2] = uvOne + new Vector2(1f, 0f);
            allUVs[count * 2 + 3] = uvTwo + new Vector2(1f, 0f);

            allTriangles[(count * 2 * 3) - 6] = count * 2 - 2;
            allTriangles[(count * 2 * 3) - 5] = count * 2 - 1;
            allTriangles[(count * 2 * 3) - 4] = count * 2 + 3;
            allTriangles[(count * 2 * 3) - 3] = count * 2 - 2;
            allTriangles[(count * 2 * 3) - 2] = count * 2 + 3;
            allTriangles[(count * 2 * 3) - 1] = count * 2 + 2;

        }

        mesh.vertices = allVertices;
        mesh.uv = allUVs;
        mesh.triangles = allTriangles;

        mesh.bounds = new Bounds(Vector3.zero, new Vector3(innerRadius + circleWidth, 0.1f, innerRadius + circleWidth));

        busy = false;

    }


}

No comments:

Post a Comment