Why the waypoints list and the agents list are empty? - c#

I created a WaypointsClass class :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System;
using UnityEngine.AI;
[Serializable]
public class WaypointsClass
{
public List<Transform> points = new List<Transform>();
public List<NavMeshAgent> agents = new List<NavMeshAgent>();
}
Then created WaypointsAI script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WaypointsAI : MonoBehaviour
{
public WaypointsClass waypoints;
public float distanceToContinue;
private float currentDistanceToPoint;
private int lastPointIndex;
private int goalPointIndex;
void Start()
{
//Firstly check if the waypoints are set up correctly
if (waypoints.points.Count < 1)
{
Debug.LogError("Set up the waypoints for this gameObject!");
}
else
{
//Now set up the path
lastPointIndex = 0; //Start from the index 0
waypoints.agents[0].transform.position = waypoints.points[0].position;
if (waypoints.points.Count > 1)
{
goalPointIndex = 1; //Go to the [1] waypoint
}
else
{
goalPointIndex = 0;
}
}
}
void FixedUpdate()
{
for (int i = 0; i < waypoints.agents.Count; i++)
{
//Calculate the distance and check if it should move to the next waypoint.
currentDistanceToPoint = Vector3.Distance(waypoints.agents[i].transform.position, waypoints.points[goalPointIndex].position);
if (currentDistanceToPoint <= distanceToContinue)
{
//Save the old index, totally useless in this implementation though
lastPointIndex = goalPointIndex;
//Increase goal index to change the goal waypoint to the next, (Or maybe random one?)
goalPointIndex++;
if (goalPointIndex >= waypoints.points.Count)
goalPointIndex = 0;
}
//Now move towards the current waypoint, Change this to fit your code with navMesh anyway I think I did a lot for you anyway
waypoints.agents[i].transform.LookAt(waypoints.points[goalPointIndex].position);
waypoints.agents[i].transform.Translate(Vector3.forward * Time.deltaTime * 10);
}
}
}
Then added for each group of agents and waypoints a script. Red and Blue :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class RedWaypoints : MonoBehaviour
{
public GameObject[] redWaypoints;
public NavMeshAgent redAgent;
// Start is called before the first frame update
void Start()
{
redWaypoints = GameObject.FindGameObjectsWithTag("Red_Waypoint");
WaypointsClass wpc = new WaypointsClass();
foreach (GameObject point in redWaypoints)
{
wpc.points.Add(point.transform);
}
wpc.agents.Add(redAgent);
}
// Update is called once per frame
void Update()
{
}
}
And blue :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class BlueWaypoints : MonoBehaviour
{
public GameObject[] blueWaypoints;
public NavMeshAgent blueAgent;
// Start is called before the first frame update
void Start()
{
blueWaypoints = GameObject.FindGameObjectsWithTag("Blue_Waypoint");
WaypointsClass wpc = new WaypointsClass();
foreach (GameObject point in blueWaypoints)
{
wpc.points.Add(point.transform);
}
wpc.agents.Add(blueAgent);
}
// Update is called once per frame
void Update()
{
}
}
Then I created in the Hierarchy and in the Scene group of red waypoints and red character and group of blue waypoints with blue character :
What I want to do the main goal is to move the blue character between the blue waypoints and move the red character between the red waypoints using the same WaypointsAI script. And if the blue group will have 5 character then move the 5 character between the blue waypoints same if the red group will have 50 characters they will move between the red waypoints.
The first problem for now is that the lists points and agents are empty in the WaypointsAI script.
I added a break point to this line :
if (waypoints.points.Count < 1)
And both lists are empty.
I tried to move the code in the Start() in both scripts RedWaypoints and BlueWaypoints to the Awake() and it does a new instance for the WaypointsClass class but when it's getting to the WaypointsAI scripts the lists are empty. I can't figure out why.
Even simpler in this screenshot I have two characters the one in t pose and the droid with the blue circle in the middle. and I want each one to move between other waypoints. one can move up the stairs and back the other move to the window and back :

