How to position "orbs" on Unity2D?

Hi.
Thanks in advance for reading this thread.

I’m trying to make an orb-like system on Unity2D where orb-like enemies rotate around the player and occasionally spawn a “blast” prefab. Thing is, the number of orbs need to be flexible and adapt to the amount of the proper caster enemies and they need to rotate around the player.

Rotation and dealing with the different number of enemies is easy (I’ve made an empty object positioned on the same spot as the player and got it rotating; and I’ll be using either a list or an array for keeping track of the enemies - probably the former), but I have no idea on how to implement the positioning of the orbs. Basically, I need them to position themselves at an evenly distance around the player and scattered around a circle (360º/number of orbs). Examples:
1 orb: 360º - in front of the player
2 orbs: 180º - one in the front and one in the back
3 orbs: 120º - a triangle with one at 12 o’clock, one at 8 and another at 4.
4 orbs: 90º - a diamond: 12, 3, 6, and 9 o’clock
5 orbs: 72º - a star
And so on.
They’d have to be children of the empty game object that rotates at the same position as the player.

Here’s a thought: keep track of these orb positions as a single floating point that represents the angle around the player, one float per orb.

Every frame you can calculate the precise angle those should be. You would calculate as many as you need for a given count of orbs.

The calculation would be like:

float[] angles = new angles[MAX];
for (int i = 0; i < MAX; i++)
{
  angles[i] = (360.0f * i) / MAX;
}

That will space them evenly around 360 regardless of MAX.

Every frame, the orbs would use their index position (in the master array) to look at the precise angle they’re supposed to be at, and then move towards it if they aren’t already at that precise angle.

When a new orb arrives or leaves, the overall count would cause the precise angle calculations to change, and they would now hustle to their new positions around the circle.

Then for updating their actual position in Unity3D you can just use their angle in a Z-rotated quaternion (I assume Z is your spin-around axis and you move in X,y), and rotate a Vector3.right by that amount, then assign it to the .localPosition underneath the overall spinning object, like

orb.transform.localPosition = Quaternion.Euler( 0, 0, OrbAngle) * Vector3.right;

Whew, that was dense. LMK if something didn’t make sense.

And yes, as you said, make the orbs children of the spinner, and spin that one.

NINJA EDIT: the above assignment should be to .localPosition! I did it wrong initially.

1 Like

Thanks a lot for the help!

All of it made sense and I got it up to speed. I added a variable distance and put it as a multiplier for localPosition. The only problem I’ve had is that using arrays isn’t exactly flexible and when I change the size midgame, things get funky, as I can’t change an array size once it has been initiated. This is important because the number of orbs depend on the number of enemies in the scene and the number always decreases (new enemies of this kind are never spawned, only killed).

How could I make it work in a way that, if I remove one of the orb GameObjects, the others would reposition themselves? It’s using lists, right? But how?

Yes, using lists. It’s similar to arrays but all different. Alas.

So arrays use .Length, but List uses .Count.

But both of them use the square brackets to dereference individual items, zero-based.

Lists support RemoveAt() and Add() to change them, whereas Arrays are fixed-length once made.

Once you get the hang of it, it’s not too bad.

If you have a List, you can make it a corresponding array with a .ToArray() call.

If you have an Array you can make a List out of it with the constructor:

int[] myArray = new int[] { 1,2,3};

List<int> myList = new List<int>( myArray);

So yeah, almost interchangeable, and if you need to change sizes, stay away from arrays and just use lists!

ALSO, you can also take the cheesey-oldschool way of making a single array of worst case size (the largest you’d ever need) and keep your own integer that says how many there are in the array, but that often leads to more bugs if you screw up the accounting, plus you might have to move stuff around yourself, whereas with List you can remove and insert at will.

1 Like

Hello!

Thanks a lot for the help. I’ve figured out how to get everything running and the following code works as expected with no bugs. However, I get the following Console message:

ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
System.ThrowHelper.ThrowArgumentOutOfRangeException (System.ExceptionArgument argument, System.ExceptionResource resource) (at <599589bf4ce248909b8a14cbe4a2034e>:0)
System.ThrowHelper.ThrowArgumentOutOfRangeException () (at <599589bf4ce248909b8a14cbe4a2034e>:0)
System.Collections.Generic.List`1[T].get_Item (System.Int32 index) (at <599589bf4ce248909b8a14cbe4a2034e>:0)
TransformHandler.FixedUpdate () (at Assets/Scripts/Wolf/TransformHandler.cs:42)

Code (I’ve commented line 42. It’s at the end of the script):

public class TransformHandler : MonoBehaviour
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public Transform player; //player transform to stay centralized around the player
    public float rotationSpeed; //the speed at which the orbs will rotate around the player
    public float distance; //the distance the orbs will keep from the player
    public List<GameObject> orb; //The list with the orbs GameObjects
    public List<float> angles; //list of angle values
    public GameObject prefab; //Orb prefab
    private int enemyAmount; //number of enemies when play is pressed
  

    // Start is called before the first frame update
    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player").transform;
        transform.position = player.position; //Setting the player as the center
        enemyAmount = GameObject.FindGameObjectsWithTag("Opponent").Length; //Counting the enemies
        for (int i = 0; i < enemyAmount; i++)
            orb.Add(Instantiate(prefab, transform)); //Instantiating the orbs
    }

    void Update()
    {
        //Adjusting the number of angles according to the number of orbs and then calculating their values
        while (orb.Count > angles.Count)
            angles.Add(0);
        while (orb.Count < angles.Count)
            angles.RemoveAt(0);
        for (int i = 0; i <orb.Count; i++)
            angles[i] = (360.0f * i) / orb.Count;
    }
    private void FixedUpdate()
    {
        transform.position = player.position;
        transform.Rotate(0, 0, rotationSpeed);

        for (int i = 0; i < orb.Count; i++)
            orb[i].transform.localPosition = Quaternion.Euler(0, 0, angles[i]) * Vector3.right * distance; //PROBLEM LINE!!!
    }

    //Function to be called when an enemy is destroyed
    public void ReduceOrbCount()
    { orb.RemoveAt(0); }
}

Is something wrong with my code? Like I said, it works perfectly, but there’s this error message and it’s bothering me. What should I do?

Debug it!

On the line right prior to the one indicated by the error, Debug.Log() the size of the array and the value you’re trying to use for accessing it, and then track down the issue!

Remember, FixedUpdate() and Update() do NOT get called at the same rate, so what is likely happening is you are adjusting the size, then running FixedUpdate() and the orb size mismatches the angles size, since Update() hasn’t gotten called yet.

You could probably move your present Update() functionality into your FixedUpdate()…

For more insight on timing and calling order, this is your go-to reference. Keep it handy!