Referencing scoreManager when instantiating a player - c#

I am wokring on a game where a player has to go through an enemy to score a point. But I am struggling to make the score count work. I am watching tutorials and trying various ways but something isnt working so I hope someone can help me here.
I have a player prefab, which gets instantiated when I press play in Unity. Then I have enemies spawning and when a player goes though one, the score should go to 1..and so on.
I have a gameObject in the scene with score script attached
public class Score : MonoBehaviour
{
public Text scoreText;
int score;
// Start is called before the first frame update
void Start()
{
score = 0;
}
public void ScoreUp()
{
score++;
scoreText.text = score.ToString();
}
}
Then I have a player script attached to a player prefab
public class PlayerController : MonoBehaviour
{
private Score score; //Reference to Score script
private void Start()
{
score = GameObject.FindWithTag("ReferenceManager").GetComponent<Score>();
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
//movement code
}
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Enemy"))
{
score.ScoreUp();
Debug.Log("HIT");
}
}
}
Reference manager tag is the gameObject in the scene with score script.
If someone could help me with this problem would be really awesome. Thank you

Define a gameobject variable so you can attach the score object to it in the editor
public class PlayerController : MonoBehaviour
{
[SerializeField]
public GameObject score_object;//This will allow u to select the player controller game object and set the variable to the score game object. The score game object is where the data will be stored.
private Score score;
private void Start()
{
score = score_object.GetComponent<Score>();
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
//movement code
}
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Enemy"))
{
score.ScoreUp();
Debug.Log("HIT");
}
}
}
I have never tried using the tag manager, What i suspect is that may be where your problem lies. Just try instancing it like this and see what happens.

Apparently my score collider which is attached to enemy doesnt work.
I tried the same thing but with trigger collider on its own and the score count works perfectly. So I will play around and try to figure out the correct place for that trigger collider. Thank you whoever tried to help me! :)
UPDATE:
I had two scripts attached to scoreManager, and when i removed the other script, the score count started to work. Actually not sure why 2 scripts were conflicting but this fixed the problem.

Related

Rigidbody.Addforce in"oncollisionenter" is not executing in unity c#

As I was trying to make a game in which player kick the ball when the player collides with ball and as I press Q but it is not working and not showing any error
public Rigidbody rg;
void OnCollisionEnter(Collision col)
{
if(col.gameObject.name=="ball")
{
if(Input.GetKeyDown(KeyCode.Q))
{
rg.AddForce(100,0,0);
}
}
}
Potentially try to add another sphere collider to the ball, and make that a trigger
then it is a matter of changing :
public Rigidbody rg;
void OnCollisionEnter(Collision col)
{
if(col.gameObject.name=="ball")
{
if(Input.GetKeyDown(KeyCode.Q))
{
rg.AddForce(100,0,0);
}
}
}
To :
public Rigidbody rg;
void OnTriggerEnter(Collision col)
{
if(col.gameObject.name=="ball")
{
if(Input.GetKeyDown(KeyCode.Q))
{
rg.AddForce(100,0,0);
}
}
}
The idea is that collision in unity is finicky, so your best bet is just to have a larger trigger to detect if the player is within range of the ball
Side NOTE :
I'm guessing that this is on your player, so maybe you could try this as well :
keep in mind that the "if" statement is just in case the ball object has different
name extensions, or maybe "ball (clone)" etc.
Rigidbody rb;
void Start(){
rb = gameObject.GetComponent<Rigidbody>();
}
void OnTriggerEnter(Collision col){
if(col.gameObject.name.ToString().Contains("ball") &&
Input.GetkeyDown(KeyCode.Q))
{
//here you can add force to the player, or to the ball
// for the player :
rb.AddForce(100,0,0);
// for the ball
col.GetComponent<Rigidbody>().AddForce(100,0,0);
}
}
This should give you a step in a different direction, Hopefully, this helps!
Cheers!

