Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
26. October 2015

 

In the previous tutorial we look at the process of using sprites in SFML.  Today we are going to look instead at using a sprite sheet or texture atlas.  The process is very similar to working with a normal sprite, except that you have multiple sprites on a single texture.  Loading and swapping textures in memory is an expensive operation, so holding multiple sprites together can greatly improve the speed of your game.

 

As always there is an HD video version of this tutorial available here.

 

Many game engines have implicit support for spritesheets and sprite animation, however SFML does not.  There are however libraries built on top of SFML that provide this functionality or you can roll the required functionality yourself with relative ease.  Before we can continue we need a sprite sheet, which is simply one or more images with multiple frames of animation.  This is the one I am using:

dragonFrames

 

This isn’t the full sized image however.  The source file is actually 900x1200 pixels in size.  It should be noted that this size isn’t actually ideal.  Power of two texture dimensions should be preferred ( such as 512x256, 1024x1024, 2048x2048, etc. ).  In earlier version of OpenGL including OpenGL ES 1.x, a power of two texture was actually required.  These days, it is no longer a requirement, but a PoT texture generally will perform better.  As to file dimensions, you can fairly safely go up to 2048x2048 and support even rudimentary GPU’s like the Intel HD3000 series, but sizes of 4096x4096 are generally possible on most modern desktop and mobile GPUs.   You can notice from this image that it contains 12 textures, each 300x400 pixels in size.

 

Now we need code to extract a single frame from the texture and as you will see, it’s actually remarkably easy:

// Demonstrate creating a spritesheet
#include "SFML/Graphics.hpp"

int main(int argc, char ** argv){
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");

  sf::Event event;
  sf::Texture texture;
  texture.loadFromFile("images/dragonFrames.png");

  sf::Sprite sprite(texture,sf::IntRect(0,0,300,400));


  while (renderWindow.isOpen()){
    while (renderWindow.pollEvent(event)){
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();
    }

    renderWindow.clear();
    renderWindow.draw(sprite);
    renderWindow.display();
  }
}

 

This will draw just a small rectangular portion of our source texture, representing the first frame, like so:

image

 

Really that’s all that there is to it.  To add animation, we simply change the rectangular source after the fact, like so:

// Demonstrate creating a spritesheet
#include "SFML/Graphics.hpp"

int main(int argc, char ** argv){
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");

  sf::Event event;
  sf::Texture texture;
  texture.loadFromFile("images/dragonFrames.png");

  sf::IntRect rectSourceSprite(300, 0, 300, 400);
  sf::Sprite sprite(texture,rectSourceSprite);
  sf::Clock clock;

  while (renderWindow.isOpen()){
    while (renderWindow.pollEvent(event)){
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();
    }

    if (clock.getElapsedTime().asSeconds() > 1.0f){
      if (rectSourceSprite.left == 600)
        rectSourceSprite.left = 0;
      else
        rectSourceSprite.left += 300;

      sprite.setTextureRect(rectSourceSprite);
      clock.restart();
    }

    
    renderWindow.clear();
    renderWindow.draw(sprite);
    renderWindow.display();
  }
}

 

And when run:

GIF

 

You may notice right away that animation doesn’t look right and that’s a keen eye you’ve got there.  In this example we are simply going across the top three frames of animation from left to right.  The proper animation should actually be 0,1,2,1,0 not 0,1,2,0,1,2.  That said, in a proper game you would either roll your own animation class or use an existing one.  When we get to the process of creating a complete game, we will cover this process in detail.

 

In the above example we change frames of animation by changing the sprites source texture rect with a call to setTextureRect().  As I mentioned in the previous tutorial you could actually generate a new sprite per frame if preferred, as the sf::Sprite class is very light weight.

 

The Video

Programming , , ,

25. October 2015

 

In this Closer Look At we are going to be taking a closer look at the Otter2D game engine.  The Closer Look At series is a combination of an introduction, overview and getting started tutorial that is designed to help you quickly decide if a game engine/framework/library is right for you.  Otter2D is an open sourceotterlogo C# based open source game engine built over top of the SFML framework.  Otter2D provides essentially all of the functionality of SFML as well as higher level game engine features like animations, collisions, sessions and components.

 

As always there is an HD video version of this guide available here.

 

