-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMANUAL.txt
335 lines (285 loc) · 12.3 KB
/
MANUAL.txt
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# MUGD - Manual
## Contents
1. Introduction
2. Game State stack
3. Game Objects
4. Rendering
5. Input
6. Audio
7. UI and other
# Introduction
This engine was built with the focus of establishing commonly used objects and structures that I use in my games. It is
built up from Gosu + box2D primarily with jsoncpp and boost providing extra functionality. It is not meant to be fully modular,
however some parts can be used separately.
The engine (or framework) itself is for 2D games, there is no 3D functionality and as of right now there are no plans to support
3D rendering. This is not to say that a 3D game cannot be made, it must however be rendered using 2D concepts.
Each of the managers follow certain rules and patterns. They all act as factories for the corresponding objects they manage.
They provide some query interface to get at the objects, usually via a string based name. They are designed to be configurable
but not require much more than a few callbacks after configuration to run. To handle having a single world to screen transformation,
a Camera class is used among them. This class is described in the UI and other section.
# Game State stack
Differing gameplay logic sets are separated via game states. These states are stored within the Core object on a stack. The
topmost state is the current state running. A lifecycle of a state is defined by the following callbacks:
void init();
void cleanup();
void pause();
void resume();
void update();
void draw() const;
A state is created using a standard constructor, but it is recommended to do all initialization inside the init callback. This
is paired with the cleanup callback where you should clean up any allocated memory.
A state can be paused in favor of giving focus to another state. The callback is provided in case you wish to do some temporary
memory cleanup. The resume callback allows you to reclaim any temporary memory needed.
The update and draw callbacks are the most important, as they are each called once per game tick (update first). This is where
your logic will reside. As per Gosu design, the draw is separate from update to promote good design and avoid sync issues
during render operations.
# Game Objects
The SceneGraph class provides a high level view of common scene management tasks. It is built for a component based system
which is implemented in the SceneObject and corresponding Component classes. This is carried over to the defaul level storage
schemas where a scene object is described in json format as an object composed of a list of components. An example of the
json format is shown below:
```javascript
"Objects" : [
{
"GroupName" : "Clouds_Background3",
"Children" : [
{
"Name" : "cloud1",
"Renderable" : {
"Type" : "Sprite",
"Layer" : -4,
"Image" : "Levels/cloud_1.png",
"ColorMod" : [255, 255, 255, 255],
"xScale" : 1.0,
"yScale" : 1.0
},
"Transform" : {
"Position" : [ 0.0, 0.0],
"Rotation" : 0.0
}
},
{...}
]
},
{...}
]
```
Here, you see the special component GroupComponent which is used for logical organization of scene objects. This will provide
a hierarchical structure which you can navigate at runtime. Most scene objects are described as the cloud object shown as a
child of the cloud group. Each object consists of an optional name and key:value pairs of components. Each component is named
and registered to a pluggable factory in the C++ code. Interally, a Group is a SceneObject with a GroupComponent, but the
data format allows a more succint description of this.
Physics components take care of holding a single box2d body with any number of shapes attached to it. The shapes and body
are configured via json as demonstrated below.
```javascript
"Physics" : {
"type" : "Dynamic",
"position" : [ 0.0, 0.0],
"angle" : 0.0,
"fixedRotation" : false,
"Shapes" : [
{
"type" : "Polygon",
"categoryBits" : 1,
"maskBits" : 61695,
"groupIndex" : 0,
"vertices" : [
[0.0, 0.0],
[1.0, 0.0],
[1.0, 1.0]
],
"position" : [ 0.0, 0.0],
"density" : 1.0,
"friction" : 1.0,
"restitution" : 1.0
},
{
"type" : "Rectangle",
"categoryBits" : 1,
"maskBits" : 61695,
"groupIndex" : 0,
"Width" : 0.0,
"Height" : 0.0,
"position" : [ 0.0, 0.0],
"density" : 1.0,
"friction" : 1.0,
"restitution" : 1.0
},
{
"type" : "Circle",
"categoryBits" : 1,
"maskBits" : 61695,
"groupIndex" : 0,
"radius" : 1.0,
"position" : [ 0.0, 0.0],
"density" : 1.0,
"friction" : 1.0,
"restitution" : 1.0
}
]
}
```
The body is given parameters to set the type (Dynamic, Kinematic or Static), position, angle, and if it has a fixed rotation. Fields
directly from box2d are given the same casing as box2d's API. There are three different shapes that can be described in the json
format. These are Polygons, Rectangles and Circles. Polygons accept an array verteces, squares a width and height, and circles
a radius.
Both RenderComponent and PhysComponent use a transform (position and rotation). The physics will often drive this transform
while the renderable will only read from it. So, as long as a TransformComponent is involved, the PhysComponent will write to it
and the RenderComponent will read from it. It is suggested that you use the TransformComponent whenever possible in such a
way.
To create a new component, you need to subclass two different classes, Component and Component_maker. A Component is
defined as follows:
```cpp
class Component
{
public:
Component() {}
Component(SceneObject *_obj) { m_Obj = _obj; }
//Returns the name of this component type
virtual std::string name() = 0;
//logic update callback
virtual void update() = 0;
/// Physics callback
virtual void onColStart(b2Fixture *_fix, SceneObject *_other, b2Manifold _manifold) = 0;
virtual void onColFinish(b2Fixture *_fix, SceneObject *_other, b2Manifold _manifold) = 0;
/// Message passing
virtual void onMessage(std::string _message) = 0;
//Serialization/Deserialization
virtual void encodeWith(Json::Value *_val) = 0;
virtual void initWith(Json::Value _val) = 0;
protected:
SceneObject* m_Obj;
};
```
Where name would return a string of whatever you wish to refer to this component type as. This must be the same as what you
use for the maker. The maker is a pluggable factory that gets registered with the base class. Below is the code for the physics
component as an example.
```cpp
/* in header file */
class Physcom_maker : public Component_maker
{
public:
Physcom_maker() : Component_maker("Physics") {}
protected:
Component* makeComponent(SceneObject *_obj);
static Physcom_maker s_RegisteredMaker;
};
/* in cpp file */
Physcom_maker Physcom_maker::s_RegisteredMaker;
Component* Physcom_maker::makeComponent(SceneObject *_obj)
{
return new PhysComponent(_obj);
}
```
#Rendering
The RenderManager class gives a clean interface to most rendering operations that you might desire. It does this by providing
an abstract Renderable class which you may implement or simply use one of the pre-built classes. The Renderable base class
has a update and draw methods on it. Here, draw is the most important but for some cases update is needed. The built in
renderables provide good examples of implementation. Layers are used to manage draw order. Gosu uses doubles for the layer
attribute, but here we use integer. This is to reserve some order management to the render manager itself. Each supplied
renderable is supported in the RenderComponent and the json formats are described below along with descriptions.
## Sprite
A sprite is a single image that can be displayed anywhere in the world. It has the default values from Renderable (position,
zoom, rotation, layer) plus a visible flag, center position, and scaling factors. The json format is as follows with default values
shown.
```javascript
"Renderable" : {
"Type" : "Sprite",
"Layer" : 0,
"Image" : "defaultimg.png",
"ColorMod" : [255, 255, 255, 255],
"xScale" : 1.0,
"yScale" : 1.0,
"CenterX" : 0.0,
"CenterY" : 0.0
}
```
## SpriteSheet
A sprite sheet is a collection of sprites. They are collected from a single image with a uniform grid of sprites. This class makes
use of the update method for animation.
```javascript
"Renderable" : {
"Type" : "SpriteSheet",
"Layer" : 0,
"Width" : 0,
"Height" : 0,
"Duration" : 20,
"Image" : "defaultimg.png",
"ColorMod" : [255, 255, 255, 255],
"xScale" : 1.0,
"yScale" : 1.0
}
```
## MessageBubble
meh
# Input
The InputManager provided supports recognition of three types of input patterns. These patterns are called Action, Chord and
Sequence. Each are named to keep from having to hardcode any inputs and tie yourself to a single configuration. Inputs are
not unique so you may have several names correspond to the same inputs. You can also have multiple names for different
inputs, but this is not recommended.
```javascript
ButtonMaps" : [
{
"Action" : "Game.Quit",
"Key" : "kbEscape",
"Type" : "action"
},
{
"Action" : "Menu.Select",
"Keys" : ["kbEnter", "kbSpace", "kbReturn"],
"Type" : "action"
},
{
"Chord" : "Play.PowerAttack",
"Threshold" : 200,
"Keys" : ["kbShift", "kbA"],
"Type" : "chord"
},
{
"Sequence" : "Play.Cheat1",
"Threshold" : 400,
"Keys" : ["kbUp", "kbUp", "kbDown", "kbDown", "kbLeft", "kbRight", "kbLeft", "kbRight", "kbB", "kbA"],
"Type" : "sequence"
}
]
```
An **Action** is simply a named button, this is useful for most applications where you simply need to detect the state of a
particular button. An single action may be mapped to several buttons, each will generate the desired states for the button
presses.
A **Chord** is a set of buttons that must be pressed at once. There is a default timeframe of 1/5 of a second (200 ms) where each
key must be hit after the first key in the set is pressed. It does not matter which key is pressed first, but they must all be
pressed within that time frame for the chord to be triggered.
A **Sequence** is just as it sounds, a sequence of buttons. The threshold parameter determines the maximum time in milliseconds
that can pass between button presses for the sequence to remain in process, default is 400. Once the last button is pressed, the sequence will
become active and when all buttons are released it will become idle.
Each of these can be queried through a single interface (so be careful of name clashes). The return value is one of six button
states. An invalid state means there is no input by that name, idle means it is not active, process means the chord or sequence
is currently evaluating a potential match, begin means the input just became active (press), active means it is being held, and finish
means it was just released. The begin and finish states are what you need to check for if you wish to evaluate your inputs as events
and not plain button states.
```cpp
enum actionState {
actnInvalid = -1,
actnIdle = 0,
actnProcess,
actnBegin,
actnActive,
actnFinish
}
```
# Audio
The AudioManager handles driving long term and short term sounds. There are two types of sounds, Song and Sample similar
to Gosu. Samples can be played either as ambient sounds, or as stereo sounds. When played as stereo, they are given a world
coordinate to play from and the balance is determined within the manager to give the impression that the sound is coming from
that point. Both are referenced by a name.
# UI + other
There is a set of UI objects packaged with the framework. It gives functionality for some basic UI features like buttons, textboxes,
and draggable windows. A UI is built by creating a UIContainer, either UISheet or UIWindow. A sheet will take up the whole screen
without drawing anything, so you may play UI components anywhere. A window will create a sized (resizing is currently not
supported) window that can be dragged by the mouse and closed via a close button. Either container allows you to add UI
objects to them via a paged interface. This means that you may define a default page full of components, and a secondary page
that you can switch between at runtime.
-creating objects example-
The different object managers listed above each have some functionality that requires mapping screen coordinates to game world
coordinates. To faciliate this, there is a Camera class that can be implemented to define that transformation. A parallax camera
is provided to do full parallax rendering (zoom and rotation).