reference from various scripts causing problems [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
For some reason when I'm in the normal view in-game I am able to link the scripts that I need to call in order to make an animation like so
however, whenever I start the game it automatically removes them from the slots, and it doesn't work
I am completely clueless as to why this may be happening and unity keep giving me errors that say that I'm not setting an instance to my script I really have no clue why this may be happening.
I have 3 scripts that I'm working with that is giving me the problem
one is the main script for the enemy vision (I am referencing the other scripts in this one)
the second is the enemy animation script which makes him go from idle to attack and the third is an animation for the gun model since I had to make it follow the hands of the enemy
scripts attached bellow
1st script(enemyAI):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAi : MonoBehaviour
{
public bool detected;
GameObject target;
public Transform enemy;
public GameObject Bullet;
public Transform shootPoint;
public float shootSpeed = 10f;
public float timeToShoot = 1f;
public EnemyAni Animation;
public GunAni GunAnimation;
void Start()
{
Animation = GetComponent<EnemyAni>();
GunAnimation = GetComponent<GunAni>();
}
public void Update()
{
//makes the enemy rotate on one axis
Vector3 lookDir = target.transform.position - transform.position;
lookDir.y = 0f;
//makes enemy look at the players position
if (detected)
{
enemy.LookAt(target.transform.position, Vector3.up);
enemy.rotation = Quaternion.LookRotation(lookDir, Vector3.up);
}
if (detected == true)
{
Animation.LookPlayer = true;
GunAnimation.ShootPlayer = true;
}
if (detected == false)
{
Animation.LookPlayer = false;
GunAnimation.ShootPlayer = false;
}
}
//detects the player
void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
detected = true;
target = other.gameObject;
}
}
void OnTriggerExit(Collider other)
{
if (other.tag == "Player")
{
detected = false;
}
}
}
2nd Script (EnemyAnimation):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAni : MonoBehaviour
{
public Animator animator;
public bool LookPlayer;
public void Start()
{
animator = GetComponent<Animator>();
}
public void Update()
{
if (LookPlayer == true)
{
animator.SetBool("IsShootingStill", true);
}
else
{
animator.SetBool("IsShootingStill", false);
}
}
}
3rd script (GunAnimation):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunAni : MonoBehaviour
{
public Animator animator;
public bool ShootPlayer;
public void Start()
{
animator = GetComponent<Animator>();
}
public void Update()
{
if (ShootPlayer == true)
{
animator.SetBool("IsShooting", true);
}
else
{
animator.SetBool("IsShooting", false);
}
}
}
Your code:
void Start()
{
Animation = GetComponent<EnemyAni>();
GunAnimation = GetComponent<GunAni>();
}
Searches the GameObject that holds the EnemyAI script for EnemyAni and GunAni. If the GameObject doesn't have those it will return null.
Sounds like you want to remove that code.
Note that if the scripts exist on a child of that GameObject you will need to use GetComponentInChildren
There are some things to review, good practices:
GameObject target is private for C#, but it is better to put private before.
variable names like Bullet Animation GunAnimation should always begin with lowercase, because they might be confused with a Class Name (search in internet for CamelCase and PascalCase).
Name should be clear. EnemyAni Animation is not clear enough, because Animation maybe any animation (player, enemy, cube, car, etc.).
It is better enemyAnimation to be clear, as you did with GunAnimation (only just with lower case at beginning)
As slaw said, the Animation must be in the GO attached.
about last item
Let's say you have an empty GO (GameObject) called GameObject1 and you attach EnemyAi script to it.
In Runtime (when game mode begins), it will try to find Animation = GetComponent<EnemyAni>();, but it won't find it
Why?
Because GetComponent<> searches for the Component(Class) that is in the <> (in this case EnemyAni) in its GO (in this case, GameObject1), but the unique script attached to GameObject1 is EnemyAI.
So, you have 3 options:
Attach EnemyAni to GameObject1
Create another GO (for example, GameObjectEnemyAni), attach the script EnemyAni and drag and drop GameObjectEnemyAni to GameObject1 and delete Animation = GetComponent<EnemyAni>(); in Start
Keep in mind: if you leave that line of code, instead of getting the script EnemyAni from GameObjectEnemyAni, it will run the code Animation = GetComponent<EnemyAni>(); in Start, and obviously, it won't find it
Create events. It's a really good practice for avoiding code tight coupling, but that is more advanced stuff.

Scoring system collect item not updating Text UI - Unity C#

I have created 2 scripts one which handles the collection of the item and another which handles the score being displayed to the screen. Currently the sound plays and the item is destroyed onTriggerEnter but the score's UI-Text does not update. I have tried to add the score inside ScoringSystem and CollectGem both scripts are below.
ScoringSystem is attached to a GameObject with TextScore assigned to the slot.
Can anybody see why the text would not be adding 1 on collection?
CollectGem
public class CollectGem : MonoBehaviour
{
public AudioSource collectNoise;
public void OnTriggerEnter(Collider collider)
{
collectNoise.Play();
ScoringSystem.theScore += 1;
Destroy(gameObject);
}
}
Scoring System
public class ScoringSystem : MonoBehaviour
{
public GameObject score;
public static int theScore;
void update()
{
score.GetComponent<Text>().text = "Gems: " + theScore;
}
}
Careful, spelling matters very much in our code.
void update() != void Update()
Seems like you just forgot to capitalize the name of "Update".