Doing what I do for a living I generally am surprised by a game engine or tool.  If I haven’t used it personally I have at least heard of it.  Otter on the other hand took me completely by surprise and for the most part it’s been a fun (and quick!) journey.  There isn’t a ton to Otter2D, it’s a code only framework built over a framework I am already familiar with.  If you have prior SFML experience, Otter2D will come quickly to you.  Due to the composition of Otter2D, this closer look is going to lean much more toward the code/tutorial side.  Let’s jump in.

 

Getting Started

 

Otter2D is available as a zip file (direct download link) or you can build from current source on Bitbucket.  The code is distributed under this license, excerpted below:

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

In case you are wondering, there are no conditions after the colon.  So basically it’s a license that lets you do pretty much whatever you want so long as you keep the license intact.

Getting set up is easy but could be better.  Unfortunately Otter2D is not configured to be used as an external library which is a bit of an odd choice.  Instead you add the Otter2D project to your solution and link to it as a dependency in your project, like so:

image

Full instructions on getting started Visual Studio are available here.

 

Your First Application

 

Now that you’ve got Otter downloaded and configured, let’s create our first application.

using Otter;

namespace OtterScratchPad
{
    class Program
    {
        static void Main(string[] args)
        {
            Game game = new Game();
            game.Color = Color.White;

            Scene scene = new Scene();

            Entity entity1 = new Entity();
            entity1.AddGraphic(Image.CreateCircle(90,Color.Red));
            scene.Add(entity1);

            game.Start(scene);
        }
    }
}

 

When you run this example you get:

image

 

Otter2D code is very similar to a traditional Entity Component System (ECS) in it’s approach.  Game is the heart of your application, while scene represents your game world.  The scene itself is composed of entities, which in turn contain defined components like Graphic we used above, or actual components, which we will see later.  A more common approach to the above solution is to derive your game objects from Entity instead, like so:

 

using Otter;

namespace OtterScratchPad
{
    class Player : Entity
    {
        public Player(float x, float y) : base(x, y)
        {
            this.AddGraphic(Image.CreateCircle(120, Color.Red));
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Game game = new Game();
            game.Color = Color.White;

            Scene scene = new Scene();

            Entity entity1 = new Player(0,0);
            scene.Add(entity1);

            game.Start(scene);
        }
    }
}

 

Those two examples are functionally equivalent.  As you can see in the second example when we pass coordinates to our Player Entity the origin by default is the top left corner, both of the screen as well as the sprite.  We will look at changing this shortly.  Congratulations however, you just created your first successful Otter2D application.  Easy eh?  Let’s move on and add some sophistication.

 

Input and Image Loading

 

In our next example, instead of creating a graphic procedurally, we are instead going to load a sprite.  For the source images, I am using the freely available sprites announced here.  Specifically I am using this sprite of a B17 bomber. 

 1

Obviously you can use whatever image you wish, so long as the file format is supported.  Keep in mind, Otter2D is built over SFML so it has the same graphics support as SFML which means your sprite can be in bmp, hdr, gif, jpg, png, pic, psd, tga formats.  Additionally in this example we are going to look at handling keyboard input to move our image left and right in response to key presses.  Alright, on to the code:

using Otter;

namespace OtterScratchPad
{
    class PlayerImage : Entity
    {
        public PlayerImage(float x, float y) : base(x, y)
        {
            this.AddGraphic(new Image("1.png"));
        }
        public override void Update()
        {
            if (Input.KeyDown(Key.Right))
            {
                this.X++;
            }
            if (Input.KeyDown(Key.Left))
            {
                this.X--;
            }

            base.Update();
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Game game = new Game();
            game.Color = Color.White;

            Scene scene = new Scene();

            Entity entity1 = new PlayerImage(0,0);
            scene.Add(entity1);

            game.Start(scene);
        }
    }
}

 

And when you run it:

example1

 

Note when you launch an Otter2D application, it doesn’t have focus and thus wont receive keyboard events.  This seems to be a bug in the underlying SFML libraries.  I spent a few minutes trying to fix it and sadly couldn’t.  Shouldn’t be a big deal as a production game wont use a console window to construct the main window, so this shouldn’t occur.  As you can see from this example, loading sprites and polling input are both trivial exercises, so let’s move on to animations.

 

