Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
14. August 2015

 

In this part of the Paradox3D game engine tutorial series we are now going to look at how you actually program your games.  In the end you will discover that it’s actually a pretty straightforward process, but could certainly use some streamlining.  ( The option to generate a .cs file when you add a script component would be a nice little time saver… ).  Minor quibble however… let’s jump in.  The code in this particular example was written to target version 1.2.  If the code doesn’t work any more, be sure to check the comments for suggestions.  If there is no fix there, please email me.

 

As always, there is an HD video of this process available here or embedded below.

 

Creating a new Script

Scripting in Paradox is a two step process.  First you create the script, generally in Visual Studio.  Then you attach the script to an entity, either programmatically, or using the editor.  We are going to look at the process of creating the script first.

 

In Visual Studio, inside your .Game folder, create a new cs file.

image

 

I personally called mine ExampleScript, outside of standard variable naming requirements, the name really doesn't matter.  We now have two options as to how we want to implement our script.  It can either be a SyncScript or AsyncScript, we will show an example of both. 

A SyncScript as the name suggests, runs Syncronously.  That is, the game loop iterates over and over and we frame our script’s update function is called and we handle the logic of our script.  An AsyncScript on the other hand, takes advantage of C# 5’s async functionality, and allows your script to run in parallel.  This could lead to performance gains on multi processor machines.  Which works best is ultimately up to you and your game’s design.

SyncScript example:

using System;
using SiliconStudio.Paradox.Engine;

namespace ScriptingDemo
{
    public class ExampleScriptSync : SyncScript
    {
        public override void Update()
        {

            if (Game.IsRunning)
            {
                if (Input.IsKeyDown(SiliconStudio.Paradox.Input.Keys.Left))
                {
                    this.Entity.Transform.Position.X -= 0.1f;
                }
                if (Input.IsKeyDown(SiliconStudio.Paradox.Input.Keys.Right))
                {
                    this.Entity.Transform.Position.X += 0.1f;
                }
            }
        }
    }
}

 

AsyncScript example:

using System;
using System.Threading.Tasks;
using SiliconStudio.Paradox.Engine;

namespace ScriptingDemo
{
    public class ExampleScriptAsync : AsyncScript
    {
        public override async Task Execute()
        {
            while (Game.IsRunning)
            {
                await Script.NextFrame();

                if (Input.IsKeyDown(SiliconStudio.Paradox.Input.Keys.Left))
                {
                    this.Entity.Transform.Position.X -= 0.1f;
                }
                if (Input.IsKeyDown(SiliconStudio.Paradox.Input.Keys.Right))
                {
                    this.Entity.Transform.Position.X += 0.1f;
                }
            }
        }
    }
}

 

This particular tutorial isn’t actually about how you program Paradox, so don’t pay too much attention to how the code works, that will all be explained later.  Just be aware that both Async and Sync scripts do the same thing, transform the Entity they are attached to along the X axis when the Left or Right arrow keys are pressed.  The important take away points are that your script derive from one of the two mentioned classes, that your script has access to the entity it is attached to and actually has access to the entire game engine, allowing you to do just about anything.  Update() is not the only callback function implemented, there is also one for Start and Cancel available, if you need to do startup or cleanup functionality.

 

One final extremely important note…  MAKE SURE YOUR CLASS IS PUBLIC!   Otherwise it will not be available in the editor!  Sorry, I’ll stop yelling now.

 

Implement one of the two scripts ( or both, it doesn’t matter ), then compile your project to make sure you haven't made any errors.  We are now ready to attach the script to an entity in Paradox Editor.

 

Attaching a Script using Paradox Studio

 

Now that we have a script, we can attach it to one or more entities in our scene.  In an ideal world, Paradox Studio should notice the changes you made, and pop up a dialog telling you so.  Unfortunately, at least right now, it rarely succeeds with the first script you create.  In this case, simple do a quick restart of Studio using the menu, File->Reload project.

image

 

Now in the 3D view, select the entity you want to attach a script to.  If you are unfamiliar with operating Paradox Studio, please refer to this tutorial.  I am going to attach this script to the sphere model created in a default scene:

image

 

Now go to the Property Grid and press the Add Component button and select Scripts from the drop down.

image

 

Now scroll down to the Scripts component that should have been added, Click the green plus sign next to Script, then in the drop down for Item 0, select your script.

image

 

Run your game using the toolbar:

image

 

You can now control the sphere using the arrow keys:

AttachingArrowKeyScript

 

A couple cool things here.  First this shows that the same script can be used to control multiple entities.  We could attach the exact same script to our camera, the light, another model, etc… and it would just work.  Second, you can attach multiple scripts to the same entity.

 

What we didn’t cover

We covered the basics of attach a script to an Entity in Paradox, and I think it should give you a good idea of how you add logic to entities in your game.  There are two things we didn’t cover (yet), that I think it should be important to be aware of before we move on.

 

First, in addition to the SyncScript and AsyncScript classes, there is a third scripting type, StartupScript.  This is a type of script that is called when your object is created.  The major difference is it is not called each frame or async, like the other two scripts.

 

Second is the game class.  If you look in your generated project, in each platform you will see an entry point, like this one for the Windows platform:

image

 

Here are the contents of that script:

using SiliconStudio.Paradox.Engine;

namespace ScriptingDemo
{
    class ScriptingDemoApp
    {
        static void Main(string[] args)
        {
            using (var game = new Game())
            {
                game.Run();
            }
        }
    }
}

 

As you can see, the heart of this script is to create an instance of Game, then call Run().  If you require more control over the lifecycle of your game, you can easily derive your own game from the Game class and create an instance of it instead.  We will see a simple example of this process in the next tutorial.

 

Don’t worry if you are a bit lost on the specifics of the code, I had no intention of explaining how the code actually works, those posts will be coming in the near future.  You should however have a good idea now of how you create a script and attach it to your game entities.

 

The Video

Programming


11. August 2015

 

Welcome to the very first tutorial in the Paradox Game Engine tutorial series, this section is going to cover installation and getting started.  The process is in reality incredibly straight forward, so this should be fairly short.  If you are unfamiliar with the Paradox Game Engine, consider starting here for an indepth review of Paradox’s functionality.

 

As with most tutorials on GameFromScratch, this one is also available as an HD video.

 

Getting Started

To get started, of course you are going to need download a few things.

 

Either version of Visual Studio will work.  If you don’t have a license, remember the community edition is free for small teams and people who make less than $1million per year in revenue.  You don’t specifically need to have Visual Studio installed, Paradox actually ships a copy of MSBuild and the .NET framework includes the C# compiler.  If you want to instead use your favorite text editor, that is an option.  These tutorials are all going to assume you are using Visual Studio however.

 

If you are going to be using Visual Studio, and don’t already have it installed, make sure it is installed before installing Paradox, as part of the Paradox installation will install a plugin to Visual Studio.  I am not going to go through the Visual Studio installation process; however if you choose strictly default settings, it will work out just fine.

 

Once you have Visual Studio installed, make sure you run it at least once to allow it to do some late step configurations.  This may not be strictly necessary, but better safe than sorry.

 

Next run the Paradox Installer you downloaded earlier.

image

 

The install process is extremely straight forward, basically just asking you which shortcuts you wish to create.  Once complete, the Paradox Launcher will be run.  This is the application you use to create new or open existing Paradox projects, as well as managing which version of Paradox you have installed:

image

 

It will now detect that you don’t have an SDK installed ( assuming you don’t have a prior install ), when prompted, choose yes:

image

 

It will then ask if you wish to install the Visual Studio integration, again click yes:

image

 

Your Paradox install should now be complete.

 

Creating Your First Project

 

Now that Paradox is installed and configured, let’s create our first project.  In the Launcher, click the big purple Start button,  your actual version number could obviously differ from mine.

image

 

This will launch the New/Open Project dialog.  You may notice there are several examples you can load.  In this case we are going to create a new project instead. 

image

 

The only thing to be aware of here is when naming your game, be sure to use a C# namespace friendly value.  For example, Game3D works, but 3DGame will work here, but then blow up when you get to the project in Visual Studio.  For a very detailed description of what’s allowed and what’s not in a C# variable see here.  Simple rule of thumb however, don’t start with a number or punctuation other than an underscore and keep away from oddball characters and you should be good.

 

Once you click the Select button, you will be taken to another dialog:

image

 

Here you can choose which platforms you want to support.  You may notice that Windows Store and Windows Phone are both showing “The Requirements to build for this platform are not met on this machine”, that is because I do not have the proper signing keys configured.  The value “Windows 10” might also be a bit confusing, as a normal “Windows” project will run just fine on a Windows 10 machine.  In this case “Windows 10” refers to the now renamed Universal apps, that enable your code to run on desktop and mobile Windows platforms.  It’s also important to note that Android and iOS development require a Xamarin license.

 

The final options are for configuring the starting graphics configuration as well as the starting orientation.  Once done, click OK.  This will then launch Paradox Studio:

image

 

Paradox creates a simple preconfigured scene for you.  Most importantly it sets up all the graphics configuration for you.  Feel free to delete everything from the scene, although I would recommend keeping at least the camera initially.

 

You can now test that your Visual Studio integration works properly.  In the toolbar you should see this:

image

 

If you have multiple versions of Visual Studio installed, you should be able to select which one you wish to use.  In my case I have only Visual Studio 2015 installed.  Either click the version you want to run, or the toolbar Visual Studio icon for the default.   Visual Studio should now open with your project displayed:

image

 

Looking in Solution Explorer you should see a project ending in .Game, as well as one project per platform you selected.  The .Game project is where the majority of your game logic will go, while platform specific initialization code will go in each platform project, if required that is.  You should also now see a new menu Paradox if you install completed correctly:

image

 

That’s it.  Paradox is now up and running and you’ve created your first project.  Stay tuned for the next part where we take a tour of Paradox Studio.

 

The Video

 

Programming


25. July 2015

 

In this chapter we are going to look at using audio in XNA.  Originally XNA supported one way of playing audio, using XACT (Cross Platform Audio Creation Tool ).  Since the initial release they added a much simplified API.  We will be taking a look at both processes.

 

There is an HD video of this chapter available here.

 

When playing audio there is always the challenge of what formats are supported, especially when you are dealing with multiple different platforms, all of which have different requirements.  Fortunately the content pipeline takes care of a great deal of the complications for us.  Simply add your audio files ( mp3, mp4, wma, wav, ogg ) to the content pipeline and it will do the rest of the work for you.   As you will see shortly though, it is also possible to load audio files outside of the content pipeline.  In this situation, be aware that certain platforms do not support certain formats ( for example, no wma support on Android or iOS, while iOS doesn’t support ogg but does support mp3 ).  Unless you have a good reason, I would recommend you stick to the content pipeline for audio whenever possible.

 

The Perils of MP3

Although MP3 is supported by MonoGame, you probably want to stay away from using it. Why?
Patents. If your game has over 5,000 users you could be legally required to purchase a license. From a legal perspective, Ogg Vorbis is superior in every single way. Unfortunately Ogg support is not as ubiquitous as we'd like it to be.

 

Adding Audio Content using the Content Pipeline

This process is virtually identical to adding a graphic file in your content file.

image

 

Simply add the content like you did using right click->Add Existing Items or the Edit menu:

image

 

If it is a supported format you will see the Processor field is filled ( otherwise it will display Unknown ).  The only option here is to configure the mp3 audio quality, a trade off between size and fidelity.

 

Playing a Song

Now let’s look at the code involved in playing the song we just added to our game.

// This example shows playing a song using the simplified audio api

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Example1
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Song song;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            this.song = Content.Load<Song>("prepare");
            MediaPlayer.Play(song);
            //  Uncomment the following line will also loop the song
            //  MediaPlayer.IsRepeating = true;
            MediaPlayer.MediaStateChanged += MediaPlayer_MediaStateChan
                                             ged;
        }

        void MediaPlayer_MediaStateChanged(object sender, System.
                                           EventArgs e)
        {
            // 0.0f is silent, 1.0f is full volume
            MediaPlayer.Volume -= 0.1f;
            MediaPlayer.Play(song);
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                ButtonState.Pressed || Keyboard.GetState().IsKeyDown(
                Keys.Escape))
                Exit();

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            base.Draw(gameTime);
        }
    }
}

 

Notice that we added the using statement Microsoft.Xna.Framework.Media.  We depend on this for the MediaPlayer and Song classes.  Our Song is loaded using the ContentManager just like we did earlier with Texture, this time with the type Song.  Once again the content loader does not use the file’s extension.  Our Song can then be played with a call to MediaPlayer.Play().  In this example we wire up a MediaStateChanged event handler that will be called when the song completes, decreasing the volume and playing the song again.

 

Playing Sound Effects

 

This example shows playing sound effects.  Unlike a Song, SoundEffects are designed to support multiple instances being played at once.  Let’s take a look at playing SoundEffect in MonoGame:

// Example showing playing sound effects using the simplified audio 
api
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using System.Collections.Generic;

namespace Example2
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        List<SoundEffect> soundEffects;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            soundEffects = new List<SoundEffect>();
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw 
            textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            soundEffects.Add(Content.Load<SoundEffect>("airlockclose"))
                             ;
            soundEffects.Add(Content.Load<SoundEffect>("ak47"));
            soundEffects.Add(Content.Load<SoundEffect>("icecream"));
            soundEffects.Add(Content.Load<SoundEffect>("sneeze"));

            // Fire and forget play
            soundEffects[0].Play();
            
            // Play that can be manipulated after the fact
            var instance = soundEffects[0].CreateInstance();
            instance.IsLooped = true;
            instance.Play();
        }


        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                ButtonState.Pressed || Keyboard.GetState().IsKeyDown(
                Keys.Escape))
                Exit();

            if (Keyboard.GetState().IsKeyDown(Keys.D1))
                soundEffects[0].CreateInstance().Play();
            if (Keyboard.GetState().IsKeyDown(Keys.D2))
                soundEffects[1].CreateInstance().Play();
            if (Keyboard.GetState().IsKeyDown(Keys.D3))
                soundEffects[2].CreateInstance().Play();
            if (Keyboard.GetState().IsKeyDown(Keys.D4))
                soundEffects[3].CreateInstance().Play();


            if (Keyboard.GetState().IsKeyDown(Keys.Space))
            {
                if (SoundEffect.MasterVolume == 0.0f)
                    SoundEffect.MasterVolume = 1.0f;
                else
                    SoundEffect.MasterVolume = 0.0f;
            }
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            base.Draw(gameTime);
        }
    }
}

 

Note the using Microsoft.Xna.Framework.Audio statement at the beginning.  Once again we added our audio files using the Content Pipeline, in this case I added several WAV files.  They are loaded using Content.Load() this time with the type SoundEffect.  Next it is important to note the two different ways the SoundEffects are played.  You can either call Play() directly on the SoundEffect class.  This creates a fire and forget instance of the class with minimal options for controlling it.  If you have need for greater control ( such as changing the volume, looping or applying effects ) you should instead create a SoundEffectInstance using the SoundEffect.CreateInstance() call.  You should also create a separate instance if you want to have multiple concurrent instances of the same sound effect playing.  It is important to realize that all instances of the same SoundEffect share resources, so memory will not increase massively for each instance created.  The number of simultaneous supported sounds varies from platform to platform, with 64 being the limit on Windows Phone 8, while the Xbox 360 limits it to 300 instances.  There is no hard limit on the PC, although you will obviously hit device limitations quickly enough.

 

In the above example, we create a single looping sound effect right away.  Then each frame we check to see if the user presses 1,2,3 or 4 and play an instance of the corresponding sound effect.  If the user hits the spacebar we either mute or set to full volume the global MasterVolume of the SoundEffect class.  This will affect all playing sound effects.

 

Positional Audio Playback

Sound effects can also be positioned in 3D space easily in XNA. 

// Display positional audio

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;

namespace Example3
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SoundEffect soundEffect;
        SoundEffectInstance instance;
        AudioListener listener;
        AudioEmitter emitter;


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            
            soundEffect = this.Content.Load<SoundEffect>("circus");
            instance = soundEffect.CreateInstance();
            instance.IsLooped = true;

            listener = new AudioListener();
            emitter = new AudioEmitter();

            // WARNING!!!!  Apply3D requires sound effect be Mono!  
            Stereo will throw exception
            instance.Apply3D(listener, emitter);
            instance.Play();
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                ButtonState.Pressed || Keyboard.GetState().IsKeyDown(
                Keys.Escape))
                Exit();

            if (Keyboard.GetState().IsKeyDown(Keys.Left))
            {
                listener.Position = new Vector3(listener.Position.X-0.
                                    1f, listener.Position.Y, listener.
                                    Position.Z);
                instance.Apply3D(listener, emitter);
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Right))
            {
                listener.Position = new Vector3(listener.Position.X + 
                                    0.1f, listener.Position.Y, 
                                    listener.Position.Z);
                instance.Apply3D(listener, emitter);
            }

            if (Keyboard.GetState().IsKeyDown(Keys.Up))
            {
                listener.Position = new Vector3(listener.Position.X, 
                                    listener.Position.Y +0.1f, 
                                    listener.Position.Z);
                instance.Apply3D(listener, emitter);
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Down))
            {
                listener.Position = new Vector3(listener.Position.X, 
                                    listener.Position.Y -0.1f, 
                                    listener.Position.Z);
                instance.Apply3D(listener, emitter);
            }            
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            base.Draw(gameTime);
        }
    }
}

 

In this example, we load a single SoundEffect and start it looping infinitely.  We then create an AudioListener and AudioEmitter instance.  The AudioListener represents the location of your ear within the virtual world, while the AudioEmitter represents the position of the sound effect.  The default location of both is a Vector3 at (0,0,0).  You set the position of a SoundEffect by calling Apply3D().  In our Update() call, if the user hits an arrow key we updated the Position of the AudioListener accordingly.  After changing the position of a sound you have to call Apply3D again.  As you hit the arrow keys you will notice the audio pans and changes volume to correspond with the updated position.  It is very important that your source audio file is in Mono ( as opposed to Stereo ) format if you use Apply3D, or an exception will be thrown.

 

Using XACT

As mentioned earlier, XACT used to be the only option when it came to audio programming in XNA.  XACT is still available and it enables your audio designer to have advanced control over the music and sound effects that appear in your game, while the programmer uses a simple programmatic interface.  One big caveat is XACT is part of the XNA installer or part of the Direct X SDK as is not available on Mac OS or Linux.  If you wish to install it but do not have an old version of Visual Studio installed, instructions can be found here ( https://www.gamefromscratch.com/post/2015/07/23/Installing-XNA-Tools-Like-XACT-without-Visual-Studio-2010.aspx ).  If you are on MacOS or Linux, you want to stick to the simplified audio API that we demonstrated earlier.

Xact is installed as part of the XNA Studio install, on 64bit Windows by default the Xact executable will be located in C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\Tools.  Start by running AudConsole3.exe:

image

 

The XACT Auditioning Tool needs to be running when you run the Xact tool.

Then launch Xact3.exe

image

First create a new project:

image

 

Next right click Wave Banks and select New Wave Bank

image

 

Drag and drop your source audio files into the Wave Bank window:

image

 

Now create a new Sound Bank by right clicking Sound Bank and selecting New Wave Bank

image

 

Now drag the Wave you wish to use from the Wave Bank to the Sound Bank

a1

 

Now create a Cue by dragging and dropping the Sound Bank to the Cue window.  Multiple files can be added to a cue if desired.

a2

 

You can rename the Cue, set the probability to play if you set several Sounds in the Cue and change the instance properties of the Cue in the properties window to your left:

image

Now Build the results:

image

 

This will then create two directories in the folder you created your project in:

image

 

These files need to be added directly to your project, you do not use the content pipeline tool!  Simply copy all three files to the content folder and set it’s build action to Copy.

image

 

Now let’s look at the code required to use these generated files:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;

namespace Example4
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        AudioEngine audioEngine;
        SoundBank soundBank;
        WaveBank waveBank;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw 
            textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            audioEngine = new AudioEngine("Content/test.xgs");
            soundBank = new SoundBank(audioEngine,"Content/Sound Bank.
                        xsb");
            waveBank = new WaveBank(audioEngine,"Content/Wave Bank.
                       xwb");

            soundBank.GetCue("ak47").Play();
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                ButtonState.Pressed || Keyboard.GetState().IsKeyDown(
                Keys.Escape))
                Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here

            base.Draw(gameTime);
        }
    }
}

 

First you create an AudioEngine using the xgs file, then a SoundBank using the xsb and a WaveBank unsing the xwb file.  We then play the Cue we created earlier with a call to SoundBank.GetQue().Play().  This process allows the audio details to be configured outside of the game while the programmer simply uses the created Que.

 

Finally it is possible to play audio files that weren’t added using the content pipeline or using Xact using a Uri. 

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw 
            textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // URL MUST be relative in MonoGame
            System.Uri uri = new System.Uri("content/background.mp3",
                             System.UriKind.Relative);
            Song song = Song.FromUri("mySong", uri);
            MediaPlayer.Play(song);
            MediaPlayer.ActiveSongChanged += (s, e) => {
                song.Dispose();
                System.Diagnostics.Debug.WriteLine("Song ended and 
                                                   disposed");
            };
        }

 

First you create a Uri that locates the audio file you want to load.  We then load it using the method FromUri, passing in a name as well as the uri.  One very important thing to be aware of here, on XNA you could use any URI.  In MonoGame it needs to be a relative path.

 

The Video

 

Programming


28. June 2015

 

In this chapter we are going to explore handling input from the keyboard, mouse and gamepad in your MonoGame game.  XNA/MonoGame also have support for mobile specific input such as motion and touch screens, we will cover theses topics in a later topic.

 

There is an HD video of this chapter available here.

 

XNA input capabilities were at once powerful, straightforward and a bit lacking.  If you come from another game engine or library you may be shocked to discovered there is no event driven interface out of the box for example.  All input in XNA is done via polling, if you want an event layer, you build it yourself or use one of the existing 3rd party implementations.  On the other hand, as you are about to see, the provided interfaces are incredibly consistent and easy to learn.

 

Handling Keyboard Input

 

Let’s start straight away with a code sample:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Text;

namespace Example1
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Vector2 position;
        Texture2D texture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
            position = new Vector2(graphics.GraphicsDevice.Viewport.
                       Width / 2 -64, 
                                    graphics.GraphicsDevice.Viewport.
                                    Height / 2 -64);
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            texture = this.Content.Load<Texture2D>("logo128");
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            // Poll for current keyboard state
            KeyboardState state = Keyboard.GetState();
            
            // If they hit esc, exit
            if (state.IsKeyDown(Keys.Escape))
                Exit();

            // Print to debug console currently pressed keys
            System.Text.StringBuilder sb = new StringBuilder();
            foreach (var key in state.GetPressedKeys())
                sb.Append("Key: ").Append(key).Append(" pressed ");

            if (sb.Length > 0)
                System.Diagnostics.Debug.WriteLine(sb.ToString());
            else
                System.Diagnostics.Debug.WriteLine("No Keys pressed");
            
            // Move our sprite based on arrow keys being pressed:
            if (state.IsKeyDown(Keys.Right))
                position.X += 10;
            if (state.IsKeyDown(Keys.Left))
                position.X -= 10;
            if (state.IsKeyDown(Keys.Up))
                position.Y -= 10;
            if (state.IsKeyDown(Keys.Down))
                position.Y += 10;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            spriteBatch.Draw(texture, position);
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}

 

