-
Notifications
You must be signed in to change notification settings - Fork 0
/
Architecture
141 lines (102 loc) · 5.61 KB
/
Architecture
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
This document reviews the architectural aspects of the Jogo framework.
One important aspect on how Perl threading works is the fact that Perl
has a share-nothing threading model, which means that data initialized
or modified in one thread *after* the spawn of another thread is not
seen by the other thread.
The good part is that Perl threads are really concurrent, as opposed
to Python or Ruby, for instance, that doesn't have any concurrent
execution.
Parallelization is a really usefull thing when it comes to gaming, but
dealing with the "share-nothing" model requires a bit more effort.
This framework aims to provide a transparent way of dealing with that
by doing a MVC architecture, where the Views, the Models and the
Controllers communicate with each other via event queues.
Follows some highlights on how Jogo works:
* A Controller object will hold references to one or more component
managers;
* A Component Manager is a queue consumer and is responsible for the
initialization and destruction of its specific objects, by routing
the events to them;
* Each component manager owns a Queue;
* Each component manager runs in a different thread;
* One component manager may register itself as a listener for events
of a different component manager, meaning that all events
generated inside the other component manager will be sent to this
manager's queue;
* A component manager holds the reference to all objects that it
manages;
* A component manager fires events when objects are initialized and
destructed;
* The cross-thread identification of an object is its refaddr;
* A Compoment Manager implements "Inversion Of Control", similar to
Bread::Board;
* All methods called from the component manager to any individual
objects pass the component manager as an argument, similar to how
Catalyst pass the context variable;
* Events can be fired both by the Component Manager as by the
components themselves, part of the enqueued message is the refaddr
of the originating object. Objects registers themselves as
listeners of specific objects for specific event types and are
notified only for that events.
A sample flow of a pong game:
1 - Initialization
1.1 - The controller initializes, as part of its initialization,
three Component Managers are also initialized. a Model, a
View::Screen and a View::Audio.
1.2 - The controller connects the two view managers as listeners of the
model manager.
1.3 - The Model manager is connected to the controller itself;
1.4 - Now the controller goes to the each component's initialization.
1.4.1 - The model will load the game info and initialize the game
area, the two pedals, the ball, the collision checker and
the score checker.
1.4.2 - As soon as the model objects are created, events are fired
with that information. Both View::Screen and View::Audio
receives them.
1.4.3 - The View::Audio initalization will load the sounds to memory
and wait for events. When it receives the event about the
collision and score checkers creation, it initialize FX
objects that registers themselves as listeners to both
objects.
1.4.4 - The View::Screen manager will load initialize the sprites to
memory and when it receives the events of the pedals and
ball creation will initialize the specific sprites that will
be connected to the move events generated by them. A initial
rendering is dispatched.
1.4.5 - Both the view::screen and view::audio will block waiting for
new events.
1.4.6 - The model manager will consume the events in a non-blcking
fashion, doing SDL::delay for each iteration to implement
all the movement logic.
2 - In game:
2.1 - The controller keeps waiting for SDL events. When it receives
the keyup or keydown for the up and down keys for each of the
pedals (the control of the keymapping is the role of the
controller). The controller will turn key-down-up events into
command states (moveup, movedown) and for each change of
command state, it will fire an event.
2.2 - The model will receive the command state change, and change
the velocity of the pedal, which will start to move.
2.3 - As the timely evaluations of the model happens, "moved" events
are going to be fired. This events are going to be propagated
to the View::Screen manager, which will route to the pedal
sprite, which will reposition itself.
2.4 - The timely evaluation of the model will also do the collision
checking. When a collision happens, an event will be fired, so
that the View::Audio manager can route to the FX object of the
collision.
3 - Destruction:
3.1 - When the controller is being destructed, it will send a
destroy event for each component manager, which will finish
its loop and finish the thread.
3.2 - The controller then joins all threads started by it.
How the code should look like?
The best reference is certainly Bread::Board. The idea is that most of
the code will be declarative, so the component manager will basically
describe which components should be initialized immediatly, which
components should be created as response to the initialization of
objects in other managers.
There will be singleton objects, like Sprites and Sounds that need to
be initialized only once, and are them accesible to efemerous objects,
such as the "pedal view" or the "ball view" (ok, these are not
efemerous, but think about the bullet in the Zumbis game).