Reputation: 121
Game design : when the animal hit the hitbox, the game will be over. When player click on the animal, player will gain point.
I have 3 scripts which is GameManager, PlayerDataClass, ScoreManager.
The playerDataClass is not attached to any object. the code is as below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class PlayerDataClass
{
public string Name
{
get
{
return Name;
}
set
{
Name = value;
}
}
public int Score
{
get
{
return Score;
}
set
{
Score = value;
}
}
public DateTime DateOfPlaying
{
get
{
return DateOfPlaying;
}
set
{
DateOfPlaying = value;
}
}
// constructor
public PlayerDataClass(string playerName, int playerScore)
{
Name = playerName;
Score = playerScore;
DateOfPlaying = DateTime.Today; // set the date to be current date
}
}
I have attached the ScoreManager(Script) to the ScoreManager(GameObject). The aim of this code is to show player score. The code is as below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
public class ScoreManager : MonoBehaviour
{
// create a reference based on PlayerDataClass
public List<PlayerDataClass> testPlayer;
void Start()
{
//cannot call showscore and setscore or not game will crush
}
void Init()
{
if (testPlayer != null)
{
return;
}
// create the list of playerdataclass
testPlayer = new List<PlayerDataClass>();
testPlayer.Add(new PlayerDataClass("Harvey", 50)); //testing purpose
}
// used to show all the player score in the list
public void ShowScore()
{
Init();
// only call if the list is not equal to null
if(testPlayer != null)
{
// loop through the list
for (int i = 0; i <= testPlayer.Count; i++)
{
print(testPlayer[i].Name);
print(testPlayer[i].Score);
print(testPlayer[i].DateOfPlaying.ToString("dd/MM/yyyy"));
}
}
else
{
print("the list is empty");
}
}
// used to new player into the list
public void SetScore(string playName, int playScore)
{
Init();
// add new player with new score into the list
testPlayer.Add(new PlayerDataClass(playName, playScore));
}
void Update()
{
//cannot call showscore and setscore or not game will crush
}
}
GameManager(Script) is attached to the GameManager(object). GameManager(Script) has boolen gameOver, which will pop out a game over menu to the user when the game is over. I will show the top 5 players result in this menu.
There is no Update() in the GameManager(Script).
GameManager(Script) is as below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class GameManager : MonoBehaviour
{
public static GameManager instance;
[HideInInspector]
public List<GameObject> animalList = new List<GameObject>();
// create player score list array to display
// public List<PlayerDataClass> playerScoreList = new List<PlayerDataClass>();
private int playerScore;
// use to pause the game
private bool gamePause = false;
private bool gameOver = false;
public TextMeshProUGUI scoreText;
public AnimalSpawnner spawner;
public GameObject gameOverPanel;
public Button restartButton;
// use to show individual score inside the score panel
public TextMeshProUGUI showScoreText;
// show score panel for individual
public GameObject scorePanel;
// public PlayerDataClass testPlayerPlayerData;
// public ScoreManager testPlayer;
// public GameObject scoreManager;
public int PlayerScore
{
get
{
return playerScore;
}
set
{
playerScore = value;
scoreText.SetText("Score: " + playerScore);
}
}
public bool GameOver
{
get
{
return gameOver;
}
set
{
gameOver = value;
if (gameOver)
{
gameOverPanel.SetActive(true);
spawner.enabled = false;
// playerScoreList.Add(new PlayerDataClass("James", 123));
/*
for(int i = 0; i < playerScoreList.Count; i++)
{
print("my name is " + playerScoreList[i].Name);
print("my age is " + playerScoreList[i].Score);
}
*/
for (int i = 0; i < animalList.Count; i++)
{
Destroy(animalList[i]);
}
}
else
{
gameOverPanel.SetActive(false);
spawner.enabled = true;
// Destroy(testPlayer);
//destroy score manager
}
}
}
public bool GamePause
{
get
{
return gamePause;
}
set
{
gamePause = value;
// only pause game when it is not gameOver
if(gameOver == false)
{
if (gamePause)
{
spawner.enabled = false;
// if the game is pause, set spawned gameobject to be deactivate
for (int i = 0; i < animalList.Count; i++)
{
animalList[i].SetActive(false);
}
}
else
{
scorePanel.SetActive(false);
for (int i = 0; i < animalList.Count; i++)
{
animalList[i].SetActive(true);
}
spawner.enabled = true;
}
}
}
}
void Awake()
{
if (instance != null)
{
return;
}
instance = this;
scoreText.SetText("Score: " + playerScore);
restartButton.onClick.AddListener(OnClickRestart);
}
private void OnClickRestart()
{
GameOver = false;
}
}
The animal has OnTriggerEnter2D function, which will trigger boolean gameOver when the animal hit the hitbox. I tried to called ShowScore() function from ScoreManager in this part, the game crushed.
I failed to show the player score when the game over menu pop out. I would like to ask, how should i implement my scoreManager function so that i can show the player score when the game over menu pop out ? Thank you.
Upvotes: 0
Views: 45
Reputation: 90724
The problem are your Properties in PlayerDataClass
!!
Have a closer look on what happens here:
public string Name
{
get
{
return Name;
}
set
{
Name = value;
}
}
public int Score
{
get
{
return Score;
}
set
{
Score = value;
}
}
public DateTime DateOfPlaying
{
get
{
return DateOfPlaying;
}
set
{
DateOfPlaying = value;
}
}
Every time you read a properties value its getter is called; every time you assign a value to it its setter is called. Even then if this reading or assigning happens within these getter or setter itself.
Lets e.g. say you want to e.g. assign a value to Name
.
So what happens is
set
{
Name = value;
}
where again you assign a value to the property Name
so it calls the setter of this property over and over again in an recursive endless loop! Basically what you did here equals a method call like
public void SetName(string value)
{
SetName(value);
}
The same would happen for reading any of these properties since e.g.
get
{
return Name;
}
equals a method like
public string GetName()
{
return GetName();
}
The app crushes because you do this already in the constructor of PlayerDataClass
so it doesn't matter in which method .. as soon as you call any new PlayerDataClass(x, y)
the constructor causes an endless loop!
In your case you actually don't need any properties, you could instad just make it simple fields:
public class PlayerDataClass
{
public string Name;
public int Score;
public DateTime DateOfPlaying;
// constructor
public PlayerDataClass(string playerName, int playerScore)
{
Name = playerName;
Score = playerScore;
DateOfPlaying = DateTime.Today; // set the date to be current date
}
}
Using properties actually makes only sense if you a) want to implement some special behavior like value validation in the getter and/or setter or b) want to limit the access by e.g. only allow reading of a public propertie but assign the value only private. In all other cases you should rather simply use fields.
or if you want to use properties use either autoproperties (I'll use examples for show the limitations of access)
public class PlayerDataClass
{
// this one can be assigned from any class also afterwards
public string Name {get; set; }
// This one can be assigned afterwards but only from this class
// e.g. via a method, it is read-only for any other class
public int Score {get; private set; }
// This one can only be assigned in the constructor of this class
// and not be changed by anyone afterwards
// it is read-only for any other class and this one except for the constructor
public DateTime DateOfPlaying {get; }
// constructor
public PlayerDataClass(string playerName, int playerScore)
{
Name = playerName;
Score = playerScore;
DateOfPlaying = DateTime.Today; // set the date to be current date
}
}
or use properties with backing field like (same access limitation examples as before)
public class PlayerDataClass
{
private string name;
private int score;
private DateTime dateOfPlaying;
public string Name
{
get { return name; }
set { name = value; }
}
public int Score
{
get { return score; }
private set { score = value; }
}
public DateTime DateOfPlaying
{
get { return dateOfPlaying; }
}
// constructor
public PlayerDataClass(string playerName, int playerScore)
{
name = playerName;
score = playerScore;
dateOfPlaying = DateTime.Today; // set the date to be current date
}
}
Upvotes: 0