Now that my first year of CMGT (Or ICAD) here at Buas is finished, I would like to go over each block and talk about what I’ve done.

You can also find all of the code here: https://github.com/thatalloguy/Bomberman

Overview (of the block)

I would describe block A as a block where you find your footing, see how you handle the study. In my opinion it’s best to focus on actually being productive and passing the block, rather than doing something crazy.

Now the basic premise of the block is: Create a simple arcade game with the provided template. Besides that, we also got some “homework” every friday, which consisted of:

  • Simple number guessing game (with the use of std*) *i think.
  • That same guessing game but with stars that move down when guessing wrong or explode when the number is guessed.
  • Simple breakout game.
  • Bombjack (spread out over multiple fridays)

Important to note is that these were required for us to do.


The Template

The template we used was Jacco’s Tmpl8, which has all of that Jacco’s charm. Some people adore it saying that its “misunderstood”, others dislike it. Personally I think it’s fine for what it tries to do. It also has a very specific code style, which I think teaches you on how to adopt to more non-conventional code styles.

It has also has some deliberate limitations and flaws, made for you to overcome. The template basically provides you with a simple math library (which I liked :) ) and the ability to render sprites. The template also does its rendering on the CPU, writing the pixels directly into a texture, all stored on the CPU.


Starting

Starting off, I first analyzed the template, drawing the conclusions I wrote above. My intake assignment had a pretty solid, simple 2D opengl renderer. So I asked wether or not I was allowed to modify the template. My teacher told me that, while I can modify it, I cannot replace it entirely.

My first couple of weeks were spent on getting the foundation up and running. Stuff like player(s), map rendering and physics. (With physics being quite a pain).

beginning

Architecture

codebase

My final codebase

Asset Manager

Since I’d be needing to load different assets and having multiple references to them, I decided to write a simple form of an AssetManager.

assetManager.h
 1#pragma once
 2
 3#ifndef TEXTURE_AMOUNT
 4#define TEXTURE_AMOUNT 4
 5#endif
 6
 7
 8#ifndef SHADER_AMOUNT
 9#define SHADER_AMOUNT 6
10#endif
11
12namespace bomberman
13{
14	struct ShaderConfig
15	{
16		const char* vertPath;
17		const char* fragPath;
18	};
19
20	enum class TextureType
21	{
22		TILES	 = 0,
23		ARROW	 = 1,
24		MENU	 = 2,
25		TEXT	 = 3,
26	};
27
28	enum class ShaderType
29	{
30		DEFAULT		= 0,
31		ANIMATED	= 1,
32		DEBUG		= 2,
33		DEPTH		= 3,
34		SCREEN		= 4,
35		TILE		= 5
36	};
37
38
39	static constexpr const char* texturePaths[4] = {
40		"assets/tiles.png",
41		"assets/arrow.png",
42		"assets/mainscreen.png",
43		"assets/text.png"
44	};
45
46	static constexpr ShaderConfig shaderPaths[6] = {
47		ShaderConfig{"assets/shaders/default.vert", "assets/shaders/default.frag"},
48		ShaderConfig{"assets/shaders/animated.vert", "assets/shaders/animated.frag"},
49		ShaderConfig{"assets/shaders/debug.vert", "assets/shaders/debug.frag"},
50		ShaderConfig{"assets/shaders/default.vert", "assets/shaders/depth.frag"},
51		ShaderConfig{"assets/shaders/screen.vert", "assets/shaders/screen.frag"},
52		ShaderConfig{"assets/shaders/tile.vert", "assets/shaders/tile.frag"},
53	};
54
55
56	namespace Assets
57	{
58		bool LoadAssets();
59		void UnloadAssets();
60
61		GLTexture* GetTexture(TextureType type);
62		Shader* GetShader(ShaderType type);
63	}
64;
65}

First thing I like to point out is: All of the asset path and types are hardcoded. Now of course you could define these externally, but lets be honest, thats just overengineering for a project of this scope. Many programmers that come to the study who are already quite familiar with C++ fall into the overengineering trap. Including myself.

You will realize that at this project scale that if you have a super cool engineered system, nobody gives a shit and 9/10 times you don’t need it. I will play devils advocate and say that IF you’re very passionate about a specific system or aspect and you want too dive deeper into it, go ahead! As long as it doesn’t come at the cost of the project its all fair game!

