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);
}
}
}
Related
I've recently started coding on Unity, trying to make a game. So long it's been fine, but I faced a problem.
I've implemented a script for the Attack System:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AttackDamage : MonoBehaviour
{
private float attackDamage = 20;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.GetComponent<Health>() != null)
{
Health health = other.GetComponent<Health>();
health.TakeDamage(attackDamage);
}
}
}
And I also implemented one for the Health System:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Health : MonoBehaviour
{
public Image healthBar;
public float healthAmount = 100;
private void Update()
{
if (healthAmount <= 0)
{
SceneManager.LoadScene(0);
}
}
public void TakeDamage(float Damage)
{
healthAmount -= Damage;
healthBar.fillAmount = healthAmount / 100;
}
public void Healing(float healPoints)
{
healthAmount += healPoints;
healthAmount = Mathf.Clamp(healthAmount, 0, 100);
healthBar.fillAmount = healthAmount / 100;
}
}
And it works prety well.
But as you read on the title, the attack only actually works right after I move. If I try to attack while I'm not moving, the attackArea appears on the scene, but doesn't deal damage. And i can't figure out why.
Do you have any idea on what could be the problem?
Here there's also a video of what actually happens, in the game:
https://drive.google.com/drive/folders/1BTYTNz_yzus-eRLnjsgB0hsYLU5sQm2k?usp=sharing
I have no idea on how to solve this problem, since I've copied the code from a source online, which actually works prorperly.
The code is exactly the same, apart form the script for the Health System, which is not shown on the video, but which also shouldn't make that much of a difference.
So i really don't know how to handle this.
Thanks for the help :)
If you are looking to deal continuous damage while any health-character is in the trigger, use OnTriggerStay2d instead.
Otherwise, if you are looking to deal damage when the user presses a button, you can do the following:
Have a hit-box collider that stores all enemy in range. (By adding enemy to a list when it enters the hit-box, and removing them from list when they exit.)
When the attack is triggered, fetch all enemy in the list from 1. and deal damage to all.
Code-wise, looks something like this:
public class AttackDamage : MonoBehaviour
{
private HashSet<Health> inRange = new HashSet<Health>();
private float attackDamage = 20;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.GetComponent<Health>() != null)
{
// Add to set of characters that are in range of attack
inRange.Add(other.GetComponent<Health>());
}
}
private void OnTriggerExit2D(Collider2D other)
{
var charHealth = other.GetComponent<Health>();
if (charHealth != null) {
// Remove, since it exit the range of the attacking character.
inRange.Remove(charHealth);
}
}
// Call this function whenever you want to do damage to all characters in range.
public void Attack(){
foreach(var character in inRange){
character.TakeDamage(attackDamage);
}
}
}
Then somewhere else... (example, in your player.)
public class YourPlayer {
// ...
// Inspector reference to the hitbox
[SerializeField]
private AttackDamage attackHitbox;
private void Update(){
// Attack when button is pressed (or something).
if (Input.GetKeyDown(KeyCode.A)) {
attackHitbox.Attack();
}
}
// ...
}
Finally, if you are looking to deal damage at specific points in an animation, the term you should search for is Key-Frame. Look for a tutorial on Animation, then Key-Frame.
Once you learn about it, you can use the above mentioned method, but call the damage script on your desired key-frames.
This is my first time making a 2d game. I'm trying to load a scene if the amount of objects with tag "Enemy" is zero. Line 22 doesn't seem to work. I don't get any errors.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelLoader : MonoBehaviour
{
public Animator transition;
public float transitionTime;
public GameObject enemyPrefab;
public GameObject[] enemy;
// Update is called once per frame
void Update()
{
if (enemy == null)
{
enemy = GameObject.FindGameObjectsWithTag("Enemy");
Debug.Log("null!");
LoadNextLevel();
}
}
public void LoadNextLevel()
{
StartCoroutine(LoadLevel(SceneManager.GetActiveScene().buildIndex + 1));
}
IEnumerator LoadLevel(int levelIndex)
{
transition.SetTrigger("Start");
yield return new WaitForSeconds(transitionTime);
SceneManager.LoadScene(levelIndex);
}
}
enemy = GameObject.FindGameObjectsWithTag("Enemy");
Initially enemy is null.
So, in the first frame (the first time Update() is called) it satisfies the if condition and then assign something to enemy.
Then, in the second and later frames, enemy is not null anymore, so it does not satisfies if (enemy == null) condition and the lines in the if statement does not be executed. It means that, the lines enemy = GameObject.FindGameObjectsWithTag("Enemy") and LoadNextLevel(); will never be executed after the first frame.
My suggestion is that rewrite the Update() just like this:
void Update() {
enemy = GameObject.FindGameObjectsWithTag("Enemy"); // get "Enemies" first
// then do something by using it
if (enemy == null) {
Debug.Log("null!");
LoadNextLevel();
}
}
So, I solved the question. The problem was that the condition was before assigning the variable.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelLoader : MonoBehaviour
{
public Animator transition;
public float transitionTime;
// Update is called once per frame
void Update()
{
GameObject[] enemy = GameObject.FindGameObjectsWithTag("Enemy");
if (enemy == null || enemy.Length == 0)
{
LoadNextLevel();
}
//Debug.Log(GameObject.FindGameObjectsWithTag("Enemy"));
}
public void LoadNextLevel()
{
StartCoroutine(LoadLevel(SceneManager.GetActiveScene().buildIndex + 1));
}
IEnumerator LoadLevel(int levelIndex)
{
transition.SetTrigger("Start");
yield return new WaitForSeconds(transitionTime);
SceneManager.LoadScene(levelIndex);
}
}
I believe that it's because the variable enemy is of type array and not a list. Arrays are more difficult to deal with and are of a fixed size.
Instead, consider using a List:
List<GameObject> enemy = new List<GameObject>();
Here are some additional resources to assist you with understanding the difference between arrays and lists.
https://www.w3schools.com/cs/cs_arrays.php
https://www.c-sharpcorner.com/article/c-sharp-list/
The difference/when to use each
Array versus List<T>: When to use which?
https://csharp-station.com/c-arrays-vs-lists/
https://www.educba.com/c-sharp-array-vs-list/
Right now the game shows ads each and every time the player dies. I want the ads to appear after the third death of the player. Please guide me as I am a beginner with no real knowledge of coding. Please also specify if I need to attach the script to a specific game object.
I have put the ads code below. Thanks.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public GameObject gameOverCanvas;
public GameObject Interstitial;
public int DeathCount = 0;
private void Start()
{
Time.timeScale = 1;
}
public void GameOver()
{
gameOverCanvas.SetActive(true);
Time.timeScale = 0;
DeathCount++;
}
private bool DeathCountCheck()
{
if (DeathCount == 3)
{
// Reset the variable, otherwise it will increase indefinitely and the condition will not be met in the future
Interstitial.GetComponent<InterstitialAds>().ShowInterstitial();
DeathCount = 0;
return true;
}
return false;
}
public void Replay()
{
SceneManager.LoadScene(0);
}
if "deathCount" increases each time the player dies, then when the ad is meant to show you just check that variable and reset it appropriately.
// Function that checks if there were 3 deaths
private bool DeathCountCheck()
{
if (deathCount == 3)
{
// Reset the variable, otherwise it will increase indefinitely and the condition will not be met in the future
deathCount = 0;
return true;
}
return false;
}
Then in the "ShowInterstitialAd" just add another condition
public void ShowInterstitial()
{
if (Advertisement.IsReady(interstitialID) && DeathCountCheck())
{
Advertisement.Show(interstitialID);
}
}
I can't get my respawning in my game to work, I have followed numerous tutorials, but have been failing to change one small aspect of them. I have a health variable in my game, and all the tutorials have the player die and respawn right upon touching the enemy. I want it to be so you take damage when touching an enemy, and when your health reaches 0 you are brought to a game over scene. I just can't seem to figure it out. I am new to game dev so I have tried my absolute hardest to solve the problem on my own, but to no avail. This is pretty much my last resort. I appreciate any help I can get.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PlayerHealth : MonoBehaviour
{
public static PlayerHealth instance;
public int maxHealth;
public int health;
public int GameOver;
public event Action DamageTaken;
public int Health
{
get
{
return health;
}
}
// Start is called before the first frame update
void Awake()
{
if(instance == null)
{
instance = this;
}
}
private void Start()
{
health = maxHealth;
}
// Update is called once per frame
public void TakeDamage()
{
if(health <= 0)
{
return;
}
health -= 1;
if(DamageTaken != null)
{
DamageTaken();
}
}
public void heal()
{
if (health >= maxHealth)
{
return;
}
health += 1;
if (DamageTaken != null)
{
DamageTaken();
}
}
void OnCollisionEnter(Collision other)
{
if(other.gameObject.tag == "Enemy")
{
TakeDamage();
}
if(health <= 0)
{
SceneManager.LoadScene(GameOver);
}
}
}
I don't see anything wrong with your code specifically. It could be a different issue in the editor, which is usually the case. To debug try one of the following:
Check that the OnCollisionEnter is actually being triggered
Check that the Enemey has the Enemy Tag.
You can check 1 & 2 with the following code:
void OnCollisionEnter(Collision other)
{
// Check what the tag is
print(other.gameObject.tag);
...
}
There should now be messages when you run the program. Other things to check:
Make sure the player object has the PlayerHealth script attached to it.
Make sure that the player has a RigidBody
Make sure the enemy has a collider
Make sure the enemy collider is not set to Trigger
I'm pretty sure that
animation.Play("DoorOpen");
Would play the animation "DoorOpen", but when i'm trying to put it in my code, it just giving me an error message:
The Animation attached to this GameObject (null if there is none attached).
using UnityEngine;
using System.Collections;
public class DoorPhysics : MonoBehaviour {
int Open = 0;
// Update is called once per frame
void Update() {
if (Open == 0) {
if (Input.GetKeyDown("e")) {
animation.Play("DoorOpen");
}
}
}
}
You need to show location of gameobjects in unity, they do not know eachother, you have to always use :
GameObject.GetComponent<T>()
GetComponentInParent<T>()
GetComponentInChildren<T>()
best practice is to get object references at Start()
also you should attach IMPORTANT!!! Animation component to the object this script it attached to
public class DoorPhysics : MonoBehaviour {
public Animation animation;
int Open = 0;
void Start()
{
animation=GameObject.GetComponent<Animation>(); //if your have derived type change Animation to good class DoorAnimation for example
}
void Update()
{
if (Open == 0) {
if (Input.GetKeyDown("e")) {
this.animation.Play("DoorOpen");
}
}
}
}
if that code wont work, you will need show me your GameObject hierarchy
And if your just start your trip with it learn MonoBehaviour call order
and life cycles of events