Need help with multiple updating transform positions

Hi all,
So I’m trying to get my player in my game to attack the closest target within a distance when the player isn’t moving. I’m new to programming, and I have mostly been using the Unity Learn site and googling to figure out solutions to the problems that arise. However I can’t seem to figure out how to find the transform positions of multiple “Enemies” within my game. I can get it to work if I initialize my enemy gameobject constantly in the update method, but it only handles one random gameobjects position at a time. So the mechanic of getting the player to attack the closest enemy doesn’t work, the player just attacks whatever gameobjects position was initialized first. How do I go about writing this code so that I can find all the positions of the enemies that spawn within a wave, and make it so my player attacks the closest one? Perhaps store multiple Vector3 variables somehow? Also if you have any suggestions or advice for my code, I’ll happily take any criticism. I know it’s probably sloppy and inefficient.
Here is my code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Assertions.Must;
using UnityEngine.UIElements;

public class PlayerController : MonoBehaviour
{
    protected Joystick joystick;
    private Animator playerAnim;
    private Rigidbody playerRb;
    private GameObject[] enemy;
    private GameObject firePos;

    public GameObject arrowPrefab;
    public HealthBar healthBar;

    public int maxHealth = 100;
    public int currentHealth;
    private int enemyCount;
   
    private float movementSpeed = 5.0f;
    private float minAttackDistance = 8.0f;
    private float rotationSpeed = 0.5f;
    private float distance;
    private float timer;
    private float aimTime = 0.5f;
    private float attackDelay = 1.0f;
    private float attackTimer = 0f;
    private float arrowDelay = 0.2f;

    public bool gameOver;
    
    void Start()
    {
        currentHealth = maxHealth;
        healthBar.SetMaxHealth(maxHealth);

        //Gets the fire position game object thats attached to the palyer
        firePos = GameObject.Find("FirePos");
        //Gets the Enemy Gameobject
        enemy = GameObject.FindGameObjectsWithTag("Enemy");
        //gets the animator on the player
        playerAnim = GetComponentInChildren<Animator>();
        //gets the Rigidbody on the player
        playerRb = GetComponent<Rigidbody>();
        //get the joystick asset from the scene
        joystick = FindObjectOfType<Joystick>();
    }

    void Update()
    {
        
        timer += Time.deltaTime;

        enemyCount = FindObjectsOfType<EnemyController>().Length;
        if (enemyCount == 0)
        {
            enemy = GameObject.FindGameObjectsWithTag("Enemy");
        }

        //Code trying to get player to attack enemy within range
        distance = Vector3.Distance(transform.position, enemy.transform.position);

        //sets the animations to play for the player based on parameters
        if (playerRb.velocity.magnitude > 0)
        {
            playerAnim.SetBool("Walk_b", true);
            attackTimer = 0f;
        }

         else if (playerRb.velocity.magnitude == 0)
        {
            playerAnim.SetBool("Walk_b", false);
            attackTimer += Time.deltaTime;
        }

        if (playerRb.velocity.magnitude > 3)
        {
            playerAnim.SetBool("Run_b", true);
        }

        else if (playerRb.velocity.magnitude < 3)
        {
            playerAnim.SetBool("Run_b", false);
        }

        if (currentHealth == 0)
        {
            playerAnim.SetBool("Death_b", true);
            gameOver = true;
            Debug.Log("Game Over!");

        }
        //Rotates the player towards enemy when within distance
        if (distance < minAttackDistance)
        {
            //Rotates the player in the direction of the enemy
            GenerateEnemyPos();
            Quaternion targetRotation = Quaternion.LookRotation(GenerateEnemyPos() - transform.position);
            targetRotation.x = 0;
            targetRotation.z = 0;
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed);
        }
        //Statement that makes the player attack if within range and can attack
        if (distance < minAttackDistance && playerRb.velocity.magnitude == 0 && timer >= attackDelay && attackTimer > aimTime)
        {
            playerAnim.SetBool("Attack_b", true);
            InvokeRepeating("Attack", arrowDelay, attackDelay);
        }
        
        // gets the rigidbody component from the player
        var rigidbody = GetComponent<Rigidbody>();

        // adds force to the player in the direction of the joystick so the player moves if the game is not over
        if (!gameOver)
        {
            rigidbody.velocity = new Vector3(joystick.Horizontal * movementSpeed, rigidbody.velocity.y, joystick.Vertical * movementSpeed);
            //sets the joystick input for the for the rotation
            Vector2 input = new Vector2(joystick.Horizontal, joystick.Vertical);
            Vector2 inputDir = input.normalized;

            //makes it so the players rotation doesnt snap back to the forward position
            if (inputDir != Vector2.zero)
            {
                //Rotates the player based on the angle of the joystick
                transform.eulerAngles = Vector3.up * Mathf.Atan2(inputDir.x, inputDir.y) * Mathf.Rad2Deg;
            }
        }
        else if (gameOver)
        {
            movementSpeed = 0f;
            rigidbody.velocity = new Vector3(movementSpeed, movementSpeed, movementSpeed);
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        //When the player comes into contact with an enemy, hurt the player 20HP
        if (collision.gameObject.CompareTag("Enemy"))
        {
            TakeDamage(20);
            playerAnim.SetTrigger("Damage_trig");

            if (currentHealth < 0)
            {
                currentHealth = 0;
            }
        }
    }
    //Method that decreases the health on the health bar
    void TakeDamage(int damage)
    {
        currentHealth -= damage;

        healthBar.SetHealth(currentHealth);
    }

    private void OnTriggerEnter(Collider collision)
    {
        //When the player collects a heartpickup heal the player 50HP
        if (collision.gameObject.CompareTag("Heart"))
        {
            HealPlayer(50);
        }

    }
    //Method that increases the health on the health bar
    void HealPlayer(int health)
    {
        currentHealth += health;
        healthBar.SetHealth(currentHealth);

        if (currentHealth > maxHealth)
        {
            currentHealth = maxHealth;
        }
    }
    void Attack()
    {
        timer = 0f;
        //spawns arrow to shoot at enemy
        Instantiate(arrowPrefab, firePos.transform.position, transform.rotation);
        CancelInvoke();
        playerAnim.SetBool("Attack_b", false);
    }

    private Vector3 GenerateEnemyPos()
    {
        Vector3 enemyPos = enemy.transform.position;

        return enemyPos;
    }
}

Thanks in advance!

When you instantiate your enemies keep them in a list

private List<GameObject> Enemies = new List<GameObject>();

public void SpawnWave()
{
    // Eg:
    var enemy = Instantiate(obj, transform, identity);
    Enemies.Add(enemy);
}

public GameObject GetClosestEnemy(GameObject relativeTo)
{
    GameObject closestEnemy = null;
    float closestDistance = float.MaxValue;

    foreach (var enemy in Enemies)
    {
        var distance = Vector3.Distance(relativeTo.transform.position, enemy.transform.position);
        if (distance < closestDistance)
        {
            closestDistance = distance;
            closestEnemy = enemy;
        }
    }

    return closestEnemy;
}

Then use like:

var closestEnemy = GetClosestEnemy(player);

Make sure to remove enemies that have died from the Enemies list.

@ShadyProductions Thank you! This was very helpful.

So, since my SpawnWave(); method is controlled by a SpawnManager script, I put the list and the method in that script. Then I put the GetClosestEnemy(); method in my PlayerController script since the player is the thing looking for the closest enemy.

Here is my script for the SpawnManager:

using System.Collections;
using System.Collections.Generic;
using System.Security.Principal;
using UnityEngine;

public class SpawnManager : MonoBehaviour
{
    public List<GameObject> enemies = new List<GameObject>();
    public GameObject enemyPrefab;
    
    private int enemyCount;
    private int waveNumber = 1;
    private float spawnRange = 9.0f;
    

    // Start is called before the first frame update
    void Start()
    {
        SpawnEnemyWave(waveNumber);
    }

    // Update is called once per frame
    void Update()
    {
        enemyCount = FindObjectsOfType<EnemyController>().Length;
        if (enemyCount == 0)
        {
            waveNumber++;
            SpawnEnemyWave(waveNumber);
        }
    }

    private Vector3 GenerateSpawnPos()
    {
        float spawnPosX = Random.Range(-spawnRange, spawnRange);
        float spawnPosZ = Random.Range(-spawnRange, spawnRange);
        Vector3 randomPos = new Vector3(spawnPosX, 0, spawnPosZ);

        return randomPos;
    }

    public void SpawnEnemyWave(int enemiesToSpawn)
    {
        for (int i = 0; i < enemiesToSpawn; i++)
        {
            var enemy = Instantiate(enemyPrefab, GenerateSpawnPos(), enemyPrefab.transform.rotation);
            enemies.Add(enemy);
        }
    }
}

I believe this script is good. Now my PlayerController script. I’m having trouble changing code I’ve written to work with this new code. First off, is there a way to reference the enemies List from another script? Second, these two if statements is how I was controlling the Player’s rotation and projectile spawning:

    //Rotates the player towards enemy when within distance
            if (distance < minAttackDistance)
            {
                //Rotates the player in the direction of the enemy
                var closestEnemy = GetClosestEnemy(player);
                Quaternion targetRotation = Quaternion.LookRotation(closestEnemy.transform.position - transform.position);
                targetRotation.x = 0;
                targetRotation.z = 0;
                transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed);
            }
            //Statement that makes the player attack if (within range) and (can attack)
            if (distance < minAttackDistance && playerRb.velocity.magnitude == 0 && timer >= attackDelay && attackTimer > aimTime)
            {
                playerAnim.SetBool("Attack_b", true);
                InvokeRepeating("Attack", arrowDelay, attackDelay);
            }
void Attack()
    {
        timer = 0f;
        //spawns arrow to shoot at enemy
        Instantiate(arrowPrefab, firePos.transform.position, transform.rotation);
        CancelInvoke();
        playerAnim.SetBool("Attack_b", false);
    }

Is there a way to use the new var closestEnemy to get the player to rotate and fire at the closest enemy? And only when the closest enemy is within a certain distance, like attack range? Maybe with something like Transform.LookAt? Lastly, (if there is a way to reference lists from other scripts) the enemyController script is what controls the removal of enemies when their health reaches zero. So do I just put enemies.Remove(enemy) in the if statement to remove from List?

Again I want to thank you for your time. This is my first project and I’m still in the process of learning. So I apologize if these questions or my code seems redundant. :slight_smile: