Perl animated game. Playing with Perl, Inline C++ and SDL2 (part 1!)

ibrierley

Ian

Posted on March 20, 2021

Perl animated game. Playing with Perl, Inline C++ and SDL2 (part 1!)

Alt Text

TLDR; We're going to create a (very basic) animated game using Perl, Inline and C++, screenshot of it in action at the top of this page! Github link to code here, part 2 blog here

Be gentle, this is my first ever post!

Firstly, I'm not a C++ programmer. I don't really know SDL2 either. Also my Perl is quite ordinary :D. However I do like exploring, and I'm going to do the odd blog not as a tutorial, but just highlighting my experiences. Some things I'll get wrong or won't understand, but that's fine, it may highlight weak points in myself, documentation, or something else!

Secondly, say hello to a new Camel at the top, drawn by a friend Tommy Cameron for this blog, and will appear in the SDL2 graphics later. More of his stuff here.

With that disclaimer out of the way, there have been quite a few times I've wanted to explore the following....

1) Integrating Perl with other languages, especially with none trivial objects or data.
2) Integrating Perl with system libraries.
3) Integrating Perl with graphics.
4) Play with SDL2 and OpenGL. I can't really tell if Perl SDL is relevant any more.
5) Look at Perl c stuff. All the Perl SV variables and functions feel too difficult to use when in explore mode to amend their code. Whoever named all the SV stuff needs erm, something doing to them :). It's always put me off, so I'm trying easier routes to explore.

So, here we go, lets see if I can achieve some of the above. In this case with C++ and Inline (I want to explore FFI->Platypus to compare at some point as well).

What is SDL ? It's a "Simple" DirectMedia layer that comes with most systems. It's mainly used for 2d graphics manipulation in its own graphics window, but also deals with sounds, keyboard events and things like that. SDL2 adds some new features, like storing textures in graphics card memory, interfacing with OpenGL and a few bits like that.

What is Inline ? Inline is a way of combining another languages code to intermingle with Perl code. You can currently Inline C, C++, Python and some others, as well as have a stab at creating your Inline module for a new language if you want! At a simple level, it parses the code, and looks for structs, method signatures etc, and makes them available to Perl. You need to be wary of complex datatypes though, you will need to convert those yourself in code, we'll do some of that.

So firstly, let's make sure SDL2 is installed. I'm using Linux Mint (and also the Raspberry PI) , so some the initial package install may vary depending on your distro.

sudo apt install libsdl2-2.0-0 libsdl2-dev
sudo apt install libsdl2-image-dev libsdl2-image-2.0-0
Enter fullscreen mode Exit fullscreen mode

If you're not sure what the package names are, try searching with apt or yum, eg

apt search libsdl2  or yum search libsdl2
Enter fullscreen mode Exit fullscreen mode

You will need the library itself and the dev packages for compiling against.

Next, lets install Perls Inline::CPP via CPAN.

cpan install Inline::Struct
cpan install Inline::cpp
Enter fullscreen mode Exit fullscreen mode

Make a cup of tea if you are doing this on the Pi. Or two. Or cut out the middle man and go straight for the Cider.

Still here (and sober)? :)

If you get stuck on any of the Inline bits, it's worth checking out Inline::CPP docs and Inline docs

So, we need to get Inline working with the system SDL library we have just installed. Let's have a go. We'll start with our usual strict, warnings, Dumper, and a highres time module.

use strict;
use warnings;
use Data::Dumper;
use Time::HiRes qw( usleep gettimeofday );
Enter fullscreen mode Exit fullscreen mode

Now we'll get to the fiddly bit, that may vary, but works for me on both systems...

use Inline CPP => config => libs => '-L/usr/local/lib -lSDL2 -lSDL2_image -lSDL-Wl,-rpath=/usr/local/lib';
#use Inline CPP => config => ccflags => '-Wall -c -std=c++11 -I/usr/local/include';
#use Inline CPP => config => inc => '-I/usr/local/include';
#use Inline CPP => config => cc => '/usr/bin/g++';
Enter fullscreen mode Exit fullscreen mode

The first line tells Inline::CPP which libraries we want to use, and where to find them. If you want to add a new library to interface with, add it to this line. You can find the docs in library imports here.
The rest is for the compiler, where it is and what options to compile with. I've left some options commented out, that could be useful to test if you have a problem, but may cause problems for others on a different OS.

IMPORTANT: If you edit some of the CPP config bits above, you may need to delete the Inline cache to reset it. Inline creates some cache files in your current directories ./_Inline folder. You can safely delete this any time you need to reset it. You shouldn't need to bother with this if just amended most normal source code.

So, to include C++ code with our Perl and Inline::CPP, we need to tell Perl where the C++ source code is. We can tell it to look for code after the "DATA" section with

use Inline CPP => 'DATA';
Enter fullscreen mode Exit fullscreen mode

At this point, I'm going to jump straight in and add some! Add this to the end of the code.

__DATA__
__CPP__

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#include <map>

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int NUMBER_OF_IMAGES = 2;

SDL_Renderer *renderer = NULL;
SDL_Window *window;
SDL_Surface* tempSurface;
Enter fullscreen mode Exit fullscreen mode

We're basically including the relevant header files for SDL2 to make the SDL resources available to C++ and a few other useful starting points.

