OcEngine
Go back
OcEngine is a custom game engine made in my 2nd year at the Breda University of Applied Science. We made this engine with a team of 7 people. My role in this project was as engine programmer.

My main contributions to this project were the core engine systems, Such as the Entity/world system, The tool GUI system, level loading, the gameplay framework and setting up the Bullet Physics engine and connecting it to our entity/world system.

Code samples below
OcEngine

This is the simple Entity/World system I wrote for the engine. It's a very simple Object-oriented inheritance based entity system where entities can inherit from a base class so that they can be added to a world, which is a container of entities.

Some code such as forward declarations, includes etc are removed for readabily on the website.

HEADER:
						
class World {
	public:
		World();
		~World();

		void Load(const std::string& worldName);

		void Unload();


		void Update(float deltaTime);

		void OnGUI();

		typename std::enable_if<std::is_base_of<Entity, T>::value, HEntity>::type
			AddEntity(
			const glm::vec3& position = glm::vec3(0.0f, 0.0f, 0.0f),
			const glm::quat& rotation = glm::quat (1.0f, 0.0f, 0.0f, 0.0f),
			const glm::vec3& scale = glm::vec3(1.0f, 1.0f, 1.0f)
		);

		void DestroyEntity(HEntity handle);

		// @Note: unordered map lookup.
		Entity& GetEntity(HEntity handle);

		size_t NumberOfEntities();

		void DestroyAllEntitys();

		glm::vec3 GetCarSpawnPosition(int32_t index);

		bool IsUnloaded();
	private:
		void LoadNewWorld();

		bool m_loadNewWorld = false;
		std::string m_newWorldToLoad{};

		int32_t m_nextEntityUid = 0;
		std::unordered_map m_entities = {};

		std::vector m_carSpawnPositions;
	};

	template
	typename std::enable_if::value, HEntity>::type
	World::AddEntity(
		const glm::vec3& position,
		const glm::quat& rotation,
		const glm::vec3& scale)
	{
		T* t = new T(*this, typeid(T).name(), position, rotation, scale);
		t->m_flags |= (1 << 0);
		t->m_uid = m_nextEntityUid++;
		
		m_entities.emplace(t->m_uid, t);

		return { t->m_uid };
	}
							
CPP:
					
	World::World() 
	{
		// @Hack, the racetrack we use has only 4 grid slots,
		// we then index into this vector with the gridslot value from blender to set the position.
		m_carSpawnPositions.reserve(4);
		m_carSpawnPositions.resize(4);
	}

	World::~World()
	{
		for (auto& it : m_entities)
		{
			it.second->OnDestroy();
		}

		for (auto& it : m_entities)
		{
			delete(it.second);
			it.second = nullptr;
		}

		m_entities.clear();
	}

	void World::Load(const std::string& worldName) {
		m_loadNewWorld = true;
		m_newWorldToLoad = worldName;
	}

	void World::Unload() {
		for (const auto& it : m_entities) {
			it.second->m_name += "(DELETED)";
			it.second->m_flags |= ENTITY_FLAG_DESTROYED;
		}
		m_racetrackSpline = SplineLoader();
	}

	void World::Update(float deltaTime) 
	{
		if (m_loadNewWorld) {
			this->LoadNewWorld();
			m_loadNewWorld = false;
		}

		if (m_transitionState)
		{
			m_transitionState = false;
			Application::GetStateMachine().ChangeState(m_newState);
			return;
		}

		for (const auto& it : m_entities) {
			auto entity = it.second;
			if (entity->m_flags & ENTITY_FLAG_NEW) {
				entity->OnCreate();
				entity->m_flags &= ~ENTITY_FLAG_NEW;
			}
		}

		for (auto& it : m_entities) {
			it.second->Update(deltaTime);
			it.second->UpdateComponents(deltaTime);
		}

		std::vector destroyedEntitiesIDs{};
		for (auto& it : m_entities) {
			if (it.second->m_flags & ENTITY_FLAG_DESTROYED) {
				it.second->RemoveAllComponents();
				it.second->OnDestroy();
				destroyedEntitiesIDs.emplace_back(it.first);
			}
		}

		if (destroyedEntitiesIDs.size() > 0) {
			for (size_t i = 0; i < destroyedEntitiesIDs.size(); i++) {
				uint32_t uid = destroyedEntitiesIDs[i];
				delete (m_entities.at(uid));
				m_entities.erase(uid);
			}
		}
	}

	void World::OnGUI()
	{
		// Some DearImGui code omitted for readabily.
	}

	void World::DestroyEntity(HEntity entity) {
		GetEntity(entity).m_flags |= ENTITY_FLAG_DESTROYED;
	}

	Entity& World::GetEntity(HEntity handle) {
		auto it = m_entities.find(handle.uid);

		OCASSERT(it != m_entities.end());

		return *it->second;
	}

	size_t World::NumberOfEntities() {
		return m_entities.size();
	}

	void World::DestroyAllEntitys()
	{
		for (auto i = 0; i < m_entities.size(); i++)
		{
			m_entities[i]->m_flags |= ENTITY_FLAG_DESTROYED;

		}
	}
	glm::vec3 World::GetCarSpawnPosition(int32_t index) 
	{
		assert(index >= 0 && index <= m_carSpawnPositions.size());

		return m_carSpawnPositions[index];
	}

	bool World::IsUnloaded() {
		return m_loadNewWorld;
	}
						
The Entity class:
						
	enum {
		ENTITY_FLAG_NEW = (1 << 0),
		ENTITY_FLAG_DESTROYED = (1 << 1)
	};

	struct HEntity { uint32_t uid = 0; };

	class Entity {
	public:
		// Initialize an entity.
		// World: the world that this entity is a part of,
		// Position, rotation and scale: the initial position, rotation and scale of this entity.
		// Name: the name this entity will have.
		Entity(
			World& world,
			const std::string& name,
			glm::vec3 position,
			glm::quat rotation,
			glm::vec3 scale)
			: 
			m_world(world), m_name(name), m_position(position),
			m_rotation(rotation), m_scale(scale) { }

		virtual ~Entity() { }

		template
		T* AttachComponent();

		void UpdateComponents(float deltaTime);

		void InspectComponents();

		// Gets called at the beginning of the frame when this entity is constructed.
		virtual void OnCreate() { };

		// Gets called at the end of the frame when this entity is destroyed.
		virtual void OnDestroy() { };

		// Gets called every single frame.
		// deltaTime: the time in between frames.
		virtual void Update(float deltaTime) {};

		// Gets called when this entity is selected in the debug menu.
		virtual void OnGUI() { };

		// Gets called on the frame when a physics object that is-
		// attached to this entity collides with another physics object.
		// Collider: a handle to the colliding entity.
		virtual void OnCollision(HEntity collider) {}

		// Gets called on the frame when a physics object that is-
		// attached to this entity no longer collides with another physics object.
		// Collider: a handle to the entity that is no longer being collided with.
		virtual void OnSeparation(HEntity collider) {}
		
		void RemoveAllComponents();

		std::string m_name = "Unknown Entity";
		int32_t m_flags = 0;
		uint32_t m_uid = 0;

		glm::vec3 m_position = glm::vec3(0.0f, 0.0f, 0.0f);
		glm::vec3 m_scale = glm::vec3(1.0f, 1.0f, 1.0f);
		glm::quat m_rotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f);

	protected:
		std::vector m_components;
		World& m_world;
	};

	template
	T* Entity::AttachComponent() {
		T* t = new T(*this);
		t->m_name = typeid(t).name();
		m_components.push_back(t);

		return t;
	}