Listati di codice completi

In questo argomento è contenuto l'intero listato di codice per un'applicazione che utilizza processori di Modifiche e inerzia.

Classe GamePiece

Nel codice riportato di seguito viene illustrata l'intera classe GamePiece.

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Windows.Input.Manipulations;

namespace ManipulationXNA
{

    public class GamePiece
    {
        #region PrivateMembers
        // The sprite batch used for drawing the game piece.
        private SpriteBatch spriteBatch;
        // The position of the game piece.
        private Vector2 position;
        // The origin used for rendering the game piece.
        // Gets set to be the center of the piece.
        private Vector2 origin;
        // The texture for the piece.
        private Texture2D texture;
        // The bounds of the game piece. Used for hit testing.
        private Rectangle bounds;
        // The rotation of the game piece, in radians.
        private float rotation;
        // The scale, in percentage of the actual image size. 1.0 = 100%.
        private float scale;
        // The view port, used to detect when to bounce.
        private Viewport viewport;
        // The manipulation processor for this game piece.
        private ManipulationProcessor2D manipulationProcessor;
        // The inertia processor for this game piece.
        private InertiaProcessor2D inertiaProcessor;
        // Flag to indicate that inertia processing should start or continue.
        private bool processInertia;
        // Flag to indicate whether this piece has captured the mouse.
        private bool isMouseCaptured;
        // Used during manipulation to indicate where the drag is occurring.
        private System.Windows.Point dragPoint;
        // The color of the game piece.
        private Color pieceColor;
        // Represents how spongy the walls act when the piece bounces.
        // Must be <= 1.0 (if greater than 1.0, the piece will accelerate on bounce)
        // 1.0 = no slowdown during a bounce.
        // 0.0 (or less) = won't bounce.
        private float spongeFactor = 0.925f;
        #endregion

        /*******************************************/

        #region Constructor
        public GamePiece(SpriteBatch spriteBatch, string fileName)
        {
            // For brevity, omitting checking of null parameters.
            this.spriteBatch = spriteBatch;

            // Get the texture from the specified file.
            texture = Texture2D.FromFile(spriteBatch.GraphicsDevice, fileName);

            // Initial position set to 0,0.
            position = new Vector2(0);

            // Set the origin to be the center of the texture.
            origin = new Vector2(texture.Width / 2.0f, texture.Height / 2.0f);

            // Set bounds. bounds.X and bounds.Y are set as the position or scale changes.
            bounds = new Rectangle(0, 0, texture.Width, texture.Height);

            // Create manipulation processor.
            Manipulations2D enabledManipulations =
                Manipulations2D.Translate | Manipulations2D.Rotate;
            manipulationProcessor = new ManipulationProcessor2D(enabledManipulations);

            manipulationProcessor.Pivot = new ManipulationPivot2D();
            manipulationProcessor.Pivot.Radius = texture.Width / 2;

            manipulationProcessor.MinimumScaleRotateRadius = 10.0f;

            manipulationProcessor.Started += OnManipulationStarted;
            manipulationProcessor.Delta += OnManipulationDelta;
            manipulationProcessor.Completed += OnManipulationCompleted;

            // Create inertia processor.
            inertiaProcessor = new InertiaProcessor2D();
            inertiaProcessor.Delta += OnInertiaDelta;
            inertiaProcessor.Completed += OnInertiaCompleted;

            inertiaProcessor.TranslationBehavior.DesiredDeceleration = 0.0001F;
            inertiaProcessor.RotationBehavior.DesiredDeceleration = 1e-6F;
            inertiaProcessor.ExpansionBehavior.DesiredDeceleration = 0.0001F;

            // Save the view port. Used to detect when the piece needs to bounce.
            viewport = spriteBatch.GraphicsDevice.Viewport;

            // Set the piece in a random location.
            Random random = new Random((int)Timestamp);
            X = random.Next(viewport.Width);
            Y = random.Next(viewport.Height);

            // Set a random orientation.
            rotation = (float)(random.NextDouble() * Math.PI * 2.0);

            dragPoint = new System.Windows.Point(double.NaN, double.NaN);
            pieceColor = Color.White;

            // Set scale to normal (100%)
            Scale = 1.0f;
        }
        #endregion

        /*******************************************/

        #region PublicProperties
        public float Scale
        {
            get { return scale; }
            set 
            { 
                scale = value;
                bounds.Width = (int)(texture.Width * value);
                bounds.Height = (int)(texture.Height * value);
                // Setting X and Y (private properties) causes 
                // bounds.X and bounds.Y to adjust to the scale factor.
                X = X;
                Y = Y;
            }
        }

        public Color PieceColor
        {
            get { return pieceColor; }
            set { pieceColor = value; }
        }

        public Rectangle Bounds
        {
            get { return bounds; }
        }
        #endregion

        /*******************************************/

        #region ProcessInertia
        public void ProcessInertia()
        {
            if (processInertia)
            {
                inertiaProcessor.Process(Timestamp);
            }
        }
        #endregion

