You are on page 1of 17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

HOME TWITTER FACEBOOK RSS ATOM FORUM

Lines,Antialiasing,Timing,Ortho
ViewAndSimpleSounds

Like

+3

Welcometomy21stOpenGLTutorial!Comingupwithatopicforthistutorialwasextremelydifficult.Iknowalotofyouaretiredof
learningthebasics.Everyoneisdyingtolearnabout3Dobjects,Multitexturingandallthatothergoodstuff.Forthosepeople,I'msorry,
butIwanttokeepthelearningcurvegradual.OnceI'vegoneastepaheadit'snotaseasytotakeastepbackwithoutpeoplelosing
interest.SoI'dprefertokeeppushingforwardatasteadypace.
IncaseI'velostafewofyou:)I'lltellyouabitaboutthistutorial.Untilnowallofmytutorialshaveusedpolygons,quadsandtriangles.
SoIdecideditwouldbenicetowriteatutorialonlines.Afewhoursafterstartingthelinetutorial,Idecidedtocallitquits.Thetutorial
wascomingalongfine,butitwasBORING!Linesaregreat,butthere'sonlysomuchyoucandotomakelinesexciting.Ireadthrough
myemail,browsedthroughthemessageboard,andwrotedownafewofyourtutorialrequests.Outofalltherequeststherewereafew
questionsthatcameupmorethanothers.So...Idecidedtowriteamultitutorial:)
Inthistutorialyouwilllearnabout:Lines,AntiAliasing,OrthographicProjection,Timing,BasicSoundEffects,andSimpleGameLogic.
Hopefullythere'senoughinthistutorialtokeepeveryonehappy:)Ispent2dayscodingthistutorial,andIt'stakenalmost2weeksto
writethisHTMLfile.Ihopeyouenjoymyefforts!
Attheendofthistutorialyouwillhavemadeasimple'amidar'typegame.Yourmissionistofillinthegridwithoutbeingcaughtbythe
badguys.Thegamehaslevels,stages,lives,sound,andasecretitemtohelpyouprogressthroughthelevelswhenthingsgettough.
AlthoughthisgamewillrunfineonaPentium166withaVoodoo2,afasterprocessorisrecommendedifyouwantsmoother
animation.
Iusedthecodefromlesson1asastartingpointwhilewritingthistutorial.Westartoffbyaddingtherequiredheaderfiles.stdio.his
usedforfileoperations,andweincludestdarg.hsothatwecandisplayvariablesonthescreen,suchasthescoreandcurrentstage.
?
1 //ThisCodeWasCreatedByJeffMolofee2000
2 //IfYou'veFoundThisCodeUseful,PleaseLetMeKnow.
3
4 #include<windows.h>//HeaderFileForWindows
5 #include<stdio.h>//StandardInput/Output
6 #include<stdarg.h>//HeaderFileForVariableArgumentRoutines
7 #include<gl\gl.h>//HeaderFileForTheOpenGL32Library
8 #include<gl\glu.h>//HeaderFileForTheGLu32Library
9 #include<gl\glaux.h>//HeaderFileForTheGlauxLibrary
10
11 HDChDC=NULL;//PrivateGDIDeviceContext
12 HGLRChRC=NULL;//PermanentRenderingContext
13 HWNDhWnd=NULL;//HoldsOurWindowHandle
14 HINSTANCEhInstance;//HoldsTheInstanceOfTheApplication
Nowwesetupourbooleanvariables.vlinekeepstrackofthe121verticallinesthatmakeupourgamegrid.11linesacrossand11up
anddown.hlinekeepstrackofthe121horizontallinesthatmakeupthegamegrid.Weuseaptokeeptrackofwhetherornotthe'A'
keyisbeingpressed.
filledisFALSEwhilethegridisn'tfilledandTRUEwhenit'sbeenfilledin.gameoverisprettyobvious.IfgameoverisTRUE,that'sit,the
gameisover,otherwiseyou'restillplaying.antikeepstrackofantialiasing.IfantiisTRUE,objectantialiasingisON.Otherwiseit'soff.
activeandfullscreenkeeptrackofwhetherornottheprogramhasbeenminimizedornot,andwhetheryou'rerunninginfullscreen
modeorwindowedmode.
?
1 boolkeys[256];//ArrayUsedForTheKeyboardRoutine
2 boolvline[11][10];//KeepsTrackOfVerticleLines
3 boolhline[10][11];//KeepsTrackOfHorizontalLines
4 boolap;//'A'KeyPressed?
5 boolfilled;//DoneFillingInTheGrid?
6 boolgameover;//IsTheGameOver?
7 boolanti=TRUE;//Antialiasing?
8 boolactive=TRUE;//WindowActiveFlagSetToTRUEByDefault
9 boolfullscreen=TRUE;//FullscreenFlagSetToFullscreenModeByDefault
Nowwesetupourintegervariables.loop1andloop2willbeusedtocheckpointsonourgrid,seeifanenemyhashitusandtogive
objectsrandomlocationsonthegrid.You'llseeloop1/loop2inactionlaterintheprogram.delayisacountervariablethatIuseto
slowdownthebadguys.Ifdelayisgreaterthanacertainvalue,theenemiesaremovedanddelayissetbacktozero.
Thevariableadjustisaveryspecialvariable!Eventhoughthisprogramhasatimer,thetimeronlycheckstoseeifyourcomputeristoo
fast.Ifitis,adelayiscreatedtoslowthecomputerdown.OnmyGeForcecard,theprogramrunsinsanelysmooth,andveryveryfast.
AftertestingthisprogramonmyPIII/450withaVoodoo3500TV,Inoticedthattheprogramwasrunningextremelyslow.Theproblemis
thatmytimingcodeonlyslowsdownthegameplay.Itwontspeeditup.SoImadeanewvariablecalledadjust.adjustcanbeany
valuefrom0to5.Theobjectsinthegamemoveatdifferentspeedsdependingonthevalueofadjust.Thelowerthevaluethe
smoothertheymove,thehigherthevalue,thefastertheymove(choppyatvalueshigherthan3).Thiswastheonlyrealeasywayto

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

1/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

makethegameplayableonslowsystems.Onethingtonote,nomatterhowfasttheobjectsaremovingthegamespeedwillneverrun
fasterthanIintendedittorun.Sosettingtheadjustvalueto3issafeforfastandslowsystems.
Thevariablelivesissetto5sothatyoustartthegamewith5lives.levelisaninternalvariable.Thegameusesittokeeptrackofthe
levelofdifficulty.Thisisnotthelevelthatyouwillseeonthescreen.Thevariablelevel2startsoffwiththesamevalueaslevelbutcan
increaseforeverdependingonyourskill.Ifyoumanagetogetpastlevel3thelevelvariablewillstopincreasingat3.Thelevelvariable
isaninternalvariableusedforgamedifficulty.Thestagevariablekeepstrackofthecurrentgamestage.
?
1 intloop1;//GenericLoop1
2 intloop2;//GenericLoop2
3 intdelay;//EnemyDelay
4 intadjust=3;//SpeedAdjustmentForReallySlowVideoCards
5 intlives=5;//PlayerLives
6 intlevel=1;//InternalGameLevel
7 intlevel2=level;//DisplayedGameLevel
8 intstage=1;//GameStage
Nowwecreateastructuretokeeptrackoftheobjectsinourgame.WehaveafineXposition(fx)andafineYposition(fy).These
variableswillmovetheplayerandenemiesaroundthegridafewpixelsatatime.Creatingasmoothmovingobject.
Thenwehavexandy.Thesevariableswillkeeptrackofwhatintersectionourplayerisat.Thereare11pointsleftandrightand11
pointsupanddown.Soxandycanbeanyvaluefrom0to10.Thatiswhyweneedthefinevalues.Ifwecouldonlymoveoneof11
spotsleftandrightandoneof11spotsupanddownourplayerwouldjumparoundthescreeninaquick(nonsmooth)motion.
Thelastvariablespinwillbeusedtospintheobjectsontheirzaxis.
?
1 structobject//CreateAStructureForOurPlayer
2{
3 intfx,fy;//FineMovementPosition
4 intx,y;//CurrentPlayerPosition
5 floatspin;//SpinDirection
6 };
Nowthatwehavecreatedastructurethatcanbeusedforourplayer,enemiesandevenaspecialitemwecancreatenewstructures
thattakeonthecharacteristicsofthestructurewejustmade.
Thefirstlinebelowcreatesastructureforourplayer.Basicallywe'regivingourplayerstructurefx,fy,x,yandspinvalues.Byadding
thisline,wecanaccesstheplayerxpositionbycheckingplayer.x.Wecanchangetheplayerspinbyaddinganumbertoplayer.spin.
Thesecondlineisabitdifferent.Becausewecanhaveupto9enemiesonthescreenatatime,weneedtocreatetheabovevariables
foreachenemy.Wedothisbymakinganarrayof9enemies.thexpositionofthefirstenemywillbeenemy[0].x.Thesecondenemy
willbeenemy[1].x,etc.
Thelastlinecreatesastructureforourspecialitem.Thespecialitemisanhourglassthatwillappearonthescreenfromtimetotime.
Weneedtokeeptrackofthexandyvaluesforthehourglass,butbecausethehourglassdoesn'tmove,wedon'tneedtokeeptrackof
thefinepositions.Insteadwewillusethefinevariables(fxandfy)forotherthingslaterintheprogram.
?
1 structobjectplayer;//PlayerInformation
2 structobjectenemy[9];//EnemyInformation
3 structobjecthourglass;//HourglassInformation
Nowwecreateatimerstructure.Wecreateastructuresothatit'seasiertokeeptrackoftimervariablesandsothatit'seasiertotellthat
thevariableisatimervariable.
Thefirstthingwedoiscreatea64bitintegercalledfrequency.Thisvariablewillholdthefrequencyofthetimer.WhenIfirstwrotethis
program,Iforgottoincludethisvariable.Ididn'trealizethatthefrequencyononemachinemaynotmatchthefrequencyonanother.
Bigmistakeonmypart!Thecoderanfineonthe3systemsinmyhouse,butwhenItesteditonafriendsmachinethegameranWAY
tofast.Frequencyisbasicallyhowfasttheclockisupdated.Goodthingtokeeptrackof:)
Theresolutionvariablekeepstrackofthestepsittakesbeforeweget1millisecondoftime.
mm_timer_startandmm_timer_elapsedholdthevaluethatthetimerstartedat,andtheamountoftimethathaselapsedsincethethe
timerwasstarted.Thesetwovariablesareonlyusedifthecomputerdoesn'thaveaperformancecounter.Inthatcaseweendupusing
thelessaccuratemultimediatimer,whichisstillnottobadforanontimecriticalgamelikethis.
Thevariableperformance_timercanbeeitherTRUEofFALSE.Iftheprogramdetectsaperformancecounter,thevariable
performance_timervariableissettoTRUE,andalltimingisdoneusingtheperformancecounter(alotmoreaccuratethanthe
multimediatimer).Ifaperformancecounterisnotfound,performance_timerissettoFALSEandthemultimediatimerisusedfortiming.
Thelast2variablesare64bitintegervariablesthatholdthestarttimeoftheperformancecounterandtheamountoftimethathas
elapsedsincetheperformancecounterwasstarted.
Thenameofthisstructureis"timer"asyoucanseeatthebottomofthestructure.Ifwewanttoknowthetimerfrequencywecannow
checktimer.frequency.Nice!
?
1 struct//CreateAStructureForTheTimerInformation
2 {
3 __int64frequency;//TimerFrequency
4 floatresolution;//TimerResolution
5 unsignedlongmm_timer_start;//MultimediaTimerStartValue
6 unsignedlongmm_timer_elapsed;//MultimediaTimerElapsedTime
7 boolperformance_timer;//UsingThePerformanceTimer?
8 __int64performance_timer_start;//PerformanceTimerStartValue
9 __int64performance_timer_elapsed;//PerformanceTimerElapsedTime
10 }timer;//StructureIsNamedtimer
Thenextlineofcodeisourspeedtable.Theobjectsinthegamewillmoveatadifferentratedependingonthevalueofadjust.Ifadjust
is0theobjectswillmoveonepixelatatime.Ifthevalueofadjustis5,theobjectswillmove20pixelsatatime.Sobyincreasingthe
valueofadjustthespeedoftheobjectswillincrease,makingthegamerunfasteronslowcomputers.Thehigheradjustishowever,the

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

2/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

choppierthegamewillplay.
Basicallysteps[]isjustalookuptable.Ifadjustwas3,wewouldlookatthenumberstoredatlocation3insteps[].Location0holds
thevalue1,location1holdsthevalue2,location2holdsthevalue4,andlocation3holdthevalue5.Ifadjustwas3,ourobjectswould
move5pixelsatatime.Makesense?
?
1 intsteps[6]={1,2,4,5,10,20};//SteppingValuesForSlowVideoAdjustment
Nextwemakeroomfortwotextures.We'llloadabackgroundscene,andabitmapfonttexture.Thenwesetupabasevariablesowe
cankeeptrackofourfontdisplaylistjustlikewedidintheotherfonttutorials.FinallywedeclareWndProc().
?
1 GLuinttexture[2];//FontTextureStorageSpace
2 GLuintbase;//BaseDisplayListForTheFont
3
4 LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);//DeclarationForWndProc
Nowforthefunstuff:)Thenextsectionofcodeinitializesourtimer.Itwillcheckthecomputertoseeifaperformancecounteris
available(veryaccuratecounter).Ifwedon'thaveaperformancecounterthecomputerwillusethemultimediatimer.Thiscodeshould
beportablefromwhatI'mtold.
Westartoffbyclearingallthetimervariablestozero.Thiswillsetallthevariablesinourtimerstructuretozero.Afterthat,wecheckto
seeifthereisNOTaperformancecounter.The!meansNOT.Ifthereis,thefrequencywillbestoredintimer.frequency.
Iftherewasnoperformancecounter,thecodeinbetweenthe{}'sisrun.Thefirstlinesetsthevariabletimer.performance_timerto
FALSE.Thistellsourprogramthatthereisnoperformancecounter.Thesecondlinegetsourstartingmultimediatimervaluefrom
timeGetTime().Wesetthetimer.resolutionto0.001f,andthetimer.frequencyto1000.Becausenotimehaselapsedyet,wemakethe
elapsedtimeequalthestarttime.
?
1 voidTimerInit(void)//InitializeOurTimer(GetItReady)
2 {
3 memset(&timer,0,sizeof(timer));//ClearOurTimerStructure
4
5 //CheckToSeeIfAPerformanceCounterIsAvailable
6 //IfOneIsAvailableTheTimerFrequencyWillBeUpdated
7 if(!QueryPerformanceFrequency((LARGE_INTEGER*)&timer.frequency))
8 {
9 //NoPerformaceCounterAvailable
10 timer.performance_timer=FALSE;//SetPerformanceTimerToFALSE
11 timer.mm_timer_start=timeGetTime();//UsetimeGetTime()ToGetCurrentTime
12 timer.resolution=1.0f/1000.0f;//SetOurTimerResolutionTo.001f
13 timer.frequency=1000;//SetOurTimerFrequencyTo1000
14 timer.mm_timer_elapsed=timer.mm_timer_start;//SetTheElapsedTimeToTheCurrentTime
15 }
Ifthereisaperformancecounter,thefollowingcodeisruninstead.Thefirstlinegrabsthecurrentstartingvalueoftheperformance
counter,andstoresitintimer.performance_timer_start.Thenwesettimer.performance_timertoTRUEsothatourprogramknowsthere
isaperformancecounteravailable.Afterthatwecalculatethetimerresolutionbyusingthefrequencythatwegotwhenwecheckedfor
aperformancecounterinthecodeabove.Wedivide1bythefrequencytogettheresolution.Thelastthingwedoismaketheelapsed
timethesameasthestartingtime.
Noticeinsteadofsharingvariablesfortheperformanceandmultimediatimerstartandelapsedvariables,I'vedecidedtomake
seperatevariables.Eitherwayitwillworkfine.
?
1 else
2 {
3 //PerformanceCounterIsAvailable,UseItInsteadOfTheMultimediaTimer
4 //GetTheCurrentTimeAndStoreItInperformance_timer_start
5 QueryPerformanceCounter((LARGE_INTEGER*)&timer.performance_timer_start);
6 timer.performance_timer=TRUE;//SetPerformanceTimerToTRUE
7 //CalculateTheTimerResolutionUsingTheTimerFrequency
8 timer.resolution=(float)(((double)1.0f)/((double)timer.frequency));
9 //SetTheElapsedTimeToTheCurrentTime
10 timer.performance_timer_elapsed=timer.performance_timer_start;
11 }
12 }
Thesectionofcodeabovesetsupthetimer.Thecodebelowreadsthetimerandreturnstheamountoftimethathaspassedin
milliseconds.
Thefirstthingwedoissetupa64bitvariablecalledtime.Wewillusethisvariabletograbthecurrentcountervalue.Thenextline
checkstoseeifwehaveaperformancecounter.Ifwedo,timer.performance_timerwillbeTRUEandthecoderightafterwillrun.
Thefirstlineofcodeinsidethe{}'sgrabsthecountervalueandstoresitinthevariablewecreatedcalledtime.Thesecondlinetakes
thetimewejustgrabbed(timeandsubtractsthestarttimethatwegotwhenweinitializedthetimer.Thiswayourtimershouldstartout
prettyclosetozero.Wethenmultiplytheresultsbytheresolutiontofindouthowmanysecondshavepassed.Thelastthingwedois
multiplytheresultby1000tofigureouthowmanymillisecondshavepassed.Afterthecalculationisdone,ourresultsaresentbackto
thesectionofcodethatcalledthisprocedure.Theresultswillbeinfloatingpointformatforgreateraccuracy.
Ifwearenotusingthepeformancecounter,thecodeaftertheelsestatementwillberun.Itdoesprettymuchthesamething.Wegrab
thecurrenttimewithtimeGetTime()andsubtractourstartingcountervalue.Wemultiplyitbyourresolutionandthenmultiplytheresult
by1000toconvertfromsecondsintomilliseconds.
?
1
2
3
4
5
6

floatTimerGetTime()//GetTimeInMilliseconds
{
__int64time;//timeWillHoldA64BitInteger

if(timer.performance_timer)//AreWeUsingThePerformanceTimer?
{

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

3/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

7 QueryPerformanceCounter((LARGE_INTEGER*)&time);//GrabTheCurrentPerformanceTime
8 //ReturnTheCurrentTimeMinusTheStartTimeMultipliedByTheResolutionAnd1000(ToGetMS)
9 return((float)(timetimer.performance_timer_start)*timer.resolution)*1000.0f;
10 }
11 else
12 {
13 //ReturnTheCurrentTimeMinusTheStartTimeMultipliedByTheResolutionAnd1000(ToGetMS)
14 return((float)(timeGetTime()timer.mm_timer_start)*timer.resolution)*1000.0f;
15 }
16 }
Thefollowingsectionofcoderesetstheplayertothetopleftcornerofthescreen,andgivestheenemiesarandomstartingpoint.
Thetopleftofthescreenis0onthexaxisand0ontheyaxis.Sobysettingtheplayer.xvalueto0wemovetheplayertothefarleft
sideofthescreen.Bysettingtheplayer.yvalueto0wemoveourplayertothetopofthescreen.
Thefinepositionshavetobeequaltothecurrentplayerposition,otherwiseourplayerwouldmovefromwhatevervalueit'satonthe
finepositiontothetopleftofthescreen.Wedon'twanttoplayertomovethere,wewantittoappearthere,sowesetthefinepositions
to0aswell.
?
1 voidResetObjects(void)//ResetPlayerAndEnemies
2{
3 player.x=0;//ResetPlayerXPositionToFarLeftOfTheScreen
4 player.y=0;//ResetPlayerYPositionToTheTopOfTheScreen
5 player.fx=0;//SetFineXPositionToMatch
6 player.fy=0;//SetFineYPositionToMatch
Nextwegivetheenemiesarandomstartinglocation.Thenumberofenemiesdisplayedonthescreenwillbeequaltothecurrent
(internal)levelvaluemultipliedbythecurrentstage.Remember,themaximumvaluethatlevelcanequalis3andthemaximum
numberofstagesperlevelis3.Sowecanhaveatotalof9enemies.
Tomakesurewegivealltheviewableenemiesanewposition,weloopthroughallthevisibleenemies(stagetimeslevel).Weset
eachenemiesxpositionto5plusarandomvaluefrom0to5.(themaximumvaluerandcanbeisalwaysthenumberyouspecify
minus1).Sotheenemycanappearonthegrid,anywherefrom5to10.Wethengivetheenemyarandomvalueontheyaxisfrom0to
10.
Wedon'twanttheenemytomovefromit'soldpositiontothenewrandompositionsowemakesurethefinex(fx)andy(fy)valuesare
equaltotheactualxandyvaluesmultipliedbywidthandheightofeachtileonthescreen.Eachtilehasawidthof60andaheightof
40.
?
1 for(loop1=0;loop1<(stage*level);loop1++)//LoopThroughAllTheEnemies
2 {
3 enemy[loop1].x=5+rand()%6;//SelectARandomXPosition
4 enemy[loop1].y=rand()%11;//SelectARandomYPosition
5 enemy[loop1].fx=enemy[loop1].x*60;//SetFineXToMatch
6 enemy[loop1].fy=enemy[loop1].y*40;//SetFineYToMatch
7 }
8}
TheAUX_RGBImageReccodehasn'tchangedsoI'mskippingoverit.InLoadGLTextures()wewillloadinourtwotextures.Firstthe
fontbitmap(Font.bmp)andthenthebackgroundimage(Image.bmp).We'llconvertboththeimagesintotexturesthatwecanuseinour
game.Afterwehavebuiltthetextureswecleanupbydeletingthebitmapinformation.Nothingreallynew.Ifyou'vereadtheother
tutorialsyoushouldhavenoproblemsunderstandingthecode.
?
1 intLoadGLTextures()//LoadBitmapsAndConvertToTextures
2 {
3 intStatus=FALSE;//StatusIndicator
4 AUX_RGBImageRec*TextureImage[2];//CreateStorageSpaceForTheTextures
5 memset(TextureImage,0,sizeof(void*)*2);//SetThePointerToNULL
6
7 if((TextureImage[0]=LoadBMP("Data/Font.bmp"))&&//LoadTheFont
8 (TextureImage[1]=LoadBMP("Data/Image.bmp")))//LoadBackgroundImage
9 {
10 Status=TRUE;//SetTheStatusToTRUE
11
12 glGenTextures(2,&texture[0]);//CreateTheTexture
13
14 for(loop1=0;loop1<2;loop1++)//LoopThrough2Textures
15 {
16 glBindTexture(GL_TEXTURE_2D,texture[loop1]);
17 glTexImage2D(GL_TEXTURE_2D,0,3,TextureImage[loop1]>sizeX,TextureImage[loop1]>sizeY,
18 0,GL_RGB,GL_UNSIGNED_BYTE,TextureImage[loop1]>data);
19 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
20 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
21 }
22
23 for(loop1=0;loop1<2;loop1++)//LoopThrough2Textures
24 {
25 if(TextureImage[loop1])//IfTextureExists
26 {
27 if(TextureImage[loop1]>data)//IfTextureImageExists
28 {
29 free(TextureImage[loop1]>data);//FreeTheTextureImageMemory
30 }
31 free(TextureImage[loop1]);//FreeTheImageStructure
32 }
33 }
34 }
35 returnStatus;//ReturnTheStatus

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

4/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

36 }
Thecodebelowbuildsourfontdisplaylist.I'vealreadydoneatutorialonbitmaptexturefonts.Allthecodedoesisdividesthe
Font.bmpimageinto16x16cells(256characters).Each16x16cellwillbecomeacharacter.BecauseI'vesettheyaxisupsothat
positivegoesdowninsteadofup,it'snecessarytosubtractouryaxisvaluesfrom1.0f.Otherwisetheletterswillallbeupsidedown:)If
youdon'tunderstandwhat'sgoingon,gobackandreadthebitmaptexturefonttutorial.
?
1 GLvoidBuildFont(GLvoid)//BuildOurFontDisplayList
2 {
3 base=glGenLists(256);//Creating256DisplayLists
4 glBindTexture(GL_TEXTURE_2D,texture[0]);//SelectOurFontTexture
5 for(loop1=0;loop1<256;loop1++)//LoopThroughAll256Lists
6 {
7 floatcx=float(loop1%16)/16.0f;//XPositionOfCurrentCharacter
8 floatcy=float(loop1/16)/16.0f;//YPositionOfCurrentCharacter
9
10 glNewList(base+loop1,GL_COMPILE);//StartBuildingAList
11 glBegin(GL_QUADS);//UseAQuadForEachCharacter
12 glTexCoord2f(cx,1.0fcy0.0625f);//TextureCoord(BottomLeft)
13 glVertex2d(0,16);//VertexCoord(BottomLeft)
14 glTexCoord2f(cx+0.0625f,1.0fcy0.0625f);//TextureCoord(BottomRight)
15 glVertex2i(16,16);//VertexCoord(BottomRight)
16 glTexCoord2f(cx+0.0625f,1.0fcy);//TextureCoord(TopRight)
17 glVertex2i(16,0);//VertexCoord(TopRight)
18 glTexCoord2f(cx,1.0fcy);//TextureCoord(TopLeft)
19 glVertex2i(0,0);//VertexCoord(TopLeft)
20 glEnd();//DoneBuildingOurQuad(Character)
21 glTranslated(15,0,0);//MoveToTheRightOfTheCharacter
22 glEndList();//DoneBuildingTheDisplayList
23 }//LoopUntilAll256AreBuilt
24 }
It'sagoodideatodestroythefontdisplaylistwhenyou'redonewithit,soI'veaddedthefollowingsectionofcode.Again,nothingnew.
?
1 GLvoidKillFont(GLvoid)//DeleteTheFontFromMemory
2{
3 glDeleteLists(base,256);//DeleteAll256DisplayLists
4}
TheglPrint()codehasn'tchangedthatmuch.TheonlydifferencefromthetutorialonbitmapfonttexturesisthatIhaveaddedtheability
toprintthevalueofvariables.TheonlyreasonI'vewrittenthissectionofcodeoutissothatyoucanseethechanges.Theprint
statementwillpositionthetextatthexandypositionthatyouspecify.Youcanpickoneof2charactersets,andthevalueofvariables
willbewrittentothescreen.Thisallowsustodisplaythecurrentlevelandstageonthescreen.
NoticethatIenabletexturemapping,resettheviewandthentranslatetotheproperx/yposition.Alsonoticethatifcharacterset0is
selected,thefontisenlargedoneandhalftimeswidthwise,anddoubleit'soriginalsizeupanddown.IdidthissothatIcouldwritethe
titleofthegameinbigletters.Afterthetexthasbeendrawn,Idisabletexturemapping.
?
1 GLvoidglPrint(GLintx,GLinty,intset,constchar*fmt,...)//WhereThePrintingHappens
2 {
3 chartext[256];//HoldsOurString
4 va_listap;//PointerToListOfArguments
5
6 if(fmt==NULL)//IfThere'sNoText
7 return;//DoNothing
8
9 va_start(ap,fmt);//ParsesTheStringForVariables
10 vsprintf(text,fmt,ap);//AndConvertsSymbolsToActualNumbers
11 va_end(ap);//ResultsAreStoredInText
12
13 if(set>1)//DidUserChooseAnInvalidCharacterSet?
14 {
15 set=1;//IfSo,SelectSet1(Italic)
16 }
17 glEnable(GL_TEXTURE_2D);//EnableTextureMapping
18 glLoadIdentity();//ResetTheModelviewMatrix
19 glTranslated(x,y,0);//PositionTheText(0,0BottomLeft)
20 glListBase(base32+(128*set));//ChooseTheFontSet(0or1)
21
22 if(set==0)//IfSet0IsBeingUsedEnlargeFont
23 {
24 glScalef(1.5f,2.0f,1.0f);//EnlargeFontWidthAndHeight
25 }
26
27 glCallLists(strlen(text),GL_UNSIGNED_BYTE,text);//WriteTheTextToTheScreen
28 glDisable(GL_TEXTURE_2D);//DisableTextureMapping
29 }
TheresizecodeisNEW:)InsteadofusingaperspectiveviewI'musinganorthoviewforthistutorial.Thatmeansthatobjectsdon'tget
smallerastheymoveawayfromtheviewer.Thezaxisisprettymuchuselessinthistutorial.
Westartoffbysettinguptheviewport.Wedothisthesamewaywe'ddoitifweweresettingupaperspectiveview.Wemakethe
viewportequaltothewidthofourwindow.
Thenweselecttheprojectionmatrix(thingmovieprojector,itinformationonhowtodisplayourimage).andresetit.
Immediatelyafterweresettheprojectionmatrix,wesetupourorthoview.I'llexplainthecommandindetail:
Thefirstparameter(0.0f)isthevaluethatwewantforthefarleftsideofthescreen.Youwantedtoknowhowtouseactualpixelvalues,

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