Is this script resource intensive? If so, how do one improve?

This is a simple script that turns a saw blade in my game. The problem is there is approx 18 active blades on the scene, at a time. I am trying to eliminate any probability of lag. This made me wonder if using such a script in "Update", can cause lag?
public class SawBladesRotate : MonoBehaviour
{
public bool GameOver;
public GameObject Player;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
GameOver = Player.GetComponent<PlayerController>().GameOver;
if(GameOver == false)
{
transform.Rotate(new Vector3(0, 0, -45) * Time.deltaTime);
}
}
}
Put this on top of the Start method as class field
private PlayerController playerController;
and this into Start:
playeController = Player.GetComponent<PlayerController>()
Then re-use the reference:
private void Update()
{
if(playerController.GameOver) return;
//...
}
The rest is fine but ofcourse it always depends completely on your usecase.
Even more efficient it would be to directly reference the Component within unity:
[SerializeField] private PlayerController playerController;
Now you can simply drag&drop the Player GameObject into that field in the Inspector and can get rid of the GetComponent call.

How to debug why my Unity3D game does not increase score?

I'm doing some practice with Unity. What I'm basically trying to do is by using the singleton design pattern from the gameManager script. I want to prompt the player to hit space, causing score to increase after each time, health to decrease after each time, and money increase once health reaches 0 and the cube is destroyed. I think I got some script assignments mixed up or I'm not sure how to reference the right game object. For whatever reason, hitting space won't increase score or decrease health.
I got 3 texts: score, health, and money, along with a cube in the middle of the scene, with the script, DestroyBySpace, assigned to it.
I have a gameManager empty object assigned to the gameManager script
Here's the script for Game Manager:
private int currentScore;
private int currentHealth;
private int currentMoney;
public Text scoreText;
public Text healthText;
public Text moneyText;
public static GameManager instance;
void Start()
{
currentScore = 0;
currentHealth = 20;
currentMoney = 0;
updateScore();
updateMoney();
updateHealth();
}
void Awake()
{
instance = this;
}
public void adjustScore(int newScore)
{
currentScore += newScore;
updateScore();
}
public void adjustHealthAndMoney(int newHealth, int newMoney)
{
currentHealth -= newHealth;
updateHealth();
if (currentHealth == 0)
{
currentMoney += newMoney;
updateMoney();
Destroy(gameObject);
}
}
void updateScore()
{
scoreText.text = "Score: " + currentScore;
}
void updateHealth()
{
healthText.text = "Health: " + currentHealth;
}
void updateMoney()
{
moneyText.text = "Money: " + currentMoney;
}
and here's my script for DestroyBySpace:
public int scoreValue;
public int healthValue;
public int moneyValue;
public GameObject target;
public GameManager gameManager;
void Start()
{
GameObject gameManagerObject = GameObject.FindWithTag("GameManager");
if (gameManagerObject != null)
{
gameManager = gameManagerObject.GetComponent<GameManager>();
}
if (gameManager == null)
{
Debug.Log("Cannot find Game Manager script");
}
}
void onTriggerEnter()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GameManager.instance.adjustHealthAndMoney(healthValue,moneyValue);
GameManager.instance.adjustScore(scoreValue);
}
}
If you could help steer me in the right direction, I would appreciate it.
I think you've mixed up what the monobehavior onTriggerEnter() does.
onTriggerEnter() is called when another GameObject enters the collider of the GameObject the script is attached to. This is not the place to check for keyboard presses.
What you probably want to use is the Update() monobehavior. It runs every frame, so you are guaranteed that if the user presses a key the function will be able to see it.
Your code would look something like this:
void Update(){
if (Input.GetKeyDown(KeyCode.Space))
{
GameManager.instance.adjustHealthAndMoney(healthValue,moneyValue);
GameManager.instance.adjustScore(scoreValue);
}
}
Also, I think you misunderstood what gameObject refers to when you did Destroy(gameObject) in the gameManager script. gameObject refers to the gameObject the script it's mentioned in is attached to. This means when you do Destroy(gameObject) in the gameManager script you are destroying the object that the gameManager script it attached to.
To get rid of the GameObject called target you need to get access to it. You can do that in a couple ways:
1) Destroy(GameObject.Find("target"));
This directly searches for a GameObject called "target" and destroys it. The downside to this is that it requires the GameObject to be called exactly "target" and if there are multiple GameObjects called "target" it will only destroy the first one.
2) Create a public variable (like public GameObject target) and store the physical instance of the target in it through the inspector. Then when you want to destroy it, just do Destroy(target);
This is how I would most likely do it.

Categories

Resources