summaryrefslogtreecommitdiff
path: root/osframework/source/demos/Demo2/Board.cpp
blob: 4bc7f4ac7bd189fcf160d69a2579b67c36e1e776 (plain)
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
#include "Board.h"
#include "GameApp.h"
#include "SexyAppFramework/Graphics.h"

// See the Draw method for more information on using the Color class.
#include "SexyAppFramework/Color.h"

// Why are we including ImageFont.h and not Font.h? Font.h is just a generic
// base class. ImageFont creates fonts from an image that contains all the
// text characters as well as a text file that indicates character widths
// and kerning information, as well as some more advanced features not used
// in this tutorial such as font layers, etc.
#include "SexyAppFramework/ImageFont.h"

// The Image.h file just declares basic functions. All images are either of 
// the DDImage or MemoryImage type. For this demo, we will use DDImage
// types, as they are the type returned by the image loading code.
// A DDImage is actually derived from MemoryImage, so where an Image or
// MemoryImage is required, a DDImage will suffice as well. A DDImage
// contains optimized code for use with DirectX 7+.
#include "SexyAppFramework/DDImage.h"

// The Rectangle template, used to specify X, Y, Width, Height
#include "SexyAppFramework/Rect.h"

// The SexyAppFramework resides in the "Sexy" namespace. As a convenience,
// you'll see in all the .cpp files "using namespace Sexy" to avoid
// having to prefix everything with Sexy::
using namespace Sexy;

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
Board::Board(GameApp* theApp)
{
	mApp = theApp;

	// Start by fully glowing and making the pulse decrease
	mPulseAmt = 255;
	mIncPulse = false;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
Board::~Board()
{
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Board::Update()
{
	// Let the parent class update as well. This will increment
	// the variable mUpdateCnt which is an integer that indicates 
	// how many times the Update() method has been called. Since our
	// Board class is updated 100 times per second, this variable will
	// increment 100 times per second. As you will see in later demos,
	// we will use this variable for animation since its value represents
	// hundredths of a second, which is for almost all games a good
	// enough timer value and doesn't rely on the system clock function
	// call.
	Widget::Update();

	// Every frame (100 times per second) let's change the pulse amount by
	// 1. When we reach either 0 or 255 (the lower/upper bounds of
	// a color value) we'll reverse the pulsing direction. See the Draw()
	// method for its usage: we're going to draw an image with varying intenstiy
	// over time.
	if (mIncPulse)
	{
		if (++mPulseAmt >= 255)
		{
			mIncPulse = false;
			mPulseAmt = 255;
		}
	}
	else
	{
		if (--mPulseAmt <= 0)
		{
			mIncPulse = true;
			mPulseAmt = 0;
		}
	}

	// Let's play the timer sound effect once every 2 second.
	// Since this method is called 100 times per second, just mod
	// mUpdateCnt by 200. We do that by calling GameApp's PlaySample()
	// method and specifying the integral ID of the sound to play. This
	// ID is the same as we used when loading the sound in GameApp::LoadingThreadProc()
	if (mUpdateCnt % 200 == 0)
		mApp->PlaySample(1);

	// What about playing a sound but making it play on either the left or
	// right speaker? And what about pitch shifting? These are all possible,
	// but we won't learn about them until a later demo.



	// For this and most of the other demos, you will see the function
	// below called every Update() call. MarkDirty() tells the widget
	// manager that something has changed graphically in the widget and
	// that it needs to be repainted. All widgets follow this convention.
	//		In general, if you don't need
	// to update your drawing every time you call the Update method
	// (the most common case is when the game is paused) you should
	// NOT mark dirty. Why? If you aren't marking dirty every frame,
	// then you aren't drawing every frame and thus you use less CPU
	// time. Because people like to multitask, or they may be on a laptop
	// with limited battery life, using less CPU time lets people do
	// other things besides play your game. Of course, everyone
	// will want to play your game at all times, but it's good to be
	// nice to those rare people that might want to read email or
	// do other things at the same time. 
	//		In this particular demo, we
	// won't be nice, as the purpose is to bring you up to speed as
	// quickly as possible, and so we'll dispense with optimizations
	// for now, so you can concentrate on other core issues first.
	//		In general, this is the last method called in the Update
	// function, but that is not necessary. In fact, the MarkDirty
	// function can be called anywhere, in any method (although
	// calling it in the Draw method doesn't make sense since it is
	// already drawing) and even multiple times. Calling it multiple
	// times does not do anything: only the first call makes a difference.
	MarkDirty();
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Board::Draw(Graphics* g)
{
	// And now for the good stuff! The Graphics object, "g", is 
	// automatically created and passed to this method by the 
	// WidgetManager and can be thought of as the main screen 
	// bitmap/canvas upon which all drawing will be done. This object
	// is double buffered automatically so you don't need to worry
	// about those details. All you need to do is instruct the object
	// that you would like to draw something to it, and when the
	// WidgetManager gets done letting all widgets draw to the
	// Graphics object, it will then blit everything to the screen
	// at once. 

	// First, let's start by clearing the screen to black. 
	// As you'll recall from Demo1, we set the color with SetColor
	// and pass in a Color object that contains either 3 or 4 parameters,
	// that represent the r,g,b,a values (alpha is 255 if only 3 specified).
	g->SetColor(Color(0, 0, 0));
	g->FillRect(0, 0, mWidth, mHeight);

	// Now let's print some text. In order to do so, we need to instruct the
	// graphics object as to which font we want to use. We do that with
	// the SetFont method. Then we call the DrawString method and pass in
	// a normal string and give it an X, Y to draw at. But wait! If we just
	// draw a string right now, we won't see it. Can you guess why?
	// The graphics object is still using the color, white, which we
	// set a few lines above. Thus, when it goes to draw the string,
	// it will draw it in white. How do you change the text color?
	// Easy: it's just like you learned for drawing those primitives:
	// just use SetColor and you're done! It doesn't matter whether you
	// set the color before or after you set the font, fyi.
	g->SetColor(Color(255, 0, 0));
	g->SetFont(mApp->mTextFont);
	g->DrawString(_S("Woo! Text! Ya!"), 10, 10);

	// Wait a minute, didn't we say to draw at a Y value of 10? Then why does it 
	// appear to be drawing at about 0? That's because the text is offset
	// by the ascent of the font. A discussion of font terms is beyond the
	// scope of this demo, but basically just remember that if you want to
	// display the text at the exact Y coordinate, that you should add
	// the value of the font's ascent. You can get the ascent by calling
	// GetAscent() on the font in question. I'm going to use the same
	// Y value of 10 just to show you the slight difference using GetAscent makes.
	g->SetColor(Color(55, 90, 255));
	// We already set the font, no need to set it again unless we want a new one
	g->DrawString(_S("I like Techno and Drum n' Bass"), 170, 10 + mApp->mTextFont->GetAscent());

	// And to help you out visually, let's draw a line at Y coordinate 10:
	g->SetColor(Color(255, 255, 255));
	g->DrawLine(0, 10, mWidth, 10);

	// Just for fun, let's change the font. This font, as you can see from its image, 
	// only contains digits and a few symbols. As an example, I will draw a string
	// containing only those characters as well as a string containing characters that
	// the font can't represent to show you what would happen.
	g->SetFont(mApp->mNumberFont);
	g->SetColor(Color(255, 255, 0));
	g->DrawString(_S("+200"), 10, 40);
	g->DrawString(_S("+200 pts"), 10, 60);

	// You can also get the width of a string in pixels for any given font. In addition, you
	// can use a printf style function, StrFormat, which is defined in Common.h, to 
	// format strings. You might want to look at Common.h for some...common...and handy functions.
	g->SetColor(Color(0, 255, 255));
	SexyString myString = StrFormat(_S("You got %d points!"), 147);
	g->SetFont(mApp->mTextFont);
	g->DrawString(myString, 10, 80);
	g->SetColor(Color(0, 255, 0));
	g->DrawString(_S(" I am to the right of that previous string."), 10 + mApp->mTextFont->StringWidth(myString), 80);
	

	// What about some other common text functions, like justifying and word wrapping? 
	// Not a problem. The Widget class contains some useful features.
	// For justification, the easiest is to use the WriteString method. 
	// The valid justification values are:
	//	-1: Left justified (same as DrawString)
	//	0: Centered
	//	1: Right justified
	//	
	//	For centered/right justified, you can also specify a width indicating the size of the region you
	//	are printing in. The sole purpose of that is to control the centering/right aligning. 
	g->SetColor(Color(255, 255, 255));
	WriteString(g, _S("Left justified at X of 200"), 200, 95, -1, -1);
	WriteString(g, _S("Centered using app width"), 0, 110, mWidth, 0);
	WriteString(g, _S("Centered using width of 200, X of 200"), 200, 125, 200, 0);

	// And now for a word wrapping example. With this function, you specify a rectangular region
	// that represents the "box" in which you want the text to be displayed. You can also
	// set the line spacing, or use -1 for the default font height, and you can also set
	// the justification using the same values as for WriteString. We'll also draw the "box" using
	// the DrawRect function, for illustrative purposes. You may notice that I didn't offset the
	// Y coordinate of the text by the font's ascent: that's because WriteWordWrapped automatically
	// does that for you.
	g->SetColor(Color(255, 255, 255));
	g->DrawRect(30, 140, 200, 400);
	g->SetColor(Color(0, 255, 0));
	WriteWordWrapped(g, Rect(30, 140, 200, 400), _S("This is some text that is wrapped inside of the rectangle \
												at X of 30, Y of 140, width of 200, height of 400. It has \
												been wrapped using the default left justification and the \
												default line spacing."), -1, -1);

	////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// Enough talk, let's draw images!
	// The simplest way to draw an image is with, coincidentally, the DrawImage method.
	// Just specify the image and the X, Y to draw at and boom! It's done.
	g->DrawImage(mApp->mTurbotImg, 230, 140);

	// What about changing the color of an image? This is called colorization, and it works just like
	// all the previous examples: You call SetColor with the color you want. But wait! There's 
	// an extra step: Because colorizing an image on the fly is slower than normal drawing, you have
	// to explicity tell the graphics object that you want to enable it. This is done by calling the
	// SetColorizeImages method of the Graphics class with a parameter value of "true".
	// IMPORTANT: You should call SetColorizeImages again but with a parameter of "false" when you
	// are done drawing your colorized image! Otherwise all subsequent image drawing calls will
	// be colorized using the most recent value from SetColor. You may call SetColor either before
	// or after SetColorizeImages, it doesn't matter. For this example, we'll draw an image normally
	// and then colorized. We'll also use the GetHeight() method to draw this image just below the
	// previous one.
	g->DrawImage(mApp->mOpaqueBeamImg, 230, 140 + mApp->mTurbotImg->GetHeight());

	g->SetColor(Color(255, 0, 0));
	g->SetColorizeImages(true);
	g->DrawImage(mApp->mOpaqueBeamImg, 230 + mApp->mOpaqueBeamImg->GetWidth(), 140 + mApp->mTurbotImg->GetHeight());
	g->SetColorizeImages(false);

	// You may have noticed that part of the previous two images was drawn offscreen. The graphics object by
	// default sets a clipping region to the size of the widget, and anything drawn outside of it
	// is clipped, or not drawn. You'll see some more advanced uses of clipping and widgets in a later demo.

	// There's another cool effect you can do to images: Drawing additively instead of normally.
	// Additive drawing is just what the name implies: when you draw the image, instead of
	// drawing using the exact RGBA values of the image, it actually combines them with whatever
	// pixel values there are in the location in which it is placed. One common use of this is
	// to draw the same image, but brighter: To do so, you'd just draw the image normally first,
	// then draw the same image on top of it but additively. Let's try an example and draw
	// our first image, mApp->mTurbotImg, additively to get a bright effect. 
	g->DrawImage(mApp->mTurbotImg, 230 + mApp->mTurbotImg->GetWidth(), 140);

	// Enable additive drawing with the call below. IMPORTANT: You should set the
	// drawmode back to Graphics::DRAWMODE_NORMAL when done, so that other images
	// don't get drawn additively. Additive drawing is slower than colorization,
	// so keep that in mind if you are concerned about your game's performance.
	g->SetDrawMode(Graphics::DRAWMODE_ADDITIVE);	
	g->DrawImage(mApp->mTurbotImg, 230 + mApp->mTurbotImg->GetWidth(), 140);
	g->SetDrawMode(Graphics::DRAWMODE_NORMAL);

	// You can even draw an image colorized and additively. This is comonly used to
	// implement a pulsing sort of effect. Let's do just that: the variable,
	// mPulseAmt, is updated in our Update method and controls the RGB value
	// that we'll use to make our image pulse. Again, be sure to set things back
	// to normal when you are done. Also, it matters not in which order the
	// SetDrawMode, SetColor, and SetColorizeImages calls are made.
	g->DrawImage(mApp->mTurbotImg, 230 + mApp->mTurbotImg->GetWidth() * 2, 140);

	g->SetDrawMode(Graphics::DRAWMODE_ADDITIVE);	
	g->SetColor(Color(mPulseAmt, mPulseAmt, mPulseAmt));
	g->SetColorizeImages(true);
	g->DrawImage(mApp->mTurbotImg, 230 + mApp->mTurbotImg->GetWidth() * 2, 140);
	g->SetDrawMode(Graphics::DRAWMODE_NORMAL);
	g->SetColorizeImages(false);

	// And finally, let's just have a simple example of alpha blending. We'll
	// Draw our first image, a picture of a moon, normally, and draw
	// our friend Turbot at reduced opacity on top of it. The result is you can
	// see through turbot to the moon below. Notice that we set the color
	// for turbot to r=255, g=255, b=255, alpha=128: This results in the
	// RGB values not being changed, only the alpha. Using a value of 255 for
	// a colorization amount basically means that the color component will not
	// be altered.
	g->DrawImage(mApp->mMoonImg, 400, 300);
	g->SetColor(Color(255, 255, 255, 60));
	g->SetColorizeImages(true);
	g->DrawImage(mApp->mTurbotImg, 400, 300);
	g->SetColorizeImages(false);

}