In my game there is a score. When an object collides, a set increment is added to the score. There is one scoreboard. What is happening in my game is that when an object collides it hijacks the scoreboard to show only its own person history of scores. When the next object collides, it takes over the scoreboard and shows its own personal history. I am trying to show the amalgamate score of all of the objects put together, each contributing their part to a grand total.
void OnCollisionEnter (Collision col)
{
if(col.gameObject.name == "Plane")
{
score += 50;
textMeshComponent = GameObject.Find ("Score").GetComponent<TextMesh> ();
textMeshComponent.text = score.ToString ();
}
}
There are 10 of (col.gameObject.name) and there is one "Plane" that they all interact with.
How can I make the score a conglomerate like I described? I am really at a loss for how to manipulate the (col.gameObject.name) side of that equation.
Thanks for your advice.
Make score a member of Plane, instead of this, and make it public so all the other gameObjects can get it.
You can also create score as its own gameObject, or create a gameObject that contains all the globals.
Let me suggest another approach although not recommended textMeshComponent.text = (score+(int.Parse(textMeshComponent.text))).toString();
now this will add points as a whole regardless each gameobject adding its own to the main value
Consider having an object in your scene whose sole concern is counting (and maybe displaying) the score. Lets name it ScoreController.
Whenever an event occurs for which you want to award score to the player, the responsible code just calls your ScoreController and tells them how much score to award.
Your ScoreController GameObject might have a script like this:
public class ScoreController : MonoBehaviour
{
public TextMesh scoreDisplay;
public int Score { get; private set; }
void Start()
{
UpdateView();
}
public void AwardScore(int amount)
{
Score += amount;
UpdateView();
}
private void UpdateView()
{
scoreDisplay.text = Score.ToString();
}
}
Now, other code can award score by calling
GameObject.FindObjectOfType<ScoreController>().AwardScore(123);
If you want to read up on related topics, consider reading about the Model-View-Control design pattern, and about SoC (seperation of concerns).
Related
Im in my 1st year in game design / dev ,
and my project is to make a simpler "binding of isaac" type of game 3D
I managed to make Dungeon Generation (with a lot of help from YouTube lol)
And now I'm working on a system that will detect in each room if all the enemies have died so that the doors in that room will be in "open" mode [bool] .
my idea was to create a C# script in the prefab of each room.
and it goes something like this > Timer [after 5sec to check if all enemies in rooms is dead] and loop until they are.
1- now the problem is if they are all dead in room X how to keep room Y closed ? [ because its the same prefab for the Dungeon generation]
2 - and what is the best way to check if they are all dead? [the easiest way for me is just by using: game tag "enemies" ] but using colliders / tags is very costly...
using Unity/c# btw
Any kind of help would be greatly appreciated
:)
Instead of using a timer to check periodically (if the player kills the last monster 0.01s after the previous check, they have to wait 4.99s before the doors open), have the room manage a collection of monsters in it, and when a monster dies have the monster notify the room. Psuedo code for the room:
private bool DoorsOpen = true;
private List<GameObject> MonstersInMe { get; set; }
private void GenerateMonster(GameObject monsterPrefab)
{
var monster = Instantiate(monsterPrefab);
monster.transform.SetParent(this.transform);
MonstersInMe.Add(monster);
}
public void RemoveMonster(GameObject monster)
{
MonstersInMe.Remove(monster);
if (MonstersInMe.Count == 0)
{
OnClear();
}
}
private void OnClear()
{
DoorsOpen = false;
// do whatever else
}
The room shouldn't be dependent on the Prefab after its been instantiated, so you shouldn't have to worry about room X confusing room Y due to being the same prefab.
In general instead of poll checking values or existence of things to be more efficient you want to make your code as event driven as possible.
I would have a dedicated component on the monsters so you can make all your code event based and track when one is destroyed:
public class Monster : MonoBehaviour
{
// event to be invoked when this monster is destroyed
public UnityEvent<Monster> whenDestroyed;
private void OnDestroy()
{
whenDestroyed.Invoke(this);
}
}
Then in your rooms you could do something like e.g.
public class BasicRoom : MonoBehaviour
{
// the default prefab to spawn, can be different for each room instance
[SerializeField] protected Monster defaultMonsterPrefab;
private readonly HashSet<Monster> monsters = new ();
// event to be invoked when this room is cleared
// per default needs at least one monster to have existed
public UnityEvent whenCleared;
// making this virtual - maybe different rooms do different things
// also adding an optional prefab parameter to allow to overrule the default fallback prefab
public virtual void SpawnMonster(Monster monsterPrefab = null)
{
// if monsterPrefab is not passed in use the defaultMonsterPrefab as fallback instead
if(monsterPrefab == null)
{
monsterPrefab = defaultMonsterPrefab;
}
// create the monster instance from the given prefab
// optionally as a child of he room
var instance = Instantiate(monsterPrefab, transform);
// keep track of the created monsters
monsters.Add(monster);
// and react when it gets destroyed
instance.whenDestroyed.AddListener(OnMonsterDestroyed);
}
// make this virtual - maybe different rooms do different / additional things
protected virtual void OnMonsterDestroyed(Monster monster)
{
monsters.Remove(monster);
CheckAllDestroyed();
}
// again making this virtual - maybe there is a special room that has different condition
// also make this public so you could still check it from somewhere else if needed
public virtual bool CheckIsCleared()
{
if(monsters.Count == 0)
{
whenCleared?.Invoke();
return true;
}
return false;
}
}
Now in the whenAllDestroyed field you can either assign callbacks via the Unity Inspector (like e.g. for Button.onClick) or via code like
someRoom.whenCleared.AddListener(OpenDoor);
By having some methods virtual you could have different room implementations with different behavior. One could e.g. not have monsters at all but simply play some sequence or whatever and automatically call whenCleared after X seconds
I have made my first Unity3D game. It's a racing game with the infinite road. I made some pickup coins too.
the problem is when I pick up the coins and then I go to shop menu to see whether it's added to the total coins or not, it is not added till I play another round and then the coins i collected the round before will be added.
It's like a delay or something.
Is it because I don't know how can I use a value in different scenes? or it,s something else.
someone told me to use PlayerPrefs, I used it in my scripts but for this, I mean the count of coins, I don't know how to use it.
The script bellow is my player script in which I count the pickup coins and some other things related to the player. I only bring the Ontrigger and count related codes.
void Start () {
CameraAnimation = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Animator>();
_GameManager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();
_AudioSource = GetComponent<AudioSource>();
count = 0;
SetCountText ();
}
void OnTriggerEnter(Collider other){
if(other.gameObject.tag == "Coins") {
count = count + 1;
SetCountText ();
}
}
public void SetCountText(){
CountText.text = count.ToString ();
}
The code below is my calculateCoin in which I add a count of collected coins to the previous value and show the total in shop scene textbox:
public class CalculateCoin : MonoBehaviour{
// Use this for initialization
public static int Coin = 10000;
public Text ShowCoin;
void Start () {
ShowCoin.text = PlayerPrefs.GetInt("Coin").ToString();
Coin =PlayerPrefs.GetInt("Coin")+Player.count;
PlayerPrefs.SetInt ("Coin", Coin);
}
}
First Option,
You can add 'DontDestroyOnLoad' for specific GameObject that include data as container. so It could be used as shared data between scene.
or Make 'Singleton' class and use it as data container it might be option.
I am coding a tower defense game in Unity, and I've ran into a snag while trying to figure out a way to place towers. My idea is to be able to click an art asset in the game when the player has a certain amount of points, and it replaces that art asset with a tower. Unfortunately, even when the player has the right amount of points, the object does not instantiate. I have made sure to link the prefab to the script, but it doesn't work. I'm stumped, the logic of the code seems right but maybe someone can help me figure out what's wrong here.
public class PointManager : MonoBehaviour
{
public int pointCount;
public Text pointDisplay;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
pointDisplay.text = "Points: " + pointCount;
}
}
public class PlaceTower: MonoBehaviour
{
public GameObject Tower;
private GameObject firstTower;
int placeCost = 25;
private PointManager pointsGained;
// Start is called before the first frame update
void Start()
{
pointsGained = GameObject.FindGameObjectWithTag("Point").GetComponent<PointManager>();
}
// Update is called once per frame
void Update()
{
}
private void OnMouseDown()
{
if (pointsGained.pointCount >= placeCost)
{
firstTower = Instantiate(Tower, transform.position, Quaternion.identity);
//Destroy(this.gameObject);
}
}
}
I got it. The problem was not with my code, but with my prefabs. In the documentation, I misread that OnMouseDown works on objects with colliders. While I had set up a circle collider on the object I was trying to instantiate, I had failed to put one on the object I was trying to instantiate from. Doing so fixed the problem immediately. A simple mistake that I would have completely glanced over had it not been for a second opinion. Thank you, Pac0!
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class score : MonoBehaviour {
public int ballValue;
public Text scoretext;
// Use this for initialization
void Start () {
ballValue = 0;
}
// Update is called once per frame
void Update () {
scoretext.text = ballValue.ToString();
}
void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject.tag == "bucket")
{
ballValue = ballValue + 1;
}
}
}
ok guys what am i doing wrong over here ,i am a beginner.what i am trying to achieve here is i want my ball to fall down to the bucket and get 1 point or score ,my ball has a actual circle collider and a rigidbody and my bucket has box collider which is a trigger and both of these are prefabs which is being used multiple times in the games just in case if anyone want to know.so can anyone tell me what i am doing wrong hereor can someone guide me to the right tutorial .thank you
(after playing with it for a whilei am able to get 1 point it does not increase and i am getting this error)
Object reference not set to an instance of an object.
and it refers to this line.
void Update () {
scoretext.text = ballValue.ToString();
}
ok guys i just found the real problem,as i said the bucket is a prefab which randomly generating like the pipes in flappy bird, so after creating my core system i drag and drop the text ui into the given place ,and i apply the changes and delete that prefab and when i go back to asset and check that prefab the given place for text says none.so how can i link the text ui directly to the script so it wont dlete it self.
A way to get around this, is having a static class that holds the score and then have your buckets invoke a method in the static class to increase the score.
All you have to do then, is have an empty game object with the static class attached.
After reading and trying many different examples, I am still stuck with this fairly simple problem on getting a score from one level to the next in Unity.
I have a C# script that handles my game logic for a level:
First, I set my level variables:
public class GameLogic : MonoBehaviour {
public GUIText countText;
public GUIText targetCity;
public GUIText gameOverText;
public GUIText endScoreText;
public GUIText timerText;
public Texture2D bgImage;
private int count;
public GameObject cityPrefab;
List<MyCity> mycities;
public float finalScore; // I Want this value to be available when my next scene loads
private float startTime;
After this, my level code executes fine, until the GameOver condition is met (time is up). Then, this code executes:
public void GameOver ()
{
gameOverText.text = "time is up!";
endScoreText.text = "You have found " + count.ToString() + " cities. Good wrok!";
Destroy (GameObject.FindWithTag("EmptyCity"));
Destroy (GameObject.FindWithTag("City"));
Destroy (GameObject.FindWithTag("TargetCity"));
finalScore = count; // SO AT THIS STAGE, MY FINAL SCORE IS SET AND READY TO BE PASSED TO THE NEXT SCENE. QUESTION IS HOW?
StartCoroutine(Wait());
}
IEnumerator Wait() {
yield return new WaitForSeconds(7);
Application.LoadLevel (3);
}
So, I end my level with an updated value in public float 'finalScore'. So far so good. Now my problem starts: Level 3 loads and all gameobjects from level 2 are destroyed. In my game, level 3 is a simple Unity scene where I congratulate the player on his performance. I want to have access to that public float finalScore from my previous scene (level2).
I know I have to use Dontdestroyonload somewhere. But I don't know how. How and where do I create a GameObject that has the public float 'finalScore' in it? How do I call that GameObject in my new level so that I can do something like this in my new level:
public GUIText ContratsOnScore;
void SetContratsText() {
CongratsOnScore = "Congratulations, you scored" + (finalScoreFloatValue from Previous level).ToString();
The DontDestroyOnLoad method is callable on an object, not on a single variable. It represents the best possible approach for a value of that kind, that needs to be "exported" from your actual scene.
However, you can also use PlayerPrefs. They are intended for informations at a "higher" level of persistency, like flags for unlocked levels, final scores for a ranking system, or attributes of a customizable character. That's why the DontDestroyOnLoad way is better suited (for your "temporary" score), but you can use this one also. Basically, PlayerPrefs are information stored in the registry of your operative system.
Before exiting from the actual scene, save the score this way:
PlayerPrefs.SetInt("Player Score", finalScore);
And, when next scene starts, initialize score's variable by retrieving that information:
finalScore = PlayerPrefs.GetInt("Player Score");
(here a similar post)
Someone else said, and I shamelessly quote, use a static variable for your data in a C# class, and there you go...
(Still I'd prefer to pass parameters to LoadLevel itself, but that's another story)