Unity - Leaderboard data is delayed when showing up - c#

I'm a beginner to coding. A friend of mine used to help me but now he's busy so I have to do things alone. The game has a function that remember the name the player input before playing the game as well as the score of the player. Right now, my problem is that when I play a game, it doesn't show up in my leaderboard scene. And then when I play another round with a different name, I can see the previous score but not my current one. It's like the data is delayed to appear.
void ReplaceRank(string player, int currentScore) {
string oldName;
int oldScoreNumber;
for (int i = 0; i < 10; i++) {
if (currentScore > scores[i]) {
highscoreShow.enabled = true;
oldScoreNumber = scores[i];
oldName = PlayerPrefs.GetString(oldPlayerName[i]);
PlayerPrefs.SetInt(oldScore[i], currentScore);
PlayerPrefs.SetString(oldPlayerName[i], player);
Debug.Log(currentScore);
if (i <= 9) {
ReplaceRank(oldName, oldScoreNumber);
}
break;
}
}
}
private void GetAllKeys() {
oldScore = new List<string>();
oldPlayerName = new List<string>();
scores = new List<int>();
for (int i = 11; i < 21; i++) {
if (PlayerPrefs.GetString("score" + i + "Name", "") == "") {
PlayerPrefs.SetString("score" + i + "Name", "");
}
oldPlayerName.Add("score" + i + "Name");
oldScore.Add("score" + i);
scores.Add(PlayerPrefs.GetInt("score" + i, 0));
}
}

Even though this seems trivial its not , saving is kinda tricky, they are a few things you can do to make it easier e.g you should save the scores as a list
Saving
add scores to list
convert list of scores to Json
save json string in player prefs
Loading
get json string from player prefs
convert Json to list
updated list in script
wrote this class quickly.. its tested and works but you might have to modify it to suit your needs and improve it
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace ScoreBoard
{
[Serializable]
public class PlayerScore
{
public string Name;
public int Score;
public PlayerScore(string name, int score)
{
Name = name;
Score = score;
}
}
[Serializable]
// we need to store list of scores in a container class so we can change it to json
public class PlayerScoresRankedListContainer
{
public List<PlayerScore> PlayerScoresRanked = new List<PlayerScore>();
}
[Serializable]
public class ScoresRanked : MonoBehaviour
{
public static PlayerScoresRankedListContainer PlayerScoresListContainer =new PlayerScoresRankedListContainer();
public void Awake()
{
//example of usage
//get saved items
if (PlayerPrefs.GetString("PlayerScoresRanked").Length > 0)
{
PlayerScoresListContainer.PlayerScoresRanked = GetSortedListFromPlayerPrefs();
DebugShowScores();
}
else
{
//test the class asving items
AddScoreToRankedList("player1", 1000);
AddScoreToRankedList("player2", 20);
AddScoreToRankedList("player3", 100);
SaveListAsJSONInPlayerPrefs();
}
}
private void AddScoreToRankedList(string player, int currentScore)
{
var score = new PlayerScore(player, currentScore);
if (DoesScoreAlreadyExist(score))
{
//we remove a score if it already exists so we can updated it
//you might not need this maybe you just want to keep adding scores
PlayerScoresListContainer.PlayerScoresRanked.RemoveAll(x => x.Name == score.Name);
PlayerScoresListContainer.PlayerScoresRanked.Add(score);
}
else
{
PlayerScoresListContainer.PlayerScoresRanked.Add(score);
}
}
public void SaveListAsJSONInPlayerPrefs()
{
var jsonlist = JsonUtility.ToJson(PlayerScoresListContainer);
Debug.Log("LOG ITEMS BEING SAVED: "+jsonlist);
PlayerPrefs.SetString("PlayerScoresRanked", jsonlist);
}
public List<PlayerScore> GetSortedListFromPlayerPrefs()
{
var jsonlist = PlayerPrefs.GetString("PlayerScoresRanked");
var ListContainer = JsonUtility.FromJson<PlayerScoresRankedListContainer>(jsonlist);
var listsorted = ListContainer.PlayerScoresRanked.OrderByDescending(x => x.Score).ToList();
return listsorted;
}
public bool DoesScoreAlreadyExist(PlayerScore scoreToChcek)
{
if (PlayerScoresListContainer.PlayerScoresRanked.Exists(x => x.Name == scoreToChcek.Name))
{
return true;
}
else
{
return false;
}
}
public void DebugShowScores()
{
foreach (var playerScore in PlayerScoresListContainer.PlayerScoresRanked)
{
Debug.Log(playerScore.Name + " " + playerScore.Score);
}
}
}
}

