用二次贝塞尔曲线绘制随机路径

问题描述 投票:0回答:2

我想绘制我的对象将遵循的平滑*和随机路径,我决定使用二次贝塞尔曲线(但我对其他想法持开放态度)。

我的代码以随机但不平滑*的方式移动我的对象。

预览:https://youtu.be/Eg9PEKuH4zA

我的问题是:怎样才能让方向变化更顺畅?我应该完全放弃我的贝塞尔曲线解决方案还是有办法改进我的代码以实现我想要的?

*平滑==没有突然的方向变化

我的代码:

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

public class Lights : MonoBehaviour {
    public float paintHeight = -90.0f;
    public static int NUMBER_OF_LIGHTS = 3;
    private static int BEZIER_PATH_POINTS = 100;
    private float GOLDEN_COLOR_RATIO = 0.618033988749895f;
    private Light[] lights = new Light[NUMBER_OF_LIGHTS];

    Vector3 RandomPoint() {
        float obj_width = gameObject.GetComponent<RectTransform>().rect.width;
        float screenX = Random.Range(-obj_width / 2, obj_width / 2);

        float obj_height = gameObject.GetComponent<RectTransform>().rect.height;
        float screenY = Random.Range(-obj_height / 2, obj_height / 2);

        return new Vector3(screenX, screenY, -paintHeight);
    }


    Vector3 QuadraticBezierPoint(Vector3 startPoint, Vector3 endPoint, Vector3 vertexPoint, float t) {
        /*
         *         vertex
         *        /╲
         *       /   ╲
         *      /  p   ╲
         *     /  . .    ╲
         *    / .      ·   ╲
         *   /·           ·  ╲
         *  start           · end
         *
         *  0 < t < 1
         *
         * B(t) = (1 - t)^2 * P0 + 2 * (1-t) * t * P1 + t^2 * P2
         * 
         */
        return Mathf.Pow((1 - t), 2) * startPoint + 2 * (1 - t) * t * vertexPoint + Mathf.Pow(t, 2) * endPoint;
    }

    Color RandomColor() {
        float h = Random.Range(0.0f, 1.0f) + GOLDEN_COLOR_RATIO;
        h %= 1;
        return Color.HSVToRGB(h, 0.99f, 0.99f);
    }

    void Start() {
        for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
            GameObject light_obj = new GameObject();
            Light light = light_obj.AddComponent<Light>();
            light.type = LightType.Point;
            light.range = 10.0f;
            light.intensity = 3.5f;
            light.renderMode = LightRenderMode.ForcePixel;
            light.name = "Light" + i;
            light.transform.parent = gameObject.transform;
            lights[i] = light;
        }

        StartCoroutine("Move");
    }

    IEnumerator Move () {
        Dictionary<string, Vector3>[] light_points = new Dictionary<string, Vector3>[NUMBER_OF_LIGHTS];
        Dictionary<string, Color>[] light_colors = new Dictionary<string, Color>[NUMBER_OF_LIGHTS];
        for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
            light_points[i] = new Dictionary<string, Vector3>();
            light_colors[i] = new Dictionary<string, Color>();
            //light_points[i]["startPoint"] = RandomPoint();
            //light_points[i]["vertexPoint"] = RandomPoint();
            light_points[i]["endPoint"] = RandomPoint();
            light_colors[i]["nextColor"] = RandomColor();
        }

        while(true) {
            for (int i = 0; i < NUMBER_OF_LIGHTS; i++) {
                light_points[i]["startPoint"] = light_points[i]["endPoint"];
                light_points[i]["vertexPoint"] = RandomPoint();
                light_points[i]["endPoint"] = RandomPoint();

                light_colors[i]["currentColor"] = light_colors[i]["nextColor"];
                light_colors[i]["nextColor"] = RandomColor();
            }

            for (int i = 0; i < BEZIER_PATH_POINTS; i++) {
                float percent = (float)i / BEZIER_PATH_POINTS;
                for (int j = 0; j < NUMBER_OF_LIGHTS; j++) {
                    lights[j].transform.localPosition = QuadraticBezierPoint(
                        light_points[j]["startPoint"],
                        light_points[j]["endPoint"],
                        light_points[j]["vertexPoint"],
                        percent
                    );
                    lights[j].color = Color.Lerp(light_colors[j]["currentColor"], light_colors[j]["nextColor"], percent);
                }
                yield return new WaitForSeconds(0.02f);
            }
        }
    }
}
c# unity-game-engine bezier unity3d-2dtools
2个回答
1
投票

如果您有一条从 A 到 B 的曲线,其中有某个中间点,则必须相应地选择 C,以便 B 处的角度 (A,B,C) 不会太小或太大。

此外,您必须以适当的方式选择中间点,请参见例如https://www.articleincell.com/2012/bezier-splines/ ,这样方向变化就不会太大


0
投票

您可以一直使用 NURBS 或其他样条线,但根据您发布的内容,可能有一种更简单、等效的方法来获得平滑曲线。

您创建的每条曲线都有 3 个点,分别是代码的起点、顶点和终点。 我们将它们称为 S、V 和 E。此外,让我们随时间观察它们,因此

{S0,V0,E0}
制作第一条曲线,
{S1,V1,E1}
制作第二条曲线,依此类推。

现在您确保

S1=E0
S2=E1
等等。 这意味着您的灯光不会随机跳跃,但切换贝塞尔曲线时运动可能会突然。

为了确保运动平稳,请尝试确保

S1 = E0 = 0.5 * (V0+V1)
。 这实际上意味着您随机生成 S0 和 V0。 然后,对于每条新曲线,您都会生成一个新的 V,旧 V 和新 V 之间的中点就是曲线断点。

这相当于拥有统一的2次B样条曲线。

这是一些可能具有说明性的示例代码。

// initialize with randomness
// we need a previous v and a current v for the first iteration
// So, the first start point will be halfway between these two points
prev_v = random_point()
v = random_point()

while( true )
{
    next_v = random_point()
    s = 0.5 * (prev_v + v) // s is halfway between prev_v and v
    e = 0.5 * (v + next_v) // e is halfway between v and next_v
    yield_movement_bezier( s, v, e )

    // prep for next iteration
    prev_v = v
    v = next_v
}
© www.soinside.com 2019 - 2024. All rights reserved.