forked from gamealgorithms/defense
-
Notifications
You must be signed in to change notification settings - Fork 0
/
InputManager.cs
335 lines (294 loc) · 8.98 KB
/
InputManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
//-----------------------------------------------------------------------------
// InputManager checks for key binds and adds them to the active binds list
// as appropriate.
// The implementation is similar to the one discussed later in Chapter 5.
//
// __Defense Sample for Game Programming Algorithms and Techniques
// Copyright (C) Sanjay Madhav. All rights reserved.
//
// Released under the Microsoft Permissive License.
// See LICENSE.txt for full details.
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
namespace defense
{
public enum eBindType
{
JustPressed, // Was just pressed
JustReleased, // Was just released
Held // Was just pressed OR being held
}
public enum eBindings
{
UI_Exit = 0,
UI_ProjectileTower,
UI_SlowTower,
UI_Upgrade,
UI_Delete,
UI_Stop,
UI_Slow,
UI_Fast,
Pan_Left,
Pan_Forward,
Pan_Right,
Pan_Back,
NUM_BINDINGS
}
public class BindInfo
{
public BindInfo(Keys Key, eBindType Type)
{
m_Key = Key;
m_Type = Type;
}
public Keys m_Key;
public eBindType m_Type;
}
public enum eMouseState
{
Default = 0,
ScrollUp,
ScrollRight,
ScrollDown,
ScrollLeft,
MAX_STATES
}
public class InputManager : defense.Patterns.Singleton<InputManager>
{
// Keyboard binding map
private SortedList<eBindings, BindInfo> m_Bindings;
private void InitializeBindings()
{
m_Bindings = new SortedList<eBindings, BindInfo>();
// UI Bindings
m_Bindings.Add(eBindings.UI_Exit, new BindInfo(Keys.Escape, eBindType.JustPressed));
m_Bindings.Add(eBindings.UI_ProjectileTower, new BindInfo(Keys.E, eBindType.JustPressed));
m_Bindings.Add(eBindings.UI_SlowTower, new BindInfo(Keys.S, eBindType.JustPressed));
m_Bindings.Add(eBindings.UI_Upgrade, new BindInfo(Keys.R, eBindType.JustPressed));
m_Bindings.Add(eBindings.UI_Delete, new BindInfo(Keys.X, eBindType.JustPressed));
m_Bindings.Add(eBindings.UI_Stop, new BindInfo(Keys.Space, eBindType.JustPressed));
m_Bindings.Add(eBindings.UI_Slow, new BindInfo(Keys.OemMinus, eBindType.JustPressed));
m_Bindings.Add(eBindings.UI_Fast, new BindInfo(Keys.OemPlus, eBindType.JustPressed));
// Camera Bindings
m_Bindings.Add(eBindings.Pan_Left, new BindInfo(Keys.Left, eBindType.Held));
m_Bindings.Add(eBindings.Pan_Forward, new BindInfo(Keys.Up, eBindType.Held));
m_Bindings.Add(eBindings.Pan_Right, new BindInfo(Keys.Right, eBindType.Held));
m_Bindings.Add(eBindings.Pan_Back, new BindInfo(Keys.Down, eBindType.Held));
}
private SortedList<eBindings, BindInfo> m_ActiveBinds = new SortedList<eBindings, BindInfo>();
// Mouse Data
private MouseState m_PrevMouse;
private MouseState m_CurrMouse;
eMouseState m_MouseState = eMouseState.Default;
public eMouseState MouseState
{
get { return m_MouseState; }
set { m_MouseState = value; }
}
// The mouse position according to Windows
private Point m_DeviceMousePos = Point.Zero;
// The mouse position taking into account deltas, no clamping
private Point m_ActualMousePos = Point.Zero;
// Mouse position with clamping
private Point m_MousePos = Point.Zero;
public Point MousePosition
{
get { return m_MousePos; }
}
// Keyboard Data
private KeyboardState m_PrevKey;
private KeyboardState m_CurrKey;
public void Start()
{
InitializeBindings();
m_PrevMouse = Mouse.GetState();
m_CurrMouse = Mouse.GetState();
m_DeviceMousePos.X = m_CurrMouse.X;
m_DeviceMousePos.Y = m_CurrMouse.Y;
m_ActualMousePos = m_DeviceMousePos;
m_MousePos = m_ActualMousePos;
ClampMouse();
m_PrevKey = Keyboard.GetState();
m_CurrKey = Keyboard.GetState();
}
private void ClampMouse()
{
if (m_MousePos.X < 0)
{
m_MousePos.X = 0;
}
if (m_MousePos.Y < 0)
{
m_MousePos.Y = 0;
}
if (m_MousePos.X > GraphicsManager.Get().Width)
{
m_MousePos.X = GraphicsManager.Get().Width - GlobalDefines.iMouseCursorSize / 4;
}
if (m_MousePos.Y > GraphicsManager.Get().Height)
{
m_MousePos.Y = GraphicsManager.Get().Height - GlobalDefines.iMouseCursorSize / 4;
}
}
public void UpdateMouse(float fDeltaTime)
{
m_PrevMouse = m_CurrMouse;
m_CurrMouse = Mouse.GetState();
m_DeviceMousePos.X = m_CurrMouse.X;
m_DeviceMousePos.Y = m_CurrMouse.Y;
m_ActualMousePos = m_DeviceMousePos;
m_MousePos = m_ActualMousePos;
ClampMouse();
// Check for click
if (JustPressed(m_PrevMouse.LeftButton, m_CurrMouse.LeftButton))
{
// If the UI doesn't handle it, send it to GameState
if (GameState.Get().UICount == 0 ||
!GameState.Get().GetCurrentUI().MouseClick(m_MousePos))
{
GameState.Get().MouseClick(m_MousePos);
}
}
// Do camera pans and zoom
if (GameState.Get().State == eGameState.Gameplay &&
!GameState.Get().IsPaused)
{
if (m_MousePos.X < GlobalDefines.fCameraScroll)
{
GameState.Get().Camera.AddToPan(Vector3.Left);
}
else if (m_MousePos.X > (GraphicsManager.Get().Width -
GlobalDefines.fCameraScroll))
{
GameState.Get().Camera.AddToPan(Vector3.Right);
}
if (m_MousePos.Y < GlobalDefines.fCameraScroll)
{
GameState.Get().Camera.AddToPan(Vector3.Forward);
}
else if (m_MousePos.Y > (GraphicsManager.Get().Height -
GlobalDefines.fCameraScroll))
{
GameState.Get().Camera.AddToPan(Vector3.Backward);
}
int iWheelChange = m_CurrMouse.ScrollWheelValue - m_PrevMouse.ScrollWheelValue;
if (iWheelChange != 0)
{
// No delta time modifier because iWheelChange will vary based on FPS
float fZoomChange = -1.0f * iWheelChange * GlobalDefines.fCameraZoomSpeed;
GraphicsManager.Get().Zoom += fZoomChange;
}
}
}
public void UpdateKeyboard(float fDeltaTime)
{
m_PrevKey = m_CurrKey;
m_CurrKey = Keyboard.GetState();
m_ActiveBinds.Clear();
// Build the list of bindings which were triggered this frame
foreach (KeyValuePair<eBindings, BindInfo> k in m_Bindings)
{
Keys Key = k.Value.m_Key;
eBindType Type = k.Value.m_Type;
switch (Type)
{
case (eBindType.Held):
if ((m_PrevKey.IsKeyDown(Key) &&
m_CurrKey.IsKeyDown(Key)) ||
(!m_PrevKey.IsKeyDown(Key) &&
m_CurrKey.IsKeyDown(Key)))
{
m_ActiveBinds.Add(k.Key, k.Value);
}
break;
case (eBindType.JustPressed):
if (!m_PrevKey.IsKeyDown(Key) &&
m_CurrKey.IsKeyDown(Key))
{
m_ActiveBinds.Add(k.Key, k.Value);
}
break;
case (eBindType.JustReleased):
if (m_PrevKey.IsKeyDown(Key) &&
!m_CurrKey.IsKeyDown(Key))
{
m_ActiveBinds.Add(k.Key, k.Value);
}
break;
}
}
if (m_ActiveBinds.Count > 0)
{
// Send the list to the UI first, then any remnants to the game
if (GameState.Get().UICount != 0)
{
GameState.Get().GetCurrentUI().KeyboardInput(m_ActiveBinds);
}
GameState.Get().KeyboardInput(m_ActiveBinds);
}
}
public void Update(float fDeltaTime)
{
UpdateMouse(fDeltaTime);
UpdateKeyboard(fDeltaTime);
}
protected bool JustPressed(ButtonState Previous, ButtonState Current)
{
if (Previous == ButtonState.Released &&
Current == ButtonState.Pressed)
{
return true;
}
else
{
return false;
}
}
public Ray CalculateMouseRay()
{
// create 2 positions in screenspace using the cursor position. 0 is as
// close as possible to the camera, 1 is as far away as possible.
Vector3 nearSource = new Vector3(m_MousePos.X, m_MousePos.Y, 0f);
Vector3 farSource = new Vector3(m_MousePos.X, m_MousePos.Y, 1f);
// use Viewport.Unproject to tell what those two screen space positions
// would be in world space. we'll need the projection matrix and view
// matrix, which we have saved as member variables. We also need a world
// matrix, which can just be identity.
GraphicsDevice GraphicsDevice = GraphicsManager.Get().GraphicsDevice;
Matrix projectionMatrix = GraphicsManager.Get().Projection;
Matrix viewMatrix = GameState.Get().CameraMatrix;
Vector3 nearPoint = GraphicsDevice.Viewport.Unproject(nearSource,
projectionMatrix, viewMatrix, Matrix.Identity);
Vector3 farPoint = GraphicsDevice.Viewport.Unproject(farSource,
projectionMatrix, viewMatrix, Matrix.Identity);
// find the direction vector that goes from the nearPoint to the farPoint
// and normalize it....
Vector3 direction = farPoint - nearPoint;
direction.Normalize();
// and then create a new ray using nearPoint as the source.
return new Ray(nearPoint, direction);
}
// Convert key binding to string representing the name
// TODO: THIS IS NOT LOCALIZED
public string GetBinding(eBindings binding)
{
Keys k = m_Bindings[binding].m_Key;
string name = Enum.GetName(typeof(Keys), k);
if (k == Keys.OemPlus)
{
name = "+";
}
else if (k == Keys.OemMinus)
{
name = "-";
}
return name;
}
}
}