I am trying to build a system that finds the nearest position of an object and uses it to transform the Player to a specific position. I understand the issue but I couldn't eliminate the yellow exclamation mark. The program is somehow working, but I'd like to see the solution.
The issue according to UnityConsole is: You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all UnityEngine.MonoBehaviour:.ctor ()
Scripts are: #1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class ScoreMultiplerFinder : MonoBehaviour
{
private static ScoreMultiplerFinder instance;
private List<GameObject> checkpoints = new List<GameObject>();
public List<GameObject> Checkpoints { get { return checkpoints; } }
public static ScoreMultiplerFinder Singleton
{
get
{
if (instance == null)
{
instance = new ScoreMultiplerFinder();
instance.Checkpoints.AddRange(
GameObject.FindGameObjectsWithTag("Checkpoint"));
instance.checkpoints = instance.checkpoints.OrderBy(waypoint => waypoint.name).ToList();
}
return instance;
}
}
}
#2(inPlayerObject)
public void FindClosestMultiplier()
{
float lastDist = Mathf.Infinity;
for (int i = 0; i < ScoreMultiplerFinder.Singleton.Checkpoints.Count; i )
{
GameObject thisWP = ScoreMultiplerFinder.Singleton.Checkpoints[i];
float distance = Vector3.Distance(transform.position, thisWP.transform.position);
if (distance < lastDist)
{
currentIndex = i;
lastDist = distance;
lastScoreMultip.position = ScoreMultiplerFinder.Singleton.Checkpoints[currentIndex].transform.position;
}
}
}
Thanks in advance and have a great day!
CodePudding user response:
The error/warning is pretty much telling you what to do instead. A correct way to lazy initialize the Singleton would be
private List<GameObject> checkpoints;
// First of all be consequent and let nobody change the content of this
public IReadOnlyList<GameObject> Checkpoints => checkpoints;
public static ScoreMultiplerFinder Singleton
{
get
{
if(instance) return instance;
// before creating one check if there maybe is one already first
instance = FindObjectOfType<ScoreMultiplerFinder>();
if(instance) return instance;
// GameObject is one of the very few UnityEngine.Object types where using
// "new" is actually okey
instance = new GameObject(nameof(ScoreMultiplerFinder)).AddComponent<ScoreMultiplerFinder>();
return instance;
}
}
private void Awake ()
{
// Without this your "Singleton" is not complete and you rather only have a
// lazy factory property.
// For a valid singleton pattern you have to make sure that there actually
// exists only one single instance at a time!
if(instance && instance != this)
{
Destroy (gameObject);
return;
}
instance = this;
checkpoints = GameObject.FindGameObjectsWithTag("Checkpoint").OrderBy(waypoint => waypoint.name).ToList();
}
And then if you already use Linq you are already familiar with OrderBy
.
Your second script can be shrinked down to
public void FindClosestMultiplier()
{
// Simply get the waypoints
// - "OrderBy" them by distance. using the "sqrMagnitude" is faster and for ordering has exactly the same effect as
// "Distance" which uses the more expensive "magnitude"
// - use "FirstOrDefault" to get either the closest item or "null" if there wasn't any at all
var closest = ScoreMultiplerFinder.Singleton.Checkpoints.OrderBy(c => (transform.position - c.transform.position).sqrMagnitude).FirstOrDefault();
// Was there any at all?
if(!closest) return;
// Otherwise "closest" is the closest waypoint -> do something with it
lastScoreMultip.position = closest.position;
}
Actually as mentioned I don't think you need a MonoBehaviour
at all.
You could as well simply have
public static class ScoreMultiplerFinder
{
private static List<GameObject> checkpoints;
public static IReadOnlyList<GameObject> Checkpoints
{
get
{
if(checkpoints == null) checkpoints = GameObject.FindGameObjectsWithTag("Checkpoint").OrderBy(waypoint => waypoint.name).ToList();
return checkpoints;
}
}
}
and then accordingly do
public void FindClosestMultiplier()
{
var closest = ScoreMultiplerFinder.Checkpoints.OrderBy(c => (transform.position - c.transform.position).sqrMagnitude).FirstOrDefault();
if(!closest) return;
lastScoreMultip.position = closest.position;
}