Reputation: 49
I have a player class in Unity which all works fine except for my ClosestTarget function. The function works how I wanted it to but now it only ever picks Cube 2 (The last element in the list) even if I'm closer to another cube.
The class is as shown:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Player : MonoBehaviour
{
public int health; //Current health
public int stamina; //Current stamina
public int maxHealth = 100; //Constant for max health
public int maxStamina = 500; //Constant for max stamina
protected CharacterController chCtrl; //Reference to the character controller
protected CharacterMotor chMotor; //Reference to the character motor
public float walkSpeed = 3; //Speed at which the player walks
public float runSpeed = 20; //Speed at which the player runs
public bool isWalking = false; //Check for whether the player is walking
public bool isRunning = false; //Check for whether the player is running
public bool isFatigued = false; //Check for whether the player is fatigued
public List<Transform> targets; //Create a list of transforms
public Transform currentTarget;
float distance = Mathf.Infinity;
// Use this for initialization
void Start ()
{
//Get the character controller assigned to the current game object
chCtrl = GetComponent<CharacterController> ();
//Get the character motor assigned to the current game object
chMotor = GetComponent<CharacterMotor> ();
//Set the stamina to the max stamina
stamina = maxStamina;
//Set the health to the max health
health = maxHealth;
//Initialise the targets list
targets = new List<Transform>();
//Call the function to retrieve all buttons in the scene
AddAllButtons();
}
// Update is called once per frame
void Update ()
{
//Call the function to set the speed of the player
SetSpeed ();
ClosestTarget();
}
public void SetSpeed ()
{
//Set the player to walking speed by default
float speed = walkSpeed;
//If the stamina is less than or equal to 0
if (stamina <= 0)
{
//Set the player as fatigued
isFatigued = true;
//Set the player to walking
speed = walkSpeed;
//Set stamina to 0
stamina = 0;
}
//If the stamina is greater than or equal to max stamina
if(stamina >= maxStamina)
{
//Set the stamina to the max stamina
stamina = maxStamina;
//Set the player as not fatigued
isFatigued = false;
}
//If the player is moving along either the x or y axis
if (Input.GetAxis("Horizontal") !=0 || Input.GetAxis("Vertical") !=0)
{
//If the player is fatigued
if(isFatigued == true)
{
//Set the player to walking speed
speed = walkSpeed;
//Player is not running
isRunning = false;
//Player is walking
isWalking = true;
//Start increasing stamina
stamina++;
}
//If the player is touching the ground and the user is either pressing left shift or right shift
else if (chCtrl.isGrounded && Input.GetKey ("left shift") || Input.GetKey ("right shift") && isFatigued == false )
{
//Set the player to running speed
speed = runSpeed;
//Player is running
isRunning = true;
//Player is not walking
isWalking = false;
//Start reducting stamina
stamina--;
}
else
{
//Set the player to walking speed
speed = walkSpeed;
//Player is not running
isRunning = false;
//Player is walking
isWalking = true;
//Start increasing stamina
stamina++;
}
}
else
{
//Player is not running
isRunning = false;
//Player is not walking
isWalking = false;
//Start increasing stamina
stamina++;
}
//Set the players speed to either walkSpeed or runSpeed
chMotor.movement.maxForwardSpeed = speed;
}
void AddAllButtons()
{
//Create an array that contains all game objects tagged with 'button'
GameObject[] buttons = GameObject.FindGameObjectsWithTag("Button");
//For each of the game objects in the array
foreach(GameObject button in buttons)
{
//Add the transform of the button
AddButton(button.transform);
}
}
void AddButton(Transform button)
{
//Add the transform of the button into the targets list
targets.Add(button);
}
void ButtonCheck(Transform button)
{
Vector3 dir = (button.position - transform.position).normalized;
float direction = Vector3.Dot(dir, transform.forward);
Debug.Log(direction);
if(Input.GetKeyDown(KeyCode.E))
if(direction > 0.7F && Vector3.Distance(currentTarget.position, transform.position) < 2.0F)
{
print("Button has been clicked");
}
}
void ClosestTarget()
{
foreach (Transform button in targets)
{
Vector3 diff = (button.position - transform.position);
float curDistance = Vector3.Distance(transform.position, button.position );
Debug.Log(curDistance);
if (curDistance < distance )
{
currentTarget = button;
distance = curDistance;
}
}
}
}
As said, the problem is with the ClosestTargets function. It's finding the distance for every cube but it only ever selects the last element in the targets list.
It still shows the distance from every cube in the console.
Upvotes: 1
Views: 7389
Reputation: 1665
I use this extension method to find the closest gameObject (warning it uses linq):
public static GameObject ReturnClosestObject(this GameObject go, float radius, LayerMask layerMask)
{
Collider[] closeObjects = Physics.OverlapSphere(go.transform.position, radius, layerMask);
closeObjects = closeObjects.OrderBy((collider) => Vector3.Distance(collider.gameObject.transform.position, go.transform.position)).ToArray();
GameObject returnedObject = null;
if(closeObjects.FirstOrDefault().Exist())
{
returnedObject = closeObjects.FirstOrDefault().gameObject;
}
return returnedObject;
}
The downside to this method is that it will only detect objects that overlapwith the projected sphere and miss objects that are inside it completly. So I will often compliment it with this recursive function that projects a finit amount of sphere at various distances.
Note while they have similier names, the above is a extension method and below is a instance method. They work together hand and hand.
public GameObject FindClosestGameObject(int startingLookUpDistance, int maxLookUpDistance, int numberOfSteps)
{
GameObject closestGameObject = gameObject.ReturnClosestObject(startingLookUpDistance, LayerMasks.gameObjects);
bool gameObjectNotFound = (closestGameObject.Equals(null));
if(gameObjectNotFound)
{
if(startingLookUpDistance <= maxLookUpDistance)
{
float stepDistance = maxLookUpDistance / numberOfSteps;
return FindClosestBuildingObject((int)(startingLookUpDistance + stepDistance), maxLookUpDistance, numberOfSteps);
}
return null;
}
else
{
return closestGameObject;
}
}
This top method works by specifying your starting distance and your max distance, you then specify how many iterations of the function you want to perform. Based on the number of iterations it will cast a n spheres equal distances apart in between your start and stop bounds.
Upvotes: 1
Reputation: 13146
My guess: distance
is class member and correctly initialised to Mathf.Infinity
but it's never reset. So on the first call to ClosestTarget ()
Cube2 might be the one closest to player as Player.Update
might called later. So distance
contains a value too tiny (0?) for other objects to have a chance.
Upvotes: 1