Sprite Animations

 

In this example I’ve taken the three different frames of the B17 bomber and created a single 3x1 768x256 using ImageMagick.  The end result was:

ss

 

Not very exciting an animation, but each frame the propeller has turned slightly.  Let’s look at the process in Otter2D of using an animated sprite from a single sheet.  In this example we also show how to move the sprites origin or pivot point to it’s middle.

using Otter;

namespace OtterScratchPad
{
    class PlayerSpriteMap : Entity
    {
        enum Animations { Idle };
        Spritemap<Animations> spriteMap = new Spritemap<Animations>("ss.png", 256, 256);

        public PlayerSpriteMap(float x, float y) : base(x, y)
        {
            this.AddGraphic(spriteMap);
            spriteMap.CenterOrigin();
            spriteMap.Add(Animations.Idle, new int[] { 0, 1, 2 }, new float[] { 9.0f, 4.0f, 10.0f });
            spriteMap.Play(Animations.Idle);
        }
        public override void Update()
        {

            if (Input.KeyDown(Key.Right))
            {
                this.X += 300f * this.Game.RealDeltaTime / 1000;
            }
            if (Input.KeyDown(Key.Left))
            {
                this.X -= 300f * this.Game.RealDeltaTime / 1000;
            }

            base.Update();
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            Game game = new Game();
            game.Color = Color.White;

            Scene scene = new Scene();

            Entity entity1 = new PlayerSpriteMap(game.HalfWidth,game.HalfHeight);
            scene.Add(entity1);

            game.Start(scene);
        }
    }
}

 

Run it and:

 

example2

 

You may have to squint to see it, but that is certainly an animated sprite.  Instead of using Image we instead use a SpriteMap which takes the individual sprite dimensions as well as the spritesheet file name in it’s constructor.  The entity is then centered with a call to CenterOrigin().  Animations is simply a user defined enum that is used as the key within the sprite map.  When the map is added we pass in an array of ints or strings representing the frames offset within the source image as well as the duration for each frame of animation.

 

Collisions

 

Next we will modify the earlier example so we now have two airplane sprites that can collide with each other.  In addition to adding two sprites to the world, the example has also been changed so you can create both a player and non-player sprite, so only one responds to keyboard input.  Additionally instead of simply moving via X++, we change it so we move at a fixed frame rate depending on the elapsed time since the last frame.

using Otter;

namespace OtterScratchPad
{
    class PlayerSpriteMapWithCollisions : Entity
    {
        enum Animations { Idle };
        Spritemap<Animations> spriteMap = new Spritemap<Animations>("ss.png", 256, 256);

        enum ColliderTypes { Planes };
        bool isPlayer;
        public PlayerSpriteMapWithCollisions(float x, float y, bool player) : base(x, y)
        {
            isPlayer = player;
            this.AddGraphic(spriteMap);
            spriteMap.CenterOrigin();
            spriteMap.Add(Animations.Idle, new int[] { 0, 1, 2 }, new float[] { 9.0f, 4.0f, 10.0f });
            spriteMap.Play(Animations.Idle);

            this.AddCollider(new BoxCollider(256, 256, ColliderTypes.Planes));
            this.Collider.CenterOrigin();
        }

        public override void Update()
        {
            if (isPlayer)
            {
                if (Input.KeyDown(Key.Right))
                {
                    this.X += 100f * this.Game.RealDeltaTime / 1000;
                }
                if (Input.KeyDown(Key.Left))
                {
                    this.X -= 100f * this.Game.RealDeltaTime / 1000;
                }

                // Check for collisions
                if (this.Collider.Overlap(X, Y, ColliderTypes.Planes))
                    this.X = 0f;
            }
            base.Update();
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            Game game = new Game();
            game.Color = Color.White;

            Scene scene = new Scene();

            Entity entity1 = new PlayerSpriteMapWithCollisions(0,game.HalfHeight,true);
            Entity entity2 = new PlayerSpriteMapWithCollisions(game.Width - 128, game.HalfHeight, false);

            scene.Add(entity1);
            scene.Add(entity2);

            game.Start(scene);
        }
    }
}

 

And run it:

example3

 

You may notice the collision isn’t amazingly accurate.  This is because in this example we used a box collider but there is some dead pixel space between our wing and the end of the image.  If you require increased accuracy you could instead us a pixel collider, but it will have a profound effect on performance.

 

