Unity 使用SpriteShape製作2D遊戲任意形狀地形

一. 安裝SpriteShape

打開Unity菜單Window-Package Manager
在這裏插入圖片描述
因爲我用的Unity版本是2019.2.14f12D SpriteShape插件還屬於preview階段,所以我要打開Show preview packages
搜索spriteshape即可看到插件,點擊install即可。
在這裏插入圖片描述
安裝後,還可以導入工程例子和拓展資源
在這裏插入圖片描述

二. 創建Sprite Shape Profile

Sprite Shape Profile是用於配置特定形狀類型的所有選項的資源。 通過它我們可以分配要使用的精靈,並告訴Sprite Shape如何渲染它們。
在Project新建一個目錄SpriteShapeRes,然後在這個目錄上右鍵菜單Create-Sprite Shape Profile
可以看到有兩個子菜單:Open ShapeClosed Shape
在這裏插入圖片描述
這兩個看名字就可以理解了,如下圖:
在這裏插入圖片描述
比如這裏創建一個Open Shapeprofile文件
在這裏插入圖片描述
爲其添加一個精靈圖
在這裏插入圖片描述

三. 製作一個Open Shape地形

在場景中創建一個空物體
在這裏插入圖片描述
給這個物體添加Sprite Shape Controller組件
在這裏插入圖片描述
賦值Profile屬性
在這裏插入圖片描述
此時即可看到場景中出現了一個閉合的形狀
在這裏插入圖片描述
Is Open Ended勾選上,就不是閉環的了
在這裏插入圖片描述 在這裏插入圖片描述

四. 編輯地形

點擊形狀編輯按鈕,即可在場景中對形狀進行編輯
在這裏插入圖片描述
在這裏插入圖片描述
這裏我們發現路徑是一段一段的,我們想要銜接處是那種圓滑過渡,只需把每個點的Tangent Mode改成Continuous即可
在這裏插入圖片描述
在這裏插入圖片描述
這時發現銜接是圓滑的,但是圖片是斷開的,這是因爲我們沒有設置border
選中精靈圖,點擊Sprite Editor
在這裏插入圖片描述
設置左右兩邊的border,最後點擊Apply
在這裏插入圖片描述
此時場景中的就得到了我們想要的效果了
在這裏插入圖片描述

五. 添加地形碰撞

想要角色可以在地形上行走,我們還要給地形添加碰撞。
添加Polygon Collider 2DComposite Collider 2D
此時看到的碰撞是這樣的
在這裏插入圖片描述
再掛上Geometry Collider腳本(這個腳本是上面Extras工具包中的,代碼見文章末尾),然後勾選一下Update Collider
在這裏插入圖片描述
在這裏插入圖片描述
很接近我們想要的效果了,此時再勾選Composite Collider 2DUsed By EffectorPolygon Collider 2DUsed By Composite即可
在這裏插入圖片描述
看到了我們想要的效果
在這裏插入圖片描述
還可以調節Offset,可以調整碰撞的整體偏移,
在這裏插入圖片描述
在這裏插入圖片描述

六. Geometry Collider代碼

using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Experimental.U2D;
using UnityEngine.U2D;

#if UNITY_EDITOR
using UnityEditor;
#endif

[ExecuteAlways]
public class GeometryCollider : MonoBehaviour
{
    [SerializeField]
    bool m_UpdateCollider = false;

