|
Hello! I'm new to ECS with Ark. I'm trying to make a simple Pong game using it (I know it's overkill, but I want to understand the concepts). I'm running into a problem when filtering. I want to be able to distinguish between Here's how I'm currently creating them: // main.go
player1 := g.world.NewEntity()
mapPlayer := ecs.NewMap4[components.Position, components.Velocity, components.Size, components.IsPlayer](&g.world)
mapPlayer.NewEntity(
&components.Position{X: 20, Y: 200},
&components.Velocity{DY: 0, Speed: 5},
&components.Size{W: 10, H: 60, Color: color.White},
&components.IsPlayer{},
ecs.Rel[components.IsPlayer](player1),
)
player2 := g.world.NewEntity()
mapPlayer.NewEntity(
&components.Position{X: 610, Y: 200},
&components.Velocity{DY: 0, Speed: 5},
&components.Size{W: 10, H: 60, Color: color.White},
&components.IsPlayer{},
ecs.Rel[components.IsPlayer](player2),
)Then in my // systems/input.go
type InputSystem struct {
player1 *ecs.Filter2[components.Velocity, components.IsPlayer]
player2 *ecs.Filter2[components.Velocity, components.IsPlayer]
}
func (s *InputSystem) Initialize(w *ecs.World, entity1, entity2 ecs.Entity) {
s.player1 = ??? // how do I filter just player1 here?
s.player2 = ??? // and just player2 here?
}So my questions are:
// main.go
player1 := g.world.NewEntity()
mapPlayer.NewEntity(
&Position{X: 20, Y: 200},
&Velocity{DY: 0, Speed: 5},
&Size{W: 10, H: 60, Color: color.White},
&Player{ID: 1}, // <---- Use this to differentiate players
)Thanks for any help or tips! PD: This project is very cool and I just want to say, it's also really well-maintained. You can clearly see the effort and care that’s been put into it, and I really appreciate that. 🙌 Thanks again! |
Replies: 1 comment
|
Hi @TanZng, thank you for giving Ark a try and for posting your question! Using an ECS is indeed a bit overkill for a small game like pong, so I will try to answer your question in a way that would scale well to e.g. a massive RTS with dozens of players, each commanding a huge army. ;) First, you should ideally store your player entities in a resource so that they are accessible where you need them, without having them as method arguments. You can easily query for all entities that have a relation to a particular player, as described here (note that there are two ways, I will focus on the second, more flexible one). Your input system would look like this: type InputSystem struct {
filter *ecs.Filter2[components.Velocity, components.IsPlayer]
players ecs.Resource[Players] // Accessor for the resource where your player entities are stored
}
func (s *InputSystem) Initialize(w *ecs.World) {
s.filter = s.filter.New(w) // We use a simple filter here and specify relation targets when creating queries.
s.players = ecs.NewResource[Players](w) // Instantiate the resource accessor.
}
func (s *InputSystem) Update(w *ecs.World) {
players := s.players.Get() // Get the actual resource object.
// Query for entities with relation to Player1 (or Players[0] if number of players is not fixed).
query := s.filter.Query(Rel[components.IsPlayer](player.Player1))
for query.Next() {
// ...
}
// Query for entities with relation to Player2.
query := s.filter.Query(Rel[components.IsPlayer](player.Player2))
for query.Next() {
// ...
}
}Note that you can also do it the other way round and get a relation target for an entity instead of querying over a target: query := s.filter.Query()
for query.Next() {
player := query.GetRelation(1) // IsPlayer is the 2nd component, i.e. index 1.
// ...
}However, note that this (combined with if/else) will be slower than querying over targets, which you should prefer if possible. Also note that there is Hope this helps you. Thank you for the appreciative words! |
Hi @TanZng,
thank you for giving Ark a try and for posting your question!
Using an ECS is indeed a bit overkill for a small game like pong, so I will try to answer your question in a way that would scale well to e.g. a massive RTS with dozens of players, each commanding a huge army. ;)
First, you should ideally store your player entities in a resource so that they are accessible where you need them, without having them as method arguments.
You can easily query for all entities that have a relation to a particular player, as described here (note that there are two ways, I will focus on the second, more flexible one). Your input system would look like this: