I have an array InputFields, in my situation - six InputFields. How can I get the text and save it from each InputField?
Everything should work like this: I turn on the app, I change one, two or all of the input field, then turn off the application. Again, I turn on, and input fields have to be values which I wrote earlier.
I have a code that must seem to work properly, but this code works like this: it takes only the last value entered by the user and writes it to the last input field.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class SaveText : MonoBehaviour {
public GameObject[] InputFields;
public static string n;
public void Start ()
{
for(int i = 0; i < InputFields.Length; i++)
{
InputFields[i].GetComponent<InputField> ().text = PlayerPrefs.GetString (InputFields[i].name);
Debug.Log (PlayerPrefs.GetString (InputFields[i].name));
n = InputFields[i].name;
var input = InputFields[i].GetComponent<InputField> ();
var se = new InputField.SubmitEvent ();
se.AddListener (SubmitName);
input.onEndEdit = se;
}
}
public void SubmitName(string arg)
{
PlayerPrefs.SetString (n, arg);
}
An array of input fields I initialize dragging in Unity each input field in free cell in Script Component.
Well, you are using a single variable for all the different player prefs variables, n. So when you change a field (with SubmitName) it uses that n pref variable and changes it. This will correspond to the last value you gave to n in your loop.
An option would be to have that be an array too (private string prefValues[]) or to pass the calling input field to SubmitName (or rather it's name) and use that instead of n.
A little example (from your code you can actually change your GameObject[] to InputField[] which saves some GetComponent calls):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EventTest : MonoBehaviour
{
public InputField[] inputFields;
void Start ()
{
foreach(InputField field in inputFields)
{
var se = new InputField.SubmitEvent();
se.AddListener(delegate {
SubmitText(field.name, field.text);
});
field.onEndEdit = se;
}
}
public void SubmitText(string prefKey, string prefVal)
{
Debug.Log("Saved " + prefVal + " to " + prefKey);
}
}
Edit:
Added lines for saving/loading from prefs in the code below. For my test setup I just have two scenes. Both have two input fields and a button that calls that scene change. I can type stuff on the fields as I like, change scenes and it stays. Also upon restarting playmode in the editor.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class EventTest : MonoBehaviour
{
public InputField[] inputFields;
void Start ()
{
foreach(InputField field in inputFields)
{
// Get field values from player prefs if existing
// (it should only not exist the very first time or if you delete/clear the prefs)
if(PlayerPrefs.HasKey(field.name))
field.text = PlayerPrefs.GetString(field.name);
var se = new InputField.SubmitEvent();
se.AddListener(delegate {
SubmitText(field.name, field.text);
});
field.onEndEdit = se;
}
}
public void SubmitText(string prefKey, string prefValue)
{
// store the typed text of the respective input field
PlayerPrefs.SetString(prefKey, prefValue);
}
public void ChangeScene()
{
if(SceneManager.GetActiveScene().name == "Scene1")
{
SceneManager.LoadScene("Scene2");
}
else
{
SceneManager.LoadScene("Scene1");
}
}
}
If you want to save the data, after the application stopped or if it gets paused, you need to add some more functions to your MonoBehavior so that it can handle additional actions on a system end or pause.
Look at following functions in the documentation
OnApplicationQuit()
OnApplicationPause()
These functions will be called automatically. Inside these functions you need to iterate through the textfields and save them. It is one of the last functions that will be called before the program will lose it's recourses.
Regarding the Scene change, you would need the new scene to tell the old one to do any further actions. Like you can read in this post, there is only a way to know if a new scene was loaded.
Maybe you have a place where you can save the last active scene object and then call the last scenes function to do the saving process while the object is not cleared.
Related
I'm making a store for my game and I'm making a system where the score I get in the game is saved and every time you play it adds and saves too, but this last part isn't working I don't know why, I'm taking a variable from another script called score it receives the value I got and had to add it to the previous value but it doesn't happen.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CoinsManager : MonoBehaviour
{
public GameObject YellowSquare;
private int totalScore;
void Start()
{
YellowSquare.GetComponent<Text>().text = GameController.score.ToString();
totalScore = PlayerPrefs.GetInt("lastScore", GameController.score);
}
void Update()
{
SaveMoney();
}
void SaveMoney()
{
totalScore = GameController.score++;
PlayerPrefs.SetInt("lastScore", totalScore);
}
}
Why is the GameController.score completely detached from this? I would expect you rather do
// actually load the previous score into GameController.score
GameController.score = PlayerPrefs.GetInt("lastScore", GameController.score);
// then AFTER this update the text
YellowSquare.GetComponent<Text>().text = GameController.score.ToString();
.. and then in Update rather do
// Btw do you really want your score to be a counter of how many FRAMES you have played? o.O
GameController.score++;
PlayerPrefs.SetInt("lastScore", GameController.score);
// also update the text after changing the value
YellowSquare.GetComponent<Text>().text = GameController.score.ToString();
I'm making a 2D game where you're in the middle of the screen and you move round an endless green (screen) world and white cubes spawn randomly around you, and I have finished the game mechanics and a main menu and game over screens. The one thing I'm trying to add now is a high score. I did a bit of research and found PlayerPrefs is probably the way to do it. I have a seperate scene for my main menu and my gameplay level (which includes the game over screen). I have no error messages. I have created a HSSetter (High Score Setter) script on the high score text in the main menu screen:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HSSetter : MonoBehaviour
{
public Text highScoreText;
// Start is called before the first frame update
void Start()
{
highScoreText.text = "High Score: " + PlayerPrefs.GetInt("HighScore").ToString();
}
// Update is called once per frame
void Update()
{
highScoreText.text = "High Score: " + PlayerPrefs.GetInt("HighScore").ToString();
}
}
and in my score script which is in my actual game level, here's the bit where I try to create the high score:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class score : MonoBehaviour
{
public int scoreCount = 0;
public int highScoreIFA;
void Start()
{
highScoreIFA = PlayerPrefs.GetInt("HighScore");
}
void Update()
{
if (scoreCount >= highScoreIFA)
{
PlayerPrefs.SetInt("HighScore", scoreCount);
}
}
public void AddToScore()
{
if (isHit == true) // i know this if loop works
{
scoreCount += 1; // and this, I use it to change the score text in-game.
isHit = false;
}
}
}
In AddToScore(), I increment scoreCount.
Through some debugging, I have found that everything in the HSSetter script works - when I change the highScoreText.text, the text on screen changes, which led me to believe the issue might be with the change of scenes? Thanks!
Multiple things you should do here
The first you already updated in your question afterwards: You had the condition wrong and always updated only
if(highScoreIFA > scoreCount)
which would almost always be the case.
Now you have changed it to
if(scoreCount >= highScoreIFA)
which still is not good since if the score is equal there is no reason to update it, yet.
I would rather use
if(scoreCount > highScoreIFA)
so only really update it when needed.
Secondly in both scripts do not use Update at all! That is extremely inefficient.
I would rather use event driven approach and only change and set stuff in the one single moment it actually happens.
You should only one single class (e.g. the score) be responsible and allowed to read and write the PlayerPrefs for this. I know lot of people tent to use the PlayerPrefs for quick and dirty cross access to variables. But it is exactly this: Quick but very dirty and error prone.
If you change the keyname in the future you'll have to do it in multiple scripts.
Instead rather let only the score do it but then let other scripts reference it and retrieve the values directly from that script instead
And finally you should use
PlayerPrefs.Save();
to create checkpoints. It is automatically done in OnApplicationQuit, bit in case your app is force closed or crashes the User would lose progress ;)
Might look like
public class score : MonoBehaviour
{
public int scoreCount = 0;
// Use an event so every other script that is interested
// can just register callbacks to this
public event Action<int> onHighScoreChanged;
// Use a property that simply always invoked the event whenever
// the value of the backing field is changed
public int highScoreIFA
{
get => _highScoreIFA;
set
{
_highScoreIFA = value;
onHighScoreChanged?.Invoke(value);
}
}
// backing field for the public property
private int _highScoreIFA;
private void Start()
{
highScoreIFA = PlayerPrefs.GetInt("HighScore");
}
public void AddToScore()
{
if (isHit == true) // i know this if loop works
{
scoreCount += 1; // and this, I use it to change the score text in-game.
isHit = false;
// Only update the Highscore if it is greater
// not greater or equal
if (scoreCount > highScoreIFA)
{
PlayerPrefs.SetInt("HighScore", scoreCount);
// Save is called automatically in OnApplicationQuit
// On certain checkpoints you should call it anyway to avoid data loss
// in case the app is force closed or crashes for some reason
PlayerPrefs.Save();
}
}
}
}
Then your other script only listens to the event and updates its display accordingly. It is even questionable if both scripts should not rather simply be one ;)
public class HSSetter : MonoBehaviour
{
public Text highScoreText;
// Reference your Score script here
[SerializeField] private score _score;
private void Awake ()
{
// Find it on runtime as fallback
if(!_score) _score = FindObjectOfType<score>();
// Register a callback to be invoked everytime there is a new Highscore
// Including the loaded one from Start
_score.onHighScoreChanged += OnHighScoreChanged;
}
private void OnDestroy()
{
_score.onHighScoreChanged += OnHighScoreChanged;
}
private void OnHighScoreChanged(int newHighScore)
{
highScoreText.text = $"High Score: {newHighScore}";
}
}
I am trying to create a game in Unity 2d. I have finished most of what I want to do and have moved on to the enemies. The enemies (dragons) come in from different points of screen. To do this I have placed sprite game objects where I want the dragon to spawn. I have made all of these objects a child of another object called DragonAncores. I attached a script to DragonAncores which says this...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragonTracker : MonoBehaviour {
// is gold dragon in play?
public bool GoldDragonInit = false;
// curently active dragons
public int DragonCount = 0;
// defalts to 5
public int Difficulty = 5;
}
I am then attaching a script to each sprite which will eventually summon in a dragon Prefab (containing 2 colliders and an animator) biased of If statment logic derived from the other variables.
Below is the code I am using.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dragons : MonoBehaviour {
// same as GoldDragonInit
bool GoldDragonSpawn = false;
// same number as DragonCount in DragonTrackeer
int LiveDragons;
// same as Difficulty
int DifLev;
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
System.Random RNG= new System.Random();
void update()
{
RSpawn=RNG.Next(0,2)
DragonType=RNG.Next(0,101)
if (RSpawn = 1) ;
{
if (LiveDragons > DifLev) ;
{
if (DragonType > 99) ;
{
// summon regular dragon
}
if (DragonType = 100) ;
{
if (GoldDragonSpawn = true) ;
{
// summon gold dragon
}
}
}
}
}
}
This is throwing up this error list.
This shows my hierarchy in unity and the anchor points (the Squair crosshair looking things)
I have looked for other threads that adress this topic and they all try different methods, none work.
I am using Unity 2018.2.18f1
There are a few errors in your code here. The following is incorrect.
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
The correct way to access this, seeing as you said DragonAncors is the parent would be:
GetComponentInParent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
This sets the GoldDragonInit Boolean to the value of GoldDragonSpawn. This has to be inside a function, as you have it outside of a function I presume you needed this set on start. Therefore I have placed it in the void Start() function. This is called at the start of the game(loaded scene).
You also do not need semi-colons ; after an if statement, however it does need to appear after every line of difinitive code. The code you have provided should instead look like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dragons : MonoBehaviour {
// same as GoldDragonInit
bool GoldDragonSpawn = false;
// same number as DragonCount in DragonTrackeer
int LiveDragons;
// same as Difficulty
int DifLev;
void Start()
{
// variables from other script
GetComponentInParent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
}
System.Random RNG= new System.Random();
void update()
{
RSpawn=RNG.Next(0,2);
DragonType=RNG.Next(0,101);
if (RSpawn = 1)
{
if (LiveDragons > DifLev)
{
if (DragonType > 99)
{
// summon regular dragon
}
if (DragonType = 100)
{
if (GoldDragonSpawn = true)
{
// summon gold dragon
}
}
}
}
}
}
This works because DragonTracker is a script in the objects parent. If this was not the case then GetComponentInParent().GoldDragonInit = GoldDragonSpawn; would be replaced like so:
[SerializeField]
private GameObject DragonAncors;
void Start()
{
DragonAncors.GetComponent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
}
This is not valid c# code:
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
Why? Because it isn't inside a method.
Also, the comment is wrong. It isn't getting a variable (typo, too), its setting a variable in another script!
The reason for the first...16 problems Visual Studio is complaining about are because of this line.
At this location you are only allowed to declare methods, fields, and properties and you're currently trying to access another class and change one of its members (something you can only do inside a method).
Additionally, you have .cs which I assume is because "DragonAnchors.cs is the file name!" which you don't need to do. I'm not sure how to go about rewriting this line (inside Start()) as I'm not sure what you're trying to actually do. That is, I don't know where an instance of DragonAnchors actually resides. You're calling GetComponent(), which is typically reserved for accessing components attached to game objects, but as you've attempted to do so on a static class reference, I'm not sure if you meant to call it on this or on something else.
This is how you can get to DragonTracker:
DragonTracker dt = GameObject.Find("DragonAncores").GetComponent<DragonTracker>()
Debug.Log(dt.DragonCount);
There are a lot of errors there and they may take some steps to go through, but first things first you should clear up the issue with the code you're trying to use being unsupported. Go into the project settings and change the compiler language version as it notes on the 5th error down. This should allow you to use the newer functionality.
This is my original code and it's working fine but the text content is already in the editor in a Text UI and therefore it's also in the variable textField.
So I don't want to type the text over again also inside the code. For example the word toBeSearched is "almost" so using the string format it will generate new random number each time I press on space between the words: "almost" and "hours".
But since the text is already in the textField variable I want to parse the place I want to add the random numbers automatic using indexof and substring.
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
public class CustomText : MonoBehaviour
{
public string toBeSearched;
public Text textField;
private void Start()
{
GenerateRandomHour();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GenerateRandomHour();
}
}
private void GenerateRandomHour()
{
string numberName = Random.Range(1, 10).ToString();
textField.text = string.Format("Hello my friend, It's about time to wakeup. You were sleeping for almost {0} hours. My name is NAVI and i'm your navigation helper in the game.", numberName);
}
}
This is the code with the indexof and substring I tried to use but it#s not working good.
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
public class CustomText : MonoBehaviour
{
public string toBeSearched;
public Text textField;
private void Start()
{
GenerateRandomHour();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GenerateRandomHour();
}
}
private void GenerateRandomHour()
{
string result = null;
string result1 = null;
int ix = textField.text.IndexOf(toBeSearched);
if (ix != -1)
{
result = textField.text.Substring(0, ix + toBeSearched.Length);
result1 = textField.text.Substring(ix + toBeSearched.Length, textField.text.Length - (ix + toBeSearched.Length));
}
result1 = result1.TrimStart();
string numberName = Random.Range(1, 10).ToString();
textField.text = string.Format(result + " {0} " + result1, numberName);
}
}
Again the word toBeSearched is "almost" and I used a breakpoint and result contain the text until almost: "Hello my friend, It's about time to wakeup. You were sleeping for almost" and the variable result1 contain the rest of the text: "hours. My name is NAVI and i'm your navigation helper in the game."
But now when I'm pressing the space key it's inserting each time a new random number but it's not replacing the whole text like in the original code above.
So the result is many numbers in the text.
Not sure why it's not working like the original code.
The reason why it's not working is because you're changing the value of textField.text, so when you repeat the process the substrings are generated with this new value, including the number. You shouldn't give up your first solution, it's much cleaner. Store the original value of the text field on Start:
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
public class CustomText : MonoBehaviour
{
public string toBeSearched;
public Text textField;
private string defaultValue;
private void Start()
{
defaultValue = textField.text;
GenerateRandomHour();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
GenerateRandomHour();
}
}
private void GenerateRandomHour()
{
string numberName = Random.Range(1, 10).ToString();
textField.text = string.Format(defaultValue, numberName);
}
}
Add the {0} directly to your text field value in the inspector:
Hello my friend, It's about time to wakeup. You were sleeping for almost {0} hours. My name is NAVI and i'm your navigation helper in the game.
The problem is when you are adding a number in a specified spot, so every time you press space, it is using the existing text as the template and thus the old number stays,along with adding a new one.
Instead, you want to search between the two words "almost" and "hours" so you can replace whatevers between them. You can do this the by adding another indexOf search, but itll be easier using regex.
string result = Regex.Replace(textField.text, "(? <=almost).+?(?=hours)" , numberName);
textField.text = result;
Although your first solution looks cleaner, and if you want to keep the text in the inspector, Nathalia's answer would work
Iam confused saving data to variable with different script file.
For Example :
File player.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class player : MonoBehaviour {
public int coin = 1000;
}
File levelupstorage1raw.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using System.Linq;
public class levelUpStorage1Raw : MonoBehaviour {
public player Player;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void OnClickWood () {
Player.coin = Player.coin + 1000;
}
}
In this script player.cs :
public class player : MonoBehaviour {
public int coin = 1000;
}
I have inisial the coin = 1000, this mean int coin have 1000.
In Other script file levelupstorage1raw.cs :
public player Player;
// Use this for initialization
public void OnClickWood () {
Player.coin = Player.coin + 1000;
}
I have add more coin 1000 to Player.coin.
Now my question is : where the Player.coin 1000 is save ? is it save to it own file variable Player.coin or
is it save to player.cs file variable coin ?
If it save to player.cs file variable coin then variable coin now should have 2000.
You clearly have to learn a bit more about programming. Try looking for tutorials online.
Regarding your question, if I simplify things a bit to give you an idea:
A variable is something that can change. What is written in your Player.cs file is the default value, the one it will have when starting the program. When you write Player.coin = Player.coin + 1000;, the variable value changes, so Player.coin is now 2000.
At this point the first value (1000) is lost because the variable in your program has been set to 2000. But this has nothing to do with the file.
When you run your program, your file has already been transformed to a program. The file with the code is just there to tell the computer how to create the program, but it won't be modified when you run the program.
I hope this helps you understand what happens in your example. I still thing you should look for tutorials. Even basic stuff will be really difficult to achieve if you don't know the basics