Home > Products > SophiaFramework UNIVERSE > Tutorial > Breakout > - 9 / 9 -

Appendix - 9 / 9 -

Note

The breakout application is the first time the SFCApplication class is used in the tutorial series. It demonstrates the fundamentals of creating games with SophiaFramework UNIVERSE without using the GUI Framework.

* This appendix assumes readers have already gone through the HelloWorld tutorial and appendix.

Block Class Code Overview

Block.hpp

The Block header file first declares structs to save data concerning the ball, racket and blocks. The advantage of using these structs, is having all the specification data for each object in one place.

These structs typically contain an SFXShape variable, specific to the type of shape desired ( ball -> SFXCircle, racket -> SFXRectangle, etc. ). And an SFXRGBColor variable ( color ) where the color of the object is set. The ball and racket also have SFXGrid variables representing their velocity, while the block has two Sint16 and one Uint16 variables respectively representing block life, score and type.

Two general purpose functions, MoveBlocks() and Animate(), are declared, along with four handler functions: OnAppStart, OnAppResume, OnAppSuspend and OnKey.

Variables and functions that will be used exclusively in Breakout are also declared. Finally two callback functions, CBMoveBlocks() and CBAnimate(), are declared to move entire blocks and to animate the application.

Block Constructor

The Block class constructor mainly performs initializations. It sets the current status to APP_STATUS_NOTHING and the drawIntervall variable to ( 1000 / DRAW_RATE ).

It also sets the color of the racket and ball.

Initializing Function

The Initialize() function is where Breakout's application variables are initialized.

A graphics instance is obtained through the SFXGraphics GetInstance() function and saved in _pgraphics. The background color is set using the SFXGraphics SetBackColor() function.

Game and message area coordinates are determined ( for details see Initialize Application ).

The width and height of a basic block are calculated and then set using the SFXRectangle SetHeight() and SetWidth() functions. The calculations consist of figuring out how big the blocks can be so that there are exactly BLOCK_V_NUM * BLOCK_H_NUM displayed blocks on screen.

Racket height and width are set by taking those of the block, and multiplying them by a size ratio constant ( RACKET_WIDTH2BLOCK_NUM / RACKET_WIDTH2BLOCK_DEN ).

The radius of the ball is set to be half of the racket height.

Dividing the _gameArea's height by the TRAVEL_TIME, and adjusting the result by multiplying it with the _drawInterval produces the ball's y-direction speed.

The spare ball radius ( spareBallR ) is set to that of the usingBall's through the SFXCircle GetRadius() function. The spare balls's coordinates ( spareBallX and spareBallY ) are calculated from the message area coordinates such that they are displayed in the lower right screen corner.

The racket speed ( _racketSpeed )is equal to the ball speed multiplied by a speed ratio constant ( RACKET_SPEED2BALL_NUM / RACKET_SPEED2BALL_DEN ).

Drawing Blocks

DRawing Blocks

In this function, if the variable n representing the nth block is equal to -1, all the blocks are drawn through recursive calls. A false flag representing a destroyed block, drawn as a clear rectangle.

The SFXGraphics ClearRectangle() function produces a "clear" rectangle by setting the rectangle's fill color, to that of the background. ( For details on drawing see Drawing Images and Characters )


Block:       Block

Ball:           Ball

Racket:     Racket

Message: Message

Drawing the Racket

See Drawing Images and Characters.

Drawing the Ball

See Drawing Images and Characters.

Drawing Messages

See Drawing Images and Characters.

Creating Stages

Creating Stage

This is where the different stages are created and initialized. CreateStage() argument n, saves the current stage.

The default case here is stage 1, where all blocks are of the same type ( BLOCK_TYPE_NORMAL ) and are immobile ( !_blockMotion ).

Blocks are drawn individually, row-by-row starting from the top left corner or the screen. Each block's attributes ( type, color, life, etc. ) are initialized.

The code to create stage 3 is almost identical, except for the _blockMotion variable now being set to TRUE.

Stage 2's blocks are not created row-by-row, but clolumn-by-column. For every new column, the first block is a Hard block, the second one is a Normal block and the third block in an Invisible block. Three rows of blocks are created and each block is initialized in the way as was done for stage 1.


Then, another loop copies the three rows of blocks, such that 6 rows of blocks are obtained.

The final stage combines the block drawing techniques of stage 2, with stage 3's block motion.

Collision Detection

Collision

The first part of the Animate() function prevents the racket from traversing the horizontal screen limits. If the rackets velocity is in the negative ( left ) direction and the racket's left side touches the screens left side ( line & LINE_LEFT ), the racket velocity is set to zero, stopping the racket.

The processing for a collision between the ball and a valid ( !BLOCK_TYPE_NONE ) block negates the ball's x-direction speed if the ball hits the side of a block, (line & LINE_LEFT) || (line & LINE_RIGHT), and if it has speed in the x-direction. Else, its y-direction speed is negated.

When the ball hits the bottom or top of a block, (line & LINE_BOTTOM) || (line & LINE_TOP), and it has speed in the y-direction, then that speed is negated. Else, its x-direction speed is negated.

If the ball makes contact with an inside line of the block, then both components of velocity are negated.