Related

Tmodloader Custom NPC Cannot be Spawned

I've made an NPC in Tmodloder, a little girl known as the florist, and I have the mod HERO's MOD installed, it shows up in the npc spawn list, yet not as a town NPC, and also won't spawn when clicked, unlike all other NPCs and entities in the game
These are my Files:
Florist.ini
[Stats]
frameCount=26
animationType=0
aiStyle=7
height=40
width=20
damage=0
defense=2
lifeMax=250
scale=1
soundHit=1
soundKilled=1
type=-1
knockBackResist=.1
townNPC=True
friendly=True
Florist.cs
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.Audio;
using Terraria.DataStructures;
using Terraria.GameContent.Creative;
using Terraria.ID;
using Terraria.ModLoader;
namespace ElanTown.NPCs
{
public class Florist : ModNPC
{
public static bool TownSpawn() {
return true;
}
public static string SetName() {
if(Main.rand.Next(2)==0){
return "Sophie";
}
else
{
return "Amy";
}
}
public static string Chat() {
int result = Main.rand.Next(3);
string text = "";
if (result == 0)
{
text = "I Asked {Name of Guide} for a bow, this isn't what I expected.";
}
else if (result == 1)
{
text = "Hey there! Would You Like to Buy some Flowers?";
}
else if (result == 2)
{
text = "Would You like to buy some seeds?";
}
return text;
}
public static void SetupShop(Chest chest) {
int index=0;
chest.item[index].SetDefaults(309);
index++;
chest.item[index].SetDefaults(307);
index++;
chest.item[index].SetDefaults(310);
index++;
chest.item[index].SetDefaults(312);
index++;
chest.item[index].SetDefaults(308);
index++;
chest.item[index].SetDefaults(2357);
index++;
chest.item[index].SetDefaults(311);
index++;
}
}
}
Florist.png
Florist Head.png
If anyone was wondering, the chest.item[index].SetDefaults({int}); in Florist.cs was the internal item IDs of all the herb seeds

Pubnub null reference exception in unity

I am trying to get my leaderboard working in unity using pubnub. I tried to follow and alter this tutorial but when I run it in unity, nothing shows up:
In my code, the only thing that's different from the tutorial is the publish key and subscribe key, where I set to my own one.
this is the screenshot of the keys:
this is the screenshot of the storage and playback setting:
this is the screenshot of the pubnub function setting, it says "it's automatically set to on when a module is running":
this is the screenshot of the only module in the app:
this is my code, nothing is print to the console when I tried to set usernames and scores(see comment below):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using PubNubAPI;
using UnityEngine.UI;
using SimpleJSON;
public class MyClass
{
public string username;
public string score;
public string refresh;
}
public class leaderboard : MonoBehaviour
{
public static PubNub pubnub;
public Text Line1;
public Text Line2;
public Text Line3;
public Text Line4;
public Text Line5;
public Text Score1;
public Text Score2;
public Text Score3;
public Text Score4;
public Text Score5;
public Button SubmitButton;
public InputField FieldUsername;
public InputField FieldScore;
//public static leaderboard instance;
//public Object[] tiles = {}
// Use this for initialization
void Start()
{
Button btn = SubmitButton.GetComponent<Button>();
btn.onClick.AddListener(TaskOnClick);
// Use this for initialization
PNConfiguration pnConfiguration = new PNConfiguration();
pnConfiguration.PublishKey = "pub-c-450b-redacted";
pnConfiguration.SubscribeKey = "sub-c-03aa-redacted";
pnConfiguration.LogVerbosity = PNLogVerbosity.BODY;
pnConfiguration.UUID = Random.Range(0f, 999999f).ToString();
pubnub = new PubNub(pnConfiguration);
Debug.Log(pnConfiguration.UUID);
MyClass fireRefreshObject = new MyClass();
fireRefreshObject.refresh = "new user refresh";
string firerefreshobject = JsonUtility.ToJson(fireRefreshObject);
pubnub.Fire() // This will trigger the leaderboard to refresh so it will display for a new user.
.Channel("submit_score")
.Message(firerefreshobject)
.Async((result, status) =>
{
if (status.Error)
{
Debug.Log(status.Error);
Debug.Log(status.ErrorData.Info);
}
else
{
Debug.Log(string.Format("Fire Timetoken: {0}", result.Timetoken));
}
});
pubnub.SubscribeCallback += (sender, e) =>
{
SubscribeEventEventArgs mea = e as SubscribeEventEventArgs;
if (mea.Status != null)
{
}
if (mea.MessageResult != null)
{
Dictionary<string, object> msg = mea.MessageResult.Payload as Dictionary<string, object>;
string[] strArr = msg["username"] as string[];
string[] strScores = msg["score"] as string[];
int usernamevar = 1;
//setting usernames, nothing is printed to the console
foreach (string username in strArr)
{
string usernameobject = "Line" + usernamevar;
GameObject.Find(usernameobject).GetComponent<Text>().text = usernamevar.ToString() + ". " + username.ToString();
usernamevar++;
Debug.Log(username);
}
//setting scores, nothing is printed to the console
int scorevar = 1;
foreach (string score in strScores)
{
string scoreobject = "Score" + scorevar;
GameObject.Find(scoreobject).GetComponent<Text>().text = "Score: " + score.ToString();
scorevar++;
Debug.Log(score);
}
}
if (mea.PresenceEventResult != null)
{
Debug.Log("In Example, SusbcribeCallback in presence" + mea.PresenceEventResult.Channel + mea.PresenceEventResult.Occupancy + mea.PresenceEventResult.Event);
}
};
pubnub.Subscribe()
.Channels(new List<string>() {
"leaderboard_scores"
})
.WithPresence()
.Execute();
//TaskOnClick();
}
public void TaskOnClick()
{
var usernametext = FieldUsername.text;// this would be set somewhere else in the code
var scoretext = FieldScore.text;
MyClass myObject = new MyClass();
myObject.username = FieldUsername.text;
myObject.score = FieldScore.text;
string json = JsonUtility.ToJson(myObject);
pubnub.Publish()
.Channel("submit_score")
.Message(json)
.Async((result, status) =>
{
if (!status.Error)
{
Debug.Log(string.Format("Publish Timetoken: {0}", result.Timetoken));
}
else
{
Debug.Log(status.Error);
Debug.Log(status.ErrorData.Info);
}
});
//Output this to console when the Button is clicked
Debug.Log("You have clicked the button!");
}
}
If there are any other things you would need to know, please let me know.
Sorry for my English. Thanks in advance.

Is there a way to save data from an array into a text file