        #region UpdateFromMouse
        public bool UpdateFromMouse(MouseState mouseState)
        {
            if (mouseState.LeftButton == ButtonState.Released)
            {
                if (isMouseCaptured)
                {
                    manipulationProcessor.CompleteManipulation(Timestamp);
                }
                isMouseCaptured = false;
            }

            if (isMouseCaptured ||
               (mouseState.LeftButton == ButtonState.Pressed &&
               bounds.Contains(mouseState.X, mouseState.Y)))
            {
                isMouseCaptured = true;

                Manipulator2D[] manipulators = new Manipulator2D[] 
                {
                    new Manipulator2D(0, mouseState.X, mouseState.Y)
                };

                dragPoint.X = mouseState.X;
                dragPoint.Y = mouseState.Y;
                manipulationProcessor.ProcessManipulators(Timestamp, manipulators);
            }

            // If the right button is pressed, stop the piece and move it to the center.
            if (mouseState.RightButton == ButtonState.Pressed)
            {
                processInertia = false;
                X = viewport.Width / 2;
                Y = viewport.Height / 2;
                rotation = 0;
            }
            return isMouseCaptured;
        }
        #endregion

        #region Draw
        public void Draw()
        {
            spriteBatch.Draw(
                texture, position,
                null, pieceColor, rotation,
                origin, scale,
                SpriteEffects.None, 1.0f);
        }

        public void Draw(Rectangle bounds)
        {
            spriteBatch.Draw(texture, bounds, pieceColor);
        }
        #endregion

        /*******************************************/

        #region PrivateProperties
        private long Timestamp
        {
            get 
            {
                // Get timestamp in 100-nanosecond units.
                double nanosecondsPerTick = 1000000000.0 / System.Diagnostics.Stopwatch.Frequency;
                return (long)(System.Diagnostics.Stopwatch.GetTimestamp() / nanosecondsPerTick / 100.0);
            }
        }

        private float X
        {
            get { return position.X; }
            set
            {
                position.X = value;
                manipulationProcessor.Pivot.X = value;
                bounds.X = (int)(position.X - (origin.X * scale));
            }
        }

        private float Y
        {
            get { return position.Y; }
            set
            {
                position.Y = value;
                manipulationProcessor.Pivot.Y = value;
                bounds.Y = (int)(position.Y - (origin.Y * scale));
            }
        }
        #endregion

        /** Manipulation and Inertia events ********/

        #region OnManipulationStarted
        private void OnManipulationStarted(object sender, Manipulation2DStartedEventArgs e)
        {
            if (inertiaProcessor.IsRunning)
            {
                inertiaProcessor.Complete(Timestamp);
            }
            processInertia = false;
        }
        #endregion

        #region OnManipulationDelta
        private void OnManipulationDelta(object sender, Manipulation2DDeltaEventArgs e)
        {
            //// Adjust the position and rotation of the game piece.
            float deltaX = e.Delta.TranslationX;
            float deltaY = e.Delta.TranslationY;
            if (dragPoint.X != double.NaN || dragPoint.Y != double.NaN)
            {
                // Single-manipulator-drag-rotate mode. Adjust for drag / rotation
                System.Windows.Point center = new System.Windows.Point(position.X, position.Y);
                System.Windows.Vector toCenter = center - dragPoint;
                double sin = Math.Sin(e.Delta.Rotation);
                double cos = Math.Cos(e.Delta.Rotation);
                System.Windows.Vector rotatedToCenter =
                    new System.Windows.Vector(
                        toCenter.X * cos - toCenter.Y * sin,
                        toCenter.X * sin + toCenter.Y * cos);
                System.Windows.Vector shift = rotatedToCenter - toCenter;
                deltaX += (float)shift.X;
                deltaY += (float)shift.Y;
            }

            X += deltaX;
            Y += deltaY;
            rotation += e.Delta.Rotation;
        }
        #endregion

        #region OnManipulationCompleted
        private void OnManipulationCompleted(object sender, Manipulation2DCompletedEventArgs e)
        {
            inertiaProcessor.TranslationBehavior.InitialVelocityX = e.Velocities.LinearVelocityX;
            inertiaProcessor.TranslationBehavior.InitialVelocityY = e.Velocities.LinearVelocityY;
            inertiaProcessor.RotationBehavior.InitialVelocity = e.Velocities.AngularVelocity;
            processInertia = true;
        }
        #endregion

