Subversion Repositories AndroidProjects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
244 chris 1
#include "Board.h"
2
#include "GameApp.h"
3
#include "SexyAppFramework/Graphics.h"
4
 
5
// See the Draw method for more information on using the Color class.
6
#include "SexyAppFramework/Color.h"
7
 
8
// The Image.h file just declares basic functions. All images are either of 
9
// the DDImage or MemoryImage type. For this demo, we will use DDImage
10
// types, as they are the type returned by the image loading code.
11
// A DDImage is actually derived from MemoryImage, so where an Image or
12
// MemoryImage is required, a DDImage will suffice as well. A DDImage
13
// contains optimized code for use with DirectX 7+.
14
#include "SexyAppFramework/DDImage.h"
15
 
16
// The Rectangle template, used to specify X, Y, Width, Height
17
#include "SexyAppFramework/Rect.h"
18
 
19
// We're going to be making a button in this demo so we need to
20
// include this file.
21
#include "SexyAppFramework/ButtonWidget.h"
22
 
23
// We're going to add our own button widget, which requires knowing about the
24
// WidgetManager.
25
#include "SexyAppFramework/WidgetManager.h"
26
 
27
#include "SexyAppFramework/ImageFont.h"
28
 
29
// The SexyAppFramework resides in the "Sexy" namespace. As a convenience,
30
// you'll see in all the .cpp files "using namespace Sexy" to avoid
31
// having to prefix everything with Sexy::
32
using namespace Sexy;
33
 
34
//////////////////////////////////////////////////////////////////////////
35
//////////////////////////////////////////////////////////////////////////
36
Board::Board(GameApp* theApp)
37
{
38
        mApp = theApp;
39
 
40
        // Start off drawing the first frame of mLightningImg
41
        mAnimFrame = 0;
42
 
43
        mButton = NULL;
44
 
45
        mMouseX = mMouseY = 0;
46
        mLeftDown = mRightDown = mMiddleDown = false;
47
}
48
 
49
//////////////////////////////////////////////////////////////////////////
50
//////////////////////////////////////////////////////////////////////////
51
Board::~Board()
52
{
53
        delete mButton;
54
}
55
 
56
//////////////////////////////////////////////////////////////////////////
57
//////////////////////////////////////////////////////////////////////////
58
void Board::Update()
59
{
60
        // Let the parent class update as well. This will increment
61
        // the variable mUpdateCnt which is an integer that indicates 
62
        // how many times the Update() method has been called. Since our
63
        // Board class is updated 100 times per second, this variable will
64
        // increment 100 times per second. As you will see in later demos,
65
        // we will use this variable for animation since its value represents
66
        // hundredths of a second, which is for almost all games a good
67
        // enough timer value and doesn't rely on the system clock function
68
        // call.
69
        Widget::Update();
70
 
71
        // Let's update our animation frame every 5 update ticks. This
72
        // is equivalent to 20 times per second.
73
        if (mUpdateCnt % 5 == 0)
74
        {
75
                // In GameApp we specified how many rows and columns this image
76
                // had. Thus, if our current frame of animation exceeds the number
77
                // of frames that we have, we should reset the animation frame back
78
                // to 0.
79
                if (++mAnimFrame >= mApp->mLightningImg->mNumRows)
80
                        mAnimFrame = 0;
81
        }
82
 
83
 
84
 
85
        // For this and most of the other demos, you will see the function
86
        // below called every Update() call. MarkDirty() tells the widget
87
        // manager that something has changed graphically in the widget and
88
        // that it needs to be repainted. All widgets follow this convention.
89
        //              In general, if you don't need
90
        // to update your drawing every time you call the Update method
91
        // (the most common case is when the game is paused) you should
92
        // NOT mark dirty. Why? If you aren't marking dirty every frame,
93
        // then you aren't drawing every frame and thus you use less CPU
94
        // time. Because people like to multitask, or they may be on a laptop
95
        // with limited battery life, using less CPU time lets people do
96
        // other things besides play your game. Of course, everyone
97
        // will want to play your game at all times, but it's good to be
98
        // nice to those rare people that might want to read email or
99
        // do other things at the same time. 
100
        //              In this particular demo, we
101
        // won't be nice, as the purpose is to bring you up to speed as
102
        // quickly as possible, and so we'll dispense with optimizations
103
        // for now, so you can concentrate on other core issues first.
104
        //              In general, this is the last method called in the Update
105
        // function, but that is not necessary. In fact, the MarkDirty
106
        // function can be called anywhere, in any method (although
107
        // calling it in the Draw method doesn't make sense since it is
108
        // already drawing) and even multiple times. Calling it multiple
109
        // times does not do anything: only the first call makes a difference.
110
        MarkDirty();
111
}
112
 
