Unity 使用SpriteShape制作2D游戏任意形状地形

本文详细介绍了如何在Unity中使用SpriteShape插件创建地形,并实现地形的编辑、碰撞体的添加与优化,包括安装插件、创建地形、编辑形状、设置精灵边框、生成碰撞体等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 安装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 2D(2D多边形碰撞),此时看到的碰撞是这样的
在这里插入图片描述
可以看到碰撞的形状是默认的一个五角星,我们不可能挨个点挨个点去编辑这个碰撞提让它和路面形状一致。怎么办呢,通过脚本来搞定。
挂上Geometry Collider脚本(这个脚本是上面Extras工具包中的,代码见文章末尾),然后勾选一下Update Collider
在这里插入图片描述
在这里插入图片描述
碰撞体形状对了,但是碰撞体的边太多了,可以通过Composite Collider 2D组件来合并。
挂上Composite Collider 2D组件,然后勾选Polygon 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
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林新发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值