Added event-driven keyboard encapsulation
This commit is contained in:
parent
e41bf50f59
commit
1fb795161d
104
TrueCraft.Client/Input/KeyboardComponent.cs
Normal file
104
TrueCraft.Client/Input/KeyboardComponent.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace TrueCraft.Client.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates keyboard input in an event-driven manner.
|
||||
/// </summary>
|
||||
public sealed class KeyboardComponent : GameComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised when a key for this keyboard component is pressed.
|
||||
/// </summary>
|
||||
public event EventHandler<KeyboardKeyEventArgs> KeyDown;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a key for this keyboard component is released.
|
||||
/// </summary>
|
||||
public event EventHandler<KeyboardKeyEventArgs> KeyUp;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state for this keyboard component.
|
||||
/// </summary>
|
||||
public KeyboardState State { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new keyboard component.
|
||||
/// </summary>
|
||||
/// <param name="game">The parent game for the component.</param>
|
||||
public KeyboardComponent(Game game)
|
||||
: base(game)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this keyboard component.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
State = Keyboard.GetState();
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this keyboard component.
|
||||
/// </summary>
|
||||
/// <param name="gameTime">The game time for the update.</param>
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
var newState = Keyboard.GetState();
|
||||
Process(newState, State);
|
||||
State = newState;
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a change between two states.
|
||||
/// </summary>
|
||||
/// <param name="newState">The new state.</param>
|
||||
/// <param name="oldState">The old state.</param>
|
||||
private void Process(KeyboardState newState, KeyboardState oldState)
|
||||
{
|
||||
var currentKeys = newState.GetPressedKeys();
|
||||
var lastKeys = oldState.GetPressedKeys();
|
||||
|
||||
// LINQ was a saviour here.
|
||||
var pressed = currentKeys.Except(lastKeys);
|
||||
var unpressed = lastKeys.Except(currentKeys);
|
||||
|
||||
foreach (var key in pressed)
|
||||
{
|
||||
var args = new KeyboardKeyEventArgs(key, true);
|
||||
if (KeyDown != null)
|
||||
KeyDown(this, args);
|
||||
}
|
||||
|
||||
foreach (var key in unpressed)
|
||||
{
|
||||
var args = new KeyboardKeyEventArgs(key, false);
|
||||
if (KeyUp != null)
|
||||
KeyUp(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when this keyboard component is being disposed of.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Whether Dispose() called this method.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
KeyDown = null;
|
||||
KeyUp = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
11
TrueCraft.Client/Input/KeyboardEventArgs.cs
Normal file
11
TrueCraft.Client/Input/KeyboardEventArgs.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace TrueCraft.Client.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the event data for keyboard events.
|
||||
/// </summary>
|
||||
public class KeyboardEventArgs : EventArgs
|
||||
{
|
||||
}
|
||||
}
|
32
TrueCraft.Client/Input/KeyboardKeyEventArgs.cs
Normal file
32
TrueCraft.Client/Input/KeyboardKeyEventArgs.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace TrueCraft.Client.Input
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class KeyboardKeyEventArgs : KeyboardEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Keys Key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsPressed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="isPressed"></param>
|
||||
public KeyboardKeyEventArgs(Keys key, bool isPressed)
|
||||
{
|
||||
Key = key;
|
||||
IsPressed = isPressed;
|
||||
}
|
||||
}
|
||||
}
|
@ -94,6 +94,9 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Input\KeyboardComponent.cs" />
|
||||
<Compile Include="Input\KeyboardEventArgs.cs" />
|
||||
<Compile Include="Input\KeyboardKeyEventArgs.cs" />
|
||||
<Compile Include="Input\MouseButton.cs" />
|
||||
<Compile Include="Input\MouseButtonEventArgs.cs" />
|
||||
<Compile Include="Input\MouseComponent.cs" />
|
||||
|
@ -37,9 +37,10 @@ namespace TrueCraft.Client
|
||||
private BoundingFrustum CameraView;
|
||||
private Camera Camera;
|
||||
private bool MouseCaptured;
|
||||
private KeyboardState PreviousKeyboardState;
|
||||
private KeyboardComponent KeyboardComponent { get; set; }
|
||||
private MouseComponent MouseComponent { get; set; }
|
||||
private GameTime GameTime { get; set; }
|
||||
private Microsoft.Xna.Framework.Vector3 Delta { get; set; }
|
||||
|
||||
private BasicEffect OpaqueEffect, TransparentEffect;
|
||||
|
||||
@ -61,6 +62,10 @@ namespace TrueCraft.Client
|
||||
PendingMainThreadActions = new ConcurrentBag<Action>();
|
||||
MouseCaptured = true;
|
||||
|
||||
var keyboardComponent = new KeyboardComponent(this);
|
||||
KeyboardComponent = keyboardComponent;
|
||||
Components.Add(keyboardComponent);
|
||||
|
||||
var mouseComponent = new MouseComponent(this);
|
||||
MouseComponent = mouseComponent;
|
||||
Components.Add(mouseComponent);
|
||||
@ -83,9 +88,10 @@ namespace TrueCraft.Client
|
||||
Mouse.SetPosition(centerX, centerY);
|
||||
Camera = new Camera(GraphicsDevice.Viewport.AspectRatio, 70.0f, 0.1f, 1000.0f);
|
||||
UpdateCamera();
|
||||
PreviousKeyboardState = Keyboard.GetState();
|
||||
Window.ClientSizeChanged += (sender, e) => CreateRenderTarget();
|
||||
MouseComponent.Move += OnMouseComponentMove;
|
||||
KeyboardComponent.KeyDown += OnKeyboardKeyDown;
|
||||
KeyboardComponent.KeyUp += OnKeyboardKeyUp;
|
||||
CreateRenderTarget();
|
||||
}
|
||||
|
||||
@ -151,46 +157,83 @@ namespace TrueCraft.Client
|
||||
base.OnExiting(sender, args);
|
||||
}
|
||||
|
||||
protected virtual void UpdateKeyboard(GameTime gameTime, KeyboardState state, KeyboardState oldState)
|
||||
private void OnKeyboardKeyDown(object sender, KeyboardKeyEventArgs e)
|
||||
{
|
||||
if (state.IsKeyDown(Keys.Escape))
|
||||
Exit();
|
||||
|
||||
// TODO: Rebindable keys
|
||||
// TODO: Horizontal terrain collisions
|
||||
|
||||
if (state.IsKeyDown(Keys.F2) && oldState.IsKeyUp(Keys.F2)) // Take a screenshot
|
||||
switch (e.Key)
|
||||
{
|
||||
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".truecraft", "screenshots", DateTime.Now.ToString("yyyy-MM-dd_H.mm.ss") + ".png");
|
||||
if (!Directory.Exists(Path.GetDirectoryName(path)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
using (var stream = File.OpenWrite(path))
|
||||
RenderTarget.SaveAsPng(stream, RenderTarget.Width, RenderTarget.Height);
|
||||
ChatInterface.AddMessage(string.Format("Screenshot saved as {0}", Path.GetFileName(path)));
|
||||
// Quit the game.
|
||||
case Keys.Escape:
|
||||
Exit();
|
||||
break;
|
||||
|
||||
// Take a screenshot.
|
||||
case Keys.F2:
|
||||
TakeScreenshot();
|
||||
break;
|
||||
|
||||
// Move to the left.
|
||||
case Keys.A:
|
||||
case Keys.Left:
|
||||
Delta += Microsoft.Xna.Framework.Vector3.Left;
|
||||
break;
|
||||
|
||||
// Move to the right.
|
||||
case Keys.D:
|
||||
case Keys.Right:
|
||||
Delta += Microsoft.Xna.Framework.Vector3.Right;
|
||||
break;
|
||||
|
||||
// Move forwards.
|
||||
case Keys.W:
|
||||
case Keys.Up:
|
||||
Delta += Microsoft.Xna.Framework.Vector3.Forward;
|
||||
break;
|
||||
|
||||
// Move backwards.
|
||||
case Keys.S:
|
||||
case Keys.Down:
|
||||
Delta += Microsoft.Xna.Framework.Vector3.Backward;
|
||||
break;
|
||||
|
||||
// Toggle mouse capture.
|
||||
case Keys.Tab:
|
||||
MouseCaptured = !MouseCaptured;
|
||||
break;
|
||||
}
|
||||
|
||||
Microsoft.Xna.Framework.Vector3 delta = Microsoft.Xna.Framework.Vector3.Zero;
|
||||
}
|
||||
|
||||
if (state.IsKeyDown(Keys.Left) || state.IsKeyDown(Keys.A))
|
||||
delta += Microsoft.Xna.Framework.Vector3.Left;
|
||||
if (state.IsKeyDown(Keys.Right) || state.IsKeyDown(Keys.D))
|
||||
delta += Microsoft.Xna.Framework.Vector3.Right;
|
||||
if (state.IsKeyDown(Keys.Up) || state.IsKeyDown(Keys.W))
|
||||
delta += Microsoft.Xna.Framework.Vector3.Forward;
|
||||
if (state.IsKeyDown(Keys.Down) || state.IsKeyDown(Keys.S))
|
||||
delta += Microsoft.Xna.Framework.Vector3.Backward;
|
||||
|
||||
if (delta != Microsoft.Xna.Framework.Vector3.Zero)
|
||||
private void OnKeyboardKeyUp(object sender, KeyboardKeyEventArgs e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
var lookAt = Microsoft.Xna.Framework.Vector3.Transform(
|
||||
delta, Matrix.CreateRotationY(MathHelper.ToRadians(Client.Yaw)));
|
||||
// Stop moving to the left.
|
||||
case Keys.A:
|
||||
case Keys.Left:
|
||||
Delta -= Microsoft.Xna.Framework.Vector3.Left;
|
||||
break;
|
||||
|
||||
Client.Position += new TrueCraft.API.Vector3(lookAt.X, lookAt.Y, lookAt.Z) * (gameTime.ElapsedGameTime.TotalSeconds * 4.3717);
|
||||
// Stop moving to the right.
|
||||
case Keys.D:
|
||||
case Keys.Right:
|
||||
Delta -= Microsoft.Xna.Framework.Vector3.Right;
|
||||
break;
|
||||
|
||||
// Stop moving forwards.
|
||||
case Keys.W:
|
||||
case Keys.Up:
|
||||
Delta -= Microsoft.Xna.Framework.Vector3.Forward;
|
||||
break;
|
||||
|
||||
// Stop moving backwards.
|
||||
case Keys.S:
|
||||
case Keys.Down:
|
||||
Delta -= Microsoft.Xna.Framework.Vector3.Backward;
|
||||
break;
|
||||
}
|
||||
|
||||
if (state.IsKeyUp(Keys.Tab) && oldState.IsKeyDown(Keys.Tab))
|
||||
MouseCaptured = !MouseCaptured;
|
||||
}
|
||||
|
||||
private void OnMouseComponentMove(object sender, MouseMoveEventArgs e)
|
||||
@ -214,6 +257,17 @@ namespace TrueCraft.Client
|
||||
}
|
||||
}
|
||||
|
||||
private void TakeScreenshot()
|
||||
{
|
||||
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".truecraft", "screenshots", DateTime.Now.ToString("yyyy-MM-dd_H.mm.ss") + ".png");
|
||||
if (!Directory.Exists(Path.GetDirectoryName(path)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
using (var stream = File.OpenWrite(path))
|
||||
RenderTarget.SaveAsPng(stream, RenderTarget.Width, RenderTarget.Height);
|
||||
ChatInterface.AddMessage(string.Format("Screenshot saved as {0}", Path.GetFileName(path)));
|
||||
}
|
||||
|
||||
protected override void Update(GameTime gameTime)
|
||||
{
|
||||
GameTime = gameTime;
|
||||
@ -254,9 +308,16 @@ namespace TrueCraft.Client
|
||||
Client.Position.Y + MultiplayerClient.Height, Client.Position.Z, Client.Yaw, Client.Pitch, false));
|
||||
NextPhysicsUpdate = DateTime.Now.AddMilliseconds(1000 / 20);
|
||||
}
|
||||
var state = Keyboard.GetState();
|
||||
UpdateKeyboard(gameTime, state, PreviousKeyboardState);
|
||||
PreviousKeyboardState = state;
|
||||
|
||||
if (Delta != Microsoft.Xna.Framework.Vector3.Zero)
|
||||
{
|
||||
var lookAt = Microsoft.Xna.Framework.Vector3.Transform(
|
||||
Delta, Matrix.CreateRotationY(MathHelper.ToRadians(Client.Yaw)));
|
||||
|
||||
Client.Position += new TrueCraft.API.Vector3(lookAt.X, lookAt.Y, lookAt.Z)
|
||||
* (gameTime.ElapsedGameTime.TotalSeconds * 4.3717);
|
||||
}
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user