Insert player name with new high score - c#

I'm creating a game with a scoring system, but at the moment the players' names are not included with their high score. The idea is that the player is able to add their name once the game over screen appears and provided they have achieved a new high score.
Initially the system would only save one high score, but now 5 high scores are saved which will be called within a table elsewhere, which is where I would also like the player name to be displayed.
I'm not that familiar with C# so do not know how to incorporate this sort of user input, so I'm happy to hear the available options.
This is my code for the score system:
public class ScoreManager : MonoBehaviour
{
public Text scoreText;
public Text hiScoreText;
public Text gameOverScoreText;
public float scoreCount;
public float hiScoreCount;
public float distance;
public float pointsPerSecond;
public bool scoreIncreasing;
//create array for last 5 high scores
float[] highScores = new float[5];
//create list for scores
public List<float> ScoreHistory;
// Start is called before the first frame update
void Start()
{
//if there is a high score, the game will register it, otherwise it will be 0 - this is for one score
/*if (PlayerPrefs.HasKey("HighScore"))
{
hiScoreCount = PlayerPrefs.GetFloat("HighScore");
}*/
//obtain last 5 scores
ScoreHistory = LoadScoresFromPlayerPrefs(5);
// Set the current high score to be the highest score in the player history - if there is no high score saved, high score will be 0
if (ScoreHistory.Count > 0)
{
hiScoreCount = ScoreHistory[0];
}
else
{
hiScoreCount = 0;
}
}
// Update is called once per frame
void Update()
{
if (scoreIncreasing)
{
//adding points every frame/update => but shows decimals
scoreCount += pointsPerSecond * Time.deltaTime;
//scoreCount += Vector3.Distance(transform.position, camera.position);
}
if(scoreCount > hiScoreCount)
{
hiScoreCount = scoreCount;
//saves value called high score and the hiScoreCount value - not used if saving more than one score
//PlayerPrefs.SetFloat("HighScore", hiScoreCount);
}
//adding text to score
//Mathf.Round rounds scoreCount and hiScoreCount to nearest whole
scoreText.text = "Score: " + Mathf.Round(scoreCount);
hiScoreText.text = "High Score: " + Mathf.Round(hiScoreCount);
gameOverScoreText.text = "Your Score: " + Mathf.Round(scoreCount);
}
//function which needs a number value to take in - can be used more than once
public void AddScore(int pointsToAdd)
{
//adding points to score
scoreCount += pointsToAdd;
}
// Save the current score to the list of scores, and then write them to the player prefs
public void SaveCurrentScore()
{
ScoreHistory.Add(scoreCount);
//put scores in order
ScoreHistory.Sort();
for (int i = 0; i< ScoreHistory.Count; i++)
{
//key is the name of the value being stored
var key = "High Score " + i;
//value is what is being stored (i.e. the high scores) => ScoreHistory is being used as each score is being saved
var value = ScoreHistory[i];
//high scores are being saved using PlayerPrefs
PlayerPrefs.SetFloat(key, value);
}
}
// Loads the scores from the player prefs and returns them in a list of floats
private List<float> LoadScoresFromPlayerPrefs(int maximumNumberOfScoresToLoad)
{
//no visibility modifiers - this is a local variable
List<float> LoadScores = new List<float>();
//loop will run once per score
for (int i = 0; i < maximumNumberOfScoresToLoad; i++)
{
//key is the name of the value being stored
var key = "High Scores " + i;
//will return value of the key if there is one, otherwise will return default value of 0.0
//PlayerPrefs.GetFloat(key);
var score = PlayerPrefs.GetFloat(key);
LoadScores.Add(score);
}
return LoadScores;
}
}

