Wednesday, March 20, 2019

MonoGame: Static Camera Tutorial and code


Some of you may have noticed my series on openGL with openTK in C# that I wrote some years ago. Since then my life has been quite full of stuff happening so I never got around to look at lightning or other stuff to make sure that I would end up with a working engine.

Lately I found MonoGame and started to experiment with it, turns out that it has much of the stuff that I want to use so I ended up writing some stuff with it.

Here I thought that I would share a basic static camera class and its usage as it took some time for me to understand how to get it working together with the BasicEffect class provided by MonoGame.

Some code. Let's start to look at the interface that we would want to use with out cameras.

public interface ICamera
{
    Matrix ViewMatrix { get; }
    Matrix ProjectionMatrix { get; }
    void Update(GameTime gameTime);
}

We want our cameras to expose their View and Projection matrices as they will be used to integrate the camera with MonoGame BasicEffect.
Also, as we will be using this interface with all of our cameras we may want to update it in each frame, hence the Update(GameTime gametTime) method. GameTime is provided by MonoGame and contains two timespans, time since game start and time since last frame.


Next we look at the actual StaticCamera, a camera that once created will remain static in the world. I.e. you place it at a position and point it towards something interesting and it will look at that position until you remove the camera. Useful for some scenarios and a good stepping point for creating more advanced camera classes.

We want our camera to have a Position in in the game world and a Direction.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
public class StaticCamera : ICamera
{
    public Vector3 Position { get; }
    public float FieldOfView { get; }
    public float NearPlane { get; }
    public float FarPlane { get; }
    public Vector3 Direction { get; }
    public Matrix ViewMatrix { get; }
    public Matrix ProjectionMatrix { get; }

    public StaticCamera(GraphicsDevice graphicsDevice, float fieldOfViewDegrees, float nearPlane, float farPlane)
        : this(graphicsDevice, fieldOfViewDegrees, nearPlane, farPlane, Vector3.Zero, -Vector3.UnitZ)
    { }

    public StaticCamera(GraphicsDevice graphicsDevice, float fieldOfViewDegrees, float nearPlane, float farPlane, Vector3 position, Vector3 target)
    {
        FieldOfView = fieldOfViewDegrees * 0.0174532925f;
        Position = position;
        Direction = Vector3.Normalize(target - position);
        ViewMatrix = Matrix.CreateLookAt(Position, Direction, Vector3.Up);
        ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
            FieldOfView,
            graphicsDevice.Viewport.AspectRatio,
            nearPlane,
            farPlane);
    }
    public void Update(GameTime gameTime)
    { }
}

We have two constructors, the first one takes a GraphicsDevice reference that we need to figure out what resolution and especially what aspect ration our current view port has. (i.e. screen resolution/window size)
Also, we want to specify the lense angle in degrees. I.e. 60 degree lense.
The near and far clipping planes, i.e. we will only want to render objects that are between those two distances from the camera.
The first constructor creates a camera located at coordinates (0, 0, 0) looking inward (0, -1, 0)

The second constructor takes also the position and target to look at.
We calculate the radians of the camera lense by multiplying the field of view degrees with 0.01745...
After that we figure out the direction that the camera points at by subtracting the camera location from the target and normalizing the result.

To get the ViewMatrix we use the Matrix.CreateLookAt function that takes Position, Direction and an up vector
To get the ProjectionMatrix we use the Matrix.CreatePerspectiveFieldOfView function that takes the field of view, aspect ration and near/far planes.
This is pretty much all for a simple static camera using the MonoGame framework.

Next step is to use the camera.

private ICamera _camera;
protected override void LoadContent()
{
 _camera = new StaticCamera(GraphicsDevice, 60, 1, 200, new Vector3(1, -10, 0), Vector3.Zero);
}

I put the above code in my Game1.cs file. As the default Game1 class inherits from Game, it will have GraphicsDevice provided and we just need to send it into the constructor together with field of view angle, near and far planes, position and target coordinates to look at.

All of my game objects inherit from the following abstract GameObject class
public abstract class AGameObject
{
 public Vector3 Coordinates;
 public Vector3 Rotation;
 public Vector3 Velocity;
 public float Scale = 1f;
}
So each game object has a position in the game world that is stored in Coordinates, it also has an rotation, velocity and scale.


Whenever we want to render a game object in a scene, we wrap it in a Renderable object that also has a model and a Draw method:
public class Renderable
{
 public VertexPositionNormalTexture[] Model;
 public BasicEffect BasicEffect;
 public AGameObject GameObject;

