- Home /
How to add Bezier Curves on a randomly moving object? 3d
I'm making a 3d dragon-snake that moves from point to point in the air, turning and facing the direction it goes, but it moves in straight lines and stops too long in one spot before going onto the next. I want to use Bezier curve or something similar that can make those movements smoother.
I'm not good at coding or math and have researched similar situations, they are all very different and I cant imagine how I could code something similar into what I'm already working with? I've spent too long trying to wrap my head around this, any ideas or suggestions appreciated.
thankyou :}
here's the movement script using System.Collections; using System.Collections.Generic; using UnityEngine;
public class SerpentHeadMovement : MonoBehaviour
{
Vector3 destination;
Vector3 originalPosition;
//how far the dragon head can move from start
public float maxDist = 5.0f;
//how far the dragon head can move in a single decision
public float maxMovement = 2.0f;
// Start is called before the first frame update
public float distThreshold = 0.3f;
public float speed = 1.0f;
public float rotSpeed = 3.14f;
void Start()
{
originalPosition = transform.position;
NewDestination();
}
// Update is called once per frame
void Update()
{
//have we reached destination?
if (Vector3.Distance(transform.position, destination) < distThreshold)
{
//choose a new destination.
NewDestination();
}
//move towards destination
Vector3 subdestination = (destination - transform.position) * speed * Time.deltaTime;
//save current rotation
Quaternion rot = transform.rotation;
transform.LookAt(transform.position + subdestination, Vector3.up );
//save desired rot
Quaternion destrot = transform.rotation;
transform.rotation = Quaternion.Lerp(rot, destrot, Time.deltaTime * rotSpeed);
transform.position += subdestination;
}
void NewDestination()
{
destination = transform.position + Random.insideUnitSphere * maxMovement;
if (Vector3.Distance(destination, originalPosition) > maxDist)
{
destination = transform.position + Random.insideUnitSphere * maxMovement;
}
}
}
Also here is some other possible examples of related things that I found, but I've already got a headache looking at most of them. And most of them are really outdated idk if they will even help me.
https://answers.unity.com/questions/12689/moving-an-object-along-a-bezier-curve.html
https://answers.unity.com/questions/1700903/how-to-make-quadratic-bezier-curve-through-3-point.html
https://answers.unity.com/questions/1507662/how-to-make-multiple-evenly-spaced-objects-follow.html
Answer by jackmw94 · Nov 11, 2020 at 03:32 PM
I found this question really interesting! I haven't used Bezier curves before so got a bit too involved with the process. Below is a script to demonstrate them in action, it constructs a set of points along a curved bezier-ised line given the waypoint positions you pass it:
It looks long but it's half comments!
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class BezierFun : MonoBehaviour
{
// Only setup required is populating these two fields:
// Make a new gameobject and add a line renderer component. Give is a new material if you're feeling fancy
// Make 3+ cubes and drag em all in
[SerializeField] private LineRenderer _lineRenderer;
[SerializeField] private Transform[] _targets;
// Update just here to show GetWholePath in action
private void Update()
{
if (_targets.Length < 3) return;
Vector3[] points = GetWholePath(_targets);
_lineRenderer.positionCount = points.Length;
_lineRenderer.SetPositions(points.ToArray());
}
private static Vector3[] GetWholePath(Transform[] targets)
{
// this weird looking 'Select' argument is LINQ
// it says for our collection of transforms, instead give me a collection of each of their positions
// a nicer way for doing a big ol for loop getting each transforms' position
return GetWholePath(targets.Select(p => p.position).ToArray());
}
// I think this function needs 3 or more positions to work, haven't tested with any less..
private static Vector3[] GetWholePath(Vector3[] positions)
{
// We start by finding the points in between each of our positions
// These intermediate points form the start and end of each bezier
// We have to do this because otherwise the bezier lines don't match up nicely
// Since the angle of lines in and out of the intermediate points is 180 degrees, we now know they will!
List<Vector3> targetPoints = new List<Vector3>();
for (var index = 0; index < positions.Length - 1; index++)
{
Vector3 firstPosition = positions[index];
Vector3 secondPosition = positions[index + 1];
Vector3 intermediate = firstPosition + (secondPosition - firstPosition) / 2f;
targetPoints.Add(firstPosition);
targetPoints.Add(intermediate);
}
// Last is more LINQ, just means 'positions[positions.Length - 1];'
targetPoints.Add(positions.Last());
// Now target points contains all the positions interleaved with the positions between them
// [0] = Position 1
// [1] = Point between 1 and 2
// [2] = Position 2
// [3] = Point between 2 and 3 ...
int numBeziers = positions.Length - 2;
List<Vector3> points = new List<Vector3>();
// We can move straight towards the first intermediate point
points.AddRange(GetPointsLinear(targetPoints[0], targetPoints[1]));
for (int i = 1; i <= numBeziers; i++)
{
// Start index is the index of the intermediate point
// Just converts an incrementing index to where the intermediate points sit in the targetPoints array
// i=1 -> 1, i=2 -> 3, i=3 -> 5, etc..
int startIndex = i * 2 - 1;
// Bezier goes between two intermediate points with the actual position from the tranform as the control point
points.AddRange(GetPointsBezier(targetPoints[startIndex], targetPoints[startIndex + 1], targetPoints[startIndex + 2]));
}
// Like with the first point, we can move straight to the last point
points.AddRange(GetPointsLinear(targetPoints[targetPoints.Count - 2], targetPoints[targetPoints.Count - 1]));
return points.ToArray();
}
private static Vector3[] GetPointsLinear(Vector3 pt1, Vector3 pt2, int lineDivisor = 10)
{
// just a flat line divided up. don't really need to do this tbh
Vector3[] points = new Vector3[lineDivisor + 1];
for (int i = 0; i < lineDivisor + 1; i++)
{
points[i] = Vector3.Lerp(pt1, pt2, i / (float) lineDivisor);
}
return points;
}
private static Vector3[] GetPointsBezier(Vector3 pt1, Vector3 pt2, Vector3 pt3, int lineDivisor = 10)
{
// LineDivisor essentially how many points we want
// More points is more precision but if you have too many then this could mess with your 'reached destination' function
Vector3[] points = new Vector3[lineDivisor + 1];
for (int i = 0; i < lineDivisor + 1; i++)
{
// Line fraction goes from 0 to 1
float lineFraction = i / (float) lineDivisor;
// The Lerp function finds a point between two values at a given fraction
// So if the fraction were 0, the result would be the first value
// 0.5 is half way, 0.25 a quarter of the way
// This is the main bezier logic:
// We find a point along the line from 1 to 2
Vector3 start = Vector3.Lerp(pt1, pt2, lineFraction);
// We find a point along the line from 2 to 3
Vector3 end = Vector3.Lerp(pt2, pt3, lineFraction);
// We find a point along the line that connects the two previous points
Vector3 linePoint = Vector3.Lerp(start, end, lineFraction);
points[i] = linePoint;
}
return points;
}
}
Thank you again, Jack! I feel like I'm actually starting to understand this now, you're explanations are really neat. I'll definitely try and fiddle around with this today and my project, I'll update if I'm successful. Thanks again
Well, your code works, it draws a nice looking bezier curve between objects.
Even though its really neat I have no clue how to make it useful for my goals and project, I just cant understand how to use this to make objects follow beziers, and generate them or something. Idk Thanks anyway
Ah you sounded so hopeful before D: $$anonymous$$y thinking was that at the start of your route you'd pass your waypoint positions into GetWholePath and that would give you waypoints along the curve, your dragon-snake could then follow these curve waypoints.
Don't feel like you should be using this though, this is just one way to achieve what you want to do. At the end of the day, if you're doing this for fun then put the fun first and if this is more frustrating than helpful then don't use it :)
Alternatively you could have it so the dragon-snake has a limit on how fast it can turn, it will always move forward in the direction its facing and will always try turn towards the next waypoint?
Did you ever find a solution to this? I had a bad feeling that I took the fun of the problem solving away from this one and just gave you an awkward script to incorporate..
Your answer
Follow this Question
Related Questions
I need some help with my script. Im just a beginner and dont know whats happening. 1 Answer
Circular curve using bezier formel 1 Answer
How to move in a direction rather than in axis using joysticks? 1 Answer
Trying to get Tank like movement after my camera broke my movement 0 Answers
3D Movement but the player can only move backwards (Beginner) 1 Answer