We are going to re-use the same basic example for all the examples in this chapter.  It simply draws a sprite centered to the screen, then we manipulate the position in Update()

image

 

In this particular example, when the user hits keys, they are logged to the debug console:

image

 

Now let’s take a look at the keyboard specific code.  It all starts with a call to Keyboard.GetState(), this returns a struct containing the current state of the keyboard, including modifier keys like Control or Shift.  It also contains a method named GetPressedKeys() which returns an array of all the keys that are currently pressed.  In this example we simply loop through the pressed keys, writing them out to debug.  Finally we poll the pressed state of the arrow keys and move our position accordingly.

 

Handling Key State Changes

One thing you might notice with XNA is you are simply checking the current state of a key.  So if a key is pressed or not.  What if you only want to respond when the key is first pressed?  This requires a bit of work on your behalf.

    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Vector2 position;
        Texture2D texture;
        KeyboardState previousState;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
            position = new Vector2(graphics.GraphicsDevice.Viewport.
                       Width / 2 -64, 
                                    graphics.GraphicsDevice.Viewport.
                                    Height / 2 -64);

            previousState = Keyboard.GetState();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            texture = this.Content.Load<Texture2D>("logo128");
        }

        protected override void Update(GameTime gameTime)
        {
            KeyboardState state = Keyboard.GetState();
            
            // If they hit esc, exit
            if (state.IsKeyDown(Keys.Escape))
                Exit();

            // Move our sprite based on arrow keys being pressed:
            if (state.IsKeyDown(Keys.Right) & !previousState.IsKeyDown(
                Keys.Right))
                position.X += 10;
            if (state.IsKeyDown(Keys.Left) & !previousState.IsKeyDown(
                Keys.Left))
                position.X -= 10;
            if (state.IsKeyDown(Keys.Up))
                position.Y -= 10;
            if (state.IsKeyDown(Keys.Down))
                position.Y += 10;

            base.Update(gameTime);

            previousState = state;
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            spriteBatch.Draw(texture, position);
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }

 

The changes to our code are highlighted.  Essentially if you want to check for changes in input state ( this applies to gamepad and mouse events too ), you need to track them yourself.  This is a matter of keeping a copy of the previous state, then in your input check you check not only if a key is pressed, but also if it was pressed in the previous state.  If it isn’t this is a new key press and we respond accordingly.  In the above example on Left or Right arrow presses, we only respond to new key presses, so moving left or right requires repeatedly hitting and releasing the arrow key.

 

Handling Mouse Input

Next we explore the process of handling Mouse input.  You will notice the process is almost identical to keyboard handling.  Once again, let’s jump right in with a code example.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Text;

namespace Example2
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Vector2 position;
        Texture2D texture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
            position = new Vector2(graphics.GraphicsDevice.Viewport.
                       Width / 2 ,
                                    graphics.GraphicsDevice.Viewport.
                                    Height / 2 );

            
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            texture = this.Content.Load<Texture2D>("logo128");
        }

        protected override void Update(GameTime gameTime)
        {
            MouseState state = Mouse.GetState();

            // Update our sprites position to the current cursor 
            location
            position.X = state.X;
            position.Y = state.Y;

            // Check if Right Mouse Button pressed, if so, exit
            if (state.RightButton == ButtonState.Pressed)
                Exit();

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            spriteBatch.Draw(texture, position, origin:new Vector2(64,
                             64));
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}

 

When you run this example, the texture will move around relative to the location of the mouse.  When the user clicks right, the application exits.  The logic works almost identically to handling Keyboard input.  Each frame you check the MouseState by calling Mouse.GetState().  This MouseState struct contains the current mouse X and Y as well as the status of the left, right and middle mouse buttons and the scroll wheel position.  You may notice there are also values for XButton1 and XButton2, these buttons can change from device to device, but generally represent a forward and back navigation button.  On devices with no mouse support, X and Y will always be 0 while each button state will always be set to ButtonState.Released. If you are dealing with a multi touch device this code will continue to work, although the values will only reflect the primary (first) touch point.  We will discuss mobile input in more detail in a later chapter.  As with handling Keyboard events, if you want to track changes in event state, you will have to track them yourself.

 

If you add the following code to your update, you will notice some interesting things about the X,Y position of the mouse:

        protected override void Update(GameTime gameTime)
        {
            MouseState state = Mouse.GetState();

            // Update our sprites position to the current cursor 
            location
            position.X = state.X;
            position.Y = state.Y;

            System.Diagnostics.Debug.WriteLine(position.X.ToString() + 
                                   "," + position.Y.ToString());
            // Check if Right Mouse Button pressed, if so, exit
            if (state.RightButton == ButtonState.Pressed)
                Exit();

            base.Update(gameTime);
        }

The X and Y values are relative to the Window’s origin.  That is (0,0) is the top left corner of the drawable portion of the window, while (width,height) is the bottom right corner.  However, if you are in a Windowed app, the mouse pointer location continues to be updated, still relative to the top left corner of the application window.

image

 

You can also set the position of the cursor in code using the following line:

            if(state.MiddleButton == ButtonState.Pressed)
                Mouse.SetPosition(graphics.GraphicsDevice.Viewport.
                                  Width / 2,
                    graphics.GraphicsDevice.Viewport.Height / 2);

 