Your problem : you cant access to the instance of class created in another script.
If i have understood your problem... I suggest you to use Static Class and Static list so you could access from anyscripts:
public static class Waypoints
{
public static List<Transform> Redpoints = new List<Transform>();
public static List<NavMeshAgent> Redagents = new List<NavMeshAgent>();
public static List<Transform> Bluepoints = new List<Transform>();
public static List<NavMeshAgent> Blueagents = new List<NavMeshAgent>();
}
public class RedWaypoints : MonoBehaviour
{
public GameObject[] redWaypoints;
public NavMeshAgent redAgent;
// Start is called before the first frame update
void Start()
{
redWaypoints = GameObject.FindGameObjectsWithTag("Red_Waypoint");
foreach (GameObject point in redWaypoints)
{
Waypoints.Redpoints.Add(point.transform);
}
Waypoints.Redagents.Add(redAgent);
}
:
:
}
public class BlueWaypoints : MonoBehaviour
{
public GameObject[] blueWaypoints;
public NavMeshAgent blueAgent;
// Start is called before the first frame update
void Start()
{
blueWaypoints = GameObject.FindGameObjectsWithTag("Blue_Waypoint");
foreach (GameObject point in blueWaypoints)
{
Waypoints.Bluepoints.Add(point.transform);
}
Waypoints.Blueagents.Add(blueAgent);
}
:
:
}
From another Script, if you want to access first Red point item, you just type Waypoints.Redpoints[0] and for your First Red agent you just type Waypoints.Redagents[0] and same thing for blue side
In WaypointsAI you dont need to declare public WaypointsClass waypoints;

Related

How to load death scene in C#/unitygames?

haven't posted on here before but I have been trying for a while to create a game and would like a death/game over sort of scene to appear when the player loses all 3 of their lives. I have a functioning game manager and my player can lose lives (they have 3). This is all being done in unity games and is 2d (idk if that helps). I currently have other stuff in my scene loader script that works fine so I will post the whole thing but I am having issues with the bottom most code!
Thank you!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneLoader : MonoBehaviour
{
public string scenename;
public GameManager GM;
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.tag == "Player")
{
SceneManager.LoadScene(scenename);
}
}
private void Deathscene()
{
if(GM.LifeTotal == 0)
{
SceneManager.LoadScene(Bob);
}
}
}
Gamemanager script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public int PotionsCollected = 0;
public int LifeTotal = 3;
public Text PotionsOutput;
public Text LifeOutput;
void Update()
{
PotionsOutput.text = "Potions: " + PotionsCollected;
LifeOutput.text = "Life: " + LifeTotal;
}
public void CollectPotion()
{
PotionsCollected++;
}
public void UsePotion()
{
PotionsCollected--;
}
public void LoseLife()
{
LifeTotal--;
}
}
What you can do is from your Unity Editor go to File->Build Settings and then drag and drop inside the active scenes window your death scene.
Then an index will be generated on the right side of the window and you can use that index to load the scene. like this:
SceneManager.LoadScene("Use your generated index");
NOTE: Use your index as a number and not as a string.
UPDATED Solution:
public void LoseLife()
{
LifeTotal--;
if(LifeTotal <= 0)
{
SceneManager.LoadScene("Use your generated index");
}
}
I supposse LoseLife() it's called when the enemy attacks your player.

How to make object appear when a certain torso is called using C# in Unity

This is the code I used for a tutorial
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Trial_Changer : MonoBehaviour
{
[Header("Sprite To Change")]
public SpriteRenderer bodyPart;
[Header("Sprites to Cycle Through")]
public List<Sprite> options = new List<Sprite>();
public static int currentTorsoOption = 0;
public void NextOption ()
{
currentTorsoOption++;
if (currentTorsoOption >= options.Count)
{ currentTorsoOption = 0; }
bodyPart.sprite = options[currentTorsoOption];
}
public void PreviousOption()
{
currentTorsoOption--;
if (currentTorsoOption <= 0)
{ currentTorsoOption = options.Count - 1; }
bodyPart.sprite = options[currentTorsoOption];
}
}
**This is the code I am using to try to say "if torso 2 is called, create object"
**
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpeechAppears : MonoBehaviour
{
public GameObject speechBubbleTOSpawn;
private int Torso = Trial_Changer.currentTorsoOption;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Torso == 2)
{
Instantiate(speechBubbleTOSpawn);
}
}
}
Except the torso just appears always.
What do I do to fix this?
I want an object to appear when torso 2 is on the screen but instead the object is just always present. What do I do ?
The culprit is this line :
private int Torso = Trial_Changer.currentTorsoOption;
Since you assign value Torso only once at the start of the game, its value always be the same.
Try this :
private int Torso => Trial_Changer.currentTorsoOption;
This change Torso from a field to a property, and when Torso is used, it will read the current value of Trial_Changer.currentTorsoOption.

