Feeds:
Posts
Comments

Archive for July, 2012

So I’ve added a new game to the downloads page – Blackjack. It’s a fairly basic game featuring yourself, 4 randomly named computer players (well, randomly named from a list of 30) and a dealer. You can see all the cards that are dealt (bar the dealer’s “hole” card) although this may change in future versions. The computer follows the basic strategy from the Wikipedia page on the game. If you, or the computer, is unfortunate to go bust during the game, your name will go black and get struck through making it obvious. You play through the use of buttons on the screen and there is a running commentary (current player and their score) going in the top left corner. Give it a look, as always it’s programmed in C++11 and SDL, using Code::Blocks and compiled using GCC 4.7.

There’s also been an update to Brick Buster – the text has changed to white (whew, big stuff!) and now when you destroy a brick it’s score will pop up on the screen where the brick used to be to let you know what you just scored. This only hangs around for 750ms (3/4 of a second) and is programmed through a rethinking of the ParticleEngine class. The Particle class was scrapped, and ParticleEngine renamed to ScoreEngine. It is now passed an SDL_Surface* along with the original Point2D in it’s constructor. This SDL_Surface* is rendered at the Point2D until it is considered “dead” (i.e. once the 750ms have run out) when it is erased.

The code that creates the ScoreEngines is shown below. The important thing was to remove the ScoreEngine code from the ball->move() call because only GameState’s can create SDL_Surface*’s, it makes no sense for the Ball to have any knowledge of an ImageCache. I’ve also made a fairly major change to where PowerUp’s are generated, finally finding a solution that allows me to remove the logic from the Ball class and put it in the Level class. Again, it makes no sense for the Ball to have any knowledge of the PowerUps in the game as it doesn’t have to interact with them (like the bricks) and the only reason the logic was there because that was how I first coded it (the ball hit a brick and a powerup was randomly generated). The moveScore return value was even there for ages before I realised that I could move the logic out because a moveScore > 0 meant that a brick had been destroyed and so a powerup could be randomly generated. Anyway, that is also shown below in the new Ball movement logic in Level.

for(auto it = balls.begin(); it != balls.end();)
{
    int moveScore = (*it)->move(bat->getBox(), bat->getXVel(), bricks, powerUps);

    if(moveScore  < 0)
        it = balls.erase(it);
    else if (moveScore > 0)
    {
        sEngines.push_back(std::make_shared<ScoreEngine>((*it)->getPosition(), imgCache->getText(brickFont, "", moveScore)));
        generatePowerUp((*it)->getPosition());

        levelScore += moveScore;
        ++it;
    }
    else
    {
        ++it;
    }
}

The new code is up on the downloads page.

Read Full Post »

Inspired by this thread over at Gamedev, I set out on yet another learning journey to build my own particle engine. After looking at the links on that thread, I remembered that the legendary Lazy Foo has his own article on Particle Engines and headed over there to have a perusal (as you should, his explanation will make a lot more sense than mine…).

So it turns out that as with most of these things, you need to set up the base class first (well, it’s not technically a base class in the normal sense of the word, but it’s the basis of our engine) – a Particle class. Of course as each particle is drawn to the screen that makes it a Sprite in my project. An incredibly simple sprite at that too. One function to manage the sprite on a frame by frame basis and one to calculate if it’s dead. The two private functions are for creating the image filename and ensuring the particles appear in a circle.

class Particle : public Sprite
{
    private:
        int frame;

        inline const double distance(const Point2D p1, const Point2D p2) const;
        std::string chooseImage();
    public:
        Particle(Point2D point);
        ~Particle() = default;

        void manage();
        bool isDead();
};

Creation of a particle is a fairly simple routine – randomly create points either side of the centre point, ensuring that they’re no more than a distance of 75 from the centre (the radius, ensuring that the particles are in a circular shape). Particles are given a random frame number so they don’t all appear and disappear at the same time. chooseImage() randomly chooses whether the image will be dark grey or light grey.

