Coin count display display different amount - Unity 2D game - c#

I'm creating a 2D car game using unity. Basic idea is adding coin count when player collect coins and display collected coin in the end-game menu. In my CoinCount script, in OnTriggerEnter2D I add coin value to current collected coins.But when game over some times its shows correct answer and some times it's add 2 or more values to collected values.
//CoinCount script
void OnTriggerEnter2D(Collider2D colInfo)
{
int inc = 0;
if (colInfo.gameObject.CompareTag("Coin"))
{
inc = 5;
coinCount += inc;
print("coinCount" + coinCount);
AudioManager.instance.PlaySound("Coin");
}
currentBalance += inc;
PlayerPrefs.SetInt("CoinCount", currentBalance);
PlayerPrefs.Save();
}
I tried maintain a public class for manage coins but it will give the same result. As an example if I collect 5 coins with the value of 5, it should display 25 as total coin count. sometimes it's 30 and 35.
public class GameScoreManager : MonoBehaviour
{
public static int value;
public Text currentScoreText;
// Start is called before the first frame update
void Start()
{
value = 0;
}
// Update is called once per frame
void Update()
{
currentScoreText.text = "$" + value.ToString();
print("Game coin count" + value);
}
}
This is the CoinCount script after I created the GameScoreManager class.
void OnTriggerEnter2D(Collider2D colInfo)
{
if (colInfo.gameObject.CompareTag("Coin"))
{
GameScoreManager.value += scoreValue;
}
}
I attached this script for each coins for destroy it self after hitting the player
public class DestroyCoin : MonoBehaviour
{
public GameObject effect;
//This script destroy the coin
void OnTriggerEnter2D(Collider2D colInfo)
{
if (colInfo.CompareTag("Player"))
{
Instantiate(effect, transform.position, transform.rotation);
Destroy(gameObject);
}
}
}
Much appreciate your help