Note, we can now reference SDL objects like SDL_Window and things like that.
There's a really great and useful C++ SDL2 tutorial here. I'd recommend giving it a view, and the first sections of that, will closely match some of the code here, because it may get tricky without having an appreciation of SDL.

I'm now going to create my very own C++ class (I'm new to these!). With this, we can initialise an SDL window to test everything works.

We can create an SDL Window and renderer (an object we can manipulate to display to the window) with

SDL_CreateWindow("SDL2 Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
Enter fullscreen mode Exit fullscreen mode

and I'm now going to include that in a c++ class init function (below DATA again in your code), that we'll call from Perl...

class Sdl {
  private:

  public:

    Sdl() {
        std::cout << "C++ Constructor for Sdl\n";
    }   
    ~Sdl() {
        std::cout << "C++ Destructor for Sdl\n";
    }


    int init() {
        int x = SDL_Init(SDL_INIT_VIDEO);
        if( x == 0 ) {
            window = SDL_CreateWindow("Perl/Inline/C++ Game!!!",
                                       SDL_WINDOWPOS_CENTERED,
                                       SDL_WINDOWPOS_CENTERED,
                                       SCREEN_WIDTH, SCREEN_HEIGHT,
                                       0);
            renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
        } else {
            std::cout << "Had a problem creating a window! " << SDL_GetError();
        }
        if( window == NULL ) {
            std::cout << "Window null";
            exit( 0 );
        }
        if( renderer == NULL ) {
            std::cout << "Renderer null";
            exit( 0 );
        }
        return 0;
  }

};
Enter fullscreen mode Exit fullscreen mode

I'm going to leave some debugging in for you, so you can see what happens later.

NOW, we can access this class from Perl! Any C++ classes are available from Perl that you create. There are no accessor/setter/getter functions automatically, so you need to create those yourself.

Sidenote, if you want to use a c++ struct, you can change one of the Inline statements earlier to include some. This would look like

use Inline CPP => 'DATA', structs => ['State'];
my $state = Inline::Struct::State->new;
my $is_alive = $state->alive();

#in your c++ code...
struct State {
    int alive;
};
typedef struct State State;
Enter fullscreen mode Exit fullscreen mode

This would access a State struct that you declared in your c++. The handy thing here is that any Structs declared, do have getters and setters, eg $state->alive, rather than $state->{ alive } or whatever.

We're not using structs just at the moment (just thought it useful to be aware as an option), so back on to the rest....

At the top of page, after your "use" commands, add the following lines.

my $sdl = Sdl->new;
$sdl->init();
sleep 2; # just so you can see the window open!
Enter fullscreen mode Exit fullscreen mode

Our complete code should look like (include both c++ & Perl sections in one big file for the moment)...

Perl

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use Time::HiRes qw( usleep gettimeofday );

use Inline CPP => config => libs => '-L/usr/local/lib -lSDL2 -lSDL2_image -lSDL-Wl,-rpath=/usr/local/lib';

use Inline CPP => 'DATA';

my $sdl = Sdl->new;
$sdl->init();
sleep 2;


__DATA__
__CPP__
Enter fullscreen mode Exit fullscreen mode

c++

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#include <map>

const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int NUMBER_OF_IMAGES = 2;

SDL_Renderer *renderer = NULL;
SDL_Window *window;
SDL_Surface* tempSurface;

class Sdl {
  private:
  public:

    Sdl() {
        std::cout << "C++ Constructor for Sdl\n";
    }   
    ~Sdl() {
        std::cout << "C++ Destructor for Sdl\n";
    }

    int init() {
        int x = SDL_Init(SDL_INIT_VIDEO);
        if( x == 0 ) {
            window = SDL_CreateWindow("Perl/Inline/C++ Game!!!",
                                       SDL_WINDOWPOS_CENTERED,
                                       SDL_WINDOWPOS_CENTERED,
                                       SCREEN_WIDTH, SCREEN_HEIGHT,
                                       0);
            renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
        } else {
            std::cout << "Had a problem creating a window! " << SDL_GetError();
        }
        if( window == NULL ) {
            std::cout << "Window null";
            exit( 0 );
        }
        if( renderer == NULL ) {
            std::cout << "Renderer null";
            exit( 0 );
        }
        return 0;
  }

};
Enter fullscreen mode Exit fullscreen mode

If you run it, Perl should access the Sdl class with Sdl->new and $sdl->init(), which will open a new (empty) window and wait for a few seconds before quitting.

So that's the end of part 1! Part 2 we'll get some images displaying and moving.

It's only getting a window up, but it's the nuts and bolts of getting Inline, a class working, and SDL. I'll try and flesh it out with more Perl code, to add a game loop and some things moving about!

Note, we can probably remove the Inline dependency once we've finished, as there's a converter to XS.

Conclusions so far, are that it's relatively easy to incorporate. Memory cleanup seems to behave so far (c++ objects seem to get cleared up, but bad code in c++ will probably behave worse than Perl), and it's avoided so far any evil SV type bits (although there may be a small bit of that in part2!).

Have fun everyone, and thanks for reading!

Part 2 is now here

(I can be reached on FB or ians.coding at gmail.com)

(I'm Linux only, so if there are any Windows/Mac users, who can try the SDL lib section at the start and let me know any modifications to work on Windows/Mac I'd be happy!)

💖 💪 🙅 🚩
ibrierley
Ian

Posted on March 20, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related