Sound and Music

 

Next lets look at adding audio support to our game.  Let’s start by adding some sound effects to our player:

    class PlayerImageWithSound : Entity
    {
        // 30 cal burst:: http://www.freesound.org/people/Hamp999/sounds/151620/
        Sound sound1 = new Sound("sound1.wav");

        // Bomb drop http://www.freesound.org/people/sunch/sounds/274090/
        Sound sound2 = new Sound("sound2.wav");

        public PlayerImageWithSound(float x, float y) : base(x, y)
        {
            this.AddGraphic(new Image("1.png"));

            //Center within and without
            this.Graphic.CenterOrigin();
        }
        public override void Update()
        {
            if (Input.KeyDown(Key.Num1))
            {
                sound1.Play();
            }
            if (Input.KeyDown(Key.Num2))
            {
                sound2.Play();
            }

            base.Update();
        }
    }

 

When you hit the 1 key a certain sound effect plays, hit the 2 key a different sound effect plays.  There are two important things to note with audio in Otter2D.  Once again, it depends on SFML for the audio code so Otter supports the file formats that SFML supports ( which are too many to list ).  Additionally playing a sound halts earlier versions of that sound, so if you want multiple concurrent plays of the same sound, you need to create multiple instances.

 

Now let’s take a look at a music example.  Instead of extending another Entity, we will be creating our own Scene this time.

using Otter;

namespace OtterScratchPad
{
    class PlayerImageWithSound : Entity
    {
        // 30 cal burst:: http://www.freesound.org/people/Hamp999/sounds/151620/
        Sound sound1 = new Sound("sound1.wav");

        // Bomb drop http://www.freesound.org/people/sunch/sounds/274090/
        Sound sound2 = new Sound("sound2.wav");

        public PlayerImageWithSound(float x, float y) : base(x, y)
        {
            this.AddGraphic(new Image("1.png"));

            //Center within and without
            this.Graphic.CenterOrigin();
        }
        public override void Update()
        {
            if (Input.KeyDown(Key.Num1))
            {
                sound1.Play();
            }
            if (Input.KeyDown(Key.Num2))
            {
                sound2.Play();
            }

            base.Update();
        }
    }

    class SceneWithMusic : Scene
    {
        // Sound file is http://www.freesound.org/people/Diboz/sounds/216071/
        // Play, not looping
        public Music song = new Music("music.ogg", false);

        public SceneWithMusic() : base()
        {
            song.Play();
        }

        public override void Update()
        {
            base.Update();

            if (Input.MouseButtonPressed(MouseButton.Left))
                Music.GlobalVolume -= 0.1f;
            if (Input.MouseButtonPressed(MouseButton.Right))
                Music.GlobalVolume += 0.1f;

            if (Input.KeyPressed(Key.Space))
            {
                // Fast forward 10 seconds on spacebar if 10 seconds remain in play time
                if ((song.Offset + 10000) < song.Duration)
                    song.Offset += 10000;
            }
            if (!song.IsPlaying)
            {
                System.Console.WriteLine("Music stopped");
                Game.Close();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Game game = new Game();
            game.Color = Color.White;

            Scene scene = new SceneWithMusic();

            Entity entity1 = new PlayerImageWithSound(0,game.HalfHeight);


            scene.Add(entity1);

            game.Start(scene);
        }
    }
}

In this example there is now music playing when you start your game and you can fast forward the song by pressing the space bar.  Additionally global (all sound effects and music ) volume can be increased and decreased using the left and right mouse button, also illustrating how easy it is to poll the mouse for input as well. 

 

Components

 

I mentioned earlier that Otter2D has component support?  Well we saw a bit of it in action with the addition of Graphic objects to our Entity.  In this case though we are going to drop in slightly more advanced components, as well as create one of our own.