I would go about solving such an issue by linking the addition of the coins value to the player's score DIRECTLY to destruction of the coin.
You might think that in the same game frame your coin gets a collision event to self destruct, and your player gets an event to add the coins value to his score.
If you're a guru and you know how the unity engine's internal mechanisms work by heart, be my guest and use that knowledge to write sloppy code that still works. But for a mere mortal like me, good practice can avoid this type of issues... (for example what if the coin's destruction causes no more collision for the player to add a value to the score?)
Use the same collision event to add a value to the player's score and destroy the coin for good, making sure such an event does not occur again. Even set the coin's value to 0 at that moment, just to be doubble-safe.

I manage to solve the problem by identifying the colider. Checked it's attached in the beginning and then disable the collision after the first hit.
void OnTriggerEnter2D(Collider2D colInfo)
{
int inc = 0;
//if (colInfo.CompareTag("Coin"))
if (colInfo.gameObject.CompareTag("Coin") && colInfo.gameObject.GetComponent<CircleCollider2D>().enabled)
{
colInfo.gameObject.GetComponent<CircleCollider2D>().enabled = false;
Destroy(colInfo.gameObject);
inc = 5;
coinCount += inc;
print("coinCount" + coinCount);
// AudioManager.instance.PlaySound("Coin");
currentBalance += inc;
}
PlayerPrefs.SetInt("CoinCount", currentBalance);
PlayerPrefs.Save();
}
Thanks and learnt a lot with this

Related

Why is a console log going off when it's not supposed to?

Basically, I have a system set up where one file has a damage value, and another file has the health.
void Attack()
{
// Detect enemies in range of attack
Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(attackPoint.position, attackRange, enemyLayers);
// Damage them
foreach(Collider2D enemy in hitEnemies)
{
Debug.Log("We hit " + enemy.name);
enemy.GetComponent<Enemy>().TakeDamage(attackDamage);
}
}
it is worth noting that attackDamage is set to 40.
for my enemy script, this is the important code
public class Enemy : MonoBehaviour
{
public int maxHealth = 100;
int currentHealth;
// Start is called before the first frame update
void Start()
{
currentHealth = maxHealth;
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
// Play hurt animation
if (currentHealth <= 0);
{
Die();
}
}
void Die()
{
Debug.Log("Enemy died!");
// Die animation
// Disable the enemy
}
The problem here is that when I run the console and the enemy it says the enemy has 60 health, it still runs the 'Enemy died!', even though that should only happen if it's equal to or under 0. By the way i'm using unity on c#
I believe the most likely issue is hitEnemies contains duplicates or already dead enemies. You can check if the enemy is already dead or not to solve this. You could also use .Distinct to remove duplicates but it will take more time to run the attack method.
Remember, a debugger can be your best friend. Use it to see what is really going on in your game. It will let you trace and follow through your code to see what is happening and why it does things.

Collision Detection in Child Colliders, From Parent Object (Unity3D)

I'm trying to make a script that allows the head hitbox, and the chest hitbox which are children to the main game object, to be detected by the main script so that damage can take place. For example, the main script knows when the head collider is hit, vs the body collider. (Currently, doesn't work even if enabled). I've tried many scripts and searched for answers and I really can't find them. Please help. Note, the comments as far as I know have little to nothing to do with the issue I'm having. (Edit): Clarified which part of the script I'm having trouble with.
Here's the part of the script that is giving me trouble:
public void OnTriggerEnter(Collider collider)
{
myLastHit = collider.gameObject.GetComponent<PunchDetection>().punched;
foreach(Transform sibling in transform.parent)
{
if(sibling.name == "HeadHitbox")
{
collider.enabled = true;
//Collider col = child.GetComponent<SphereCollider>();
if (canTakeDamage && myLastHit is SphereCollider)
{
TakeDamage(15);
StartCoroutine(damageTimer());
}
}
if (sibling.name == "RibsHitbox")
{
collider.enabled = true;
//Collider col = child.GetComponent<CapsuleCollider>();
if (canTakeDamage && myLastHit is CapsuleCollider)
{
TakeDamage(5);
StartCoroutine(damageTimer());
}
}
}
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
healthBar.SetHealth(currentHealth);
}
private IEnumerator damageTimer()
{
canTakeDamage = false;
yield return new WaitForSeconds(damageTimeout);
canTakeDamage = true;
}```
Why not [SerialisedField] protected RigidBody headHitChhildObject, ribHitChhildObject;
Drag and drop to inspector. Than in start function.
headHitChhildObject = findObjectOftType ().gameObject.GetComponent()
ribHitChhildObject= findObjectOfType (). gameObject.GetComponent ()
(Do this seperate for each object)
Change take damage parameters to
Public Void TakeDamage (Collider triggerObject) --- I would use a float personally for damage/health, but that's just me.
In on trigger function.
TakeDamage(collision)
Inside Take Damage Fumction have the following lines
For if (triggerObject.attachedRigidBody ! = null && siblings.name = = putname)
currentHealth - = damage amount
For the Else If(triggerObject. attachedRigidBody ! = null && siblings.name = = putOtherName)
currentHealth - = damage amount
(My more optimal suggestion) Or better yet, have a Master script for
handling the hits.(listener) Pretty much take damage and numerator.
public Void On Enable() MyGameEvents.onTriggerColliderEvent +=
TakeDamage ;
public Void On Disable()
MyGameEvents.onTriggerColliderEvent -= TakeDamage ;
a delegation script for handling events across scripts(not just these
scripts, but other scripts, instead of constant cross referencing
multiple times and long code!
public static class MyGameEvents or something you chose
public delegate void OnTriggerCollider(Collider triggerObject,
int amount) ;
Public static OnTriggerCollider onTriggerColliderEvent ;
In a script on each trigger children components just have;
[SerialisedField] protected int damageToCall // Set in the inspector,
so you can experiment with this)
OnTriggerEnter(Collider collider)
MyGameEvents.onTriggerColliderEvwnr?.Invoke(Collider, damageToCall)
and you can use the public static MyGameEvents for handling and coding that is reoccurring across other scripts. Such as camera shake, ground shake, body part shake, health regen when certain req's are met. Power Up Events, object destroy on Zero health events.
Score points, etc.
Think of it as a way more reusable and versatile unity events all the YouTube dev wannabes don't use properly.

Wrong number converting int to string c#

I have simple coin counter, in 2d Unity game. But It's count one coin as 2. I think that it's because of wrong converting int to string.
public GameObject coin; // Gameobject with coin
public Text CoinCounter; // Text with counter that shows in game
private float TotalCounter = 0; // Float for counting total amount of picked up coins
{
TotalCounter = Convert.ToInt32((CoinCounter.text)); // Converting text counter to Numbers
}
private void Update()
{
TotalCounter = Convert.ToInt32((CoinCounter.text)); // Updating Counter evry frame update
Debug.Log(TotalCounter); // Showing Counter in Console
}
private void OnTriggerEnter2D(Collider2D collision)
{
TotalCounter = (TotalCounter + 1); // adding 1 to total amount when player touching coin
CoinCounter.text = TotalCounter.ToString(); // Converting to Text, and showing up in UI
coin.SetActive(false); // Hiding coin
}
So, in Debug Log it's showing right Total amount, but in UI, It's Showing wrong number. As example, When Total Amount is 1 it's showing 2 etc.
It's better to write your convertor code into the trigger void , after that check it ; this might be happened because of update function, try like that and check again: `
public GameObject coin;
public Text CoinCounter;
private float TotalCounter = 0;
private void Update()
{}
private void OnTriggerEnter2D(Collider2D collision)
{
TotalCounter = (TotalCounter + 1);
Debug.Log(TotalCounter);
CoinCounter.text = TotalCounter.ToString();
Debug.Log(CoinCounter.text);
coin.SetActive(false);
}
`
You don't have to do it in Update but only when you actually change it.
The method you are looking for is probably int.TryParse
you should use int for amounts (unless will you have values like 1.5 coins later)
Than you execute your code everytime it collides ... with anything. You should only collide with a coin instead. Either use tags or compare with the referenced value in your case
public GameObject Coin; // Gameobject with coin
public Text CoinCounter; // Text with counter that shows in game
private int _totalCounter = 0; // Int for counting total amount of picked up coins
// I guess it's supposed to be Start here
void Start()
{
// Converting text counter to Numbers
int.TryParse(CoinCounter.text, out _totalCounter);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject != Coin) return;
// later this should probably rather be
//if(collision.gameObject.tag != "Coin") return
_totalCounter += 1; // adding 1 to total amount when player touching coin
CoinCounter.text = _totalCounter.ToString(); // Converting to Text, and showing up in UI
Coin.SetActive(false); // Hiding coin
// later this should probably rather be
//collision.gameObject.SetActive(false);
}
Problem was not in converting, trigger worked twice. Need check if the coin is enabled before disabling it and adding to the coin counter. Eg:
if (coin.activeSelf)
{
coin.SetActive(false);
Debug.Log("Object is not active ");
TotalCounter += 1;
Debug.Log("Total Counter + :" + TotalCounter);
CoinCounter.text = TotalCounter.ToString();
Debug.Log("Text after +:" + CoinCounter.text);
}

Updating how many enemies are remaining - Unity

I'm pretty new to Unity and I've had a look around for similar problems, but I suck at transferring it over into my program.
Anyway, I basically have a class called Scoring that will keep track of how many enemies there are on the level. I want to pass this value into another class called Bullet_explosive. In this class, it will remove one from that total when an enemy has been hit with the bullet. After it has removed one from the total, I want this value to be passed back into Scoring so that it can be displayed on the screen to the player.
Probably been answered a million times, but I'm sick of not knowing how to implement it into my own program.
Thanks in advance.
Here's the Scoring class:
public class Scoring : MonoBehaviour {
// Create gameobject to store the text object within
public GameObject textObject;
// Holds the text displayed on screen
Text actualText;
// Holds the remaining number of enemies
public static int enemiesRemaining = 12;
// Use this for initialization
void Start ()
{
// Stores the gameobject called EnemiesRemaining
textObject = GameObject.Find ("EnemiesRemaining");
// Gets the text component of that gameobject
actualText = textObject.GetComponent<Text> ();
// Stores what text the display will actually show
actualText.text = "Enemies Remaining: " + enemiesRemaining;
}
// Update is called once per frame
void Update ()
{
// Updates the display
actualText.text = "Enemies Remaining: " + enemiesRemaining;
}
Here's the Bullet_explosive class:
public class Bullet_explosive : MonoBehaviour {
// Lifespan of the bullet
float lifespan = 1.5f;
// Setting up game objects
public GameObject fireEffect;
public GameObject explosion;
public GameObject theGate;
//Passing through the enemies remaining
private static int score;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
score = Scoring.enemiesRemaining;
lifespan -= Time.deltaTime;
// Once the lifespan reaches 0, bullet is destroyed
if (lifespan <= 0)
{
Explode ();
}
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Enemy")
{
// Reduces the remaining enemies
score -= 1;
// Checks for no remaining enemies
if (score <= 0)
{
// Removes the gate
Destroy(GameObject.FindWithTag ("Gate"));
}
// Changes the tag of the target hit
collision.gameObject.tag = "Untagged";
// Applies visual effects at the position and rotation of the target
Instantiate (fireEffect, collision.transform.position, Quaternion.identity);
Instantiate (explosion, collision.transform.position, Quaternion.identity);
// Removes bullet and target
Explode();
Destroy (collision.gameObject);
}
}
void Explode()
{
Destroy (gameObject);
}
I find it to be too much effort to have two static fields that mean exactly the same thing. You should only make one field for that and always refer to that same field in the Scoring class.
public class Bullet_explosive : MonoBehaviour {
// Lifespan of the bullet
float lifespan = 1.5f;
// Setting up game objects
public GameObject fireEffect;
public GameObject explosion;
public GameObject theGate;
// Use this for initialization
void Start () { }
// Update is called once per frame
void Update ()
{
/* no "score" updating needed here in Update() */
lifespan -= Time.deltaTime;
// Once the lifespan reaches 0, bullet is destroyed
if (lifespan <= 0)
{
Explode ();
}
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Enemy")
{
// Reduces the remaining enemies
//Directly modify that one static field
Scoring.enemiesRemaining -= 1;
// Checks for no remaining enemies
if (Scoring.enemiesRemaining <= 0) //here too
{
// Removes the gate
Destroy(GameObject.FindWithTag ("Gate"));
}
// Changes the tag of the target hit
collision.gameObject.tag = "Untagged";
// Applies visual effects at the position and rotation of the target
Instantiate (fireEffect, collision.transform.position, Quaternion.identity);
Instantiate (explosion, collision.transform.position, Quaternion.identity);
// Removes bullet and target
Explode();
Destroy (collision.gameObject);
}
}
void Explode()
{
Destroy (gameObject);
}
And that should be it.

Player.cs error CS1525: Unexpected symbol `public'

Hey guys I am getting this error as i mentioned in the title Ive been trying to debug since last night and finally got down to this one error left i cant seem to beat. here is the code please have a look if you get the chance the error is saying its on line (107,16) and sorry for the link i just joined here don't know how to put it in code.
using UnityEngine;
using System.Collections;
using UnityEngine.UI; //Allows us to use UI.
namespace Completed
{
public class Player
{
public float restartLevelDelay = 1f;
public int pointsPerFood = 10;
public int pointsPerSoda = 20;
public int wallDamage = 1;
public Text foodText;
public AudioClip moveSound1;
public AudioClip moveSound2;
public AudioClip eatSound1;
public AudioClip eatSound2;
public AudioClip drinkSound1;
public AudioClip drinkSound2;
public AudioClip gameOverSound;
private Animator animator;
private int food;
private Vector2 touchOrigin = -Vector2.one;
//Start overrides the Start function of MovingObject
protected override void Start ()
{
//Get a component reference to the Player's animator component
animator = GetComponent<Animator>();
//Get the current food point total stored in GameManager.instance between levels.
food = GameManager.instance.playerFoodPoints;
//Set the foodText to reflect the current player food total.
foodText.text = "Food: " + food;
//Call the Start function of the MovingObject base class.
base.Start ();
}
//This function is called when the behaviour becomes disabled or inactive.
private void OnDisable ()
{
GameManager.instance.playerFoodPoints = food;
}
private void Update ()
{
//If it's not the player's turn, exit the function.
if(!GameManager.instance.playersTurn) return;
int horizontal = 0; //Used to store the horizontal move direction.
int vertical = 0; //Used to store the vertical move direction.
//Check if we have a non-zero value for horizontal or vertical
if(horizontal != 0 || vertical != 0)
{
AttemptMove<Wall> (horizontal, vertical);
}
}
protected override void AttemptMove <T> (int xDir, int yDir)
{
//Every time player moves, subtract from food points total.
food--;
//Update food text display to reflect current score.
foodText.text = "Food: " + food;
//Call the AttemptMove method of the base class, passing in the component T (in this case Wall) and x and y direction to move.
base.AttemptMove <T> (xDir, yDir);
//Hit allows us to reference the result of the Linecast done in Move.
RaycastHit2D hit;
//If Move returns true, meaning Player was able to move into an empty space.
if (Move (xDir, yDir, out hit))
{
//Call RandomizeSfx of SoundManager to play the move sound, passing in two audio clips to choose from.
SoundManager.instance.RandomizeSfx (moveSound1, moveSound2);
}
{
//Since the player has moved and lost food points, check if the game has ended.
CheckIfGameOver ();
//Set the playersTurn boolean of GameManager to false now that players turn is over.
GameManager.instance.playersTurn = false;
}
//OnCantMove overrides the abstract function OnCantMove in MovingObject.
//It takes a generic parameter T which in the case of Player is a Wall which the player can attack and destroy.
public override void OnCantMove <T> (T component)
{
//Set hitWall to equal the component passed in as a parameter.
Wall hitWall = component as Wall;
//Call the DamageWall function of the Wall we are hitting.
hitWall.DamageWall (wallDamage);
//Set the attack trigger of the player's animation controller in order to play the player's attack animation.
animator.SetTrigger ("playerChop");
}
//OnTriggerEnter2D is sent when another object enters a trigger collider attached to this object (2D physics only).
private void OnTriggerEnter2D (Collider2D other)
{
//Check if the tag of the trigger collided with is Exit.
if(other.tag == "Exit")
{
//Invoke the Restart function to start the next level with a delay of restartLevelDelay (default 1 second).
Invoke ("Restart", restartLevelDelay);
//Disable the player object since level is over.
enabled = false;
}
//Check if the tag of the trigger collided with is Food.
else if(other.tag == "Food")
{
//Add pointsPerFood to the players current food total.
food += pointsPerFood;
//Update foodText to represent current total and notify player that they gained points
foodText.text = "+" + pointsPerFood + " Food: " + food; //nothing
//Call the RandomizeSfx function of SoundManager and pass in two eating sounds to choose between to play the eating sound effect.
SoundManager.instance.RandomizeSfx (eatSound1, eatSound2);
//Disable the food object the player collided with.
other.gameObject.SetActive (false);
}
//Check if the tag of the trigger collided with is Soda.
else if(other.tag == "Soda")
{
//Add pointsPerSoda to players food points total
food += pointsPerSoda;
//Update foodText to represent current total and notify player that they gained points
foodText.text = "+" + pointsPerSoda + " Food: " + food;
//Call the RandomizeSfx function of SoundManager and pass in two drinking sounds to choose between to play the drinking sound effect.
SoundManager.instance.RandomizeSfx (drinkSound1, drinkSound2);
//Disable the soda object the player collided with.
other.gameObject.SetActive (false);
}
}
//Restart reloads the scene when called.
private void Restart ()
{
//Load the last scene loaded, in this case Main, the only scene in the game.
Application.LoadLevel (Application.loadedLevel);
}
//LoseFood is called when an enemy attacks the player.
//It takes a parameter loss which specifies how many points to lose.
public void LoseFood (int loss)
{
//Set the trigger for the player animator to transition to the playerHit animation.
animator.SetTrigger ("playerHit");
//Subtract lost food points from the players total.
food -= loss;
//Update the food display with the new total.
foodText.text = "-" + loss + " Food: " + food;
//Check to see if game has ended.
CheckIfGameOver ();
}
//CheckIfGameOver checks if the player is out of food points and if so, ends the game.
private void CheckIfGameOver ()
{
//Check if food point total is less than or equal to zero.
if (food <= 0)
{
//Call the PlaySingle function of SoundManager and pass it the gameOverSound as the audio clip to play.
SoundManager.instance.PlaySingle (gameOverSound);
//Stop the background music.
SoundManager.instance.musicSource.Stop();
//Call the GameOver function of GameManager.
GameManager.instance.GameOver ();
}
}
}
}
In the function protected override void AttemptMove , remove the open {
// ... previous code ...
if (Move (xDir, yDir, out hit))
{
//Call RandomizeSfx of SoundManager to play the move sound, passing in two audio clips to choose from.
SoundManager.instance.RandomizeSfx (moveSound1, moveSound2);
}
// { REMOVE THIS ONE!
//Since the player has moved and lost food points, check if the game has ended.
CheckIfGameOver ();
//Set the playersTurn boolean of GameManager to false now that players turn is over.
GameManager.instance.playersTurn = false;

Categories