I am making a quiz with both unity and c#. At the end there is a survey with toggle questions and an in input field and I am using arrays to save the data from the survey. Is there any way I can take the whatever I store in my array and put it in a text file so I can open it later?
Here is my code for the survey:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIScript07 : MonoBehaviour
{
public GameObject[] questionGroupArr;
public QAClass07[] qaArr;
public GameObject AnwerPanel;
void Start()
{
qaArr = new QAClass07[questionGroupArr.Length];
}
void Update()
{
}
public void SubmitAnswer()
{
for (int i = 0; i < qaArr.Length; i++)
{
qaArr[i] = ReadQuestionAndAnswer(questionGroupArr[i]);
}
}
QAClass07 ReadQuestionAndAnswer(GameObject questionGroup)
{
QAClass07 result = new QAClass07();
GameObject q = questionGroup.transform.Find("Question").gameObject;
GameObject a = questionGroup.transform.Find("Answer").gameObject;
result.Question = q.GetComponent<Text>().text;
if (a.GetComponent<InputField>() != null)
{
result.Answer = a.transform.Find("Text").GetComponent<Text>().text;
}
else if (a.GetComponent<InputField>()== null)
{
string s = "";
int counter = 0;
for (int i = 0; i < a.transform.childCount; i++)
{
if (a.transform.GetChild(i).GetComponent<Toggle>().isOn)
{
if (counter != 0)
{
s = s + ",";
}
s = s + a.transform.GetChild(i).Find("Label").GetComponent<Text>().text;
counter++;
}
if (i == a.transform.childCount - 1)
{
s = s + ".";
}
}
result.Answer = s;
}
return result;
}
[System.Serializable]
public class QAClass07
{
public string Question = "";
public string Answer = "";
}
}
You can save your array data as json object and save it as text file.
If you want your custom classes to save to json too, you should add **[System.Serializable] which you added already. Then for saving any serialized object you can create a serialized class that you can add every variable. Here is sample workflow.
[Serializable]
public class SaveObject
{
public GameObject[] questionGroup;
public string playerName;
public QAClass07[] qaArr;
public GameObject AnswerPanel;
}
[System.Serializable]
public class QAClass07
{
public string Question = "";
public string Answer = "";
}
public void Save()
{
var saveObject = new SaveObject()
{
AnswerPanel = GetAnswerPanel(),
playerName = GetPlayerName(),
qaArr = GetqaArr,
questionGroup = GetquestionGroup
};
string saveText = JsonUtility.ToJson(saveObject);
File.WriteAllText(Application.persistentDataPath + "/SAVES/save.txt", saveText);
}
public void LoadQ()
{
if (File.Exists(Application.persistentDataPath + "/SAVES/save.txt"))
{
isLoading = true;
string saveText = File.ReadAllText(Application.persistentDataPath + "/SAVES/save.txt");
var saveObject = JsonUtility.FromJson<SaveObject>(saveText);
// You can load back all objects from saveObject in same way. Load and Get methods are random. You get the point
LoadAnswerPanel(saveObject.AnswerPanel);
}
}
The reason there is a class named SaveObject is collecting all data in one class and save to json and load it back from json easily.

Need help understanding why data is being removed from all my lists

