How to instantiate prefab after specified prefab have been instantiated? - c#

I have Enemy.cs to stochastically instantiate enemies.
Then I want to get a hero.cs to instantiate heroes.
The requirement is to instantiate the hero following the corresponding enemy.
For example, find enemy_01 in scene, then instantiate hero_01.
Find enemy_02 in scene, then instantiate hero_02.
Without enemy_03, hero_03 should not be instantiated.
Even harder, I want to choose which hero to be instantiated.
For example, with enemy_01 in scene, 80 % chance to instantiate hero_01, 20 % chance to instantiate hero_02.
With enemy_02 in scene, 50 % chance to instantiate hero_02, 50 % chance to instantiate hero_03.
But without enemy_03, hero_03 can not be instantiated.
Could you give me an idea or thinking?
My Hero.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Hero_list : MonoBehaviour
{
[System.Serializable]
public class Hero
{
public GameObject Hero_prefab;
public GameObject Need_prefab;
}
public List <Hero> Heroes = new List <Hero> ();
public static Hero_list instance;
public void findEnemy()
{
GameObject[] respawns = GameObject.FindGameObjectsWithTag("Enemy");
foreach (GameObject respawn in respawns)
{
Debug.Log(respawn);
}
}
void Update ()
{
findEnemy();
}
}

One possibility is to let the enemy call findEnemy() of Hero.cs when an it is spawned, either in its Start() or OnAwake() method. Then spawn the hero.
Since these are just normal functions, you can execute whatever logic you want in them, thus being able to chain instantiation of game objects.

Related

How to call a script component from a GameObject that is child to another GameObject, but from other GameObject?

In my unity project, I have a GameObject called "Spawn_manager", that has another Child GameObject called "Enemy_Container". The script of Spawn_Manager makes "Enemy" prefabs instantiate inside the "Enemy_Container". I am trying to acess the "Enemy" script(also named Enemy) from another script.
Anyone knows how I can get it?
I tried writing this:
private Enemy _enemy;
private void Start()
{
_enemy = GameObject.Find("Enemy").GetComponent<Enemy>();
}
But obviously it is not working, but I can't get it to work. Basically, I wanna the script, and the path would be Spawn_Manager>Enemy_Container>Enemy.
If you want to make the Enemy easily accessible you can add all spawned Enemy to static list. Like this:
public class Enemy : MonoBehaviour
{
public static List<Enemy> AllEnemy { get; private set; } = new List<Enemy>();
private void Awake()
{
AllEnemy.Add(this);
}
private void OnDestroy()
{
AllEnemy.Remove(this);
}
}
Using Find is demanding and not a reliable operation. If you will have any other GameObjects with name Enemy it will found them instead of desired one.
in this case i think that problem is that after instantiation prefab new GameObject have name in format $"{prefab.name}(Clone)".

unity wont let me attatch object to variable

Unity wont let me drag and drop a game object into the variable
heres my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public class GameManager : Singleton<GameManager>
{
[SerializeField] private Player currentPlayer;
public Player CurrentPlayer
{
get { return currentPlayer; }
}
private void Awake()
{
Assert.IsNotNull(currentPlayer);
}
}
Your public class GameManager : Singleton<GameManager> should be a monobehaviour.
Monobehaviours are the type to inherit from when you to add functionality to a gameObject. You cannot drag whatever to a script that just hold a [SerializeField].
You need to have a Monobehaviour inherited class as a component of a gameobject (meaning that it should be attached to it). There is where you can drag the componenets into the public or the private [SerializeField] fields shown in the editor when you select the correspoding gameobject.
Your script must inherit from Monobehavior for you to be able to do that. And since Singleton is a generic type, that wont work.

The local function 'OnTriggerEnter2D' is declared but never used

I don't know how to fix it. It's a 2D game. When you collide with the box, it should load/teleport you to the next scene, but it doesn't.
I tried everything on the internet and it didn't work.
using UnityEngine;
using UnityEngine.SceneManagement;
public class VictoryZone : MonoBehaviour
{
public void LoadNextLevel()
{
void OnTriggerEnter2D(Collider2D collider)
{
SceneManager.LoadScene(1);
}
}
}
I expect it to teleport me to my next level.
It looks like you nested the OnTriggerEnter2D() function inside of LoadNextLevel()? That's what the Local Function warning is referring to.
Those should be two separate functions, not one within the other. OnTriggerEnter2D() is a function of MonoBehavior; the MonoBehavior (VictoryZone) is what gets notified of the collision.
using UnityEngine.SceneManagement;
public class VictoryZone : MonoBehaviour {
public void LoadNextLevel() {
SceneManager.LoadScene(1);
}
void OnTriggerEnter2D(Collider2D collider) {
LoadNextLevel();
}
}
Note: you might also need to check the GameObject associated with collider to make sure that it's a player, and not an enemy or something (if, hypothetically, you had enemies or projectiles or other objects with colliders moving into the victory zone).

Inspector value cannot access from another class in Unity3d

