Introducing Craftwork Engine

draft craftwork engine logo

A couple of days ago I started merging the open source MonoGame CraftworkGUI project into my private Craftwork Engine project. Now the whole engine project is open source on github. Craftwork Engine is a 2D game engine based on MonoGame targeting mobile plaforms like Android, iOS and Windows Phone.

The engine is intended to follow the SOLID design principles but it isn’t quite there yet. So I’m also going to be refactoring the code along the way and blogging about why I think it fits better into the SOLID design principles, hopefully stimulating some interesting discussion to improve the design along the way.

Over the next couple of weeks I’ll be talking about how the engine works via a series of simple tutorials and examples while I’m refactoring and adding new features.

The get the ball rolling I think a good place to start is sprites. If you’ve worked on any kind of 2D game before, chances are you’ve used sprites in one way or another. However, if you’re using XNA or MonoGame you’ll quickly find out that there is no Sprite class provided for you. So the first thing I created in Craftwork Engine is a simple Sprite class that fits nicely into the SpriteBatch Draw methods.

    public class Sprite
    {
        public Sprite(TextureRegion textureRegion)
        {
            TextureRegion = textureRegion;
            Scale = Vector2.One;
            Colour = Color.White;
            Origin = Vector2.Zero;
            Rotation = 0;
            Depth = 0;
            Effect = SpriteEffects.None;
        }

        public Sprite(Texture2D texture)
            : this(new TextureRegion(texture))
        {
        }

        public Sprite(Texture2D texture, Rectangle sourceRectangle)
            : this(new TextureRegion(texture, sourceRectangle))
        {
        }

        public TextureRegion TextureRegion { get; set; }
        public Color Colour { get; set; }
        public Vector2 Origin { get; set; }
        public SpriteEffects Effect { get; set; }
        public float Depth { get; set; }
        public Vector2 Position { get; set; }
        public Vector2 Scale { get; set; }
        public float Rotation { get; set; }

        public Sprite Clone()
        {
            var sprite = new Sprite(TextureRegion)
            {
                Colour = Colour,
                Origin = Origin,
                Effect = Effect,
                Depth = Depth,
                Position = Position,
                Scale = Scale,
                Rotation = Rotation
            };

            return sprite;
        }

        public Texture2D Texture
        {
            get
            {
                return TextureRegion.Texture;
            }
        }

        public Rectangle SourceRectangle
        {
            get
            {
                return TextureRegion.Rectangle;
            }
        }

        public Point OriginPoint
        {
            get
            {
                return new Point((int)(Origin.X * SourceRectangle.Width), 
                    (int)(Origin.Y * SourceRectangle.Height));
            }
        }

        public Rectangle DestinationRectangle
        {
            get
            {
                var originPoint = OriginPoint;
                int x = (int)(Position.X - originPoint.X);
                int y = (int)(Position.Y - originPoint.Y);
                int width = (int)SourceRectangle.Width;
                int height = (int)SourceRectangle.Height;
                return new Rectangle(x, y, width, height);
            }
        }

        public override string ToString()
        {
            return string.Format("{0}", TextureRegion);
        }

You’ll also notice that the Sprite can take a TextureRegion in it’s constructor. This is so a sprite can be used with a texture atlas. I’ll talk more about that later, but for completeness I’ll include the TextureRegion class here.

    public class TextureRegion
    {
        public TextureRegion(Texture2D texture, int x, int y, int width, int height)
            : this(texture, new Rectangle(x, y, width, height))
        {
        }

        public TextureRegion(Texture2D texture)
            : this(texture, new Rectangle(0, 0, texture.Width, texture.Height))
        {
        }

        public TextureRegion(Texture2D texture, Rectangle rectangle)
        {
            Texture = texture;
            Rectangle = rectangle;
        }

        public Texture2D Texture { get; private set; }
        public Rectangle Rectangle { get; private set; }

        public int X
        {
            get
            {
                return Rectangle.X;
            }
        }

        public int Y
        {
            get
            {
                return Rectangle.Y;
            }
        }

        public int Width
        {
            get
            {
                return Rectangle.Width;
            }
        }

        public int Height
        {
            get
            {
                return Rectangle.Height;
            }
        }
    }

The final handy thing to know is how to render the Sprite with a SpriteBatch Draw call. For that I’ve created a handy extension method on the SpriteBatch class like this:

public static void Draw(this SpriteBatch spriteBatch, Sprite sprite)
{            
        spriteBatch.Draw(sprite.Texture, sprite.Position, sprite.SourceRectangle, 
             sprite.Color, sprite.Rotation, sprite.OriginPoint.ToVector2(), 
             sprite.Scale, sprite.Effect, sprite.Depth);
}

public static Vector2 ToVector2(this Point point)
{
        return new Vector2(point.X, point.Y);
}

Using the sprite class is simple. First create a sprite in the LoadContent method.

var texture = Content.Load<Texture2D>("splat");
_sprite = new Sprite(texture)
{
        Origin = new Vector2(0.5f, 0.5f),
        Position = new Vector2(100, 100)
};

Then render the sprite with the SpriteBatch extension method.

_spriteBatch.Begin();
_spriteBatch.Draw(sprite);
_spriteBatch.End();
  • Andrew Lynch

    Just wanted to drop an encouraging note here to let you know that I like what you’re doing, and hope to see more. :-) Thank you.

    • Dylan Wilson (@craftworkgames)

      Thanks Andrew. It’s nice to know when someone is listening. I’ve had a lot going on lately and I haven’t had time to get back to the blog but encouraging notes are good motivation :)

  • Tomasz Kot

    This Sprite class is a nice piece of code!
    One note : I would consider using MemberwiseClone in Clone method.

    • Dylan Wilson (@craftworkgames)

      Thanks. Good idea.