Last thing I want to point out is my usage of namespaces. I use a namespace instead of a class, because all of the functions would be static anyways. You could say to implement a proper singleton pattern, but I find this implementation cleaner and it works fine for this scenario.

Audio

Nothing worthy to note, just an incredibly simple layer above the MiniAudio library.

Entities

For this project, an entity is anything you see on screen. Previously I’ve discussed my dislike at the time for many different Entity models, including the “OOP way”. For this project, the teachers pushed OOP to the students. It even was required in the ILO’s to display understanding and great usage of OOP. Honestly even without the push from the teachers, I would still argue that OOP is best for this project.

c++
 1class Entity
 2{
 3public:
 4	//Note We the entity do NOT manage the memory of the texture.
 5	Entity() = default;
 6	virtual ~Entity() = default;
 7
 8	virtual void Initialize() = 0;
 9	virtual void PrepareRender() = 0;
10	virtual void Tick(float deltaTime) = 0;
11	virtual void Destroy() = 0;
12
13	float2 GetPosition() const { return position;  }
14	float2 GetScale() const { return scale; }
15
16	void SetPosition(const float2& newPosition) { position = newPosition;  }
17	void SetScale(const float2& newScale) { scale = newScale;  }
18
19	GLTexture* GetTexture() const { return texture; }
20	Shader* GetShader() const { return shader;  }
21
22protected:
23	float2 position{ 0, 0 };
24	float2 scale{ 0, 0 };
25
26	uint id{ 0 }; // NOTE: Unused
27	// When the entity gets initialized it itself is responsible for retrieving its desired shader and texture from the Renderer.
28	GLTexture* texture{nullptr};
29	Shader* shader{nullptr};
30	
31};

This is the base entity class on which every other entity is based on, with a couple of things to note. First, there is a PrepareRender function, here the entity can upload anything it needs to its shader. Secondly notice my usage of raw pointers, a bad habit that stuck until Block C.

Only a couple of entities inherit directly from the Entity class. Many inheriting from the AnimatedEntity class, which can (you guessed it!) be animated! codebase

Rendering

One of the more special things about my project is the way I render my scene, since im not using the built in template renderer.

The first thing I do when rendering the scene (after clearing the screen) is rendering all of the static tiles. Static tiles are the ones that never change. I can do this all in 1 drawcall because I have all of the static tiles in 1 batch.

rendering1

Afterwards I draw all of the dynamic objects in the scene, starting with every breakable tile. Important to note: I never did batching for dynamic entities so every entity (including the breakable tiles) is 1 drawcall. I never did this because time :( and other things took priority, also my game was fast enough but still.

rendering2

Then I draw the rest of the entities, which are the player(s), enemies, power-ups and (sometimes) bonuses.

rendering3

All of the rendering previously isn’t actually done to actual screen, instead its done to a framebuffer (texture you can render to). This is because if the game is played with 2 players, we need to render the scene multiple times.

So finally I render all of the player perspectives to the actual screen and draw the UI on-top of that.

rendering4

Gameplay (programming)

One of my biggest weaknesses: writing clean gameplay code. Honestly I think I did okay for this project (except the collision code, that was horrible). I think the reason why it wasn’t a complete mess is because I stook to OOP closely. For example I have a base power-up class, which handles everything from collision to rendering, etc. And then every different type of power-up inherits from that class, same goes for the enemies and bonuses.

It was also quite nice that the grading was made in a way that you could easily scale up. In the beginning I chose intermediate, not wanting to bite more off than I could chew. However I actually ended up going for hard, implementing every enemy, bonus and power-up, because it was so easy to do.


Reflection (the boring kind)

At the end of every block, we reflect on what we did, how we felt and what we can do to improve in the future.

Looking back at it, I write about being happy on actually finishing the project, making a finished game. Also being happy it being actually stable and running.

For the difficult parts of the block I write about some bugs I struggled with. Also mentioning my dislike towards the LevelManager and Game class, noting them as messy.

For improvement I write about breaking my tasks into smaller tasks (Something I never ended up doing). Also noting on trying to avoid techdebt because of laziness.




Anyways, tune in next time when I cover the hell that was block B in part 2!