I want to make a kill counter but for some reason it's not working.
Here is what i did
I create a new empty GameObject Game Manager And add a new component Score, Here is the code:
using UnityEngine;
using UnityEngine.UI;
public class Score : MonoBehaviour
{
public Text kills_UI;
private int Kills_Counts; //How many kills
public void Increase_score() //This will update the UI text to the current kills count
{
Kills_Counts++;
kills_UI.text = Kills_Counts.ToString();
}
}
After that i called this function in the enemy script BulletColision after he is killed :
using UnityEngine;
public class BulletColision : MonoBehaviour
{
Score kills_score;
void Start()
{
kills_score = GetComponent<Score>();
}
public void OnCollisionEnter2D(Collision2D others) //When a bullet collide with en enemy prefab
{
if (others.gameObject.CompareTag("Enemy"))
{
Destroy(gameObject); //Destroy the enemy
kills_score.Increase_score(); //Calling the function from 'GameManager'
Destroy(others.gameObject); //Destroy the bullet
}
}
}
The problem is with your kills_score reference, if you do:
kills_score = GetComponent<Score>();
You are searching for Score component on your BulletCollision, which I guess do not have Score component since it's a bullet.
Quick fix:
Attach to your GameManager a new TAG like "GameManager", then use
kills_score = GameObject.FindWithTag("GameManager").GetComponent<Score>();
instead of
kills_score = GetComponent<Score>();
To quickly validate this make your score variable public and see in your editor if the reference is correctly set.
Also and as a side note, try to maintain your variables with lowerCamelCame nomenclature, meaning that starts with lower case.
Related
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CountScoreAtWall : MonoBehaviour
{
//Varriables
public GameObject[] prefabs;
public Text animalPassedText;
public int animalPassed;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
animalPassedText.text = animalPassed.ToString();
}
void OnTriggerEnter(Collider collision)
{
if (collision.gameObject.tag == tag)
{
animalPassed = animalPassed + 1;
Debug.Log("Animal Passed showed");
}
}
I want to let my clone Prefabs from Instantiate() touch the wall and then the score will Increase by 1 to animalPassedText but I don't know how to do that.
So please help me. ToT
Possible solutions:
1- Either the wall or your prefab should have a rigidbody to make OnTriggerEnter work. So check if one of your game objects has a rigidbody and they both have colliders.
2- Maybe your "tag" variable is not matching with collision.gameObject.tag, so if block is not triggered. Print collision.gameObject.tag and tag to see if they are matching.
I can't get the Player Gameobject to reappear. The player deactivates the moment the Timeline starts when they touch the box collider, but the player never reactivates. I have tried using Player.SetActive(true) in the coroutine and even using an Invoke method with no luck. Any ideas on how to fix this? Please help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
public class TimelineTrigger : MonoBehaviour
{
// calling items for Unity
public PlayableDirector timeline;
public GameObject Player;
public GameObject CutsceneCollider;
public GameObject CutsceneMC;
// Start is called before the first frame update
void Start()
{
// calls the playable director and turns off the MC for the scene
timeline = timeline.GetComponent<PlayableDirector>();
CutsceneMC.SetActive(false);
}
void Update()
{
Player = GameObject.FindGameObjectWithTag("Player");
}
private void EnableAfterTimeline()
{
Player = GameObject.FindGameObjectWithTag("Player");
Player.SetActive(true);
}
public void OnTriggerEnter2D (Collider2D col)
{
if (col.CompareTag("Player"))
{
// plays the cutscene and starts the timer
timeline.Play();
Player.SetActive(false);
Invoke("EnableAfterTimeline", 18);
CutsceneMC.SetActive(true);
StartCoroutine(FinishCut());
}
IEnumerator FinishCut()
{
// once the cutscene is over using the duration, turns off the collider and the MC.
yield return new WaitForSeconds(17);
CutsceneMC.SetActive(false);
CutsceneCollider.SetActive(false);
}
}
}
The issue here is that coroutines in Unity can't be run on inactive GameObjects, so FinishCut never gets executed.
This can be worked around by having a separate MonoBehaviour in the scene to which the responsibility of running a coroutine can be off-loaded. This even makes it possible to start static coroutines from static methods.
using System.Collections;
using UnityEngine;
[AddComponentMenu("")] // Hide in the Add Component menu to avoid cluttering it
public class CoroutineHandler : MonoBehaviour
{
private static MonoBehaviour monoBehaviour;
private static MonoBehaviour MonoBehaviour
{
get
{
var gameObject = new GameObject("CoroutineHandler");
gameObject.hideFlags = HideFlags.HideAndDontSave; // hide in the hierarchy
DontDestroyOnLoad(gameObject); // have the object persist from one scene to the next
monoBehaviour = gameObject.AddComponent<CoroutineHandler>();
return monoBehaviour;
}
}
public static new Coroutine StartCoroutine(IEnumerator coroutine)
{
return MonoBehaviour.StartCoroutine(coroutine);
}
}
Then you just need to tweak your code a little bit to use this CoroutineHandler to run the coroutine instead of your inactive GameObject.
public void OnTriggerEnter2D (Collider2D col)
{
if (col.CompareTag("Player"))
{
// plays the cutscene and starts the timer
timeline.Play();
Player.SetActive(false);
Invoke("EnableAfterTimeline", 18);
CutsceneMC.SetActive(true);
CoroutineHandler.StartCoroutine(FinishCut()); // <- Changed
}
IEnumerator FinishCut()
{
// once the cutscene is over using the duration, turns off the collider and the MC.
yield return new WaitForSeconds(17);
CutsceneMC.SetActive(false);
CutsceneCollider.SetActive(false);
}
}
If you have only one Player instance and it's accessible from the start, you'd better set it once in the Start method.
void Start()
{
// calls the playable director and turns off the MC for the scene
timeline = timeline.GetComponent<PlayableDirector>();
CutsceneMC.SetActive(false);
Player = GameObject.FindGameObjectWithTag("Player");
}
void Update()
{
}
private void EnableAfterTimeline()
{
Player.SetActive(true);
}
I am new to Unity and was trying, after some suggestions, to use tags to know the number of enemies i have in each level and move to the next scene right after eliminating all enemies. This is the script i use on enemy gameobjects. I've also tagged each of them with the "enemy" tag in unity inspector but it still doesn't work when i run the game. After killing all the enemies, it didnĀ“t change to next scene (Success!). Any ideas on what I'm doing wrong? Any other suggestions?
Thanks a lot for the help.
Enemies Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class BadguyScript : MonoBehaviour
{
public GameObject[] enemies;
public int maxHealth;
public int curHealth;
private Animator myAnimator;
private bool isDead;
[SerializeField]
private float DespawnTime = 2.5f;
[SerializeField]
private string DeathAnimHash = "isDead";
void Start()
{
myAnimator = GetComponent<Animator>();
myAnimator.enabled =true;
myAnimator.SetBool (DeathAnimHash ,isDead);
maxHealth = 1;
curHealth = maxHealth;
}
void Update()
{
if (curHealth < 1)
{
isDead = true;
myAnimator.SetBool (DeathAnimHash ,isDead);
Destroy(gameObject,DespawnTime);
}
enemies = GameObject.FindGameObjectsWithTag("enemy"); // Checks if enemies are available with tag "Enemy".
if (enemies.Length == 0)
{
SceneManager.LoadScene("SucessScene"); // Load the scene with name "SucessScene"
}
}
void OnTriggerEnter2D(Collider2D col)
{
if (isDead)
return;
if (col.tag == "bullet")
{
curHealth -= 1;
Destroy(col.gameObject);
}
}
}
I would create a script holder gameobject for this and put a GameManager script inside it. And inside GameManager.cs which should be a singleton class you can have a property like this:
int _enemyNumber;
public int EnemyNumber{
get{
return _enemyNumber;
}
set{
_enemyNumber = value;
}
}
And when you need to change these values, use some functions you will create inside this game controller such as:
public void DecreaseEnemyCount(){
//do the logic here
}
public void SetEnemyCount(){
//do the logic here
}
Also you can find information about creating a singleton class here
You create a list with all enemies, its a good practice, cause you'll gain performance. But you're verifing if enemies.Lenght == 0, what will never occur, because before you are adding the gameObject in the list enemies = GameObject.FindGameObjectsWithTag("enemy");
In the start method, you can search for all enemies and add then in your array, and in the update or onTriggerEnter you remove it from your array and validate the array lenght. I think it'll be more easy.
Instead of adding the script to a new gameManager script attached to an empty game object cause now once all enemies are killed the script will not be in work but if added to an empty gameobject it will be working always.
I am making a pinball game in Unity, and I have an issue. When the pinball collides with a cylinder to add points to the score, it does not work. I have tagged the cylinders in Unity and have attached this script to the pinball. It doesn't even show up in the debug log.
Thanks for any advice.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class Score : MonoBehaviour {
public int scorePoint = 10;
public int MaxScore;
public Text ScoreText;
// Use this for initialization
void Start () {
ScoreText = GetComponent<Text>();
ScoreText.text = "Score: " + scorePoint;
}
void OnTriggerEnter (Collider other)
{
if (other.gameObject.tag == "Cylinder")
{
Debug.Log("Collision detected");
scorePoint+=10;
}
}
// Update is called once per frame
void Update()
{
}
}
Make sure you have a box collider on each object. OnTriggerEnter is only called when two box collider hit each other. This is the most likely culprit of why its not working but without more information I can't guarantee it.
I'm working on random coin generator. I have included the code below:
timer -= Time.deltaTime;
if (timer <= 0)
{
Vector3 position = new Vector3(Random.Range(11f, 14f), Random.Range(-0.7f, 4.5f), 0);
Instantiate(coins, position, transform.rotation);
timer = delayTimer;
}
and I add a collision to pick the coin and make a score text:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Collisions : MonoBehaviour {
public Text scoreText;
private float score;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
scoreText.text = "Score : " + score;
}
private void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag == "Player")
{
Destroy(gameObject);
scoreText.text = "Score : " + score;
score++;
}
}
}
so now when I need to add the Text to the prefab I can't
and if I assign the text in somehow to the prefab it doesn't count any score
You need a script or a prefab to know about a certain object in the scene. right?
Whenever you find yourself in this situation you must revise your approach. Keep in mind:
You can't assign a reference of an object from a scene to a prefab. That's basically impossible. prefabs aren't supposed to know about the scene.
You can assign a reference of an object from a scene to another object from the same scene.
You can assign a reference of an object from (or within) a prefab to any object (either to another prefab or same prefab or within a prefab or to an object in the scene)
In other words:
you can drag and drop anything anywhere except for from scene to prefab
Alternative?
There are many.
Why not try singleton pattern? It's easy to learn and easy to use. It's impeccable with objects which only have one instance. Like a GameController or GuiController or a UserProfileController etc.
Having a singleton GuiController:
public class GuiController : MonoBehaviour
{
//singleton setup
static GuiController instance;
void Awake() { instance = this; }
//now it's ready for static calls
public UnityEngine.UI.Text MyText;
//static method which you can call from anywhere
public static void SetMyText(string txt) { instance.MyText.text = txt; }
}
Have an object in the scene (better be the canvas). attach this script to it. Set the reference of My Text in it. In your collisions script just call GuiController.SetMyText("Score : " + score)
Edit
There is actually a slight mistake with your Collision script.
the field score which is defined in Collisions resets to 0 in the new object every time a game object with this script is being instantiated. This happens because score is not defined as a static member of Collisions and any new object naturally has its own members i.e. a new score. In fact, it shouldn't be in Collisions it is better to have a GameController script handling these kinds of data.
Again with the singleton pattern:
public class GameController : MonoBehaviour
{
static GameController instance;
void Awake() { instance = this; }
float score;
public static float Score { get { return instance.score; } set { instance.score = value; } }
}
Attach the script to a game object and instead of score++ you can write GameController.Score++;