This code will center the mouse position to the middle of the screen when the user presses the middle button.

 

Finally its common to want to display the mouse cursor, this is easily accomplished using:

            IsMouseVisible = true;

This member of the Game class toggles the visibility of the system mouse cursor.

image

 

 

Handling Gamepad Input

 

Now we will look at handling input from a gamepad or joystick controller.  You probably wont be surprised to discover the process is remarkably consistent.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Text;

namespace Example3
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Vector2 position;
        Texture2D texture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
            position = new Vector2(graphics.GraphicsDevice.Viewport.
                       Width / 2,
                                    graphics.GraphicsDevice.Viewport.
                                    Height / 2);
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            texture = this.Content.Load<Texture2D>("logo128");
        }

        protected override void Update(GameTime gameTime)
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Escape)) Exit();

            // Check the device for Player One
            GamePadCapabilities capabilities = GamePad.GetCapabilities(
                                               PlayerIndex.One);
            
            // If there a controller attached, handle it
            if (capabilities.IsConnected)
            {
                // Get the current state of Controller1
                GamePadState state = GamePad.GetState(PlayerIndex.One);

                // You can check explicitly if a gamepad has support 
                for a certain feature
                if (capabilities.HasLeftXThumbStick)
                {
                    // Check teh direction in X axis of left analog 
                    stick
                    if (state.ThumbSticks.Left.X < -0.5f) 
                        position.X -= 10.0f;
                    if (state.ThumbSticks.Left.X > 0.5f) 
                        position.X += 10.0f;
                }

                // You can also check the controllers "type"
                if (capabilities.GamePadType == GamePadType.GamePad)
                {
                    if (state.IsButtonDown(Buttons.A))
                        Exit();
                }
            }
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            spriteBatch.Draw(texture, position, origin: new Vector2(64,
                             64));
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}

 

When you run this example, if there is a controller attached, pressing left or right on the analog stick with move the sprite accordingly.  Hitting the A button (or pressing Escape for those without a controller) will cause the game to exit.

 

The logic here is remarkably consistent with Mouse and Keyboard event handling.  The primary difference is the number of controllers attached and the capabilities of each controller can vary massively, so the code needs to responding appropriately.  In the above example, we check only for the first controller attached, by passing PlayerIndex to our GetEvents call.  You can have up to 4 controllers attached, and each needs to be polled separately. 

 

Supported Gamepads


On the PC there are a plethora of devices available with a wide range of capabilities. You can have up to 4 different controllers attached, each accesible by passing the appropriate PlayerIndex to GetEvents(). The following device types can be returned:

  • AlternateGuitar
  • ArcadeStick
  • BigButtonPad
  • DancePad
  • DrumKit
  • FlightStick
  • GamePad
  • Guitar
  • Unknown
  • Wheel


Obviously each devices supports a different set of features, which can be polled invdividually using the GamePadCapabilities struct returned by Gamepad.GetCapabilities().


Buttons on a GamePad controller are treated just like Keys and MouseButtons, with a value of Pressed or Released.  Once again, if you want to track changes in state you need to code this functionality yourself.  When dealing with Analog sticks, the value returned is a Vector2 representing the current position of the stick.  A value of 1.0 represents a stick that is fully up or right, while a value of –1.0f represents a stick that is full left or down.  A stick at 0,0 is un-pressed.

 

There is a small challenge with dealing with analog controls however that you should be aware of.  Even when a stick is not pressed, it is almost never at the position (0.0f,0.0f), the sensors often return very small fluctuations from complete zero.  This means if you respond directly to input without taking into account these small variations, your sprites will “twitch” while they are supposed to be stationary.  This is worked around using something called a dead zone.  This is a range of motions, or motion values, that are considered too small to be registered.  You can think of a deadzone as a value that is “close enough to zero to be considered zero”.

 

You have a couple options with XNA/MonoGame for dealing with deadzones.  The default is IndependentAxis, which compares each axis against the deadzone separately, Circular which combines the X and Y values together before comparison to the dead zone (recommended for controls the use both axis together, such as a thumbstick controlling 3D view), and finally None, which ignores the deadzone completely.  You would generally choose None if you don’t care about a dead zone, or wish to implement it yourself.

           GamePadState state = GamePad.GetState(PlayerIndex.One, 
                                GamePadDeadZone.Circular);

 

As you can see, XNA's Input handling is somewhat sparse compared to other game engines, but does provide the building blocks to make more complex systems if required. The approach to handling input across devices is remarkably consistent, making it easier to use and hopefully resulting in less unexpected behavior and bugs.

 

 

The Video

Programming


15. June 2015

 

In this chapter we are going to look closely at the structure of a typical XNA game.  By the end you should have a better idea of the life cycle of a typical MonoGame application, from program creation, loading content, the game loop, unloading content and exiting.

 

If you prefer videos to text, you can watch this content in HD video right here.

 

Let’s start by looking at the code for an automatically generated application, stripped of comments.  There are two code files in the generated project, Program.cs and [YourProjectName].cs.  Let’s start with Program.cs

using System;

namespace Example1
{
#if WINDOWS || LINUX
    public static class Program
    {
        [STAThread]
        static void Main()
        {
            using (var game = new Game1())
                game.Run();
        }
    }
#endif
}

The heart of this code is the creation of our Game object, then calling it’s Run() method, which starts our game executing, kicking off the game loop until the Game execution finishes.  We will talk about the game loop in a bit more detail later on.  Otherwise this is a standard C# style application, with Main() being the applications entry point.  This is true for  Windows and Linux applications at least, which is the reason for the #if  preprocessor directive.  We will discuss the entry point of various other platforms later on, so don’t worry about this too much.  Also note, if you didn’t select Windows as your platform when creating this project, your own Program.cs file contents may look slightly different.  Again, don’t worry about this right now, the most important thing is to realize that Program creates and runs your Game derived class.