Particle::Particle(Point2D point) : Sprite(chooseImage())
{
    //Set offsets
    short xDir, yDir;
    switch(rand() % 4)
    {
        case 0: xDir = 1; yDir = 1; break;
        case 1: xDir = -1; yDir = 1; break;
        case 2: xDir = 1; yDir = -1; break;
        case 3: xDir = -1; yDir = -1; break;
    }

    //ensure that the smoke appears in a circular shape
    do
    {
        position.x = point.x + (xDir * (rand() % 75));
        position.y = point.y + (yDir * (rand() % 75));
    }
    while(distance(point, position) > 75);

    //Initialize animation
    frame = rand() % 5;
}

manage() increments the frame counter each frame and isDead checks whether the frame counter has cleared 10, the maximum number of frames a particle can exist for.

The ParticleEngine class handles the management, creation and destruction of the particles in it’s constructor and managePartices() function. The two iterator functions are so that the particles can be drawn from drawSpriteCntr(T) in the Level class. isRunning() provides a check to see if the engine’s running time has expired.

class ParticleEngine
{
    private:
        using ParticlePtr = std::shared_ptr<Particle>;
        const unsigned short numberOfParticles = 500;
        const unsigned short engineTime = 350;

        Point2D position;
        std::vector<ParticlePtr> particles;
        const int startTime;

    public:
        ParticleEngine(Point2D point);
        ~ParticleEngine() = default;

        void manageParticles(int groundLevel);
        bool isRunning();

        std::vector<ParticlePtr>::iterator begin() { return std::begin(particles); }
        std::vector<ParticlePtr>::iterator end() { return std::end(particles); }
};

The constructor takes the Point2D passed in and moves it a little bit to centre the explosion on the point of impact, then creates the numberOfParticles stated by the const in the header.

ParticleEngine::ParticleEngine(Point2D point) : startTime(SDL_GetTicks())
{
    position.x = point.x + 4;
    position.y = point.y + 10;

    for( int p = 0; p < numberOfParticles; p++ )
    {
        particles.push_back(std::make_shared<Particle>(position));
    }
}

manageParticles() iterates through the particles, managing each of them (i.e. incrementing their frame counter). It checks whether they have expired and if so erases the current particle and pushes a new one onto the vector in it’s place.

void ParticleEngine::manageParticles(int groundLevel)
{
    for(auto it = std::begin(particles); it != std::end(particles);)
    {
        (*it)->manage();

        if((*it)->isDead())
        {
            it = particles.erase(it);
            particles.push_back(std::make_shared<Particle>(position));
        }
        else
            ++it;
    }
}

Running the engine in the Level class is relatively simple after everything we’ve now set up. Add a std::vector<std::shared_ptr> to the Level members, then when a building is hit we call these two lines. The first one to position the engine, the second one to create it.

Point2D smokePos = bomb->getPosition();
pEngines.push_back(std::make_shared<ParticleEngine>(smokePos));

Then every ParticleEngine in the vector has to be managed at the end of the logic() function, through the following code, which basically says if the engine is running then manage it, otherwise erase it. Simple.

for(auto it = std::begin(pEngines); it != std::end(pEngines);)
{
    if((*it)->isRunning())
    {
        (*it)->manageParticles(groundLevel);
        ++it;
    }
    else
    {
        it = pEngines.erase(it);
    }
}

Finally we have to draw the engines to the screen. Again, thanks to our templated drawSpriteCntr(T) function, and our iterator functions in ParticleEngine, this is simple – iterate through the engines std::vector and draw them all to screen.

for(const auto& pE : pEngines)
{
    drawSpriteCntr(*pE);
}

And just like that, we have a working particle engine that produces effects such as this:

Now, no-one is going to mistake this for smoke anytime soon, but for me it does the job just fine. I’m sure I will return to this in an effort to improve it in the future but for now it’s a portable engine class that produces nice smoke effects that can easily be modified to produce other particle effects (fire for instance).

Read Full Post »

AudioCache