If you want the user to enter a name you'll either have to use
Unity's UI system (e.g. input field) or
code something yourself (e.g. observing Input.inputString after finish), which I would only recommend if you have something like old arcades, allowing only 3 characters.
In both cases you need to be able to detect the end of your user's input with a detectable submit (e.g. return key, or a dedicated UI button). Afterwards you can just store it with the respective points.
To bind the points and name together just add a struct containing both
public struct ScoreData {
public float Score { get; set; }
public string Name { get; set; }
}
and use it for your ScoreHistory
public List<ScoreData> ScoreHistory;
private string playerName; // store the name input by player here
...
public void SaveCurrentScore()
{
ScoreHistory.Add(new ScoreData { Score = scoreCount, Name = playerName });
//put scores in order
ScoreHistory = ScoreHistory.OrderBy(s => s.Score).ToList();
for (int i = 0; i < ScoreHistory.Count; i++)
{
//key is the name of the value being stored
var scoreKey = "High Score " + i;
var nameKey = "High Score Playername " + i;
//value is what is being stored (i.e. the high scores) => ScoreHistory is being used as each score is being saved
var value = ScoreHistory[i];
//high scores are being saved using PlayerPrefs
PlayerPrefs.SetFloat(scoreKey, value.Score);
PlayerPrefs.SetString(nameKey, value.Name);
}
}

Related

When trying to display a hand of cards from a shuffled deck, it fills every hand with the Ace of Clubs

