I have a script where i take an integer from player prefs.
Below in code i am comparing value taken from player prefs and value taken from script name,casted to integer.In debug I see that values are changing f.e
first is 1 and second is 1,then after i button click,one value is increased by 1,and now
first is 1 and second is 2.But my code executes same as first time,but it should behave diferrently bcs if values are same it should do this,and if values are diferent it should do that.
Below is my code
Problem is when i use value from PlayerPrefs.
nazivKategorije = PlayerPrefs.GetString("kategorija");
if (nazivKategorije == "male")
{
brojOA = PlayerPrefs.GetInt("BrojOdgAsocijacije");
PlayerPrefs.SetInt("BROJOA", brojOA);
}else if (nazivKategorije == "srednje")
{
brojOA = PlayerPrefs.GetInt("BrojOdgAsocijacijeS");
PlayerPrefs.SetInt("BROJOA", brojOA);
}
else if (nazivKategorije == "velike")
{
brojOA = PlayerPrefs.GetInt("BrojOdgAsocijacijeV");
PlayerPrefs.SetInt("BROJOA", brojOA);
}
Debug.Log("NAZIV KATEGORIJE" + nazivKategorije);
//If I declare int above,and use its values there is not problem with this part of code.
In other script i increase this PlayerPrefs values.So to repeat:
when i had one value declared above,on top all worked fine.Now when I added this values from PlayerPrefs code wont run,actualy dont compare if:
brojBroja == broj or NOT.
First time it compare,and then it doesnt,It just throw first part of code no matter are values equal or not.
private void OnEnable()
{
int brojBroja= PlayerPrefs.GetInt("BROJOA");
if (brojBroja == broj)
{
GameEvents.OnBoardCompleted += ShowKonacnoRjesenje;
}
if (brojBroja != broj)
{
GameEvents.OnBoardCompleted += nePrikazujOpet;
}
}
private void OnDisable()
{
GameEvents.OnBoardCompleted -= ShowKonacnoRjesenje;
GameEvents.OnBoardCompleted -= nePrikazujOpet;
}
Related
Currently I am using Toggles to select a list of images and rather ran into an interesting problem.
I am using a Toggle Group called Radio Group and have 3 toggles under it. Each time when a toggle is selected the command
PlayerPrefs.SetInt("SaveToggleId", id);
is run. In this the id number is 0 for toggle 1, 1 for toggle 2 and so on.
So when I try to read this data the next time , the following set of code always reads 0 when used in Start and the correct value when used in Awake
toggleGroupId = PlayerPrefs.GetInt("SaveToggleId");
toggleGroupObject = GetComponent<ToggleGroup>();
SelectStartingToggle(toggleGroupId);
When I used this code in conjuction with the Debug.log() statements in various places what I found is when used in Start , it first reads from the function associated when the first toggle is selected and therby stores 0 . But when I use it in Awake it reads the right value stored in PlayerPrefs and selects the correct initial value
My explanation would be that because Awake is executed before Start , it has ample time to read from PlayerPrefs which gives the correct value. Also when I used only the number in the Start() as follows
SelectStartingToggle(3);
it correctly selected the right toggle whereas when I used PlayerPref instead of number ,it chose the wrong value.
Is my explanation correct or am I missing something?Also how to make sure the code execution is halted until the data from PlayerPref is read. Here is the full code:
public class RadioButtonSystem : MonoBehaviour
{
ToggleGroup toggleGroupObject;
private int toggleGroupId;
// Start is called before the first frame update
private void Awake()
{
toggleGroupId = PlayerPrefs.GetInt("SaveToggleId");
toggleGroupObject = GetComponent<ToggleGroup>();
Debug.Log("SaveToggleId........" + toggleGroupId);
SelectStartingToggle(toggleGroupId);
}
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void onSelectedToggle1()
{
SaveToggleId(0);
}
public void onSelectedToggle2()
{
SaveToggleId(1);
}
public void onSelectedToggle3()
{
SaveToggleId(2);
}
public void SelectStartingToggle(int id)
{
Toggle[] toggles = GetComponentsInChildren<Toggle>();
toggles[id].isOn = true;
}
public void SaveToggleId(int id)
{
PlayerPrefs.SetInt("SaveToggleId", id);
Debug.Log("SaveToggleId........saving..........." + PlayerPrefs.GetInt("SaveToggleId"));
}
/* Toggle GetSelectedToggle()
{
Toggle[] toggles = GetComponentsInChildren<Toggle>();
foreach (var t in toggles)
if (t.isOn) return t; //returns selected toggle
return null; // if nothing is selected return null
}*/
}
Playerprefs are saved upon OnApplicationQuit(). If you want to save it immediately, call PlayerPrefs.Save(). After PlayerPrefs.SetInt().
Btw, from the unity scripting api:
This function will write to disk potentially causing a small hiccup, therefore it is not recommended to call during actual gameplay.
In the edit/project settings there is a tab called script execution order, where you can set the order in which your scripts will be executed. For example, you have a "LoadManager" script, you set its priority to 1, and you set everything that relys on it to a greater number like 10.
If you fo this, nothing eill start executing until your manager script finished.
In my game I need to check for a specific keyboard combination, lets say Left Shift + B.
If I do it normally with
if(Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.B)) {
Debug.Log("correct");
}
It will accept everything as long as it has those two controls, so Left Shift + B, Left Shift + C + B, Left Shift + any keyboard button to be honest + B will return true as well.
My question is if I can detect somehow if ONLY those two were pressed, I already tried going foreach char c in KeyCode and detecting whether that Input was my B and then setting bool to correct or false but that doesn't really work.
Any ideas?
Event.current and OnGUI
Though usually it is not used so often anymore you can check the input in OnGUI using Event.current.
Every time a key goes down, store it in a list of currently pressed keys. When this keys goes up remove it from the list. Then you can simply check if the list contains the according keys and if the length matches the expected key amount.
public HashSet<KeyCode> currentlyPressedKeys = new HashSet<KeyCode>();
private void OnGUI()
{
if (!Event.current.isKey) return;
if (Event.current.keyCode != KeyCode.None)
{
if (Event.current.type == EventType.KeyDown)
{
currentlyPressedKeys.Add(Event.current.keyCode);
}
else if (Event.current.type == EventType.KeyUp)
{
currentlyPressedKeys.Remove(Event.current.keyCode);
}
}
// Shift is actually the only Key which is not treated as a
// EventType.KeyDown or EventType.KeyUp so it has to be checked separately
// You will not be able to check which of the shift keys is pressed!
if (!Event.current.shift)
{
return;
}
// As said shift is check on another way so we want only
// exactly 1 key which is KeyCode.B
if (currentlyPressedKeys.Count == 1 && currentlyPressedKeys.Contains(KeyCode.B))
Debug.Log("Only Shift + B");
}
It has to be done in OnGUI since there might be multiple events in one single frame. This is exclusive and will ony fire while Shift + B is pressed.
If you rather put this somewhere in your scene and make the values static
public class KeysManager : MonoBehaviour
{
public static bool ShiftPressed;
public static HashSet<KeyCode> currentlyPressedKeys = new HashSet<KeyCode>();
private void OnGUI()
{
if (!Event.current.isKey) return;
if (Event.current.keyCode != KeyCode.None)
{
if (Event.current.type == EventType.KeyDown)
{
currentlyPressedKeys.Add(Event.current.keyCode);
}
else if (Event.current.type == EventType.KeyUp)
{
currentlyPressedKeys.Remove(Event.current.keyCode);
}
}
ShiftPressed = Event.current.shift;
}
}
Then you can as before use something like
private void Update()
{
if (KeysManager.ShiftPressed && KeysManager.currentlyPressedKeys.Count == 1 && KeysManager.currentlyPressedKeys.Contains(KeyCode.B))
{
Debug.Log("Only Shift + B exclusively should trigger this");
}
}
Iterating with Input.GetKey through all KeyCode
Alternatively you could as commented check all possible keys.
However, this might or might not be an issue regarding performance. You'll have to test that and decide whether it is acceptable in your specific case.
using System.Linq;
...
// will store all buttons except B and LeftShift
KeyCode[] otherKeys;
private void Awake ()
{
// This simply returns an array with all values of KeyCode
var allKeys = (KeyCode[])Enum.GetValues(typeof(KeyCode));
// This uses Linq Where in order to only keep entries that are different from
// KeyCode.B and KeyCode.LeftShift
// ToArray finally converts the IEnumerable<KeyCode> into a KeyCode[]
otherKeys = allKeys.Where(k => k != KeyCode.B && k != KeyCode.LeftShift).ToArray();
}
private void Update()
{
if(Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.B) && !AnyOtherKeyPressed())
{
// Happens while ONLY LeftShift + B is pressed
}
}
// Return true if any other key
// is pressed except B and LeftShift
private bool AnyOtherKeyPressed()
{
foreach (var keyCode in otherKeys)
{
if(Input.GetKey(keyCode)) return true;
}
return false;
}
Maybe we worry too much and it doesn't matter (I woudln't believe that but just theoretically ^^) than you could even take it one level up and make it more flexible.
[Serializable]
public class KeyCombo
{
// Note I'll be lazy here .. you could create a custom editor
// for making sure each keyCode is unique .. but another time
public List<KeyCode> keyCodes = new List<KeyCode>();
// This will show an event in the Inspector so you can add callbacks to your keyCombos
// this is the same thing used in e.g. Button onClick
public UnityEvent whilePressed;
// Here all other keyCodes will be stored
[HideInInspector] public KeyCode[] otherKeys;
// Return true if any other key
// is pressed except B and LeftShift
public bool AnyOtherKeyPressed()
{
foreach (var keyCode in otherKeys)
{
if (Input.GetKey(keyCode)) return true;
}
return false;
}
}
public List<KeyCombo> keyCombos = new List<KeyCombo>();
private void Awake()
{
// This simply returns an array with all values of KeyCode
var allKeys = (KeyCode[])Enum.GetValues(typeof(KeyCode));
foreach (var keyCombo in keyCombos)
{
// This uses Linq Where in order to only keep entries that are different from
// the ones listed in keyCodes
// ToArray finally converts the IEnumerable<KeyCode> into a KeyCode[]
keyCombo.otherKeys = allKeys.Where(k => !keyCombo.keyCodes.Contains(k)).ToArray();
}
}
private void Update()
{
foreach (var keyCombo in keyCombos)
{
if (keyCombo.keyCodes.All(Input.GetKey) && !keyCombo.AnyOtherKeyPressed())
{
keyCombo.whilePressed.Invoke();
}
}
}
With this you can now add multiple KeyCombos and check them individually - However be aware that every additional keyCombo also means one additional iteration through all other keys so ... it's far away from perfect.
You can use Event.current.modifiers if you're doing combined keystrokes like Alt + Q etc. Something like this
if (!Event.current.isKey || Event.current.keyCode == KeyCode.None) return;
switch (Event.current.type) {
case EventType.KeyDown:
if (Event.current.modifiers == EventModifiers.Alt) {
switch (Event.current.keyCode) {
case KeyCode.Q:
break;
default:
break;
}
}
break;
default:
break;
}
I'm extremely new to programming so bear with me.
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
GameObject newItem = Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
newItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
newItem.GetComponent<ButtonSetter>().item_count += 1;
newItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
//SEE CONTINUED POST
}
}
So, within the else statement, I want to basically say,
They got their first wood, so we created a panel to display information about the wood they have. Now I want to say, since we have already instantiated the displayPanel, we just want to work from that and adjust the object's variable that controls the integer part of "Wood: 0"
This should be
newItem.GetComponent<ButtonSetter>().item_count
But if I try accessing that within the else statement as shown:
newItem.GetComponent<ButtonSetter>().item_count += 1;
It tells me that newItem does not exist within the current context.
How can I Instantiate something under certain conditions, then after the condition was met, take the instantiated object and work with its script/variables?
Visual representation of my issue
I think you need to restructure your code a little bit.
So here's how I would do it
public class Item : MonoBehaviour {
public string itemName;
public int count;
}
I will create a base class Item for wood, iron or whatever is there in your game.
Next, I will Create a Handler Script, that keeps track of all Items that are clicked/not Clicked
Handler.cs
public class Handler : MonoBehaviour {
public Item itemPrefab;
public Dictionary<string, Item> displayItems= new Dictionary<string, Item>();
public void OnWoodClicked()
{
OnItemClicked("wood");
}
public void OnIronClicked()
{
OnItemClicked("iron");
}
private void OnItemClicked(string itemKey)
{
if (displayItems.ContainsKey(itemKey))
{
displayItems[itemKey].count++;
}
else
{
Item item = Instantiate(itemPrefab);
item.count = 1;
item.itemName=itemKey;
displayItems.Add(itemKey, item);
}
}
}
So To keep track of created items, I have created a dictionary public Dictionary<string, Item> displayItems;
In this script OnItemClicked(string itemKey) Method will check If Item of this type is already created or not. (By checking if that key exists)
If that item is not created then we will Instantiate new Item (Prefab of display Item you want to show) and add that to the dictionary according to its key.
But if it already exists, then simply access that object as displayItems[itemKey] as you like
So for example, if you click on Wood, you will get access to wood display item as displayItems["wood"].
I hope this helps.
Just place a comment if you want me to make it clearer.
As it's currently written, newItem only lives in the "IF" clause of the code, so the "ELSE" clause can't see it.
If you assign it to a class-level field (like it appears you have _hasWood assigned at the class level) then you will be able to access it in either the "IF" or "ELSE" blocks, and maintain the count for the life of the object "clickWood()" is called from.
private bool _hasWood;
private GameObject _woodItem;
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
_woodItem= Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
_woodItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
_woodItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
}
}
I've been messing around with a word game in Unity C# and have come to a standstill regarding an anti-cheat mechanic I want to implement.
Once the first letter has been entered into the input field, I start running a 2 second timer. After the 2 seconds, in the player does not submit or type another letter, the input field should lock the previously typed letters in place on the input field and any letters typed after should be typed after it.
Here's the code I have so far:
currTime = 0;
hasInput = false;
lockedString = "";
void Update(){
if(hasInput){
currTime += Time.deltaTime * 1;
if(currTime >= 2){
//Stores current string value of input field
lockedString = inputField.text;
}
}
}
void OnInputValueChange(){
currTime = 0;
hasInput = true;
if(lockedString != ""){
inputField.text = lockedString + inputField.text;
}
}
Right now I'm running OnInputValueChange() whenever the input field's value is changed. I also manage to store the string that's been entered so far once the timer hits 2 seconds, but I do not know how to make it so that the input field "locks" the locked string into the front and allow changes to the letters typed behind it. The code inputField.text = lockedString + inputField.text; simply adds the lockedString variable to the input field every time the value gets changed.
The desired outcome is as such in pseudo-code:
//User types "bu"
//2 second timer starts
//During these 2 seconds, user can delete "bu" or continue typing
//User deletes "bu" and types "ah"
//Once the 2 second timer ends, whatever string is now in input is locked
//"ah" is now locked at the front of the input field
//After locking "ah", user cannot delete it anymore, but can continue typing
Any insight to how I might achieve something like this will be most helpful. Thanks for taking the time to help, I really appreciate it!
Currently, you are simply concatenating the string. You will want to check if the string starts with the same characters, and if not, completely overwrite the input:
void Update() {
if (hasInput && ((Time.time - inputTime) > 2f))
{
//Stores current string value of input field
lockedString = inputField.text;
hasInput = false;
}
}
void OnInputValueChange() {
inputTime = Time.time;
hasInput = true;
if ((lockedString.Length > 0) && (inputField.text.IndexOf(lockedString) != 0)) {
// Replace invalid string
inputField.text = lockedString;
// Update cursor position
inputField.MoveTextEnd(false);
}
}
Note: I have implemented an alternate method of measuring the time passed. Feel free to replace this with your own method.
I want to know how to detect if the user has released the mouse button the first time, or the times after that:
Pseudo-Code:
if *first* (Input.GetMouseButtonUp(0))
{
do something
}
if *second, third, fourth..etc.* (Input.GetMouseButtonUp(0))
{
do something else
}
I really have no idea on how to accomplish this. I'm sure it's pretty simple though!
This is only an idea, but you could do this using a flag variable, like this:
private static bool WasFirstTimeReleased = false;
if (Input.GetMouseButtonUp(0))
{
if (!WasFirstTimeReleased)
{
WasFirstTimeRelease = true;
//do your stuff for first time
}
else
{
//do your stuff for all other times
}
}
Generally, you have to remember somewhere how many times button was released. Just create field in yours class:
private int clicks = 0;
then:
if (Input.GetMouseButtonUp(0))
{
if(clicks == 0)
{
// do something on first click
}
else
{
// do something on further click
}
clicks++;
}
If the object where you store clicks counter is created each time you press mouse button then mark counter using static word.
Keep track of mouse clicks:
int _leftUp;
void Update()
{
var leftUp = Input.GetMouseButtonUp(0);
if (leftUp) _leftUp++;
// etc ...
}
Easiest way to accomplish this would be to use a counter to check the number of times the user has released the button.
private int releaseCounter = 0;
And then in an if statement:
if (Input.GetMouseButtonUp(0)) {
releaseCounter++;
//If you know on which release you want the code to be executed,
//replace the x with that number.
if (releaseCounter == x) {
//your code here
}
//If you want the code to be executed at set intervals.
//replace the x with the interval number.
if(releaseCounter%x == 0) {
//your code here
}
}
Hope I helped.