Category: blog
My personal blog.
Haven’t uploaded some pixel art in a while, so here is Gravelord Nito from Dark Souls! He’s my favourite boss from Dark Souls by a long shot I think. ;)
Dark Souls fan art
Here’s a short game I made when I was sick. You have to infect everyone with your ickyness.
Pixel Swamp
Here’s a new game I made for the cyberpunk game jam over at itch.io. I made it over 5 days with Ben Weatherall doing the cyber-art and Tim Shiel cranking the cyber-music. It’s about hacking robots while plummeting to your doom, and it requires fast typing and hacking skills. Play it now. You can also listen to the official sound track over here.
An entity system
Here’s a cross-post from my Moonman devlog.
Update: Well it’s been a while, but here’s my first update for 2014. I haven’t touched MM code for a month but instead have been designing and implementing a cleaner and simpler entity system. I’ve been meaning to do this for quite a while now, but after using Unity and looking at other bits of code like entityx I decided to finally attempt it. This is helped in part by the new c++ support in VS2013. The system is also data-oriented — all components and entities are tightly packed in memory. I use a similar free list setup as in MM. I also had to use some c++ techniques I haven’t used before, such as variadic templates and typelists. But that is all behind the scenes, this is what the API looks like:
[cpp]
EntitySystem es;
// Create an entity
// and add some components
Entity& e = es.create();
e.add(Transform(4,5));
e.add(Health(10));
e.add(Physics(0,-10));
e.add(ShortDescription(“Ben”));
e.add(Description(“An architecture-obsessed programmer.”));
// Create another entity
// with different components
// this time using intializer_list shorthand
Entity& chest = es.create();
chest.add(Transform(-4.5f, 0.8f));
chest.add(Inventory{
{ Item::SWORD },
{ Item::POTION, 4 },
{ Item::POTION, 3 },
{ Item::ARROW, 64 }
});
// If we need to keep a reference to an entity
// then we use ID’s (uint32s)
ID chestId = chest.id;
// Then later on somewhere we can get the entity
// and do something with it, e.g.,
if (es.has(chestId)){
Entity& ch = es.lookup(chestId);
// Shift the chest one unit horizontally
Transform& tr = ch.get
tr.x += 1;
}
// Iterating through all entities
// can be done with a range-based for loop
for (Entity& e : es.entities()){
std::cout << e;
}
// Likewise, we can iterate through all
// components of a particular type. For
// example a PhysicsSystem might want to
// process all the Physics components.
for (auto p: es.components
// Apply viscocity
p.vy *= 0.9f;
p.vx *= 0.9f;
// Move entity
Entity& e = es.lookup(p.entity);
Transform& tr = e.get
tr.x += p.vx;
tr.y += p.vy;
}
// Everything is done with references
// if e doesn’t contain C, then
// e.get
// (which can be checked for validity)
Entity& e = es.lookup(id);
Health& health = e.get
health.health = 666;
if (health){
// It’s valid
}
// Components themselves are just structs
// The CRTP gives them a unique class id
// that is used to store them in EntitySystem
struct Health: public Component
float health;
bool poisoned;
Health(float health = 0.f, bool poisoned = false) :health(health), poisoned(poisoned){}
std::string what() const; // human readable rep
static const char* Name(); // name
};
// The logic of a component is implemented
// in a system. e.g., the HealthSystem might
// be responsible for decreasing a character’s
// health if they are poisoned.
class HealthSystem : public ISystem {
public:
bool implements(int componentIndex) override {
return Health::Index()==componentIndex;
}
void setup(Entity& e) override {
e.get
}
void update(EntitySystem& es, double dt) override {
for (Health& h : es.components
if (h.poisoned){
h.health -= 0.1f * (float)dt;
if (h.health <= 0){
// Create KILL EVENT
}
}
}
}
};
[/cpp]
Besides this I've also been thinking about having Script components. These will be a special type of component that uses traditional polymorphism and lambdas to offer a concise and easy way to implement special behaviours. For example, I could attach a custom c++ poison script to a entity like this:
[cpp]
Script* newPoisonerScript(Entity& e){
// variables to be captured by the lambdas
float* duration = new float(0.f);
float* timer = new float(0.f);
auto poisoner = new Script("poisoner");
poisoner->destroy = [duration, timer](Entity& e){
Health& health = e.get
if (health) health.poisoned = false;
delete duration;
delete timer;
};
poisoner->start = [](Entity&e){
Health& health = e.get
if (health) health.poisoned = true;
};
poisoner->update = [timer, duration](Entity& e, double dt){
*duration += (float) dt;
Health& health = e.get
if (health){
if (health.poisoned){
*timer -= (float) dt;
if (*timer < 0){
health.health -= 1;
*timer = 1.0f;
}
}
}
};
poisoner->finished = [duration](Entity& e){return *duration>1.5f;};
return poisoner;
}
[/cpp]
Anyway, once I’ve finalised the new system it’s going to take a couple of days to incorporate it into Moonman, but I definitely think it’s worth the deviation. Basically it means that I’ll turn macro’ed code that looks like this (see devlog for explanation of how it works):
[cpp]
ATTRIB(e, x) += 0.1f;
bool isOnGround = ATTRIB_OR(e, is_on_ground, false);
bool hasPhysics = HAS_ATTRIB(e, is_physics);
[/cpp]
Into nicely autocompleting code that looks like this:
[cpp]
e.get
// or with shorthand
e.transform().x += 0.1f;
// and
bool isOnGround = e.get
bool hasPhysics = e.has
[/cpp]