构建2D弧形“平台”

需求

通过设置参数来生成一个弧形“平台”,玩家角色可在其上移动(平台需有物理体)


画弧工具测试.gif

方案

构建平台外观(画出平台)

使用LineRenderer

构建2D弧形“平台”

使用EdgeCollider2D

具体步骤

参数

弧半径
起点相对于原点的角度
弧角度

弧宽度
弧外观的前后显示顺序(通过设置其z坐标)

构成弧的片段的数量

算得构成圆弧的各点的位置

点的数量 = 片段数量 + 1
因期望起点是从右边中间开始,且开始是向上画弧,因此使用如下式子计算各点的x和y值

float angle = startAngle - 90;
for (int i = 0; i < pointAmount; i++)
{
    currentPointPos.x = Mathf.Sin(Mathf.Deg2Rad * angle) * radius;
    currentPointPos.y = Mathf.Cos(Mathf.Deg2Rad * angle) * radius;
    posArray[i] = currentPointPos;
    angle += angleSum / segmentAmount;
}
画弧

为了保证画出的弧是圆滑的,因此可使用如下式子,根据弧度、弧宽度、半径,算得合适的片段数量(此处认为片段的长度为弧宽度)

// 片段数量 = 弧长/片段长度, 片段长度=弧宽度
float arcLength = Mathf.Deg2Rad * angleSum * radius;
int amount =(int)(arcLength / width);

根据各点的位置,将其设置为LineRenderer的点和碰撞体的点即可,在设置时还需设置弧宽度
EdgeCollider2D的边半径(edgeRadius)应为弧宽度的1/2

// 获取点
Vector2[] posArray = GetPointsPos(startAngle, angleSum, radius, PointAmount);

lineRenderer.positionCount = PointAmount;
lineRenderer.startWidth = width;
lineRenderer.endWidth = width;

// 画弧(外观)
for (int i = 0; i < posArray.Length; i++)
{
    lineRenderer.SetPosition(i, new Vector3(posArray[i].x, posArray[i].y, zValueOfLine));
}

// 重建碰撞体
edgeCollider.edgeRadius = width/2;
// 至少要有2个点才能构成一条边
if (posArray.Length > 1)
    edgeCollider.points = posArray;
其他细节

实测发现设置弧的角度为360时,画出的圆有接缝,因此当要画圆时,可将角度设置为360+一个值(弧度越宽,这个值需越大)

为了实时看到修改结果,可在OnValidate()中调用画弧方法
OnValidate()被调用的时机:官方文档中对OnValidate()的描述

  • 所属的脚本被载入
  • 脚本的某些值在Inspector窗口中被修改时

完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// 
/// 画圆弧测试
/// 

[RequireComponent(typeof(LineRenderer))]
[RequireComponent(typeof(EdgeCollider2D))]
public class MyTest : MonoBehaviour
{
    // 半径
    public float radius = 5f;
    // 起点角度
    public float startAngle = 0f;
    // 圆弧的角度
    [Range(0, 365)]
    public float angleSum = 270f;
    // 圆弧的宽度
    [Range(0,10)]
    public float width = 0.5f;
    // 外观的前后显示顺序(通过设置其z坐标)
    public float zValueOfLine = 0f;
    // 自动设置片段数量
    public bool isAutoSetSegmentAmount = true;
    // 片段数量
    public int segmentAmount = 100;
    private int PointAmount { get { return segmentAmount + 1; } }

    private LineRenderer lineRenderer;
    private EdgeCollider2D edgeCollider;

    private bool isInited;

    // 载入 和 上述某值发生变化时调用
    void OnValidate()
    {
        ReInit();
    }

    void ReInit()
    {
        isInited = false;
        Init();
    }

    void Init()
    {
        if (isInited)
            return;

        if (lineRenderer == null)
        {
            lineRenderer = GetComponent();
            // 圆弧可以拖动
            lineRenderer.useWorldSpace = false;
        }

        if (edgeCollider == null)
            edgeCollider = GetComponent();

        // 根据数据画圆
        DrawArc();

        isInited = true;
    }

    // 根据起始角度、弧度、弧半径、构成弧的各点的数量,算得各点的位置
    Vector2[] GetPointsPos(float startAngle, float angleSum, float radius, int pointAmount)
    {
        Vector2[] posArray = new Vector2[pointAmount];
        Vector2 currentPointPos = Vector2.zero;

        // 计算起点的角度(-90表示从右边中间为起点开始画弧)
        float angle = startAngle - 90;

        // float angle = startAngle;
        for (int i = 0; i < pointAmount; i++)
        {
            currentPointPos.x = Mathf.Sin(Mathf.Deg2Rad * angle) * radius;
            currentPointPos.y = Mathf.Cos(Mathf.Deg2Rad * angle) * radius;
            // currentPointPos.x = Mathf.Cos(Mathf.Deg2Rad * angle) * radius;
            // currentPointPos.y = Mathf.Sin(Mathf.Deg2Rad * angle) * radius;

            posArray[i] = currentPointPos;
            angle += angleSum / segmentAmount;
        }

        return posArray;
    }
    // 根据弧度、弧线粗细、弧半径,计算片段的数量
    int CalcSegmentAmount(float angleSum, float width, float radius)
    {
        // 片段数量 = 弧长/片段长度, 片段长度=弧宽度
        float arcLength = Mathf.Deg2Rad * angleSum * radius;
        int amount =(int)(arcLength / width);
        return amount;
    }
    // 用linerenderer画弧,用EdgeCollider2D构建碰撞体
    void DrawArc()
    {
        if (isAutoSetSegmentAmount)
            segmentAmount = CalcSegmentAmount(angleSum, width, radius);

        // 获取点
        Vector2[] posArray = GetPointsPos(startAngle, angleSum, radius, PointAmount);

        lineRenderer.positionCount = PointAmount;
        lineRenderer.startWidth = width;
        lineRenderer.endWidth = width;

        // 画弧(外观)
        for (int i = 0; i < posArray.Length; i++)
        {
            lineRenderer.SetPosition(i, new Vector3(posArray[i].x, posArray[i].y, zValueOfLine));
        }

        // 重建碰撞体
        edgeCollider.edgeRadius = width/2;
        // 至少要有2个点才能构成一条边
        if (posArray.Length > 1)
            edgeCollider.points = posArray;
    }
}

下一步

感觉做个类似《超级马里奥银河》的2D游戏也不错,下一步需考虑准确设置角色受到的重力(吸引力)方向,并据此调整其朝向

初步的设想是从角色脚下向下发射射线,将角色重力朝向设置为射线碰触到的“地面”的法线的反方向

你可能感兴趣的:(构建2D弧形“平台”)