 public Renderable(AGameObject gameObject, GraphicsDevice graphicsDevice)
 {
  GameObject = gameObject;
  BasicEffect = new BasicEffect(graphicsDevice)
  {
   AmbientLightColor = Vector3.One,
   LightingEnabled = true,
   DiffuseColor = Vector3.One,
  };
 }

 public void Draw(GraphicsDevice graphicsDevice, ICamera camera)
 {
  var t2 = Matrix.CreateTranslation(GameObject.Coordinates.X, GameObject.Coordinates.Y, GameObject.Coordinates.Z);
  var r1 = Matrix.CreateRotationX(GameObject.Rotation.X);
  var r2 = Matrix.CreateRotationY(GameObject.Rotation.Y);
  var r3 = Matrix.CreateRotationZ(GameObject.Rotation.Z);
  var s = Matrix.CreateScale(GameObject.Scale);

  BasicEffect.World = r1 * r2 * r3 * s * t2;

  BasicEffect.View = camera.ViewMatrix;
  BasicEffect.Projection = camera.ProjectionMatrix;
  
  BasicEffect.EnableDefaultLighting();

  foreach (var pass in BasicEffect.CurrentTechnique.Passes)
  {
   pass.Apply();

   graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, Model, 0, Model.Length / 3);
  }

 }
}

Here we can see that the render method takes a graphicsDevice and camera. Here we take the ViewMatrix from the camera and put it in the BasicEffect.View, and the same goes for the ProjectionMatrix that is put in BasicEffect.Projection.
The BasicEffect.World receives the rotated, scaled and translated matrix generated from the GameObject.

Now we need to look up our Draw method in the main game file (Game1.cs) and place calls to render our Renderables there
private List<Renderable> _scene = new List<Renderable>();
private readonly RasterizerState _rasterizerState = new RasterizerState
{
    CullMode = CullMode.CullClockwiseFace
};

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.RasterizerState = _rasterizerState;
    GraphicsDevice.Clear(Color.CornflowerBlue);
    foreach(var x in _scene)
    {
        x.Draw(GraphicsDevice, _camera);
    }
    base.Draw(gameTime);
}
First we tell the graphics device to save some time by culling triangles that are backwards facing, we then clear the scene to a background color.
For simplicity we store all our Renderables in a list called _scene and iterate through it and call Draw on each element.


I hope this helps someone out there to get unstuck when starting to use MonoGame.

All code provided as-is. This is copied from my own code-base, May need some additional programming to work. Use for whatever you want, how you want! If you find this helpful, please leave a comment or share on social media, not required but much appreciated! :)

Saturday, December 8, 2018

F# typed tuples and how to pattern match them


Noticed a warning fly-by in the compiler log in Visual Studio
warning FS0026: This rule will never be matched

When I opened the location that it complained about I saw the following code from my main event reducer:
let HandleEvent (state:State) (event:ApplicationEvent) : State =
 match event with 
 | ApplicationEvent (h, p) -> 
  {
   CurrentPlayer = CurrentPlayerReducer.HandleEvent state h p
   LastEventId = Some h.Id
   Timestamp = h.Timestamp
   PlayerStore = PlayerReducer.HandleEvent state.PlayerStore h p
   SpaceBodyStore = SpaceBodyReducer.HandleEvent state.SpaceBodyStore h p
  }
 | _ -> state

The warning was that the _ -> state rule will never match and I started to wonder if there might be a better way to write this thing.
The ApplicationEvent is defined as follows:
type ApplicationEvent = ApplicationEvent of EventHeader * obj
For those of you that have read my previous posts about Event Sourcing in F# may notice that the event definition has evolved a little bit since my last post in that series, no worries, I will post more when I have something working.

As you can see the ApplicationEvent type is pretty much just a discriminated union of 1 that has a named tuple.
This all makes it easy to handle the application event in the system as one object but when you want to look at the header and payload separately you will have to pattern match it to extract its parts, hence the pattern matching in the function above.

After a little bit of thinking I tried the following instead:
let HandleEvent (state:State) (ApplicationEvent (h, p)) : State =
 {
  CurrentPlayer = CurrentPlayerReducer.HandleEvent state h p
  LastEventId = Some h.Id
  Timestamp = h.Timestamp
  PlayerStore = PlayerReducer.HandleEvent state.PlayerStore h p
  SpaceBodyStore = SpaceBodyReducer.HandleEvent state.SpaceBodyStore h p
 }
I.e. I pattern match already in the in parameters to the function and thus get rid of the whole match construct in the function, and it works!
A day when you learn something new is not a wasted day!

So hope this helps someone out there!

Wednesday, October 17, 2018

Teaching myself Unity - Logging to screen