Predefined Platform Values

One of the major features of MonoGame over XNA is the addition of several other supported platforms. In addition to WINDOWS and LINUX symbols, the following platforms have been defined:

  • ANDROID
  • IOS
  • LINUX
  • MONOMAC
  • OUYA
  • PSM
  • WINDOWS
  • WINDOWS_PHONE
  • WINDOWS_PHONE81
  • WINDOWSRT
  • WEB

Of course, new platforms are being added all of the time, so this list may not still be current. You can look up the definitions in the MonoGame sources in the file /Build/Projects/MonoGame.Framework.definition in the MonoGame GitHub repository.

Please note, there are plenty of other defines per platform, for example iOS, Android, MacOS, Ouya and WindowsGL all also define OPENGL. You can use these predefined symbols for implementing platform or library specific code.

Now let's move on to our Game class

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Example1
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                ButtonState.Pressed || Keyboard.GetState().IsKeyDown(
                Keys.Escape))
                Exit();
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            base.Draw(gameTime);
        }
    }
}

Our game is derived from the class Microsoft.Xna.Framework.Game and is the heart of our application. The Game class is responsible for initializing the graphics device, loading content and most importantly, running the application game loop. The majority of our code is implemented by overriding several of Game’s protected methods.

 

Let’s take a look at the code, starting from the top.  We create two member variables, graphics and spriteBatch.  GraphicsDeviceManager requires a reference to a Game instanceWe will cover the GraphicsDeviceManager and SpriteBatch classes shortly in the graphics chapter, so please ignore them for now.

 

Next we override the Initialize(), LoadContent and UnLoadContent methods.  The most immediate question you’ve probably got is, why have an Initialize() method at all, why not just do initialization in the constructor.  First, behavior of calling a virtual function from a constructor can lead to all kinds of hard to find bugs in a C# application.  Second, you don’t generally want to do any heavy lifting in a constructor.  The existence of LoadContent() however, will often leave you will an empty Initialize() method.  As a general rule, perform inititalizations that are required ( like GraphicsDeviceManager’s allocation ) for the object to be valid in the constructor, perform long running initializations ( such as procedural generation of terrain ) in Initialize() and load all game content in LoadContent().

 

Next we override Update() and Draw(), which is essentially the heart of your application’s game loop.  Update() is responsible for updating the state of your game world, things like polling input or moving entities, while Draw is responsible for drawing your game world.  In our default Update() call, we check for the player hitting the back button or escape key and exit if they do.  Don’t worry about the details, we will cover Input shortly.  In Draw() we simply clear the screen to CornFlower Blue ( an XNA tradition ).  You will notice in both examples we call the base class as well.

 

What's a game loop?


A game loop is essentially the heart of a game, what causes the game to actually run. The following is a fairly typical game loop:

void gameLoop(){
   while (game != DONE){
      getInput();
      physicsEngine.stepForward();
      updateWorld();
      render();
   }
   cleanup();
}

 

As you can see, it's quite literally a loop that calls the various functions that make your game a game.  This is obviously a rather primitive example but really 90% of game loops end up looking very similar to this.

 

However, once you are using a game engine or framework, things behave slightly different.  All this stuff still happens, it’s just no longer your code’s responsibility to create the loop.  Instead the game engine performs the loop and each iteration it then calls back to your game code. This is where the various overridden function such as update() and draw() are called.  Looking at our sample loop above though, you might notice a physics engine call.  XNA doesn’t have a built in physics engine, so instead of the game loop updating the physics, you will have to do it yourself in your games update() call.

 

When you run this code you should see:

image

 

Please note, depending on what platform you are running on, this window may or may not be created full screen.  On Windows it defaults to windowed, while on MacOS it defaults to full screen.  Hit the Escape key, or Back button if you have a controller installed, to exit the application.

 

Let’s take a quick look at a program’s lifecycle with this handy graphic.

programFlow

In a nutshell the game is created, initialized, content is loaded, the game loop runs until Exit() is called, then the game cleans up and exits.  There are actually a few more methods behind the scenes, such as BeginDraw() and EndDraw(), but for most games, this is sufficient detail.

 

Our current example isn’t exactly exciting because absolutely nothing happens.  Let’s create a slightly more interesting example, one that draws a rectangle on screen and rolls it across the screen.  Don’t worry about the specifics, we will cover graphics in more detail shortly.

 

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

// This example simply adds a red rectangle to the screen
// then updates it's position along the X axis each frame.
namespace Example2
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D texture;
        Vector2 position;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            position = new Vector2(0, 0);
        }

        protected override void Initialize()
        {
            texture = new Texture2D(this.GraphicsDevice, 100, 100);
            Color[] colorData = new Color[100 * 100];
            for (int i = 0; i < 10000; i++)
                colorData[i] = Color.Red;

            texture.SetData<Color>(colorData);
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                ButtonState.Pressed || Keyboard.GetState().IsKeyDown(
                Keys.Escape))
                Exit();

            position.X += 1;
            if (position.X > this.GraphicsDevice.Viewport.Width)
                position.X = 0;
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            spriteBatch.Draw(texture,position);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

 

When we run this we will see:

gif1

 

Nothing exciting, but at least our game does something now.  Again, don’t worry overly about the details of how, we will cover this all later.  What we want to do is look at a few key topics when dealing about dealing with the game loop. 

 

Pausing Your Game

 

Pausing your game is a pretty common task, especially when an application loses focus.  If you take the above example, minimize the application, then restore it and you will notice the animation continued to occur, even when the game didn’t have focus.  Implementing pause functionality is pretty simple though, let’s take a look at how:

        protected override void Update(GameTime gameTime)
        {
            if (IsActive)
            {
                if (GamePad.GetState(PlayerIndex.One).Buttons.Back == 
                    ButtonState.Pressed || Keyboard.GetState().
                    IsKeyDown(Keys.Escape))
                    Exit();
                position.X += 1;
                if (position.X > this.GraphicsDevice.Viewport.Width)
                    position.X = 0;
                base.Update(gameTime);
            }
        }

