I've done it again .. uggh

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
MrGodin
Posts: 721
Joined: November 30th, 2013, 7:40 pm
Location: Merville, British Columbia Canada

I've done it again .. uggh

Post by MrGodin » November 28th, 2017, 1:43 am

Well it seems I am never quite satisfied with anything i make and went ahead and changed my game framework to be component based.

Code: Select all

#include "includes.h"
#include <bitset>
#include <utility>
class Component;
class Entity;
class EntityManager;
using ComponentID = std::size_t;
using GroupID = std::size_t;
inline ComponentID getComponentTypeID()
{
	static ComponentID cID = 0;
	return cID++;
}
template <typename T>
inline ComponentID getComponentTypeID() noexcept
{
	static ComponentID id = getComponentTypeID();
	return id;
}

constexpr static  ComponentID maxComponents = 32;
constexpr static GroupID maxGroups = 32;
using ComponentBitSet = std::bitset<maxComponents>;
using ComponentArray = std::array<Component*, maxComponents>;
using GroupBitSet = std::bitset<maxGroups>;

enum GroupLabels : std::size_t
{
	groupMap,
	groupPlayers,
	groupEnemies,
	groupCollider
};


class Component
{
public:
	Entity* owner;
	virtual void Update(const float& dt) {}
	virtual void Init() {};
	virtual void Draw() {};
};

class Entity
{
private:
	ComponentBitSet m_componentBitset;
	ComponentArray m_componentArray;
	GroupBitSet m_groupBitSet;
	std::vector<std::unique_ptr<Component>> m_components;
	bool m_active = true;
	EntityManager& manager;
public:
	
	Entity(EntityManager& manager):manager(manager)
	{};
	virtual ~Entity() {}
	virtual void Draw()
	{
		for (auto& comp : m_components)
			comp->Draw();
	};
	virtual void Update(const float& dt)
	{
		for (auto& comp : m_components)
			comp->Update(dt);
	};
	bool Active()const { return m_active; };
	void Destroy() { m_active = false; }
	template <typename Type>
	bool HasComponent()const
	{
		return m_componentBitset[getComponentID<Type>()];
	}
	template <typename Type, typename... TArgs>
	Type& AddComponent(TArgs&&... mArgs)
	{
		Type* comp(new Type(std::forward<TArgs>(mArgs)...));
		std::unique_ptr<Component> uPtr{ comp };
		m_components.emplace_back(std::move(uPtr));
		m_componentArray[getComponentTypeID<Type>()] = comp;
		m_componentBitset[getComponentTypeID<Type>()] = true;
		comp->owner = this;
		comp->Init();
		
		return *comp;
	}
	template<typename Type>
	Type& GetComponent()const
	{
		auto ptr(m_componentArray[getComponentTypeID<Type>()]);
		return *static_cast<Type*>(ptr);
	}

	bool HasGroup(const GroupID& id)const
	{
		return m_groupBitSet[id];
	}
	void AddGroup(const GroupID& id);
	
	void RemoveGroup(const GroupID& id)
	{
		m_groupBitSet[id] = false;
	}
};
implemented like so

Code: Select all

void Game::InitPlayer()
{
	// create entity
	m_player = &m_entityMgr->Add();
	// add components
	// transform
	TransformComponent* comp = &m_player->AddComponent<TransformComponent>(Vec2f({ 100.0f,100.0f }), Vec2f({ 0.0f,0.0f }), Vec2f({ 24.0f,32.0f }));
	comp->acceleration = 56.0f;
	comp->Scale(Vec2f(2.0f, 2.0f));
	// sprite
	TextureManager::ImageClip clip = m_textureHandler->GetClip("Colin", 1);
	SpriteComponent* sprite = &m_player->AddComponent<SpriteComponent>(*clip.bitmap, clip.rect);
	// animate the sprite
	std::size_t myints[] = { 1,0,1,2 };
	std::vector<std::size_t> indices(myints, myints + sizeof(myints) / sizeof(std::size_t));
	sprite->AddAnimationSequence("walk_left", AnimationSeq(indices, 3, 300));
	sprite->AddAnimationSequence("walk_down", AnimationSeq(indices, 2, 300));
	sprite->AddAnimationSequence("walk_right", AnimationSeq(indices, 1, 300));
	sprite->AddAnimationSequence("walk_up", AnimationSeq(indices, 0, 300));
	// set to current sequnce
	sprite->SetCurrentAnimationSeq("walk_left");
	// add a keyboard component
	m_player->AddComponent<KeyboardController>(*m_inputMgr.get());
	// add attributes
	m_player->AddComponent<DefenceComponent>();
	m_player->AddComponent<AttackComponent>();
	m_player->AddComponent<ExperienceComponent>();
	m_player->AddComponent<VitalityComponent>();
	m_player->AddComponent<MagicComponent>();
	m_player->AddGroup(groupPlayers);

	
}
Component example

