Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How to arrange objects along a bezier curve

Discussion in 'Scripting' started by synthc, Aug 7, 2023.

  1. synthc

    synthc

    Joined:
    Oct 22, 2019
    Posts:
    30
    I'm trying to create a Slay The Spire style targeting arrow for my card game. After some research, it appears that the cubic bezier curve formula is used to achieve the curve on the nodes of the arrow. I've found some equations for this, but my math skills are lacking and I have no idea how to actually implement these.



    I've written a script that creates and positions arrow nodes between an anchor point and the current mouse position in a straight line; but I could use some help figuring out the algorithm for positioning the points along a bezier curve:

    arrow.jpg

    Code (CSharp):
    1. public class ArrowHandler : MonoBehaviour
    2.     {
    3.         [SerializeField] private GameObject ArrowHeadPrefab;
    4.         [SerializeField] private GameObject ArrowNodePrefab;
    5.         private RectTransform Rect;
    6.         private RectTransform Anchor;
    7.         private RectTransform Arrowhead;
    8.         private List<RectTransform> Nodes = new List<RectTransform>();
    9.         private float ScaleFactor;
    10.         private int NumNodes = 5;
    11.         private readonly float ScaleMultiplier = 0.03f;
    12.  
    13.         private void Awake()
    14.         {
    15.             Rect = GetComponent<RectTransform>();
    16.             ScaleFactor = ArrowHeadPrefab.transform.localScale.x;
    17.  
    18.             Anchor = Instantiate(ArrowNodePrefab, transform).GetComponent<RectTransform>();
    19.             Arrowhead = Instantiate(ArrowHeadPrefab, transform).GetComponent<RectTransform>();
    20.  
    21.             for (int i = 0; i < NumNodes; i++)
    22.             {
    23.                 Nodes.Add(Instantiate(ArrowNodePrefab, transform).GetComponent<RectTransform>());
    24.             }
    25.         }
    26.  
    27.         private void Update()
    28.         {
    29.             // Set anchor point to origin:
    30.             Anchor.position = new Vector2(Rect.position.x, Rect.position.y);
    31.  
    32.             // Set arrowhead to mouse position:
    33.             Vector3 position = Camera.main.ScreenToWorldPoint(Mouse.current.position.ReadValue());
    34.             position.z += (gameObject.transform.position - position).z;
    35.             Arrowhead.position = position;
    36.  
    37.             int i = 0;
    38.             foreach (RectTransform node in Nodes)
    39.             {
    40.                 // Lerp nodes so that they are evenly spaced between anchor and arrowhead:
    41.                 node.position = Vector2.Lerp(Anchor.position, Arrowhead.position, (float)(i + 1) / (Nodes.Count + 1));
    42.  
    43.                 // Math magic to position nodes along a cubic bezier curve goes here...
    44.  
    45.                 // Set node rotation based on arrowhead (mouse) position:
    46.                 node.rotation = Quaternion.Euler(new Vector3(0, 0, Vector2.SignedAngle(Vector2.up, node.position - Arrowhead.position)));
    47.  
    48.                 // Scale based on number of nodes:
    49.                 float scale = ScaleFactor * (1f - ScaleMultiplier * (Nodes.Count - 1 - i));
    50.                 node.localScale = new Vector3(scale, scale, 1);
    51.                 i++;
    52.             }
    53.  
    54.             Arrowhead.rotation = Quaternion.Euler(new Vector3(0, 0, Vector2.SignedAngle(Vector2.up, Arrowhead.position)));
    55.         }
    56.     }
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Splines are a pretty deep topic, but Catlike Coding has a good tutorial on them: https://catlikecoding.com/unity/tutorials/curves-and-splines/

    There are algorithms in the link for finding the point and direction along a bezier spline.

    To evenly distribute along a curve you need to find a point via distance, which requires iterating a long a curve bit by bit, measuring the distance between each point, until you've hit said distance.

    Unity has a package for splines that may be of use: https://docs.unity3d.com/Packages/com.unity.splines@2.4/manual/index.html
     
    _geo__, Yoreki, Bunny83 and 1 other person like this.
  3. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,588
    If you want to improve your understanding on Bezier curves, Sebastian Lague also made a short video series about implementing those. I always find his explanations and visualizations pretty helpful and, if i remember correctly, i even used this as an entry point when i needed to implement flexible paths for a study in VR back at uni.



    There should be plenty of implementations on the asset store if you just need a working bezier curve asset.
     
  4. synthc

    synthc

    Joined:
    Oct 22, 2019
    Posts:
    30
    Thanks, that tutorial really helped to demystify all of this. My issue was that I was trying to generalize the cubic bezier formula so that it would work with any number of points - not realizing that the formula only works with exactly four control points.

    The solution was to:
    1. Use the cubic bezier formula
      Code (csharp):
      1. B(t) = (1-t)^3 * P0 + 3 * (1-t)^2 * t * P1 + 3 * (1-t) * t^2 * P2 + t^3 * p3
      to generate four control points.
    2. Use those control points to create a spline.
    3. Position the nodes along the spline; with their positions being a function of the number of nodes.
    I ended up using the free asset Bezier Solution to create the spline and get points along it. So instead of lerping, I use
    Code (CSharp):
    1. Spline.GetPoint(float)(n + 1) / (Nodes.Count + 1)
    to get the interpolated points along the spline.