Shapegame is a simple and minimal 2D game engine for drawing shapes in C++ with a focus on ease of use and clarity.
Want to quickly draw some shapes without learning a heavy-weight game engine like Unreal or Unity? This aims to be that; minimal shape drawing in C++.
- Download The Shapegame Library zip from here.
- Unzip the files and put them in a directory where you want to work.
- On Mac and Linux install dependency GLFW on your system (it is included on Windows).
- Create a test file called
main.cpp
with the following contents:
#include "shapegame.hpp"
using namespace shapegame;
int main() {
Game game(400, 400, "My New Game");
game.scene->setBackgroundColor(Color::BLUE);
game.scene->addChild(new TriangleIsosceles(100, 100, Position(100, 100), Color::BLACK));
game.run();
}
- Compile it and link with glfw and Shapegame. I was able to compile with the following commands:
- MacOS:
g++ -std=c++17 -o main main.cpp -I./shapegame/include shapegame/platform/mac/shapegame.a -lglfw -framework Cocoa -framework IOKit -framework CoreVideo -framework OpenGL
- Windows On windows utilize the pre configured Visual Studio solution located here https://github.com/hasahmed/shapegame-example-shmup.git You should be able to simply open the solution in Visual Studio and run it as long as you have to proper C++ development components installed (Game development with C++). It comes preloaded with an example game, so you don't need to copy the above code.
- Linux:
g++ -std=c++17 -o main main.cpp `pkg-config --cflags glfw3` -I./shapegame/deps -I./shapegame/include shapegame/platform/linux/shapegame.a `pkg-config --libs glfw3`
- Run it from the command line with
./main
(./main.exe
in MSYS2 on windows)
If you run into issues feel free to open an issue on github or send me an email
You can check out the example SHMUP game here https://github.com/hasahmed/shapegame-example The compilation instructions are the same as you'll find in the Quickstart
The Engine effectivly consists of Shapes, MultiShapes and the Scene.
A Shape
is anything that can be drawn. A MultiShape
is simple a class that glues multiple Shapes
together.
MultiShape
act exactly like Shapes
other than that aspect. For that reason from here on out they will both simply
be refered to as Shapes
. When something applies to only one or the other that will be stated explicitly.
Shapes will only be drawn if they are in the Scene. Shapes and Objects are added to the Scene
via the Scene::addChild method. Once a Shape or Object is added to the scene it can recieve a number of callbacks.
When these callbacks are called depend on what callback they are, but the most straightforward one to talk about is
the Object::update callback. If your class overrides this method, then it will be called every frame. An
example of how you might use that is a Triangle that moves down 10 pixles every frame.
#include "shapegame.hpp"
using namespace shapegame;
class MovingTriangle : public TriangleIsosceles {
public:
MovingTriangle(): TriangleIsosceles() {
this->setPosition(100, 10);
}
void update() override {
this->translate(0, 10); // called every frame
}
};
int main() {
Game g;
g.scene->addChild(new MovingTriangle());
g.run();
}
A list of every overrideable callback and their documentation can be found here
- Windows
- Mac*
- Linux
*Apple nerfed to macOS opengl drivers presumably to push the adoption of metal so your graphics hardware may have to be better than intel integrated graphics 4000 for a pleasent experience.
This is a list of every class in the engine that is note worthy for users of the engine. I.e. this isn't documentation for people who want to modify engine code. You'll have to read the code for that! If you find that the following documentation is lacking, please open a github issue about it, or better yet a pull request!
This is the base class for every object in the game. It contains common functionality, and the virtual methods to be overriden by YOUR shapes
- void kill()
- Object* getParent()
- void translate(float x, float y);
- virtual void setPosition(float x, float y);
- virtual void setPosition(Point pos);
- virtual void update()
- virtual void onAdd()
- virtual void onRemove()
- virtual void onKill()
- virtual void onKeyPress(Kb::Key key, Input::Action action);
- virtual void onMouseClick(Mouse::Btn btn, Input::Action action);
- virtual void update()
- virtual void onAdd()
- virtual void onRemove()
- virtual void onKill()
- virtual void onKeyPress(Kb::Key key, Input::Action action);
- virtual void onMouseClick(Mouse::Btn btn, Input::Action action);
Constructs a default Object with a position of 0, 0
Constructs an Object with a position with whatever passed in x and y
Constructs an Object with a position of whatever passed in Point
The position of the object. This determines where the object is located on the x, y axes. For the most part this doesn't matter for the Object class. It starts to matter when dealing with things that are actually going to be drawn on the screen i.e. Shape and MultiShape
Calling this method will queue and object to be removed from the Scene when it is safe to do so.
if overridden this method will be called every frame
if overridden this method will be called right after the object is added to the Scene
if overridden this method will be called right after the object is added to the Scene
if overridden this method will be called right after the object is removed from the Scene
if overridden this method will be called when a key is pressed. The Key will be passed in along with the action.
if overridden this method will be called the mouse is clicked. The mouse button that was clicked will be passed to the method along with the action
This will return a pointer to the Objects parent. An Object will only have a parent if it is a child of a MulitShape. Otherwise it will simply return null.
Sets the pos property of the object to x and y.
Sets the pos property of the object to pos.
Moves the object from its current position x, and y pixels on their respective axes.
Shape Extends Entity
Shape class won't actually ever be used directly by the user of this library. It is mostly a wrapper around the TRUE base class for every drawable object in this engine. The Triangle
The color property of the shape. Determines what color it is a drawn as on the screen. Is of type Color
Triangle Extends Shape
Everything in computer graphics is all about triangles. This engine is no different. This is the shape that makes up all other shapes by combining them in MultiShapes
- Triangle(Position first, Point second, Point third);
- Triangle(Position first, Point second, Point third, Color color);
It should be noted that there is no property in triangle called first
to accompany second
and third
because
the pos property of Object is used instead.
Constructs a Triangle object whose points are at the locations specified by the first, second, and third arguments. It should be noted that triangles points are placed onto the screen in a clockwise manner.
Same as constructor above, but accepts a Color argument to set the color of the triangle.
This property of the triangle specifies where the 2nd point of the triangle will be placed on the x, y axes. It should be noted that triangles points are placed onto the screen in a clockwise manner. It should also be noted that the Object::pos property represents the first point of the triangle.
Same as Triangle::second except the 3rd point to be placed.
MultiShape's are just like regular shapes, except they allow you to combine multiple Objects together in a nice package via the addShape. As stated in the Triangle docs, this is the class that is used to make every shape that ships with the engine other than Triangle. In reality MultiShape probably isn't the best name because regular Objects can be added to MultiShapes as well.
- MultiShape(Position pos);
- bool removeShape(Object* obj);
- void addShape(Object* shape);
- void addShape(std::unique_ptr<Object> shape);
- std::vector<Object*>& getShapes();
MultiShape::MultiShape(Position pos);
Constructs a MultiShape at the given position pos.
MultiShape::removeShape(Object obj);
This removes an object specified by a pointer to that object from the MultiShapes children. Note This method is named poorly. Despite being named as though it only operates on shapes, it operates on Objects and any class derived from that.
void MultiShape::addShape(Object *obj);
Adds a child shape to this multishape, and takes over the memory of the object passed in. The MultiShape is now responsible for whatever memory is given to it. It creates a smart pointer out of it, and that is freed when the MultiShape is.
void MultiShape::addShape(std::unique_ptr<Object> obj)
Adds a child shape to this multishape.
std::vector<Object*>& MultiShape::getShapes()
Returns a vector of pointers to the shapes that are currently a child of the MultiShape
The scene is the container for every object that you want to be a part of your game. When an Object is in the the scene, all of its overloaded callbacks are called at the appropriate times. The scene also owns the memory of all of its containing objects. It handles freeing them when the program ends, as well as when they are killed.
- Object* addChild(Object *obj);
- Object* addChild(std::unique_ptr<Object> obj);
- template T* addChildAs(T uniqueShape){
- void setBackgroundColor(Color& color);
Adds a obj to the scene, as well as takes control of the memory of the obj. A pointer to the object is returned. Once added to the scene the obj has all of its overriden callbacks called at the appropriate times.
Same as addChild above, but excepts a smart pointer. Still returns a raw pointer to the object.
template T* addChildAs(T uniqueShape){
Same as addChild except also has a template argument for what the returned pointer should be cast to. This is convenient for being able to construct and add an object to the scene and assigne it to a variable in the same line. Returns a raw pointer to the object cast to type T. E.g.
#include <iostream>
#include "shapegame.hpp"
using namespace shapegame;
class Car : public Triangle {
public:
Car(): Triangle() {}
void honk(){
std::cout << "BEEP BEEP" << std::endl;
}
};
int main() {
Game game;
auto car = game.scene->addChildAs<Car>(std::make_unique<Car>());
car->honk();
game.run();
}
void setBackgroundColor(Color& color)
Sets the background color of the scene to the value of color.
Entity Extends Object
This class is the 'Entity' part of the Entity Component System. Shapes extend from this class, so its likely that most of the objects you deal with as a user of this engine will be Entities that you can add Components to.
- void addComponent(Component *compo);
- void addComponent(std::unique_ptr<Component> compo);
void addComponent(Component *compo)
Creates a new smart pointer out of compo and adds it to the Entity.
void addComponent(std::unique_ptr<Component> compo)
Adds compo to the entity.
This is the 'Component' part of the engines 'Entity Component System'. This is used to add functionality to objects without the use of inheritance. As of right now the only method that can be utilized is the update method, but in the future there could likely be support of all of the callbacks.
virtual void update(Entity *ent)
This method will be called every frame with the Entity that it is attached to being passed in as the ent argument.
Color class is used to reprsent colors in this engine. The colors are reprsented using RGBA where RGBA can have a value between 0 and 1. E.g. red would be r = 1, g = 0, b = 0, a = 1.
- Color()
- Color(float red, float green, float blue)
- Color(float red, float green, float blue, float alpha)
- void set(float red, float green, float blue);
- void set(float red, float green, float blue, float alpha);
- void set(Color& c);
- float* getRawColor();
Constructs a default color with rgba = 1 (black).
Constructs a color with corrisponding rgb and alpha of 1.
Constructs a color with corrisponding rgba
The red property of the color. Can be a value between 0 and 1, where 1 is the most red it can be and 0 is the least.
The green property of the color. Can be a value between 0 and 1, where 1 is the most green it can be and 0 is the least.
The blue property of the color. Can be a value between 0 and 1, where 1 is the most blue it can be and 0 is the least.
The alpha (opacity) property of the color. Can be a value between 0 and 1, where 1 is the most opaque it can be and 0 is the least.
Sets the rgb values of the color and assigns alpha (a) a value of 1
Sets the rgba values of the color.
Color::set(Color &color)
Sets the rgba values of the object to that of color.
Returns a pointer to the underlying rgba values as an array of length 4.
Position Extends Point
Position represents the position of an object on the x, y axes.
Position(); Position(float x, float y); Position(Point pos);
Constructs a default Position with an x, y of 0, 0.
Constructs a Position with given x, y.
Position::Position(Point pos)
Constructs a Position with an x and y that are the same as pos.
Constains an x, y value in order to represent a point on the screen graph.
The x property of the Point
The y property of the Point
returns the x value.
returns the y value.
sets the x value.
sets the y value.
Namespace wherein all of the other input stuff is contained.
static bool down(Key key)
returns true if key is down. False otherwise.
static bool up(Key key)
returns true if key is up. False otherwise.
The following are members of the enum class Kb::Key:
- SPACE
- APOSTROPHE
- COMMA
- MINUS
- PERIOD
- SLASH
- N0
- N1
- N2
- N3
- N4
- N5
- N6
- N7
- N8
- N9
- SEMICOLON
- EQUAL
- A
- B
- C
- D
- E
- F
- G
- H
- I
- J
- K
- L
- M
- N
- O
- P
- Q
- R
- S
- T
- U
- V
- W
- X
- Y
- Z
- LEFT_BRACKET
- BACKSLASH
- RIGHT_BRACKET
- GRAVE_ACCENT
- WORLD_1
- WORLD_2
- ESCAPE
- ENTER
- TAB
- BACKSPACE
- INSERT
- DELETE
- RIGHT
- LEFT
- DOWN
- UP
- PAGE_UP
- PAGE_DOWN
- HOME
- END
- CAPS_LOCK
- SCROLL_LOCK
- NUM_LOCK
- PRINT_SCREEN
- PAUSE
- F1
- F2
- F3
- F4
- F5
- F6
- F7
- F8
- F9
- F10
- F11
- F12
- F13
- F14
- F15
- F16
- F17
- F18
- F19
- F20
- F21
- F22
- F23
- F24
- F25
- KP_0
- KP_1
- KP_2
- KP_3
- KP_4
- KP_5
- KP_6
- KP_7
- KP_8
- KP_9
- KP_DECIMAL
- KP_DIVIDE
- KP_MULTIPLY
- KP_SUBTRACT
- KP_ADD
- KP_ENTER
- KP_EQUAL
- LEFT_SHIFT
- LEFT_CONTROL
- LEFT_ALT
- LEFT_SUPER
- RIGHT_SHIFT
- RIGHT_CONTROL
- RIGHT_ALT
- RIGHT_SUPER
- MENU
This will walk you through setting up your environment for engine development on windows, mac, linux. If you already have your environment set up for general C++ development, then this shouldn't bee too bad.
- Download Visual Studio 2019 (any edition)
- Install Game Development With C++ components
- Install latest Python NOTE The build step requires python 3 and it expects it to be called python. You should be able to type python into cmd.exe and have it run python3
- Open this repos .sln file in visual studio.
This will allow you to make changes to the static library. In order to actually run it you will need to set up a project that depends on the static library, or use the example project linked above, and simply copy over the changes after they are made (I know I know, this process could use some automation)
Assuming your dev environment is already set up for C++ development:
- Type
brew install glfw
- Type
make
- Run with `./bin/main.test
Assuming your dev environment is already set up for C++ development:
- Type
sudo apt-get install glfw3
- Type
make
- Run with `./bin/main.test
FAQ is probably misleading because none of these questions have actually been asked of me, they are just questions that would probably come to my mind when reading about a game engine.
This Engine is targeted primarily towards people who want to learn C++, and would like to do so graphically, but also don't want to learn a giant game engine.
You probably shouldn't. This engine is a toy intended for drawing shapes easily in C++. If you want to make a real game I would personally recommend Godot. If you just want to draw some shapes using C++ and aren't worried about performance, or shipping a game then this might be for you.
Why write a game engine? Good question. There isn't a good reason. There are about a million game engines and they are all probably better and faster than this one and a few are open source. But I wrote this game engine as a learning experience for myself. I wanted to learn C++ and OpenGL. To that end I consider it a success. I am by no means a master of either, but I know more than when I started.