Unity3D Input.GetKeyDown not working - c#

I am having an issue where Input.GetKeyDown is not working when I try to proceed in my text adventure game.
Once I get to the part of the game where the story talks about picking up a needle off the ground, I prompt the user to press one of three keys.
I have tried to change the keys the the player presses and that did not fix the issue.
As well I have added a Debug.Log statement to make sure that the key is being pressed.
public class ComicTextController : MonoBehaviour {
//publically exposing the gameText varible
public Text gameText;
//setting the Enum for states
private enum States { comic1, comic2, comic3, comic4, comic5, tryagain }; //Place new states here in the {} brackets
//States from enum varible
private States myState;
// Use this for initialization
void Start () {
myState = States.comic1;
}
// Update is called once per frame
void Update () {
if (myState == States.comic1)
{
state_comic1();
} else if(myState == States.comic2)
{
state_comic2();
} else if (myState == States.tryagain)
{
state_tryagain();
}
else if (myState == States.comic3)
{
state_comic3();
} else if(myState == States.comic4)
{
state_comic4();
}
}
void state_comic1()
{
gameText.text = "Rocket: Make sure to look both ways before crossing the street!I love going to Phoenix Comics." +
"They’re always so nice to me there.Did you know they do game events nearly every night of the… uh oh!" +
"Is that a needle on the ground? What do we do?\n\n"+
"Player: Pick it up. (Press the P key to pick it up)\n\n" +
"Player: Ignore it, go inside the store, and forget about it. (Press I to ignore)\n\n" +
"Player: Notice where it is, go inside the store, and tell an adult. (Press the S key to go into the Store)";
if (Input.GetKeyDown(KeyCode.P))
{
state_comic2();
Debug.Log ("Pressed the P Key");
} else if (Input.GetKeyDown(KeyCode.I))
{
state_comic3();
} else if (Input.GetKeyDown(KeyCode.S))
{
state_comic4();
}
}
void state_comic2()
{
gameText.text = "Rocket: No way! Sharp needles (sharps) are hazardous. They may be bright and colorful, but they’re not for children." +
"Maybe we should tell an adult?\n\n" +
"Press the T Key to try again";
if (Input.GetKeyDown(KeyCode.T))
{
state_tryagain();
}
}
void state_tryagain()
{
gameText.text = "Rocket: Make sure to look both ways before crossing the street!I love going to Phoenix Comics." +
"They’re always so nice to me there.Did you know they do game events nearly every night of the… uh oh!" +
"Is that a needle on the ground? What do we do?\n\n" +
"Player: Ignore it, go inside the store, and forget about it. (Press I to ignore)\n\n" +
"Player: Notice where it is, go inside the store, and tell an adult. (Press the S key to go into the Store)";
if (Input.GetKeyDown(KeyCode.I))
{
state_comic3();
} else if (Input.GetKeyDown(KeyCode.S))
{
state_comic4();
}
}
void state_comic3()
{
gameText.text = "Rocket: Although they seem like trash because they’re abandoned on the ground," +
"sharp needles (sharps) are hazardous, especially where there are lots of people walking around." +
"Maybe we should tell an adult?\n\n" +
"Press the T key to try again";
if (Input.GetKeyDown(KeyCode.T))
{
state_tryagain();
}
}
void state_comic4()
{
gameText.text = "Rocket: They were so grateful we let them know about the sharp needles (sharps) out in front." +
"Now they can carefully clean them up or let the city authorities know. Good work!\n\n" +
"Press the Right Arrow key to advance";
if (Input.GetKeyDown(KeyCode.RightArrow))
{
SceneManager.LoadScene("UrbanJungleStreet");
}
}
}
The issue is in the state_comic1() method.

Related

Unity function stepping out of execution and debugging when I search for gameobject