This is my first time using C#. I have to convert an old project from Java. The old project works, but when I attempted to convert it, something went wrong? The Card class sets the default card to the Ace of Spades, but it is displaying Ace of Clubs. Ace and Clubs are each the first listed of the enums so I guess it is not using the default?
Since it is displaying the same card I thought it would be something wrong with my shuffle method..but I am unsure at this point.
There is a Card, Deck, and Hand class. Then Enums for Suit and Face.
UPDATE: I believe the error is in the method directly below? I need to figure out how to use the 'n' in c#. It is needed in a different class.
In Java I had it as:
public Card(int n)
{
face = Face.values()[n % 13];
suit = Suit.values()[n % 4];
} //end Card (int n) method
c#:
public Card(int n) //??
{
var face = (Face) 13;
var suit = (Suit) 4;
}
The code above is in the Card class? I know that is not too helpful, but the only other way it to post ALL of the code here.
//Some of the Deck class
public void Shuffle()
{
Random ran = new Random();
for (int nextCard = 0; nextCard < deck.Length; nextCard++)
{
Card hold = deck[nextCard];
int random = ran.Next(deck.Length);
deck[nextCard] = deck[random];
deck[random] = hold;
}
}
public Card DealACard()
{
if (nextCard > 51)
{
return null;
}
return deck[nextCard++];
}
public Hand DealAHand(int handSize)
{
Hand hand = new Hand(handSize);
for (int i = 0; i < handSize; i++)
{
hand.AddCard(DealACard());
}
return hand;
}
//Some of the Hand Class
public void AddCard(Card card)
{
hand[cardsInHand] = card;
cardsInHand++;
}
public override string ToString()
{
String handToString = ""; //string to hold display format
//for loop to display each card in a hand
for (int n = 0; n < cardsInHand; n++)
{
handToString += hand[n].ToString() + "\n";
}
return handToString;
}
// Driver Class
Deck deck1 = new Deck();
int cardsToGet = 53;
do
{
Console.Write("How many cards are in one hand? ");
int handSize = Convert.ToInt32(Console.ReadLine());
// int handSize = Console.Read();
Console.Write("How many players are playing? ");
int players = Convert.ToInt32(Console.ReadLine());
cardsToGet = handSize * players;
if (cardsToGet < 53) // if to shuffle deck and display players' hands
{
deck1.Shuffle();
for (int i = 0; i < players; i++) // displays each players hand
{
Console.WriteLine("\nPlayer " + (i + 1) + ":");
Console.WriteLine(deck1.DealAHand(handSize));
}
}
else
{
Console.WriteLine("\nThere are not enough cards in the deck to deal " + players + " hands of " + handSize + " cards. Try again.\n");
}
}
while (cardsToGet > 52);
It is suppose to ask for a number of cards per hand and a number of players then displays a hand for each player without duplicating cards. Currently, it fills every players hand with Ace of Clubs. There are no errors showing.
Now that you've updated your question, this is answerable. The mistake is in your Card constructor, which no matter the value of n that you pass in, creates every card with the Face with value 13 and the Suit with value 4. You have the right method for turning an int into a Face or Suit enum (just cast it), so you just need to do the modulo operation like your Java version did:
public Card(int n)
{
var face = (Face) n % 13;
var suit = (Suit) n % 4;
}
Well, almost. The var keyword in C# just creates a local variable that's not visible outside the scope it's declared in: in this case, the constructor. What you meant to do is assign values to instance properties of your Card class. You haven't shown us what those properties are named, but I'm going to assume they're named Face and Suit (with initial uppercase as is C# naming convention); rename as appropriate:
public Card(int n)
{
this.Face = (Face) n % 13;
this.Suit = (Suit) n % 4;
}
And now your cards should all be different, rather than all being the ace of clubs.
Hello TJ and welcome to S/O, coming from other languages can be a learning curve. Same as if I switched to Java. I quickly threw this together to introduce working with lists and classes within C#. Not perfect, but does what you are looking to get at. Hope it helps. I originally wrote this with C# via a WPF application vs Console app, but the core is all the same (only thing that would be different is the "MessageBox.Show()" that shows each hand vs the Console output which I also included.
I am notorious for commenting through my code and hope this all (or most of it) makes sense as you dive into C#...
I'm starting with enums for the card faces and suits.
public enum CardFace
{
Two = 0,
Three = 1,
Four = 2,
Five = 3,
Six = 4,
Seven = 5,
Eight = 6,
Nine = 7,
Ten = 8,
Jack = 9,
Queen = 10,
King = 11,
Ace = 12
}
public enum CardSuit
{
Hearts = 0,
Clubs = 1,
Diamonds = 2,
Spades = 3
}
Next, a class to represent a single card
public class SingleCard
{
public CardFace Face { get; set; }
public CardSuit Suit { get; set; }
// place-holder for randomizing cards
public int RndNumber { get; set; }
// return the name of the card based on it's parts as single string
public string NameOfCard { get { return $"{Face} of {Suit}"; } }
}
Now, the class for building the initial deck of cards, shuffling, dealing and displaying the cards (of the entire deck, or of the individual hands)
public class DeckOfCards
{
public List<SingleCard> SingleDeck { get; private set; } = new List<SingleCard>();
public List<SingleCard> ShuffledDeck { get; private set; }
// create a single random generator ONCE and leave active. This to help prevent
// recreating every time you need to shuffle and getting the same sequences.
// make static in case you want multiple decks, they keep using the same randomizing object
private static Random rndGen = new Random();
public DeckOfCards()
{
// build the deck of cards once...
// Start going through each suit
foreach (CardSuit s in typeof(CardSuit).GetEnumValues())
{
// now go through each card within each suit
foreach (CardFace f in typeof(CardFace).GetEnumValues())
// Now, add a card to the deck of the suite / face card
SingleDeck.Add(new SingleCard { Face = f, Suit = s });
}
// so now you have a master list of all cards in your deck declared once...
}
public void ShuffleDeck()
{
// to shuffle a deck, assign the next random number sequentially to the deck.
// don't just do random of 52 cards, but other to prevent duplicate numbers
// from possibly coming in
foreach (var oneCard in SingleDeck)
oneCard.RndNumber = rndGen.Next(3901); // any number could be used...
// great, now every card has a randomized number assigned.
// return the list sorted by that random number...
ShuffledDeck = SingleDeck.OrderBy( o => o.RndNumber).ToList();
}
public void DisplayTheCards( List<SingleCard> theCards )
{
// show the deck of cards, or a single person's hand of cards
var sb = new StringBuilder();
foreach (var c in theCards)
sb = sb.AppendLine( c.NameOfCard );
MessageBox.Show(sb.ToString());
}
public void ConsoleDisplayTheCards(List<SingleCard> theCards)
{
// show the deck of cards, or a single person's hand of cards
foreach (var c in theCards)
Console.WriteLine(c.NameOfCard);
}
public List<List<SingleCard>> DealHands( int Players, int CardsPerHand )
{
// create a list of how many hands to be dealt...
// each player hand will consist of a list of cards
var Hands = new List<List<SingleCard>>(Players);
// prepare every players hand before dealing cards
for (var curPlayer = 0; curPlayer < Players; curPlayer++)
// each player gets their own list of cards
Hands.Add( new List<SingleCard>());
// prepare card sequence to deal
var nextCard = 0;
// loop for as many cards per hand
for (var oneCard = 0; oneCard < CardsPerHand; oneCard++)
{
// loop every player gets a card at a time vs one player gets all, then next player
for (var curPlayer = 0; curPlayer < Players; curPlayer++)
// add whatever the next card is to each individual's hand
Hands[curPlayer].Add(ShuffledDeck[nextCard++]);
}
return Hands;
}
}
I did not include the input of getting how many players, how many cards as you already had that. I also did not validate total cards to be dealt as you had that too. HTH

