When running the following code in a loop (var enterKey = Keyboard.GetState().IsKeyDown(Keys.Enter);) pushing the enter key returns true even after I've released the key.
Any ideas as to what I'm doing wrong?
Try checking like this:
KeyboardState newState;
public void Update(...)
{
newState = Keyboard.GetState();
if(newState.IsKeyDown(Keys.Enter))
{
*do what you want here*
}
}
This way, on each update you are updating current keyboard state (which keys are pressed), so you can check for different key presses in each frame.
EDIT:
You are likely to want to check for a single click, so instead of asking 2 question, here is a "bonus" to your question. Code is similar:
KeyboardState newState, oldState;
public void Update(...)
{
//gets keyboard state for this single frame
newState = Keyboard.GetState();
//checks if enter is down
if(newState.IsKeyDown(Keys.Enter))
{
*do what you want here*
}
// checks if enter is clicked
// if statement asks if in this frame, enter button is down
// AND if enter button was not down in the last frame
// this way, if statement below will fire only on each click
if(newState.IsKeyDown(Keys.Enter) && oldState.IsKeyUp(Keys.Enter))
{
*do what you want here*
}
//set old state to new state, so the next frame knows
//what was happening in frame before that one
oldState = newState;
}
I suspect the state of the keyboard only gets refreshed between Update(...) cycles (or a close equivalent thereof) so looping is not going to do anything useful.
To detect changes in keyboard state, you'll want to compare keyboard states between successive calls to Update(...).
Restarting my computer fixed the problem, it must have been a bug outside of the code.
Related
I'm working with a 3D avatar and it's my first time working with animations. I have already done one which goes from the idle animation to another animation. It does the transition when the value of a slidebar is below 40, here is the code:
public Slider barraAlimento;
void Start()
{
anim = GetComponent<Animator>();
}
void Update()
{
if (barraAlimento.value <= 40)
{
anim.SetBool("IrHambriento", true);
}
else
{
anim.SetBool("IrHambriento", false);
}
}
This works correctly, but my problem is that now I want to do another transition. When I click a button, the value of the slidebar increases (that's how is triggered the previous animation), so I want a transition between the previous animation and the new one when the button is pressed. I have tried this, but it doesn't work:
public Button botonManzana;
void Start()
{
anim = GetComponent<Animator>();
botonManzana.onClick.AddListener(ButtonManzanaClicked);
}
void ButtonManzanaClicked()
{
anim.SetBool("IrComiendo", true);
anim.SetBool("IrComiendo", false);
}
The boxes and arrows are like this, if that's useful information.
Animation transition
I believe you're looking for a trigger rather than a bool.
Change the bool value in your animator from Boolean to Trigger and change your code to...
void ButtonManzanaClicked()
{
anim.SetTrigger("IrComiendo");
}
This will make a transition happen every time you "Trigger" the trigger, like a one time thing, that doesn't require any boolean true -> false for resetting like you're attempting in this code.
The reason your code isn't working, I believe, is because you're re-setting it back to false in the same frame, and I think the animation system will look at the values at the end of each code frame, so to speak.
this is probably happening because in:
void ButtonManzanaClicked()
{
anim.SetBool("IrComiendo", true);
anim.SetBool("IrComiendo", false);
}
you are setting it to false right after you set it to true.
Try putting only anim.SetBool("IrComiendo", true); in the button click event, and anim.SetBool("IrComiendo", false); only when you want it to go back to the "Hambriento" animation
EDIT:
try doing this:
change "IrComiendo" to trigger (just to not need to change its value to false)
OBS: use anim.SetTrigger("IrComiendo"); to call it
set the connections as in the image:
* connection not necessary if its impossible that the value of the slider still smaller than 40 after the button is clicked
** put this connection if you want it to go to Comiendo even if it isnt hambriento
OBS: in the button method you just need change the value of the slider and call "IrComiendo"
hope it helped
the problem is that when I click on one object, all other objects (with the same name as that object) make a sound.
This is the code I'm using:
void OnMouseOver()
{
if (Input.GetMouseButton(0))
{
if (this.gameObject.name == "door")
{
anim.SetBool("open", !(anim.GetBool("open")));
SoundManagerScript.PlaySound("door");
}
if (this.gameObject.name == "window")
{
anim.SetBool("open", !(anim.GetBool("open")));
SoundManagerScript.PlaySound("window");
}
}
}
How to prevent playing a sound for all objects?
Converting my comment to an answer. The problem in the script is related to user input.
Input.GetMouseButton returns whether the given mouse button is held down. Therefore, your if statement is true while mouse is held down. This is the reason why you have some many sounds playing continuously because you Input.GetMouseButton returns true on several frames during the interaction .
Instead Input.GetMouseButtonDown should be used. Because it returns true on the frame the user pressed the given mouse button. It will return false when mouse is held down. Therefore, it will return true only once when mouse is pressed.
you could always try and set up an array of these objects that contain the sound and then turn them off all at the same time.
public gameObject[] itemsWithSound;
for(int i = 0; i < itemsWithSound.length; i++)
{
itemsWithSound.SoundManagerScript.StopSound();
}
If i'm using like now GetKeyDown instead KeyDown pressing the key "o" does nothing.
But if i'm using GetKey and then KeyCode.O when i press on the O key the character stop walking but i need to keep pressing the O key all the time if i release the key the character will continue walking.
What i want to do is once when i click once on the key O the character will stop walking even when releasing the key and then if i click on the key "p" it will resume the walking.
The reason i'm using the speed property for pause/continue the walking is that if i will make:
gameObject.GetComponent<Animator> ().Stop();
The character stop walking but then the rest of the scene/world is moving to the character. And when i tried to use:
gameObject.GetComponent<Animator> ().Play();
Then i need to put some parameter in the Play like Play("Walk") but it didn't work.
That's why i decided to use the speed property for pause/continue.
using UnityEngine;
using System.Collections;
public class Ai : MonoBehaviour {
Animator _anim;
// Use this for initialization
void Start () {
_anim = GetComponent<Animator> ();
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.Z)) {
_anim.speed = 20.0f;
}
if (Input.GetKeyDown ("o"))
gameObject.GetComponent<Animator> ().speed = 0;
if(Input.GetKeyDown("p"))
gameObject.GetComponent<Animator>().speed = 2;
}
}
Input.GetKeyX can only get keys that are specified under Edit > Project Settings > Input.
You need to specify your key there if it does not match one of the existing ones.
Just change one of the existing ones if you don't need it or change the size to create an extra on. The name of the axis is what you put into Input.GetKeyX, for example Input.GetKeyDown("Fire1").
Edit:
Some more indepth information in the docs: https://docs.unity3d.com/Manual/ConventionalGameInput.html
I'm making the game Snake. It's all working but if you press buttons too quickly it has an unfortunate behavior. Before I started attacking this issue, the code handled a single key press per loop iteration, and if you pressed a key multiple times in a row, it took that many loops before you could change direction.
My initial change was to read all the keys that have been pressed since the last game loop, and only apply the last one. This worked, but really, if I press two keys, I want them both to be applied. What I don't want is for me to be stuck going right because I pressed right three times.
So my next approach was to peek at the next inputted character and see if it's the same as the last one. If so, read it out of the stream, and try again.
My understanding is that Console.KeyAvailable returns true if there is any unread data in the input stream, and Console.In.Peek() returns the first character of data in the input stream, or else hangs until there is data there.
Here's my current code:
private static void UpdateDirection()
{
ConsoleKey key = ConsoleKey.Escape; // dummy key
Dir curDir = s_snakeDir;
lock (s_consoleLock)
{
if (Console.KeyAvailable)
{
key = Console.ReadKey(true).Key;
// ignore multiple key presses of the same direction
while (Console.KeyAvailable)
{
int next = Console.In.Peek();
if (next != (int)key)
{
break;
}
// read and ignore next character
Console.ReadKey(true);
}
}
switch (key)
{
// do stuff...
}
}
}
If I press two keys on the same loop, the game stalls. What happens (as far as I can tell) is it gets to int next = Console.In.Peek(); and waits for the next character.
What I don't understand is why Console.In.Peek() needs to wait. If Console.KeyAvailable is true, then there should be a character in Console.In's stream.
EDIT
I believe I've figured out the issue. Console.In.Peek() is looking for characters, not key presses, and the arrow buttons don't have characters associated with them. So my next question is how to do what I want to do.
I also made a snake multiplayer game (source code here) and i had exact the same poblem:
If somebody presses a key for a while then the direction is set very delayed...
I solved this problem with a keyBuffer and a keyCheck thread:
List<ConsoleKeyInfo> keyBuffer = new List<ConsoleKeyInfo>();
new Thread(() =>
{
while(true)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true);
if (keyBuffer.Count > 0)
{
if (keyBuffer[keyBuffer.Count - 1] != key)
keyBuffer.Add(key);
}
else
keyBuffer.Add(key);
}
Thread.Sleep(1);
}
}).Start();
while(true)
{
if (keyBuffer.Count>0)
{
switch (keyBuffer[0].Key)
{
//SwitchSomeStuff
}
keyBuffer.RemoveAt(0);
}
//PlayGame
}
There's no conceivable point in not using Console.ReadKey(). You are only interested in the position of the keys to control the snake, not what letter they produce. WASD is ZQSD in France for example. And you get a use out of the cursor keys, you can't do that with Console.Read()
I am working on a game tutorial with XNA but I am modifying it slightly. Instead of adding projectiles to an array based on a duration, I am trying to add them to the array by pressing the SpaceBar. So this way the user has some control over firing the missiles.
if (currentKeyboardState.IsKeyDown(Keys.Space))
{
AddProjectile(player.Position + new Vector2(player.Width / 2, 0));
}
private void AddProjectile(Vector2 position)
{
Projectile projectile = new Projectile();
projectile.Initialize(GraphicsDevice.Viewport, projectileTexture, position);
projectiles.Add(projectile);
}
However I am running into a minor problem. Since I am using the method IsKeyDown() missiles will be added to the projectiles array until the space bar is released. Is there a method that will register a key press rather than IsKeyDown().
Add a field that keeps track of the previous keyboard state
KeyboardState _previousKeyboardState = Keyboard.GetState();
You will update this state at the end of the Update. You can now do edge detection:
KeyboardState currentKeyboardState = Keyboard.GetState();
if(_previousKeyboardState.IsKeyUp(Keys.Space)
&& currentKeyboardState.IsKeyDown(Keys.Space))
{
//action
}
_previousKeyboardState = currentKeyboardState; //update previous state
The thinking is: If on previous pass the key had not been pressed, but now it is pressed, do this action.