    int m_HashCode = 0;

    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (m_UpdateCollider)
            Bake(gameObject, false);
    }

    static public void Bake(GameObject go, bool forced)
    {
        var spriteShapeController = go.GetComponent<SpriteShapeController>();
        var spriteShapeRenderer = go.GetComponent<SpriteShapeRenderer>();
        var polyCollider = go.GetComponent<PolygonCollider2D>();
        var geometryCollider = go.GetComponent<GeometryCollider>();

        if (spriteShapeController != null && polyCollider != null)
        {
            var spline = spriteShapeController.spline;
            if (geometryCollider != null)
            { 
                int splineHashCode = spline.GetHashCode();
                if (splineHashCode == geometryCollider.m_HashCode && !forced)
                    return;
                geometryCollider.m_HashCode = splineHashCode;
            }
            NativeArray<ushort> indexArray;
            NativeSlice<Vector3> posArray;
            NativeSlice<Vector2> uv0Array;
            NativeArray<SpriteShapeSegment> geomArray;
            spriteShapeRenderer.GetChannels(65536, out indexArray, out posArray, out uv0Array);
            geomArray = spriteShapeRenderer.GetSegments(spline.GetPointCount() * 8);

            NativeArray<ushort> indexArrayLocal = new NativeArray<ushort>(indexArray.Length, Allocator.Temp);

            List<Vector2> points = new List<Vector2>();
            int indexCount = 0, vertexCount = 0, counter = 0;
            for (int u = 0; u < geomArray.Length; ++u)
            {
                if (geomArray[u].indexCount > 0)
                {
                    for (int i = 0; i < geomArray[u].indexCount; ++i)
                    {
                        indexArrayLocal[counter] = (ushort)(indexArray[counter] + vertexCount);
                        counter++;
                    }
                    vertexCount += geomArray[u].vertexCount;
                    indexCount += geomArray[u].indexCount;
                }
            }
            Debug.Log(go.name + " : " + counter);
            OuterEdges(polyCollider, indexArrayLocal, posArray, indexCount);
        }
    }

    // Generate the outer edges from the Renderer mesh. Based on code from www.h3xed.com
    static void OuterEdges(PolygonCollider2D polygonCollider, NativeArray<ushort> triangles, NativeSlice<Vector3> vertices, int triangleCount)
    {
        // Get just the outer edges from the mesh's triangles (ignore or remove any shared edges)
        Dictionary<string, KeyValuePair<int, int>> edges = new Dictionary<string, KeyValuePair<int, int>>();
        for (int i = 0; i < triangleCount; i += 3)
        {
            for (int e = 0; e < 3; e++)
            {
                int vert1 = triangles[i + e];
                int vert2 = triangles[i + e + 1 > i + 2 ? i : i + e + 1];
                string edge = Mathf.Min(vert1, vert2) + ":" + Mathf.Max(vert1, vert2);
                if (edges.ContainsKey(edge))
                {
                    edges.Remove(edge);
                }
                else
                {
                    edges.Add(edge, new KeyValuePair<int, int>(vert1, vert2));
                }
            }
        }

        // Create edge lookup (Key is first vertex, Value is second vertex, of each edge)
        Dictionary<int, int> lookup = new Dictionary<int, int>();
        foreach (KeyValuePair<int, int> edge in edges.Values)
        {
            if (lookup.ContainsKey(edge.Key) == false)
            {
                lookup.Add(edge.Key, edge.Value);
            }
        }

        // Create empty polygon collider
        polygonCollider.pathCount = 0;

        // Loop through edge vertices in order
        int startVert = 0;
        int nextVert = startVert;
        int highestVert = startVert;
        List<Vector2> colliderPath = new List<Vector2>();
        while (true)
        {

            // Add vertex to collider path
            colliderPath.Add(vertices[nextVert]);

            // Get next vertex
            nextVert = lookup[nextVert];

            // Store highest vertex (to know what shape to move to next)
            if (nextVert > highestVert)
            {
                highestVert = nextVert;
            }

            // Shape complete
            if (nextVert == startVert)
            {

                // Add path to polygon collider
                polygonCollider.pathCount++;
                polygonCollider.SetPath(polygonCollider.pathCount - 1, colliderPath.ToArray());
                colliderPath.Clear();

                // Go to next shape if one exists
                if (lookup.ContainsKey(highestVert + 1))
                {

                    // Set starting and next vertices
                    startVert = highestVert + 1;
                    nextVert = startVert;

                    // Continue to next loop
                    continue;
                }

                // No more verts
                break;
            }
        }
    }

#if UNITY_EDITOR

    [MenuItem("SpriteShape/Generate Geometry Collider", false, 358)]
    public static void BakeGeometryCollider()
    {
        if (Selection.activeGameObject != null)
            GeometryCollider.Bake(Selection.activeGameObject, true);
    }

#endif
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章