Code: Select all

#include "Components.h"
#include "Utils2D_v2.h"
class TransformComponent : public Component
{
	
	D2D1_MATRIX_3X2_F matTrans = D2D1::Matrix3x2F::Identity();
	D2D1_MATRIX_3X2_F matScale = D2D1::Matrix3x2F::Identity();
	D2D1_MATRIX_3X2_F matRotation = D2D1::Matrix3x2F::Identity();
	Vec2f center = { 0.0f,0.0f };
	Vec2f size = { 0.0f,0.0f };
public:
	TransformComponent() = default;
	TransformComponent(Vec2f& p, Vec2f& v, Vec2f& s)
		:position(p), velocity(v),size(s)
	{}
	virtual void Update(const float& dt)override
	{
		position += velocity * acceleration * dt;
		Resize(size);
	}
	virtual void Init()override 
	{
		center = Vec2f(position + (size * 0.5f));
	};
	Vec2f Center()const
	{
		return center;
	}
	Vec2f Dimensions()const
	{
		return size;
	}
	RectF Rect()
	{
		return{position.x,position.y,position.x + size.width,position.y + size.height};
	}
	void Resize(const Vec2f& dimensions)
	{
		size = dimensions;
		center = Vec2f(position + (size * 0.5f));
	}
	D2D1_MATRIX_3X2_F Matrix()
	{
		return matRotation* matScale* matTrans;
	}
	void  Rotate(const float& angle)
	{
		matRotation = D2D1::Matrix3x2F::Rotation(angle, center.ToD2DPointF());
	}
	void  Scale(const Vec2f& dimensions)
	{
		
		matScale = D2D1::Matrix3x2F::Scale({dimensions.width, dimensions.height	},
			center.ToD2DPointF());
	}
	void Translate(const Vec2f& offset)
	{
		matTrans = D2D1::Matrix3x2F::Translation({ offset.x,offset.y }) * matTrans;
	}
	void Translate(const D2D1::Matrix3x2F& mat)
	{
		matTrans = mat * matTrans;
	}
public:
	Vec2f position = { 0.0f,0.0f };
	Vec2f velocity = { 0.0f,0.0f };
	float acceleration = 0.0f;
};
Curiosity killed the cat, satisfaction brought him back

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: I've done it again .. uggh

Post by chili » November 29th, 2017, 6:36 am

Component architectures are pretty nifty. I'm probably going to do a hybrid of component and inheritance in project Twin at some point.
Chili

MrGodin
Posts: 721
Joined: November 30th, 2013, 7:40 pm
Location: Merville, British Columbia Canada

Re: I've done it again .. uggh

Post by MrGodin » November 30th, 2017, 1:24 am

I'd be interested to see how you'd do that. I watched your recent video and i like the "dice" roll to determine a hit ect. I think i'll implement something similar to that :)
Curiosity killed the cat, satisfaction brought him back

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: I've done it again .. uggh

Post by chili » November 30th, 2017, 3:04 am

It has a very pen-and-paper vibe that I appreciate :D
Chili

User avatar
cyboryxmen
Posts: 190
Joined: November 14th, 2014, 2:03 am

Re: I've done it again .. uggh

Post by cyboryxmen » November 30th, 2017, 7:06 am

Entity Component Systems are becoming my favourite way of implementing game engines. Personally, I prefer using a type list over ids. Makes the interface more type safe. Here's my implementation on github.
Zekilk

MrGodin
Posts: 721
Joined: November 30th, 2013, 7:40 pm
Location: Merville, British Columbia Canada

Re: I've done it again .. uggh

Post by MrGodin » November 30th, 2017, 7:47 am

@cyboryxmen that's a cool way of doing thing as well. Gotta love how many ways to implement stuff eh
Curiosity killed the cat, satisfaction brought him back

Post Reply