Elixir Newbie

Ep 34 Elixir Game Development using ECSx w/ Andrew Berrien

August 20, 2023 Brooklin Myers Episode 34
Elixir Newbie
Ep 34 Elixir Game Development using ECSx w/ Andrew Berrien
Show Notes Transcript Chapter Markers

Today, we're stepping into the not-too-common world of game creation in Elixir with a special guest, a friend and a colleague.

Andrew Berrien is a developer at DockYard. He is also the brain behind the Entity-Component-System (ECSX) Framework for Elixir. He tells is about his journey building the framework and how you can get started with Game Development in Elixir.

Andrew explains the core concepts of entities, components, systems, tags, and managers—each crucial to bringing a game to life.

We also discuss the potential of running multiple games on a single application, leveraging concurrency, and exploring fault tolerance. We end with a glimpse into the ECSX and Live Dashboard's workings, designed to make game design easy and efficient for both newbies and experienced developers. So, tune in to explore Andrew's fascinating journey and his dedication to expanding the horizons of what Elixir can achieve.

Links of this episode

Blogpost about ECSx at DockYard
ECSx Tutorial
ECSx Repo
Andrew's Twitter

---

Want to learn more about LiveView Native? Take my CodeBeam America Workshop on March 6, 2024.

For the latest updates on DockYard Academy, join our Newsletter.

Brooklin Myers:

Hello everyone and welcome to the Elixir Newby podcast. Today I am joined by my friend, coworker and author of the Entity Component System, ecsx Framework for Elixir, which is for building games and simulations and bringing that to the Elixir ecosystem, which is incredibly exciting to see that ecosystem expand. Game development is something that I think most folks haven't traditionally thought about. Now there are some applications being built in Elixir that are kind of like simulation focused. I believe there's a flight simulator that is Elixir focused on their back end, but it's not something that I think we should have traditionally gone in the direction of. So I'm really excited for Andrew's work here. I've been getting to follow him along on this project and see how the library has been evolving. So, andrew, I'm super excited to have you here. Thank you so much for being here.

Andrew Berrien:

Thank you so much for having me, Brooklin. I'm glad to be here.

Brooklin Myers:

We get to chat fairly frequently about Elixir Dr Academy, so we both work together at Dockyard, but the ECSX Framework is something that you have been working, I believe, kind of like in your spare time, finding room for it. So how on earth do you manage both the workload of work and also develop a fully featured library at the same time? How are you managing this?

Andrew Berrien:

Yeah, so I have to give full credit to Dockyard. I'm a senior Elixir engineer at Dockyard and one of the things that Dockyard does is provide we have 32 hour workweeks, monday through Thursday, and then on Fridays we have what's called Dockyard Fridays where we're able to work on open source contributions, work on our own hobby projects, do independent learning, things like that basically just a skill up day. And so for me I started a program, a mentorship program, with another Elixir engineer who is much more experienced than me, and we were looking for a project to work on. And you know, at the time I was not particularly new to Elixir but at the same time, like I still was trying to approach with a beginner's mind of like what is something that I can do that would push me outside of my Elixir comfort zone. And you know, one of those things was a general usage of OTP in projects. Like I think a lot of Elixir developers end up kind of almost being forced into web development all day, every day. You know that's like the main use case for for Elixir and you know, obviously OTP plays a big role in Phoenix and in live view, but I think that there's there's other ways where OTP is such a versatile group of technologies that there's there's a lot of applications for it that I just had never really done, had much experience with, and is, you know, is you could still say that, like, there's there's a lot of avenues out there, especially with regard to, like, embedded systems, where you know most, most Elixir engineers have don't really have any experience with that, but my goal was to find something that would push me to stretch my OTP muscles, so to speak, but also end up with, hopefully, something that could be a useful library for for the Elixir community.

Andrew Berrien:

I was looking specifically for something that I felt like the Elixir community was lacking or, at the very least, short on, and I think that was the one thing that, ever since I started Elixir like, especially coming from object oriented languages and and a math background too, which is, you know, like I feel like goes really goes hand in hand with game development, and so that was that was always something that was on my mind in other languages. And then, coming to Elixir, like, it's almost like you hit a brick wall, like you raise you, you say the phrase game development and everyone just like is, like no, you like, you don't even before the, you, the, the words even come off your lips, people are just like, no, you, we don't do that here. And so I think that one of one of the things that I was really thinking of was like why? And then, what's potential solutions could Elixir have in its toolkit, with, you know, having having OTP available to us? Like what can we, how can we use those tools in order to get around some of the perceived limitations of game development in Elixir?

Andrew Berrien:

And that was that was a long, a long process of me and my mentor both working back and forth, like having ideas, implementing ideas, watching those ideas fail, benchmarking different ideas, having things that just were were slower or harder to reason, about adding unnecessary complexity, like trimming the fat, like you know there was.

Andrew Berrien:

There was just so much like, if you were to see what the project looked like at the beginning, it was really kind of a mess and was slowly polished and polished and polished until eventually it was something usable. And then, at that point, at that point, it leads to several other challenges which are like, okay, how to, how to get this out there in the community, how to, how to prove it, how to how to even sell ECS because, like you know some people who are already in the game development space have already heard of ECS. Like maybe they're using the ECS implementation in Unity and they're like, oh, this is awesome, but I think for most people they've they've never even heard of it before, so definitely like there were. There were several other challenges, even non programming related that that it became clear that we were going to have to face in in bringing this library to the community and and finding a way that it can be useful to people for their own projects.