    class CustomComponent : Component
    {
        public override void Update()
        {
            base.Update();
            float alpha = Entity.GetGraphic<Image>().Alpha;
            alpha -= 0.005f;
            if (alpha <= 0.0f)
                alpha = 1.0f;
            Entity.GetGraphic<Image>().Alpha = alpha;
        }
    }
    // A Player Entity with components attached
    class PlayerWithComponents : Entity
    {
        public PlayerWithComponents(float x, float y) : base(x, y)
        {
            this.AddGraphic(new Image("1.png"));

            Axis axis = Axis.CreateWASD();
            BasicMovement movement = new BasicMovement(100, 100, 20);
            movement.Axis = axis;
            AddComponents(
                axis,
                movement,
                new CustomComponent()
                );
            this.Graphic.CenterOrigin();
        }
    }

 

And when you run it:

example4

 

Here we’ve used a set of built in keys, one for applying movement to your entity.  The parameters passed in limit x and y velocity as well as the rate of acceleration.  Then we create another component that maps the WASD keys to an axis, which can be used to drive our movement component.  Finally we create a custom component that changes our alpha over time.

 

Documentation and Community

 

Otter2D’s documentation consists of a collection of examples here and a generated reference manual available here.  It’s not a huge body of work however Otter2D is extremely simple to use so doesn’t really need much more.  Additionally it is built on top of SFML, so that underlying documentation and community are also available.  That said, it is built on the C# bindings of SFML and that community is much smaller than the main SFML community.

 

In terms of community there is a small forum available here.  It’s not extremely active but it is a place to get your questions answered.  There are a handful of tutorials on the web but not a huge amount by any definition of the word.

 

Conclusion

 

Otter2D is a pretty solid 2D game engine built over top of a well established framework.  It was extremely easy to learn and use, the design is clean enough you can intuit most of the process.   On major flaw I never discussed was the support for the Ogmo level editor, which IMHO was a very bad choice.  First off, it simply doesn't work, loaded levels are mangled.  Worse though, Ogmo itself was a frustrating mess to work with… every single change to the map (adding a layer, changing size, tile format, etc. ), completely destroyed everything you had done up to that point.  Adding support for a better editor such as Tiled would be relatively short work, but this was by far the most frustrating part of working with Otter2D.

 

Otherwise it is exactly what it is.  A clean, easy to use, fairly full featured 2D C# game engine.  As it is built over SFML and in C# deploying to platforms such as Android or iOS is most likely going to be painful ( no documentation exists ) and will most certainly require a Xamarin license.  For a 2D desktop title or as a learning game engine however, Otter2D would be a very solid choice.  As odd as it sounds, Otter2D was simply fun to work with, somewhat like Love2D, especially after dealing with much more complex engines or even SFML itself.

 

The Video

Programming , ,

23. October 2015

 

In a past life in which I sat in a cubicle and someone actually gave me a pay check every week I was a huge fan of Xamarin products.  They do very a good job of enabling .NET developers to leverage their skill across many platforms.  In fact, they are the technology that Unity is built on top off.  That said, I became increasingly less of a fan when the checks stopped and expenses came out of my own pocket!

 

There are many people out there that view making money from software as somehow evil.  I am certainly not one of those people.  In that corporate environment, where developers are paid salaries, rent is paid for office space, taxes are paid, etc…  the price of a software tool like Xamarin is trivial to justify.  In the world of indie game development though, this is often simply not the case.

 

Thing is, Xamarin has become a necessary evil for so many C# based game engines ( MonoGame, WaveEngine, Duality, Paradox, etc ) if you want to port to iOS or Android.  Many of these developers will never see a dime from their efforts, while a select few will become massively rich and a certain middle ground will eek out a living doing what they love.  It’s the later two groups that keep companies like Unity and Unreal afloat, and those two groups don’t come into being without the former group.

 

The challenge with Xamarin has always been their license structure has always been pretty awful for amateur developers.  How many people have chosenn not to work in C# simply because their is a price tag attached?  After years of awaiting a newer friendlier license structure (or Microsoft buyout), hope is on the horizon.

 

Today, in response to Xamarin’s recent acquisition of RoboVM, I ended up in this Twitter conversation with Nat Friedman, CEO of Xamarin:

 

First, in regard to the acquisition of RoboVM and how long the free for LibGDX developers offer will be extended:

image

 

Then more on the indie friendly nature of Xamarin, or lack thereof:

image

(Portion excerpted, Twitter message threading is bizarre)

image

image

 

This is news I am certain many C# game developers and tool providers are going to be delighted to hear.  Hopefully something happens fairly soon, as I’ve been waiting about 6 years and counting at this point! ;)

Programming, News , ,

22. October 2015

 

As part of the ongoing Tiled Map Editor tutorial this part looks at using Object Layers, which can be used to interface your tiled map with your games code.  This particular example includes the code using TypeScript and the Phaser game library.  If you need more information on Phaser I’ve done a tutorial series here.

 

The video of this tutorial is available here or embedded below.

 

The Code

index.html

 

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Hello Phaser</title>
    <link rel="stylesheet" href="app.css" type="text/css" />
    <script src="phaser.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <div id="content"></div>
</body>
</html>

app.ts

/// <reference path="phaser.d.ts"/>


class ObjectEntity {
    height: number;
    name: string;
    properties: any;
    rectange: boolean;
    rotation: number;
    type: string;
    visible: boolean;
    width: number;
    x: number;
    y: number;
}

class SimpleGame {
    game: Phaser.Game;
    map: Phaser.Tilemap;
    layer: Phaser.TilemapLayer;
    player: Phaser.Sprite;
    winZone: Phaser.Rectangle;

