adamkdean

software engineering

XNA style Game class with Python/Pygame

By Adam K Dean on

XNA/MonoGame are great. You have such a wealth of classes at your disposable. There have been some great games made with it; Terraria and Magicka among them. But when you just want to visualise something that you're coding, well, it just seems too much effort.

Take for example my Circle primitive class for XNA/MonoGame. It takes that entire class just to render a circle. You'd think XNA would come with it, but no, not circles. OK, let's just make lines, it comes with easy lines doesn't it. Not really. It seems like they didn't really want to offer you a simple solution, instead leaving that stuff up to you. Which is fine if you're making a big application/game, but when you just want to get something onto the screen, there has to be an easier way.

And there is: Pygames for Python. I know, it's just icky high-level pseudo language right. They don't even use braces! But it's actually pretty good. It's easy, it's quick, and it's simple.

One thing I do like about XNA is the way the Game class is laid out. I find it really intuitive and nice. So I went ahead and attempted to implement this in Python using Pygames.

Bear in mind this is my first ever real Python script, so it might have a few no-no's in there. Also, you may need this font if you want to run this. Either that or change "../fonts/visitor1.ttf" to None.

import sys
import pygame
from pygame.locals import * #@UnusedWildImport

class Game:
    def __init__(self, width, height, caption="Game"):
        self.width = width
        self.height = height
        self.caption = caption
        self.framerate = 30        
        self.foreground_color = (255, 255, 255)
        self.background_color = (100, 149, 237)
        self.initialize()
        self.loop()

    def initialize(self):
        pygame.init()
        pygame.display.set_caption(self.caption)
        self.screen = pygame.display.set_mode((self.width, self.height))        
        self.font = pygame.font.Font("../fonts/visitor1.ttf", 20)

    def loop(self):
        self.clock = pygame.time.Clock()
        while 1:
            gametime = self.clock.get_time()
            self.update(gametime)
            self.render(gametime)
            self.clock.tick(self.framerate)

    def update(self, gametime):        
        self.text = self.font.render("FPS: %d" % self.clock.get_fps(), 
                                     1, self.foreground_color)
        self.handle_input(pygame.event.get())

    def render(self, gametime):
        surface = pygame.Surface(self.screen.get_size())
        surface.convert()
        surface.fill(self.background_color)
        surface.blit(self.text, (8, 6))        
        self.screen.blit(surface, (0, 0))
        pygame.display.flip()

    def handle_input(self, events):
        for event in events:
            if event.type == pygame.QUIT: 
                sys.exit()
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_ESCAPE:
                    sys.exit()

if __name__ == "__main__":
    game = Game(800, 600, "A test game")

Looks legit to me!

Looks legit, right?

Unable to load DLL 'openal32.dll' The specified module could not be found.

By Adam K Dean on

This appears to be a common problem encountered after having just installed MonoGame.

Unable to load DLL 'openal32.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

It's really easy to fix though, download and install OpenAL and everything should work just fine.

Circle primitive class for XNA/MonoGame

By Adam K Dean on

I've just been playing a little with XNA (or MonoGame as it's called these days) and needed to draw circles. If you've used XNA then you'll know that nothing like that is provided for you. Drawing circles isn't too difficult really, they're basically just lots of little lines at different angles.

I've come up with the following class. It has a default number of points harcoded but you can overload that, allowing you to create triangles, rectangles, pentagons, hexagons etc.

Update: The number of points now is determined by the radius of the circle. The current calculation of Radius * Math.PI seems to work just fine. I don't know if there is any "golden" ratio, but I'm fine with this one.

Note, I've adopted my new style of putting fields at the bottom of the class. I like it, you get to the code much quicker.

public class Circle
{
    public Circle(float x, float y, int radius,
        GraphicsDeviceManager graphics)
        : this(x, y, radius, Color.White, graphics) { }

    public Circle(float x, float y, int radius,
        Color color, GraphicsDeviceManager graphics)
    {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.color = color;
        this.graphics = graphics;

        Initialize();
    }

    public void Draw()
    {
        effect.CurrentTechnique.Passes[0].Apply();
        graphics.GraphicsDevice.DrawUserPrimitives
            (PrimitiveType.LineStrip, vertices, 0, vertices.Length - 1);
    }

    private void Initialize()
    {
        InitializeBasicEffect();
        InitializeVertices();
    }

    private void InitializeBasicEffect()
    {
        effect = new BasicEffect(graphics.GraphicsDevice);
        effect.VertexColorEnabled = true;
        effect.Projection = Matrix.CreateOrthographicOffCenter
            (0, graphics.GraphicsDevice.Viewport.Width,
             graphics.GraphicsDevice.Viewport.Height, 0,
             0, 1);
    }