I currently have questions and answers being loaded in from a .json file into unity and then this data is placed inside a unansweredquestions list which then when a new question is generated it removes the current question being displayed. however, it also removes the data main list where the information is loaded in to. The code below is what is used to do this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.UI;
public class QuestionHandler : MonoBehaviour
{
[SerializeField] public Text questionText;
[SerializeField] public Text answerAText;
[SerializeField] public Text answerBText;
[SerializeField] public Text answerCText;
[SerializeField]
private QuestionData _QuestionData = new QuestionData();
public static List<Question> unansweredQuestions;
private Question currentQuestion;
private QuestionData questionData;
public void SaveIntoJson()
{
string question = JsonUtility.ToJson(_QuestionData, true);
System.IO.File.WriteAllText(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData/QuestionData.json", question);
Debug.Log(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData/QuestionData.json");
}
// Start is called before the first frame update
void Start()
{
if(!Directory.Exists(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData"))
{
Directory.CreateDirectory(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData");
File.Create(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData/QuestionData.json");
SaveIntoJson();
}
Load();
Debug.Log(_QuestionData.questions[0].questionName);
//if the unansweredQuestion list has no data or all the questions have been removed it will copy in the data from the _QuestionData list
if (unansweredQuestions == null || unansweredQuestions.Count == 0)
{
Debug.Log("No questions present loading in saved data");
unansweredQuestions = _QuestionData.questions;
}
Debug.Log(unansweredQuestions[0].questionName);
SetCurrentQuestion();
}
// Update is called once per frame
void Update()
{
}
public void SetCurrentQuestion()
{
int randomQuestionIndex = Random.Range(0, unansweredQuestions.Count);
currentQuestion = unansweredQuestions[randomQuestionIndex];
questionText.text = currentQuestion.questionName;
answerAText.text = currentQuestion.answerA;
answerBText.text = currentQuestion.answerB;
answerCText.text = currentQuestion.answerC;
}
public void SetNewCurrentQuestion()
{
if (unansweredQuestions == null || unansweredQuestions.Count <= 0)
{
Debug.Log("No more questions left in the list!");
_QuestionData = questionData;
}
else
{
//removes current question from the list so no question comes up twice
unansweredQuestions.Remove(currentQuestion);
//randomly picks a new question out the remaining questions
int randomQuestionIndex = Random.Range(0, unansweredQuestions.Count);
currentQuestion = unansweredQuestions[randomQuestionIndex];
questionText.text = currentQuestion.questionName;
answerAText.text = currentQuestion.answerA;
answerBText.text = currentQuestion.answerB;
answerCText.text = currentQuestion.answerC;
Debug.Log(_QuestionData.questions.Count);
Debug.Log(unansweredQuestions.Count);
}
}
void Load()
{
string filePath = System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData/QuestionData.json");
string data = System.IO.File.ReadAllText(filePath);
questionData = JsonUtility.FromJson<QuestionData>(data);
Debug.Log("Got Data!");
//sets the loaded questiondata into the game question list
_QuestionData = questionData;
}
private void OnApplicationQuit()
{
//SaveIntoJson();
}
}
//format of the questions within the game
[System.Serializable]
public class QuestionData
{
public List<Question> questions = new List<Question>();
}
[System.Serializable]
public class Question
{
public string questionName;
public string answerA;
public bool isA;
public string answerB;
public bool isB;
public string answerC;
public bool isC;
}
Shows the main unity screen with the question data down the left side:
Any help understanding this would be great I've tried using debug.log to see what's going on but I cannot work it out.
You are doing
unansweredQuestions = _QuestionData.questions;
So after this line both fields point to the same list reference. => When you later remove the question via
unansweredQuestions.Remove(currentQuestion);
This item is removed from the _QuestionData.questions since it is the same list.
To avoid that you rather should create and work on a copy of the list instead like
unansweredQuestions = new List<Question>(_QuestionData.questions);
Then for the saving you would probably want to only keep the unansweredQuestions except for the first time
public void SaveIntoJson(bool overwriteWithUnanswered = true)
{
if(overwriteWithUnanswered) _QuestionData.questions = unansweredQuestions;
var question = JsonUtility.ToJson(_QuestionData, true);
System.IO.File.WriteAllText(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData/QuestionData.json", question);
Debug.Log(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData/QuestionData.json");
}
And pass in false only the first time in `Start
if(!Directory.Exists(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData"))
{
Directory.CreateDirectory(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData");
File.Create(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/QuestionGameData/QuestionData.json");
SaveIntoJson(false);
}
Two further notes:
In general for filepaths you should always use Path.Combine instead of manually concatenate + "/"
you should store the path once instead of all the time getting it again and again
private readonly string filePath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop), "QuestionGameData", "QuestionData.json");
and then simply re-use it everywhere like e.g in
public void SaveIntoJson(bool overwriteWithUnanswered = true)
{
if(overwriteWithUnanswered) _QuestionData.questions = unansweredQuestions;
var question = JsonUtility.ToJson(_QuestionData, true);
Debug.Log(filePath);
System.IO.File.WriteAllText(filePath, questions);
}
Because it is a reference copy. Create a new list
//sets the loaded questiondata into the game question list
_QuestionData.questions = new List<Question>(questionData);
List<Question> mainlist = new List<Question>();
List<Question> sublist = new List<Question>();
Question current;
[System.Serializable]
public class Question
{
public string questionName;
public string answerA;
public bool isA;
public string answerB;
public bool isB;
public string answerC;
public bool isC;
public override string ToString()
{
return $"Question {questionName} = Answer {answerA} ";
}
}
public void Load() {
mainlist = new List<Question>() {
new Question(){questionName="Question A",answerA="A" },
new Question(){questionName="Question B",answerA="B" },
new Question(){questionName="Question C",answerA="C" },
new Question(){questionName="Question D",answerA="D" },
};
// sublist = mainlist; if you uncommment these you will get the same behavior you have.
sublist = new List<Question>(mainlist); ///Do these in order to avoid reference problems
}
public void showList(List<Question> sublist)
{
foreach (var item in sublist)
{
Console.WriteLine(item);
}
}
public void showList()
{
showList(mainlist);
showList(sublist);
}
public void SetCurrentQuestion()
{
int randomQuestionIndex = new Random().Next(0, mainlist.Count);
current = mainlist[randomQuestionIndex];
}
public void Remove()
{
sublist.Remove(current);
}
static void Main(string[] args)
{
var p = new Program();
p.Load();
p.SetCurrentQuestion();
p.Remove();
p.showList();
Console.WriteLine("Hello World!");
}
}

Applying a method onto all instances of class inside a list

I am picking up C# and as a beginners exercise I tried to code a simple Tekken Tournament roster. The problem is, that I cannot find a way how to apply a method of class Fighter onto the whole list of fighters, as there are no names of the variable, or at least I think that is the problem.
using System;
using System.Collections.Generic;
namespace TekkenConsoleTournament
{
class MainClass
{
public static void Main (string[] args)
{
Console.WriteLine ("Enter nicknames of players, finished by 0: \n");
int numFighter=0;
String nickname;
List <Fighter> allFighters = new List<Fighter>();
while ((nickname = Console.ReadLine()) != "") {
allFighters.Add(new Fighter(nickname));
numFighter ++;
}
Console.WriteLine(allFighters.ForEach(Fighter.getName()));
Console.WriteLine(foreach(Fighter in allFighters) {getName();});
//for (int counter = 0; counter <= fighter; counter++) {
// Fighter[counter.getName();
//}
}
}
}
And the Fighter.cs
using System;
namespace TekkenConsoleTournament
{
public class Fighter
{
private int matches, won, lost, draw;
String name;
public Fighter (String name)
{
this.matches = this.won = this.lost = 0;
this.name = name;
}
public void wonMatch()
{
matches++;won++;
}
public void lostMatch()
{
matches++;lost++;
}
public void drawMatch()
{
matches++;draw++;
}
public int[] getStats()
{
int[] stats = {this.matches,this.won,this.lost,this.draw};
return stats;
}
public String getName()
{
return this.name;
}
}
}
At first, I d like to print the names of all fighters, then maybe the stats and then as an exercise try making the code remember who fought who and put them in two dimensional matrix based on the result of the fight.
Use a foreach loop ?
foreach(var fighter in allFighters)
{
// do something with fighter
var name = fighter.getName();
Console.WriteLine(name);
}
I assume there is a getName method, if there is not use your field or property name to get current fighter's name.
Your mistake is you are putting ForEach loops inside of Console.WriteLine method.You should do the opposite.And this syntax is wrong:
foreach(Fighter in allFighters)
It should be like this:
foreach(Fighter fighter in allFighters)
Or you can use var as shown in the above code.
You're close, but not quite. Try these options:
Console.WriteLine(string.Join(", ", allFighters.Select(f => f.getName())));
allFighters.ForEach(fighter => Console.Writeline(fighter.getName()));
foreach (Fighter f in allFighters)
{
Console.WriteLine(f.getName());
}

Categories

Resources