Next Scene not loading when requested to

I have created a game where when the user breaks all the blocks he is taken to the next scene but this is not happening despite adding all of the scenes I have in the build settings. I have no errors whatsoever and the scene is written correctly. Can someone help me resolve this, please?
This is the build settings
Bricks script : (where the scene is called)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bricks : MonoBehaviour {
public LevelManager myLevelManager;
public static int brickCount = 0;
public int maxNumberOfHits = 0;
int timesHit;
public AudioClip BlockBreaking;
// Use this for initialization
void Start () {
timesHit = 0;
if(this.gameObject.tag == "BrickHit")
{
brickCount++;
}
if(this.gameObject.tag == "BrickHitTwice")
{
brickCount++;
}
}
void OnCollisionEnter2D()
{
timesHit++;
if (timesHit == maxNumberOfHits)
{
brickCount--;
Destroy(this.gameObject);
}
if(brickCount == 0)
{
myLevelManager.LoadLevel("Level1.2"); //THIS SCENE IS NOT LOADING
}
if(this.gameObject.tag == "BrickHit") //If the gameObject (Block One Point) with the tag "BrickHit" is hit
{
Scores.scoreValue += 1;//The user will be rewarded 1 point
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
if(this.gameObject.tag == "BrickHitTwice") //If the gameObject (Block Two Points) with the tag "BrickHitTwice" is hit
{
Scores.scoreValue += 2; //The user will be rewarded 2 points
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
}
LevelManager Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelManager : MonoBehaviour {
public void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
I suspect that your bug may lie in the fact that you're Destroy()ing the gameObject before it can load the next scene; you get a race condition on what will finish first; LoadScene or Destroy() - which would explain why it sometimes work. You should never assume it is a bug in the framework before understanding your problem.
Try putting the Destroy() after the LoadScene() or with a delay to understand if this is your issue.
Also, your LevelManager can be made static and doesn't need to inherit from MonoBehaviour since it doesn't use gameObject functionality.
public static class LevelManager {
public static void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
Used by doing LevelManager.LoadLevel("MyLevel");, but then you may question what is more effective, doing LevelManager.LoadLevel or SceneManager.LoadLevel, as they will do the exact same thing.
The main issue that you're having is not having a single source to check brickCount instead each individual brick is maintaining its own count. I would recommend moving the brick counting logic into a separate class. It would seem like LevelManager would a good place for it. So in LevelManager add:
private int brickCount = 0;
public void AddBrick()
{
brickCount++;
}
public void RemoveBrick()
{
brickCount--;
// Check if all bricks are destroyed
if (brickCount == 0)
{
LoadLevel("Level1.2");
}
}
And then in your brick script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bricks : MonoBehaviour {
public LevelManager myLevelManager;
public int maxNumberOfHits = 0;
int timesHit;
public AudioClip BlockBreaking;
// Use this for initialization
void Start () {
timesHit = 0;
myLevelManager.AddBrick();
// I'm not sure why you were checking the tag here, since the result was the same
}
void OnCollisionEnter2D()
{
timesHit++;
if (timesHit == maxNumberOfHits)
{
myLevelManager.RemoveBrick();
Destroy(this.gameObject);
}
/* This looks like the player is getting score whether the brick is destroyed or not. Also, it would appear the player won't get scored on the final brick */
if(this.gameObject.tag == "BrickHit") //If the gameObject (Block One Point) with the tag "BrickHit" is hit
{
Scores.scoreValue += 1;//The user will be rewarded 1 point
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
if(this.gameObject.tag == "BrickHitTwice") //If the gameObject (Block Two Points) with the tag "BrickHitTwice" is hit
{
Scores.scoreValue += 2; //The user will be rewarded 2 points
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
}
}

How can I position random objects on x,y,z using object pool?

I have two scripts each one attached to another empty GameObject:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooler : MonoBehaviour
{
[System.Serializable]
public class Pool
{
public string tag;
public GameObject prefab;
public int size;
}
#region Singleton
public static ObjectPooler Instance;
private void Awake()
{
Instance = this;
}
#endregion
public List<Pool> pools;
public Dictionary<string, Queue<GameObject>> poolDictionary;
// Start is called before the first frame update
void Start()
{
poolDictionary = new Dictionary<string, Queue<GameObject>>();
foreach(Pool pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for(int i = 0; i < pool.size; i++)
{
GameObject obj = Instantiate(pool.prefab);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
poolDictionary.Add(pool.tag, objectPool);
}
}
public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
{
if(!poolDictionary.ContainsKey(tag))
{
Debug.LogWarning("Pool with tag " + tag + " doesn't exist.");
return null;
}
GameObject objectToSpawn = poolDictionary[tag].Dequeue();
objectToSpawn.SetActive(true);
objectToSpawn.transform.position = position;
objectToSpawn.transform.rotation = rotation;
IPooledObject pooledObj = objectToSpawn.GetComponent<IPooledObject>();
if(pooledObj != null)
{
pooledObj.OnObjectSpawn();
}
poolDictionary[tag].Enqueue(objectToSpawn);
return objectToSpawn;
}
}
And the second one and here I added inside the FixedUpdate the random part:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeSpawner : MonoBehaviour
{
ObjectPooler objectPooler;
// Start is called before the first frame update
void Start()
{
objectPooler = ObjectPooler.Instance;
}
private void FixedUpdate()
{
var randp = new Vector3(Random.Range(0, 300), Random.Range(0, 300), Random.Range(0, 300));
objectPooler.SpawnFromPool("Cube", randp, Quaternion.identity);
}
}
It does what I want so far, But did I add the random part in the right script and place?
And how can I make that instead of generating nonstop random objects it will generate them only when I will change a Range slider of the size variable?
For example [Range(1,150])
And when I change the value it will add/remove the objects in the FixedUpdate? (It should be Update it's FixedUpdate since before that I used a Rigidbody but not now).
The idea is to either to change the size or if I set the size for example to 1000 and then to use a Range slider to change the number of using objects for example 445 or 500 or 1000.
And each time I change the size it will random the objects in other random positions. But once and not all the time like now in the FixedUpdate.
Each time changing the size change the objects positions randomly.
So if the size I changed it to 10 change the position of 10 objects randomly and use only these 10 objects. If I change the size to 700 then reposition randomly 700 objects and use the 700. (Not sure if it's right to say use or destroy).
UPDATE:
This is what I tried in the the first script I added the oldSize variable and the Range:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooler : MonoBehaviour
{
[System.Serializable]
public class Pool
{
public string tag;
public GameObject prefab;
[Range(1, 150)]
public int size;
public int sizeOld;
}
#region Singleton
public static ObjectPooler Instance;
private void Awake()
{
Instance = this;
}
#endregion
public List<Pool> pools;
public Dictionary<string, Queue<GameObject>> poolDictionary;
// Start is called before the first frame update
void Start()
{
poolDictionary = new Dictionary<string, Queue<GameObject>>();
foreach(Pool pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for(int i = 0; i < pool.size; i++)
{
GameObject obj = Instantiate(pool.prefab);
obj.SetActive(false);
objectPool.Enqueue(obj);
}
poolDictionary.Add(pool.tag, objectPool);
}
}
private void Update()
{
}
public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
{
if(!poolDictionary.ContainsKey(tag))
{
Debug.LogWarning("Pool with tag " + tag + " doesn't exist.");
return null;
}
GameObject objectToSpawn = poolDictionary[tag].Dequeue();
objectToSpawn.SetActive(true);
objectToSpawn.transform.position = position;
objectToSpawn.transform.rotation = rotation;
IPooledObject pooledObj = objectToSpawn.GetComponent<IPooledObject>();
if(pooledObj != null)
{
pooledObj.OnObjectSpawn();
}
poolDictionary[tag].Enqueue(objectToSpawn);
return objectToSpawn;
}
}
And in the second script in the Start I'm using once the whole objects for example I start the game when the Range value is at 27 and then when changing the value I'm updating the oldSize:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeSpawner : MonoBehaviour
{
ObjectPooler objectPooler;
// Start is called before the first frame update
void Start()
{
objectPooler = ObjectPooler.Instance;
foreach (ObjectPooler.Pool pool in objectPooler.pools)
{
var randp = new Vector3(Random.Range(0, 300), Random.Range(0, 300), Random.Range(0, 300));
objectPooler.SpawnFromPool("Cube", /*transform.position*/ randp, Quaternion.identity);
}
}
private void Update()
{
foreach (ObjectPooler.Pool pool in objectPooler.pools)
{
if (pool.size != pool.sizeOld)
{
int diff = pool.size - pool.sizeOld;
pool.sizeOld = pool.size;
// Spawn new diff number of objects if diff is positive
var randp = new Vector3(Random.Range(0, 300), Random.Range(0, 300), Random.Range(0, 300));
objectPooler.SpawnFromPool("Cube", /*transform.position*/ randp, Quaternion.identity);
}
}
//var randp = new Vector3(Random.Range(0, 300), Random.Range(0, 300), Random.Range(0, 300));
//objectPooler.SpawnFromPool("Cube", transform.position /*randp*/, Quaternion.identity);
}
}
But in fact the spawners in the Hierarhcy never changed there is all the time 27. If the Range is lower then 27 it will use less objects but if it's higher then 27 still in the hierarchy there will be only 27 spawners.
The size of spawners never change. And then when moving the Range left right it will fill the spawners use them until the end and that's it it will never change it will use them all but only 27.
Even if Range value is 150 for example still there are 27 spawners in the hierarhcy and in the game it self and not 150.
It does what I want so far, But did I add the random part in the right script and place ?
I don't know, if you ask me is 1 + 1 = 2, I can give you an answer.
And how can I make that instead generating nonstop random objects it will generate them only when I will change a Range slider of the size variable ?
You need another size field:
public class Pool
{
public string tag;
public GameObject prefab;
[Range(1, 150)]
public int size;
private int sizeOld;
}
Then in the Update of FixedUpdate do a simple compare:
void Update()
{
foreach(Pool pool in pools)
{
if(pool.size != pool.sizeOld)
{
int diff = pool.size - pool.sizeOld;
pool.sizeOld = pool.size;
// Spawn new diff number of objects if diff is positive
}
}
}
And when I change the value it will add/remove the objects in the FixedUpdate ?
Depends on where you call spawning method.

Unity3D C# code to teleport

I want my player to teleport to a GameObject location When I get 7 points.
When I pick up my item and my points change to 7 I want my player to teleport to GameObject's location (Cube) Here is the script C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FpsScoreScript : MonoBehaviour
{
public int points;
public Transform Destination;
public void Start()
{
}
public void Update()
{
if (points == 7)
{
//teleport code here
}
}
}
How do it get it to work. I want to be teleported to the object that is linked to the "public Transform Destination;" Thanks for the answers.
Its true that your player will not move because of the check you had put in the update() function. Now this is the code that will make your player teleport.
Theory:
The code has a private bool isTeleported and a private function Teleport(). In the update function we will check if the points are equal to 7 and isTeleported is false and then call Teleport() function. And there we will set isTeleported to true so that the check in the update() function becomes false so the player will not teleport.
public class FpsScoreScript : MonoBehaviour
{
public int points;
public Transform Destination;
bool isTeleported = false;
public void Update()
{
if (points == 7 && !isTeleported)
{
Teleport();
}
}
void Teleport(){
isTeleported = true;
player.transform.position = destination.transform.position;
}
Simply set your current object's position to Destination's position.
gameObject.transform.position = Destination.position;
However if you put this directly in:
if (points == 7)
{
gameObject.transform.position = Destination.position;
}
You are going to get teleported to the cube forever since your points have not changed and Update is called every frame. You will need to have something to prevent that, like resetting points to 0, for example.
You Might Not Be Able to Move The Player Since Your Player Is Stuck In The Ground. Is That The Case? BTW, Here's The Final Fixed Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FpsScoreScript : MonoBehaviour
{
public int points;
public Transform destination;
public bool teleported;
public void Start()
{
}
public void Update()
{
if (points == 7 && !teleported)
{
gameObject.transform.position = destination.position;
teleported = true;
}
}
}

Categories