//----------------------------------------------------------------------------- // DemoGame.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input.Touch; using Microsoft.Xna.Framework.Media; namespace Graphics3DSample { /// /// This is the main type for your game /// public class Graphics3DSampleGame : Game { const int buttonHeight = 70; const int buttonWidth = 70; const int buttonMargin = 15; GraphicsDeviceManager graphics; Spaceship spaceship; Checkbox[] lightEnablingButtons; Checkbox perpixelLightingButton; Checkbox animationButton; Checkbox backgroundTextureEnablingButton; float cameraFOV = 45; // Initial camera FOV (serves as a zoom level) float rotationXAmount = 0.0f; float rotationYAmount = 0.0f; float? prevLength; Texture2D background; Animation animation; Vector2 animationPosition; // Mouse input tracking private MouseState prevMouseState; private float? prevMouseDragLength = null; /// /// Applies zoom delta to cameraFOV and clamps the value /// private void ApplyZoomDelta(float delta) { cameraFOV -= delta; cameraFOV = MathHelper.Clamp(cameraFOV, maxFOV, minFOV); } /// /// Resets gesture/mouse drag state for zoom /// private void ResetZoomGestureState() { prevLength = null; prevMouseDragLength = null; } /// /// Applies rotation deltas to the camera /// private void ApplyRotationDelta(float deltaX, float deltaY) { rotationXAmount += deltaX; rotationYAmount -= deltaY; } /// /// Toggles checkboxes at a given screen position /// private void ToggleCheckboxesAt(Point pos) { foreach (var checkbox in lightEnablingButtons) { if (checkbox.Bounds.Contains(pos)) checkbox.IsChecked = !checkbox.IsChecked; } if (perpixelLightingButton.Bounds.Contains(pos)) perpixelLightingButton.IsChecked = !perpixelLightingButton.IsChecked; if (animationButton.Bounds.Contains(pos)) animationButton.IsChecked = !animationButton.IsChecked; if (backgroundTextureEnablingButton.Bounds.Contains(pos)) backgroundTextureEnablingButton.IsChecked = !backgroundTextureEnablingButton.IsChecked; } /// /// Provides SporiteBatch to components that draw sprites /// public SpriteBatch SpriteBatch { get; private set; } const float minFOV = 60; const float maxFOV = 30; /// /// Initialization that does not depend on GraphicsDevice /// public Graphics3DSampleGame() { Content.RootDirectory = "Content"; graphics = new GraphicsDeviceManager(this); graphics.IsFullScreen = false; graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight; graphics.ApplyChanges(); IsMouseVisible = true; } /// /// Initialization that depends on GraphicsDevice but does not depend on Content /// protected override void Initialize() { GraphicsDevice.DepthStencilState = new DepthStencilState() { DepthBufferEnable = true }; SpriteBatch = new SpriteBatch(GraphicsDevice); CreateSpaceship(); CreateLightEnablingButtons(); CreateBackgroundTextureEnablingButton(); CreatePerPixelLightingButton(); CreateAnimationButton(); //Initialize gestures support - Pinch for Zoom and horizontal drag for rotate TouchPanel.EnabledGestures = GestureType.FreeDrag | GestureType.Pinch | GestureType.PinchComplete; base.Initialize(); } /// /// Loads content and creates graphics resources. /// protected override void LoadContent() { background = Content.Load("Textures/spaceBG"); animation = CreateAnimation(); spaceship.Load(this.Content); base.LoadContent(); } /// /// Creates animation /// /// private Animation CreateAnimation() { XDocument doc = null; using (var stream = TitleContainer.OpenStream("Content/AnimationDef.xml")) { doc = XDocument.Load(stream); } // Load multiple animations form XML definition XName name = XName.Get("Definition"); var definitions = doc.Document.Descendants(name); // Get the first (and only in this case) animation from the XML definition var definition = definitions.First(); Texture2D texture = Content.Load(definition.Attribute("SheetName").Value); Point frameSize = new Point(); frameSize.X = int.Parse(definition.Attribute("FrameWidth").Value); frameSize.Y = int.Parse(definition.Attribute("FrameHeight").Value); Point sheetSize = new Point(); sheetSize.X = int.Parse(definition.Attribute("SheetColumns").Value); sheetSize.Y = int.Parse(definition.Attribute("SheetRows").Value); TimeSpan frameInterval = TimeSpan.FromSeconds((float)1 / int.Parse(definition.Attribute("Speed").Value)); //Calculate the animation position (in the middle fot he screen) animationPosition = new Vector2((graphics.PreferredBackBufferWidth / 2 - frameSize.X), (graphics.PreferredBackBufferHeight / 2 - frameSize.Y)); return new Animation(texture, frameSize, sheetSize, frameInterval); } /// /// Creates spaceship /// private void CreateSpaceship() { spaceship = new Spaceship(); spaceship.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(cameraFOV), GraphicsDevice.Viewport.AspectRatio, 10, 20000); } /// /// Creates light enabling buttons /// private void CreateLightEnablingButtons() { lightEnablingButtons = new Checkbox[3]; for (int n = 0; n < lightEnablingButtons.Length; n++) { lightEnablingButtons[n] = new Checkbox(this, "Buttons/lamp_60x60", new Rectangle(GraphicsDevice.Viewport.Width - (n + 1) * (buttonWidth + buttonMargin), buttonMargin, buttonWidth, buttonHeight), true); this.Components.Add(lightEnablingButtons[n]); } } /// /// Creates per-pixel lighting button /// private void CreatePerPixelLightingButton() { perpixelLightingButton = new Checkbox(this, "Buttons/perPixelLight_60x60", new Rectangle(GraphicsDevice.Viewport.Width - (buttonWidth + buttonMargin), GraphicsDevice.Viewport.Height - (buttonHeight + buttonMargin), buttonWidth, buttonHeight), false); this.Components.Add(perpixelLightingButton); } /// /// Creates animation button /// private void CreateAnimationButton() { animationButton = new Checkbox(this, "Buttons/animation_60x60", new Rectangle(buttonMargin, GraphicsDevice.Viewport.Height - (buttonHeight + buttonMargin), buttonWidth, buttonHeight), false); this.Components.Add(animationButton); } /// /// Create texture enabling button /// private void CreateBackgroundTextureEnablingButton() { backgroundTextureEnablingButton = new Checkbox(this, "Buttons/textureOnOff", new Rectangle(buttonMargin, buttonMargin, buttonWidth, buttonHeight), false); this.Components.Add(backgroundTextureEnablingButton); } /// /// Updates spaceship rendering properties /// protected override void Update(GameTime gameTime) { // Handle touch/mouse input first HandleInput(); // Handle keyboard input KeyboardState keyboardState = Keyboard.GetState(); // Allows the game to exit if (keyboardState.IsKeyDown(Keys.Escape) || GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); spaceship.Rotation = GetRotationMatrix(); spaceship.View = GetViewMatrix(); spaceship.Lights = lightEnablingButtons.Select(e => e.IsChecked).ToArray(); spaceship.IsTextureEnabled = true; spaceship.IsPerPixelLightingEnabled = perpixelLightingButton.IsChecked; spaceship.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(cameraFOV), GraphicsDevice.Viewport.AspectRatio, 10, 20000); if (animationButton.IsChecked) { animation.Update(gameTime); } base.Update(gameTime); } private void HandleInput() { // Touch input while (TouchPanel.IsGestureAvailable) { GestureSample gestureSample = TouchPanel.ReadGesture(); switch (gestureSample.GestureType) { case GestureType.FreeDrag: ApplyRotationDelta(gestureSample.Delta.X, gestureSample.Delta.Y); break; case GestureType.Pinch: float gestureValue = 0; float gestureLengthToZoomScale = 10; Vector2 gestureDiff = gestureSample.Position - gestureSample.Position2; gestureValue = gestureDiff.Length() / gestureLengthToZoomScale; if (prevLength != null) // Skip the first pinch event ApplyZoomDelta(gestureValue - prevLength.Value); prevLength = gestureValue; break; case GestureType.Tap: Point tapPos = new Point((int)gestureSample.Position.X, (int)gestureSample.Position.Y); ToggleCheckboxesAt(tapPos); break; case GestureType.PinchComplete: ResetZoomGestureState(); break; default: break; } } // Mouse input parity MouseState mouseState = Mouse.GetState(); // Mouse click for UI elements if (prevMouseState.LeftButton == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed) { Point mousePos = new Point(mouseState.X, mouseState.Y); ToggleCheckboxesAt(mousePos); } // Mouse drag for rotation (left button) if (mouseState.LeftButton == ButtonState.Pressed && prevMouseState.LeftButton == ButtonState.Pressed) { int deltaX = mouseState.X - prevMouseState.X; int deltaY = mouseState.Y - prevMouseState.Y; ApplyRotationDelta(deltaX, deltaY); } // Mouse wheel for zoom (FOV) if (mouseState.ScrollWheelValue != prevMouseState.ScrollWheelValue) { float wheelDelta = mouseState.ScrollWheelValue - prevMouseState.ScrollWheelValue; float zoomScale = 0.05f; // Adjust sensitivity as needed ApplyZoomDelta(wheelDelta * zoomScale); } // Mouse drag with right button for pinch-like zoom (optional) if (mouseState.RightButton == ButtonState.Pressed && prevMouseState.RightButton == ButtonState.Pressed) { float gestureLengthToZoomScale = 10f; float gestureValue = Vector2.Distance(new Vector2(mouseState.X, mouseState.Y), new Vector2(prevMouseState.X, prevMouseState.Y)) / gestureLengthToZoomScale; if (prevMouseDragLength != null) ApplyZoomDelta(gestureValue - prevMouseDragLength.Value); prevMouseDragLength = gestureValue; } else if (mouseState.RightButton == ButtonState.Released) { ResetZoomGestureState(); } prevMouseState = mouseState; } /// /// Gets spaceship rotation matrix /// /// private Matrix GetRotationMatrix() { Matrix matrix = Matrix.CreateWorld(new Vector3(0, 250, 0), Vector3.Forward, Vector3.Up) * Matrix.CreateFromYawPitchRoll((float)Math.PI + MathHelper.PiOver2 + rotationXAmount / 100, rotationYAmount / 100, 0); return matrix; } /// /// Gets spaceship view matrix /// private Matrix GetViewMatrix() { return Matrix.CreateLookAt( new Vector3(3500, 400, 0) + new Vector3(0, 250, 0), new Vector3(0, 250, 0), Vector3.Up); } /// /// Draws the game /// protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.MonoGameOrange); if (backgroundTextureEnablingButton.IsChecked) { SpriteBatch.Begin(); SpriteBatch.Draw(background, Vector2.Zero, Color.White); SpriteBatch.End(); } var lights = lightEnablingButtons.Select(e => e.IsChecked).ToArray(); // Set render states. GraphicsDevice.BlendState = BlendState.Opaque; GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise; GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap; // This draws game components, including the currently active menu screen. // Draw the spaceship model spaceship.Draw(); if (animationButton.IsChecked) { DrawAnimation(); } base.Draw(gameTime); } /// /// Draws animation /// private void DrawAnimation() { float screenHeight = graphics.PreferredBackBufferHeight; float scale = (float)(graphics.PreferredBackBufferWidth / 480.0); SpriteBatch.Begin(); animation.Draw(SpriteBatch, animationPosition, 2.0f, SpriteEffects.None); SpriteBatch.End(); } } }