Score not changing when collision occurs - c#

I have been trying to get my score to work correctly when the player collides with one of the collectibles but the score does not seem to change when the collision occurs, I am not sure why this is happening.
In my collectible I have this:
class BlueBall : Obj
{
public BlueBall(Vector2 pos)
: base(pos)
{
position = pos;
spriteName = "BlueBall";
solid = false;
score = 0;
}
public override void Update()
{
if (!alive) return;
Player player = CheckCollisionAgainst<Player>();
if (player != null)
{
score = score + 20;
alive = false;
}
I am drawing in the Game1 class with:
spriteBatch.DrawString(font, "Score: " + score.ToString(), scorePos, Color.White);
So if the Player Collides with the BlueBall, 20 should be added to the score and the BlueBall should disappear, it does disappear but the score does not change, why is this?
At this moment I am declaring my score in the Game1 class with public int score and Vector2 scorePos to position it. I then Initialize the score with score = 0; and then Load in the scorePos values in Update.

From your code, and your comments, it is clear that there are actually two score variables. One that is owned by the game:
public class Game
{
private int score;
void Draw(...)
{
spriteBatch.DrawString(font, "Score: " + score.ToString(), scorePos, Color.White);
}
}
and one that is owned by the ball:
public class BlueBall : Obj
{
private int score;
public void CheckCollisions()
{
if (collision)
score += 20;
}
}
Now, those two score variables have absolutely no relation to each other. The fact that they are named the same is totally irrelevant.
To make a quick analogy, imagine programming objects are boxes. Each variable is a "counter". You put a counter in the "Game" box and call it "score". You put another counter in the "Ball" box and also call it "score". Obviously, modifying the counter in the "Ball" box won't affect the one in the "Game" box and vice versa. In general, this concept is called "scoping" in programming. Each variable is scoped to its box, which can be a class, function, or even a using block (scopes are created by {}).
Now that you understand the problem, there are a couple ways to fix it:
Create a global variable. You can't actually do that in C#, but you can create another object that both other objects have access to. Both objects than reference/modify it's "score" property. A simple (and poor practice) example would be:
public static class GlobalState
{
public static int GameScore { get; set; }
}
All references to score would become GlobalState.GameScore. Again, this is just to get you going, this kind of code is really bad practice, and can cause bugs (especially in multi-threaded situations).
Have the ball raise an event on collision. The game would register for this event and increment its "score" variable appropriately.
Pass the game's "score" variable to the collision method by ref, so that it can change it. Warning: This is included for completeness, and this should only be done in specific circumstances, of which yours is probably not one of them.
Pass a delegate to the ball on creation to invoke when it needs to increment the score. This is really a variation of the event method.
I'm sure there are others. Given your experience level, I would start with the global approach (as its the simplest and requires the least amount of knowledge) and then move to learning about events and go with that approach in the future.

Related

C# - Unable to increment an int beyond 1. Console prints 1 and it does not increase, despite triggering multiple times

The game is simple, your Player has a running animation and must jump to dodge obstacles, but isn't moving. The obstacles and background are.
My gut instinct is that it is out of scope somehow or is perhaps overflowing - the score increment was increasing far beyond what I had anticipated when I had the AddScore() inside of the Destroy(gameObject); if condition instead of its own function.
However, at this point I am very confused why it isn't working. As a bonus, I cannot get Audio to play from the second commented bit (Value cannot be null.) As for why that happens, no idea. I definitely have the source I have it attached to to the prefab that is spawned, and said spawn should trigger that sound to play when it passes under the player when they jump, I originally thought that there was an issue where the object was deleted before it could reference its audio source but I am unsure.
Edit: I am going to leave the 'bonus' issue above even though I literally fixed it as I typed this up. (I had to add an Audio Source component to the prefab I was spawning.)
I still, for the life of me, cannot get an integer to go above 1 and print to the console. It might be driving me a little insane. Please help friends. I have googled a ton and none of the suggestions from other comments fixed my issue.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveLeft : MonoBehaviour
{
private float speed = 30;
private PlayerController playerControllerScript;
private float leftBound = -15;
private float scoreZone = -3;
public AudioClip scoreSound;
private int score = 0;
private AudioSource playerAudio;
// Start is called before the first frame update
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerController>();
playerAudio = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
// Moves objects as long as it is not game over.
if (playerControllerScript.gameOver == false)
{
transform.Translate(Vector3.left * Time.deltaTime * speed);
}
//Assigns scoring system, buggy. Commented out sound because of errors.
if (transform.position.x < scoreZone && gameObject.CompareTag("Obstacle"))
{
//playerAudio.PlayOneShot(scoreSound, 1);
}
//Destroy object out of bounds
if (transform.position.x < leftBound && gameObject.CompareTag("Obstacle"))
{
Destroy(gameObject);
AddScore();
}
}
void AddScore()
{
//Score goes to 1.
score++;
//prints 1 in console but does not increase over 1.
Debug.Log("Score! Your Score is" + score);
Debug.Log(score);
}
}
Tried: Numerous changes to configuration of ++, x = x +1, x++, x +=, etc. No idea. Am lost.
This is an issue caused by the local member int. You have an object, which has been created and this MoveLeft component is attached to it. You then destroy this object on collision, and therefore this component as well. You’ll have added one to the local instance int score, but then you destroy the component and lose this local instance.
I suspect what you thought was happening is that all the scripts/components shared the same variable values. That would only be true if you if you made the int score member a static int score, which means the member is the same shared amongst all instances of this component.
Instead of making score static, you might want to have a “Game Manager” that exposes public functions to allow you to increment and retrieve the current score.

How to create a winning condition when a certain number of game objects have spawned

I am trying to get my 3-D Tic-Tac-Toe game project to work, I have game objects which are named cells that are instantitated I press OnMouseDown() click it makes a cell object spawn in its grid space. I don't want to use UI with the basic prefabs I created. Is there a way to get my game objects instantiated and once it reaches a certain number as a winning condition? I have considered using pathfinding but I am not certain if that would be the correct approach. I have looked every where to find a solution that is unique to my problem but could not find a solution. Perhaps, I am asking the wrong questions but I am desperate so that is why I came her to see if I could get input on how to approach this issue.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.SceneManagement;
public class Cell : MonoBehaviour
{
public GameObject cell;
public GameObject negCell;
public GameObject alter;
public Transform transPos;
[SerializeField]
private bool isTapped = false;
private int counted;
public int gameObjectCount;
void Start()
{
gameObjectCount = GameObject.FindGameObjectsWithTag("Cell1").Length;
}
void Update()
{
}
public void OnMouseDown(int counted) //click and point to create and deestroy objects
{
counted = gameObjectCount;
isTapped = true;
transPos = alter.transform;
Instantiate(cell, transform.position, Quaternion.identity);
StartCoroutine(WaitForObject());
Debug.Log("Alter Destroyed!");
gameObjectCount++;
DestroyGameObject();
return;
}
IEnumerator WaitForObject()
{
if (isTapped == true)
{
Instantiate(negCell, -transform.position, Quaternion.identity);
isTapped = false;
}
yield return new WaitForSeconds(3f);
DestroyGameObject();
}
void DestroyGameObject()
{
if(gameObject == alter)
{
DestroyImmediate(alter, true);
}
else
{
DestroyImmediate(cell, true);
}
}
}
There are two easy ways to achieve this.
The first one would be to add a static member in your class, let's say :
private static int _instanceCounter = 0;
This will act as a class instances counter.
All you have to do is to increment this variable every time you instantiate a new game object.
Finally, base your win condition on the number of instances of the class you want.
You can also decrement this variable if for some reason at a moment you call the Destroy method on a specific game object.
The other way would be to use the FindObjectsOfType method from Unity which returns an array of all instances in your current scene.
By accessing the length of this array, you'll have the number of instances.
However, this only count for the current number of instances when this method is called. Note that you can also include the inactive game objects from the scene (those which are in grey within your hierarchy panel).
You now have two ways to do it, depending on how you want to achieve your win condition, i.e. the total of game objects instantiated OR a specific number of game objects at a given time.

Why does my high score change no matter what? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
Im using unity to create a game. When the player dies the game over scene is loaded. No matter what happens, the high score is always set to the score value. Why?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HighScoreText : MonoBehaviour
{
int score;
int highScore;
Text text;
// Start is called before the first frame update
void Start()
{
text = GetComponent<Text>();
}
// Update is called once per frame
void Update()
{
score = Score.score;
if(score > highScore)
{
highScore = score;
}
text.text = highScore.ToString();
}
}
It is very important to understand what happens when you load a scene or even reload a scene.
It is also very important to understand that start IS not a constructor. It is called the first frame a script is enabled before any update methods are called you can read more about that here: https://docs.unity3d.com/ScriptReference/MonoBehaviour.Start.html
So let's analyze your problem, you load the gameOverScene, which starts by creating all your gameObjects and initializing your scripts. This includes your highScoreText object(The gameObject with this script attached). Now we have a thing called Scope. It is possible to have 2 separate highScoreObjects, each of them would have their own values for highscore, since each value in your script is not global or static values that get shared. Also an important thing to note, since this value is not static and is a member to that individual object, when the scene gets unloaded, all the objects in the scene get destroyed. Which means the value you stored for highscore will get deleted.
So scene gets created, your objects get created with their default values if they arent static. Scene gets destroyed all your objects get destroyed and so does all the values you stored on them. So the solution would be to find a way to keep that value persistent through scenes... Seems like we have a few options from this per the comments above, and from what I have said about static values...
1 option is to make highscore static, now the value will not get destroyed when the scene changes... but it is shared across all highscore options...
another option is to make a singleton, and the pattern in unity for a singleton is to typically have a gameobject marked with (DontDestroyOnLoad) then in the start method detecting if this singleton existed if it does and it isnt me, destroy myself. if it doesn't make it me.
Personally, since I dont know the full scope of your project, I would just store it in the class you have already made called 'Score' make it static, make an accessor for it, and a method to check if current score is greater then highscore then return for example:
class Score {
public static int score;
private static int highScore;
public static void CheckForNewHighScore() {
if(score > highScore)
{
highScore = score;
}
}
public static int getHighScore()
{
return highScore;
}
}
This will take care of the problem you have with losing information when reloading the scene, and keep the highscore...
With this you can also change your highScoreText class to something like this
public class HighScoreText : MonoBehaviour
{
Text text;
// Start is called before the first frame update
void Start()
{
Score.CheckForNewHighScore();
int currentHighScore = Score.getHighScore();
text = GetComponent<Text>();
if(text != null) {
text.text = currentHighScore .ToString();
}
}
// Update is called once per frame
void Update()
{
// Removed everything from here because... well if they are
// on the gameover screen their score shouldn't increase
// So no reason to keep reseting the value.
}
}

Unity 3DCheck if int is met and do something?

In my game the player is able to pickup leaves, stones and a wooden log. I want to add conditions to the player that will activate when a certain pickup is equal to 5.
The player consists of 5 modules and each module will be replaced by a pickup when something is picked up. This means the player can consist out of 5 leaves, 5 rocks or 5 wooden logs or a mix of those items.
[Header("Real-Time Statistics")]
public int numberOfLeaves;
public int numberOfLogs;
public int numberOfRocks;
This aspect is shown in the inspector and gets updated when the player finds a pickup.
void OnTriggerStay2D(Collider2D other){
if(Input.GetKeyDown(KeyCode.P)){
if(other.gameObject.tag == "Leaf"){
Pickup(1); // 1 = leaf, according to the ModuleStateAndTextureController script.
numberOfLeaves += 1;
Destroy(other.gameObject); // destroy the pickup item as we are picking it up
}
if(other.gameObject.tag == "WoodLog"){
Pickup(2); // 2 = wood log, according to the ModuleStateAndTextureController script.
numberOfLogs += 1;
Destroy(other.gameObject); // destroy the pickup item as we are picking it up
}
if(other.gameObject.tag == "Rock"){
Pickup(3); // 3 = rock, according to the ModuleStateAndTextureController script.
numberOfRocks += 1;
Destroy(other.gameObject); // destroy the pickup item as we are picking it up
}
}
}
This part of the script adds a number to the int when a certain pickup is found. I have a similar part within the script when the player drops a pickup.
How would I write a script that checks if a player meets certain conditions, I.e. if the player consists of 5 leaves he would be able to jump higher and descend slower?
What I had in mind was something like: If the player consists out of 5 leaves jumpPower = 2000; or something like that. This would be a trait added within the player object I guess, but I also need to know how to use those int on other objects i.e. A trigger that can check if the player consists out of 3 leaves and 2 wooden logs.
I hope someone can help me set this up, because I'm having a hard time scripting as a designer.
If understood well your need, this is a simple sample of what you could use.
You could use delegates combined with properties to make things happen when the value of a variable is set.
public Class MyClass : MonoBehaviour {
// Delegates, like a pointer in C, but to method(s) instead of variable
public delegate void valueLogsChangedDelegate (int valueLogs);
public valueLogsChanged valueLogsChanged = delegate { };
private int _numberOfLogs;
// Property, when you set numberOfLogs (eg numberOfLogs = 10), every thing in "set" is executed
public int numberOfLogs {
get {
return _numberOflogs;
}
set {
_numberOfLogs = value;
valueLogsChanged(_numberOflogs);
}
}
/// <summary>
/// Awake is called when the script instance is being loaded.
/// </summary>
void Awake()
{
// Subscribe to the delegate, you can add as many methods as you want. Every methods that subscribe to the delegate will be excuted when the delegate is called
valueLogsChanged += Method;
}
void Method(int valueLogs)
{
if (valueLogs > 5)
{
jumpPower = 2000;
}
}
}
I am tired so i may have make a mistake. Morover, if I did not understood your need, excuse me!

Score Count not working on a prefab

This is semi complicated of a question but I'll do my best to explain it:
I am making this mobile game in which you have to shoot four cubes. I'm trying to make it so when the cubes are shot by a bullet, they're destroyed and a UI text says 1/4, to 4/4 whenever a cube is shot. But it's being really weird and only counts to 1/4 even when all four cubes are shot and destroyed. I put these two scripts on the bullets (I made two separate scripts to see if that would do anything, it didn't)
And to give a better idea of what I'm talking about, here's a screenshot of the game itself.
I've been using Unity for about 6 days, so I apologize for anything I say that's noob-ish.
EDIT
So I combined the two scripts onto an empty gameobject and here's the new script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class GameManagerScript : MonoBehaviour {
public GameObject cubes;
public Text countText;
public int cubeCount;
public Transform target;
// Use this for initialization
void Start () {
}
void OnTriggerEnter(Collider other)
{
cubes = other.gameObject;
}
// Update is called once per frame
void Update () {
cubes.transform.position = Vector3.MoveTowards(transform.position, target.position, 1f * Time.deltaTime);
if (cubes.gameObject.tag == "BULLET")
{
cubeCount = cubeCount + 1;
countText.text = cubeCount + "/4";
cubes.SetActive(false);
}
}
}
ANOTHER EDIT
I tried everything, so is there a way to detect when all the children in a parent on the Hierarchy are destroyed? Instead of counting up? This can give a better idea:
So I want to be able to detect when Cube, Cube1, Cube2, and Cube3 have all been destroyed.
The answer is pretty simple: Since every individual bullet has that script, each bullet has its own score.
For something like a score you want a single spot to store it, e.g. a script on an empty gameobject that serves as game controller. Just access that in the collision and increase the score (maybe have a look on singletons here).
You can combine those two scripts and actually it might be better to not have this on the bullet, but on the target because there are probably less of them which will save you some performance. (And it does more sense from a logical point of view.)
Edit:
I assume you create the bullets using Instantiate with a prefab. A prefab (= blueprint) is not actually in the game (only objects that are in the scene/hierarchy are in the game). Every use of Instantiate will create a new instance of that prefab with it's own version of components. A singleton is a thing that can only exist once, but also and that is why I mention it here, you can access it without something like Find. It is some sort of static. And an empty gameobject is just an object without visuals. You can easily create one in unity (rightclick > create empty). They are typically used as container and scriptholders.
Edit:
What you want is:
An empty gameobject with a script which holds the score.
A script that detects the collision using OnTriggerEnter and this script will either be on the bullets or on the targets.
Now, this is just a very quick example and can be optimized, but I hope this will give you an idea.
The script for the score, to be placed on an empty gameobject:
public class ScoreManager : MonoBehaviour
{
public Text scoreText; // the text object that displays the score, populate e.g. via inspector
private int score;
public void IncrementScore()
{
score++;
scoreText.text = score.ToString();
}
}
The collision script as bullet version:
public class Bullet : MonoBehaviour
{
private ScoreManager scoreManager;
private void Start()
{
scoreManager = GameObject.FindWithTag("GameManager").GetComponent<ScoreManager>(); // give the score manager empty gameobject that tag
}
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Target") == true)
{
// update score
scoreManager.IncrementScore();
// handle target, in this example it's just destroyed
Destroy(other.gameObject);
}
}
}

Categories