Just recently I was playing around with some moving platforms I had created for my game Shadow of the Orient. My game has horizontal and vertical moving platforms but it also has circular moving platforms as seen in the video below:
I wanted to add a visual aid to the radius of the circle which the platform follows along but i wasn’t quite sure how to do that – so I looked around on Google and found an article that explained how to work with the Line Renderer. After some messing around I was able to get my visual aid working quite nicely. The reason I wanted the visual aid was to give players a clue as to how much the platform needs to move before it comes into reach. So without further ado below is how i created my circular moving platform with the visual aid.
Setting up the structure
To set this up I first created a parent game object (Circular_Platform) which contains my main scripts for moving the platform along with the LineRenderer component to draw the visual aid. I then created my platform game object (Spiked_Platform) and added it as a child element of the parent game object as seen in the following reference:
The Point_Gear_1 game object can be ignored for this tutorial – its the spinning gear i added to the center of the circle radius which rotates in the same direction as the moving platform.
Next I assigned the following two components to the “Circular_Platform” parent game object:
CircularMovement.cs
using UnityEngine; public enum CircularDirection { Clockwise, CounterClockwise } public class CircularMovement : MonoBehaviour { [SerializeField] private GameObject objectToMove; [SerializeField] private bool reverseHalfway = false; [SerializeField] CircularDirection circularDirection = CircularDirection.Clockwise; [SerializeField, Range(0f, 20f)] private float startPosition = 0; [SerializeField, Range(0f, 50f)] private float angularSpeed = 1; [SerializeField, Range(0f, 50f)] private float rotationRadius = 20; //Internal vars private float _angle = 0f; private float _posX, _posY = 0f; private bool _flipDirection = false; private Vector2 _objecToMovePosition; private Transform _transform; private void Awake() { _transform = transform; } private void Start() { if(startPosition > 0) { //Set a starting point on the radius _posX = _transform.position.x + Mathf.Cos(startPosition) * rotationRadius; _posY = _transform.position.y + Mathf.Sin(startPosition) * rotationRadius; //Set the starting angle _angle = startPosition; //Assign the position to the moving object _objecToMovePosition.Set(_posX, _posY); objectToMove.transform.position = _objecToMovePosition; } } void Update() { Move(); } private void Move() { //Check if the moving object should reverse course back to the starting point if (reverseHalfway) { if (Mathf.Abs(_angle) > 3.14) _flipDirection = true; else if (Mathf.Abs(_angle) <= 0.01f) _flipDirection = false; } //Check which direction to move if (circularDirection == CircularDirection.Clockwise) _angle = _flipDirection ? _angle + Time.deltaTime * angularSpeed : _angle - Time.deltaTime * angularSpeed; else _angle = _flipDirection ? _angle - Time.deltaTime * angularSpeed : _angle + Time.deltaTime * angularSpeed; //Calculate the angle of the x and y positions and include the rotation radius _posX = _transform.position.x + Mathf.Cos(_angle) * rotationRadius; _posY = _transform.position.y + Mathf.Sin(_angle) * rotationRadius; //Assign the movment to the object that needs to be moved _objecToMovePosition.Set(_posX, _posY); objectToMove.transform.position = _objecToMovePosition; } #if UNITY_EDITOR private void OnDrawGizmosSelected() { Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(transform.position, rotationRadius); Gizmos.color = Color.green; float _posX = transform.position.x + Mathf.Cos(_angle) * rotationRadius; float _posY = transform.position.y + Mathf.Sin(_angle) * rotationRadius; Gizmos.DrawCube(new Vector2(_posX, _posY), new Vector3(3, 3, 3)); } #endif }
This code is responsible for moving the platform along a circular path and includes settings for speed, radius, starting position and a few more. I also added a visual gizmo so you can see the radius of the circle update in the unity editor as seen in the video below:
CircleLineRenderer.cs
The next component is a script I came across on Google and I’m sharing it here so you have a point of reference.
using UnityEngine; [RequireComponent(typeof(LineRenderer))] public class CircleLineRenderer : MonoBehaviour { private const float DOTTED_LINE_TICKNESS_RATIO = 2.5f; private const int NUM_CAP_VERTICES = 6; [Tooltip("The radius of the circle perimeter")] public float radius = 2; [Tooltip("The width of the line for the circle perimeter")] public float lineWidth = 0.05f; [Tooltip("Check this to use the dotted line material for the circle perimeter line")] public bool isDotted = false; [Tooltip("The material for the plain line")] public Material plainMaterial; [Tooltip("The material for the dotted line")] public Material dottedMaterial; private LineRenderer lineRenderer; void Start() { lineRenderer = GetComponent<LineRenderer>(); lineRenderer.useWorldSpace = false; lineRenderer.numCapVertices = NUM_CAP_VERTICES; SetRadius(radius); } void Update() { //While testing in-editor, refresh the circle each frame so we can test the circle by changing the fields in the inspector. if (Application.isEditor) SetRadius(radius); } //Call this method from other scripts to adjust the radius at runtime public void SetRadius(float pRadius) { radius = pRadius; if (radius <= 0.1f) { lineRenderer.positionCount = 0; return; } float tickness = lineWidth; if (isDotted) tickness *= DOTTED_LINE_TICKNESS_RATIO; lineRenderer.startWidth = tickness; lineRenderer.endWidth = tickness; //Calculate the number of vertices needed depending on radius so it always looks round. //For instance, huge circles need proportionaly less vertices than smaller ones to look good. //Source : http://stackoverflow.com/questions/11774038/how-to-render-a-circle-with-as-few-vertices-as-possible float e = 0.01f; //constant ratio to adjust, reduce this value for more vertices float th = Mathf.Acos(2 * Mathf.Pow(1 - e / radius, 2) - 1); //th is in radian int numberOfVertices = Mathf.CeilToInt(2 * Mathf.PI / th); lineRenderer.positionCount = numberOfVertices + 1; for (int i = 0; i < numberOfVertices + 1; i++) { float angle = (360f / (float)numberOfVertices) * (float)i; lineRenderer.SetPosition(i, radius * new Vector3(Mathf.Cos(Mathf.Deg2Rad * angle), Mathf.Sin(Mathf.Deg2Rad * angle), 0)); } //Update material depending if its a dotted line or a plain one. if (isDotted) { lineRenderer.material = dottedMaterial; lineRenderer.materials[0].mainTextureScale = new Vector3(2 * Mathf.PI * radius * (1 / tickness), 1, 1); } else { lineRenderer.material = plainMaterial; lineRenderer.materials[0].mainTextureScale = Vector3.one; } } //Call this method from other scripts to adjust the width of the line at runtime public void SetWidth(float pWidth) { lineWidth = pWidth; SetRadius(radius); } //Call this method from other scripts to switch between plain and dotted line at runtime public void SetIsDotted(bool pIsDotted) { if (isDotted != pIsDotted) { isDotted = pIsDotted; SetRadius(radius); } } }
This script creates the visual aid for my platform using the LineRenderer component. In order for this to work correctly you will need to create a material to get your visual aid to display in the game engine – in the image below you can see that I have the “Is Dotted” parameter checked on and i assigned a material called “Dotted_Line_Renderer” to the “Dotted Material” parameter.
The following image shows the material which i created:
It’s to important to note that the tiling needs to be set to 1,1 and below is the graphic which I assigned to the material slot:
And that’s about it in a nutshell. To be honest i had never used the LineRenderer component and only became aware of it because ChatGPT recommended using it for drawing a repeating pattern along a line in Unity…yup ChatGPT…the A.I. modeling language that’s going to change the world in a big, big way.
Have questions? Feel free to post them below in the comments section.