I've played around quite a lot with OpenGL and built my own engine to some extent (as you can see in other posts on this blog). But I want to create games, doing that with a home-written engine puts a lot of focus on the engine and very little on the actual game. So I decided to try out Unity as it seems popular and has nice integration with Visual Studio


Getting started

Install both and start Unity.
First screen in Unity. Just click on the New button to the upper right to create our first project
Name the project and select where it should be placed on disk. I also disabled unity analytics as I don't think that this test project would have any usage for it.


Configure script editor

Next step is to make sure that Unity thinks that Visual Studio should be used for script editing
Go to Edit menu -> Preferences and in the popup select External Tools and make sure that External Script Editor is set to Visual Studio 2017 (Community). Or any other version of Visual Studio if you have the licensed versions

Logging to screen

I like to have stuff happening on the screen, and in my mind the first step should be to add logging to screen so that we can follow what happens in the game without having to tab out to the Unity editor (or when we run the game outside of the development environment)
So, how to accomplish logging to screen?

In the project tab. Assets folder, create a new folder and name it Scripts
  • Create a new folder called scripts (see above picture)
  • Create a new C# script in that folder and call it Logger
In the SampleScene create a new empty GameObject and call it Screen Logger
  • Create a new GameObject and call it Screen Logger
  • Select the Screen Logger GameObject and drag the Logger script to the Inspector view for the screen logger to attach the script to the GameObject
Screen Logger Game Object with attached Logger script in the Inspector view
Next, let's create the UI component that we will log to.
  • Right click in the Sample Scene tab and select UI -> Text
  • Rename the newly created Text to Log
Scene view with the Log Text control (New Text), 2D view selected in the upper left corner and the white line is the borders of the canvas

  • Go to the Scene view and click on the 2D button at the top so that we can position our UI component
  • Select the Log Game Object and move your mouse cursor over the Scene view and press F on the keyboard. This will find and focus the view on that particular component. 
  • Zoom out with your mouse-wheel so that you see the canvas borders
Resized textbox to fill the upper area of the canvas
  • Resize the textbox so that it fills the upper area of the canvas. Notice the color change of the canvas border when the textbox is at the border. 
In the Inspector, set stretch and anchor to the top of the canvas
  • Select the text box and go to the inspector. Select stretch and anchor point according to the picture above

Logger script

So, now we have our UI component setup and placed at the top of the canvas area, lets look at the Logger script.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Logger : MonoBehaviour
{
    public static Logger Instance { get; private set; }
    public Text Log;
    private List<string> _logLines = new List<string>();

    void Start()
    {
        Instance = this;
    }

    void Update()
    {
        // test code to check that this works
        Write(Time.fixedTime.ToString());
    }

    public void Write(string text)
    {
        _logLines.Add(text);
        if (_logLines.Count > 3)
            _logLines.RemoveAt(0);
        Log.text = string.Join(Environment.NewLine, _logLines.ToArray());
    }
}

  • Each script is generated with a Start and Update method. 
  • The static Logger Instance will help us access this script from outside (Singleton pattern). We assign it with this in the Start method (Start is guaranteed to only execute once)
  • Next we add a using UnityEngine.UI; line to the top and a public variable called Text Log. This will allow us access to an UI component in the scene. We will connect them in a later step, here we just say that we will be using a GameObject of type Text
  • After that we define a private variable that holds a list of strings that have been logger and initialize it inline
  • Now, create a new method called Write that takes a string as parameter.
  • Add the input parameter to the list of strings and then we make sure that the list doesn't grow by removing old items if there are more then 3 items in the collection.
  • Lastly we join all strings in the list with a new line between each and assign it to the text field of the Text UI component.
And that's pretty much it. You can of course play around with more information but this is the basic logging capability that I am after.
Notice the Update method calls the Write each frame, this is only for testing purposes and the whole Update method can be removed once we know that the script works.

Connect our Log text element to our Logger script in the Screen Logger game object inspection Click on the small circle to the right of the field to popup the Select Text dialog. It will show all GameObjects in the scene of the type Text.
So, our script is done. Now we just have to connect the correct Text component to it
  • In the inspector for Screen Logger, click on the small circle to the right of the Log field to popup the Select Text dialog
  • As we only have one Text component in the scene at the moment, just select it and close the popup


The end result of our logger in action, currently printing out the time each frame

So it works, you can now remove the Update method from the script as it is not really needed.

Usage from other scripts:


Logger.Instance.Write("information that we want to put on screen");

That's it!

All code provided as-is. This is copied from my own code-base, May need some additional programming to work. Use for whatever you want, how you want! If you find this helpful, please leave a comment or share a link on social media, not required but appreciated! :)