Whilst I’m happy with my games and the way they’ve come together, there has always been one thing missing – you can only play them in complete silence. There is no music, no sound effects, nothing to hook you in and help you to become part of the game. Every good game has sound and the best games make it an integral part of the experience. Whilst the rise of mobile gaming has reduced the impact of sound (in my experience at least – I very rarely have the sound on my iPhone turned on), in the PC and console market thousands is spent on creating the right atmosphere through the music and sound effects and also using sound as a key to the user about what’s about to happen (the music in Left 4 Dead is a prime example of this).

So to rectify this in my games (at a basic level anyway) I set out to integrate some audio. Luckily SDL has an extension library built to deal with sound already – SDL_mixer. I chose Bomber Run as my testing project of choice, figuring that the plane’s engine, building destruction and title menu music would give me enough variety to learn through.

Originally I started out using this wonderful tutorial to get a basic level of music playing on one game state. However when I tried to coordinate this across multiple game states (title and level), things got ugly and I came across a lot of null pointer segfault issues. This lead me to create an AudioCache that handled all of my sounds in the same way that my ImageCache handles all of my graphical needs.

In essence, AudioCache provides a wrapper around the SDL_mixer function calls and stores the sound files in a map in the same way that ImageCache stores it’s images, with the key being a std::string (i.e. the path to the file). I based all the functions on the ImageCache functions, because why try to reinvent the wheel?

The AudioCache class interface is pretty simple. The get…() function calls return the music or sound effect, whilst the play and stop methods are obvious. The load..() methods do the loading and flush() is used in the destructor (as in ImageCache)

class AudioCache
{
    public:
        AudioCache();
        virtual ~AudioCache();

        Mix_Music* getMusic(const std::string filename);
        Mix_Chunk* getEffect(const std::string filename);
        void playMusic(Mix_Music* music, int loops);
        void playEffect(int channel, Mix_Chunk* effect, int loops);
        void stopMusic();
        void stopChannel(int channel);

    private:
        std::map<std::string, Mix_Music*> music;
        std::map<std::string, Mix_Chunk*> effects;

        Mix_Music* loadMusic(const std::string file);
        Mix_Chunk* loadEffect(const std::string file);
        void flush();
};

The constructor for the AudioCache is incredibly simple. Initialize the SDL Audio subsystem, then open SDL_mixer with some settings

AudioCache::AudioCache()
{
    SDL_InitSubSystem(SDL_INIT_AUDIO);

    if(Mix_OpenAudio(22050, AUDIO_S16SYS, 1, 2048) == -1)
    {
        printf("Unable to open audio!\n");
        exit(80);
    }
}

To get a music file or a sound effect, I use the same method as in the ImageCache for getting an image or text out of the map. Iterate through it to find the filename and if it isn’t there, add it to the map and return the file. This shows the method for getting a Mix_Music* file

Mix_Music* AudioCache::getMusic(const std::string file)
{
    auto i = music.find(file);

    if(i == std::end(music))
    {
        try
        {
            Mix_Music* temp = loadMusic(file.c_str());
            i = music.insert(i, std::make_pair(file, temp));
        }
        catch(const char* e)
        {
            printf("%s", e);
            exit(81);
        }
    }

    return i->second;
}

The play and stop methods are literally just wrappers for the SDL_mixer functions, keeping all the audio controls to one class and removing the need for any knowledge of what’s going on in the game state. It also allows me to add in any other functionality that I may want to do before or after calling the relevant functions (i.e. exception checking).

I’ve added an AudioCache pointer to my base GameState class, meaning that every GameState can access it and play sound. The base class now looks like this:

class GameState
{
    protected:
        ImageCache* imgCache;
        AudioCache* audioCache;
        StateManager* stateMan;

    public:
        GameState(ImageCache* ic, AudioCache* ac, StateManager* sm) : imgCache(ic), audioCache(ac), stateMan(sm) {}
        virtual void handleEvents(SDL_Event& event) = 0;
        virtual void logic() = 0;
        virtual void draw() = 0;
        virtual ~GameState() {};
};

Looks like some kind of Game Engine is emerging here…

Read Full Post »