GameMain.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. //-----------------------------------------------------------------------------
  2. // DemoGame.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Xml.Linq;
  11. using Microsoft.Xna.Framework;
  12. using Microsoft.Xna.Framework.Audio;
  13. using Microsoft.Xna.Framework.Content;
  14. using Microsoft.Xna.Framework.Graphics;
  15. using Microsoft.Xna.Framework.Input;
  16. using Microsoft.Xna.Framework.Input.Touch;
  17. using Microsoft.Xna.Framework.Media;
  18. namespace Graphics3DSample
  19. {
  20. /// <summary>
  21. /// This is the main type for your game
  22. /// </summary>
  23. public class Graphics3DSampleGame : Game
  24. {
  25. const int buttonHeight = 70;
  26. const int buttonWidth = 70;
  27. const int buttonMargin = 15;
  28. GraphicsDeviceManager graphics;
  29. Spaceship spaceship;
  30. Checkbox[] lightEnablingButtons;
  31. Checkbox perpixelLightingButton;
  32. Checkbox animationButton;
  33. Checkbox backgroundTextureEnablingButton;
  34. float cameraFOV = 45; // Initial camera FOV (serves as a zoom level)
  35. float rotationXAmount = 0.0f;
  36. float rotationYAmount = 0.0f;
  37. float? prevLength;
  38. Texture2D background;
  39. Animation animation;
  40. Vector2 animationPosition;
  41. // Mouse input tracking
  42. private MouseState prevMouseState;
  43. private float? prevMouseDragLength = null;
  44. /// <summary>
  45. /// Applies zoom delta to cameraFOV and clamps the value
  46. /// </summary>
  47. private void ApplyZoomDelta(float delta)
  48. {
  49. cameraFOV -= delta;
  50. cameraFOV = MathHelper.Clamp(cameraFOV, maxFOV, minFOV);
  51. }
  52. /// <summary>
  53. /// Resets gesture/mouse drag state for zoom
  54. /// </summary>
  55. private void ResetZoomGestureState()
  56. {
  57. prevLength = null;
  58. prevMouseDragLength = null;
  59. }
  60. /// <summary>
  61. /// Applies rotation deltas to the camera
  62. /// </summary>
  63. private void ApplyRotationDelta(float deltaX, float deltaY)
  64. {
  65. rotationXAmount += deltaX;
  66. rotationYAmount -= deltaY;
  67. }
  68. /// <summary>
  69. /// Toggles checkboxes at a given screen position
  70. /// </summary>
  71. private void ToggleCheckboxesAt(Point pos)
  72. {
  73. foreach (var checkbox in lightEnablingButtons)
  74. {
  75. if (checkbox.Bounds.Contains(pos))
  76. checkbox.IsChecked = !checkbox.IsChecked;
  77. }
  78. if (perpixelLightingButton.Bounds.Contains(pos))
  79. perpixelLightingButton.IsChecked = !perpixelLightingButton.IsChecked;
  80. if (animationButton.Bounds.Contains(pos))
  81. animationButton.IsChecked = !animationButton.IsChecked;
  82. if (backgroundTextureEnablingButton.Bounds.Contains(pos))
  83. backgroundTextureEnablingButton.IsChecked = !backgroundTextureEnablingButton.IsChecked;
  84. }
  85. /// <summary>
  86. /// Provides SporiteBatch to components that draw sprites
  87. /// </summary>
  88. public SpriteBatch SpriteBatch { get; private set; }
  89. const float minFOV = 60;
  90. const float maxFOV = 30;
  91. /// <summary>
  92. /// Initialization that does not depend on GraphicsDevice
  93. /// </summary>
  94. public Graphics3DSampleGame()
  95. {
  96. Content.RootDirectory = "Content";
  97. graphics = new GraphicsDeviceManager(this);
  98. graphics.IsFullScreen = false;
  99. graphics.SupportedOrientations =
  100. DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
  101. graphics.ApplyChanges();
  102. IsMouseVisible = true;
  103. }
  104. /// <summary>
  105. /// Initialization that depends on GraphicsDevice but does not depend on Content
  106. /// </summary>
  107. protected override void Initialize()
  108. {
  109. GraphicsDevice.DepthStencilState = new DepthStencilState() { DepthBufferEnable = true };
  110. SpriteBatch = new SpriteBatch(GraphicsDevice);
  111. CreateSpaceship();
  112. CreateLightEnablingButtons();
  113. CreateBackgroundTextureEnablingButton();
  114. CreatePerPixelLightingButton();
  115. CreateAnimationButton();
  116. //Initialize gestures support - Pinch for Zoom and horizontal drag for rotate
  117. TouchPanel.EnabledGestures = GestureType.FreeDrag | GestureType.Pinch | GestureType.PinchComplete;
  118. base.Initialize();
  119. }
  120. /// <summary>
  121. /// Loads content and creates graphics resources.
  122. /// </summary>
  123. protected override void LoadContent()
  124. {
  125. background = Content.Load<Texture2D>("Textures/spaceBG");
  126. animation = CreateAnimation();
  127. spaceship.Load(this.Content);
  128. base.LoadContent();
  129. }
  130. /// <summary>
  131. /// Creates animation
  132. /// </summary>
  133. /// <returns></returns>
  134. private Animation CreateAnimation()
  135. {
  136. XDocument doc = null;
  137. using (var stream = TitleContainer.OpenStream("Content/AnimationDef.xml"))
  138. {
  139. doc = XDocument.Load(stream);
  140. }
  141. // Load multiple animations form XML definition
  142. XName name = XName.Get("Definition");
  143. var definitions = doc.Document.Descendants(name);
  144. // Get the first (and only in this case) animation from the XML definition
  145. var definition = definitions.First();
  146. Texture2D texture = Content.Load<Texture2D>(definition.Attribute("SheetName").Value);
  147. Point frameSize = new Point();
  148. frameSize.X = int.Parse(definition.Attribute("FrameWidth").Value);
  149. frameSize.Y = int.Parse(definition.Attribute("FrameHeight").Value);
  150. Point sheetSize = new Point();
  151. sheetSize.X = int.Parse(definition.Attribute("SheetColumns").Value);
  152. sheetSize.Y = int.Parse(definition.Attribute("SheetRows").Value);
  153. TimeSpan frameInterval = TimeSpan.FromSeconds((float)1 / int.Parse(definition.Attribute("Speed").Value));
  154. //Calculate the animation position (in the middle fot he screen)
  155. animationPosition = new Vector2((graphics.PreferredBackBufferWidth / 2 - frameSize.X),
  156. (graphics.PreferredBackBufferHeight / 2 - frameSize.Y));
  157. return new Animation(texture, frameSize, sheetSize, frameInterval);
  158. }
  159. /// <summary>
  160. /// Creates spaceship
  161. /// </summary>
  162. private void CreateSpaceship()
  163. {
  164. spaceship = new Spaceship();
  165. spaceship.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(cameraFOV),
  166. GraphicsDevice.Viewport.AspectRatio, 10, 20000);
  167. }
  168. /// <summary>
  169. /// Creates light enabling buttons
  170. /// </summary>
  171. private void CreateLightEnablingButtons()
  172. {
  173. lightEnablingButtons = new Checkbox[3];
  174. for (int n = 0; n < lightEnablingButtons.Length; n++)
  175. {
  176. lightEnablingButtons[n] = new Checkbox(this, "Buttons/lamp_60x60",
  177. new Rectangle(GraphicsDevice.Viewport.Width - (n + 1) * (buttonWidth + buttonMargin),
  178. buttonMargin, buttonWidth, buttonHeight), true);
  179. this.Components.Add(lightEnablingButtons[n]);
  180. }
  181. }
  182. /// <summary>
  183. /// Creates per-pixel lighting button
  184. /// </summary>
  185. private void CreatePerPixelLightingButton()
  186. {
  187. perpixelLightingButton = new Checkbox(this, "Buttons/perPixelLight_60x60",
  188. new Rectangle(GraphicsDevice.Viewport.Width - (buttonWidth + buttonMargin),
  189. GraphicsDevice.Viewport.Height - (buttonHeight + buttonMargin),
  190. buttonWidth, buttonHeight), false);
  191. this.Components.Add(perpixelLightingButton);
  192. }
  193. /// <summary>
  194. /// Creates animation button
  195. /// </summary>
  196. private void CreateAnimationButton()
  197. {
  198. animationButton = new Checkbox(this, "Buttons/animation_60x60",
  199. new Rectangle(buttonMargin,
  200. GraphicsDevice.Viewport.Height - (buttonHeight + buttonMargin),
  201. buttonWidth, buttonHeight), false);
  202. this.Components.Add(animationButton);
  203. }
  204. /// <summary>
  205. /// Create texture enabling button
  206. /// </summary>
  207. private void CreateBackgroundTextureEnablingButton()
  208. {
  209. backgroundTextureEnablingButton = new Checkbox(this, "Buttons/textureOnOff",
  210. new Rectangle(buttonMargin, buttonMargin, buttonWidth, buttonHeight), false);
  211. this.Components.Add(backgroundTextureEnablingButton);
  212. }
  213. /// <summary>
  214. /// Updates spaceship rendering properties
  215. /// </summary>
  216. protected override void Update(GameTime gameTime)
  217. {
  218. // Handle touch/mouse input first
  219. HandleInput();
  220. // Handle keyboard input
  221. KeyboardState keyboardState = Keyboard.GetState();
  222. // Allows the game to exit
  223. if (keyboardState.IsKeyDown(Keys.Escape)
  224. || GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
  225. this.Exit();
  226. spaceship.Rotation = GetRotationMatrix();
  227. spaceship.View = GetViewMatrix();
  228. spaceship.Lights = lightEnablingButtons.Select(e => e.IsChecked).ToArray();
  229. spaceship.IsTextureEnabled = true;
  230. spaceship.IsPerPixelLightingEnabled = perpixelLightingButton.IsChecked;
  231. spaceship.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(cameraFOV),
  232. GraphicsDevice.Viewport.AspectRatio, 10, 20000);
  233. if (animationButton.IsChecked)
  234. {
  235. animation.Update(gameTime);
  236. }
  237. base.Update(gameTime);
  238. }
  239. private void HandleInput()
  240. {
  241. // Touch input
  242. while (TouchPanel.IsGestureAvailable)
  243. {
  244. GestureSample gestureSample = TouchPanel.ReadGesture();
  245. switch (gestureSample.GestureType)
  246. {
  247. case GestureType.FreeDrag:
  248. ApplyRotationDelta(gestureSample.Delta.X, gestureSample.Delta.Y);
  249. break;
  250. case GestureType.Pinch:
  251. float gestureValue = 0;
  252. float gestureLengthToZoomScale = 10;
  253. Vector2 gestureDiff = gestureSample.Position - gestureSample.Position2;
  254. gestureValue = gestureDiff.Length() / gestureLengthToZoomScale;
  255. if (prevLength != null) // Skip the first pinch event
  256. ApplyZoomDelta(gestureValue - prevLength.Value);
  257. prevLength = gestureValue;
  258. break;
  259. case GestureType.Tap:
  260. Point tapPos = new Point((int)gestureSample.Position.X, (int)gestureSample.Position.Y);
  261. ToggleCheckboxesAt(tapPos);
  262. break;
  263. case GestureType.PinchComplete:
  264. ResetZoomGestureState();
  265. break;
  266. default:
  267. break;
  268. }
  269. }
  270. // Mouse input parity
  271. MouseState mouseState = Mouse.GetState();
  272. // Mouse click for UI elements
  273. if (prevMouseState.LeftButton == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed)
  274. {
  275. Point mousePos = new Point(mouseState.X, mouseState.Y);
  276. ToggleCheckboxesAt(mousePos);
  277. }
  278. // Mouse drag for rotation (left button)
  279. if (mouseState.LeftButton == ButtonState.Pressed && prevMouseState.LeftButton == ButtonState.Pressed)
  280. {
  281. int deltaX = mouseState.X - prevMouseState.X;
  282. int deltaY = mouseState.Y - prevMouseState.Y;
  283. ApplyRotationDelta(deltaX, deltaY);
  284. }
  285. // Mouse wheel for zoom (FOV)
  286. if (mouseState.ScrollWheelValue != prevMouseState.ScrollWheelValue)
  287. {
  288. float wheelDelta = mouseState.ScrollWheelValue - prevMouseState.ScrollWheelValue;
  289. float zoomScale = 0.05f; // Adjust sensitivity as needed
  290. ApplyZoomDelta(wheelDelta * zoomScale);
  291. }
  292. // Mouse drag with right button for pinch-like zoom (optional)
  293. if (mouseState.RightButton == ButtonState.Pressed && prevMouseState.RightButton == ButtonState.Pressed)
  294. {
  295. float gestureLengthToZoomScale = 10f;
  296. float gestureValue = Vector2.Distance(new Vector2(mouseState.X, mouseState.Y), new Vector2(prevMouseState.X, prevMouseState.Y)) / gestureLengthToZoomScale;
  297. if (prevMouseDragLength != null)
  298. ApplyZoomDelta(gestureValue - prevMouseDragLength.Value);
  299. prevMouseDragLength = gestureValue;
  300. }
  301. else if (mouseState.RightButton == ButtonState.Released)
  302. {
  303. ResetZoomGestureState();
  304. }
  305. prevMouseState = mouseState;
  306. }
  307. /// <summary>
  308. /// Gets spaceship rotation matrix
  309. /// </summary>
  310. /// <returns></returns>
  311. private Matrix GetRotationMatrix()
  312. {
  313. Matrix matrix = Matrix.CreateWorld(new Vector3(0, 250, 0), Vector3.Forward, Vector3.Up) *
  314. Matrix.CreateFromYawPitchRoll((float)Math.PI + MathHelper.PiOver2 + rotationXAmount / 100, rotationYAmount / 100, 0);
  315. return matrix;
  316. }
  317. /// <summary>
  318. /// Gets spaceship view matrix
  319. /// </summary>
  320. private Matrix GetViewMatrix()
  321. {
  322. return Matrix.CreateLookAt(
  323. new Vector3(3500, 400, 0) + new Vector3(0, 250, 0),
  324. new Vector3(0, 250, 0),
  325. Vector3.Up);
  326. }
  327. /// <summary>
  328. /// Draws the game
  329. /// </summary>
  330. protected override void Draw(GameTime gameTime)
  331. {
  332. GraphicsDevice.Clear(Color.MonoGameOrange);
  333. if (backgroundTextureEnablingButton.IsChecked)
  334. {
  335. SpriteBatch.Begin();
  336. SpriteBatch.Draw(background, Vector2.Zero, Color.White);
  337. SpriteBatch.End();
  338. }
  339. var lights = lightEnablingButtons.Select(e => e.IsChecked).ToArray();
  340. // Set render states.
  341. GraphicsDevice.BlendState = BlendState.Opaque;
  342. GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
  343. GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  344. GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
  345. // This draws game components, including the currently active menu screen.
  346. // Draw the spaceship model
  347. spaceship.Draw();
  348. if (animationButton.IsChecked)
  349. {
  350. DrawAnimation();
  351. }
  352. base.Draw(gameTime);
  353. }
  354. /// <summary>
  355. /// Draws animation
  356. /// </summary>
  357. private void DrawAnimation()
  358. {
  359. float screenHeight = graphics.PreferredBackBufferHeight;
  360. float scale = (float)(graphics.PreferredBackBufferWidth / 480.0);
  361. SpriteBatch.Begin();
  362. animation.Draw(SpriteBatch, animationPosition, 2.0f, SpriteEffects.None);
  363. SpriteBatch.End();
  364. }
  365. }
  366. }