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.
Related
I have several in-game menus that open up when I press the Tab key (Items, Map, Lore etc..) and I want to make them move in a loop when the user is pressing the right/left buttons I've added.
For example:
If the order is Items|Map|Lore, and the player is looking at the Map menu, if they press right they reach Lore, and if they press right again they reach Items (rather than the current nothingness).
This is my left button code (same goes for right button of course but with -2560f for the x value):
public void LeftUIButton()
{
var moveMenusRight = new Vector3(2560f, 0, 0);
foreach(GameObject canvas in uICanvases.Keys)
{
Vector3 targetPosition = canvas.transform.position + canvas.transform.TransformVector(moveMenusRight);
StartCoroutine(LerpCanvas(canvas, targetPosition, uICanvasesSlidingTime));
}
}
The uICanvases is a dictionary I'm filling up in my Awake test method:
private Dictionary<GameObject, Vector3> uICanvases = new Dictionary<GameObject, Vector3>();
private void Awake()
{
gameOverScreen.SetActive(false);
// Adds all the GameObjects that are tagged as "UICanvas" to a list to be worked on.
foreach(GameObject canvas in FindObjectsOfType<GameObject>())
{
if (canvas.CompareTag("UICanvas"))
{
uICanvases.Add(canvas, canvas.transform.position);
}
}
}
I also want to keep the code as generic as possible, without committing to anything hard-coded.
Is there a way to do this?
I am making a 2d RTS style game in which you have the ability to select units in game with a click and drag selection box. When ever a unit is inside of this selection box and the left mouse button is let go the unit will be added to a "selected" list and their animation will be changed from the idle animation to the selected idle animation. After the selection box is draw again all the units in the selected list will be removed and their animations set back to the idle position, then all the units inside the new selection box will be added to the list and set to the selected idle animation. The problem is that if any of these units in the new selected box had been previously selected they will just switch to the idle animation and stay there instead going to the selected animation. I tested this without animations and it worked, it would stay as the selected sprite when selected again instead of switching to the idle sprite and staying there. I am pretty new at C# so this could very well be a very simple problem to fix.
Thanks
Here is the code:
ControllableUnits ControlUnitSelected;
public List<ControllableUnits> selectedControllableUnits;
private Vector3 mousePosition;
public void Awake()
{
selectedControllableUnits = new List<ControllableUnits>();
selectionAreaTransform.gameObject.SetActive(false);
ControlUnitSelected = this.gameObject.GetComponent<ControllableUnits>();
}
public void Update()
{
//Transforming Selection Area
if (Input.GetMouseButton(0))
{
Vector3 currentMousePosition = UnitControlClass.GetMouseWorldPosition();
Vector3 lowerLeft = new Vector3(
Mathf.Min(mousePosition.x, currentMousePosition.x),
Mathf.Min(mousePosition.y, currentMousePosition.y)
);
Vector3 upperRight = new Vector3(
Mathf.Max(mousePosition.x, currentMousePosition.x),
Mathf.Max(mousePosition.y, currentMousePosition.y)
);
selectionAreaTransform.position = lowerLeft;
selectionAreaTransform.localScale = upperRight - lowerLeft;
}
if (Input.GetMouseButtonDown(0))
{
mousePosition = UnitControlClass.GetMouseWorldPosition();
selectionAreaTransform.gameObject.SetActive(true);
}
//Creating the selection process
if (Input.GetMouseButtonUp(0))
{
Collider2D[] collider2DArray = Physics2D.OverlapAreaAll(mousePosition, UnitControlClass.GetMouseWorldPosition());
foreach (ControllableUnits control in selectedControllableUnits)
{
//if(ControlUnitSelected != null)
control.animPeasant.Play("Peasant_Idle");
control.isSelected = false;
}
selectedControllableUnits.Clear();
foreach (Collider2D collider2D in collider2DArray)
{
ControllableUnits controllableUnit = collider2D.GetComponent<ControllableUnits>();
if (controllableUnit != null)
{
controllableUnit.animPeasant.Play("Peasant_Idle_Selected");
controllableUnit.isSelected = true;
selectedControllableUnits.Add(controllableUnit);
}
}
selectionAreaTransform.gameObject.SetActive(false);
}
}
I guess the problem is when you call Play many times, Unity tries to merge these animations that results a random state finally.
It can be solved by giving the exact playback time.
controllableUnit.animPeasant.Play("Peasant_Idle_Selected", 0, 0f);
I'm having trouble getting my Player object to continue doing what it was doing after invoking an event.
I am working on a little platformer using Monogame, and have run into a bit of an issue. The way I'm handling collisions is I have a container that holds the game area's solid collision masks (simple Rectangles) and my Player has a mask. When the player moves, it invokes an event proposing its new position. The GameController takes that event and uses it to compare the player's proposed position and collision mask against the solids, and if it finds any collisions, passes those collisions into the player, where the player processes them to adjust its Proposed Position and then finally moves.
The problem is it seems as though once the event is invoked, the GameController does its thing and I never return to the code in my Player until I purposely try to move. I'm relatively new to events and event handling, and it sounded like this was a good way to have the GameController and Player communicate, but is it interrupting the Player's code flow and never returning to it? I guess my thought process was that the game would process its event handling method, then return to where we left off in the Player's code, but that either is not how it works, or I'm missing a step.
ActorPlayer Properties
Creates the Event to be fired off when the Player indicates any movement.
public delegate void PlayerMoveEventHandler(object source, EventArgs e);
public event PlayerMoveEventHandler PlayerMoved;
GameController Initialize
Subscribes to the Player's PlayerMoved event, assigning the OnPlayerMove method to it (see that method below)
Player = new ActorPlayer(0, PlayerIndex.One, new Vector2(112, 104));
Player.PlayerMoved += OnPlayerMove;
ActorPlayer Update
At Line 23, the ActorPlayer calls the OnPlayerMove() method, which fires off the event for any subscribers
public void Update(GameTime gameTime)
{
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
ProcessInput(deltaTime);
if(IsGrounded == false)
{
// Add Gravity
FallVector = FallVector + new Vector2(0, Global.GRAVITY_RATE * deltaTime);
if (FallVector.Y > 4f)
{
FallVector = new Vector2(0, 6f);
}
Velocity = Velocity + FallVector;
}
if (Velocity != new Vector2(0))
{
ProposedPosition = Position + Velocity;
CreateProposalMask(16, 24);
OnPlayerMoved();
if (IsColliding == true)
{
for (int i = Collisions.Count - 1; i >= 0; i--)
{
HandleCollision(Collisions.ElementAt(i));
Collisions.RemoveAt(i);
}
IsColliding = false;
}
Position = ProposedPosition;
Velocity = new Vector2(0);
CreateCollisionMask(16, 24);
}
}
ActorPlayer OnPlayerMoved
Triggers the event if any subscribers are listening
protected virtual void OnPlayerMoved()
{
PlayerMoved?.Invoke(this, EventArgs.Empty);
}
GameController OnPlayerMove
The method GameController runs if my Player's PlayerMoved event is triggered
public void OnPlayerMove(object source, EventArgs e)
{
// Check for Collisions against ProposedPosition on Player
foreach (EntityCollisionSolid solid in MapContainer.CollisionSolids)
{
if (Player.CollisionMask.Intersects(solid.CollisionMask) == true)
{
Rectangle _collision = Rectangle.Intersect(Player.CollisionMask, solid.CollisionMask);
Player.AddCollision(_collision);
}
}
if(Player.IsGrounded == true)
{
Rectangle belowPlayerMask = new Rectangle(Player.CollisionMask.X, Player.CollisionMask.Bottom, Player.CollisionMask.Width, 1);
foreach (EntityCollisionSolid solid in MapContainer.CollisionSolids)
{
if (belowPlayerMask.Intersects(solid.CollisionMask) == true)
{
return;
}
}
Player.PlayerFalling();
}
}
My assumption is that it would flow like this:
Player Checks for Input / Gravity. Creates a Velocity.
Player Combines their current Position with Velocity to create a movement Proposal.
If any Velocity exists, Player invokes its OnPlayerMoved event.
GameController is subscribed to that event, so it jumps in and uses the ProposalPosition to check for collisions that will occur if the player moves.
If the GameController finds any collisions, it adds Collision Rectangles to the Player's list to adjust their Proposal and avoid going into the wall/floor.
The Player continues after this, checking if any Collisions have been added to its list. If so, it adjusts its ProposalPosition.
Finally, the Position is updated to ProposalPosition.
What is actually happening is that when the OnPlayerMoved event triggers, the GameController does exactly as it was asked, but then nothing happens until I provide input again. It is as though it leaves the player's Update method during the event call and never returns to it. That may be how Events are supposed to work and I'm just unaware.
EDIT Thanks for everyone's suggestions! I'm still new to this, so I apologize for the mess of code shared before :) I have updated the code snippets above to properly show the delegate and EventHandler creation, subscription, triggering, and handling, as suggested below! The setup I have above works for what I was trying to accomplish.
Wow, so I had my GameController subscribed to the event, but the GameController hadn't been finished on my end, so portions of my collision detection were still being handled in my Main() loop. Moving those over to my GameController and adjusting the Update methods fixed the issue. Many thanks to Peter for helping me understand Events much better! I'd have just given up on the idea altogether soon without his input :)
Using the InputManager prefab from the HoloToolkit asset and implementation code the user can tap and hold on a given object and then move their hand left or right (along the x-plane) to rotate the object along the Y axis or up and down (along the y-plane) to rotate the object on the X axis.
However there appears to be a bug. If the users gaze comes off the object, the rotation stops immediately until the users gaze returns to the object. Is this the intended functionality? If so, how does one preserve the current object being changed via the navigation gesture and allow it to continue being manipulated until the users hand leaves the FOV or the user releases the tap and hold gesture?
Goal is to utilize the tap and hold gesture but not require the users gaze to be locked onto the object during the entirety of them rotating it. This is quite difficult with small or awkwardly shaped objects.
Implementation code:
[Tooltip("Controls speed of rotation.")]
public float RotationSensitivity = 2.0f;
private float rotationFactorX, rotationFactorY;
public void OnNavigationStarted(NavigationEventData eventData)
{
Debug.Log("Navigation started");
}
public void OnNavigationUpdated(NavigationEventData eventData)
{
rotationFactorX = eventData.CumulativeDelta.x * RotationSensitivity;
rotationFactorY = eventData.CumulativeDelta.y * RotationSensitivity;
//control structure to prevent dual axis movement
if (System.Math.Abs(eventData.CumulativeDelta.x) > System.Math.Abs(eventData.CumulativeDelta.y))
{
//rotate focusedObject along Y-axis
transform.Rotate(new Vector3(0, -1 * rotationFactorX, 0));
}
else
{
//rotate focusedObject along X-axis
transform.Rotate(new Vector3(-1 * rotationFactorY, 0, 0));
}
}
public void OnNavigationCompleted(NavigationEventData eventData)
{
Debug.Log("Navigation completed");
}
public void OnNavigationCanceled(NavigationEventData eventData)
{
Debug.Log("Navigation canceled");
}
You need to call these methods:
NavigationRecognizer = new GestureRecognizer();
NavigationRecognizer.SetRecognizableGestures(GestureSettings.Tap);
NavigationRecognizer.TappedEvent += NavigationRecognizer_TappedEvent;
ResetGestureRecognizers();
This is for a tapped event, but doing the other gesutres is simple as adding the event callback for them and using an | OR selector on the SetRecognizableGestures() call. e.g.
NavigationRecognizer.SetRecognizableGestures(GestureSettings.Tap | GestureSettings.NavigationX);
Draco18s answer is safer, but this solution works as well because
the InputManager prefab implements a stack for us.
On Navigation started, clear the stack and push the object being 'Navigated' onto the stack,
InputManager.Instance.ClearModalInputStack();
InputManager.Instance.PushModalInputHandler(gameObject);
On Navigation completed or canceled, pop it off the stack. InputManager.Instance.PopModalInputHandler();
Add this to your own implementation script, no need to adjust any pre-existing scripts on the InputManager.
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.