C# WFA - How to make a string list sort in the same positions as a sorted integer list

I am trying to make a high score system for a quiz game I have made. I have got all the scores and their names to read in, sort the scores and put them in rich text boxes (one for scores, one for their names). After I used .Sort() on the integer list (scores), the scores were in the correct order but the names (string list) no longer matched up with the scores.
Here is my code:
public partial class frmhighScore : Form
{
public frmhighScore()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
this.Name = "High Score";
}
string[] contains;
string[] scorenames;
List<int> scores = new List<int>(){ };
List<string> names = new List<string>(){ };
private void highScore_Load(object sender, EventArgs e)
{
scores.Clear();
names.Clear();
scorenames = File.ReadAllLines(AppDomain.CurrentDomain.BaseDirectory + "scorenames.txt");
foreach (string line in scorenames)
{
gameClass.scorenames.Add(line);
}
for (int x = 0; x < gameClass.scorenames.Count(); x++)
{
contains = gameClass.scorenames[x].Split(':');
names.Add(contains[0]);
scores.Add(Convert.ToInt32(contains[1]));
}
scores.Sort();
scores.Reverse();
for (int a = 0; a < scores.Count; a++)
{
}
for (int y = 0; y < names.Count(); y++)
{
richTextBox1.Text += names[y];
richTextBox1.Text += Environment.NewLine;
}
for (int z = 0; z < scores.Count(); z++)
{
richTextBox2.Text += scores[z];
richTextBox2.Text += Environment.NewLine;
}
}
}
gameClass.scorenames is a string list in my class which is used to read in the details from the text file. All ofther variables are local.
richTextBox1 is for the names and richTextBox2 is for the scores
Here is a screenshot of what the form currently looks like:
Current high score form
And here is the text file that I am reading in from (scorenames.txt):
r:6
bob:10
So as you can see, the names are not matched up with the sorted scores
So my final question is, how would I make it so that the names (bob / r) match up with their scores r is 4, bob is 10?
Any help would be greatly appreciated, thanks.
I think you just need to re model your display entity to bundle both Name and Score together. You might need to change your code somewhat similar to below snippet (I haven't taken care of new line format though)
public class DisplayCard
{
public int Score { get; set; }
public string Name { get; set; }
}
List<DisplayCard> ScoreCard = new List<DisplayCard>();
for (int x = 0; x < gameClass.scorenames.Count(); x++)
{
contains = gameClass.scorenames[x].Split(':');
var name = contains[0];
var score = Convert.ToInt32(contains[1]);
ScoreCard.Add(new DisplayCard { Name = name, Score = score });
}
var sortedCard = ScoreCard.OrderBy(o => o.Score).ToList();
foreach (var card in sortedCard)
{
richTextBox1.Text += card.Name;
richTextBox2.Text += card.Score;
/* take care of new line logic*/
}
You might create objects for each player which includes their score. Then use LINQ to sort them by the score, which will then retain the association with their name: https://msdn.microsoft.com/en-us/library/bb534966.aspx

How to prevent same object from being picked from array twice in a row

I am having a bit of a problem figuring out how to randomly get an object out of a list that wasn't picked on the last update of a script. When this randomly instantiated object is spawned and reaches a certain y value, it will set itself to inactive. So when this object is not active, it will go through an array and pick another object at random. However, I do not want to include the previous active object.
example: blue ball was first active. Moves on the Y axis. Becomes inactive. Next object spawn should have no chance of being a blue ball. Any ideas and help will be greatly appreciated.
My code is below
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ballGeneratorShooter : MonoBehaviour
{
private int ballSelector;
private TagNumber theTagNumber;
public ObjectPooler[] theObjectballPools;//In my inspector, I have 5 prefab gameObjects attached
List<ObjectPooler> changingBalls;
public bool changeBalls;
public GameObject currentBall;
public GameObject newBall;
// Use this for initialization
void Start()
{
changingBalls = new List<ObjectPooler>();
currentBall = newBall;
}
// Update is called once per frame
void Update()
{
if (newBall == null)
{
ballSelector = Random.Range(0, theObjectballPools.Length);
newBall = theObjectballPools[ballSelector].GetPooledObject();
newBall.transform.position = transform.position;
newBall.transform.rotation = transform.rotation;
newBall.SetActive(true);
}
if (newBall.activeInHierarchy == false)
{
if (changeBalls)
{
for (int i = 0; i < theObjectballPools.Length; i++)
{
if (theObjectballPools[i].GetPooledObject().GetComponent<TagNumber>().tag != currentBall.GetComponent<TagNumber>().tag)
{
changingBalls.Add(theObjectballPools[i]);
}
//changingballs.Add(theObjectballPools[i]);
}
ballSelector = Random.Range(0, changingBalls.Count);
newBall = theObjectballPools[ballSelector].GetPooledObject();
Debug.Log(changingBalls.Count);
newBall.transform.position = transform.position;
newBall.transform.rotation = transform.rotation;
newBall.SetActive(true);
currentBall = newBall;
changeBalls = false;
changingBalls.Clear();
}
}
}
}
You need to store the random number to variable (lastRandomNum) each time you generate a random number. Now, use the function below that can generate a random number with exclusion.
int RandomWithExclusion(int min, int max, int exclusion)
{
var result = UnityEngine.Random.Range(min, max - 1);
return (result < exclusion) ? result : result + 1;
}
You pass in 0 to min, then theObjectballPools.Length or changingBalls.Count to the max, then finally, lastRandomNum value to the exclusion parameter.
You also need a boolean variable to determine if this is the first run. If this is the first run, use the Unity's Random.Range function then set the firstRun to false. If it is not the firstRun, use the RandomWithExclusion function from this answer and pass in the lastRandomNum value to exclude it. Also, store the generated random number to the lastRandomNum variable to be used next frame.
Below is a simplified version of what I said above. You have to incorporate that to your code.
GameObject[] yourItem = null;
bool firstRun = true;
int lastRandomNum = 0;
void Update()
{
if (firstRun)
{
firstRun = false;
//First run, use Random.Range
lastRandomNum = UnityEngine.Random.Range(0, yourItem.Length);
}
else
{
//Not First run, use RandomWithExclusion
lastRandomNum = RandomWithExclusion(0, yourItem.Length, lastRandomNum);
}
//Do something with the lastRandomNum value below
newBall = theObjectballPools[lastRandomNum].GetPooledObject();
}
int RandomWithExclusion(int min, int max, int exclusion)
{
var result = UnityEngine.Random.Range(min, max - 1);
return (result < exclusion) ? result : result + 1;
}
Try linq :
public class Ball
{
public static List<Ball> balls = new List<Ball>();
public int value { get; set; }
public Boolean active { get; set; }
public Ball() {}
public Ball(int size)
{
// initial class
Random rand = new Random();
for (int i = 0; i < size; i++)
{
balls.Add(new Ball(){ value = rand.Next(), active = false});
}
}
public Ball GetRandom()
{
Random rand = new Random();
Ball randomBall = balls.Where(x => x.active == false).Select((x) => new { value = x, randX = rand.Next() }).OrderBy(x => x.randX).FirstOrDefault().value;
randomBall.active = true;
return randomBall;
}
}

How to save my high scores to a text file unity

I have created a streamreader and streamwriter to load and create the file. But im having trouble iterating through the list of the top five high scores to update the high scores when a new one is achieved.
public int highscore;
public static int score;
//public new string name;
private string initials;
public List<int> highscores;
//public List<Person> highscores;
Text HighScoreText;
void Awake()
{
HighScoreText = GetComponent<Text>();
score = 0;
highscores = new List<int>();
score = PlayerPrefs.GetInt("Score",0);
highscore = PlayerPrefs.GetInt("HighScore", 0); ;
LoadScore();
UpdateScore();
}
void UpdateScore()
{
HighScoreText.text = "HighScore:" + score;
//Here I get an error cannot apply indexing with type int
if (score > highscores[4].s)
{
highscores.Add(score);
highscores.Sort();
highscore = score;
HighScoreText.text = highscore.ToString(); ;
}
}
You declared highscores as a list of int: public List<int> highscores;
Then you are checking if (score > highscores[4].s) { ... } what is the .s for? You don't need it at all. Just check if (score > highscores[4]) and your code shall compile normally.
Also, after compiling, you would want to check your logic by debugging your code statement by statement. Using F-10 step-over option if you are using Visual Studio.

XNA sorting score from array

I want to create a highscore board for my game.
the score board contain the top 5 scores in the text file
the text file are something like this:
alpha, 3500
beta, 3600
gamma, 2200
delta, 3400
epsilon, 2000
and this is my codes :
[Serializable]
public struct HighScoreData
{
public string[] PlayerName;
public int[] Score;
public int Count;
public HighScoreData(int count)
{
PlayerName = new string[count];
Score = new int[count];
Count = count;
}
}
static HighScoreData highScores;
this codes for reading data from text file and already add sorting in it:
try
{
using (StreamReader sr = new StreamReader("highscore.txt"))
{
string line;
int i = 0;
//file = new StreamReader(filePath);
while ((line = sr.ReadLine()) != null)
{
string[] parts = line.Split(',');
highScores.PlayerName[i] = parts[0].Trim();
highScores.Score[i] = Int32.Parse(parts[1].Trim());
i++;
Array.Sort(highScores.Score);
}
}
}
this is how I draw it :
for (int i = 0; i < 5; i++)
{
spriteBatch.DrawString(spriteFont, i + 1 + ". " + highScores.PlayerName[i].ToString()
, new Vector2(200, 150 + 50 * (i)), Color.Red);
spriteBatch.DrawString(spriteFont, highScores.Score[i].ToString(),
new Vector2(550, 150 + 50 * (i)), Color.Red);
}
the problem is when I run the game, it only sorting the scores and not the player name. and also, the first and secod scores in text file are identified as "0". it displayed like this :
alpha 0
beta 0
gamma 2000
delta 2200
epsilon 3400
what must I do, so the program can sorting all the data in the text file, not only the score...?
Another option without comparers using LINQ based on Blau's sample:
struct PlayerScore
{
public string Player;
public int Score;
public int DataYouWant;
}
then a sample of populating the list and sorting it:
List<PlayerScore> scores = new List<PlayerScore>();
Random rand = new Random();
for (int i = 0; i < 10; i++)
{
scores.Add(new PlayerScore()
{
Player = "Player" + i,
Score = rand.Next(1,1000)
});
}
scores = (from s in scores orderby s.Score descending select s).ToList();
foreach (var score in scores)
{
Debug.WriteLine("Player: {0}, Score: {1}", score.Player, score.Score);
}
Make a struct named PlayerScore
struct PlayerScore
{
public string Player;
public int Score;
public int DataYouWant;
public static int Compare(PlayerScore A, PlayerScore B)
{
return A.Score - B.Score;
}
}
and then to Sort call only one time, (outside the while) to the sort method this way:
Array.Sort<PlayerScore>( yourArray, PlayerScore.Compare );
Do you really need to have more than HighScoreData instance? I think that no.. so you store your highscores this way:
static PlayerScore[] highScores = new PlayerScore[MaxHighScorePlayers];

Categories

Resources