My problem is that when all the enemies are killed the scene that should be loaded is not loading. I did add the scene to the Build setting (it has an index of 3) but it is still not loading. The script I created is attached to an empty object and not directly to the sprite (is that okay?). Can someone tell me why the scene isn't loading? Thank you.
This image is for to show you the EnemySpawner empty object inspector
EnemySpawner Script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemySpawner : MonoBehaviour {
[SerializeField] GameObject EnemyPreFab;
[SerializeField] int MaxEnemies = 30;
[SerializeField] float EnemySpawnTime = 1.00001f;
[SerializeField] GameObject FirstWaypoint;
int CurrentNumOfEnemies = 0;
public LevelManager myLevelManager;
public int maximumnumberofhits = 0;
int timesEnemyHit;
IEnumerator SpawningEnemies()
{
while(CurrentNumOfEnemies <= MaxEnemies)
{
GameObject Enemy = Instantiate(EnemyPreFab, this.transform.position, Quaternion.identity);
CurrentNumOfEnemies++;
yield return new WaitForSeconds(EnemySpawnTime);
}
}
void Start()
{
StartCoroutine(SpawningEnemies());
timesEnemyHit = 0;
if (this.gameObject.tag == "EnemyHit")
{
CurrentNumOfEnemies++;
}
}
void OnCollisionEnter2D()
{
timesEnemyHit++;
if (timesEnemyHit == maximumnumberofhits)
{
CurrentNumOfEnemies--;
Destroy(this.gameObject);
}
if (CurrentNumOfEnemies == 0)
{
myLevelManager.LoadLevel("NextLevelMenu");
Debug.Log("LevelLoaded");
}
}
}
LevelManger 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);
}
}
EnemyShooting Script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyShooting : MonoBehaviour {
[SerializeField] float EnemyLaserSpeed = 10f;
[SerializeField] float EnemyLaserFireTime;
[SerializeField] GameObject LaserBulletEnemyPreFab;
[SerializeField] int MaxNumberOfHits = 1;
int CurrentNumberOfHits = 0;
Coroutine FireCoroutine;
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag == "PlayerLaser")
{
if(CurrentNumberOfHits < MaxNumberOfHits)
{
CurrentNumberOfHits++;
Destroy(collider.gameObject);
Score.ScoreValue += 2;//The user will be rewarded 1 point
}
}
}
void DestroyEnemy()
{
if(CurrentNumberOfHits >= MaxNumberOfHits)
{
Destroy(gameObject);
}
}
private void Fire()
{
FireCoroutine = StartCoroutine(ShootContinuously());
}
void BecomeVisible()
{
Fire();
}
IEnumerator ShootContinuously()
{
while (true)
{
GameObject LaserBulletEnemy = Instantiate(LaserBulletEnemyPreFab, this.transform.position, Quaternion.identity) as GameObject;
LaserBulletEnemy.GetComponent<Rigidbody2D>().velocity = new Vector2(0, EnemyLaserSpeed);
EnemyLaserFireTime = Random.Range(0.5f, 0.9f);
yield return new WaitForSeconds(EnemyLaserFireTime);
}
}
// Use this for initialization
void Start () {
BecomeVisible();
}
// Update is called once per frame
void Update () {
DestroyEnemy();
}
}
EnemyPathing script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyPathing : MonoBehaviour {
[SerializeField] List<Transform> WayPoints;
[SerializeField] float EnemyMovingSpeed = 5f;
int WayPointIndex = 0;
void EnemyMoving()
{
if (WayPointIndex <= WayPoints.Count - 1)
{
var TargetedPosition = WayPoints[WayPointIndex].transform.position; //The position of where the enemy needs to go
TargetedPosition.z = 0f;
var MoveThisFrame = EnemyMovingSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(this.transform.position, TargetedPosition, MoveThisFrame);
if(transform.position == TargetedPosition)
{
WayPointIndex++;
}
}
else
{
Destroy(gameObject);
}
}
// Use this for initialization
void Start () {
transform.position = WayPoints[WayPointIndex].transform.position;
}
// Update is called once per frame
void Update () {
EnemyMoving();
}
}
Problem
You're checking for collisions on the SPAWNER; when someone hits the Spawner it counts down enemies. But the Spawner doesn't have a collision box in the screenshot so it can never be hit. The Scene changing code can never be called.
So the game, based on the code, looks like this:
Spawn X enemies,
Hit the Spawner X times,
(Removed: Destroy the Spawner,)
Change scene.
I'm guessing this is conceptually incorrect and you actually want to check collisions on the spawned enemies, which will then count up the amount of destroyed enemies, and change the scene when they are all dead.
Solution
Conceptually, what you want is:
Spawn X enemies
Count up variable for every enemy
On Enemy death, count it down
When 0, change scene
So how do we code this?
Well, every enemy needs a reference to the object that holds the count. You can do this in several ways, when I personally do it I usually have just one spawner that is responsible for everyone so I make that one a Singleton, that can be references from anywhere:
EnemySpawner
public class EnemySpawner : MonoBehaviour
{
public static Spawner Instance = null;
int CurrentNumOfEnemies = 0;
// ... etc
void Start()
{
if (Instance == null)
Instance = this;
// Spawn enemies like you do already, CurrentNumOfEnemies++ for every spawned
}
public OnEnemyDeath() {
CurrentNumOfEnemies--;
if (CurrentNumOfEnemies < 1)
{
// You killed everyone, change scene:
LevelManager.LoadLevel("Your Level");
}
}
}
Enemy script (I don't know how your current code looks, but here's a minimal solution based on how I THINK your code looks):
void OnDestroy()
{
// This will run automatically when you run Destroy() on this gameObject
EnemySpawner.Instance.OnEnemyDeath(); // Tell the EnemySpawner that someone died
}
This will only work if you have exactly only ONE spawner. If you have multiple ones you will have to send a reference to the instance of its spawner to every spawned enemy. I can show you how to do ths too, if you wish.
Bonus content
LevelManager doesn't need to be on a GameObject, it can be static instead:
Remove the LevelManager script from any GameObject
Change your LevelManager code to this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public static class LevelManager
{
public static void LoadLevel(string name)
{
Debug.Log("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
Now you can use it from ANYWHERE, without needing to initialize a reference to any script or GameObject:
LevelManager.LoadLevel("My Level");
myLevelManager.LoadLevel("NextLevelMenu"); is never executed, because you destroy the object in the if-test above.
Related
I have 7 game objects or more. I want to run only one gameObject after the other. Starting with Gameobject 0 --> GameObject 1 --> GameObject 2. At the moment my script activates a new object each time Space is pressed, and the old ones are not deactivated.
Thanks for your help!
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ToggleTest : MonoBehaviour
{
public GameObject[] objects;
public int objCount = 0;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
objCount += 1;
objects[objCount].SetActive(true);
}
}
}
If I understand your question correctly, try this:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ToggleTest : MonoBehaviour
{
public GameObject[] objects;
public int objCount = 0;
private float smallDelay = 0.1f;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && smallDelay <= 0)
{
objCount += 1;
objects[objCount].SetActive(true);
objects[objCount-1].SetActive(false); // this is only if you want to deactivate the previous object.
smallDelay = 0.1f;
}
smallDelay -= Time.DeltaTime;
}
}
This is not a perfect code, but because your function's if statement is one the Update() method, I think it needs a little delay. You can also change the smallDelay to a bigger number.
EDIT
This is the answer on your comment:
if (objCount == objects.Length)
{
objCount = 0;
object[objCount].setActive(false);
object[0].setActive(true);
}
add this in the if statement or somewhere else in the Update().
I am new to coding and am not quite sure how to add context from 1 script to another. For my game so far, I have been Frankensteining code from multiple sources and have head into a dead end. I want my player to take the same amount of damage as stated by enemyDamage in the 'Enemy' script. I'm not too sure what other context to give you, but if you know how you could help me out, that would be awesome!
Enemy Script
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public int health = 100;
public int enemyDamage = 10;
public GameObject deathEffect;
public void TakeDamage (int damage)
{
health -= damage;
if (health <= 0)
{
Die();
}
}
void Die ()
{
Instantiate(deathEffect, transform.position, Quaternion.identity);
Destroy(gameObject);
}
}
PlayerHealth Script
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
public class PlayerHealth : MonoBehaviour
{
public int maxHealth = 10;
public int currentHealth;
public int damage = 1;
public HealthBar healthBar;
// Start is called before the first frame update
void Start()
{
currentHealth = maxHealth;
healthBar.SetMaxHealth(maxHealth);
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
TakenDamage(1);
}
if (currentHealth <= 0)
{
PlayerDeath();
}
}
public void TakenDamage(int damage)
{
currentHealth -= damage;
healthBar.SetHealth(currentHealth);
}
void PlayerDeath()
{
UnityEngine.Debug.Log("bean guy");
}
public void OnTriggerEnter2D(Collider2D hitInfo)
{
UnityEngine.Debug.Log("We did it boys");
PlayerHealth player = hitInfo.GetComponent<PlayerHealth>();
{
UnityEngine.Debug.Log("beans");
TakenDamage(enemyDamage); // I need this to update with enemyDamage 's value
}
}
}
There are multiple ways:
Make EnemyDamage a Static Variable
public static int enemyDamage = 10;
Then you can call it in other scripts with Enemy.enemyDamage
Be aware that you can't set static variables in the Inspector.
Use GetComponent
Enemy enemy = gameObject.GetComponent(typeof(Enemy )) as Enemy;
enemy.enemyDamage
Create a GameManager
GameManager.CS:
#region Singelton
public static GameManager instance;
void Awake()
{
if (instance != null)
{
Debug.LogWarning("More than one Instance of Inventory found");
return;
}
instance = this;
}
#endregion
public int enemyDamage = 10;
Referencing the GameManager Script:
GameManager gm;
void Start()
{
gm = GameManager.instance;
}
//Calling Function in GameManager
gm.EndGame();
// Getting Value from GameManager
gm.enemyDamage();
When to use what?
If you want a more short term solution I would use a static variable, not advised with multiple enemies (different enemyDamage Values are now immosible)
If you have more variables or even functions that need to be accessible from multiple scripts I would advise you to use a Game Manager instead
You need to get a reference of the Enemy to use GetComponent, but makes it possible to add multiple different enemyDamage Values
Hey guys I want to make a sword attack. But I get the error of:
NullReferenceException: Object reference not set to an instance of an
object
These are my codes:
My weaponattack script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weaponattack : MonoBehaviour
{
Enemy Enemy;
public float power;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void OnTriggerEnter(Collider col)
{
if(col.tag == "Enemy")
{
var kılıc = GetComponent<Enemy>();
Enemy.currentHealt -= 10;
Enemy.TakeDamage();
Debug.Log("Düşman Vuruldu");
}
}
}
This is my Enemy Health scrpt:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour {
public float currentHealt;
public float maxHealth;
// Use this for initialization
void Start () {
currentHealt = maxHealth;
}
// Update is called once per frame
void Update () {
}
public void TakeDamage()
{
currentHealt -= 10;
if(currentHealt < 0)
{
die();
}
}
void die()
{
Destroy(gameObject);
}
}
Can you helo em I couldnt understan why these codes are not working why am I getting that error.
I think your problem is in this line here GetComponent<Enemy>(); This needs a game object to get the component of
Enemy = col.GetComponent<Enemy>();
Should be what you're doing
You also may have issue calling your variable Enemy exactly the same as your class Enemy, I would change this to Enemy enemy; and
enemy = col.GetComponent<Enemy>();
respectivley
It seems pretty straightforward. You don't assign an Enemy to Weaponattack and so it's barfing when you first reference Enemy (Enemy.currentHealt). Either make Enemy public or a SerializedField so you can assign it in the Inspector, or however else you want to get an Enemy into Weaponattack.
Also don't name your property the same name as your script (Enemy Enemy). Surprised you didn't get a compile error.
EDIT Based on comments:
Enemy enemy;
public void OnTriggerEnter(Collider col)
{
if(col.tag == "Enemy")
{
var kılıc = GetComponent<Enemy>();
kilic.currentHealt -= 10;
kilic.TakeDamage();
Debug.Log("Düşman Vuruldu");
}
}
This also assumes you have an Enemy attached to your Weaponattack (GetComponent)
So, i created two scripts, one named "Stats.cs" registers the player stats and the other one named "PlayerHealth.cs" "makes" the player take damage on contact and updates the Hearts in the HUD. My problem is, whenever i collide with an object that has a tag named "Projectile" it simply doesn't work, my player doesn't take damage at all. The Stats.cs script isn't in any object, the PlayerHealth.cs is in my Player object.
Stats.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Stats{
private int health;
public int maxHP = 3;
public int Health
{
get
{
//Some code
return health;
}
set
{
//Some code
health = Mathf.Clamp(value, 0, maxHP);
}
}
public void SetHealth()
{
Health = maxHP;
}
}
PlayerHealth.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
Stats playerStats = new Stats();
public int curHealth;
public int numOfHearts = 3;
public Image[] hearts;
public Sprite fullHeart;
public Sprite emptyHeart;
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.CompareTag("Projectile"))
{
Debug.Log("Hello");
DamagePlayer(1);
Destroy(other.gameObject);
}
}
public void DamagePlayer(int damage)
{
playerStats.Health -= damage;
}
// Start is called before the first frame update
void Start()
{
playerStats.SetHealth();
curHealth = numOfHearts;
}
// Update is called once per frame
void Update()
{
curHealth = playerStats.Health;
numOfHearts = playerStats.maxHP;
if (curHealth>numOfHearts){
curHealth = numOfHearts;
}
if(curHealth <= 0){
Die();
}
for (int i = 0; i < hearts.Length; i++)
{
if(i < curHealth){
hearts[i].sprite = fullHeart;
} else
{
hearts[i].sprite = emptyHeart;
}
if(i < numOfHearts){
hearts[i].enabled = true;
} else {
hearts[i].enabled = false;
}
}
}
void Die(){
//Restart
Application.LoadLevel(Application.loadedLevel);
}
}
curHealth is updating so it will stay as the actual Health in Stats and will change the images in HUD.
The Player has a RigidBody2D on him two colliders, one is a box for the body, and the other is a circle collider, so when the player crouches, the circle collider disables.
The Projectiles also have and RigidBody2D with 0 gravity (so it won't fall in mid air) and a BoxCollider2D.
I would check and make sure that the projectile is tagged as Projectile and that the BoxCollider doesn't have "Is Trigger" checked.
I should also say, iterating with that for loop in the Update is very bad practice performance wise. That is happening literally as fast as the machine can loop it and it is doing that every time. I would look into updating it on an event.
Hope this helps!
I am creating a laser defender game in unity and I have this problem where when I shoot the first enemy it takes me directly to the NextLevelMenu scene but I want it to load when all the enemies are killed(on this level I have 5 enemies to kill). I have been told that I need to send a reference to the instance of its spawner to every spawned enemy but I did not quite understand. Can someone help, please?
EnemySpawner Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemySpawner : MonoBehaviour {
[SerializeField] GameObject EnemyPreFab;
[SerializeField] int MaxEnemies = 30;
[SerializeField] float EnemySpawnTime = 1.00001f;
[SerializeField] GameObject FirstWaypoint;
int CurrentNumOfEnemies = 0;
public LevelManager myLevelManager;
public int maximumnumberofhits = 0;
public static EnemySpawner Instance = null;
int timesEnemyHit;
IEnumerator SpawningEnemies()
{
while(CurrentNumOfEnemies <= MaxEnemies)
{
GameObject Enemy = Instantiate(EnemyPreFab, this.transform.position, Quaternion.identity);
CurrentNumOfEnemies++;
yield return new WaitForSeconds(EnemySpawnTime);
}
}
void Start()
{
if (Instance == null)
Instance = this;
StartCoroutine(SpawningEnemies());
timesEnemyHit = 0;
if (this.gameObject.tag == "EnemyHit")
{
CurrentNumOfEnemies++;
}
}
public void OnEnemyDeath()
{
CurrentNumOfEnemies--;
if (CurrentNumOfEnemies < 1)
{
// You killed everyone, change scene:
LaserLevelManager.LoadLevel("NextLevelMenu");
}
}
}
EnemyShooting script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyShooting : MonoBehaviour {
[SerializeField] float EnemyLaserSpeed = 10f;
[SerializeField] float EnemyLaserFireTime;
[SerializeField] GameObject LaserBulletEnemyPreFab;
[SerializeField] int MaxNumberOfHits = 1;
int CurrentNumberOfHits = 0;
Coroutine FireCoroutine;
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag == "PlayerLaser")
{
if(CurrentNumberOfHits < MaxNumberOfHits)
{
CurrentNumberOfHits++;
Destroy(collider.gameObject);
Score.ScoreValue += 2;//The user will be rewarded 1 point
}
}
}
void DestroyEnemy()
{
if(CurrentNumberOfHits >= MaxNumberOfHits)
{
Destroy(gameObject);
EnemySpawner.Instance.OnEnemyDeath(); // Tell the EnemySpawner that someone died
}
}
private void Fire()
{
FireCoroutine = StartCoroutine(ShootContinuously());
}
void BecomeVisible()
{
Fire();
}
IEnumerator ShootContinuously()
{
while (true)
{
GameObject LaserBulletEnemy = Instantiate(LaserBulletEnemyPreFab, this.transform.position, Quaternion.identity) as GameObject;
LaserBulletEnemy.GetComponent<Rigidbody2D>().velocity = new Vector2(0, EnemyLaserSpeed);
EnemyLaserFireTime = Random.Range(0.5f, 0.9f);
yield return new WaitForSeconds(EnemyLaserFireTime);
}
}
// Use this for initialization
void Start () {
BecomeVisible();
}
// Update is called once per frame
void Update () {
DestroyEnemy();
}
}
I would add a two fields to the spawner script. EnemiesToNextLevel and KilledEnemies. Then in the OnEnemyDeath() of your spawner, you may increase KilledEnemies everytime it is called, and then ask if KilledEnemies >= EnemiesToNextLevel, before changing the scene.
Sure there are a lot of other ways to do it, but for me thats the easiest.