Experimental: Modeling entity relations
has_many, has_one, belongs_to...
has_many, has_one, belongs_to
I've added an experimental ECS::EntityRelations
extension to the ECS to make it easier to model relationships between entities. It's usually best to avoid that pattern in gamedev, so the design of the framework will continue to discourage overusing it and turning your game into some kind of Rails application... but I've found that certain kinds of problems in games just cannot be reasonably modeled any other way. The exact API will probably change before it's settled, but for now we have this:
The EntityBuilder DSL now includes these three new methods: belongs_to
, has_one
, and has_many
. Entities connected in this way will be loosely associated by their entity ID using a BelongsTo({ relation: :entity, id: nil })
component under the hood (it is automatically added to the entity on that side of the relationship).
You can name your relations anything you want - they do not have to match the name of any class. As long as the value of belongs_to[:id]
on an association matches the ID of your entity, you can create as many virtual has_one
and has_many
relations as you like with different sets of filters, and the result set will include whatever mix of entities those filters match.
Example
components do
Person({ name: "Unknown" })
Score({ points: 0 })
end
Player = entity do
belongs_to :team
Person()
end
Goal = entity do
belongs_to :team
Score()
end
Team = entity do
has_many :players, filter: [Person]
has_one :goal, filter: [Score]
end
world :example_game do
entity Player, name: "Alice", as: :alice
entity Player, name: "Bob", as: :bob
entity Team, as :nerds
systems AssignTeams, UpdateScore
end
AssignTeams = system do
filter Person
tick do |args|
entities.each do |player|
player.team = world.nerds if player.team.nil?
end
end
end
UpdateScore = system do
tick do |args|
world.teams.each do |team|
if team.goal.nil?
goal = Goal.new({ score: 0 })
goal.team = team
world.entities << [goal]
end
end
end
end
nerds = world.nerds
nerds.players #> [Player<id: 1, Alice>, Player<id: 2, Bob>]
world.alice.team #=> Team<id: 3, players: [Alice, Bob], goal: Goal<id: 4, score: 0>
nerds.goal #=> Goal<id: 4, score: 0, team: Team<id: 3, players: [Alice, Bob]>