The aim is that I’d like to simulate Entity_component_system (ECS) in Prolog.
My current workaround is that
-
Introduce the iter_entity/1
, the game loop traverse iter_entity/1
instead of entity/1
.
-
During a frame, structural changes impact entity/1
instead of iter_entity/1
. P.s. The structural changes are creating/deleting entities, adding/removing components to an entity.
-
At the end of frame, clean iter_entity(EID)
and copy entity(EID)
to iter_entity(EID)
.
See the following program.
:- dynamic iter_entity/1, entity/1, component/3.
entity(1).
entity(2).
entity(3).
entity(4).
component(1, position, vec(1,1)).
component(1, health, 100).
component(1, speed, 5).
component(1, bounds, 1).
component(2, position, vec(2,2)).
component(2, health, 200).
component(2, speed, 10).
component(2, bounds, 2).
component(3, position, vec(3,3)).
component(3, health, 300).
component(3, speed, 15).
component(3, bounds, 3).
component(4, position, vec(4,4)).
component(4, health, 400).
component(4, speed, 20).
component(4, bounds, 4).
iter_entity(1).
iter_entity(2).
iter_entity(3).
iter_entity(4).
query_entity_components(EID) :-
format("query_entity_components ~w~n", [EID]),
forall(component(EID, K, V), format("component(~w, ~w, ~w)~n", [EID, K, V])).
query_all :-
format("query_all~n"),
forall(entity(EID), format("entity(~w)~n", [EID])),
forall(component(EID, K, V), format("component(~w,~w,~w)~n", [EID, K, V])).
game_update(FrameID) :-
system_A_update(FrameID),
retractall(iter_entity(_)),
forall(entity(EID), assertz(iter_entity(EID))).
system_A_update(FrameID) :-
forall(iter_entity(EID), ignore((entity(EID), system_A_update_entity(FrameID, EID)))).
system_A_update_entity(FrameID, EID) :-
format("system_A_update_entity FrameID=~w EID=~w~n", [FrameID, EID]),
query_all,
%% In frame 1, when entity(2) update, remove entity(3),
%% then the entity(3) will become invisible immediately!
%% For example, unit_2 kill unit_3.
( FrameID=1, EID=2
-> retract(entity(3)),
retractall(component(3, _)),
query_all
; true
).
The entity/1
represents entities, the component/3
represents components, system_A_update
represents a system, the game_update
will be called in the game main loop.
The design is that
- When adding an entity during a frame, the entity can be only queried and modified in the current frame, but it cannot be called in
system_A_update_entity/2
. It will be called in the next frame.
- When removing an entity during a frame, the entity will become invisible immediately.
It seems reasonable.
Do you have any suggestions? Thanks.
Some tests:
?- game_update(1).
?- game_update(1).
system_A_update_entity FrameID=1 EID=1
query_all
entity(1)
entity(2)
entity(3)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
system_A_update_entity FrameID=1 EID=2
query_all
entity(1)
entity(2)
entity(3)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
query_all
entity(1)
entity(2)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
system_A_update_entity FrameID=1 EID=4
query_all
entity(1)
entity(2)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
true.
?- game_update(1), game_update(2),.
?- game_update(1), game_update(2).
system_A_update_entity FrameID=1 EID=1
query_all
entity(1)
entity(2)
entity(3)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
system_A_update_entity FrameID=1 EID=2
query_all
entity(1)
entity(2)
entity(3)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
query_all
entity(1)
entity(2)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
system_A_update_entity FrameID=1 EID=4
query_all
entity(1)
entity(2)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
system_A_update_entity FrameID=2 EID=1
query_all
entity(1)
entity(2)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
system_A_update_entity FrameID=2 EID=2
query_all
entity(1)
entity(2)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
system_A_update_entity FrameID=2 EID=4
query_all
entity(1)
entity(2)
entity(4)
component(1,position,vec(1,1))
component(1,health,100)
component(1,speed,5)
component(1,bounds,1)
component(2,position,vec(2,2))
component(2,health,200)
component(2,speed,10)
component(2,bounds,2)
component(3,position,vec(3,3))
component(3,health,300)
component(3,speed,15)
component(3,bounds,3)
component(4,position,vec(4,4))
component(4,health,400)
component(4,speed,20)
component(4,bounds,4)
true.