I'm trying to have the Enter key cause a new 'map' to generate for my game, but for whatever reason after implementing full-screen in it the input check won't work anymore. I tried removing the new code and only pressing one key at a time, but it still won't work.
Here's the check code and the method it uses, along with the newMap method:
public class Game1 : Microsoft.Xna.Framework.Game
{
// ...
protected override void Update(GameTime gameTime)
{
// ...
// Check if Enter was pressed - if so, generate a new map
if (CheckInput(Keys.Enter, 1))
{
blocks = newMap(map, blocks, console);
}
// ...
}
// Method: Checks if a key is/was pressed
public bool CheckInput(Keys key, int checkType)
{
// Get current keyboard state
KeyboardState newState = Keyboard.GetState();
bool retType = false; // Return type
if (checkType == 0)
{
// Check Type: Is key currently down?
retType = newState.IsKeyDown(key);
}
else if (checkType == 1)
{
// Check Type: Was the key pressed?
if (newState.IsKeyDown(key))
{
if (!oldState.IsKeyDown(key))
{
// Key was just pressed
retType = true;
}
else
{
// Key was already pressed, return false
retType = false;
}
}
}
// Save keyboard state
oldState = newState;
// Return result
return retType;
}
// Method: Generate a new map
public List<Block> newMap(Map map, List<Block> blockList, Console console)
{
// Create new map block coordinates
List<Vector2> positions = new List<Vector2>();
positions = map.generateMap(console);
// Clear list and reallocate memory previously used up by it
blockList.Clear();
blockList.TrimExcess();
// Add new blocks to the list using positions created by generateMap()
foreach (Vector2 pos in positions)
{
blockList.Add(new Block() { Position = pos, Texture = dirtTex });
}
// Return modified list
return blockList;
}
// ...
}
I never touched any of the above code for this when it broke - changing keys won't seem to fix it. Despite this, I have camera movement set inside another Game1 method that uses WASD and works perfectly. All I did was add a few lines of code here:
private int BackBufferWidth = 1280; // Added these variables
private int BackBufferHeight = 800;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferWidth = BackBufferWidth; // and this
graphics.PreferredBackBufferHeight = BackBufferHeight; // this
Content.RootDirectory = "Content";
this.graphics.IsFullScreen = true; // and this
}
When I try adding text to be displayed in the event the key is pressed, it seems that the If is never even triggered despite the correct key being pressed.
It seems that when the CheckInput method attempts to check for 'Enter' having just been pressed, it passes the first check if (newState.IsKeyDown(key)) (which returns true) but fails the second if (!oldState.IsKeyDown(key)) check. (and returns true, but shouldn't)
Rolling answer, updated
Here's how I would have wrote your code:
public class Game1 : Microsoft.Xna.Framework.Game
{
// ...
protected override void Update(GameTime gameTime)
{
// ...
// Check if Enter was pressed - if so, generate a new map
if (CheckInput(Keys.Enter, 1))
{
blocks = newMap(map, blocks, console);
}
// ...
}
// Method: Checks if a key is/was pressed
public bool CheckInput(Keys key, int checkType)
{
KeyboardState newState = Keyboard.GetState(); // Get current keyboard state
bool retType = false; // Return type
var debug_new = newState.IsKeyDown(key);
var debug_old = oldState.IsKeyDown(key);
if (checkType == 0) // Check Type: Is key currently down?
{
retType = newState.IsKeyDown(key);
}
// Should happen only once, if key wasn't pressed before
else if (checkType == 1 && newState.IsKeyDown(key))
{
// Key pressed, wasn't pressed last update -> true.
// Key pressed, but was pressed last update -> false.
retType = !oldState.IsKeydown(key);
}
oldState = newState; // Save keyboard state
return retType; // Return result
}
// ...
}
It's shorter and easier to read.
Now, when/how is your update method called from? does it happen when you hit a key? is it in a loop?
Next, If you remove (or comment out) the things you changed (in the 3rd code block), does it work?
Next, if you put a break point in your CheckInput, does it stop there everytime you hit a key?
Hopefully from there you'll be able to get some more help.
Edit:
Ok, lets see, when/why would you call the method with a CheckInput(key, 0) ?
Are you trying to avoid a double press of the Enter for example? (what's the point of those checks?)
Another edit:
Yep, I know it's being called every frame and not keypress. The only problem is I don't know know how often is that ... 60 fps vs 5 fps is a big difference.
Was wondering if there's any chance it gets out of sync somehow ... What I would have done is set a conditional debugging point there, to break on Enter pressed, released, and have a good look at your newState / oldState, to make sure the logic works the way you want (easier with the shorter code I believe :)
Fixed it by adding a line to the Update method as opposed to relying on it being updated during the CheckInput method.
Updated methods:
protected override void Update(GameTime gameTime)
{
// ...
if (CheckInput(Keys.Enter, 1))
{
blocks = newMap(map, blocks, console);
}
oldState = Keyboard.GetState();
// ...
}
// Method: Checks if a key is/was pressed
public bool CheckInput(Keys key, int checkType)
{
// Get current keyboard state
KeyboardState newState = Keyboard.GetState();
bool retType = false; // Return type
if (checkType == 0)
{
// Check Type: Is key currently down?
if (newState.IsKeyDown(key))
{
retType = true;
}
else
{
retType = false;
}
}
else if (checkType == 1)
{
// Check Type: Was the key pressed?
if (newState.IsKeyDown(key))
{
if (!oldState.IsKeyDown(key))
{
// Key was just pressed
retType = true;
}
else
{
// Key was already pressed, return false
retType = false;
}
}
}
// Return result
return retType;
}
Although I'm not entirely sure how this makes it work (especially considering it worked without this modification before) it's a solution nonetheless.
Related
I have a method that is supposed to check a player's HP and then perform some logic based on if the number is greater than 0, or less than or equal to 0.
The method works but if I type it in the code and then change the hp value of a player it won't do anything until I type in the method name again. Then it will display the correct information.
At first I thought of using some kind of loop instead of a method. If I am correct, that means I'd have to have put curly braces around all my code that needs to get checked (which means basically around the whole code and I don't want that). Same with IF statement - even if I put it at the beginning of the code I'd still have to put curly braces around all the code.
Then I thought of a method which I already mentioned. And as I said - it does what it's supposed to do but only if I paste it multiple times into the main code.
Now, my question - is there ANY way to make the method "repeat itself" constantly or, I don't know, start at some place in the code and remain active?
Here is the method:
static void Status()
{
if (Player.playerHealth <= 0)
{
Player.isDead = true;
Console.WriteLine("You are dead!");
}
else if(Player.playerHealth > 0)
{
Player.isDead = false;
Console.WriteLine("You are not dead!");
}
}
I would be really grateful for any help.
You can define playerHealth as a property. That way, any time anyone changes it, you can make some code fire, including the check that you want.
class Player
{
protected int _playerHealth = 0;
public int PlayerHealth
{
set
{
_playerHealth = value;
if (_playerHealth == 0)
{
isDead = true;
Console.WriteLine("You are dead!");
}
}
get
{
return _playerHealth;
}
}
Now you don't even need to call Status... the logic will occur automatically whenever the player's health is modified.
var player = new Player();
player.PlayerHealth = 0; //Automatically triggers message
Correct me if I'm wrong but you want to call a method everytime the player's life changes? It looks like you should use an event.
public class HealthEventArgs : EventArgs
{
public int Health { get; set; }
public HealthEventArgs(int health)
: base()
{
this.Health = health;
}
}
public class Player
{
public event EventHandler<HealthEventArgs> LifeChanged;
int _Health;
public int Health
{
get => _Health;
set
{
_Health = value;
LifeChanged?.Invoke(this, new HealthEventArgs(_Health));
}
}
}
Then in your code it would look something like that
Player player = new Player();
player.LifeChanged += (o, e) =>
{
Player p = o as Player;
int health = e.Health;
// Whatever logic here, with access to the player and its health
};
With that mechanism in place, everytime the health of a player changes the event will fire and you will have access to the player and its health and can act accordingly.
If you're not confortable with lambda expressions, the code above can also be written as such
Player player = new Player();
player.LifeChanged += PlayerLifeChanged;
public void PlayerLifeChanged(object o, HealthEventArgs e)
{
Player p = o as Player;
int health = e.Health;
// Whatever logic here, with access to the player and its health
};
I'm working on a top down game where when you press the attack button multiple times there's a 3 hit sequence. I have the 3 hit sequence working just fine, but I have two problems I can't seem to find a solution to through my google fu.
When the sequence is finished, I don't have a good way to transition back to the idle state.
When you press the button once or twice, I'm not sure how I can tell whether you haven't pressed the button again in a long enough time to decide the sequence has been cancelled.
(I'm using a state machine I made for the character controller, and the states don't inherit from monobehavior so I don't have access to coroutines in this attack state)
Here is the code, I would really appreciate some feedback and help.
private string currentAttack = "Attack1";
public AttackingState(Character character, StateMachine stateMachine)
: base(character, stateMachine)
{
}
public override void Action()
{
if (Input.GetKeyDown(KeyCode.Space))
{
switch (currentAttack)
{
case "Attack1":
{
this.character.SetTrigger("Attack2");
this.currentAttack = "Attack2";
break;
}
case "Attack2":
{
this.character.SetTrigger("Attack3");
this.currentAttack = "Attack3";
break;
}
case "Attack3":
{
this.character.SetTrigger("Attack1");
this.currentAttack = "Attack1";
break;
}
default:
break;
}
}
if (Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0)
{
character.SetTrigger("AttackCancelled");
stateMachine.SetState<WalkingState>(new WalkingState(character, stateMachine));
currentAttack = "Attack1";
}
}
public override void OnStateEnter()
{
base.OnStateEnter();
character.SetTrigger("Attack1");
}
you could track a timestamp since the last press and do something like
// stores the last attack time
private float lastAttackTime;
// whereever you want to assign this value from
// The delay before the attack is reset and starts over from 1
private const float MAX_ATTACK_INTERVAL = 1f;
if (Input.GetKeyDown(KeyCode.Space))
{
// Time.time is the time in seconds since the app was started
if(Time.time - lastAttackTime > MAX_ATTACK_INTERVAL)
{
// start over
// mimics the attack3 since there you start over at 1
currentAttack = "Attack3";
}
lastAttackTime = Time.time;
switch (currentAttack)
{
...
}
}
In general instead of string I would recommend either a simple int or an enum instead.
There's Started, Performed and Cancelled, but what if I want to detect if I'm holding down the button on a Gamepad. So basically, how do i rewrite this to a gamepad with the new input system.
private void Jump()
{
if (isTouchingGround == true && Input.GetKeyDown(KeyCode.Space))
{
isJumping = true;
jumpTimeCounter = jumpTime;
myRigidbody2D.velocity = new Vector2(myRigidbody2D.velocity.x, 1 * jumpSpeed);
}
if (Input.GetKey(KeyCode.Space) && isJumping == true)
{
if (jumpTimeCounter > 0)
{
myRigidbody2D.velocity = new Vector2(myRigidbody2D.velocity.x, 1 * jumpSpeed);
jumpTimeCounter -= Time.deltaTime;
}
else
{
isJumping = false;
}
}
if (Input.GetKeyUp(KeyCode.Space))
{
isJumping = false;
}
}
There is no equivalent!
You have to Bind all Inputs to events and then process the inputs in the methods you assign to these events. This is because the new input system does not directly use Keys to make it work on multiple devices (keyboard, controller, phone, ...) without adding more code
See here.
The new input system exposes a Gamepad object with buttons each button is a
ButtonControl that exposes accessors for the button state:
Input.GetKeyDown(KeyCode.JoystickButton2) ~= Gamepad.current.buttonWest.wasPressedThisFrame
Input.GetKey(KeyCode.JoystickButton2) ~= Gamepad.current.buttonWest.isPressed
Input.GetKeyUp(KeyCode.JoystickButton2) ~= Gamepad.current.buttonWest.wasReleasedThisFrame
Or if you want keyboard use Keyboard.
That lets you circumvent the new input system's configuration and hardcode your control scheme.
However, for supporting multiple input devices, you'd create a control scheme and use accessors on it:
Input.GetButton("fire") ~= m_Controls.fire.ReadValue<bool>()
Input.GetButtonUp("fire") ~= m_Controls.fire.triggered
Input.GetButtonDown("fire") ~= m_Controls.fire.triggered
triggered doesn't work quite like Input.GetButtonUp/Down. You'd configure the "fire" input for when during a press it should fire. You can set an input in your control scheme to be a Tap or SlowTap (ugh) and if it's a Tap, then triggered happens if it was released within the threshold time whereas SlowTaps are triggered if it's held for the threshold time.
You can also check which phase of press the input is in:
m_Controls.fire.phase == InputActionPhase.Started
If you go for this control scheme setup, see also this post about bad defaults.
I'd rewrite your code something like this (untested) and configure "jump" as a SlowTap with immediate activation:
void Jump()
{
// maybe need to clear isJumping if isTouchingGround?
if (isJumping)
{
if (m_Controls.gameplay.jump.ReadValue<bool>() && jumpTimeCounter > 0)
{
myRigidbody2D.velocity = new Vector2(myRigidbody2D.velocity.x, 1 * jumpSpeed);
jumpTimeCounter -= Time.deltaTime;
}
else
{
isJumping = false;
}
}
// Use triggered to avoid jumping again when we hold button on landing
else if (isTouchingGround && m_Controls.gameplay.jump.triggered)
{
isJumping = true;
jumpTimeCounter = jumpTime;
myRigidbody2D.velocity = new Vector2(myRigidbody2D.velocity.x, 1 * jumpSpeed);
}
}
(But I'm not particularly advanced with the new input system.)
I'd recommend playing around with the simple demo sample that can be imported from the package window. See how the different ways of using the system work.
#if !ENABLE_LEGACY_INPUT_MANAGER
static Dictionary<KeyCode, Key> lookup;
#endif
static bool GetKeyDown(KeyCode key)
{
#if ENABLE_LEGACY_INPUT_MANAGER
return Input.GetKeyDown(key);
#else
if (lookup == null)
{
lookup = new Dictionary<KeyCode, Key>();
foreach (KeyCode keyCode in Enum.GetValues(typeof(KeyCode)))
{
var textVersion = keyCode.ToString();
if (Enum.TryParse<Key>(textVersion, true, out var value))
lookup[keyCode] = value;
}
lookup[KeyCode.Return] = Key.Enter;
lookup[KeyCode.KeypadEnter] = Key.NumpadEnter;
}
return Keyboard.current[lookup[key]].wasPressedThisFrame;
#endif
}
I have a working collision system in place in my Monogame/C# project that uses a quadtree to determine potential collisions, and test them accordingly. My problem is that I want to be able to send out a method to the entity (that has been collided with), either OnCollisionEnter() or OnCollisionStay() depending on if the collision was present in the previous frame.
In the following code, OnCollisionEnter() is never called:
public class Collision : iSystem
{
private List<Entity> currentCollisions; // Collisions occurring in the current frame
private List<Entity> previousCollisions; // Collisions from the previous frame
public override void Initialize(Entity caller)
{
currentCollisions = new List<Entity>();
previousCollisions = new List<Entity>();
base.Initialize(caller);
hasInitialized = true;
}
public override void Update(GameTime gameTime)
{
List<Entity> nearby = Global.QuadTree.GetNeighbours(parent);
for (int i = 0; i < nearby.Count; i++)
{
if (nearby[i] != parent)
if (parent.Collider.Intersects(nearby[i].Collider))
{
currentCollisions.Add(nearby[i]);
AlertEntity(nearby[i]);
}
}
previousCollisions = currentCollisions; // Ready for the next frame
currentCollisions.Clear();
base.Update(gameTime);
}
public void AlertEntity(Entity entity)
{
if (previousCollisions.Contains(entity))
parent.OnCollisionStay(entity.Collider);
else
parent.OnCollisionEnter(entity.Collider); // This is never called
}
}
('parent' refers to the entity that the component 'Collision' is attached to)
If you could suggest why this is happening, I'd be grateful. Thanks
Calling currentCollisions.Clear() will also clear previousCollisions because they are both referencing the same List object. Instead of calling currentCollisions.Clear(), you should set it to a new List<Entity>().
// . . .
previousCollisions = currentCollisions; // Ready for the next frame
currentCollisions = new List<Entity>(); // Now both lists are not clear
base.Update(gameTime);
I recently started to make video games using the XNA game studio 4.0. I have made a main menu with 4 sprite fonts using a button list. They change color from White to Yellow when I press the up and down arrows.
My problem is that when I scroll through it goes from the top font to the bottom font really fast and goes straight to the last one. I am unsure why this is? Is it because I am putting it in the update method and it is calling it every 60 seconds or so?
Here is my code for when I press the arrow keys.
public void Update(GameTime gameTime)
{
keyboard = Keyboard.GetState();
if (CheckKeyboard(Keys.Up))
{
if (selected > 0)
{
selected--;
}
}
if (CheckKeyboard(Keys.Down))
{
if (selected < buttonList.Count - 1)
{
selected++;
}
}
keyboard = prevKeyboard;
}
public bool CheckKeyboard(Keys key)
{
return (keyboard.IsKeyDown(key) && prevKeyboard.IsKeyUp(key));
}
I need someone to help me slow it down to a reasonable speed.
If you could help me it would be greatly appreciated.
I think the issue is because you are not setting prevKeyboard correctly.
Try this:
public void Update(GameTime gameTime)
{
keyboard = Keyboard.GetState();
if (CheckKeyboard(Keys.Up))
{
if (selected > 0)
{
selected--;
}
}
if (CheckKeyboard(Keys.Down))
{
if (selected < buttonList.Count - 1)
{
selected++;
}
}
prevKeyboard = keyboard; // <=========== CHANGE MADE HERE
}
public bool CheckKeyboard(Keys key)
{
return (keyboard.IsKeyDown(key) && prevKeyboard.IsKeyUp(key));
}
I think its because
if (CheckKeyboard(Keys.Up))
{
if (selected > 0)
{
selected--;
// This loops executes so quick before you release button.
// Make changes here to stop the loop if the button is pressed and loop
// executed once.(May be) just **return;** would do ?
}
}
if (CheckKeyboard(Keys.Down))
{
if (selected < buttonList.Count - 1)
{
selected++;
// This loops executes so quick before you release button.
// Make changes here to stop the loop if the button is pressed and loop
// executed once.(May be) just **return;** would do ?
}
}