113
//////////////////////////////////////////////////////////////////////////
114
//////////////////////////////////////////////////////////////////////////
115
void Board::Draw(Graphics* g)
116
{
117
        // The Graphics object, "g", is 
118
        // automatically created and passed to this method by the 
119
        // WidgetManager and can be thought of as the main screen 
120
        // bitmap/canvas upon which all drawing will be done. This object
121
        // is double buffered automatically so you don't need to worry
122
        // about those details. All you need to do is instruct the object
123
        // that you would like to draw something to it, and when the
124
        // WidgetManager gets done letting all widgets draw to the
125
        // Graphics object, it will then blit everything to the screen
126
        // at once. 
127
 
128
        // First, let's start by clearing the screen to black. 
129
        // As you'll recall from Demo1, we set the color with SetColor
130
        // and pass in a Color object that contains either 3 or 4 parameters,
131
        // that represent the r,g,b,a values (alpha is 255 if only 3 specified).
132
        g->SetColor(Color(0, 0, 0));
133
        g->FillRect(0, 0, mWidth, mHeight);
134
 
135
        // Now let's try drawing a stretched image. We'll draw the original image
136
        // stretched to twice its size. Drawing a stretched image is exactly like
137
        // drawing a normal image, except that you have two extra parameters:
138
        // the stretched width and height. You can use this to draw a shrunk version
139
        // of the image as well (which we'll do second) 
140
        g->DrawImage(mApp->mTurbotImg, 0, 0, mApp->mTurbotImg->GetWidth() * 2, mApp->mTurbotImg->GetHeight() * 2);
141
        g->DrawImage(mApp->mTurbotImg, 0, 275, mApp->mTurbotImg->GetWidth() / 2, mApp->mTurbotImg->GetHeight() / 2);
142
 
143
        // The default stretching algorithm (the one used in the two examples above) uses
144
        // the slow stretching method. The slow stretching method anti-aliases the resulting
145
        // image to give a smoother, less pixelated look. While this tends to look a lot
146
        // nicer, it also requires more processing power. Thus, if you either don't need
147
        // or don't care about the anti-aliasing, you can instruct the graphics object
148
        // to use fast stretching. Just remember: unless you turn fast stretching off when
149
        // done, it will remain on. Let's give it a try by drawing the same image 
150
        // stretched twice as large using fast stretching.
151
        g->SetFastStretch(true);
152
        g->DrawImage(mApp->mTurbotImg, 275, 0, mApp->mTurbotImg->GetWidth() * 2, mApp->mTurbotImg->GetHeight() * 2);
153
        g->SetFastStretch(false);
154
 
155
        // A good use of fast/non fast stretching is to enable the slower stretching method
156
        // for users with a supported 3D card and to disable it for users that have
157
        // to run in software mode. You'll learn more about determining if a user is in
158
        // 3D mode or not in a later demo, but keep this simple optimization in mind
159
        // when creating drawing code for your games.
160
 
161
        // Another cool thing we can do is to draw an image mirrored. This is exactly
162
        // like using the DrawImage command except that you use DrawImageMirror
163
        // instead. The optional fourth argument is just a convenience switch: if it's false,
164
        // it will draw the image normally. In 3D mode, this is just as fast as a normal draw.
165
        g->DrawImageMirror(mApp->mTurbotImg, 75, 275);
166
 
167
        // But what if you want to draw the image mirrored AND stretched? It's a little different,
168
        // but still easy. The first parameter, like usual, is the image you want to draw.
169
        // The second parameter is a Rect which indicates the destination region you want to draw
170
        // the image to: The XY of the Rect is just what you think it is, it's the pixel coordinate
171
        // the image should be drawn at. The Width, Height of the Rect however is how you accomplish
172
        // the stretching/shrinking. Specify a different width/height from the original image size
173
        // and you've just accomplished resizing the original image! Note that we have a third
174
        // parameter, also a Rect. This is the source rectangle, and indicates the region of
175
        // image you wish to draw. As you'll see in our animation example, you don't have to
176
        // always draw the entire image. You can specify a rectangular region of the source image
177
        // that you wish to draw, which is useful for large strips of animation (more below).
178
        // For our current case, however, we want to draw the entire image. So we specify that
179
        // we should draw from the top left (0,0) coordinate of the source image and we should
180
        // use it's full width/height. Again, just like with DrawImage, you can use the
181
        // SetFastStretch call to set whether you want the nice, slow, smooth scaling, or the quick
182
        // and efficient exact scaling. In 3D mode, this is just as fast as a normal stretched draw.
183
        g->DrawImageMirror(mApp->mTurbotImg,
184
                                                Rect(200, 275, mApp->mTurbotImg->GetWidth() * 2, mApp->mTurbotImg->GetHeight() * 2),
185
                                                Rect(0, 0, mApp->mTurbotImg->GetWidth(), mApp->mTurbotImg->GetHeight()));
186
 
187
 
188
        // Remember that black and white image we made in GameApp::LoadingThreadCompleted?
189
        // How about we draw it now so you can see what the result looks like:
190
        g->DrawImage(mApp->mAlteredImg, 500, 0);
191
 
192
        // And now for the exciting part: animation! As you can see in ::Update, we
193
        // increment the animation frame every 5 update ticks. We don't want to use
194
        // DrawImage because that will render the entire image. Instead, we only
195
        // want to draw a particular cel. We do that with the DrawImageCel function,
196
        // specifying the row or column to draw like so:
197
        g->DrawImageCel(mApp->mLightningImg, 0, 540, mAnimFrame);
198
 
199
        // If your animation strips contain both multiple rows AND columns,
200
        // you will need to use one of the alternate forms of DrawImageCel.
201
        // DrawImageCel is really just a convenience wrapper around DrawImage.
202
        // As you may have seen, you can tell DrawImage to draw just a particular
203
        // rectangular region of the image. Here is the equivalent function
204
        // call that we could have used in place of DrawImageCel above:
205
        //g->DrawImage(mApp->mLightningImg, 
206
        //Rect(0, 540, mApp->mLightningImg->GetWidth(), mApp->mLightningImg->GetCelHeight()),
207
        //Rect(0, mApp->mLightningImg->GetCelHeight() * mAnimFrame, mApp->mLightningImg->GetWidth(), mApp->mLightningImg->GetCelHeight()));
208
        //
209
        // As you can see, DrawImageCel is a lot quicker to type. 
210
 
211
        // Let's also display the current mouse XY and which button(s) are held down.
212
        // You should recall how to set fonts and change their colors from Demo2.
213
        // You will notice that the X, Y is not updated when the cursor moves over
214
        // the button that we added. This can be explained by reading the comments
215
        // for the MouseMove/MouseDrag/MouseDown/MouseUp methods.
216
        g->SetFont(mApp->mFont);
217
        g->SetColor(Color(255, 255, 255));
218
        g->DrawString(StrFormat(_S("X, Y is %d, %d"), mMouseX, mMouseY), 630, 20);
219
 
220
        SexyString buttonStr;
221
        if (mLeftDown)
222
                buttonStr += _S("Left button is down. ");
223
        if (mRightDown)
224
                buttonStr += _S("Right button is down. ");
225
        if (mMiddleDown)
226
                buttonStr += _S("Middle button is down. ");
227
 
228
        WriteWordWrapped(g, Rect(630, 40, mWidth - 630, 300), buttonStr, -1, -1);
229
 
230
}
231
 
