文章目錄
一. 安裝SpriteShape
打開Unity菜單Window-Package Manager
因爲我用的Unity版本是2019.2.14f1
,2D SpriteShape
插件還屬於preview
階段,所以我要打開Show preview packages
,
搜索spriteshape
即可看到插件,點擊install
即可。
安裝後,還可以導入工程例子和拓展資源
二. 創建Sprite Shape Profile
Sprite Shape Profile
是用於配置特定形狀類型的所有選項的資源。 通過它我們可以分配要使用的精靈,並告訴Sprite Shape
如何渲染它們。
在Project新建一個目錄SpriteShapeRes
,然後在這個目錄上右鍵菜單Create-Sprite Shape Profile
。
可以看到有兩個子菜單:Open Shape
和Closed Shape
。
這兩個看名字就可以理解了,如下圖:
比如這裏創建一個Open Shape
的profile
文件
爲其添加一個精靈圖
三. 製作一個Open Shape地形
在場景中創建一個空物體
給這個物體添加Sprite Shape Controller
組件
賦值Profile
屬性
此時即可看到場景中出現了一個閉合的形狀
把Is Open Ended
勾選上,就不是閉環的了
四. 編輯地形
點擊形狀編輯按鈕,即可在場景中對形狀進行編輯
這裏我們發現路徑是一段一段的,我們想要銜接處是那種圓滑過渡,只需把每個點的Tangent Mode
改成Continuous
即可
這時發現銜接是圓滑的,但是圖片是斷開的,這是因爲我們沒有設置border
。
選中精靈圖,點擊Sprite Editor
設置左右兩邊的border
,最後點擊Apply
此時場景中的就得到了我們想要的效果了
五. 添加地形碰撞
想要角色可以在地形上行走,我們還要給地形添加碰撞。
添加Polygon Collider 2D
、Composite Collider 2D
此時看到的碰撞是這樣的
再掛上Geometry Collider
腳本(這個腳本是上面Extras工具包中的,代碼見文章末尾),然後勾選一下Update Collider
很接近我們想要的效果了,此時再勾選Composite Collider 2D
的Used By Effector
和Polygon Collider 2D
的Used 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
}