        #region OnInertiaDelta
        private void OnInertiaDelta(object sender, Manipulation2DDeltaEventArgs e)
        {
            // Adjust the position of the game piece.
            X += e.Delta.TranslationX;
            Y += e.Delta.TranslationY;
            rotation += e.Delta.Rotation;

            // Check to see if the piece has hit the edge of the view port.
            bool reverseX = false;
            bool reverseY = false;

            if (X > viewport.Width)
            {
                reverseX = true;
                X = viewport.Width;
            }

            else if (X < viewport.X)
            {
                reverseX = true;
                X = viewport.X;
            }

            if (Y > viewport.Height)
            {
                reverseY = true;
                Y = viewport.Height;
            }

            else if (Y < viewport.Y)
            {
                reverseY = true;
                Y = viewport.Y;
            }

            if (reverseX || reverseY)
            {
                // Get the current velocities, reversing as needed.
                // If reversing, apply sponge factor to slow the piece slightly.
                float velocityX = e.Velocities.LinearVelocityX * ((reverseX) ? -spongeFactor : 1.0f);
                float velocityY = e.Velocities.LinearVelocityY * ((reverseY) ? -spongeFactor : 1.0f);
                // Must stop inertia processing before changing parameters.
                if (inertiaProcessor.IsRunning)
                {
                    inertiaProcessor.Complete(Timestamp);
                }
                // Assign the new velocities.
                inertiaProcessor.TranslationBehavior.InitialVelocityX = velocityX;
                inertiaProcessor.TranslationBehavior.InitialVelocityY = velocityY;
                // Set flag so that inertia processing will continue.
                processInertia = true;
            }
        }
        #endregion

        #region OnInertiaCompleted
        private void OnInertiaCompleted(object sender, Manipulation2DCompletedEventArgs e)
        {
            processInertia = false;
        }
        #endregion
    }
}

Classe GamePieceCollection

Nel codice riportato di seguito viene illustrata l'intera classe GamePieceCollection.

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;

namespace ManipulationXNA
{
    public class GamePieceCollection : List<GamePiece>
    {
        #region PrivateMembersAndConstructor
        private int capturedIndex;

        public GamePieceCollection()
        {
            // No capture yet.
            capturedIndex = -1;
        }
        #endregion

        #region ProcessInertiaAndDraw
        public void ProcessInertia()
        {
            foreach (GamePiece piece in this)
            {
                piece.ProcessInertia();
            }
        }

        public void Draw()
        {
            foreach (GamePiece piece in this)
            {
                piece.Draw();
            }
        }
        #endregion

        #region UpdateFromMouse
        public void UpdateFromMouse()
        {
            MouseState mouseState = Mouse.GetState();

            // If there is a current capture and
            // that piece still reports having the capture,
            // then return. Another piece cannot have the capture now.
            if (capturedIndex >= 0 && 
                capturedIndex < Count 
                && this[capturedIndex].UpdateFromMouse(mouseState))
            {
                return;
            }

            capturedIndex = -1;
            // A higher numbered index gets first chance
            // for a capture. It is "on top" of a lower numbered index.
            for (int index = Count - 1; index >= 0; index--)
            {
                if (this[index].UpdateFromMouse(mouseState))
                {
                    capturedIndex = index;
                    return;
                }
            }
        }
        #endregion
    }
}

Classe Game1

Nel codice riportato di seguito viene illustrata l'intera classe Game1.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Windows.Input.Manipulations;

namespace ManipulationXNA
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region PrivateMembers
        // Number of game pieces.
        private const int GamePieceCount = 6;
        // The collection of game pieces.
        private GamePieceCollection faces;
        // Graphics device manager.
        private GraphicsDeviceManager graphics;
        // The sprite batch used for rendering game pieces.
        private SpriteBatch spriteBatch;
        #endregion

        #region ConstructorInitialize
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            // This is the default but assigning here explicitly
            // to show that resizing is not supported. The view port
            // boundaries used to bounce a game piece would not be
            // updated if the window was resized.
            Window.AllowUserResizing = false;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// </summary>
        protected override void Initialize()
        {
            IsMouseVisible = true;
            faces = new GamePieceCollection();
            // base.Initialize calls the LoadContent method.
            base.Initialize();
        }
        #endregion

        #region LoadContent
        /// <summary>
        /// LoadContent will be called once per game. Load all content here.
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            string filename = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
            string path = System.IO.Path.GetDirectoryName(filename) + @"\Content";

            // Scale pieces from 100% to 160%
            float scale = 1.0f;
            float scaleFactor = 0.60f / ((GamePieceCount/2)-1);
            for (int k = 0; k < GamePieceCount / 2; k++)
            {
                GamePiece face1 = new GamePiece(spriteBatch, path + @"\Face1.png");
                GamePiece face2 = new GamePiece(spriteBatch, path + @"\Face2.png");

                face1.Scale = face2.Scale = scale;
                face1.PieceColor = Color.Green;
                face2.PieceColor = Color.LightSalmon;
                faces.Add(face1);
                faces.Add(face2);
                scale += scaleFactor;
            }
        }
        #endregion

        #region UnloadContent
        /// <summary>
        /// UnloadContent will be called once per game. Unload all content.
        /// </summary>
        protected override void UnloadContent()
        {
        }
        #endregion

        #region UpdateGame
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            faces.ProcessInertia();
            faces.UpdateFromMouse();
            base.Update(gameTime);
        }
        #endregion

        #region DrawGame
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin();
            faces.Draw();
            spriteBatch.End();
            base.Draw(gameTime);
        }
        #endregion
    }
}

Vedere anche

Concetti

Utilizzo delle modifiche e dell'inerzia in un'applicazione XNA

Creazione della classe GamePiece

Creazione della classe GamePieceCollection

Creazione della classe Game1

Altre risorse

Modifiche e inerzia