Well that was simple enough.  There is a flag set, IsActive, when your game is active or not.  The definition of IsActive depends on the platform it’s running. On a desktop platform, an app is active if its not minimized AND has input focus.  On console it’s active if an overlay like the XBox guide is not being shown, while on phones it’s active if it’s the running foreground application and not showing a system dialog of some kind.  As you can see, the can pause the game by simply not performing Game::Update() calls.

 

There may be times when you wish to perform some activity when you gain/lose active status.  This can be done with a pair of event handlers:

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            position = new Vector2(0, 0);

            this.Activated += (sender, args) => {  this.Window.Title = 
                              "Active Application"; };
            this.Deactivated += (sender, args) => { this.Window.Title 
                                = "InActive Application"; };
        }

Or by overriding the functions OnActivated and OnDeactivated, which is the recommended approach:

        protected override void OnActivated(object sender, System.
                                            EventArgs args)
        {
            this.Window.Title = "Active Application";
            base.OnActivated(sender, args);
        }

        protected override void OnDeactivated(object sender, System.
                                              EventArgs args)
        {
            this.Window.Title = "InActive Application";
            base.OnActivated(sender, args);
        }
 

Controlling the Game Loop

Another challenge games face is controlling the speed games run at across a variety of different devices.  In our relatively simple example there is no problem for two reasons.  First, it’s a very simple application and not particularly taxing, meaning any machine should be able to run it extremely quickly.  Second, the speed is actually being capped by two factors, we are running at a fixed time step ( more on that later ) and we have vsync enabled, which on many modern monitors, refresh at a 59 or 60hz rate.  If we turn both of these features off and let our game run at maximum speed, then suddenly the speed of the computer it’s running on becomes incredibly important:

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            position = new Vector2(0, 0);
            this.IsFixedTimeStep = false;
            this.graphics.SynchronizeWithVerticalRetrace = false;
        }

 

By setting IsFixedTimeStep to false and Graphics.SyncronizeWithVerticalRetrace to false, our application will now run as fast as the game is capable.  The problem is, this will result in our rectangle being drawn extremely fast and at different speeds on different machines.  We obviously don’t want this, but fortunately there is an easy fix.  Take a look at Update() and you will notice it is being passed in a GameTime parameter.   This value contains the amount of time that occurred since the last time Update() was called.  You may also notice Draw() also has this parameter.  This value can be used to smooth out motion so it runs at a consistent speed across machines.  Let’s change our update call so position instead of being ++’ed each frame, we now move at a fixed rate, say 200 pixels per second.  That can be accomplished with this change to your Update() position code:

position.X += 200.0f * (float)gameTime.ElapsedGameTime.TotalSeconds;

TotalSeconds will contain the fraction of a second that have elapsed since Update() was last called, assuming of course your game is running at at least 1 frame a second!  For example, if your game is updating a 60hz ( second times per second ), then TotalSeconds will have a value of 0.016666 ( 1/60 ).  Assuming your stays pretty steady at 60fps, this results in your Update code updating the position by 3.2 pixels per frame ( 200 * 0.016 ).  However, on a computer running at 30fps, this same logic would then update position by 6.4 pixels per frame ( 2000 * (1/30) ).  The end result is the behavior of the game on both machines is identical even though one draws twice as fast as the other.

The GameTime class contains a couple useful pieces of information:

image

ElapsedGameTime holds information on how much time has happened since the last call to Update (or Draw,  completely separate values by the way ).  As you just saw, this value can be used to normalize behavior regardless to how fast the underlying machine actually performs.  The value TotalGameTime on the other hand is the amount of time that elapsed since game started, including time spend paused.  Finally there is the IsRunningSlowly flag, which is set if the game isn’t hitting it’s target elapsed time, something we will discuss momentarily.  Game also has a property called InactiveSleepTime, which along with TotalGameTime can be used to calculate the amount of time a game spent running.

 

Fixed Vs. Variable TimeStep

 

Finally let’s discuss using a FixedStep game loop instead.  Instead of messing with all of this frame normalization stuff you can instead say “Hey, Monogame, I want you to run my game at this speed” and it will do it’s best.  Lets look at this process:

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            position = new Vector2(0, 0);
            this.IsFixedTimeStep = true;
            this.graphics.SynchronizeWithVerticalRetrace = true;
            this.TargetElapsedTime = new System.TimeSpan(0, 0, 0, 0, 33); // 33ms = 30fps
        }

This will then try to call the Update() exactly 30 times per second.  If it can’t maintain this speed, it will set the IsRunningSlowly flag to true.  At the same time, the engine will try to call Draw() as much as possible, but if the machine isn’t fast enough to hit the TargetElapsedTime it will start skipping Draw() frames, calling only Update() until it’s “caught up”.  If your  You can control the maximum amount of draw calls that will be skipped per Update() by setting MaxElapsedTime.  This value is a MonoGame specific extension and is not in the original XNA.  If you don’t specify a TargetElapsedTime, the default is 60fps (1/60).

 

Ultimately the decision between using a Fixed Step game loop or not is ultimately up to you.  A fixed step game loop has the advantage of being easier to implement and provides a more consistent experience.  A variable step loop is a bit more complicated, but can result in smoother graphics on higher end machines.  The result is more pronounced on a 3D game than a 2D one, resulting in smoother controls and slightly cleaner visuals.  In a 2D game the difference is much less pronounced.

 

In the next chapter we will move on to the much more exciting topic of 2D graphics.

 

The Video

Programming


GFS On YouTube

See More Tutorials on DevGa.me!

Month List