Brooklin Myers:

So with that, it's very interesting that this started out as what seems like kind of a learning project, like, oh, I really want to learn about this, and then has grown into a okay, well now, how do we share this with the community? And so you're probably getting to learn a lot of skills that you probably hadn't even been your intention like. How do you broadcast something to the community, how do you make people aware of it? How do you make it easy to adopt? I know that you had me ages back go through and just play with the system and then try it out.

Brooklin Myers:

That was super fun, but at the time I don't think there was much nearly the same level of guides and easy entry, and so I'm pretty sure I completely flubbed it, like I had all the things in the wrong order and I I'm not sure what. I just did things incorrectly, and I know you had mentioned that that kind of inspired you to add more to it. We can talk about how you've made adoption a lot easier, but I really want to know what kinds of games, what kinds of tools like before we get into the implementation of ECS, and how this this helps and what it does. What does it let us do?

Andrew Berrien:

Yeah. So I suppose the simplest answer is it allows us to build games in Elixir. And you know, I think most people in the Elixir community who have come from other languages, they we recognize that. You know what elix, what we do in Elixir. It's not like you can't do it in other languages, like that. That part's not really up for debate. But what is up for debate is the experience that you're going to have when building in this language versus another language, like maybe you get the same end product, but what does it feel like to develop? How long does it take to develop? How many engineers did you need in order to make that happen? And I think those are the strengths that Elixir brings to the table, among many others. But I think that that's that's really one of the biggest deals to people like, especially when you have experience in other languages and a lot of other code bases. And then you come to Elixir and you're like oh, okay, this is. This actually has simplified the development process quite a bit, made the development process a lot quicker and and taken out a lot of the foot guns, and so this kind of the same philosophy is kind of what I was trying to bring to ECSX. Like it's not that you couldn't build a game in Elixir before, but what I want right now is that you can build a game in five minutes by yourself. You know, like that's that's really what I'm I'm getting at here is like the developer experience should be, should be number one. Like I want people to be able to pick this up, have a very simple interface but also a very simple design.

Andrew Berrien:

Architecture like this, even outside the ECSX framework, just the entity component system architecture as a whole is a very, in my eyes, convenient way of looking at a real time application. Like you have components and you have systems. That's that's pretty much it. Like there's you have an entire back end just with that. And components store your data. Systems are the logic that operates on the data. Like it's very, very simple and once you're actually working in a code base with that architecture, it's it's very simple to make changes. Like when you need to add a new feature, it's there's not as much pieces of code that that kind of touch your feature. Like there's a lot more compartmentalizing of what. When you have a feature like it's it's very likely that that feature is going to have some data that backs it and then some logic that affects game entities when that based on the presence or value of that component. So you have a feature, you create a component, you create a system and there's your feature and you. It is often the case that the existing components and the existing systems don't don't need to be touched. So you get a good separation of game logic, you get separation of data in a way that is still very composable, like you can have.

Andrew Berrien:

I'm not sure if you want to go too far into like the ECS architectural design process and in this talk, but basically if you have a component like a hit points component, hp, you could give that to any type of entity you want and it will now have hit points and it will now have systems that operate on hit points containing entity.

Andrew Berrien:

Entities with hit points will now be included in that selection. So if you were to give something like a bullet some hit points, like now, the bullet is now it's not just this thing that flies through the air and deals damage, but it also is now something that can take damage from things and maybe even die. So you're you're really able to extend the entities in your game really quite easily by just adding and removing components. So instead of having like, okay, this is a player character which always has these specific fields in the character, and then you have this bullet and that the bullet always has these fields, you know, velocity and such and such it's, it takes away all of the rigidity of that and instead allows you to compose components really any way you want.

Brooklin Myers:

Yeah, the, the composability really strikes me is quite interesting and it's ability to bring these systems together and bring these components together to create this functioning game. So I want to give a bit of an overview of the different terms we're using, maybe some examples, if that's possible, because we've talked so far about entities, components, systems, and then I know there's also a few other concepts, tags and managers, and I think I understand some of them and I think I'm a little bit fuzzy on some of them. So Can we start with our, our entity? That is the thing that binds our components together. Right, that is the thing, is that?

Andrew Berrien:

right, yeah, well, so In a traditional game like you would have entities and each entity Will have a set of data that always comes with that entity. If you have a player character, like it has, it's it's like a map or a struct or whatever with the fields inside it that make up that entity. In ECS X or any ECS game, an entity, entities, don't really exist as data. So if I have an entity and it's a player character and it has, let's say, hit points, level and class, how that exists in the database is you have a hit points component, a class component and A level component and that's it. Like the reason why you know that those three thing that there is a player character is because those are associated with a particular ID. So let's say you have the player characters, id is one, two, three.

Andrew Berrien:

So the components job is two things. One is to store the data. So if it has 50 hit points, like obviously has to store an integer 50. But then it also has to store what the ID of that entity that it belongs to. So your component now has your ID one, two, three of the entity and then the hit points 50. And same thing for the level. Maybe it's an entity one, two, three, level three, something like that so the entity never really, never really exists as data somewhere. The entity is just an association that all of the components have to show what entity that that component belongs to, and so I think that's one of the that's probably the most difficult thing to wrap your head around when getting into ECS is just that data is always going to be in the same place, is just that data is always components and that you associate components towards Like a metaphysical entity, like it doesn't actually.

Brooklin Myers:

The entity doesn't exist in the database somewhere, it just is right, and that is because they're all grouped together by some kind of identifier. The entity really is the identifier, in a way. That's the thing that groups all of your components together. Your components track data, perfect. And then let's talk about so. We used hit points as our example of a component. Let's continue that story to system. What is a system?

Andrew Berrien:

Right. So so we mentioned that let's say, a player has hit points, a level and a class, so these things I'm maybe even we don't, we don't pay so much attention to class at first and just focus on like hit points, even the whole. What's the whole point of having hit points like that's? That's really the question that systems ask is what is the point of this component like if you have a piece of data that's tied to your entity, like it signifies that you're going to want to use that for something, and so the system is that something. So in the case of hit points, hit points exist so that when you get hit, you take damage, so you like. That right there shows what the system logic needs to be like. The system logic is going to check for attacks that are currently about to happen, and then it's going to lower the number that's in those hit points components for anyone who's being attacked, and then and so that's one system that's like a damage system when you get hit, the damage system remove Lovers, your hit points. But hit points also serve the purpose of when they reach zero, you lose the game. So there's that's another system like what. What this system is simply going to do is just look for Hit point components that are zero or less, and if it finds that, then it's going to do something just like the. The damage component is like, it's trigger is An attack is happening like, and so that might come from something else, like in a you know a component for like attack, damage or pending attack or however whatever that trigger is, or collision even, and then once that trigger is met, then the system will Will deal the damage same with the, the death system like one. The trigger is a health, a hit point component has gone to zero, and then the effect of the logic is that that entity that has that component dies, and by trigger. Here I do want to clarify, because I think that that could that could mean a few things conceptually when people hear that word. And here a trigger is really just other component data, it's. It's not necessarily like an event, but if it is, if you do have events like an attack that's firing, like those are still modeled with components like that's that's the thing, does a lot of damage, that's that's the thing.

Andrew Berrien:

Designing with ECS is everything is a component like. You have to find some way to make it into a component, even if it seems like You're, you're not really sure how do I do this, and that's why a big part of this project is just teaching the ECS design architecture. Like you might see, you might not see as the user. How is this? A component like this seems like something that should need something special and it's like no, there there is a way to model this with components, and when you model it with components, that's what gives it the hook into the system. That's what allows the systems to know which entities to operate on. Which components to operate on is based on other component data.

Brooklin Myers:

So I want to clarify trigger here a bit for myself, because my understanding is that systems will run on every tick. Is the trigger like a condition that determines if they should Actually do something, or is a trigger outside of the system like is this some control flow I'm writing in my system run function, or is this something I'm writing outside of the system?

Andrew Berrien:

Yeah, so this is so once again, like ECS does a fantastic job of simplifying everything. Just like I said, when you have some data and you don't know how to store it, there's only one answer, like if your answer isn't components, it's wrong, like so it's similar to the actual game logic. There's only one place for game logic to live and it's not entities or components. So when you have game logic, yeah, it has to be in the system. And what was your question? It was about the selection of what, what components, like what is that trigger?

Brooklin Myers:

is that like a bit of control flow? I write inside of my system? That's like if some Character is in attacking range, do an attack like, apply some damage to their, their health or whatever it is, change a component in some way?

Andrew Berrien:

Right. So generally, how? How this is worked out for me is get all components of this type. So if you're something that is like, let's say, you're a system that is going to reduce hit points for entities that are poisoned, you know, if you're poisoned you lose five hit points per second, something like that. So what you're going to do is you're going to just and this is right in the API we have a function it's get all, and so you're gonna run Poison dot, get all, and you're gonna get all of the poison components and then from there, you know, because the entities IDs are stored in those components. Now you have a list of every entity ID that the system needs to operate on, and so if there's a situation where maybe the mere presence of the component doesn't signify it needs to operate, but the value of it, so you're fetching everything that has Hit points and checking if it's Less than ten, or maybe you want to search for a specific value. You want to find every Component, every hit point component where the value is zero, like we have an API for that as well, to search for specific component values. So really it's it's just about querying components in a way that's useful and that's why I would recommend that components Should it should have a purpose, like how I said, like with hit points, like oh, what's what's the purpose of hit points?

Andrew Berrien:

And it's like, well, does it tells you how much life your character has? But it's like that's not. The question that I'm asking is why are there hit points? Like what is the game feature that requires hit points? And when you think of it like that, now hit points becomes a trigger, now hit point and and maybe on on the other end, hit points are are going to be affected by another trigger, like, like I was was mentioning before, like attacks. You know, maybe you have an attack that is a component like this this attack has been made and so when that system runs through, it's going to look and see what are all the pending attacks. Maybe you have a pending attack component, what are all from there? That's the info that gives you the information, that this that gives the system the information that it needs in order to know who, who's going to take that damage.

Brooklin Myers:

Okay. So I think I roughly understand or at least I'm feeling confident about components and systems. That's basically you have your behavior and your data and the systems are going to run every tick. They're going to use whatever control flow they need, or maybe they'll just get all components of a particular type. Applies to operation. Change things in your system now your systems change for the next tick. The thing that I think I'm fuzzy on is tags. Can you help me understand tags, because I'm currently thinking about them like a bullion, like I've tagged this with poison, but then I'm like well, why don't I store that as a component, as some kind of a bullion field? That's where I think I'm I'm a little fuzzy.

Andrew Berrien:

Yeah, yeah, and honestly you're not that fuzzy in this case, because that that's that's pretty much spot on, like a tag is basically a bullion component. So it's it's kind of a bit of like syntactic sugar, because you don't really need to store true or false, because if you have the component then the value is always true. You know. So, there's no, there's never going to be. You know, oh, I'm going to store the poison component and have the value be false, because if that's the case, then just don't add the component. That's the magic, you know, that's still that, that thinking from okay, well, I have a character and it has a field and the field is poisoned Question mark, and then it's going to say true or false and that field has to exist for every entity in the world. You know, it's not that like with this, that player character, it could have the component or it could not have the component. So there's not really a need to store the Boolean value at all.

Andrew Berrien:

But that is a way that you can think of it is like a component. It's a component where the mere presence of the component gives you all the data you need. You don't actually have to store some kind of value like an integer or a string or something like that. You know, if you have hit points, if you have a hit point component with no value, that doesn't mean anything, it's like how many, how many. But if you have a poisoned component, it's you don't really. A value is not going to help there, like you it. If it. Maybe you have different levels of poison and so the component is poison level. That has a value.

Andrew Berrien:

But if the question is just are you poisoned or not, there's no value to store there and so tag gives you an API that is specific to that, because most of the component API revolves around the components value that it stores. The API around tags is just do you have it or not? And once again, the ability to get all and get all entities that have that tag. So if you put that poison tag on some entities like there's a some single function, poison dot, get all and you're just going to get all of the entities that have that poison component. So that's that's really going to be the main way that like almost for me at least, almost every system that I write starts with get all Of this type of component and that's going to be like the trigger that tells the system what to operate on, and tags tags are a perfect example of that. Like it's, just get everything that has the tag. That's what we want to deal with right now.

Brooklin Myers:

So that's really cool. By the way, I got really excited when you're talking about not needing to store the data of a billion. Seems like such a like, like a dough, like, like hit your head, bop your head because you had a V8 or whatever it was Moments like when you see, like if that, when, when people write, like if variable equals equals true.

Brooklin Myers:

Yeah, that is one of the most common patterns I have to train students out of. Is that like, hey, if you're checking if something is a bully and just use the Boolean, so, or if you're returning true, just use the condition that you had or flip the condition. If you're returning false, you don't need all that extra? So I do want to clarify something because it seems like I could as a developer. You know you can't. You can encourage people down the right path of development, but they're always going to be able to write things in a weird way.

Brooklin Myers:

But it doesn't make me wonder. Are there limitations of components in terms of like what data they can store? I noticed we'll talk about the generators that you've added later, I hope, I hope we have time for that but I noticed that you actually specify what type of data something is going to store in the generator, I believe. So, yeah, are there limitations to like what type of data my components can store, or recommendations or like? Because now I'm thinking, well, if I'm storing a Boolean in a component, that's maybe a better use case for a tag, so what's?

Brooklin Myers:

the situation there.

Andrew Berrien:

Yeah, this is. This is something that has evolved over the course of ECSX development and like what we would find at first is that people would create components and and then put like a map of a bunch of different data that's relevant to that component. Like they might have a instead of hit points components, they might put like just player stats and then inside it would have like a map that would say like hit points 50, mana 40, level three class, rogue and so, and what this does really is it prevents you from being able to search this data quite as efficiently. You're you're kind of coupling stuff together where it's like okay, now when you take damage and you're going to get that, you're going to get the player stats component, and but now you're getting hit points, mana level class and you're only operating on hit points. So now you've fetched three things and like this is this is one of the main reasons why I chose ECSX for elixir is because of the downsides of message passing, like when you are going to fetch some data from out of memory, like that's copied, like a copy is made from one process heap to another and that data has to be passed via a message. So one of the big problems that people have with game development and elixir is that this has to happen like hundreds of times per second. You know, thousands of times per second. And we're basically, if we were to take this to its logical conclusion, you just get the standard object oriented model of like, give me the character and you get 500 fields with it and you only needed to operate on one of them. So the whole goal of ECSX in general is to only operate on the field, so to speak, that that needs to be operated on. When you want to get a piece of data, you only get that data. You don't get any, any unrelated stuff, in order to minimize the amount of data that is passed via message and copied from process heap to process heap.

Andrew Berrien:

So when, when we were seeing this kind of anti pattern of of kind of like composite components let's call them where a component is storing a bunch of components inside of it, like the, we were like OK, well, we need a solution to this, but also like this was a great opportunity to introduce type checking. So the solution that that we landed on is components are primitives. Components are not going to be a map of data. Components are not even going to be a tuples of data components are going to be an integer or a string or like right now I believe we also have timestamps in there, like but the the.

Andrew Berrien:

The goal there is to define what type of data this component is storing. Like it doesn't make sense that hit points for one character would be an integer and for another character would be a string. Like when you define your hit points component, you're going to define that as an integer or maybe a float, and and components should stay that way. So when we do have type checking for these, if you try to create a hit points component that has a timestamp or a string or something like that, then it's going to fail and and the generators, as as you've mentioned several times, just make this even easier. Like it's already, creating a component is one quick line in your terminal, but defining that type is also just part of that line it's.

Andrew Berrien:

We took took a lot of inspiration from other successful libraries using generators like, for example, like an Ecto, like when, when you're generating something in Ecto, like it's, it's just one line in a generator. You can define the type, you can define the different fields in the schema, you can define references, etc. And so that that was a goal for us is just to be able to spin up a component. Everything that component needs in one line from the generator.

Brooklin Myers:

Generators are such a phenomenal tool and I think they're. They're. Not everyone necessarily sees their initial purpose. I think a lot of folks think, oh generator, that'll make me write code faster. Right, that's the whole purpose. That generates the code for me. In reality, I think generators are an education tool first and foremost. They show developers here is the style, here's how you should write things like the Phoenix generators are a phenomenal teaching tool showing students how everything connects together for particular resource. It's really really handy. So I'm really excited to talk about the kind of adoption efforts that you've put into ECSX and helping people explore that. But I don't want to miss out on the last piece of our system that I'm still fuzzy, which is so. We've talked about entities, components, systems tags, and the last one, I believe, is managers.

Andrew Berrien:

Right, okay, so when you create an ECSX application like, we have a setup generator and basically what the setup generator is going to do for you, amongst a few other minor things, is create this manager, and so the manager is the module that kind of ties everything together, because we had we had gone through several different, more complex designs for the, the ECS, like the underlying OTP design, and one of the things that we found that is not only simple but also very useful is the single gen server, serializing things through a single gen server and one of the big problems with using at under the hood for storing things like it. I mean, there's so many amazing positives to using at like, of course, one of them being it's in memory, so it's super freaking fast. But the downside is that you can get race conditions and if you have multiple systems that are both trying to, you know and and and we even kind of touched on it a little bit, like with hit points there's multiple reasons why your hit points might go down, like it might be because you're poisoned, it might be because you're getting attacked, it might be that that's just how the game works. Is like hit points go down over time, you know like there's all sorts of reasons. Maybe you could be getting healed and your hit points are going up from a different thing. So when when this happens, it's doesn't necessarily have a way of handling that and not having data corruption, like that's something you kind of have to build in yourself and most of the time that happens by having a gen server, basically like at the gates, you know that you have a guardian, that that is all. All traffic to and from the et's table is going to pass through this gen server in order to handle any kind of conflicts like that.

Andrew Berrien:

And the thing that we really found was that using a single gen server to serialize all of the systems it allows allows the developer to feel much safer in developing their system logic. Not only is there not race conditions, but you also now know the order in which systems are going to run and sometimes that that could be. That could be a big deal. Like, for example, like if the death system, from going down to zero hit points and losing the game, does that happen before or after you take damage? Because if the death system runs, says OK, you have five HP, you're alive, and then right afterwards you take damage. Now this tick is ending with you having Zero HP and still being alive.

Andrew Berrien:

Maybe for some cases like, that's not good, and maybe in most cases it still is, because, like, if the death is the first thing that happens, then in the next tick you're not gonna be able to do anything. You'll die first thing on that tick. But I think that it allows you to reason about your systems in a way that can be much more manageable for some systems and I shouldn't say system, I should say for some games or applications. And you know that's the one thing that we really want to do is make sure that ECSX can work in whatever different use cases people have. Like, if you have a game idea, we really want ECSX to be able to support that and not be like, oh well, it's not really a good fit because of this and this Like. We want it to be able to handle as many different cases as possible.

Brooklin Myers:

That was one of the questions I definitely had about, because one of the things that OTP is really good at is concurrency, but one of the things that games need a lot of is synchronization, and so how do you contend with those two things? How do you enable that type of making sure that things happen in the order that you expect them to and that they're predictable, right? So I'm really curious to hear more about oh, by the way, using the word systems man, I love Elixir, but one of the things that makes teaching it really hard is our use of really common popular words. The number of times I've had to say, well, the context and then realize, oh, but not like the Phoenix context, like the context of this is, or you know, oh.

Andrew Berrien:

So when we use the use macro, and components is definitely one of those too. One component of this component, yeah, even outside ECSX, like we're using component for too many things.

Brooklin Myers:

And now, like with this, it's like okay, no, it's not a function that takes a science and returns a HEEX template and so, if I'm understanding correctly this point of synchronicity, is that the manager or am I misunderstanding the kind of explanation here?

Andrew Berrien:

Yep. So what the manager is going to do is it's going to keep track. It also is something that keeps track of all your systems and all your components. So you could theoretically write code for a system but not included in the manager and it wouldn't get run Like. So that's one thing that the generators also do for you is, if you do use the generator like, you don't really have to touch your manager file ever, like when, because when you generate a system or a component like, it's automatically going to inject those into your manager. So hopefully, like in most cases, the manager is relatively hands off and just kind of does its job under the hood, which is it's going to be the counter for ticks. So it's going to keep track of when each tick needs to get run and then, when it does run those ticks, it's going to run the systems in order, and so here we have strict serializability, like we can always reason about the run order of systems and the effects of those system runs.