    private void InitializeVertices()
    {
        vertices = new VertexPositionColor[CalculatePointCount()];
        var pointTheta = ((float)Math.PI * 2) / (vertices.Length - 1);
        for (int i = 0; i < vertices.Length; i++)
        {
            var theta = pointTheta * i;
            var x = X + ((float)Math.Sin(theta) * Radius);
            var y = Y + ((float)Math.Cos(theta) * Radius);
            vertices[i].Position = new Vector3(x, y, 0);
            vertices[i].Color = Color;
        }
        vertices[vertices.Length - 1] = vertices[0];
    }

    private int CalculatePointCount()
    {
        return (int)Math.Ceiling(Radius * Math.PI);
    }

    private GraphicsDeviceManager graphics;
    private VertexPositionColor[] vertices;
    private BasicEffect effect;

    private float x;
    public float X
    {
        get { return x; }
        set { x = value; InitializeVertices(); }
    }
    private float y;
    public float Y
    {
        get { return y; }
        set { y = value; InitializeVertices(); }
    }
    private float radius;
    public float Radius
    {
        get { return radius; }
        set { radius = (value < 1) ? 1 : value; InitializeVertices(); }
    }
    private Color color;
    public Color Color
    {
        get { return color; }
        set { color = value; InitializeVertices(); }
    }
    public int Points
    {
        get { return CalculatePointCount(); }
    }
}

Using it is really easy. Create a Circle, and call circleObject.Draw().

Circle circle;

protected override void Initialize()
{
    // X, Y, Radius, Color (optional), GraphicsDeviceManager
    circle = new Circle(100f, 100f, 50f, Color.Violet, graphics);

    base.Initialize();
}

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

    circle.Draw();

    base.Draw(gameTime);
}

You can change the radius and the position of the Circle via the properties. I'll probably add the ability to rotate it.

I might add this and other primitive helper classes to a class library. If so I'll link it here in the future.

Below: four circles of varying size and number of points.

four circles of varying size and number of points

Updated: 25th March 2013

FizzBuzz? More than one line doesn't count!

By Adam K Dean on

Just doing research over at r/cscareerquestions when I stumbled upon a thread about FizzBuzz.

I've heard of it, but I've never actually done it. I decided to do it and put myself to the test.

Basically the rule is:

Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".

Seems simple enough:

static void EasyFizzBuzz()
{
    for (int i = 1; i <= 100; i++)
    {
        if (i % 3 == 0 && i % 5 == 0) Debug.WriteLine("FizzBuzz");
        else if (i % 3 == 0) Debug.WriteLine("Fizz");
        else if (i % 5 == 0) Debug.WriteLine("Buzz");
        else Debug.WriteLine(i);
    }
}

But that's too easy. I'm sure everyone comes up with that solution. I'd rather stand out if I ever get asked, and nothing stands out more than a giant line of ternary operators. (Does it count as one line if I've put it onto the next line for appearance sake? Only has one semi-colon!)

static void OneLineFizzBuzz()
{
    for (int i = 1; i <= 100; i++) Debug.WriteLine((i % 3 == 0 && i % 5 == 0) ?
        "FizzBuzz" : (i % 3 == 0) ? "Fizz" : (i % 5 == 0) ? "Buzz" : i.ToString());
}

Maybe I can come up with something more inventitive yet, just how creative can you be with such a simple problem?

Using caller information for verbose logging

By Adam K Dean on

A cool feature that has been added in Visual Studio 2012 and C# 5.0 is Caller Information attributes. Basically it allows you to see where your method was called from without having to sift through stack traces upon stack traces.

You simply prefix an optional parameter with one of the three attributes:

[CallerMemberName] provides the method or property name of the caller as a string.
[CallerFilePath] provides the full path of the source file that contains the caller as a string.
[CallerLineNumber] provides the line number in the source file at which the method is called as an int.

Learn by example. Here I've made a simple logging method, we pass to it a string, and the compiler will fill in the rest. Make sure to including System.Runtime.CompilerServices in your using statements.

static void VerboseLog(string value,
    [CallerMemberName] string callerMemberName = "",
    [CallerFilePath] string callerFilePath = "",
    [CallerLineNumber] int callerLineNumber = -1)
{
    int index = callerFilePath.LastIndexOf(Path.DirectorySeparatorChar);
    string localPath = callerFilePath.Substring(index + 1);
    Debug.WriteLine("{0} {1}:{2} {3}",
        localPath, callerMemberName, callerLineNumber, value);
}

Let's test it with a simple Console Application. Notice in the above method that I remove the full path to make it more readable. Not necessarily needed but pleasant nonetheless.

static void Main(string[] args)
{
    VerboseLog("this is a test");
    DoSomething();
}

static void DoSomething()
{
    for (int i = 0; i < 5; i++)
    {
        VerboseLog(string.Format("something {0}", i));
    }
}

This outputs:

Program.cs Main:16 this is a test
Program.cs DoSomething:24 something 0
Program.cs DoSomething:24 something 1
Program.cs DoSomething:24 something 2
Program.cs DoSomething:24 something 3
Program.cs DoSomething:24 something 4

So very simple, and yet very, very useful.