Ok, so this is something that never happened to me.
I'm working on a audio-based game, and need things to happen whenever a given midi note is played. No problem getting the note event and details, but once I call the actual function that's supposed to do something with it, something weird happens. (I'm using DryWetMidi to listen for midi NoteOn events, if that is relevant to the issue)
I'm trying to find a specific gameobject and change something within it. As soon as I search for it, the function simply stops execution without any error, exception or feedback of any kind. If I debug the code, it reaches that line fine, stops if I set a breakpoint on it, but if I click "step over" to go through the code, it simply continues execution and "steps out of the debug break" (not sure how to call this, hope it makes sense).
Here's the code
public void PlayNote(int note, int velocity)
{
Debug.Log("Playing note " + note + " at velocity " + velocity); // this logs the message fine
GameObject key = GameObject.Find(note.toString()); // execution reaches this line
// nothing is executed from this point on
if (key != null)
key.GetComponent<ObjectSetup>().SetPressed(true);
Debug.Log("Done"); // never logs anything
}
The gameobject in question is already created, as it is visible in-game, and the function works if I call it from somewhere else (let's say: from Start function of the current script).
I'm stumped, as I can't even understand why it's "jumping ship" mid-execution... If I try to search for another gameobject, or simply create a new one, it does exactly the same thing.
The first thing I see with your code is toString() instead of ‘ToString()`.
I also hazard a guess that you’re not seeing any error message because you might have unselected the error category in the console. That’s the red icon in the top right of the Console window. If that’s not selected, you won’t see any errors in the console.
You can read up about the switches at the Unity documentation.
Since you are listening to events from some external library: Are you sure these are involved on the Unity main thread?
Most of the Unity API can only be used on the main thread! I suspect this to be the issue here as it reaches the line containing GameObject.Find but nothing beyond.
You would basically need to dispatch the calls back into the Unity main thread. A pattern often used for this could look like
public class MainThreadDispatcher : MonoBehaviour
{
private static MainThreadDispatcher _instance;
public static MainThreadDispatcher Instance => _instance;
private readonly ConcurrentQueue<Action> _actions = new ();
private void Awake ()
{
if(_instance && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
// optionally
DontDestroyOnLoad(gameObject);
}
private void Update()
{
while(_actions.TryDequeue(out var action))
{
action?.Invoke();
}
}
public void DoInNextUpdate(Action action)
{
_actions.Enqueue(action);
}
}
public void PlayNote(int note, int velocity)
{
Debug.Log("Playing note " + note + " at velocity " + velocity); // this logs the message fine
GameObject key = GameObject.Find(note.toString()); // execution reaches this line
// nothing is executed from this point on
if (key != null)
key.GetComponent<ObjectSetup>().SetPressed(true);
Debug.Log("Done"); // never logs anything
}
And then you do
public void PlayNote(int note, int velocity)
{
MainThreadDispatcher.Instance.DoInNextUpdate(() =>
{
Debug.Log("Playing note " + note + " at velocity " + velocity); // this logs the message fine
GameObject key = GameObject.Find(note.ToString());
if (key && key.TryGetComponent<ObjectSetup>(out var setup))
setup.SetPressed(true);
Debug.Log("Done");
});
}

How can I fix this button script?

How can I make this code work properly? It's old but all I could find. I'm trying to make a mania type of game and I need the 4 buttons to work properly. I've tried to change the code somehow and managed to get the button be pressed down, but the thing is that the button won't go back up, they just stay as they are, pressed down. If there are any people who've done something similar to this please help and even those who've not please help too. Thanks.
private SpriteRenderer theSR;
public Sprite defaultImage;
public Sprite pressedImage;
public KeyCode keyToPress;
// Start is called before the first frame update
void Start () =>
theSR = GetComponent<SpriteRenderer>();
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(keyToPress))
{
theSR.sprite = pressedImage;
}
if(Input.GetKeyDown(keyToPress))
{
theSR.sprite = defaultImage;
}
}
}
The two conditions are identical. You probably meant to negate the later to set the button's image do defaultImage, when the key "is not down".
if (! Input.GetKeyDown(keyToPress))
{
theSR.sprite = defaultImage;
}
An if-else block whould be even neater:
if (Input.GetKeyDown(keyToPress))
{
theSR.sprite = pressedImage;
}
else
{
theSR.sprite = defaultImage;
}

How can I fix this bug in my chess game where a move shows up on top of a piece?

I was making a chess game, and all is well except that for example, a pawn, shows a move marker on a piece in front of it (pawns cant move like that). The game won't let you move there, since the move marker code is separate from the actual move code and check.
I have already tried many different approaches, such as iterating through every piece and checking if each piece is in front of the selected piece, as well as making sure that each piece iterated through is not the selected piece.
if (pieceSelected && !finishedPlacingMoveMarkers)
{
if (selectedPiece.GetComponent<BlackPawn>())
{
if (selectedPiece.transform.position.y + 1 <= 3.5)
{
foreach (Transform child in pieces.transform)
{
if (child.tag == "Piece")
{
if (obj.position == new Vector3(selectedPiece.transform.position.x, selectedPiece.transform.position.y + 1) && child.position != selectedPiece.transform.position)
{
if (child.position == new Vector3(selectedPiece.transform.position.x, selectedPiece.transform.position.y + 1))
{
noMoveHighlight = true;
break;
}
else
{
noMoveHighlight = false;
}
}
}
}
if (!noMoveHighlight)
{
GameObject move1 = Instantiate(tileMoveHighlight);
move1.transform.parent = moveTileHighlights.transform;
move1.transform.position = new Vector3(selectedPiece.transform.position.x, selectedPiece.transform.position.y + 1, -0.9f);
}
}
finishedPlacingMoveMarkers = true;
}
}
I expect the results to look like this when i select the pawn:
But they look like this:
Please help!

Unity C# Saved Game via Binary Formatting not retaining Data

Update: This issue may be caused by binaryformatter's issues with editing data in existing fields. A comment that I can no longer find described alternate methods which I am currently implementing. Will update after these methods are attempted if it is a solution to the problem.
I should start by saying I am a student so please go easy on me, I got reported when I first started coming here and hope to keep learning. I an new to unity, my C# is decent, but full of gaps since my schooling was rather terrible. I have watched hundreds of hours of unity tutorials and am studying the new concepts I learn for 4 hours every night after I get out of work, so if you see something just let me know.
This is actually a problem I have had for a while, but thought I fixed months ago. I was attempting to save games for the first time, and read into binary formatting and such to save. I had problems getting it up, but I I managed to get it to save and pull properly from a file. I verify that the data going into the file is correct, and the data coming out is correct, and even made the data private with a control function so nothing will access and change it without jumping through my debug. And yet after I leave the scope where I define the data it changes, without anything accessing my update function.
To break it down I have a class called PlayerType that stores all player information including my scenemanager, and it serializes this and saves to a file as a list. I create a for loop using the current length of the loaded list using an instance of the saveload class (this is what holds the list of save games and the access to the file) and it loops through instantiating my buttons in the order. Slot 1 will click to save game 1 and so to speak. The issue I am having is clicking slot 1 clicks slot 16, so does slot 2. In face, it seems practically random which buttons go to which slot. I should say here I am not sure whether it is actually going to the wrong slot, or simply renaming the player names wrong, but either way it does not appear this should be the case.
Here is my load function
public void Load() //Loads file from .gd file after checking if exists, then deserializes it back into a list
{
accessDataPath(false);
Debug.Log("Size of Save File" + listSize);
for (int i = 0; i < listSize; i++)
{
Debug.Log("First Loop " + SavedGames[i].returnName());
}
foreach (PlayerType player in GameManager.instance.saveStorage.returnList())
{
Debug.Log("Second Loop" + player.returnName());
}
}
and here is my accessDataPath function
public void accessDataPath(bool isSave)
{
//This stucture checks if file exists or not, then checks if saving or loading for 4 outcomes
if (isSave && SavedGames == null)//if saving and save file to update has nothing in it
{
Debug.Log("Error attempted to save null list in SaveLoad.accessDataPath!");
}
if (File.Exists(Application.persistentDataPath + "/SavedGames.gd"))//if the file exists
{
if (isSave)//if you are saving
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/SavedGames.gd", FileMode.Open);
bf.Serialize(file, SavedGames);
file.Close();
}
else //if you are not saving
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/SavedGames.gd", FileMode.Open);
SavedGames = (List<PlayerType>)bf.Deserialize(file);
file.Close();
listSize = SavedGames.Count;
}
}
else//if the file does not exist
{
if (isSave)//if you are saving
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/SavedGames.gd");
bf.Serialize(file, SavedGames);
file.Close();
}
else//if you are not saving
Debug.Log("Error Loading File. Does not Exist!");//Display Later that file does not exist to user
}
updateNames();
}//End accessDataPath
here is the function that updates the names
public void updateNames()
{
for (int i = 0; i < listSize; i++)
{
SavedGames[i].updateName("Player " + i);
Debug.Log("Bump " + i);
}
}
As you can see I verify if it exists, then check if I am saving or loading. The save function calls with a true and SavedGames is the list that is pulled from the file. Now After I run the name change I can check to see if it worked, and it does. Running the check here they all come back as the proper name, however by the time it gets back to my load function and runs my first loop they are wrong, it never leaves this section of code but as soon as it exits the scope they change to be almost random.
Now I had this problem for a while, but thought it had to do with my file maybe having corrupt data, so I deleted the save game and created new ones to test. The numbers changed, but were still not right. This tells me that somehow the file is effecting the names even though I rename them almost immediately after they come out. I am not used to loading or saving to files so I am not sure where to start here.
I will post more of my code below in case some of it stands out as being blatantly wrong, I also appreciate advice on structure as I took college classes on game programming but they were terrible and filled with gaps even if you don't spot the answer to my problem.
First I have my menu system setup to be toggled on or off, called by onclick events.
public class SaveMenu : MonoBehaviour {
public GameObject menu;
bool isActive;
bool isSave;
PopulateSave playerSaves;
public static SaveMenu instance;
void Awake()
{
isSave = false;
instance = this;
menu = GameObject.Find ("SaveList");
menu.SetActive (false);
isActive = false;
playerSaves = menu.GetComponentInChildren<PopulateSave> ();
}
public bool getActive()
{
return isActive;
}
public void toggleMenu(bool saveLoad)
{
if (isActive)
{
menu.SetActive (false);
isActive = false;
} else
{
menu.SetActive (true);
isActive = true;
}
bool isSave = saveLoad;
menu.transform.Find("Scroll View").transform.Find("Viewport").transform.Find("Content").transform.Find("NewSave").gameObject.SetActive(isSave);
Debug.Log(isSave);
}
public void updateSave()
{
playerSaves.startList();//Seems redundant but is used to make this access public with limited use
}
public bool getSave()
{
return isSave;
}
}
I call populatesaves as a child after everything is created because I had issues with it not existing when I tried to bring it in via the inspector. PopulateSaves is where most of my functional code is.
public class PopulateSave : MonoBehaviour{
public Button NewSave;
public Button OldSaves; // This is our prefab object that will be exposed in the inspector
void Awake()
{
GameManager.instance.saveStorage.Load();
Populate();
}
void Update()
{
}
void Populate()
{
Button newObj = Instantiate(NewSave, transform); // Create GameObject instance
newObj.name = "NewSave";
startList();
}
public void startList()
{
clearList();
for (int i = 1; i < GameManager.instance.saveStorage.returnCount() + 1; i++)
{
createButton(i);
}
Debug.Log("Done creating");
}
public void updateList(PlayerType newSave)
{
Button newObj;
newObj = (Button)Instantiate(OldSaves, transform);
//GameManager.instance.saveStorage.Save(i);
}
public void clearList()
{
GameObject[] gameObjects;
gameObjects = GameObject.FindGameObjectsWithTag("SaveSlot");
for (var i = 0; i < gameObjects.Length; i++)
Destroy(gameObjects[i]);
Debug.Log("Done Destroying");
}
public void ButtonClicked(int slot)
{
if (SaveMenu.instance.getSave())
{
Debug.Log("Save");
GameManager.instance.saveStorage.Save(slot);
}
else
{
Debug.Log("load");
GameManager.instance.currentPlayer.newPlayer(slot);
}
}
public void createButton(int i)
{
// Create new instances of our prefab until we've created as many as is in list
Button newObj = (Button)Instantiate(OldSaves, transform);
//increment slot names
newObj.name = "Slot " + i;
newObj.GetComponentInChildren<Text>().text = "Slot " + i;
newObj.onClick.AddListener(() => ButtonClicked(i));
}
}
Now I also have never written a listener with a script like I have here, could that section be assigning the wrong number to them? I checked to make sure all my indexes had the right numbers, if not being 1 off. One of my loops might use a setup that is 1 off but I am more worried about getting this off the ground and fixing the specifics at this point.
Here is my player storage
[System.Serializable]
public class PlayerType {
string playerName;
SceneManagement currentScene;
public PlayerType()
{
playerName = "Starting Name";
}
public void updateName(string name)
{
//Debug.Log("New Name: " + name);
playerName = name;
}
public void updateScene(SceneManagement newScene)
{
currentScene = newScene;
}
public string returnName()
{
return playerName;
}
public SceneManagement returnScene()
{
return currentScene;
}
public void newPlayer(int slotSave)
{
//Tmp player has wrong name from this point, initiated wrong?
//Debug.Log("New Name to update" + tmpPlayer.returnName());
this.updateName(GameManager.instance.saveStorage.returnSaves(slotSave).returnName());
this.updateScene(GameManager.instance.saveStorage.returnSaves(slotSave).returnScene());
}
}
Update:
Bump correctly goes through displaying 0-16
then Size of Save File displays 17 total saves
First loop then starts by outputting 'First Loop Player 15' a total of 16 times
Then it displays 16 so the last one is correct, though one off I guess.
Second loop does the same as first, unsurprisingly.
I left the call to updateNames in but commented out the lines and ran it including taking out bump.
It starts with 17 saves again and the 16 time iteration of player 15, however this time around the last one displays 'Temp Name' which I only define once at the beginning of my sceneManagement script for the current player, it should have never been saved, and even if it had been should have been overwritten by my name loop, at least that was my intent.
SceneManagement
[System.Serializable]
public class SceneManagement : MonoBehaviour {
DialogueManager dialogueManager;
PlayerType currentPlayer;
bool isSave;
bool isActive;
string sceneName;
int lineCount;
void Start()
{
isSave = false;
//loads player object for the current save
currentPlayer = new PlayerType();
currentPlayer.updateName ("Temp Name");
//This loads the prologue from the DB and sets the dialoguemanager up, defaults to prologue for now but can be updated to another scene later
isActive = true;
sceneName = "Prologue";
string conn = "URI=file:" + Application.dataPath + "/Text/Game.sqlite3";
List<string> sceneLines = new List<string>();
List<string> sceneCharacters = new List<string>();
int tmpInt;
string tmpString = "NOTHING";
int count = 0;
lineCount = 1;
IDbConnection dbConn;
dbConn = (IDbConnection)new SqliteConnection (conn);
dbConn.Open (); //Open database connection
IDbCommand dbCmd = dbConn.CreateCommand();
string sqlQuery = "SELECT Line, Flags, Character, Image, Text, Color FROM Prologue";
dbCmd.CommandText = sqlQuery;
IDataReader reader = dbCmd.ExecuteReader ();
while (reader.Read ()) {
tmpInt = reader.GetInt32 (0);
tmpString = reader.GetString (1);
sceneCharacters.Add(reader.GetString (2));
tmpInt = reader.GetInt32 (3);
sceneLines.Add(reader.GetString (4));
tmpString = reader.GetString (5);
//Debug.Log (tmpString);
//Debug.Log (count);
count++;
}
reader.Close ();
reader = null;
dbCmd.Dispose ();
dbCmd = null;
dbConn.Close ();
dbConn = null;
dialogueManager = new DialogueManager(sceneCharacters, sceneLines, lineCount);
}
//These are the returnvalues that might be used, may or may not be kept depending on future use.
public string getSceneName()
{
return sceneName;
}
public int getLineCount()
{
return lineCount;
}
public bool getSave()
{
return isSave;
}
//These return the lines for displaymanager, preventing it from directly interacting with dialoguemanager
public string getNextLine()
{
return dialogueManager.getNextLine();
}
public string getNextCharacter()
{
return dialogueManager.getNextCharacter();
}
//This function sets up visibility and is only accessed to properly display the screen after the scene has been started.
public void startScene ()
{
GameManager.instance.screenDisplay.changeVisible(true);
}
void Update()
{
if (Input.GetKeyDown ("space") & isActive) {
dialogueManager.incrementCurrentLine ();
GameManager.instance.screenDisplay.changeText(dialogueManager.getNextLine ());
GameManager.instance.screenDisplay.changeCharacter(dialogueManager.getNextCharacter ());
}
if (Input.GetKeyDown ("escape")) {
if (!PauseMenu.instance.getActive () && !SaveMenu.instance.getActive ())
{
PauseMenu.instance.toggleMenu ();
}
else if (SaveMenu.instance.getActive())
{
SaveMenu.instance.toggleMenu (true);
PauseMenu.instance.toggleMenu ();
}
else if (PauseMenu.instance.getActive())
{
PauseMenu.instance.toggleMenu ();
}
}
}
public void initialScreen()
{
SceneManager.sceneLoaded += OnLevelFinishedLoading;
}
void OnDisable()
{
SceneManager.sceneLoaded -= OnLevelFinishedLoading;
}
void OnLevelFinishedLoading (Scene scene, LoadSceneMode mode)
{
GameManager.instance.screenDisplay.initialScreen(dialogueManager.getNextLine(), dialogueManager.getNextCharacter(), null, null,
null, null, null, null);
}
public PlayerType returnPlayer()
{
return currentPlayer;
}
}
while im at it here is my GameManager too, though I havent messed with it a ton. Mostly using it as a DontDestroyOnLoad thing to hold everything else at this point.
public class GameManager : MonoBehaviour{
public static GameManager instance;
public DisplayScreen screenDisplay;
public SceneManagement managerScene;
public SaveLoad saveStorage;
public PlayerType currentPlayer;
//leave out until load data is setup
//PlayerType currentPlayer;
void Awake()
{
instance = this;
DontDestroyOnLoad (transform.gameObject);
managerScene = gameObject.AddComponent(typeof(SceneManagement)) as SceneManagement;
screenDisplay = gameObject.AddComponent (typeof(DisplayScreen)) as DisplayScreen;
saveStorage = gameObject.AddComponent (typeof(SaveLoad)) as SaveLoad;
//initialize player and sceneManager here, but only load through main menu options
//of new game or load game
}
void update()
{
}
}
I have continued troubleshooting and brought it down to a particularly confusing part for me. I took out all debugging logs and added 3 loops into my load function, they are these.
Debug.Log("LOAD CALLED");
accessDataPath(false);
for (int i = 0; i < listSize; i++)
{
Debug.Log("First Loop " + SavedGames[i].returnName());
SavedGames[i].updateName("Updated Player " + (i + 1));
}
for (int i = 0; i < listSize; i++)
{
Debug.Log("Second Loop " + SavedGames[i].returnName());
}
foreach (PlayerType player in GameManager.instance.saveStorage.returnList())
{
Debug.Log("Third Loop " + player.returnName());
}
So the first one displays player 15, then correctly displays the first loops 1-15 then temp name again, still havent 'figured out why that is popping up but I think it is related. Then the second loop iterates all wrong. Literally changed and back to back the loops are wrong, the only difference being it left the scope of the for loop. I ran a third loop using foreach to see if the type of call made a difference and it does not.
Even changing the name, and then immediately calling a loop to check shows that the values are changing. I think it might have something to do with how I am storing the objects, but I am not sure how the problem could be arising. Its not going null, and the names are changing to be the same every time so it isn't completely random. I am posting this here just after I found this hoping that soon I will solve it, but am also hoping if I do not someone else might spot something. I have the next 3 hours to work on it so I will be trying this entire time checking back every now and then. Thanks in advance for anyone that might glance at it for me.
Ok so I finally got the implementation done. I had to swap around a ton of my code, and ended up doing a wrapper object/list combination using JSON. Essentially the problem is that binary formatter messes up objects after you de-serialize them. Every time I updated my objects values they would randomly change on me for no reason without being accessed.
This was surprising as about half the posts I read on saving say its good, others like This one say that it is bad practice. I had done some research initially but had not come across the negative aspects of using it. I am still having problems, but Json is successfully retaining data and my objects are not messing up. I am pretty sure this was the problem as the only sections that I changed were my objects value structure to public for the serializing, and implemented the json structure into my SaveLoad script. This video was very helpful for the overall structure and getting started, and This thread helped me with troubleshooting when I ran into several problems.
I should also note that one thing I did not catch for a while. While Json can load lists, the initial object to be loaded must not be a list. I was attempting to save a list of my PlayerType directly into a folder, which it will not do. I ended up creating a quick object that contained my list and then saving the object. Since everywhere I read said that lists were fine it took a while to discover that this was causing part of my problem. It was not giving me any errors, just returning a blank string which most threads said was because it was not public or serializable.
Anyway here is to hoping my struggles and searches for answers might help as the things I found were quite scattered and hard to come across.

Why does getkeydown fire several times? (Unity, c#)

I am trying to teach myself C# and Unity knowledge via this course over on Udemy.com.
In one of the first examples a very basic text adventure was created.
Now starting from scratch, I have used the same game structure used there, trying to recreate my own version of this, I've run into a problem:
Input.GetKeyDown(KeyCode.R)
gets activated multiple times in a row and I haven't found the solution in hours.
Here's the relevant parts of the code:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class TextEngine : MonoBehaviour {
public Text myText;
private int oxygen;
private enum States
{
wakeup, window_0, room_1, corridor_1, corridor_left,
corridor_right, corridor_right_left, locked_supply_door,
damaged_supply_door, supply_door_keypad
}
private States myState;
// Use this for initialization
void Start() {
myState = States.wakeup;
}
// Update is called once per frame
void Update()
{
// Basic gameplay loop:
//if myState equals scenario a, then call method a
if (myState == States.wakeup) wakeup();
if (myState == States.room_1) room1();
if (myState == States.corridor_1) corridor_1();
if (myState == States.corridor_right) corridor_right();
if (myState == States.locked_supply_door) locked_supply_door();
}
void wakeup()
{
oxygen = 100;
myState = States.room_1;
}
void room1()
{
myText.text = "Oxygen Level: " + oxygen + "\n\nYou wake up. Press W to see Window. "
+"Press C to go down the Corridor.";
if (Input.GetKeyDown(KeyCode.W))
{
myState = States.window_0;
}
else if (Input.GetKeyDown(KeyCode.C))
{
myState = States.corridor_1;
}
}
void corridor_1()
{
myText.text = "Oxygen Level: " + oxygen + "\n\nYou walk the corridor to the end."
+ "You can turn Left or Right.\nFrom the Left you hear two gun shots!"
+"\nFrom the Right side comes an eerie silence."
+ "\n\nPress L to turn Left, R to turn Right. Press B to go Back.";
if (Input.GetKeyDown(KeyCode.L))
{
myState = States.corridor_left;
}
else if (Input.GetKeyDown(KeyCode.R))
{
print("R1");
myState = States.corridor_right;
}
else if (Input.GetKeyDown(KeyCode.B))
{
print("B");
myState = States.room_1;
}
}
void corridor_right()
{
myText.text = "Oxygen Level: " + oxygen + "\n\nAnother T-junction!\n"
+ "You can turn Left or Right.\nFrom the left you still hear nothing."
+ "\nFrom the Right side you hear a deep and frightening buzz!"
+ "\n\nPress L to turn Left, R to turn Right. Press B to go Back.";
if (Input.GetKeyDown(KeyCode.L))
{
myState = States.corridor_right_left;
}
else if (Input.GetKeyDown(KeyCode.R))
{
print("R2");
myState = States.locked_supply_door;
}
else if (Input.GetKeyDown(KeyCode.B))
{
print("B");
myState = States.corridor_1;
}
}
void locked_supply_door()
{
myText.text = "Oxygen Level: " + oxygen + "\n\nYou stand in front of a rusty metal door. You step closer. At eye-level there is a dusty sign, "
+ "barely readable. You wipe off the dust. It reads: 'Supply Room'. Maybe you can find some Oxygen tanks and "
+ "some communication devices in there?"
+ "\n\nPress S to Search for a handle."
+ "\n\nPress R to Ram the door, it looks weak!\n\n"
+ "Press B to go Back.";
if (Input.GetKeyDown(KeyCode.S))
{
myState = States.supply_door_keypad;
}
else if (Input.GetKeyDown(KeyCode.R))
{
print("R3");
myState = States.damaged_supply_door;
}
else if (Input.GetKeyDown(KeyCode.B))
{
print("B2");
myState = States.corridor_1;
}
}
}
What I expect to happen:
The game starts.
myState = States.wakeup; gets initalized.
wakeup() is called, myState = States.room_1; calls room1() via Update() and the text displayed on screen.
When pressing the C key, room(1) calls myState = States.corridor_1; which via Update() calls corridor_1() and the text is display again.
Everything working as expected so far. But now everthing goes wrong.
I expect this behavior:
4.A I press the R key, "R1" gets printed and via myState = States.corridor_right; the corridor_right() is called and waits for another key prompt.
But then this happens:
4.B I press the R key, "R1" gets printed and via myState = States.corridor_right; the corridor_right() is called and then without anymore input immediately "R2" gets printed, myState = States.locked_supply_door; is executed, which calls locked_supply_door() via Update() and then immediately "R3" gets printed to the console and then it changes myState = States.damaged_supply_door; where it would continue to call a method via Update(), but I've not yet implemented that method.
The steps seem to get executed correctly, but that GetKeyDown behaves more like `GetKey' and not like itself!
Why does GetKeyDown seemingly get activated multiple times, even when it's supposed to do so only once?
Most problems the people have seem to call GetKeyDowntwice, but not what seems several times.
A common problem seemed to be that the script is attached multiple times somewhere in Unity, which is not the case here: Canvas with Text Field + attaced Script.
If you have made it this far, I want to thank you for the time you already took to help me.
If only I could solve this problem, I cannot find the solution! My rubber duck also doesn't help me here.
The problem is that you are not doing the "update" with ELSE, so when you change from one state to another, it will not wait 1 frame to pass, and will directly go other if, because you changed and is correct, you enter the other if, and because a frame didn't pass till the last R was down, it will still say true to that...
So, in your "Update" function, make them with ELSE IF, not all IF. This way, when you enter one if, when the function inside finished, it will pass 1 frame to re enter.
So change Update to this:
void Update()
{
// Basic gameplay loop:
//if myState equals scenario a, then call method a
if (myState == States.wakeup) wakeup();
else if (myState == States.room_1) room1();
else if (myState == States.corridor_1) corridor_1();
else if (myState == States.corridor_right) corridor_right();
else if (myState == States.locked_supply_door) locked_supply_door();
}

Categories