5/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

soinsteadofusinganegativenumberforfarleft,I'vesetthevalueto0.Thesecondparameteristhevalueforthefarrightsideofthe
screen.Ifourwindowis640x480,thevaluestoredinwidthwillbe640.Sothefarrightsideofthescreeneffectivelybecomes640.
Thereforeourscreenrunsfrom0to639onthexaxis(640pixels).
Thethirdparameter(height)wouldnormallybeournegativeyaxisvalue(bottomofthescreen).Butbecausewewantexactpixels,we
wonthaveanegativevalue.Insteadwewillmakethebottomofthescreenequaltheheightofourwindow.Ifourwindowis640x480,
heightwillbeequalto480.Sothebottomofourscreenwillbe479.Thefourthparameterwouldnormallybethepositivevalueforthe
topofourscreen.Wewantthetopofthescreentobe0(goodoldfashionedscreencoordinates)sowejustsetthefourthparameterto
0.Thisgivesusfrom0to479ontheyaxis(480pixels).
Thelasttwoparametersareforthezaxis.Wedon'treallycareaboutthezaxissowe'llsettherangefrom1.0fto1.0f.Justenoughthat
wecanseeanythingdrawnat0.0fonthezaxis.
Afterwe'vesetuptheorthoview,weselectthemodelviewmatrix(objectinformation...location,etc)andresetit.
?
1 GLvoidReSizeGLScene(GLsizeiwidth,GLsizeiheight)//ResizeAndInitializeTheGLWindow
2 {
3 if(height==0)//PreventADivideByZeroBy
4 {
5 height=1;//MakingHeightEqualOne
6 }
7
8 glViewport(0,0,width,height);//ResetTheCurrentViewport
9
10 glMatrixMode(GL_PROJECTION);//SelectTheProjectionMatrix
11 glLoadIdentity();//ResetTheProjectionMatrix
12
13 glOrtho(0.0f,width,height,0.0f,1.0f,1.0f);//CreateOrtho640x480View(0,0AtTopLeft)
14
15 glMatrixMode(GL_MODELVIEW);//SelectTheModelviewMatrix
16 glLoadIdentity();//ResetTheModelviewMatrix
17 }
Theinitcodehasafewnewcommands.Westartoffbyloadingourtextures.Iftheydidn'tloadproperly,theprogramwillquitwithan
errormessage.Afterwehavebuiltthetextures,webuildourfontset.Idon'tbothererrorcheckingbutyoucanifyouwant.
Afterthefonthasbeenbuilt,wesetthingsup.Weenablesmoothshading,setourclearcolortoblackandsetdepthclearingto1.0f.
Afterthatisanewlineofcode.
glHint()tellsOpenGLhowtodrawsomething.InthiscasewearetellingOpenGLthatwewantlinesmoothingtobethebest(nicest)
thatOpenGLcando.Thisisthecommandthatenablesantialiasing.
Thelastthingwedoisenableblendingandselecttheblendmodethatmakesantialiasedlinespossible.Blendingisrequiredifyou
wantthelinestoblendnicelywiththebackgroundimage.Disableblendingifyouwanttoseehowcrappythingslookwithoutit.
It'simportanttopointoutthatantialiasingmaynotappeartobeworking.Theobjectsinthisgamearequitesmallsoyoumaynotnotice
theantialaisingrightoffthestart.Lookhard.Noticehowthejaggielinesontheenemiessmoothoutwhenantialiasingison.Theplayer
andhourglassshouldlookbetteraswell.
?
1 intInitGL(GLvoid)//AllSetupForOpenGLGoesHere
2 {
3 if(!LoadGLTextures())//JumpToTextureLoadingRoutine
4 {
5 returnFALSE;//IfTextureDidn'tLoadReturnFALSE
6 }
7
8 BuildFont();//BuildTheFont
9
10 glShadeModel(GL_SMOOTH);//EnableSmoothShading
11 glClearColor(0.0f,0.0f,0.0f,0.5f);//BlackBackground
12 glClearDepth(1.0f);//DepthBufferSetup
13 glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);//SetLineAntialiasing
14 glEnable(GL_BLEND);//EnableBlending
15 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);//TypeOfBlendingToUse
16 returnTRUE;//InitializationWentOK
17 }
Nowforthedrawingcode.Thisiswherethemagichappens:)
Weclearthescreen(toblack)alongwiththedepthbuffer.Thenweselectthefonttexture(texture[0]).Wewantthewords"GRID
CRAZY"tobeapurplecolorsowesetredandbluetofullintensity,andweturnthegreenuphalfway.Afterwe'veselectedthecolor,
wecallglPrint().Wepositionthewords"GRIDCRAZY"at207onthexaxis(centeronthescreen)and24ontheyaxis(upanddown).
Weuseourlargefontbyselectingfontset0.
Afterwe'vedrawn"GRIDCRAZY"tothescreen,wechangethecolortoyellow(fullred,fullgreen).Wewrite"Level:"andthevariable
level2tothescreen.Rememberthatlevel2canbegreaterthan3.level2holdsthelevelvaluethattheplayerseesonthescreen.%2i
meansthatwedon'twantanymorethan2digitsonthescreentorepresentthelevel.Theimeansthenumberisanintegernumber.
Afterwehavewrittenthelevelinformationtothescreen,wewritethestageinformationrightunderitusingthesamecolor.
?
1 intDrawGLScene(GLvoid)//Here'sWhereWeDoAllTheDrawing
2{
3 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//ClearScreenAndDepthBuffer
4 glBindTexture(GL_TEXTURE_2D,texture[0]);//SelectOurFontTexture
5 glColor3f(1.0f,0.5f,1.0f);//SetColorToPurple
6 glPrint(207,24,0,"GRIDCRAZY");//WriteGRIDCRAZYOnTheScreen
7 glColor3f(1.0f,1.0f,0.0f);//SetColorToYellow
8 glPrint(20,20,1,"Level:%2i",level2);//WriteActualLevelStats
9 glPrint(20,40,1,"Stage:%2i",stage);//WriteStageStats

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

6/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

Nowwechecktoseeifthegameisover.Ifthegameisover,thevariablegameoverwillbeTRUE.Ifthegameisover,weuse
glColor3ub(r,g,b)toselectarandomcolor.Noticeweareusing3ubinsteadof3f.Byusing3ubwecanuseintegervaluesfrom0to
255tosetourcolors.Plusit'seasiertogetarandomvaluefrom0to255thanitistogetarandomvaluefrom0.0fto1.0f.
Oncearandomcolorhasbeenselected,wewritethewords"GAMEOVER"totherightofthegametitle.Rightunder"GAMEOVER"we
write"PRESSSPACE".Thisgivestheplayeravisualmessagelettingthemknowthattheyhavediedandtopressthespacebarto
restartthegame.
?
1 if(gameover)//IsTheGameOver?
2{
3 glColor3ub(rand()%255,rand()%255,rand()%255);//PickARandomColor
4 glPrint(472,20,1,"GAMEOVER");//WriteGAMEOVERToTheScreen
5 glPrint(456,40,1,"PRESSSPACE");//WritePRESSSPACEToTheScreen
6}
Iftheplayerstillhaslivesleft,wedrawanimatedimagesoftheplayerscharactertotherightofthegametitle.Todothiswecreatea
loopthatgoesfrom0tothecurrentnumberoflivestheplayerhasleftminusone.Isubtractone,becausethecurrentlifeistheimage
youcontrol.
Insidetheloop,weresettheview.Aftertheviewhasbeenreset,wetranslatetothe490pixelstotherightplusthevalueofloop1times
40.0f.Thisdrawseachoftheanimatedplayerlives40pixelsapartfromeachother.Thefirstanimatedimagewillbedrawnat490+
(0*40)(=490),thesecondanimatedimagewillbedrawnat490+(1*40)(=530),etc.
Afterwehavemovedtothespotwewanttodrawtheanimatedimage,werotatecounterclockwisedependingonthevaluestoredin
player.spin.Thiscausestheanimatedlifeimagestospintheoppositewaythatyouractiveplayerisspinning.
Wethenselectgreenasourcolor,andstartdrawingtheimage.Drawinglinesisalotlikedrawingaquadorapolygon.Youstartoff
withglBegin(GL_LINES),tellingOpenGLwewanttodrawaline.Lineshave2vertices.WeuseglVertex2dtosetourfirstpoint.
glVertex2ddoesn'trequireazvalue,whichisniceconsideringwedon'tcareaboutthezvalue.Thefirstpointisdrawn5pixelstothe
leftofthecurrentxlocationand5pixelsupfromthecurrentylocation.Givingusatopleftpoint.Thesecondpointofourfirstlineis
drawn5pixelstotherightofourcurrentxlocation,and5pixelsdown,givingusabottomrightpoint.Thisdrawsalinefromthetopleft
tothebottomright.Oursecondlineisdrawnfromthetoprighttothebottomleft.ThisdrawsagreenXonthescreen.
AfterwehavedrawnthegreenX,werotatecounterclockwise(onthezaxis)evenmore,butthistimeathalfthespeed.Wethenselect
adarkershadeofgreen(0.75f)anddrawanotherx,butweuse7insteadof5thistime.Thisdrawsabigger/darkerxontopofthefirst
greenX.BecausethedarkerXspinsslowerthough,itwilllookasifthebrightXhasaspinningsetoffeelers(grin)ontopofit.
?
1 for(loop1=0;loop1<lives1;loop1++)//LoopThroughLivesMinusCurrentLife
2 {
3 glLoadIdentity();//ResetTheView
4 glTranslatef(490+(loop1*40.0f),40.0f,0.0f);//MoveToTheRightOfOurTitleText
5 glRotatef(player.spin,0.0f,0.0f,1.0f);//RotateCounterClockwise
6 glColor3f(0.0f,1.0f,0.0f);//SetPlayerColorToLightGreen
7 glBegin(GL_LINES);//StartDrawingOurPlayerUsingLines
8 glVertex2d(5,5);//TopLeftOfPlayer
9 glVertex2d(5,5);//BottomRightOfPlayer
10 glVertex2d(5,5);//TopRightOfPlayer
11 glVertex2d(5,5);//BottomLeftOfPlayer
12 glEnd();//DoneDrawingThePlayer
13 glRotatef(player.spin*0.5f,0.0f,0.0f,1.0f);//RotateCounterClockwise
14 glColor3f(0.0f,0.75f,0.0f);//SetPlayerColorToDarkGreen
15 glBegin(GL_LINES);//StartDrawingOurPlayerUsingLines
16 glVertex2d(7,0);//LeftCenterOfPlayer
17 glVertex2d(7,0);//RightCenterOfPlayer
18 glVertex2d(0,7);//TopCenterOfPlayer
19 glVertex2d(0,7);//BottomCenterOfPlayer
20 glEnd();//DoneDrawingThePlayer
21 }
Nowwe'regoingtodrawthegrid.WesetthevariablefilledtoTRUE.Thistellsourprogramthatthegridhasbeencompletelyfilledin
(you'llseewhywedothisinasecond).
Rightafterthatwesetthelinewidthto2.0f.Thismakesthelinesthicker,makingthegridlookmoredefined.
Thenwedisableantialiasing.Thereasonwedisableantialiasingisbecausealthoughit'sagreatfeature,iteatsCPU'sforbreakfast.
Unlessyouhaveakillergraphicscard,you'llnoticeahugeslowdownifyouleaveantialiasingon.Goaheadandtryifyouwant:)
Theviewisreset,andwestarttwoloops.loop1willtravelfromlefttoright.loop2willtravelfromtoptobottom.
Wesetthelinecolortoblue,thenwechecktoseeifthehorizontallinethatweareabouttodrawhasbeentracedover.Ifithasweset
thecolortowhite.Thevalueofhline[loop1][loop2]willbeTRUEifthelinehasbeentracedover,andFALSEifithasn't.
Afterwehavesetthecolortoblueorwhite,wedrawtheline.Thefirstthingtodoismakesurewehaven'tgonetofartotheright.We
don'twanttodrawanylinesorchecktoseeifthelinehasbeenfilledinwhenloop1isgreaterthan9.
Oncewearesureloop1isinthevalidrangewechecktoseeifthehorizontallinehasn'tbeenfilledin.Ifithasn't,filledissettoFALSE,
lettingourOpenGLprogramknowthatthereisatleastonelinethathasn'tbeenfilledin.
Thelineisthendrawn.Wedrawourfirsthorizontal(lefttoright)linestartingat20+(0*60)(=20).Thislineisdrawnallthewayto80+
(0*60)(=80).Noticethelineisdrawntotheright.Thatiswhywedon'twanttodraw11(010)lines.becausethelastlinewouldstartat
thefarrightofthescreenandend80pixelsoffthescreen.
?
1
2
3
4
5
6
7
8

filled=TRUE;//SetFilledToTrueBeforeTesting
glLineWidth(2.0f);//SetLineWidthForCellsTo2.0f
glDisable(GL_LINE_SMOOTH);//DisableAntialiasing
glLoadIdentity();//ResetTheCurrentModelviewMatrix
for(loop1=0;loop1<11;loop1++)//LoopFromLeftToRight
{
for(loop2=0;loop2<11;loop2++)//LoopFromTopToBottom
{

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

7/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

9 glColor3f(0.0f,0.5f,1.0f);//SetLineColorToBlue
10 if(hline[loop1][loop2])//HasTheHorizontalLineBeenTraced
11 {
12 glColor3f(1.0f,1.0f,1.0f);//IfSo,SetLineColorToWhite
13 }
14 if(loop1<10)//DontDrawToFarRight
15 {
16 if(!hline[loop1][loop2])//IfAHorizontalLineIsn'tFilled
17 {
18 filled=FALSE;//filledBecomesFalse
19 }
20 glBegin(GL_LINES);//StartDrawingHorizontalCellBorders
21 glVertex2d(20+(loop1*60),70+(loop2*40));//LeftSideOfHorizontalLine
22 glVertex2d(80+(loop1*60),70+(loop2*40));//RightSideOfHorizontalLine
23 glEnd();//DoneDrawingHorizontalCellBorders
24 }
Thecodebelowdoesthesamething,butitcheckstomakesurethelineisn'tbeingdrawntoofardownthescreeninsteadoftoofar
right.Thiscodeisresponsiblefordrawingverticallines.
?
1 glColor3f(0.0f,0.5f,1.0f);//SetLineColorToBlue
2 if(vline[loop1][loop2])//HasTheHorizontalLineBeenTraced
3 {
4 glColor3f(1.0f,1.0f,1.0f);//IfSo,SetLineColorToWhite
5 }
6 if(loop2<10)//DontDrawToFarDown
7 {
8 if(!vline[loop1][loop2])//IfAVerticleLineIsn'tFilled
9 {
10 filled=FALSE;//filledBecomesFalse
11 }
12 glBegin(GL_LINES);//StartDrawingVerticleCellBorders
13 glVertex2d(20+(loop1*60),70+(loop2*40));//LeftSideOfHorizontalLine
14 glVertex2d(20+(loop1*60),110+(loop2*40));//RightSideOfHorizontalLine
15 glEnd();//DoneDrawingVerticleCellBorders
16 }
Nowwechecktoseeif4sidesofaboxaretraced.Eachboxonthescreenis1/10thofafullscreenpicture.Becauseeachboxispiece
ofalargertexture,thefirstthingweneedtodoisenabletexturemapping.Wedon'twantthetexturetobetintedred,greenorblueso
wesetthecolortobrightwhite.Afterthecolorissettowhiteweselectourgridtexture(texture[1]).
Thenextthingwedoischecktoseeifwearecheckingaboxthatexistsonthescreen.Rememberthatourloopdrawsthe11lines
rightandleftand11linesupanddown.Butwedonthave11boxes.Wehave10boxes.Sowehavetomakesurewedon'tcheckthe
11thposition.Wedothisbymakingsurebothloop1andloop2islessthan10.That's10boxesfrom09.
Afterwehavemadesurethatweareinboundswecanstartcheckingtheborders.hline[loop1][loop2]isthetopofabox.hline[loop1]
[loop2+1]isthebottomofabox.vline[loop1][loop2]istheleftsideofaboxandvline[loop1+1][loop2]istherightsideofabox.Hopefully
Icanclearthingsupwithadiagram:

Allhorizontallinesareassumedtorunfromloop1toloop1+1.Asyoucansee,thefirsthorizontallinerunsalongloop2.Thesecond
horizontallinerunsalongloop2+1.Verticallinesareassumedtorunfromloop2toloop2+1.Thefirstverticallinerunsalongloop1and
thesecondverticallinerunsalongloop1+1.
Whenloop1isincreased,therightsideofouroldboxbecomestheleftsideofthenewbox.Whenloop2isincreased,thebottomofthe
oldboxbecomesthetopofthenewbox.
Ifall4bordersareTRUE(meaningwe'vepassedoverthemall)wecantexturemapthebox.Wedothisthesamewaywebrokethe
fonttextureintoseperateletters.Wedividebothloop1andloop2by10becausewewanttomapthetextureacross10boxesfromleft
torightand10boxesupanddown.Texturecoordinatesrunfrom0.0fto1.0fand1/10thof1.0fis0.1f.
Sotogetthetoprightsideofourboxwedividetheloopvaluesby10andadd0.1ftothextexturecoordinate.Togetthetopleftsideof
theboxwedivideourloopvaluesby10.Togetthebottomleftsideoftheboxwedivideourloopvaluesby10andadd0.1ftothey
texturecoordinate.Finallytogetthebottomrighttexturecoordinatewedividetheloopvaluesby10andadd0.1ftoboththexandy
texturecoordinates.
Quickexamples:
loop1=0andloop2=0

RightXTextureCoordinate=loop1/10+0.1f=0/10+0.1f=0+0.1f=0.1f
LeftXTextureCoordinate=loop1/10=0/10=0.0f
TopYTextureCoordinate=loop2/10=0/10=0.0f
BottomYTextureCoordinate=loop2/10+0.1f=0/10+0.1f=0+0.1f=0.1f

loop1=1andloop2=1

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

8/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

RightXTextureCoordinate=loop1/10+0.1f=1/10+0.1f=0.1f+0.1f=0.2f
LeftXTextureCoordinate=loop1/10=1/10=0.1f
TopYTextureCoordinate=loop2/10=1/10=0.1f
BottomYTextureCoordinate=loop2/10+0.1f=1/10+0.1f=0.1f+0.1f=0.2f

Hopefullythatallmakessense.Ifloop1andloop2wereequalto9wewouldendupwiththevalues0.9fand1.0f.Soasyoucansee
ourtexturecoordinatesmappedacrossthe10boxesrunfrom0.0fatthelowestand1.0fatthehighest.Mappingtheentiretextureto
thescreen.Afterwe'vemappedasectionofthetexturetothescreen,wedisabletexturemapping.Oncewe'vedrawnallthelinesand
filledinalltheboxes,wesetthelinewidthto1.0f.
?
glEnable(GL_TEXTURE_2D);//EnableTextureMapping
1
glColor3f(1.0f,1.0f,1.0f);//BrightWhiteColor
2
glBindTexture(GL_TEXTURE_2D,texture[1]);//SelectTheTileImage
3
if((loop1<10)&&(loop2<10))//IfInBounds,FillInTracedBoxes
4
{
5
//AreAllSidesOfTheBoxTraced?
6
if(hline[loop1][loop2]&&hline[loop1][loop2+1]&&vline[loop1][loop2]&&vline[loop1+1]
7
[loop2])
8
{
9
glBegin(GL_QUADS);//DrawATexturedQuad
10
glTexCoord2f(float(loop1/10.0f)+0.1f,1.0f(float(loop2/10.0f)));
11
glVertex2d(20+(loop1*60)+59,(70+loop2*40+1));//TopRight
12
glTexCoord2f(float(loop1/10.0f),1.0f(float(loop2/10.0f)));
13
glVertex2d(20+(loop1*60)+1,(70+loop2*40+1));//TopLeft
14
glTexCoord2f(float(loop1/10.0f),1.0f(float(loop2/10.0f)+0.1f));
15
glVertex2d(20+(loop1*60)+1,(70+loop2*40)+39);//BottomLeft
16
glTexCoord2f(float(loop1/10.0f)+0.1f,1.0f(float(loop2/10.0f)+0.1f));
17
glVertex2d(20+(loop1*60)+59,(70+loop2*40)+39);//BottomRight
18
glEnd();//DoneTexturingTheBox
19
}
20
}
21
glDisable(GL_TEXTURE_2D);//DisableTextureMapping
22
}
23
}
24
glLineWidth(1.0f);//SetTheLineWidthTo1.0f
ThecodebelowcheckstoseeifantiisTRUE.Ifitis,weenablelinesmoothing(antialiasing).
?
1 if(anti)//IsAntiTRUE?
2{
3 glEnable(GL_LINE_SMOOTH);//IfSo,EnableAntialiasing
4}
TomakethegamealittleeasierI'veaddedaspecialitem.Theitemisanhourglass.Whenyoutouchthehourglass,theenemiesare
frozenforaspecificamountoftime.Thefollowingsectionofcodeisresposiblefordrawingthehourglass.
Forthehourglassweusexandytopositionthetimer,butunlikeourplayerandenemieswedon'tusefxandfyforfinepositioning.
Insteadwe'llusefxtokeeptrackofwhetherornotthetimerisbeingdisplayed.fxwillequal0ifthetimerisnotvisible.1ifitisvisible,
and2iftheplayerhastouchedthetimer.fywillbeusedasacountertokeeptrackofhowlongthetimershouldbevisibleorinvisible.
Sowestartoffbycheckingtoseeifthetimerisvisible.Ifnot,weskipoverthecodewithoutdrawingthetimer.Ifthetimerisvisible,we
resetthemodelviewmatrix,andpositionthetimer.Becauseourfirstgridpointfromlefttorightstartsat20,wewilladdhourglass.x
times60to20.Wemultiplyhourglass.xby60becausethepointsonourgridfromlefttorightarespaced60pixelsapart.Wethen
positionthehourglassontheyaxis.Weaddhourglass.ytimes40to70.0fbecausewewanttostartdrawing70pixelsdownfromthe
topofthescreen.Eachpointonourgridfromtoptobottomisspaced40pixelsapart.
Afterwehavepositionedthehourglass,wecanrotateitonthezaxis.hourglass.spinisusedtokeeptrackoftherotation,thesame
wayplayer.spinkeepstrackoftheplayerrotation.Beforewestarttodrawthehourglassweselectarandomcolor.
?
if(hourglass.fx==1)//Iffx=1DrawTheHourglass
1
{
2
glLoadIdentity();//ResetTheModelviewMatrix
3
glTranslatef(20.0f+(hourglass.x*60),70.0f+(hourglass.y*40),0.0f);//MoveToTheFineHourglass
4
Position
5
glRotatef(hourglass.spin,0.0f,0.0f,1.0f);//RotateClockwise
6
glColor3ub(rand()%255,rand()%255,rand()%255);//SetHourglassColorToRandomColor
glBegin(GL_LINES)tellsOpenGLwewanttodrawusinglines.Westartoffbymovingleftandup5pixelsfromourcurrentlocation.
Thisgivesusthetopleftpointofourhourglass.OpenGLwillstartdrawingthelinefromthislocation.Theendofthelinewillbe5pixels
rightanddownfromouroriginallocation.Thisgivesusalinerunningfromthetoplefttothebottomright.Immediatelyafterthatwe
drawasecondlinerunningfromthetoprighttothebottomleft.Thisgivesusan'X'.Wefinishoffbyconnectingthebottomtwopoints
together,andthenthetoptwopointstocreateanhourglasstypeobject:)
?
1 glBegin(GL_LINES);//StartDrawingOurHourglassUsingLines
2 glVertex2d(5,5);//TopLeftOfHourglass
3 glVertex2d(5,5);//BottomRightOfHourglass
4 glVertex2d(5,5);//TopRightOfHourglass
5 glVertex2d(5,5);//BottomLeftOfHourglass
6 glVertex2d(5,5);//BottomLeftOfHourglass
7 glVertex2d(5,5);//BottomRightOfHourglass
8 glVertex2d(5,5);//TopLeftOfHourglass
9 glVertex2d(5,5);//TopRightOfHourglass
10 glEnd();//DoneDrawingTheHourglass
11 }
Nowwedrawourplayer.Weresetthemodelviewmatrix,andpositiontheplayeronthescreen.Noticewepositiontheplayerusingfx

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

9/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

andfy.Wewanttheplayertomovesmoothlysoweusefinepositioning.Afterpositioningtheplayer,werotatetheplayeronit'szaxis
usingplayer.spin.Wesetthecolortolightgreenandbegindrawing.Justlikethecodeweusedtodrawthehourglass,wedrawan'X'.
Startingatthetoplefttothebottomright,thenfromthetoprighttothebottomleft.
?
1 glLoadIdentity();//ResetTheModelviewMatrix
2 glTranslatef(player.fx+20.0f,player.fy+70.0f,0.0f);//MoveToTheFinePlayerPosition
3 glRotatef(player.spin,0.0f,0.0f,1.0f);//RotateClockwise
4 glColor3f(0.0f,1.0f,0.0f);//SetPlayerColorToLightGreen
5 glBegin(GL_LINES);//StartDrawingOurPlayerUsingLines
6 glVertex2d(5,5);//TopLeftOfPlayer
7 glVertex2d(5,5);//BottomRightOfPlayer
8 glVertex2d(5,5);//TopRightOfPlayer
9 glVertex2d(5,5);//BottomLeftOfPlayer
10 glEnd();//DoneDrawingThePlayer
Drawinglowdetailobjectswithlinescanbealittlefrustrating.Ididn'twanttheplayertolookboringsoIaddedthenextsectionofcode
tocreatealargerandquickerspinningbladeontopoftheplayerthatwedrewabove.Werotateonthezaxisbyplayer.spintimes0.5f.
Becausewearerotatingagain,itwillappearasifthispieceoftheplayerismovingalittlequickerthanthefirstpieceoftheplayer.
Afterdoingthenewrotation,wesetthecolortoadarkershadeofgreen.Sothatitactuallylooksliketheplayerismadeupofdifferent
colors/pieces.Wethendrawalarge'+'ontopofthefirstpieceoftheplayer.It'slargerbecausewe'reusing7and+7insteadof5and
+5.Alsonoticethatinsteadofdrawingfromonecornertoanother,I'mdrawingthispieceoftheplayerfromlefttorightandtopto
bottom.
?
1 glRotatef(player.spin*0.5f,0.0f,0.0f,1.0f);//RotateClockwise
2 glColor3f(0.0f,0.75f,0.0f);//SetPlayerColorToDarkGreen
3 glBegin(GL_LINES);//StartDrawingOurPlayerUsingLines
4 glVertex2d(7,0);//LeftCenterOfPlayer
5 glVertex2d(7,0);//RightCenterOfPlayer
6 glVertex2d(0,7);//TopCenterOfPlayer
7 glVertex2d(0,7);//BottomCenterOfPlayer
8 glEnd();//DoneDrawingThePlayer
Allwehavetodonowisdrawtheenemies,andwe'redonedrawing:)Westartoffbycreatingaloopthatwillloopthroughallthe
enemiesvisibleonthecurrentlevel.Wecalculatehowmanyenemiestodrawbymultiplyingourcurrentgamestagebythegames
internallevel.Rememberthateachlevelhas3stages,andthemaximumvalueoftheinternallevelis3.Sowecanhaveamaximumof
9enemies.
Insidetheloopweresetthemodelviewmatrix,andpositionthecurrentenemy(enemy[loop1]).Wepositiontheenemyusingit'sfinex
andyvalues(fxandfy).Afterpositioningthecurrentenemywesetthecolortopinkandstartdrawing.
Thefirstlinewillrunfrom0,7(7pixelsupfromthestartinglocation)to7,0(7pixelsleftofthestartinglocation).Thesecondlineruns
from7,0to0,7(7pixelsdownfromthestartinglocation).Thethirdlinerunsfrom0,7to7,0(7pixelstotherightofourstartinglocation),
andthelastlinerunsfrom7,0backtothebeginningofthefirstline(7pixelsupfromthestartinglocation).Thiscreatesanonspinning
pinkdiamondonthescreen.
?
1 for(loop1=0;loop1<(stage*level);loop1++)//LoopToDrawEnemies
2 {
3 glLoadIdentity();//ResetTheModelviewMatrix
4 glTranslatef(enemy[loop1].fx+20.0f,enemy[loop1].fy+70.0f,0.0f);
5 glColor3f(1.0f,0.5f,0.5f);//MakeEnemyBodyPink
6 glBegin(GL_LINES);//StartDrawingEnemy
7 glVertex2d(0,7);//TopPointOfBody
8 glVertex2d(7,0);//LeftPointOfBody
9 glVertex2d(7,0);//LeftPointOfBody
10 glVertex2d(0,7);//BottomPointOfBody
11 glVertex2d(0,7);//BottomPointOfBody
12 glVertex2d(7,0);//RightPointOfBody
13 glVertex2d(7,0);//RightPointOfBody
14 glVertex2d(0,7);//TopPointOfBody
15 glEnd();//DoneDrawingEnemyBody
Wedon'twanttheenemytolookboringeithersowe'lladdadarkredspinningblade('X')ontopofthediamondthatwejustdrew.We
rotateonthezaxisbyenemy[loop1].spin,andthendrawthe'X'.Westartatthetopleftanddrawalinetothebottomright.Thenwe
drawasecondlinefromthetoprighttothebottomleft.Thetwolinescrosseachothercreatingan'X'(orblade...grin).
?
1 glRotatef(enemy[loop1].spin,0.0f,0.0f,1.0f);//RotateTheEnemyBlade
2 glColor3f(1.0f,0.0f,0.0f);//MakeEnemyBladeRed
3 glBegin(GL_LINES);//StartDrawingEnemyBlade
4 glVertex2d(7,7);//TopLeftOfEnemy
5 glVertex2d(7,7);//BottomRightOfEnemy
6 glVertex2d(7,7);//BottomLeftOfEnemy
7 glVertex2d(7,7);//TopRightOfEnemy
8 glEnd();//DoneDrawingEnemyBlade
9 }
10 returnTRUE;//EverythingWentOK
11 }
IaddedtheKillFont()commandtotheendofKillGLWindow().Thismakessurethefontdisplaylistisdestroyedwhenthewindowis
destroyed.
?
1
2
3
4
5
6

GLvoidKillGLWindow(GLvoid)//ProperlyKillTheWindow
{
if(fullscreen)//AreWeInFullscreenMode?
{
ChangeDisplaySettings(NULL,0);//IfSoSwitchBackToTheDesktop
ShowCursor(TRUE);//ShowMousePointer

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

10/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

7 }
8
9 if(hRC)//DoWeHaveARenderingContext?
10 {
11 if(!wglMakeCurrent(NULL,NULL))//AreWeAbleToReleaseTheDCAndRCContexts?
12 {
13 MessageBox(NULL,"ReleaseOfDCAndRCFailed.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
14 }
15
16 if(!wglDeleteContext(hRC))//AreWeAbleToDeleteTheRC?
17 {
18 MessageBox(NULL,"ReleaseRenderingContextFailed.","SHUTDOWNERROR",MB_OK|
19 MB_ICONINFORMATION);
20 }
21 hRC=NULL;//SetRCToNULL
22 }
23
24 if(hDC&&!ReleaseDC(hWnd,hDC))//AreWeAbleToReleaseTheDC
25 {
26 MessageBox(NULL,"ReleaseDeviceContextFailed.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
27 hDC=NULL;//SetDCToNULL
28 }
29
30 if(hWnd&&!DestroyWindow(hWnd))//AreWeAbleToDestroyTheWindow?
31 {
32 MessageBox(NULL,"CouldNotReleasehWnd.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
33 hWnd=NULL;//SethWndToNULL
34 }
35
36 if(!UnregisterClass("OpenGL",hInstance))//AreWeAbleToUnregisterClass
37 {
38 MessageBox(NULL,"CouldNotUnregisterClass.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
39 hInstance=NULL;//SethInstanceToNULL
40 }
41
42 KillFont();//KillTheFontWeBuilt
}
TheCreateGLWindow()andWndProc()codehasn'tchangedsosearchuntilyoufindthefollowingsectionofcode.
?
intWINAPIWinMain(HINSTANCEhInstance,//Instance
1
HINSTANCEhPrevInstance,//PreviousInstance
2
LPSTRlpCmdLine,//CommandLineParameters
3
intnCmdShow)//WindowShowState
4
{
5
MSGmsg;//WindowsMessageStructure
6
BOOLdone=FALSE;//BoolVariableToExitLoop
7

8
//AskTheUserWhichScreenModeTheyPrefer
9
if(MessageBox(NULL,"WouldYouLikeToRunInFullscreenMode?","Start
10
FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
11
{
12
fullscreen=FALSE;//WindowedMode
13
}
Thissectionofcodehasn'tchangedthatmuch.Ichangedthewindowtitletoread"NeHe'sLineTutorial",andIaddedthe
ResetObjects()command.Thissetstheplayertothetopleftpointofthegrid,andgivestheenemiesrandomstartinglocations.The
enemieswillalwaysstartoffatleast5tilesawayfromyou.TimerInit()initializesthetimersoit'ssetupproperly.
?
1 if(!CreateGLWindow("NeHe'sLineTutorial",640,480,16,fullscreen))//CreateOurOpenGLWindow
2 {
3 return0;//QuitIfWindowWasNotCreated
4 }
5
6 ResetObjects();//SetPlayer/EnemyStartingPositions
7 TimerInit();//InitializeTheTimer
8
9 while(!done)//LoopThatRunsWhiledone=FALSE
10 {
11 if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))//IsThereAMessageWaiting?
12 {
13 if(msg.message==WM_QUIT)//HaveWeReceivedAQuitMessage?
14 {
15 done=TRUE;//IfSodone=TRUE
16 }
17 else//IfNot,DealWithWindowMessages
18 {
19 TranslateMessage(&msg);//TranslateTheMessage
20 DispatchMessage(&msg);//DispatchTheMessage
21 }
22 }
23 else//IfThereAreNoMessages
24 {
Nowtomakethetimingcodework.Noticebeforewedrawourscenewegrabthetime,andstoreitinafloatingpointvariablecalled
start.Wethendrawthesceneandswapbuffers.
Immediatelyafterweswapthebufferswecreateadelay.Wedothisbycheckingtoseeifthecurrentvalueofthetimer(TimerGetTime(
))islessthanourstartingvalueplusthegamesteppingspeedtimes2.Ifthecurrenttimervalueislessthanthevaluewewant,we

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

11/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

endlesslyloopuntilthecurrenttimervalueisequaltoorgreaterthanthevaluewewant.ThisslowsdownREALLYfastsystems.
Becauseweusethesteppingspeed(setbythevalueofadjust)theprogramwillalwaysrunthesamespeed.Forexample,ifour
steppingspeedwas1wewouldwaituntilthetimerwasgreaterthanorequalto2(1*2).Butifweincreasedthesteppingspeedto2
(causingtheplayertomovetwiceasmanypixelsatatime),thedelayisincreasedto4(2*2).Soeventhoughwearemovingtwiceas
fast,thedelayistwiceaslong,sothegamestillrunsthesamespeed:)
Onethingalotofpeopleliketodoistakethecurrenttime,andsubtracttheoldtimetofindouthowmuchtimehaspassed.Thenthey
moveobjectsacertaindistancebasedontheamountoftimethathaspassed.UnfortunatelyIcan'tdothatinthisprogrambecausethe
finemovementhastobeexactsothattheplayercanlineupwiththelinesonthegrid.Ifthecurrentfinexpositionwas59andthe
computerdecidedtheplayerneededtomovetwopixels,theplayerwouldneverlineupwiththeverticallineatposition60onthegrid.
?
1 floatstart=TimerGetTime();//GrabTimerValueBeforeWeDraw
2
3 //DrawTheScene.WatchForESCKeyAndQuitMessagesFromDrawGLScene()
4 if((active&&!DrawGLScene())||keys[VK_ESCAPE])//Active?WasThereAQuitReceived?
5 {
6 done=TRUE;//ESCorDrawGLSceneSignalledAQuit
7 }
8 else//NotTimeToQuit,UpdateScreen
9 {
10 SwapBuffers(hDC);//SwapBuffers(DoubleBuffering)
11 }
12
13 while(TimerGetTime()<start+float(steps[adjust]*2.0f)){}//WasteCyclesOnFastSystems
Thefollowingcodehasn'treallychanged.Ichangedthetitleofthewindowtoread"NeHe'sLineTutorial".
?
1 if(keys[VK_F1])//IsF1BeingPressed?
2 {
3 keys[VK_F1]=FALSE;//IfSoMakeKeyFALSE
4 KillGLWindow();//KillOurCurrentWindow
5 fullscreen=!fullscreen;//ToggleFullscreen/WindowedMode
6 //RecreateOurOpenGLWindow
7 if(!CreateGLWindow("NeHe'sLineTutorial",640,480,16,fullscreen))
8 {
9 return0;//QuitIfWindowWasNotCreated
10 }
11 }
ThissectionofcodecheckstoseeiftheAkeyisbeingpressedandnotheld.If'A'isbeingpressed,apbecomesTRUE(tellingour
programthatAisbeinghelddown),andantiistoggledfromTRUEtoFALSEorFALSEtoTRUE.Rememberthatantiischeckedinthe
drawingcodetoseeifantialiasingisturnedonoroff.
Ifthe'A'keyhasbeenreleased(isFALSE)thenapissettoFALSEtellingtheprogramthatthekeyisnolongerbeinghelddown.
?
1 if(keys['A']&&!ap)//If'A'KeyIsPressedAndNotHeld
2{
3 ap=TRUE;//apBecomesTRUE
4 anti=!anti;//ToggleAntialiasing
5}
6 if(!keys['A'])//If'A'KeyHasBeenReleased
7{
8 ap=FALSE;//apBecomesFALSE
9}
Nowtomovetheenemies.Iwantedtokeepthissectionofcodereallysimple.Thereisverylittlelogic.Basically,theenemiescheckto
seewhereyouareandtheymoveinthatdirection.BecauseI'mcheckingtheactualxandypositionoftheplayersandnothefine
values,theplayersseemtohavealittlemoreintelligence.Theymayseethatyouarewayatthetopofthescreen.Butbythetime
they'refinevalueactuallygetstothetopofthescreen,youcouldalreadybeinadifferentlocation.Thiscausesthemtosometimes
movepastyou,beforetheyrealizeyouarenolongerwheretheythoughtyouwere.Maysoundlikethey'rereallydumb,butbecause
theysometimesmovepastyou,youmightfindyourselfbeingboxedinfromalldirections.
Westartoffbycheckingtomakesurethegameisn'tover,andthatthewindow(ifinwindowedmode)isstillactive.Bycheckingactive
theenemieswontmoveifthescreenisminimized.Thisgivesyouaconvenientpausefeaturewhenyouneedtotakeabreak:)
Afterwe'vemadesuretheenemiesshouldbemoving,wecreatealoop.Theloopwillloopthroughallthevisibleenemies.Againwe
calculatehowmanyenemiesshouldbeonthescreenbymultiplyingthecurrentstagebythecurrentinternallevel.
?
1 if(!gameover&&active)//IfGameIsn'tOverAndProgramsActiveMoveObjects
2{
3 for(loop1=0;loop1<(stage*level);loop1++)//LoopThroughTheDifferentStages
4 {
Nowwemovethecurrentenemy(enemy[loop1]).Westartoffbycheckingtoseeiftheenemy'sxpositionislessthantheplayersx
positionandwemakesurethattheenemy'sfineypositionlinesupwithahorizontalline.Wecan'tmovetheenemyleftandrightifit's
notonahorizontalline.Ifwedid,theenemywouldcutrightthroughthemiddleoftheboxes,makingthegameevenmoredifficult:)
Iftheenemyxpositionislessthantheplayerxposition,andtheenemy'sfineypositionislinedupwithahorizontalline,wemovethe
enemyxpositiononeblockclosertothecurrentplayerposition.
Wealsodothistomovetheenemyleft,downandup.Whenmovingupanddown,weneedtomakesuretheenemy'sfinexposition
linesupwithaverticalline.Wedon'twanttheenemycuttingthroughthetoporbottomofabox.
Note:changingtheenemiesxandypositionsdoesn'tmovetheenemyonthescreen.Rememberthatwhenwedrewtheenemieswe
usedthefinepositionstoplacetheenemiesonthescreen.ChangingthexandypositionsjusttellsourprogramwhereweWANTthe
enemiestomove.
?

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

12/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

1 if((enemy[loop1].x<player.x)&&(enemy[loop1].fy==enemy[loop1].y*40))
2 {
3 enemy[loop1].x++;//MoveTheEnemyRight
4 }
5
6 if((enemy[loop1].x>player.x)&&(enemy[loop1].fy==enemy[loop1].y*40))
7 {
8 enemy[loop1].x;//MoveTheEnemyLeft
9 }
10
11 if((enemy[loop1].y<player.y)&&(enemy[loop1].fx==enemy[loop1].x*60))
12 {
13 enemy[loop1].y++;//MoveTheEnemyDown
14 }
15
16 if((enemy[loop1].y>player.y)&&(enemy[loop1].fx==enemy[loop1].x*60))
17 {
18 enemy[loop1].y;//MoveTheEnemyUp
19 }
Thiscodedoestheactualmoving.Wechecktoseeifthevariabledelayisgreaterthan3minusthecurrentinternallevel.Thatwayif
ourcurrentlevelis1theprogramwillloopthrough2(31)timesbeforetheenemiesactuallymove.Onlevel3(thehighestvaluethat
levelcanbe)theenemieswillmovethesamespeedastheplayer(nodelays).Wealsomakesurethathourglass.fxisn'tthesameas
2.Remember,ifhourglass.fxisequalto2,thatmeanstheplayerhastouchedthehourglass.Meaningtheenemiesshouldn'tbe
moving.
Ifdelayisgreaterthan3levelandtheplayerhasn'ttouchedthehourglass,wemovetheenemiesbyadjustingtheenemyfine
positions(fxandfy).Thefirstthingwedoissetdelaybackto0sothatwecanstartthedelaycounteragain.Thenwesetupaloopthat
loopsthroughallthevisibleenemies(stagetimeslevel).
?
1 if(delay>(3level)&&(hourglass.fx!=2))//IfOurDelayIsDoneAndPlayerDoesn'tHaveHourglass
2{
3 delay=0;//ResetTheDelayCounterBackToZero
4 for(loop2=0;loop2<(stage*level);loop2++)//LoopThroughAllTheEnemies
5 {
Tomovetheenemieswechecktoseeifthecurrentenemy(enemy[loop2])needstomoveinaspecificdirectiontomovetowardsthe
enemyxandypositionwewant.Inthefirstlinebelowwechecktoseeiftheenemyfinepositiononthexaxisislessthanthedesiredx
positiontimes60.(remembereachgridcrossingis60pixelsapartfromlefttoright).Ifthefinexpositionislessthantheenemyx
positiontimes60wemovetheenemytotherightbysteps[adjust](thespeedourgameissettoplayatbasedonthevalueofadjust).
Wealsorotatetheenemyclockwisetomakeitlooklikeit'srollingtotheright.Wedothisbyincreasingenemy[loop2].spinby
steps[adjust](thecurrentgamespeedbasedonadjust).
Wethenchecktoseeiftheenemyfxvalueisgreaterthantheenemyxpositiontimes60andifso,wemovetheenemyleftandspinthe
enemyleft.
Wedothesamewhenmovingtheenemyupanddown.Iftheenemyypositionislessthantheenemyfypositiontimes40(40pixels
betweengridpointsupanddown)weincreasetheenemyfyposition,androtatetheenemytomakeitlooklikeit'srollingdownwards.
Lastlyiftheenemyypositionisgreaterthantheenemyfypositiontimes40wedecreasethevalueoffytomovetheenemyupward.
Again,theenemyspinstomakeitlooklikeit'srollingupward.
?
if(enemy[loop2].fx<enemy[loop2].x*60)//IsFinePositionOnXAxisLowerThanIntendedPosition?
1 {
2 enemy[loop2].fx+=steps[adjust];//IfSo,IncreaseFinePositionOnXAxis
3 enemy[loop2].spin+=steps[adjust];//SpinEnemyClockwise
4 }
5 if(enemy[loop2].fx>enemy[loop2].x*60)//IsFinePositionOnXAxisHigherThanIntended
6 Position?
7 {
8 enemy[loop2].fx=steps[adjust];//IfSo,DecreaseFinePositionOnXAxis
9 enemy[loop2].spin=steps[adjust];//SpinEnemyCounterClockwise
10 }
11 if(enemy[loop2].fy<enemy[loop2].y*40)//IsFinePositionOnYAxisLowerThanIntendedPosition?
12 {
13 enemy[loop2].fy+=steps[adjust];//IfSo,IncreaseFinePositionOnYAxis
14 enemy[loop2].spin+=steps[adjust];//SpinEnemyClockwise
15 }
16 if(enemy[loop2].fy>enemy[loop2].y*40)//IsFinePositionOnYAxisHigherThanIntended
17 Position?
18 {
19 enemy[loop2].fy=steps[adjust];//IfSo,DecreaseFinePositionOnYAxis
20 enemy[loop2].spin=steps[adjust];//SpinEnemyCounterClockwise
21 }
22 }
}
Aftermovingtheenemieswechecktoseeifanyofthemhavehittheplayer.Wewantaccuracysowecomparetheenemyfine
positionswiththeplayerfinepositions.Iftheenemyfxpositionequalstheplayerfxpositionandtheenemyfypositionequalsthe
playerfypositiontheplayerisDEAD:)
Iftheplayerisdead,wedecreaselives.Thenwechecktomakesuretheplayerisn'toutoflivesbycheckingtoseeiflivesequals0.If
livesdoesequalzero,wesetgameovertoTRUE.
WethenresetourobjectsbycallingResetObjects(),andplaythedeathsound.
Soundisnewinthistutorial.I'vedecidedtousethemostbasicsoundroutineavailable...PlaySound().PlaySound()takesthree
parameters.Firstwegiveitthenameofthefilewewanttoplay.InthiscasewewantittoplaytheDie.WAVfileintheDatadirectory.
Thesecondparametercanbeignored.We'llsetittoNULL.Thethirdparameteristheflagforplayingthesound.Thetwomost
commonflagsare:SND_SYNCwhichstopseverythingelseuntilthesoundisdoneplaying,andSND_ASYNC,whichplaysthesound,
butdoesn'tstoptheprogramfromrunning.WewantalittledelayaftertheplayerdiessoweuseSND_SYNC.Prettyeasy!

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

13/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

TheonethingIforgottomentionatthebeginningoftheprogram:InorderforPlaySound()andthetimertowork,youhavetoinclude
theWINMM.LIBfileunderPROJECT/SETTINGS/LINKinVisualC++.Winmm.libistheWindowsMultimediaLibrary.Ifyoudon't
includethislibrary,youwillgeterrormessageswhenyoutrytocompiletheprogram.
?
1 //AreAnyOfTheEnemiesOnTopOfThePlayer?
2 if((enemy[loop1].fx==player.fx)&&(enemy[loop1].fy==player.fy))
3 {
4 lives;//IfSo,PlayerLosesALife
5
6 if(lives==0)//AreWeOutOfLives?
7 {
8 gameover=TRUE;//IfSo,gameoverBecomesTRUE
9 }
10
11 ResetObjects();//ResetPlayer/EnemyPositions
12 PlaySound("Data/Die.wav",NULL,SND_SYNC);//PlayTheDeathSound
13 }
14 }
Nowwecanmovetheplayer.Inthefirstlineofcodebelowwechecktoseeiftherightarrowisbeingpressed,player.xislessthan10
(don'twanttogooffthegrid),thatplayer.fxequalsplayer.xtimes60(linedupwithagridcrossingonthexaxis,andthatplayer.fy
equalsplayer.ytimes40(playerislinedupwithagridcrossingontheyaxis).
Ifwedidn'tmakesuretheplayerwasatacrossing,andweallowedtheplayertomoveanyways,theplayerwouldcutrightthroughthe
middleofboxes,justliketheenemieswouldhavedoneifwedidn'tmakesuretheywerelinedupwithaverticalorhorizontalline.
Checkingthisalsomakessuretheplayerisdonemovingbeforewemovetoanewlocation.
Iftheplayerisatagridcrossing(whereaverticalandhorizontallinesmeet)andhe'snottofarright,wemarkthecurrenthorizontalline
thatweareonasbeingtracedover.Wethenincreasetheplayer.xvaluebyone,causingthenewplayerpositiontobeoneboxtothe
right.
Wedothesamethingwhilemovingleft,downandup.Whenmovingleft,wemakesuretheplayerwontbegoingofftheleftsideofthe
grid.Whenmovingdownwemakesuretheplayerwontbeleavingthebottomofthegrid,andwhenmovingupwemakesurethe
playerdoesn'tgooffthetopofthegrid.
Whenmovingleftandrightwemakethehorizontalline(hline[][])underusTRUEmeaningit'sbeentraced.Whenmovingupand
downwemaketheverticalline(vline[][])underusTRUEmeaningithasbeentraced.
?
1 if(keys[VK_RIGHT]&&(player.x<10)&&(player.fx==player.x*60)&&(player.fy==player.y*40))
2 {
3 hline[player.x][player.y]=TRUE;//MarkTheCurrentHorizontalBorderAsFilled
4 player.x++;//MoveThePlayerRight
5 }
6 if(keys[VK_LEFT]&&(player.x>0)&&(player.fx==player.x*60)&&(player.fy==player.y*40))
7 {
8 player.x;//MoveThePlayerLeft
9 hline[player.x][player.y]=TRUE;//MarkTheCurrentHorizontalBorderAsFilled
10 }
11 if(keys[VK_DOWN]&&(player.y<10)&&(player.fx==player.x*60)&&(player.fy==player.y*40))
12 {
13 vline[player.x][player.y]=TRUE;//MarkTheCurrentVerticleBorderAsFilled
14 player.y++;//MoveThePlayerDown
15 }
16 if(keys[VK_UP]&&(player.y>0)&&(player.fx==player.x*60)&&(player.fy==player.y*40))
17 {
18 player.y;//MoveThePlayerUp
19 vline[player.x][player.y]=TRUE;//MarkTheCurrentVerticleBorderAsFilled
20 }
Weincrease/decreasetheplayerfinefxandfyvariablesthesamewayweincrease/decreasedtheenemyfinefxandfyvariables.
Iftheplayerfxvalueislessthantheplayerxvaluetimes60weincreasetheplayerfxpositionbythestepspeedourgameisrunningat
basedonthevalueofadjust.
Iftheplayerfxvalueisgreaterthantheplayerxvaluetimes60wedecreasetheplayerfxpositionbythestepspeedourgameis
runningatbasedonthevalueofadjust.
Iftheplayerfyvalueislessthantheplayeryvaluetimes40weincreasetheplayerfypositionbythestepspeedourgameisrunningat
basedonthevalueofadjust.
Iftheplayerfyvalueisgreaterthantheplayeryvaluetimes40wedecreasetheplayerfypositionbythestepspeedourgameis
runningatbasedonthevalueofadjust.
?
1 if(player.fx<player.x*60)//IsFinePositionOnXAxisLowerThanIntendedPosition?
2 {
3 player.fx+=steps[adjust];//IfSo,IncreaseTheFineXPosition
4 }
5 if(player.fx>player.x*60)//IsFinePositionOnXAxisGreaterThanIntendedPosition?
6 {
7 player.fx=steps[adjust];//IfSo,DecreaseTheFineXPosition
8 }
9 if(player.fy<player.y*40)//IsFinePositionOnYAxisLowerThanIntendedPosition?
10 {
11 player.fy+=steps[adjust];//IfSo,IncreaseTheFineYPosition
12 }
13 if(player.fy>player.y*40)//IsFinePositionOnYAxisLowerThanIntendedPosition?
14 {
15 player.fy=steps[adjust];//IfSo,DecreaseTheFineYPosition
16 }

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

14/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

17 }
Ifthegameisoverthefollowingbitofcodewillrun.Wechecktoseeifthespacebarisbeingpressed.IfitiswesetgameovertoFALSE
(startingthegameover).WesetfilledtoTRUE.Thiscausesthegametothinkwe'vefinishedastage,causingtheplayertobereset,
alongwiththeenemies.
Wesetthestartinglevelto1,alongwiththeactualdisplayedlevel(level2).Wesetstageto0.Thereasonwedothisisbecauseafter
thecomputerseesthatthegridhasbeenfilledin,itwillthinkyoufinishedastage,andwillincreasestageby1.Becausewesetstage
to0,whenthestageincreasesitwillbecome1(exactlywhatwewant).Lastlywesetlivesbackto5.
?
1 else//Otherwise
2 {
3 if(keys[''])//IfSpacebarIsBeingPressed
4 {
5 gameover=FALSE;//gameoverBecomesFALSE
6 filled=TRUE;//filledBecomesTRUE
7 level=1;//StartingLevelIsSetBackToOne
8 level2=1;//DisplayedLevelIsAlsoSetToOne
9 stage=0;//GameStageIsSetToZero
10 lives=5;//LivesIsSetToFive
11 }
12 }
ThecodebelowcheckstoseeifthefilledflagisTRUE(meaningthegridhasbeenfilledin).filledcanbesettoTRUEoneoftwoways.
EitherthegridisfilledincompletelyandfilledbecomesTRUEorthegamehasendedbutthespacebarwaspressedtorestartit(code
above).
IffilledisTRUE,thefirstthingwedoisplaythecoollevelcompletetune.I'vealreadyexplainedhowPlaySound()works.Thistimewe'll
beplayingtheComplete.WAVfileintheDATAdirectory.Again,weuseSND_SYNCsothatthereisadelaybeforethegamestartson
thenextstage.
Afterthesoundhasplayed,weincreasestagebyone,andchecktomakesurestageisn'tgreaterthan3.Ifstageisgreaterthan3we
setstageto1,andincreasetheinternallevelandvisiblelevelbyone.
Iftheinternallevelisgreaterthan3wesettheinternalleve(level)to3,andincreaselivesby1.Ifyou'reamazingenoughtogetpast
level3youdeserveafreelife:).Afterincreasingliveswechecktomakesuretheplayerdoesn'thavemorethan5lives.Iflivesis
greaterthan5wesetlivesbackto5.
?
1 if(filled)//IsTheGridFilledIn?
2 {
3 PlaySound("Data/Complete.wav",NULL,SND_SYNC);//IfSo,PlayTheLevelCompleteSound
4 stage++;//IncreaseTheStage
5 if(stage>3)//IsTheStageHigherThan3?
6 {
7 stage=1;//IfSo,SetTheStageToOne
8 level++;//IncreaseTheLevel
9 level2++;//IncreaseTheDisplayedLevel
10 if(level>3)//IsTheLevelGreaterThan3?
11 {
12 level=3;//IfSo,SetTheLevelTo3
13 lives++;//GiveThePlayerAFreeLife
14 if(lives>5)//DoesThePlayerHaveMoreThan5Lives?
15 {
16 lives=5;//IfSo,SetLivesToFive
17 }
18 }
19 }
Wethenresetalltheobjects(suchastheplayerandenemies).Thisplacestheplayerbackatthetopleftcornerofthegrid,andgives
theenemiesrandomlocationsonthegrid.
Wecreatetwoloops(loop1andloop2)toloopthroughthegrid.WesetalltheverticalandhorizontallinestoFALSE.Ifwedidn'tdo
this,thenextstagewouldstart,andthegamewouldthinkthegridwasstillfilledin.
Noticetheroutineweusetoclearthegridissimilartotheroutineweusetodrawthegrid.Wehavetomakesurethelinesarenot
beingdrawntofarrightordown.That'swhywechecktomakesurethatloop1islessthan10beforeweresetthehorizontallines,and
wechecktomakesurethatloop2islessthan10beforeweresettheverticallines.
?
1 ResetObjects();//ResetPlayer/EnemyPositions
2
3 for(loop1=0;loop1<11;loop1++)//LoopThroughTheGridXCoordinates
4 {
5 for(loop2=0;loop2<11;loop2++)//LoopThroughTheGridYCoordinates
6 {
7 if(loop1<10)//IfXCoordinateIsLessThan10
8 {
9 hline[loop1][loop2]=FALSE;//SetTheCurrentHorizontalValueToFALSE
10 }
11 if(loop2<10)//IfYCoordinateIsLessThan10
12 {
13 vline[loop1][loop2]=FALSE;//SetTheCurrentVerticalValueToFALSE
14 }
15 }
16 }
17 }
Nowwechecktoseeiftheplayerhashitthehourglass.Ifthefineplayerfxvalueisequaltothehourglassxvaluetimes60andthefine
playerfyvalueisequaltothehourglassyvaluetimes40ANDhourglass.fxisequalto1(meaningthehourglassisdisplayedonthe
screen),thecodebelowruns.

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

15/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

ThefirstlineofcodeisPlaySound("Data/freeze.wav",NULL,SND_ASYNC|SND_LOOP).Thislineplaysthefreeze.WAVfileinthe
DATAdirectory.NoticeweareusingSND_ASYNCthistime.Wewantthefreezesoundtoplaywithoutthegamestopping.SND_LOOP
keepsthesoundplayingendlesslyuntilwetellittostopplaying,oruntilanothersoundisplayed.
Afterwehavestartedthesoundplaying,wesethourglass.fxto2.Whenhourglass.fxequals2thehourglasswillnolongerbedrawn,
theenemieswillstopmoving,andthesoundwillloopendlessly.
Wealsosethourglass.fyto0.hourglass.fyisacounter.Whenithitsacertainvalue,thevalueofhourglass.fxwillchange.
?
1 //IfThePlayerHitsTheHourglassWhileIt'sBeingDisplayedOnTheScreen
2 if((player.fx==hourglass.x*60)&&(player.fy==hourglass.y*40)&&(hourglass.fx==1))
3{
4 //PlayFreezeEnemySound
5 PlaySound("Data/freeze.wav",NULL,SND_ASYNC|SND_LOOP);
6 hourglass.fx=2;//SetThehourglassfxVariableToTwo
7 hourglass.fy=0;//SetThehourglassfyVariableToZero
8}
Thisbitofcodeincreasestheplayerspinvaluebyhalfthespeedthatthegamerunsat.Ifplayer.spinisgreaterthan360.0fwesubtract
360.0ffromplayer.spin.Keepsthevalueofplayer.spinfromgettingtohigh.
?
1 player.spin+=0.5f*steps[adjust];//SpinThePlayerClockwise
2 if(player.spin>360.0f)//IsThespinValueGreaterThan360?
3{
4 player.spin=360;//IfSo,Subtract360
5}
Thecodebelowdecreasesthehourglassspinvalueby1/4thespeedthatthegameisrunningat.Ifhourglass.spinislessthan0.0fwe
add360.0f.Wedon'twanthourglass.spintobecomeanegativenumber.
?
1 hourglass.spin=0.25f*steps[adjust];//SpinTheHourglassCounterClockwise
2 if(hourglass.spin<0.0f)//IsThespinValueLessThan0?
3{
4 hourglass.spin+=360.0f;//IfSo,Add360
5}
ThefirstlinebelowincreasedthehourglasscounterthatIwastalkingabout.hourglass.fyisincreasedbythegamespeed(game
speedisthestepsvaluebasedonthevalueofadjust).
Thesecondlinecheckstoseeifhourglass.fxisequalto0(nonvisible)andthehourglasscounter(hourglass.fy)isgreaterthan6000
dividedbythecurrentinternallevel(level).
Ifthefxvalueis0andthecounterisgreaterthan6000dividedbytheinternallevelweplaythehourglass.WAVfileintheDATA
directory.Wedon'twanttheactiontostopsoweuseSND_ASYNC.Wewon'tloopthesoundthistimethough,sooncethesoundhas
played,itwontplayagain.
Afterwe'veplayedthesoundwegivethehourglassarandomvalueonthexaxis.Weaddonetotherandomvaluesothatthe
hourglassdoesn'tappearattheplayersstartingpositionatthetopleftofthegrid.Wealsogivethehourglassarandomvalueonthey
axis.Wesethourglass.fxto1thismakesthehourglassappearonthescreenatit'snewlocation.Wealsosethourglass.fybacktozero
soitcanstartcountingagain.
Thiscausesthehourglasstoappearonthescreenafterafixedamountoftime.
?
1 hourglass.fy+=steps[adjust];//IncreaseThehourglassfyVariable
2 if((hourglass.fx==0)&&(hourglass.fy>6000/level))//IsThehourglassfxVariableEqualTo0AndThefy
3 {//VariableGreaterThan6000DividedByTheCurrentLevel?
4 PlaySound("Data/hourglass.wav",NULL,SND_ASYNC);//IfSo,PlayTheHourglassAppearsSound
5 hourglass.x=rand()%10+1;//GiveTheHourglassARandomXValue
6 hourglass.y=rand()%11;//GiveTheHourglassARandomYValue
7 hourglass.fx=1;//SethourglassfxVariableToOne(HourglassStage)
8 hourglass.fy=0;//SethourglassfyVariableToZero(Counter)
9}
Ifhourglass.fxisequaltozeroandhourglass.fyisgreaterthan6000dividedbythecurrentinternallevel(level)wesethourglass.fx
backto0,causingthehourglasstodisappear.Wealsosethourglass.fyto0soitcanstartcountingonceagain.
Thiscausesthehourglasstodisappearifyoudon'tgetitafteracertainamountoftime.
?
1 if((hourglass.fx==1)&&(hourglass.fy>6000/level))//IsThehourglassfxVariableEqualTo1AndThefy
2 {//VariableGreaterThan6000DividedByTheCurrentLevel?
3 hourglass.fx=0;//IfSo,SetfxToZero(HourglassWillVanish)
4 hourglass.fy=0;//SetfytoZero(CounterIsReset)
5}
Nowwechecktoseeifthe'freezeenemy'timerhasrunoutaftertheplayerhastouchedthehourglass.
ifhourglass.fxequal2andhourglass.fyisgreaterthan500plus500timesthecurrentinternallevelwekillthetimersoundthatwe
startedplayingendlessly.WekillthesoundwiththecommandPlaySound(NULL,NULL,0).Wesethourglass.fxbackto0,andset
hourglass.fyto0.Settingfxandfyto0startsthehourglasscyclefromthebeginning.fywillhavetohit6000dividedbythecurrent
internallevelbeforethehourglassappearsagain.
?
1 if((hourglass.fx==2)&&(hourglass.fy>500+(500*level)))//IsThehourglassfxVariableEqualTo2AndThefy
2 {//VariableGreaterThan500Plus500TimesTheCurrentLevel?
3 PlaySound(NULL,NULL,0);//IfSo,KillTheFreezeSound
4 hourglass.fx=0;//SethourglassfxVariableToZero
5 hourglass.fy=0;//SethourglassfyVariableToZero
6}

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

16/17

9/18/2015

NeHeProductions:Lines,Antialiasing,Timing,OrthoViewAndSimpleSounds

Thelastthingtodoisincreasethevariabledelay.Ifyouremember,delayisusedtoupdatetheplayermovementandanimation.Ifour
programhasfinished,wekillthewindowandreturntothedesktop.
?
1 delay++;//IncreaseTheEnemyDelayCounter
2 }
3 }
4
5 //Shutdown
6 KillGLWindow();//KillTheWindow
7 return(msg.wParam);//ExitTheProgram
8}
Ispentalongtimewritingthistutorial.Itstartedoutasasimplelinetutorial,andflourishedintoanentertainingminigame.Hopefully
youcanusewhatyouhavelearnedinthistutorialinGLprojectsofyourown.IknowalotofyouhavebeenaskingaboutTILEbased
games.Wellyoucan'tgetmoretiledthanthis:)I'vealsogottenalotofemailsaskinghowtodoexactpixelplotting.IthinkI'vegotit
covered:)Mostimportantly,thistutorialnotonlyteachesyounewthingsaboutOpenGL,italsoteachesyouhowtousesimplesounds
toaddexcitementtoyourvisualworksofart!Ihopeyou'veenjoyedthistutorial.IfyoufeelIhaveincorrectlycommentedsomethingor
thatthecodecouldbedonebetterinsomesections,pleaseletmeknow.IwanttomakethebestOpenGLtutorialsIcanandI'm
interestedinhearingyourfeedback.
Pleasenote,thiswasanextremelylargeprojects.Itriedtocommenteverythingasclearlyaspossible,butputtingwhatthingsinto
wordsisn'taseasyasitmayseem.Iknowhoweverythingworksoffbyheart,buttryingtoexplainisadifferentstory:)Ifyou'veread
throughthetutorialandhaveabetterwaytowordthings,orifyoufeeldiagramsmighthelpout,pleasesendmesuggestions.Iwant
thistutorialtobeeasytofollowthrough.Alsonotethatthisisnotabeginnertutorial.Ifyouhaven'treadthroughtheprevioustutorials
pleasedon'temailmewithquestionsuntilyouhave.Thanks.
JeffMolofee(NeHe)
*DOWNLOADVisualC++CodeForThisLesson.
*DOWNLOADBorlandC++Builder6CodeForThisLesson.(ConversionbyChristianKindahl)
*DOWNLOADCodeWarrior5.3CodeForThisLesson.(ConversionbyScottLupton)
*DOWNLOADDelphiCodeForThisLesson.(ConversionbyMichalTucek)
*DOWNLOADDevC++CodeForThisLesson.(ConversionbyDan)
*DOWNLOADEuphoriaCodeForThisLesson.(ConversionbyEvanMarshall)
*DOWNLOADIrixCodeForThisLesson.(ConversionbyDimi)
*DOWNLOADJavaCodeForThisLesson.(ConversionbyJeffKirby)
*DOWNLOADLCCWin32CodeForThisLesson.(ConversionbyRobertWishlaw)
*DOWNLOADLinuxCodeForThisLesson.(ConversionbyMariusAndra)
*DOWNLOADLinux/GLXCodeForThisLesson.(ConversionbyMihaelVrbanec)
*DOWNLOADLinux/SDLCodeForThisLesson.(ConversionbyTiLeggett)
*DOWNLOADLWJGLCodeForThisLesson.(ConversionbyMarkBernard)
*DOWNLOADMacOSCodeForThisLesson.(ConversionbyAnthonyParker)
*DOWNLOADMacOSX/CocoaCodeForThisLesson.(ConversionbyBryanBlackburn)
*DOWNLOADMASMCodeForThisLesson.(ConversionbyChristophe)
*DOWNLOADVisualC++/OpenILCodeForThisLesson.(ConversionbyDentonWoods)
*DOWNLOADVisualStudio.NETCodeForThisLesson.(ConversionbyGrantJames)

<Lesson20Lesson22>

19972014Gamedev.Allrightsreserved.
NeHeandNeHeProductionsaretrademarksofGameDev.net,LLC
OpenGLisaregisteredtrademarkofSiliconGraphicsInc.

http://nehe.gamedev.net/tutorial/lines_antialiasing_timing_ortho_view_and_simple_sounds/17003/

17/17

You might also like