    constructor() {
        this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {
            create: this.create, preload:
            this.preload, update: this.update, render: this.render
        });
    }
    preload() {
        this.game.load.tilemap("ItsTheMap", "newMap.json", null, Phaser.Tilemap.TILED_JSON);
        var img = this.game.load.image("HF2_A2", "HF2_A2.png");
        this.game.load.image("Decepticon", "decepticonLarge.png");
    }
    update() {

        if (this.winZone.contains(this.player.x + this.player.width/2,this.player.y + this.player.height/2))
            alert("You Win!");

    }
    render() {
    }
    create() {
        this.map = this.game.add.tilemap("ItsTheMap", 32, 32, 64, 32);
        this.map.addTilesetImage("HF2_A2","HF2_A2");

        this.map.createLayer("Background").resizeWorld();

        this.player = new Phaser.Sprite(this.game, 0, 0, "Decepticon");
        this.player.width = 64;
        this.player.height = 64;
        this.game.world.addAt(this.player, 1);

        this.game.camera.follow(this.player);

        var something = this.map.objects["GameObjects"][0];
        var start = <ObjectEntity>this.map.objects["GameObjects"][0];
        var end = <ObjectEntity>this.map.objects["GameObjects"][1];


        this.winZone = new Phaser.Rectangle(end.x, end.y, end.width, end.height);

        this.player.position.set(start.x, start.y);

        this.game.input.keyboard.addKey(Phaser.Keyboard.LEFT).onUp.add(() => {
            this.player.position.add(-32, 0);
        });

        this.game.input.keyboard.addKey(Phaser.Keyboard.RIGHT).onUp.add(() => {
            this.player.position.add(32, 0);
        });

        this.game.input.keyboard.addKey(Phaser.Keyboard.UP).onUp.add(() => {
            this.player.position.add(0,-32);
        });

        this.game.input.keyboard.addKey(Phaser.Keyboard.DOWN).onUp.add(() => {
            this.player.position.add(0, 32);
        });
    }
}

window.onload = () => {
    var game = new SimpleGame();
};

 

 

The Video

 

Art, Design, Programming , , , ,

20. October 2015

 

In the previous tutorial we covered the basics of using graphics in SFML.  Chances are however your game isn’t going to be composed of simple shapes, but instead made up of many sprites.  That is exactly what we are going to cover today.

 

As always, there is an HD version of this tutorial available here

 

First off, we should start by defining what exactly a Sprite is.  In the early days of computers, sprite had special meaning as there was literally sprite hardware built into early 8bit computers.  A sprite is basically an image on screen that can move.  That’s it.   In SFML this relationship is easily demonstrated by it’s class hierarchy.

 

image

 

There is one very key concept to understand with sprites however.  A sprite in SFML represents and image or texture on screen that can be moved around.  However, it does not own the texture or image!  This makes the Sprite class fairly light weight, which certainly isn’t true about Texture or Image, the classes that actually contain all the data in the image.  Perhaps it’s easiest to start with a simple demonstration.

 

First we need an image to work with.  I am using a dragon sprite from the recent Humble Indie Gamedev Bundle.  The image looks like so:

dragonBig

 

