Fio:Iterators
From Fio
"To iterate is humane, to recurse - divine!"
Iterator
Contents |
One Design Principle of Fio is:
- Rendering is a preorder iterating process; Picking is a postorder one.
while rendering is to draw the game world and picking is to dispatch mouse event so that the player can interact with the game.
In Fio we use a typical GoF Iterator:
template<typename T> class Iterator{ public: virtual ~Iterator(){} virtual void first() = 0; virtual void next() = 0; virtual bool isDone() = 0; virtual T current() = 0; };
we can call first() to position an iterator to its beginning; next() to step to the next element in current iterating order. And when isDone() returns true, there's no element left to iterate. current() returns the element the iterator points to.
Armed with a preorder itor we can render the game with little effort
// draw root drawComponent(surface, root); // draw content PreorderItor<Component*> pre(panel); for(pre.first(); !pre.isDone(); pre.next()){ drawComponent(surface, pre.current()); }
The render itor should know how to iterate on the structure of the world and it begins from the root of the world.
Root of The World
In Fio, the world is orgnized in Composite pattern:
Component *<--------- ^ | ----|-------- | | | | Leaf Composite --
Leaf classes are those components who have no child component, such as Button, Lable, Icon etc. Comosite classes can contain other component(s) - e.g. Panel, List, ListItem, GamePanel, Scene, Layer, Cell.
Using composite, the world is orgnized into a tree structure and the root is a panel. We start from main menu of fallout2 as an example:
root panel(main) ->(6) button |->(1) cursor
when click a button to load a new game:
root panel(load) ->(1) cursor
and when the game loaded, it's more complex:
root panel(play) -> game panel -> scene -> glass layer ->* shadow | | |->* floater messages | | | |-> roof layer ->* cell ->* tile | |-> wall layer ->* cell ->* tile | | |->* active object | | | |-> cursor layer -> hex cursor | |-> tile layer ->* cell -> tile | |-> face panel -> inv button | |-> pip button | |-> option button | |-> change weapon | |-> skill dex | |-> item panel -> item icon | | |-> ap lable | | |-> targeted lable | | |-> weapon mode lable | | | |-> message list ->* list item -> lable | |-> hp counter | |-> ac counter | |-> combat icon | |-> cursor
Preorder & Postorder
- preorder
A preorder iterating process works just as the standard Painter's algorithm which is (though) mainly designed to solve the visibility problem in 3D rendering. The algorithm works like "a simple-minded painter who paints the distant parts of a scene at first and then covers them by those parts which are nearer. The painter's algorithm sorts all the polygons in a scene by their depth and then paints them in this order.". Here we draw the lower part of a screen - which is the root panel first. Then draw those ones higher than the root panel(or, the content of root panel) - embeded panel or other widgets - over their parent. The itor will visit the tree structure layer by layer from root to leafs. This way we render the screen in correct order.
- postorder
Postorder itor models kind of window system behavior. It's for event dispatching. When mouse is clicked, if the component under the cursor responds to the click, the event sinks here. Else the event dispatcher will have to look up further to find if the component's parent will take care of the click. The event might go to root panel and finally sink there if no one shows interest.
Postorder itor the reverse of preorder. The iterating starts from most specific/lowest component first and ends at more generic/higher ones in the tree of components.
- intersecting
Component need to intersect with the cursor position to response to mouse event(named picking). For most widget components are rectangle shaped (Lables, Buttons, List, Panel, etc.), it's easy to decide if the cursor is inside or not. But for game objects(doors, street lamps, critters, guns on the ground, barrier, etc), we need pixel-level intersecting. The picking will not stop when it reaches a transparent pixel of the tile image of a component. See Frm format for details of image format.