Reputation: 15109
I want to draw smooth* and random paths that my objects would follow and I decided to go with quadratic bezier curves (but I'm open to other ideas).
My code is moving my objects in a random, but not smooth* way.
Preview: https://youtu.be/Eg9PEKuH4zA
My question is: how can I make the direction changes smoother? Should I completely ditch my Bezier solution or is there a way I could polish my code to achieve what I want?
*smooth == no abrupt direction changes
My code:
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);
}
}
}
}
Upvotes: 0
Views: 1436
Reputation: 6849
You could go all the way to using NURBS or some other spline, but there is probably an easier, equivalent way to get smooth curves given what you posted.
Each curve that you're creating has 3 points, in your code start, vertex, and end. Lets call them S, V, and E. Additionally, lets look at them through time, so {S0,V0,E0}
make the first curve, {S1,V1,E1}
make the second curve, and so forth.
Right now you ensure that S1=E0
, S2=E1
, and so forth. This means that your lights don't randomly jump, but the motion can be abrupt when switching Bezier curves.
To ensure that the motion is smooth, try ensuring that S1 = E0 = 0.5 * (V0+V1)
. What that means in practice is that you randomly generate S0 and V0. Then, for every new curve you generate a new V, and the midpoint between the old V and the new V is the curve break point.
This is equivalent to having a uniform degree 2 B-Spline.
Here's some example code that might be illustrative.
// 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
}
Upvotes: 0
Reputation: 2143
If you have a curve going from A to B with some middle point, you'll have to select a C accordingly so that the angle (A,B,C) at B is not too small or too big.
Additionally you'll have to select the middle points in an appropriate way, see e.g. https://www.particleincell.com/2012/bezier-splines/ , so that the change in direction is not too big.
Upvotes: 1