232
 
233
//////////////////////////////////////////////////////////////////////////
234
//////////////////////////////////////////////////////////////////////////
235
void Board::AddedToManager(WidgetManager* theWidgetManager)
236
{
237
        // At this point, the Board class has already been added to the
238
        // widget manager. We should call our parent class' method
239
        // so that it can be sure to perform any needed tasks, first.
240
        Widget::AddedToManager(theWidgetManager);
241
 
242
        // Now let's create our first widget: a button. We do that by
243
        // telling the button what integer ID it should be identified with,
244
        // and by passing it a pointer to a button listener. Widgets have
245
        // "listeners" which respond to their particular events. Any class
246
        // can be a listener. For this particular demo, it is convenient for the
247
        // Board class to be the listener, but it is also common to use GameApp
248
        // as the main listener for all application buttons. The choice is up
249
        // to you and your needs.
250
        mButton = new ButtonWidget(1, this);
251
 
252
        // Remember how we had to position and size the Board class when we first
253
        // created it? A button is no different, it too is a widget. Let's
254
        // place this button on screen and set it's size now:
255
        mButton->Resize(675, 500, 100, 50);
256
 
257
        // Because a button can have a label on it, we should set the font to use:
258
        mButton->SetFont(mApp->mFont);
259
 
260
        // And just what should that label be? How about the word "Off".
261
        // We'll make it so that when it's clicked, it changes to "On" and
262
        // back to "Off" again.
263
        mButton->mLabel = _S("Off");
264
 
265
        // We can also change some colors, like the label color and the color
266
        // the label gets when moused over using the constants below:
267
        mButton->SetColor(ButtonWidget::COLOR_LABEL, Color(0, 0, 0));
268
        mButton->SetColor(ButtonWidget::COLOR_LABEL_HILITE, Color(0, 255, 0));
269
 
270
        // And finally, just like with the Board class, we have to add it
271
        // if we want to do anything with it.
272
        theWidgetManager->AddWidget(mButton);
273
 
274
        // If you want, you can also control the placement of the button.
275
        // You can put it in front of another widget, behind another widget,
276
        // at the top of the drawing order, or at the bottom of the
277
        // drawing order. You accomplish that with the WidgetManager's
278
        // PutInFront, PutBehind, BringToFront, BringToBack methods, respectively.
279
 
280
        // But wait, what does this button look like? If you don't specify an
281
        // image to use, the default Windows widget look will be used. We'll cover
282
        // images and widgets in a later demo.
283
}
284
 
