@Brian, asked me to to post this here instead of on Udemy.com.
This was my original message:
So I made it until the end of lesson 33 of the Unity 3rd Person Combat & Traversal course. I’m now able to lock on to the closest target. But lets say there are two or more enemies in front of me and I want to be able to switch between targets using my dpad arrow keys or a shoulder button or something. How would I do this?
I tried using ChatGPT to generate a script and it eventually kinda worked, but it kept giving me different control types like using ‘horizontal’ from the old input system, or a serializedfield where it just let me select one button. I could not figure out how to refactor the script to subscribe to the action… So I thought maybe I need to create a new state… like ActiveTargetingState and have the toggling going on there… but still I couldn’t figure it out. Any help would be welcome!
This is a script it gave me
public List<Target> targets; // List of all available targets
public CinemachineTargetGroup cineTargetgroup; // Reference to the Cinemachine target group
public Camera mainCamera; // Reference to the main camera
private Target currentTarget; // The current target that the player is locked onto
private Target selectedTarget; // The target currently selected by the player using the d-pad
public bool SelectTarget()
{
if (targets.Count == 0) { return false; }
Target closestTarget = null;
float closestTargetDistance = Mathf.Infinity;
foreach (Target target in targets)
{
Vector2 viewPosition = mainCamera.WorldToViewportPoint(target.transform.position);
if (viewPosition.x < 0 || viewPosition.x > 1 || viewPosition.y < 0 || viewPosition.y > 1)
{
continue;
}
Vector2 toCenter = viewPosition - new Vector2(0.5f, 0.5f);
if (toCenter.sqrMagnitude < closestTargetDistance)
{
closestTarget = target;
closestTargetDistance = toCenter.sqrMagnitude;
}
}
if (closestTarget == null) { return false; }
// If there is no selected target, lock onto the closest target
if (selectedTarget == null)
{
currentTarget = closestTarget;
cineTargetgroup.AddMember(currentTarget.transform, 1f, 2f);
}
// If there is a selected target, switch to it if it's on screen
else if (targets.Contains(selectedTarget))
{
currentTarget = selectedTarget;
cineTargetgroup.AddMember(currentTarget.transform, 1f, 2f);
}
return true;
}
private void UpdateSelectedTarget()
{
// Get input from the d-pad left and right buttons
float horizontalInput = Input.GetAxisRaw("Horizontal");
// If there is no horizontal input, don't do anything
if (horizontalInput == 0f)
{
return;
}
// Find the index of the current target in the list of targets
int currentIndex = targets.IndexOf(selectedTarget);
// If the current target is not found in the list, select the first target
if (currentIndex == -1)
{
currentIndex = 0;
}
// Determine the index of the next target based on the horizontal input
int nextIndex = currentIndex + (horizontalInput > 0f ? 1 : -1);
// If the next index is out of range, wrap around to the other end of the list
if (nextIndex < 0)
{
nextIndex = targets.Count - 1;
}
else if (nextIndex >= targets.Count)
{
nextIndex = 0;
}
// Update the selected target to be the next target in the list
selectedTarget = targets[nextIndex];
// If the selected target is different from the current target, switch to it
if (selectedTarget != currentTarget)
{
cineTargetgroup.RemoveMember(currentTarget.transform);
currentTarget = selectedTarget;
cineTargetgroup.AddMember(currentTarget.transform, 1f, 2f);
}
}
private void Update()
{
SelectTarget();
UpdateSelectedTarget();
}
Then it gave me this code when I asked to use the new input system, but I could not get the action event to work
private void UpdateSelectedTarget()
{
// Get input from the D-pad's horizontal axis
float horizontalInput = Input.GetAxisRaw("DPadHorizontal");
// If the horizontal input is not left or right, don't do anything
if (horizontalInput != -1f && horizontalInput != 1f)
{
return;
}
// Find the index of the current target in the list of targets
int currentIndex = targets.IndexOf(selectedTarget);
// If the current target is not found in the list, select the first target
if (currentIndex == -1)
{
currentIndex = 0;
}
// Determine the index of the next target based on the horizontal input
int nextIndex = currentIndex + (horizontalInput > 0f ? 1 : -1);
// If the next index is out of range, wrap around to the other end of the list
if (nextIndex < 0)
{
nextIndex = targets.Count - 1;
}
else if (nextIndex >= targets.Count)
{
nextIndex = 0;
}
// Update the selected target to be the next target in the list
selectedTarget = targets[nextIndex];
// If the selected target is different from the current target, switch to it
if (selectedTarget != currentTarget)
{
cineTargetgroup.RemoveMember(currentTarget.transform);
currentTarget = selectedTarget;
cineTargetgroup.AddMember(currentTarget.transform, 1f, 2f);
}
}
Then this code seemed to work but with the wrong input
using System.Collections;
using System.Collections.Generic;
using Cinemachine;
using UnityEngine;
public class Targeter : MonoBehaviour
{
[SerializeField] private CinemachineTargetGroup cineTargetgroup;
[SerializeField] private KeyCode switchLockOnKey = KeyCode.Tab;
private Camera mainCamera;
[SerializeField] private List<Target> targets = new List<Target>();
public Target CurrentTarget { get; private set; }
public Target SelectedTarget { get; private set; }
private void Start()
{
mainCamera = Camera.main;
}
private void Update()
{
SelectTarget();
UpdateSelectedTarget();
}
private void OnTriggerEnter(Collider other)
{
if (!other.TryGetComponent<Target>(out Target target))
{
return;
}
targets.Add(target);
target.OnDestroyed += RemoveTarget;
}
private void OnTriggerExit(Collider other)
{
if (!other.TryGetComponent<Target>(out Target target))
{
return;
}
RemoveTarget(target);
}
public bool SelectTarget()
{
if (targets.Count == 0) { return false; }
Target closestTarget = null;
float closestTargetDistance = Mathf.Infinity;
foreach (Target target in targets)
{
Vector2 viewPosition = mainCamera.WorldToViewportPoint(target.transform.position);
if (viewPosition.x < 0 || viewPosition.x > 1 || viewPosition.y < 0 || viewPosition.y > 1)
{
continue;
}
Vector2 toCenter = viewPosition - new Vector2(0.5f, 0.5f);
if (toCenter.sqrMagnitude < closestTargetDistance)
{
closestTarget = target;
closestTargetDistance = toCenter.sqrMagnitude;
}
}
if (closestTarget == null) { return false; }
if (SelectedTarget == null)
{
CurrentTarget = closestTarget;
cineTargetgroup.AddMember(CurrentTarget.transform, 1f, 2f);
}
else if (targets.Contains(SelectedTarget))
{
CurrentTarget = SelectedTarget;
cineTargetgroup.AddMember(CurrentTarget.transform, 1f, 2f);
}
return true;
}
public void UpdateSelectedTarget()
{
if (Input.GetKeyDown(switchLockOnKey))
{
if (SelectedTarget == null)
{
SelectedTarget = targets.Count > 0 ? targets[0] : null;
}
else
{
int index = targets.IndexOf(SelectedTarget);
SelectedTarget = targets[(index + 1) % targets.Count];
}
if (SelectedTarget != CurrentTarget)
{
cineTargetgroup.RemoveMember(CurrentTarget.transform);
CurrentTarget = SelectedTarget;
cineTargetgroup.AddMember(CurrentTarget.transform, 1f, 2f);
}
}
}
public void Cancel()
{
if (CurrentTarget == null) { return; }
cineTargetgroup.RemoveMember(CurrentTarget.transform);
CurrentTarget = null;
}
private void RemoveTarget(Target target)
{
if (CurrentTarget == target)
{
cineTargetgroup.RemoveMember(CurrentTarget.transform);
CurrentTarget = null;
}
target.OnDestroyed -= RemoveTarget;
targets.Remove(target);
}
}
I then figured instead of using the dpad left and right, I’d just use one button to toggle between the enemies in viewport. So I created an action called ‘SwitchLockOn’ and set it to Button North on the gamepad. But I cannot get it to work. Could someone help refactor the code a bit?
Thanks!!