For all ball - block collisions ( !LINE_NONE ), if the block is invisible ( BLOCK_TYPE_INVISIBLE ) its type is changed to BLOCK_TYPE_NORMAL. And blocks whose life value is now equal to zero are removed by drawing them with the background color ( DrawBlock(i, false) ), and changing their type to BLOCK_TYPE_NONE.

All of the above instructions are for when the ball is not passing ( !_ballPassing ).

Collisions between the ball and the walls of the game area ( ex. (_usingBall.velocity.GetY() < 0 && (line & LINE_TOP)) ) are simple. A top wall collision causes the ball's y-direction speed to be negated, while a collision with the side walls causes the x-direction speed to be negated.

A collision between a side of the racket and the ball negates the ball's x-direction speed.

A ball and racket top collision ( (line & LINE_TOP) ) leads to the ball's y-direction speed to be reversed. If the ball had no x-direction speed or if it had x-direction speed in opposite orientation to the racket, it is accelerated in the racket's direction of motion.

Animation

Animation for the ball and racket is done by drawing over them with the background color, and then redrawing them properly in their new coordinates with their real colors.

Drawing Sequence

The CheckEnd() function is used to produce a flag indicating what stage should be displayed. If the game is continuing (STAGE_END_CONTINUE ), the timer is reset. If the ball was missed (STAGE_END_MISS ), the user is given the choice to retry current stage, or quit the game. And if the stage has been cleared ( STAGE_END_CLEAR ), then the next stage is created, or the ShowGameClear() message is displayed.( For information concerning the timers see Animation )

Event Handling

Event Handler

The Invoke() function of the SFCInvoker class is responsible for receiving and managing all application events.

The type of the SFXEventConstRef event variable is obtained by the SFRResponder GetType() function, and used as a key to decide which handler function to call.

If the event was not properly handled ( !result ), then it will be passed on to the BREW event handler.

OnAppStart

OnAppStart() is responsible for all the procedures necessary to boot up the application. But in this case those procedures are all defined in the Initialize() function, called by OnAppStart().

OnAppResume

This function is responsible for resuming the application after it has been suspended.

It checks the applications status, if the status is APP_STATUS_PLAYING, it calls DrawBlock(), sets the timer ( if needed ) and calls the Animate() function.

For APP_STATUS_READY, it creates the current stage by calling CreateStage(_nowStage).

And for APP_STATUS_WAIT_RESTART it calls the Initialize() function.

OnAppSuspend

This is where procedures for suspending an application are defined; if needed, the animation and block movement timers are canceled.

OnKey

If the current status is APP_STATUS_READY, the status is changed to APP_STATUS_PLAYING. If need be, the block motion timer is set, and the Animate() function is called.

The Left directional key or the 4 key set the racket's velocity to the negative x-direction, and if the Right key or 6 key are pressed, the racket velocity is set to the positive x-direction.

The Select key brings the racket to a stop.

When the status is APP_STATUS_WAIT_RESTART, the Initialize() function is called.

For a status of APP_STATUS_CONTINUE, the 1 key creates a new stage ( CreateStage(_nowStage) ), while the 2 key quits the game ( ShowGameClear() ).

If the status is APP_STATUS_OPTIONS, the switch statement's key represents the stage selected by the user, and it is used to detrmine the argument of the CreateStage() function.

A status of APP_STATUS_PAUSE, means that the game is being un-paused. Therefore, the screen is cleared, the DrawBlock() function is called, if needed the timers are set, and the Animate() function is also called.

Check End

When the application needs to know what screen to animate next, it calls the CheckEnd() function. If the ball was missed ( _usingBall.ball * _gameArea == LINE_NONE ), or if a block touches the racket / bottom of screen ( (_blocks[i].block * _racket.racket) != LINE_NONE || ((_blocks[i].block * _gameArea) & LINE_BOTTOM)) then STAGE_END_MISS is returned.

If there are no more blocks remaining, _blockNumber <= 0, then STAGE_END_CLEAR is returned.

And if none of these cases are true, the game may continue and STAGE_END_CONTINUE is returned.

Show Game Over

Game Over

When there are no more balls remaining, and the last ball is missed, ShowGameOver() is called.

It performs initializations for displaying messages, calculating message coordinates, setting font, setting color, etc.

It then uses the SFXGraphics DrawText() function to draw three wide strings indicating game status and the user's score.

The DrawMessage() function is called to display one last message saying to "Push Any Button", and _status is set to APP_STATUS_WAIT_RESTART.

Show Game Clear

Identical coding to that of ShowGameOver(), except the wide strings are different in ShowGameClear.

Continue

Once again, very similar coding to that of ShowGameOver(), but in Continue() the returned _status is APP_STATUS_CONTINUE.

Stage Menu

This option is only available at the begining of a stage, APP_STATUS_READY. It instructs the user to enetr the stage of his choice, and changes the _status to APP_STATUS_OPTIONS.

Pause Menu

While the status is APP_STATUS_PLAYING, the game can be paused at anytime by pressing the Pound key.

The processing for pausing and then un-pausing the game, are similar to those for suspending and resuming.

Go back  1   2   3   4   5   6   7   8   Apdx