285
//////////////////////////////////////////////////////////////////////////
286
//////////////////////////////////////////////////////////////////////////
287
void Board::RemovedFromManager(WidgetManager* theWidgetManager)
288
{
289
        // This is called after we've been removed from the widget manager.
290
        // Again, we should let our base class do anything it needs to, first.
291
        Widget::RemovedFromManager(theWidgetManager);
292
 
293
        // We should now also remove any widgets we are responsible for. In
294
        // our current case, we made a button in AddedToManager. Let's remove
295
        // it now:
296
        theWidgetManager->RemoveWidget(mButton);
297
 
298
        // We'll delete it in our destructor.
299
}
300
 
301
//////////////////////////////////////////////////////////////////////////
302
//////////////////////////////////////////////////////////////////////////
303
void Board::ButtonDepress(int theId)
304
{
305
        // Because we told our button that we, the Board class, are 
306
        // going to listen for its particular events, this method will 
307
        // thus be called when our button has any events it wants us
308
        // to know about. In our current case, we only want to know when
309
        // the button is clicked, but you could also respond to a few others.
310
        // Let's change the label on our button whenever it is clicked, from
311
        // "Off" to "On" and back again.
312
        if (theId == 1)
313
        {
314
                // We assigned ID of 1 to our button. You'd ideally want to use
315
                // a constant, but since this is a demo and there's only 1 button,
316
                // we're going to just use a literal instead. When a button
317
                // causes an action, it calls the appropriate listener function and
318
                // let's the listener know who it is via the ID parameter.
319
                if (mButton->mLabel == _S("Off"))
320
                        mButton->mLabel = _S("On");
321
                else
322
                        mButton->mLabel = _S("Off");
323
        }
324
}
325
 
326
//////////////////////////////////////////////////////////////////////////
327
//////////////////////////////////////////////////////////////////////////
328
void Board::MouseMove(int x, int y)
329
{
330
        // This is called because the mouse cursor changed position and
331
        // the Board class was the widget closest to the cursor.
332
        // We're going to keep track of the XY coordinate whenever we
333
        // get this message for the sake of this demo.
334
        mMouseX = x;
335
        mMouseY = y;
336
}
337
 
338
//////////////////////////////////////////////////////////////////////////
339
//////////////////////////////////////////////////////////////////////////
340
void Board::MouseDrag(int x, int y)
341
{
342
        // This is called because the mouse cursor changed position  while
343
        // a button was down (a drag) and
344
        // the Board class was the widget closest to the cursor.
345
        // We're going to keep track of the XY coordinate whenever we
346
        // get this message for the sake of this demo.
347
        // Note that MouseDrag is called instead of MouseMove under
348
        // this condition.
349
        mMouseX = x;
350
        mMouseY = y;
351
}
352
 
353
//////////////////////////////////////////////////////////////////////////
354
//////////////////////////////////////////////////////////////////////////
355
void Board::MouseDown(int x, int y, int theClickCount)
356
{
357
        // Let the parent class know about this and perform any actions
358
        // it needs to.
359
        Widget::MouseDown(x, y, theClickCount);
360
 
361
        // Let's just keep track of which button is currently held down. 
362
        if (theClickCount == 3)
363
                mMiddleDown = true;
364
        else if (theClickCount > 0)
365
                mLeftDown = true;
366
        else if (theClickCount < 0)
367
                mRightDown = true;
368
}
369
 
370
//////////////////////////////////////////////////////////////////////////
371
//////////////////////////////////////////////////////////////////////////
372
void Board::MouseUp(int x, int y, int theClickCount)
373
{
374
        // Let the parent class know about this and perform any actions
375
        // it needs to.
376
        Widget::MouseUp(x, y, theClickCount);
377
 
378
        //You can actually tell if the left, right,
379
        // or middle buttons are currently held down by calling one of these
380
        // WidgetManager methods: IsLeftButtonDown, IsRightButtonDown, 
381
        // IsMiddleButtonDown. However, we're going to keep track of this
382
        // manually just to illustrate a point.
383
        if (theClickCount == 3)
384
                mMiddleDown = false;
385
        else if (theClickCount > 0)
386
                mLeftDown = false;
387
        else if (theClickCount < 0)
388
                mRightDown = false;
389
}