Andrew Berrien:

However, the one thing is that you're not getting concurrency. You are. You are just running things one after another, single core, and so the our goal for concurrency is that it's within individual systems where you would write your concurrency logic and this. This is also a good fit for elixir because of how low the bar is for introducing concurrency. It makes it extremely simple to add concurrency in a number of different ways in elixir.

Andrew Berrien:

So, yeah, your systems are running one after another, but inside that system, like when you have that hit points reducing system, like maybe you have 100 attacks. Now that's up to you. How do you want to paralyze those, do you? Is it isn't necessary? Maybe the system is just so fast you write right now. Compared to your system resources, it doesn't matter. Maybe you have this world of Warcraft kind of system where there's 10,000 players and half of them are attacking something and so you have a lot of attacks and this. This now becomes a good candidate for you to introduce concurrency into that systems logic. So the systems themselves are not concurrent, but the logic within an individual system that can be made concurrent in order to improve performance.

Brooklin Myers:

That's so cool. I was. So, as I was reading about the system, I was wondering where the point of concurrency would be, and that was where, intuitively, it made sense that, okay, well, if you have some system that's going to change the health of a bunch of things, probably the health of one thing is not going to depend on the health of another thing, and so that's where you're probably not going to have difficult conditions happen where you know, oh well, there might be some system where, like, oh, if this character's health hits 50, it unleashes an EMP attack and automatically damages everything else, and so you want to make sure that that happens afterwards and that there's no misordering there, because that'll affect how the math of the game works out. But applying the initial damage to get it below 50, well, that can happen to this character or that character and there's really no concern there. So the individual changes to all of your components you can have your concurrent operations and take advantage of some paralyzation there.

Brooklin Myers:

But at the systems level, where you really want to be able to think about things in the proper order, my damage system should apply before my death checking or revival system, and so being able to still reason about it. It's a really, really clever application of OTP and kind of that. That's synchronicity you get from having things put into a single gen server, so that's really cool. So I think that was a question I asked you a while ago when I'd first started exploring this, and so it seems like you've come to an understanding of where to leverage that concurrency.

Andrew Berrien:

Yeah, I mean there was.

Andrew Berrien:

There was definitely a question of do we introduce the concurrent, try to find some way of introducing concurrency of multiple systems running at the same time, and I'm not saying that it's completely off the table, but I think that for for most cases it's going to be simpler to reason about systems running with strict serializability and and putting that on the onto the developers to implement concurrency using the patterns from elixir in whatever way is going to serve them best, if at all, and even in some cases, like you were mentioning, like systems that have side effects that could introduce possible race conditions, like we do.

Andrew Berrien:

There are ways around that as well. With you know, pretty much anything any effect could realistically be like delegated to another system via the creation of a component. So let's say, if you're under 15% health and then it sends out an EMI which then afterwards another system reads EMP pending components. You know so if, if you are in a situation where you're in a situation where you're feeling like you're, the development or performance is being hindered by trying to do too much in one system, like it can be broken apart in order to, in order to separate them like that, just using components.

Brooklin Myers:

So can I have multiple managers or is manager like a single concept, like guys like my application supervisor almost?

Andrew Berrien:

Okay, so a manager is you. You only have one of them. But there is the question of, you know, should there be multiple games in one application? You know there's, there's also that's that's something that's been brought up as well like because, yeah, there should be one manager per game and for most people they're going to have one game in their application, and so that's that's kind of where we're at right now.

Andrew Berrien:

And, you know, something that we could look and are looking towards in the future is like the possibility of, okay, well, what if you have multiple games running on the same application?

Andrew Berrien:

Like it's not, you can't do it with one manager because all of the system logic is totally separate.

Andrew Berrien:

Like you could realistically have several games running on the same manager and the systems and the components are just doing their thing in total isolation.

Andrew Berrien:

There's nothing that's combining them into one single game like that except, I guess, the front end, but there is there is thoughts about, particularly like with the live dashboard, like we I know we haven't touched upon that yet, but they're like when it comes to monitoring your, your game, like if there's a possibility of having multiple games, one manager for each one of those multiple games, and so then when you go into, let's say, ecs ECS X is live dashboard to look at stats, to be able to organize those statistics based on okay, well, this manager is at this percent load, this manager is at this percent load, stuff like that be able to say, okay, well, I want to look at these component tables, but only the component tables for this game, not not like in don't include these other component tables from another game, but that's still something that's just on the roadmap, like we're really, like I said, we're really trying to consider all the different possibilities that people might want for their games.

Andrew Berrien:

Even if it's not something I would want for my game, like, I still want this to be, you know, useful for as many people as possible in the elixir community and maybe even people outside the elixir community. Bring them in and show them the strengths of elixir through this, through this framework, like hey, you can develop your game way quicker here than you would in Python or whatever.

Brooklin Myers:

And leverage the powers of concurrency and fault tolerance within a game, which feels very natural to me, other than you do need that point of synchronization.

Brooklin Myers:

But having the ability to apply a bunch of computations and parallel feels like it makes sense for game development to me. So the problem is, you have that synchronization process. So I'm really curious if things like could you have systems that could operate, you know, independently of each other, like those could be concurrent, so they all have to finish by the end of the tick, but you know you could parallelize multiple systems and say, well, these two systems definitely don't care about each other, right, like my health system and my plant growing system have, or those are bad examples because those aren't behaviors. My well, plant growing is not bad, but maybe my damage system, maybe those two things have really nothing to do with each other. Right, I don't care if my plant got taller or if someone attacked my plant. Those things can happen independently.

Brooklin Myers:

And so maybe we can leverage some concurrency, or maybe that's an over optimization and that type of flexibility will just make your systems harder to reason about and not really get you that much performance benefit. So I'm really curious to hear how things continue to develop in that area. I know we don't have too much time, so I really want to make sure in the last kind of bits of the episode here we managed to hit the adoption what you've done to make it kind of easy to use. What can people do if they want to try out this, this framework?

Andrew Berrien:

Yeah, ever since the beginning, like we knew that giving people an easy end to this new technology, like even if elixir is not new to you, like ECS probably is, and the API of this framework definitely is. So front and foremost of this framework is its tutorial like. I think that the this kind of Tutorial project is going to be most people's entry into the game, because it's going to cover Not just like, okay, how do you make a component, how do you make a system, but like how these really are gonna fit together and to construct a full back end, but also this is also gonna cover on some front end stuff, and I think that that's that's something that I was concerned about is like, okay, you built a game back end and then we just drop you off there, and then the the you know, maybe someone who is not an elixir person and is just trying it out is like, oh, yeah, well, so making the back end was great, but oh, I don't know anything about elixir front end. Like they're looking around like, oh, does elixir have graphics libraries? Like. And then they're like, oh, man, this isn't that great, like so part of one of the things that I wanted to do was be able to use something very simple like Phoenix live view in order to create a front end that that's.

Andrew Berrien:

That's usable and not too complex to get set up.

Andrew Berrien:

So what we're doing in the tutorial is SVG like, where you're gonna get a little bit of the basics of SVG if you're not familiar with it, and using live view to keep that SVG up to date as best it can and hooking that up into the back end, and so that's not to say that like this is the way to do it, like we're still there's. This is a front end agnostic Kind of framework like and I have been looking into some some alternative front end choices that could be added to the tutorial for those who are looking to to stretch outside of Phoenix live view. But for now what we've got is you're gonna build a back end and then you're gonna run through and build a live view front end and at the end you'll have A demo app where you can kind of sell your ship. Around the ocean will be enemy ships that try to shoot at you and then when you get close to them, you you shoot at them, you've got hit points and you can die and stuff like that.

Brooklin Myers:

That's awesome. I hope there's at least pictures at the upcoming Talk, definitely so. We've already talked a little bit about the generators, so those make it easier for people to understand how am I supposed to work with these things, because they can just generate a component, I think, generate a system as well the two generators, or there is there.

Andrew Berrien:

Yeah, so I mean that we have four generators total. One of them is the setup. When you first sit down and you mix P, h, x new and you make your new project like you're also gonna mix E, c, s, x, setup and that's going to set you up with a manager file that's already to To start running systems. And then the other three are component, tag and system. So component and tag are kind of the same thing, except tags don't have a type. So you would type E, c, s, x, gen, component and then the type of the component and bam, you have now that component is in your game, ready to go. Like you don't have to do Anything else at all, like you have a full API in order to access, update those components you have. You know your type checking, you have a module in your app and that component is module has also been injected into your manager. So everything you need to do for that is ready to go. Tag is the same way, just without a type, because it doesn't. Tags don't store data.

Andrew Berrien:

And then with a system, the system is going to do everything except write the code.

Andrew Berrien:

You know it's not not not chat GPT yet, but so it's going to create the system module.

Andrew Berrien:

It's going to make sure that your system module, like, has the appropriate macro, has the appropriate function, with the name and arity that will be expected by the manager, and all you need to do is just fill in the logic of your run function. So kind of like when you do ectogen migration, something like that, it's going to create a module at the top. It's using migration and then it's going to make that change function, but it's just empty. So it's up to you to say OK, what is that migration? Do same kind of vibe here with the system and what I found is that it makes the design process like for your game, like just a series of generator commands, like you can have a whole game idea in your head and just start typing out generator commands and before you know it you have the whole game is ready to go, except for the individual pieces of system logic. And at that point it's just use that component API, fetch the components you need, update the components you want to do, and then that's it. Your, your whole back end is done.

Brooklin Myers:

I'm realizing it was a very missed opportunity for me having built connect for for the upcoming Elixir Conf talk. I really should have used that as a chance to try out this library. I think maybe tackling ECSX and Libya native would have been a lot all at once, but maybe that'll have to be a future refactor. There's one other thing I want to make sure that we talk about, which is OK. So there's, there's good tools for adoption. We have our, our generators. We have looking at the documentation it's very well documented. You have the guides, you have this tutorial project, so people can get started as well, which is fantastic. Once I have my app running, I believe you also have built a live dashboard to make it easier to kind of understand and and observe. Is that correct?

Andrew Berrien:

Yeah, and so, for those who don't know, phoenix live dashboard is a product, is something that comes with Phoenix and allows you to inspect the state of a running application with respect to any metrics that, like telemetry that the app might be emitting. It will show the size that's being like, like, if you're using it's. It will show the size of your at tables. It will show some various things relating to Ecto if you're using that in your app like basically getting different pages for different areas of your app that you might want to inspect and so the goal here was to use this technology that pretty much everyone already has. You know, maybe they might not be using it, but if you're building an app with Phoenix, you do have access to that and so to use this technology and just simply add another page that will show ECSX stats, and so the statistics that we have right now are probably the most important. Front and center is the overall load of your game. So systems, every system runs every tick. So let's say that your server runs at you know you have a 20 tick server, so it's going to be 20 ticks per second, and so that would be 50, 50 milliseconds. So every system that you write all of your game logic has to run within 50 milliseconds. If it takes 51 milliseconds, now your systems and now your game is going to start gradually. It's going to be one millisecond behind every single tick and that's going to keep growing and growing and growing until eventually you're like a day behind. You know. You basically are on the track to system instability when you have a tick rate and system load that doesn't match up. So one of the things we want to do is say, ok, well, here's a bar that represents your 50 milliseconds, if assuming a 20 tick server, and that's configurable. So this bar represents 50 milliseconds. How much of it are you currently using? So when you pop in there, if you see that bar is full, you know your systems are using all 50 milliseconds. Like that's going to be rough. What happens if you know, tomorrow your game gets more popular and you have twice as many users, like your apps not going to handle that very well? So that's going to be your sign right there that you need to either lower the tick rate so that systems have more time to run, or optimize your system through you know concurrency, or you know a better algorithm, or whatever. And so that leads me to the second thing that's displayed with ECS X live dashboard is the time of each system. So you're going to see the average run time for each system that you have.

Andrew Berrien:

So if you have your HP system, let's say you, you're seeing, oh man, it's taking my system load is very high, I need to find something to optimize. And you look at your HP damage system and it takes one microsecond to run. You're like, oh well, yeah, I'm probably not going to optimize that too much. And then you look over at your path, finding you know system or something and it takes like a year to run and it's like, ok, that right there, if I could just optimize that system I'll be good. You know that's the problem one.

Andrew Berrien:

So being able to identify the problematic systems when you have them, identify sources of optimization, and that's something that has come up with me a lot like. Usually there's one system that is like doing a lot of work and really needs to be optimized, like it needs to have the best algorithm it needs. You know, I've played around with introducing NX for numerical computation in order to improve performance with some systems that are heavy on numerical computation. So yeah, systems and system overall system load is probably the biggest, the biggest feature for a live dashboard right now, but also the ability to say, hey, I want to see the HP of every entity in the game right now, you know.

Andrew Berrien:

Or I want to just see like, hey, how, how many entries are there in the components table of poison, like how many entities are currently poisoned? And I think that this is, you know, not quite as useful, but can be good when you, when you do have a bug, like I remember once when I had a bug that I was trying to track down in one of my games, and then I look, an entity like D spawns, like if the mob is killed or something and dies, then the component gets removed. But that wasn't happening. So what was happening is I would watch the size of this component table and it would just keep going up and up and never going down and I was like, oh well, there's my bug.

Brooklin Myers:

That is really cool. Yeah, that is a phenomenal amount of observability into your system. I am so sad this episode has to come to an end. I now have minutes to get to my next class. That is how much fun I had at this episode with you, andrew.

Brooklin Myers:

I feel like you and I can and should talk about this more because I think there's still a lot of interest and depth and if folks are interested in learning more about ECSX, andrew has an Elixir Conf talk coming up, which will be happening in the beginning of September. So if you want to come in person, I'm also going to be there speaking about Live View Native. So if you'd like to meet me and Andrew in person, I hope to see you there. Always feel free to come up and say hi. I love it when people come up and talk to me at Elixir Conf. I'll be the guy with the long hair and you'll also be able to see Andrew there. So, andrew, is there anything else before we end the episode here that you would like to shout out to or mention for folks, or kind of a call to action? Call to action here.

Andrew Berrien:

I think I really just want to shout out you, Brooklin, and the Elixir Newbie podcast and really give give a serious thank you for having me on here and giving me this platform to share my work. You know, I really appreciate that and I hope that anyone who's listening to this and is interested in Elixir and might have a game idea Give it a try. Like this is something that you can definitely get set up and get your game up and running in an afternoon. So just give it a shot and feel free to reach out if you have any questions or if you want to put an issue up on GitHub or something like that. I'm around and I'm happy to help with any questions that anyone might have.

Brooklin Myers:

Well, I want to say that I normally I very much never expect anyone to shout out the podcast, so that's very kind of you, andrew. I really appreciate that You've always been such a kind soul and a wonderful spirit to work with. For folks who don't know, andrew has been very instrumental in Dock Art Academy, has provided feedback and always been very supportive there. So really, really happy to have gotten the chance to speak with you here, really excited about the work that you're doing Expanding game development in Elixir. That is going to be it for this episode of the Elixir Newbie podcast. I want to thank all of you so much for listening. I hope you Elixir Newbies out there enjoyed this episode and learned a lot. I know I did. Thanks again.

Building Games With the ECSX Framework
Developer Experience in Elixir and ECSX
Understanding Entity-Component-System (ECS) Architecture
Limitations and Type Checking in ECSX
Multiple Managers and Games in Elixir
Understanding ECSX and Live Dashboard
Andrew's Support and Elixir Game Development