I have two classes. One called GameManager and another one Enemies.
I have two variables in GameManager which I have changed from inspector currentLevel=1 and totalEnemy=10.
// GameManager.cs
private static GameManager instance = new GameManager();
public static GameManager get(){ return instance; }
public int currentLevel;
public int curLevel { get; set; }
public int totalEnemy;
public int totLevel { get; set; }
void Start () {
curLevel = currentLevel;
totLevel = totalEnemy;
}
I'm trying to access these two variable from Eneimes class like this; but everytime it gives me curLevel = 0, but I'm expecting to get curLevel = 1. What I'm doing wrong?
// Enemies.cs
void Start () {
Debug.Log (GameManager.get().curLevel); // always output = 0
}
The line private static GameManager instance = new GameManager(); is the issue.
When a script is attached to a GameObject, an instance of the type of the script is referenced as this inside the script. In other words, there can be multiple instances of same type if the same script is attached to multiple GameObjects.
Therefore, the specific instance that have curLevel = 1 as you set in the Inspector is an instance of the type attached to the specific GameObject. This means the one should be referred to as this inside the script.
If you declare a new instance of GameManager as in your code, you are basically ignoring all values in the Inspector because the static GameManager instance is pointing to a different instance than the instance you set values for in the Inspector.
In order to use the specific instance that you declared using the Inspector, you should do the following.
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
public class GameManager : MonoBehaviour
{
private static GameManager instance;
public static GameManager get() { return instance; }
public int currentLevel;
public int curLevel { get; set; }
public int totalEnemy;
public int totLevel { get; set; }
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Debug.LogError(string.Format("GameManager.Awake(): More than one instances of this type {0} is being initialised but it's meant to be Singleton and should not be initialised twice. It is currently being initialised under the GameObject {1}.", this.GetType(), this.gameObject.name));
Destroy(gameObject);
}
curLevel = currentLevel;
totLevel = totalEnemy;
}
}
Note that I changed Start() to Awake(). This is because you are referring to values initiliased in this method from other scripts, and you cannot guarantee which Start() is called first between different MonoBehaviours in the runtime. However, Unity guarantees that Awake() is always called earlier than Start(). Further, it is Unity's best practice to initialise self-initialisable variables in Awake(), and initialise variables dependent on other scripts in Start() because of this execution order.
Lastly, there will be problems when there are multiple GameObject that has GameManager as its component in your scene. Consider a case where you have two such objects. when the scene is loaded, each of the script will call Awake(), and both of them will set private static GameManager instance; to each of the two this. The result would be one is overriden by another.
You could say that you will be careful to use this script and make sure only one GameObject has this script as its component. However, you should always write your code as if someone who do not know about your code can use it without thinking, and stupid mistakes of other people new to the project could be easily detected.
EDIT:
To respond to the OP's comment, I added code to handle when this type is initialised more than once in the project. In addition to #Kardux's suggestion, I added Debug.LogError() because I do not want the project to silently solve things. If a problem happens, I want to get notified of it.
If you are using Singletons frequently in your project, you might want to have a parent abstract class Singleton that handles this instance checking process for all child Singletons, and have GameManager inherit from Singleton.
However, use Singleton with care as it is considered a bad design pattern if misused. (And I don't know how to use it properly so I avoid using it.)

Instantiating prefabs from another script (The prefab you want to instantiate is null)

So I have an enemy spawner with a method to instantiate prefabs working just fine. Simplified:
public class EnemySpawner : MonoBehaviour {
public GameObject EnemyPrefab;
public void setEnemies()
{
Instantiate (EnemyPrefab, enemyPos, rotation);
}
void Start()
{
setEnemies();
}
}
This works fine. But it doesnt work when I call it from a different script:
public class Player : MonoBehaviour {
public EnemySpawner enemyspawner;
void Update(){
if (Input.GetMouseButtonDown (0))
{
enemyspawner= new EnemySpawner();
enemyspawner.setEnemies();
}
}
I keep getting this error:
ArgumentException: The prefab you want to instantiate is null.
What am I doing wrong?
Edit:
So I figured that I could not create a Monobeaviour by using the New keyword .I changed it to:
enemyspawner = gameObject.AddComponent<EnemySpawner> ();
enemyspawner.setEnemies();
But that still wont work.
I just read your comment about changing:
new to enemyspawner = gameObject.AddComponent<EnemySpawner>();
However, you did not properly solve the problem, you only made a workaround that won't work because your prefab, public GameObject EnemyPrefab;, won't be set.
With your new code gameObject.AddComponent<EnemySpawner>(); You are attaching a script to the Player GameObject, this will bring you problems in the future.
My Suggestion
Create an Empty GameObject and attach EnemySpawner to it and name it EnemySpawnerObj.
Then to call the function setEnemies() from another class you do this:
EnemySpawner spwner = GameObject.Find("EnemySpawnerObject").GetComponent<EnemySpawner>();
spwner.setEnemies();
So what you are doing is looking through your scene for the GameObject named EnemySpawnerObject then you get the component named EnemySpawner and then you can call that class instance.

Categories

Resources