Obviously you can use whatever image you want, just be sure to copy it into the working directory of your application.  In Visual Studio, the working directory can be located in the project’s properties panel under Debugging called Working Directory:

image

 

The image can be any of the following formats: bmp, hdr, gif, jpg, png, pic, psd, tga.  Keep in mind, not all formats are created equal.  Bitmap for example does not support transparency encoding and are generally quite large, but lose no image details and are simple to work with.  Gif has some patent issues and should generally be avoided.  Png seems like genuinely a good mix between features, size and quality and is well supported by content creation tools.

 

Ok, enough setup, let’s get to some code.

// Demonstrate sprite drawing in SFML
#include "SFML/Graphics.hpp"

int main(int argc, char ** argv){
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");

  sf::Event event;

  sf::Texture texture;
  texture.loadFromFile("images/dragonBig.png");

  sf::Sprite sprite(texture);

  while (renderWindow.isOpen()){
    while (renderWindow.pollEvent(event)){
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();
    }

    renderWindow.clear();
    renderWindow.draw(sprite);
    renderWindow.display();
  }
}

 

And when you run that:

image

 

As you can see, the experience is remarkably consistent to drawing using graphics primitives.  The big difference here is that we create our Sprite by providing a texture, which in turn we loaded from file with a call to Texture::loadFromFile().  There exist methods to load from stream or memory if preferred.  It is again important to remember that the Sprite does not own the Texture.  This means if the texture goes out of scope before the Sprite, the sprite will draw a blank rectangle.  This also means that several sprites can use the same texture.

 

Now you may have noticed that in addition to sf::Texture, there is a class called sf::Image and you may be wondering why.  There is one very simple difference at play here.  A Texture resides in the memory of your graphics card, while an image resides in system memory.  The act of copying an image from system memory to the GPU is quite expensive, so for performance reasons you almost certainly want to use Texture.  That said, Texture isn’t easily modified, so if you are working on a dynamic texture or say, creating a screen shot, Image is the better choice.  There exist methods to switch between the two types, but they are also fairly heavy in performance, so do not do them on a frame by frame basis.  At the end of the day

 

Let’s take a quick look at creating a dynamic image next.  Not something you are going to do often for most games granted, but it makes sense to mention it now.

// Demonstrate creating an Image
#include "SFML/Graphics.hpp"

int main(int argc, char ** argv){
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");

  sf::Event event;

  sf::Image image;
  image.create(640, 480, sf::Color::Black);
  
  bool isBlackPixel = false;
  sf::Color blackPixel(0,0,0,255);
  sf::Color whitePixel(255, 255, 255, 255);

  //Loop through each vertical row of the image
  for (int y = 0; y < 480; y++){
    //then horizontal, setting pixels to black or white in blocks of 8
    for (int x = 0; x < 640; x++){


      if (isBlackPixel)
        image.setPixel(x, y, blackPixel);
      else
        image.setPixel(x, y, whitePixel);
      // Every 8th flip colour
      if (!(x % 8))
        isBlackPixel = !isBlackPixel;
    }
    // Offset again on vertical lines to create a checkerboard effect
    if(!(y%8))
      isBlackPixel = !isBlackPixel;
  }

  sf::Texture texture;
  texture.loadFromImage(image);
  sf::Sprite sprite(texture);

  while (renderWindow.isOpen()){
    while (renderWindow.pollEvent(event)){
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();
    }

    renderWindow.clear();
    renderWindow.draw(sprite);
    renderWindow.display();
  }
}

 

When you run this example you should see:

image

 

Here you can see we can modify the pixels directly in our sf::Image.  However to display it on screen we still need to move it to texture and populate a sprite.  The difference is direct access to the pixel data.  Another important capability of sf::Image is the method saveToFile which enables you to well, save to file.  Obviously useful for creating screenshots and similar tasks.

 

You may notice depending on the resolution or composition of your source image that you texture may not look exactly like your source image.  This is because there is a smoothing or antialiasing filter built in to SFML to make images look smoother.  If you do not want this, perhaps going for that chunky 8bit look, you can turn it off with a call to setSmooth(false);

 

That is all we are going to cover today.  In the next tutorial part we will take a look at spritesheets, so we can have multiple different sprites in a single source image.

 

The Video

 

Programming , , ,

 

Month List

Popular Comments