Anda di halaman 1dari 24

Sin&Cos:TheProgrammer'sPals!

amarillion@yahoo.com
Introduction
InthisarticleIshalldiscussseveralgameprogrammingtechniques,allrevolvingaroundacentraltheme:
thesineandcosinefunctions.Thisarticlewillexplainsine,cosine,vectors,atan2,andsomeusefulspecial
effectssuchashowtomakehomingmissilesandhowbitmaprotationworks.Ishallstartwiththevery
basics,butlateronI'llcoversomemoreadvancedprogrammingtechniques.Thisarticlecomeswithtwelve
reallifecodedexamples,whichyoucandownloadhere.TheyaretestedwithDJGPP,andamakefilefor
DJGPPissupplied.IfyouhaveDJGPP,allyouhavetodoisunzipthesourceandthemakefileintoa
directory,andthenrun"make".Youdon'tneedanylibrariesotherthanAllegro.
Vectors
Let'sstartwithsomethingthatcansometimesbedifficulttounderstandforbeginners,becauseitishighly
abstractthevector.Avectorcanbevisualizedindifferentways.
Firstofall,youcanimagineitasanarrowtoapointinspace.Inthecaseoftwodimensionalspace,you
needtwovaluestodefinethevectoroneforthexcoordinateandonefortheycoordinate.Inthecaseof
threedimensionalspace,youwillneedathirdvalueforthezcoordinate.Thisarticlewillmostlydealwith
twodimensionalspacethough.Threedimensionalspaceismorecomplicated,andI'mnoexpertinthat.
Maybesomebodyelsefeelstheurgetowriteanarticleonthattopic...?

InthefigureaboveIhavedrawnavectorwithanxcoordinateof3andaycoordinateof2.Butthesetwo
valuesarenottheendofthestory.Forexample,ifyoudrawthisvectoroutonpaper,youcanmeasurethe
lengthofthevectoras3.6andtheanglebetweenthevectorandthexaxisas34degrees.
Ifyouthinkaboutthisfurther,youcanseethatyoudon'tevenneedthe(x,y)coordinatesofthevectorif
youalreadyknowitslengthandtheangleitmakeswiththexaxis.Itisperfectlypossibletodefineavector
fullyjustbyitslengthandangle.
Ifyouusethexandycoordinates,youareusingCartesiancoordinates.Ifyouusetheangleandlengthof
thevector,youareusingpolarcoordinates.
Let'shaveanexample.Supposeyouarewritingatopdownracinggame(somethinglikeMicroMachines).
Youwillneedawaytostorethevelocity(speedanddirection)ofaracingcar.Andhowdowedothat?
Withavector.Thisvelocityvectorisinfactthechangeintheracingcar'spositionfromoneframetothe
next(seefigurebelow).Thequestionis,shouldweuseCartesiancoordinatesorpolarcoordinatesforthis
vector?

Well,storingonlytheCartesiancoordinateshastheadvantagethatitisveryeasytocalculatethenew
positionoftheracingcarateachstep.Supposeyoustorethe(x,y)coordinatesofthevelocityvectorinthe
variablesvel_xandvel_y,andthepositionoftheracingcarinthevariablespos_xandpos_y.Allyouneed
todointhegameloopis:
pos_x+=vel_x;
pos_y+=vel_y;

Whatcouldbesimpler?
Ontheotherhand,storingthelengthandangleofthevelocityvectorhasitsadvantages,inthatitmakesit
easiertoimplementtheracingcarcontrols.ThinkaboutitiftheplayerpressesLEFT,youwanttheracing
cartoturnleft.Supposingyoustoretheangleintheintegercar_angle,youcouldusethefollowingcode:
if(key[KEY_LEFT])
{
car_angle=1;//turnonedegreetotheleft
}
if(key[KEY_RIGHT])
{
car_angle+=1;//turnonedegreetotheright
}

Andhowwouldyoudothatifyouonlystoredxandy?Youwouldneedtochangebothofthem,buthow?
Thatisalotmoredifficult!Furthermore,iftheplayerpressesUPyouwanttheracingcartogofaster.You
canachievethisbysimplyincreasingthelengthofthevector.Ifyoustorexandy,youhavetochangeboth
ofthemabit,whichisagainmorecomplicated.
Sin&cos
Okay,sonowweknowtherearetwowaystostoreavectorwithpolarcoordinatesandwithCartesian
coordinatesandthatinthiscasetheybothhavetheiradvantages.Sowhichonedoweactuallyuse?Well,
itwouldn'tbeabigproblemifweknewawaytocalculatetheangleandspeedfromthexandy
coordinates,andviceversa.
AndIwouldn'tbewritingthisarticleifthatwasn'tpossible!
FirstI'lltalkaboutconvertingfrompolartoCartesiancoordinates.Itis,ofcourse,alsopossibletoconvert
theotherway,butIwilltalkaboutthatlateron.
Therearetwofunctionsavailabletoustoaccomplishthis.Thesefunctionsaresineandcosine(sinand
cos).Whoa!Youdidn'tseethatcoming,didyou?
Thesinecanbeusedtocalculatetheycoordinateofavector,andthecosinecanbeusedtocalculatethex
coordinate.(Sometimesyouseethistheotherwayround,andoneorbothcoordinatevaluesmaybe
negated.Iencourageyoutothinkabouttheeffectthiswouldhavewhenyouhavelearnedmoreaboutthe
functions.)Thesin()andcos()functionstakeonlyoneparameter:theangle.Theyreturnanumberbetween
1and1.Ifyoumultiplythisnumberbythelengthofthevector,youwillgettheexactCartesian
coordinatesofthevector.Soyourcodewilllooklikethis:

speed_x=speed_length*cos(speed_angle);
speed_y=speed_length*sin(speed_angle);

Sothat'sit:foraracinggameyoujuststoretheangleandthelengthofthevelocityvector.Youadjustthese
accordingtotheplayer'sinput,andyoucalculatethexandycoordinateswhenyouarereadytoupdatethe
positionoftheracingcar.
Drawingacircle
Doyouwanttoseearealexample?Guessso.Butyou'llhavetowaitabit.FirstI'llgiveamoresimple
exampleofwhatsinandcosactuallydo.Infact,thisisprobablythesimplestprogramusingsinandcos
you'lleverseethatstilldoessomethingmoreorlessuseful(seecirc1.c).
voiddraw_circle()
{
intx,y;
intlength=50;
floatangle=0.0;
floatangle_stepsize=0.1;
//gothroughallanglesfrom0to2*PIradians
while(angle<2*PI)
{
//calculatex,yfromavectorwithknownlengthandangle
x=length*cos(angle);
y=length*sin(angle);
putpixel(screen,
x+SCREEN_W/2,y+SCREEN_H/2,
makecol(255,255,255));
angle+=angle_stepsize;
}
}

Output:

Solet'srunthisfunction.Whatdoesitdo?Well,itdrawssixtyoddpointsonthescreen,whichtogether
formaperfectcircle.Sohowdoesitwork?
Asyoucanseethereisavariablecalledlengthandavariablecalledangle.Thesetworepresentthelength
andtheangleofavector!Wefirstcalculatethexandycoordinatesfromthesevariables,inthesameway
asbefore,usingcosandsin.Afterthatweplotapixelatthecalculatedxandycoordinates.Finallywe
increasetheanglebyasmallincrement,butwedonotchangethelength.Weiterateseveraltimes,thus
goingthroughalotofdifferentangles.Andwhathappensifyoudrawpointsataconstantdistancefroma
fixedpointindifferentdirections?Yougetacircle!
Aboutradians
Butwhatisthat?Therearesomestrangethingsinthispieceofcode.Firstofall,whydoesitsay:while

(angle<2*PI)?Whatdoesthatmean?Andwhyistheangle_incrementsuchalowvalue?Youwould
thinkthatwithsuchalowvaluethepointsonthecirclewouldbeveryclosetoeachother,buttheyaren't.
Howcanweexplainthat?
Theansweristhatthesinandcosfunctionsdon'ttakeregulardegrees,whichyoumightbeusedto,asan
argument.Thereare360degreesinafullcirclethisnumberisthoughttohavecomefromanoldestimate
ofthenumberofdaysintheyear,ortohavebeenchosenbecauseithassomanyfactors.Butthesinand
cosfunctionswantradians,notdegrees.Thereare2*PIradiansinacircle,PIbeingamathematical
constantofroughly3.1415927.Sothereareroughly6.282radiansinacircle.Whydotheymakethingsso
difficult,youmayask?Well,thatisallfiguredoutbymathematicians,andmathematiciansarea
mysteriouskindofpeople.
Tomakeourliveseasier,wecancalculatethenumberofdegreesfromthenumberofradiansandvice
versa.Wedoitasfollows:
degrees=radians*180/PI;
radians=degrees*PI/180;

Let'sconsiderourangleincrementof0.1radians.0.1radians=0.1*180/3.142=5.7degrees.Ifyoutake
alookattheoutput,you'llnoticethatthisvaluelooksaboutrightforwhatwasdrawn.
Actuallythereasonforintroducingradiansisasfollows.Thelengthofthecircumferenceofaunitcircle(a
circleofradius1)isexactly2*PI.Thatmeansthatthelengthofthecircumferenceisexactlyequaltothe
numberofradiansinafullcircle.Dowegainanyadvantagebythisknowledge?No,butmathematicians
thinkitiscool.Programmers,ontheotherhand,don'tunlesstheyaredoingstuffwhichismuchmore
mathematicallyadvancedthanwhatwe'redoing.Lateronweshallseewhatgoodprogrammers(like
ShawnHargreaves)thinkisabetterwaytodefineanangleformostapplications.
Usefixed,notfloat
ButIalreadyhearsomepeoplesay:youareusingfloats!Floatsaresoslooooooooow!Whydon'tyouuse
fixedpointnumbers?Onnewercomputers,thereisnotmuchspeeddifference,butonoldercomputersthe
speedgainofusingfixednumbersissignificant.HereI'llpresentyouwithexactlythesamefunction,only
usingfixedpointarithmetic.First,however,I'llquicklygooverhowtomanipulatefixedpointnumbers
withAllegro.NotethatifyouuseC++youcanusethefixclass,whichisabiteasiertoworkwith,thoughI
shan'texplainithere.IfyouprograminC++andyouwanttousethefixclass,youwillhavetolookitupin
theAllegrodocs.
Rule#1:youcanconvertbetweenfloatsandfixeds,andbetweenintsandfixeds,withthefunctions
fixtoi,fixtof,itofixandftofix.
fixed_1=itofix(int_1);
int_1=fixtoi(fixed_1);
float_1=fixtof(fixed_1);

Rule#2:youcanaddandsubtracttwofixednumbers,butnotanintandafixed.Youwillneedto
converttypes.
fixed_3=fixed_1+fixed_2;
fixed_3=fixed_1fixed_2;
fixed_3=fixed_1+itofix(int_2);

Rule#3:youcandivideandmultiplybyanint,butnotbyanotherfixednumber.Youwillneedto
usethefunctionsfmul()andfdiv()(whichareinfactmacros,soyoudon'tneedtoworryabout
speed).
fixed_3=fixed_1*int_2;
fixed_3=fmul(fixed_1,fixed_2);
fixed_3=fdiv(fixed_1,fixed_2);

Nowbacktothedraw_circlefunction:Hereitisagainbutnowusingfixedpointarithmetic(circ2.c):
voiddraw_circle_fixed()
{
fixedx,y;
intlength=50;
fixedangle=0;
fixedangle_stepsize=itofix(5);
//gothroughallanglesfrom0to255
while(fixtoi(angle)<256)
{
//calculatex,yfromavectorwithknownlengthandangle
x=length*fcos(angle);
y=length*fsin(angle);
putpixel(screen,
fixtoi(x)+SCREEN_W/2,fixtoi(y)+SCREEN_H/2,
makecol(255,255,255));
angle+=angle_stepsize;
}
}

Notewehavetousefsin()andfcos()whenusingfixedpointmath.
Introducinganotherwayofrepresentingangles
Butwhatthe...?Nowitsays:while(fixtoi(angle)<256).Youjustwentthroughalengthyexplanationof
radians,andnowthis?
Wellhereyouseethewayprogrammerssometimesprefertohandleangles:theymakeuseofacirclethatis
dividedinto256parts,rangingfrom0to255.(Strictlyspeakingweincludethenumbersbetween255and
256,butnot256itself.)Let'scallthesepartsAllegrodegrees,forwantofabettername(thoughthissystem
isnotaninventionofAllegro,asthenamewouldsuggest).Why256andnot360?Wellhereisthething.
Whatwillhappenwhenyouhaveanangleof361regulardegrees?Becauseacircleisround(bydefinition),
361degreesrepresentsthesamepointas1degree.Inmuchthesameway,3*PIradiansisthesameas1*
PIradians,and257allegrodegreesisthesameas1allegrodegree.Tocheckforoutofboundangles
measuredindegreesandconvertthemtotheproperrange,youwillneedtodosomethinglikethis:
intangle_in_degrees;
while(angle_in_degrees>=360)angle_in_degrees=360;
while(angle_in_degrees<0)angle_in_degrees+=360;

ButbecauseAllegrodegreesrangefrom0to255,andthisrangecanbestoredinexactly8bits(inthecase
ofanint),weonlyneedresetalltheotherbitsandwecanbesurewehaveaneatanglerangingfrom0to
255.Wejusthavetomaskoutallbitsexceptthelower8bits.WecandothisusingthebitwiseAND
operator(&):
intallegro_degrees;
allegro_degrees&=0xFF;//keepthelowest8bits

Forthosepeoplewhodon'texactlyunderstandthebitwiseAND(&)operator:justtrustme,itisaveryeasy
waytomakesuretheangleiswithinrange.Justusethecodeaboveandyoucanbeabsolutelysurethe
angleisbetween0and255.
Ifweuseafixedpointnumbertorepresentdegreeswehavetodoitalittlebitdifferently,becausewealso
have16bitsrepresentingtheparttotherightofthepoint.Sowhatweneedtodoisexactlythesame,
exceptinsteadof8bitswekeep16+8=24bits.Hereiswhatwedo:
fixedallegro_degrees;
allegro_degrees&=0xFFFFFF;//keepthelowest24bits

Ifyouunderstandthisthenyouwillnowunderstandwhya256degreescaleisoftenbestforgame

programmers.Remember:normalpeopleuseregulardegrees,mathematiciansuseradiansandgame
programmersuseAllegrodegrees.Okay,maybethatisabitoversimplified.Ifyouusefloats,itisbetterto
useradians,becausethenormalfunctionssin()andcos()takevaluesinradians.Ifyouusefixed,asIdoin
mostexamples,itisbettertouseAllegrodegrees,becausethefunctionsfsin()andfcos()usethem,andwe
cankeepanangleinrangewithasimplebitwiseAND.
Justaswedidwithradiansanddegrees,wecancalculatethenumberofAllegrodegreesfromradiansand
regulardegrees.Withoutfurtherexplanation,hereishowtodoit:
allegro_degrees=regular_degrees*256/360;
allegro_degrees=radians*128/PI;

Togiveyouabetterideaofwhatsineandcosineactuallydo,Ihavewrittenthefollowingfunction
(circ3.c):
voiddraw_sine()
{
intlength=50;
fixedx,y;
fixedangle=0;
fixedangle_stepsize=itofix(5);
while(fixtoi(angle)<256)
{
//theangleisplottedalongthexaxis
x=angle;
//thesinefunctionisplottedalongtheyaxis
y=length*fsin(angle);
putpixel(screen,
fixtoi(x),fixtoi(y)+SCREEN_H/2,
makecol(255,255,255));
angle+=angle_stepsize;
}
}

Output:

Thefunctionlooksmoreorlessthesameasthedraw_circlefunction,butitdoessomethingdifferent.Itjust
plotsthesinefunctiononthescreen.Asyoucanseethesinefunctionlookslikeawavehencethename
'sinewave'.Youcanusethesinefunctioninyourgamesforallkindsofwavymovement,suchasaliens
movinginwaves.
Youcanchangethecodeabovetoplotthecosinefunctiontoo.Theimagebelowwascreatedwitha
modifiedversionofcirc3.c.Youcanseeasinewaveplottedinwhiteandacosinewaveplottedinred.The
functionsarecontinuousandrepeatingtheydon'tstopwhenyoureach256Allegrodegrees.Ifyoulook
closelyyoucanseethatthecosineandsinewaveshavethesameshape,theonlydifferencebeingthatthe
cosinefunctionisdisplacedalittlebit.Thedisplacementisexactly64Allegrodegreesor90normal

degrees.

Inthetablebelowaresomekeyvaluesfromthesineandcosinefunctions.Asyoucansee,bothfunctions
reachtheirmaximaandminimaatmultiplesof90regulardegrees.
normaldegrees
radians
allegrodegrees
sine
cosine
0
0
0
0
1
90
1/2pi
64
1
0
180
pi
128
0
1
270
3/2pi
192
1
0
360
2pi
256
0
1
Arealracingcar
Ok,asmallsummaryofwhatwehavelearnedsofar.Wenowknowtwodifferentwaystostorevectors,
namelypolarandCartesiancoordinates.WehavealsolearnedhowtocalculatetheCartesiancoordinatesof
avectorifweknowitspolarcoordinates.Finally,wehaveseenthreedifferentwaystostoreangles:
degrees,radiansandwhatwehavetermedAllegrodegrees.
Butnowwegobacktotheexamplewestartedwiththeracingcar.Actuallytheracingcarhereisreallya
circlewithalinerepresentingthedirectionthecarisfacingin,butwithalittleimaginationitcanbea
racingcar.Wellhereyougo(circ4.c):
voidracing_car()
{
//lengthandangleoftheracingcar'svelocityvector
fixedangle=itofix(0);
fixedlength=itofix(0);
//xandycoordinatesofthevelocityvector
fixedvel_x,vel_y;
//xandypositionoftheracingcar
fixedx=itofix(SCREEN_W/2);
fixedy=itofix(SCREEN_H/2);
while(!key[KEY_ESC])
{
//erasetheoldimage
circlefill(screen,fixtoi(x),fixtoi(y),10,makecol(0,0,0));
//checkthekeysandmovethecar
if(key[KEY_UP]&&length<itofix(2))
length+=ftofix(0.005);
if(key[KEY_DOWN]&&length>itofix(0))
length=ftofix(0.005);
if(key[KEY_LEFT])
angle=(angleitofix(1))&0xFFFFFF;
if(key[KEY_RIGHT])
angle=(angle+itofix(1))&0xFFFFFF;

//calculatethexandycoordinatesofthevelocityvector
vel_x=fmul(length,fcos(angle));
vel_y=fmul(length,fsin(angle));
//movethecar,andmakesureitstayswithinthescreen
x+=vel_x;
if(x>=itofix(SCREEN_W))x=itofix(SCREEN_W);
if(x<itofix(0))x+=itofix(SCREEN_W);
y+=vel_y;
if(y>=itofix(SCREEN_H))y=itofix(SCREEN_H);
if(y<itofix(0))y+=itofix(SCREEN_H);
//drawtheracingcar
circle(screen,fixtoi(x),fixtoi(y),10,makecol(0,0,255));
line(screen,fixtoi(x),fixtoi(y),
fixtoi(x+9*fcos(angle)),
fixtoi(y+9*fsin(angle)),
makecol(255,0,0));
//waitfor10milliseconds,orelsewe'dgotoofast
rest(10);
}
}

Everythinginthisprogramhasbeenexplainedalready.Thevelocityoftheracingcarisrepresentedbyan
angleandalength.IftheplayerpressesUP,thelengthofthevelocityvector(thespeed)isincreasedifhe
pressesDOWN,thelengthisdecreased.TheangleischangediftheplayerpressesLEFTorRIGHT.With
the24bitmask(0xFFFFFF),wemakesurethattheangleremainswithinthebasicrangeofAllegro
degrees.Afterthespeedanddirectionhavebeenadjusted,theCartesiancoordinatesvel_xandvel_yare
calculatedwithsin()andcos().Ineachiterationoftheloop,thesecoordinatesareaddedtothecoordinates
oftheracingcar.
Anotherusefulthingyoucandowithsinandcos
Ifyouunderstandallthis,youwillhavenoproblemswiththefollowingprogram.Itisanothersmall
exampleofwhatyoucandowiththisbasicknowledgeofsinandcos.Thistimewe'llusesinandcosto
animatetheorbitofaplanet.Theplanetwillberepresentedbyasmalldot,whichwillmovearoundina
circle.Hereisthecode(circ5.c):
voidorbit()
{
intx=0,y=0;
fixedangle=itofix(0);
fixedangle_stepsize=itofix(1);
//Thesedeterminetheradiusoftheorbit.
//Seewhathappensifyouchangelength_xto100:)
intlength_x=50;
intlength_y=50;
//repeatthisuntilakeyispressed
while(!keypressed())
{
//erasethepointfromtheoldposition
putpixel(screen,
fixtoi(x)+SCREEN_W/2,fixtoi(y)+SCREEN_H/2,
makecol(0,0,0));
//calculatethenewposition
x=length_x*fcos(angle);
y=length_y*fsin(angle);
//drawthepointinthenewposition
putpixel(screen,
fixtoi(x)+SCREEN_W/2,fixtoi(y)+SCREEN_H/2,

makecol(255,255,255));
//incrementtheanglesothatthepointmovesaroundincircles
angle+=angle_stepsize;
//makesureangleisinrange
angle&=0xFFFFFF;
//wait10milliseconds,orelseit'dgotoofast
rest(10);
}
}

Tryexperimentingwithdifferentvaluesforlength_xandlength_y.Ifthesetwoaredifferent,theresultwill
bethattheplanetdoesn'tmoveinacircleanymore,butinanellipse.(Notethatellipticalorbitsdonotwork
likethisinreallife.)
Ifyouwanttotrythisout,whynottrytomakeasimulationofthesolarsystem?Makingthemoonorbitthe
earthwhiletheearthisorbitingthesunisthetrickypart.
Drawingacircleinyetanotherway
InthefirstchaptersIexplainedtwodifferentwaystodrawacircle,oneusingfloatsandoneusingfixeds.
Butifyoutakealookatgfx.cintheallegro/srcdir,youwillseethatthesourceofAllegro'scircle()
functionisnothinglikethedraw_circlefunctionI'veputhere.Infact,youwon'tfindasinglesinorcosin
theentirefunction!Sohowisthatpossible?
Thecodemakesuseofoneusefulpropertyofcircles:allpointsinacircleareatthesamedistancefromthe
center.Sayyoustartatthetopofthecircle.Thecoordinatesofthetopareeasilycalculated:thex
coordinateis0andtheycoordinateisequaltotheradiusofthecircle(butnegativewithscreen
coordinates).Sowedrawapixelatthesecoordinates.Forthenextpixelwecaneithergoonepixeltothe
right,oronepixeldownandthenonepixeltotheright.Sowhichdowechoose?
ThesolutionistocalculateforeachofthetwopossibilitiesthedistancetothecenterwithPythagoras's
Theorem.Wedrawthepixelwhosedistancefromthecenterislessdifferentfromtheradiusofthecircle.

Weonlyhavetodothisforoneeighthofthecircle.Therestofthecirclecanbedrawnbymakinguseof
thehorizontal,verticalanddiagonalaxesofsymmetryofthecircle,asyoucanseeinthefigurebelow.
Notethatyoucandrawallredandyellowsectionsforthepriceofone,simplybymirroringitalongthe
greenlines.

Hereistheactualcodefromcirc6.c:
voidmy_draw_circle(BITMAP*bmp,intcenter_x,intcenter_y,intr,intcolor)
{
//xandyarethecurrentpositioninthecircle.
intx=0,y=r;
while(x<=y)
{
//Wemakeuseof8axesofsymmetryinacircle.
//Thiswaywehavefewerpointstocalculateonitscircumference.
putpixel(bmp,center_x+x,center_y+y,color);
putpixel(bmp,center_xx,center_y+y,color);
putpixel(bmp,center_x+x,center_yy,color);
putpixel(bmp,center_xx,center_yy,color);
putpixel(bmp,center_x+y,center_y+x,color);
putpixel(bmp,center_xy,center_y+x,color);
putpixel(bmp,center_x+y,center_yx,color);
putpixel(bmp,center_xy,center_yx,color);
//Thisisthemostimportantpartofthefunction.
//Wegototherightinallcases(x++).
//Weneedtodecidewhethertogodown(y).
//Thisdependsonwhichpointis
//closesttothepathofthecircle.
//GoodoldPythagoraswilltelluswhattodo.
x++;
if(abs(x*x+y*yr*r)>
abs(x*x+(y1)*(y1)r*r))
y;
}
}

Thiscodestilldoesn'tlookmuchlikethecodeinAllegro'sgfx.cfile,butthatismainlybecausethe
statement:
if(abs(x*x+y*yr*r)>
abs(x*x+(y1)*(y1)r*r))

canstillbeoptimizedalot.Infact,ifyoudoallpossibleoptimizations,youarriveatAllegro'sactual
circle()function.Ishan'tdiscusstheseoptimizationsherethough.Ithasallbeenfiguredoutbyasmartguy
workingatIBMnamedBresenham.Healsofiguredoutasmartwaytodrawlinesquickly,soyoumight
comeacrosshisnamesometimesinFAQsandtutorials.
LinkforBresenhamLine&CircleAlgorithms:http://www.gamedev.net/reference/articles/article767.asp
Vectorstheotherwayround
WehaveseenhowtogofrompolartoCartesiancoordinateswithsinandcoslikethis:

x=length*cos(angle)
y=length*sin(angle)

NowIwillexplainhowtogotheotherway.Calculatingthelengthistheeasypart,foryouonlyneedto
knowPythagoras'sTheorem:a^2+b^2=c^2.Ormorepractically:
length=sqrt(x*x+y*y)

Calculatingtheangleisabitharder.Thereisamathematicalfunctioncalledthetangent,implementedinC
inthefunctiontan(),whichcanbeusedtocalculatetheratioofytoxasfollows:
tan(angle)=y/x

Thiscaninfactbewrittenas:
tan(angle)=sin(angle)/cos(angle)

Thismeansthetanfunctionisacombinationofthesinandcosfunctions.
Theinversefunctionofthetangentiscalledthearctangent.TheCfunctionforthisisatan().Thisfunction
canbeusedtocalculateanangleifyouknowtheratioofytox,likethis:
angle=atan(y/x)

Butthereisaminorproblem:thiswillsometimesgiveyouanincorrectresult.Inthefigurebelow,yousee
twovectors,aredoneandayellowone.Theybothhavethesameytoxratio.Thatmeans,ifyoucalculate
thearctangentsofthem,youwillgetthesameresult,whichis45degrees.Thisiscorrectonlyforthe
yellowvector.Inaddition,youhavetocheckforcasesinwhichxis0,toavertdivisionbyzero.

Apartialworkaroundispossiblelikethis(partialbecausewedon'tcheckforthecasewherexis0):
if(x>0)
angle=atan(y/x);
else
angle=PI+atan(y/x);

Buttosimplifythingsthefunctionatan2()isavailableforprogrammers.Hereisanexample:

angle=atan2(y,x)

Thisfunctionwillalwaysproducethecorrectangleforanyx,ypair.Ofcourse,forfixedpointmathematics
Allegroprovidesthecounterpartfatan2().
Usingatan2():homingmissiles
Supposeyouarewritingagameinwhichtheplayercanfirehomingmissiles.Youdecidetodoitas
follows.Firstyoucalculatethedirectionofthetargetasseenfromthemissile.Thenyoucomparethisangle
withthecurrentangleofthemissile.Ifthetargetangleisgreaterthanthecurrentangle,theangleshouldbe
increased,andviceversa.
Thisisaprettygoodidea,buthowdoyoucalculatethedirectionofthetargetasseenfromthemissile?
Youcanvisualizethisasavectorfromthemissiletothetarget.Thexandycoordinatesofthisvectorcan
becalculatedveryeasilyjustsubtractthecoordinatesofthemissilefromthecoordinatesofthetarget.
Giventhexandycoordinatesofthevector,youcancalculateitsangleandlength,usingtheatan2()
functionasdescribedabove.Thelengthisnotsoimportant(itisameasureofthedistancetothetarget)but
theangleisjustwhatweneed.

Inthecodebelow,thepositionofthemissileisrepresentedbythevariablesxandy.Thevelocityofthe
missileisrepresentedbylengthandangle.First,theprogramdetermineswhetherthereisalreadyatarget
set,andifthereisn'tonetheprogramchoosesoneatrandom.Afterthatthemissileismovedanddrawnto
thescreen.Finallytheprogramdetermineswhichwaytheangleofthemissileshouldchange.Theangleto
thetargetiscalculatedinthisline:
target_angle=fatan2(target_yy,target_xx);

Theprogramusesthiscalculatedangletodeterminewhetherthemissile'sdirectionangleshouldincreaseor
decrease.Itcalculatesthedifferencebetweenthetargetangleandthecurrentangle(angletarget_angle).
Afterthatitmakessurethisdifferenceisintheproperrange:(angletarget_angle)&0xFFFFFF.Ifthis
angleislessthan128Allegrodegrees(180normaldegrees),thedirectionangleisdecreased.Otherwiseit
isincreased.
if(((angletarget_angle)&0xFFFFFF)<itofix(128))
angle=(angleangle_stepsize)&0xFFFFFF;
else
angle=(angle+angle_stepsize)&0xFFFFFF;

Hereisthewholepieceofcode(circ7.c):
voidhome_in()
{
//thex,ypositionofthehomingmissile
fixedx=itofix(SCREEN_W/2);
fixedy=itofix(SCREEN_H/2);
//theangleandlengthofthemissile'svelocityvector

fixedangle=0;
intlength=1;
fixedangle_stepsize=itofix(3);
//determineswhetherthemissilehasreached
//thetargetandanewoneshouldbechosen
intnew_target=TRUE;
//angletothetarget
fixedtarget_angle;
//positionofthetarget
fixedtarget_x,target_y;
while(!keypressed())
{
clear(screen);
//choosenewtargetrandomlywhenneeded
if(new_target)
{
target_x=itofix((SCREEN_W+rand()%(2*SCREEN_W))/4);
target_y=itofix((SCREEN_H+rand()%(2*SCREEN_H))/4);
new_target=FALSE;
}
//movethemissile
x+=length*fcos(angle);
y+=length*fsin(angle);
//ifweareveryclosetothetarget,setanewtarget
if(abs(xtarget_x)+abs(ytarget_y)<itofix(10))
new_target=TRUE;
//drawapixelwherethetargetis
putpixel(screen,fixtoi(target_x),fixtoi(target_y),
makecol(255,255,255));
//drawthemissile
//(actuallyacirclewithalinerepresentingtheangle)
circle(screen,fixtoi(x),fixtoi(y),10,makecol(0,0,255));
line(screen,fixtoi(x),fixtoi(y),
fixtoi(x)+fixtoi(9*fcos(angle)),
fixtoi(y)+fixtoi(9*fsin(angle)),
makecol(255,0,0));
//calculatetheanglefromthemissiletothetarget
target_angle=fatan2(target_yy,target_xx);
//Determinewhetherweshouldturnleftorright.
//Notethatitofix(128)representshalfacircle.
//Weuse&0xFFFFFFasatricktogetanangle
//between0and256.
if(((angletarget_angle)&0xFFFFFF)<itofix(128))
angle=(angleangle_stepsize)&0xFFFFFF;
else
angle=(angle+angle_stepsize)&0xFFFFFF;
rest(10);
}
}

Hereisascreenshot.Asyoucansee,themissileisrepresentedbyabluecirclewitharedlineinit.The
currenttargetisrepresentedbyawhitedot.

Usingthedotproduct
Thesolutionabovetacklestheproblemverywellbutthatdoesn'tmeantherearen'tanyotherpossible
solutions.Inmathematicstextbooksyoucanfindthefollowingformulatocalculatetheanglebetweentwo
vectorsaandb:
cos(angle)=(xa*xb+ya*yb)/(length(a)*length(b))

Thesubexpression(xa*xb+ya*yb)iscalledthedotproduct,andisequaltotheproductofthelengths
multipliedbythecosineoftheanglebetweenthevectors.Wewantourhomingmissiletogoonewayifthe
anglebetweenitscurrentdirectionandthetargetisbetween0and180degrees,andtheotherwayifthe
anglebetweenthecurrentdirectionandthetargetisbetween180and360degrees.
Becauseofitsnature,arccosinecannotbeusedtodeterminethedifferencebetweentherangebelow180
degreesandtherangeabove180degrees.Youcandeterminethedifferencebetweentherangebelow90
andabove270,andtherangebetween90and270,becausethecosineispositiveinthefirstcaseand
negativeinthesecond.Ifyoucan'tseethis,takealookatthepictureofthecosinewaveagain.
Ifwerotateonevectorby90degrees,wecansimplycheckiftheresultofthedotproductisbeloworabove
0,toseewhetherweshouldturnleftorright.Todothis,wemakeuseofasimpletrick:weswapthe
coordinatesandchangethesignofoneofthem.Inotherwords,wereplacexabyyaandyabyxa:
cos(angle)=(ya*xbxa*yb)/(length(a)*length(b))

Becauseweneedtoknowiftheresultispositiveornegative,andwedon'tneedtoknowtheactualvalueof
theresult,wecanleaveoutthecalculationofthelengthsofthevectors:
result=ya*xbxa*yb

Iftheresultispositive,weturnonewayiftheresultisnegative,weturntheotherway.Theresultisthe
followingpieceofcode:
if(fmul(dy,(target_xx))+fmul(dx,(target_yy))>0)
angle=(angleangle_stepsize)&0xFFFFFF;
else
angle=(angle+angle_stepsize)&0xFFFFFF;

Inthiscode,dxanddyrepresentthemissilevelocityvectorandtarget_xxandtarget_yyrepresentthe
vectortothetarget.Hereistheentireexample(circ8.c):
voiddot_product_home_in()
{
//thepositionofthehomingmissile
fixedx=itofix(SCREEN_W/2);
fixedy=itofix(SCREEN_H/2);
//theangleandlengthofthemissile'svelocityvector
fixedangle=0;
intlength=1;
fixedangle_stepsize=itofix(3);

//determineswhetherthemissilehasreached
//thetargetandanewoneshouldbechosen
intnew_target=TRUE;
//positionofthetarget
fixedtarget_x,target_y;
//vectorofmissilemovement
fixeddx,dy;
while(!keypressed())
{
clear(screen);
//choosenewtargetrandomlywhenneeded
if(new_target)
{
target_x=itofix((SCREEN_W+rand()%(2*SCREEN_W))/4);
target_y=itofix((SCREEN_H+rand()%(2*SCREEN_H))/4);
new_target=FALSE;
}
//Movethemissile.
//Westoredxanddyinvariablessothat
//wecanusethemlateroninthedotproduct.
dx=length*fcos(angle);
dy=length*fsin(angle);
x+=dx;
y+=dy;
//ifweareveryclosetothetarget,setanewtarget
if(abs(xtarget_x)+abs(ytarget_y)<itofix(10))
new_target=TRUE;
//drawapixelwherethetargetis
putpixel(screen,fixtoi(target_x),fixtoi(target_y),
makecol(255,255,255));
//drawthemissile
//(actuallyacirclewithalinerepresentingtheangle)
circle(screen,fixtoi(x),fixtoi(y),10,makecol(0,0,255));
line(screen,fixtoi(x),fixtoi(y),
fixtoi(x)+fixtoi(9*fcos(angle)),
fixtoi(y)+fixtoi(9*fsin(angle)),
makecol(255,0,0));
//Determinewhetherweshouldturnleftorright
//usingthedotproduct.
//Weuse&0xFFFFFFasatricktogetanangle
//between0and256.
if(fmul(dy,(target_xx))+fmul(dx,(target_yy))>0)
angle=(angleangle_stepsize)&0xFFFFFF;
else
angle=(angle+angle_stepsize)&0xFFFFFF;
rest(10);
}
}

(ThankstoBen"entheh"DavisforpointingthisouttomeinEFNet#allegro.)
ForsomereasonIcan'tquiteputmyfingeron,someexperiencedgameprogrammersarereluctanttouse
atan2()andpreferthedotproduct.Maybeitisbecauseatan2()canintroduceroundingerrors?Idon'tknow
forsure.Inthecaseofhomingmissiles,bothmethodsworkequallywell,anditisamatterofpersonal
preferencewhichoneyoushoulduse.
Proofreader'snote:SinceIamtheaforementioned"entheh",Icouldn'tresistthetemptationtoexplainwhy
I,andsomeotherprogrammers,preferthedotproduct.Thedotproductprovidesamethodinvolving
nothingmorecomplexthanmultiplication.Mostimplementationsofatan2()implicateacoupleof

comparisons(forthespecialcases),adivisionandthetrigonometricfunctionatan(),allofwhichare
slowerthanmultiplication.Itiscleartoseewhichmethodisbothfasterandmoreelegant.Besides,we
proggersliketoshowoffwithobscuremethods:)
Sin,cosandbitmaps:rotate_sprite
Ifyouhavereadthroughthisarticle,youshouldbynowbeamasterofsinandcos.Butthisdoesn'tmean
you'refamiliarwiththeirwidespreadapplications.Infact,thereisnoendtothepossibilitiesofferedby
thesetwofunctions.I'llgiveyouyetanotherexample:rotatingsprites.
Don'tthink,'ThisisprobablycomplicatedandtheAllegrolibraryprovidesmewithaspriterotation
functionalreadysoIdon'tneedthis.'Instead,thinkofalltheusefulmodificationsyoucanmakeifyou
knowhowthespriterotationfunctionworksrotatingtilemaps,forexample.Ormaybeyouneedthe
masked_rotate_flip_mirror_alpha_blitfunction.TheAllegrolibrarydoesn'tprovideit,soyouwouldhave
towriteyourown.
Sohowdoesitworkthen?Therearetwowaystoapproachtheproblem.Oneway,themostobvious,isto
loopthroughallpixelsofthespriteyouwanttorotate,calculateforeachpixelwhereonthescreenit
shouldgo,andthencopythepixel.Thisiscertainlypossible,buttherewillnotbeaonetoone
correspondencetothepixelsonthescreen.Thefinalpictureoftherotatedspritewillcontaingaps,while
somepixelswillbedrawntwicetothescreen.Soweshouldlookforanotherway.
Hereisthealternative.Weloopthroughallpixelsonthetargetbitmap(oftenthescreen)andcalculate
whichpixelfromthespriteshouldgothere.Thiswaywearesurethateverypixelonthescreenisfilled,
andnopixeliscopiedtwicetothescreen.
Let'sstartatscreenposition(0,0).Whichpixelfromthebitmapshouldgothere?Tomakethingseasierwe
putspriteposition(0,0)there.Wehavetostartsomewhere,don'twe?Thenwemoveonetotherightonthe
screen,toposition(1,0).Whichpixelfromthespriteshouldgothere?Thatdependsontheanglewewant
torotate.Ifwerotate0degrees,weshouldputspritepixel(1,0)there.Ifwerotate270degrees,weshould
putpixel(0,1)there.Hereishowtocalculatethatforanyangle:
sprite_x=cos(angle);
sprite_y=sin(angle);

Thenwegoonemoretotheright.Thepositioninthespritewehavetousenowis:
sprite_x=2*cos(angle);
sprite_y=2*sin(angle);

Andsoon.Sinceweareproceedinginalinearfashion,wecansimplycalculatethesinandcosonce,and
addthemtothepositioninthespriteeachtimewegoonepixeltotherightonthedestination.Takealook
atthesourcebelow(circ9.c):
voidmy_rotate_sprite(BITMAP*dest_bmp,BITMAP*src_bmp,
fixedangle,fixedscale)
{
//currentpositioninthesourcebitmap
fixedsrc_x,src_y;
//currentpositioninthedestinationbitmap
intdest_x,dest_y;
//src_xandsrc_ywillchangeeachtimebydxanddy
fixeddx,dy;
//src_xandsrc_ywillbeinitializedtostart_xandstart_y
//atthebeginningofeachnewline
fixedstart_x=0,start_y=0;
//Wecreateabitmasktomakesurexandyareinbounds.
//Unexpectedthingswillhappen

//ifthewidthorheightarenotpowersof2.
intx_mask=src_bmp>w1;
inty_mask=src_bmp>h1;
//calculateincrementsforthecoordinatesinthesourcebitmap
//forwhenwemoverightonepixelonthedestinationbitmap
dx=fmul(fcos(angle),scale);
dy=fmul(fsin(angle),scale);
for(dest_y=0;dest_y<dest_bmp>h;dest_y++)
{
//setthepositioninthesourcebitmaptothe
//beginningofthisline
src_x=start_x;
src_y=start_y;
for(dest_x=0;dest_x<dest_bmp>w;dest_x++)
{
//Copyapixel.
//Thiscanbeoptimizedalotbyusing
//directbitmapaccess.
putpixel(dest_bmp,dest_x,dest_y,
getpixel(src_bmp,
fixtoi(src_x)&x_mask,
fixtoi(src_y)&y_mask));
//advancethepositioninthesourcebitmap
src_x+=dx;
src_y+=dy;
}
//forthenextlinewehaveadifferentstartingposition
start_x=dy;
start_y+=dx;
}
}

Screenshot:

Ifyoutakealookattheselines:
dx=fmul(fcos(angle),scale);
dy=fmul(fsin(angle),scale);

Herewecalculatethesinandcosoftheangle.The'd'in'dx'and'dy'standsfor'delta'.Theserepresentthe
changeinpositioninthespriteaswegotothenextpixelonthescreen.Asyoucansee,ascalefactoris
introduced,sowecanzoominandout.
Inthefollowinglines:
putpixel(dest_bmp,dest_x,dest_y,
getpixel(src_bmp,

fixtoi(src_x)&x_mask,
fixtoi(src_y)&y_mask));

...thepixeliscopiedfromthesourcebitmap(thesprite)tothetargetbitmap(oftenthescreen).Ofcourse,
'dest'standsfor'destination'and'src'standsfor'source'.Amaskisusedtomakesurethepositioninthe
sourcebitmapisvalid,sowewon'tgetapixelthatisoutsidethesourcebitmap.Thismethodonlyworksif
thedimensionsofthesourcebitmaparepowersof2.Forexample,bitmapsof32x32or64x256would
work,butabitmapof100x100wouldn't,because100isnotapowerof2.
Withthefollowinglines,wemovetothenextpositiononthescreen.dest_xisincrementedintheforloop,
andsrc_xandsrc_yareincreasedbythepreviouslycalculateddxanddy:
src_x+=dx;
src_y+=dy;

Afterthewholelineiscompleted,thepositiononthesourcebitmapisresettothestartpositionstoredin
start_xandstart_y.Ofcourse,start_xandstart_yhavetobechangedinordertogoonelinedown.Because
apositiondownone'pixel'isperpendiculartoapositiononetotheright,weusethesametrickweused
furtherupwiththedotproductmethod:wereplacedxwithdyanddywithdx.Sohereishowthestart
positionischanged:
start_x=dy;
start_y+=dx;

Rotation
Supposeinacertaingameyouwanttorotateapointaroundanotherpoint.Forexampletheplayercan
jumpontoaropeandswingfromoneplatformtoanother.Youcanimplementtheswingingmotionasa
rotationoftheplayeraroundthepointwheretheropeisattached.Inordertodothis,youneedtocalculate
thevectorgoingfromtheplayertothecenterofrotation(wheretheropeisattached),taketheangleofthis
vector,increaseitabit,andrecalculatetheplayer'sposition.
Thisisnotsopracticalinthiscasethough,becauseyoumostlikelystoretheplayer'spositioninCartesian
coordinates.Youhavetocalculatetheangleofthevectorfromtheplayertothecenterofrotationwith
atan2().Afteryouhaveincreasedtheangle,youcancalculatethenewxandycoordinateswithsinand
cos.Hereisanexample:
angle=atan2(y,x);
length=sqrt(x*x+y*y);
angle+=1;
new_x=length*cos(angle);
new_y=length*sin(angle);

BecauseyouconvertfromCartesiancoordinatestopolarcoordinatesandthenbackagain,youcanlose
precision.Thereisabetterway:youcanmakeuseofarotationmatrix.Rotationmatrices('matrices'being
thepluralof'matrix')arewidelyusedinthe3Dgraphicsworld,buttheycanbeusedin2Djustaswell.In
short,theyprovideawaytorotateavectorwithoutconvertingtopolarcoordinates.HereIpresentthe
equations:
new_x=x*cos(angle)y*sin(angle)
new_y=x*sin(angle)+y*cos(angle)

Inthiscase,'angle'istheanglebywhichyouwanttorotatethevector.'x'and'y'aretheoldCartesian
coordinatesofthevector,and'new_x'and'new_y'arethenewCartesiancoordinatesofthisvector.AsI
saidbefore,somepeoplefindtheatan2functionawkwardandliketouseitaslittleaspossible.Withthis
method,youcanperformrotationswithoutusingatan2.Itissensibletoprecalculatecos(angle)andsin
(angle),sinceyouneedeachonetwice.
Hereisacompleteexampleusingthismethod(circ10.c).Allitdoesisrotatefourpointsaroundthecenter
ofthescreen.

voidprojection_test()
{
//initializethecoordinatesoffourdots
fixeddot_x[4]={itofix(50),itofix(50),itofix(50),itofix(50)};
fixeddot_y[4]={itofix(50),itofix(50),itofix(50),itofix(50)};
fixedangle=0;
fixedangle_stepsize=itofix(1);
//proj_xandproj_ywillcontaintheprojectionofthedots
fixedproj_x[4];
fixedproj_y[4];
inti;
//repeatthisloopuntilEscispressed
while(!key[KEY_ESC])
{
//projectallthedotstotheirnewpositionsafterrotation
for(i=0;i<4;i++)
{
proj_x[i]=fmul(dot_x[i],fcos(angle))
fmul(dot_y[i],fsin(angle));
proj_y[i]=fmul(dot_x[i],fsin(angle))+
fmul(dot_y[i],fcos(angle));
}
//drawthefourdots
for(i=0;i<4;i++)
{
putpixel(screen,
fixtoi(proj_x[i])+SCREEN_W/2,
fixtoi(proj_y[i])+SCREEN_H/2,
makecol(255,255,255));
}
rest(10);
clear(screen);
angle+=angle_stepsize;
}
}

Forinformationonrotationmatriceslookatthislink:http://www.student.hk
r.se/~pt93mm/thesis/techniques/3d_tutorial/3d.html.Thisalsocontainsinformationon3Dprojection,
whichisusedinthenextsection.
Thereisaspecialcaseofthisrotationmatrix:arotationby90degrees.Hereishowtodothat:supposeyou
haveapointwiththecoordinates(4,8)andyouwanttorotateitby90degreesaroundtheorigin.Youuse
theformulae:
new_x=x*cos(90)y*sin(90)
new_y=x*sin(90)+y*cos(90)

cos(90)is0andsin(90)is1,sowecansimplifythisequationtothefollowing:
new_x=y
new_y=x

Sothenewcoordinatesare8,4.Wehavealreadyusedthistricktwicenowyouknowwhyitworks!
Ifyouwanttorotatethepoint(4,8)by180degreesaroundtheorigin,yougetthefollowing:
new_x=x*cos(180)y*sin(180)
new_y=x*sin(180)+y*cos(180)

Or:
new_x=x
new_y=y

Sothenewcoordinatesare(4,8).
Inthetablebelowyoucanlookuphowtorotateby90,180and270degreesinthisway.

90degrees
180degrees
270degrees
360degrees
newxvalue
y
x
y
x
newyvalue
x
y
x
y
SuperNintendoMode7
Somegamesmakeuseofacooltricktodrawabitmapin3D.Thistrickcanbeusedtodrawtexturedfloors
orceilings,oranyplaneaslongasitishorizontalorvertical.ItisusedbygameslikeJazzJackrabitand
WackyRacers.TheSuperNintendoevenhasaspecialgraphicsmodetodrawgraphicsthiswayandthatis
whytherearelotsofSuperNintendogamesthatusethistrick.AmongstthemareMarioKart,FZeroand
eventheSquaresoftclassicSecretofMana.Nintendocallsthistrick"Mode7"andthatiswhatIshallbe
callingittoo.TheonlyAllegrogamethatIknowofthatmakesuseofMode7isSheep,writtenbyBen
Davis.
FZeroontheSuperNintendo:

Howdoesitwork?Itisalmostthesameasdrawingarotatedsprite,exceptthatwehavetoscaleeach
horizontallinetomakelinesthatarefartherawaysmaller.Theonlydifficultthingisthatwehavetoknow
howmuchwewanttoscaleeachline.Forthiswehavetounderstand3Dprojectionabit.
3Dprojectionisonlyasdifficultasyouwantittobe.Whatitallcomesdowntoisthatyoustartwith
coordinatesin3Dspace(space_x,space_yandspace_z)andyouwanttoturnthemintocoordinatesonthe
screen(screen_xandscreen_y).Thatmeansyouhavetomakeonenumberdisappear,namelyspace_z.
Howcanyoudothat?Bydividingeverythingbyspace_z!
space_x/space_z=screen_x
space_y/spaze_z=screen_y
space_z/space_z=1

Itseemsstrangebutitworks!space_zisthedistancetothepointinspace,andinthecaseofMode7,
space_yistheheightofthecameraabovetheplane.
InMode7youdraweachhorizontallinescaleddownaccordingtodistance.Soyouneedtoknowwhich
space_zgoeswithacertainscreen_y.Thismeanswehavetoturntheequationaround:
space_z=space_y/screen_y

Soweknowthedistanceofacertainlinewewanttodraw.Howdoweknowhowmuchtoscalethisline

then?Youhavetocalculatethedistanceinspacebetweentwopointsonthisline.Thismeansyouwantto
calculatewhatthedifferenceisinspace_xifyouchangescreen_xby1.Hereistheequation:
space_x=screen_x*space_z=1*space_z=1*space_y/screen_y

Thereisanothersmallproblem:whatistherelationshipbetweenaspacecoordinateandascreen
coordinate?Inotherwords:ifspace_xis12,howmanypixelsisthat?Tosolvethisproblemwehaveto
introduceanextrapairofvariables:scale_xandscale_y.Thesewillscalethescreen_xandscreen_ythe
rightway.Ihavefoundthatascale_xandascale_yof200.0arerightformypurposes.
LinkforMode7:http://members.madasafish.com/~kefka/mode7.htm
Hereisthecode(circ11.c).Totestyourintelligence,Iusenotspace_ybutspace_ztorepresenttheupward
direction.
/*MODE_7_PARAMSisastructcontainingallthedifferentparameters
thatarerelevantforMode7,soyoucanpassthemtothefunctions
asasingleunit*/
typedefstructMODE_7_PARAMS
{
fixedspace_z;//thisistheheightofthecameraabovetheplane
inthorizon;//thisisthenumberofpixelsline0isbelowthehorizon
fixedscale_x,scale_y;//thisdeterminesthescaleofspacecoordinates
//toscreencoordinates
}MODE_7_PARAMS;
voidmode_7(BITMAP*bmp,BITMAP*tile,fixedangle,fixedcx,fixedcy,MODE_7_PARAMSparams)
{
//currentscreenposition
intscreen_x,screen_y;
//thedistanceandhorizontalscaleofthelinewearedrawing
fixeddistance,horizontal_scale;
//maskstomakesurewedon'treadpixelsoutsidethetile
intmask_x=(tile>w1);
intmask_y=(tile>h1);
//stepforpointsinspacebetweentwopixelsonahorizontalline
fixedline_dx,line_dy;
//currentspaceposition
fixedspace_x,space_y;
for(screen_y=0;screen_y<bmp>h;screen_y++)
{
//firstcalculatethedistanceofthelinewearedrawing
distance=fmul(params.space_z,params.scale_y)/
(screen_y+params.horizon);
//thencalculatethehorizontalscale,orthedistancebetween
//spacepointsonthishorizontalline
horizontal_scale=fdiv(distance,params.scale_x);
//calculatethedxanddyofpointsinspacewhenwestep
//throughallpointsonthisline
line_dx=fmul(fsin(angle),horizontal_scale);
line_dy=fmul(fcos(angle),horizontal_scale);
//calculatethestartingposition
space_x=cx+fmul(distance,fcos(angle))bmp>w/2*line_dx;
space_y=cy+fmul(distance,fsin(angle))bmp>w/2*line_dy;
//gothroughallpointsinthisscreenline
for(screen_x=0;screen_x<bmp>w;screen_x++)
{
//getapixelfromthetileandputitonthescreen

putpixel(bmp,screen_x,screen_y,
getpixel(tile,
fixtoi(space_x)&mask_x,
fixtoi(space_y)&mask_y));
//advancetothenextpositioninspace
space_x+=line_dx;
space_y+=line_dy;
}
}
}

Screenshot:

Inthisexample,firstastructisdefinedthatholdsallparametersthatareneededtodrawaMode7plane.
ThisstructiscalledMODE_7_PARAMS.Thisstructholdsthescaleoftheview,theheightofthecamera
abovetheplane,andtheheightofthehorizononthescreen.
Asintherotate_spriteexample,weusetwomaskvariablestomakesurethatweremaininsidethetexture
bitmapanddonotattempttocopyapixelfromoutside.Thisofcourseonlyworksifthetexturebitmaphas
dimensionsthatarepowersof2,so32x16and8x8arefine,but100x50isoutofthequestion.
Incontrasttotherotate_spritefunction,wecan'tcalculatethedxanddyonlyonce.Wehavetodothatfor
eachline,becauseeachlinehasadifferentscalefactor.Thescalefactordependsonthedistanceoftheline
fromthecamera,sowehavetocalculatethatfirst:
distance=fmul(params.space_z,params.scale_y)/
(screen_y+params.horizon);
horizontal_scale=fdiv(distance,params.scale_x);
line_dx=fmul(fsin(angle),horizontal_scale);
line_dy=fmul(fcos(angle),horizontal_scale);

Nowline_dxandline_dycontainthedxanddyforthisline,thatis,thedifferenceinpositiononthesprite
ifwegofromonescreenpositiontothenext.
Afterthatwehavetocalculatethestartingpositiononthebitmap,whichisstoredinspace_xandspace_y.
Wethencopyapointfromthesourcebitmaptothedestinationbitmap:
putpixel(bmp,screen_x,screen_y,
getpixel(tile,
fixtoi(space_x)&mask_x,
fixtoi(space_y)&mask_y));

...andeachtimeround,weincrementspace_xandspace_ybythecalculatedline_dxandline_dy.Youcan
seeclearlythatthisMode7effecthasalotincommonwiththespriterotationeffect.
Mode7withobjects
Wealsowanttodrawobjects,orsprites,inourprojection.Forexample,inMarioKart,thespritesarethe

'karts'.Theseobjectsbecomesmallerwhentheyarefartheraway.Alsothecoordinatesoftheobjectonthe
bitmaphavetobetransformedintoscreencoordinateswithrespecttothecamera.Youcanaccomplishthis
witharotationmatrixasdescribedearlier.
WeneedtointroduceanextrapairofvariablestoMODE_7_PARAMSinordertobeabletoscalethe
sprites.Thesevariablesarenamedobj_scale_xandobj_scale_y.Ifyouruntheprogramcirc12.c,youcan
seeabluespheresittinginapositiononthebitmap.
Hereisthecode(circ12.c):
/*MODE_7_PARAMSisastructcontainingallthedifferentparameters
thatarerelevantforMode7,soyoucanpassthemtothefunctions
asasingleunit*/
typedefstructMODE_7_PARAMS
{
fixedspace_z;//thisistheheightofthecameraabovetheplane
inthorizon;//thisisthenumberofpixelsline0isbelowthehorizon
fixedscale_x,scale_y;//thisdeterminesthescaleofspacecoordinates
//toscreencoordinates
fixedobj_scale_x,obj_scale_y;//thisdeterminestherelativesizeof
//theobjects
}MODE_7_PARAMS;
/*draw_objectjustdrawsasingleobjectatafixedposition,although
thiscaneasilybemodifiedtoallowformoreobjects.
bmp=bitmaptodrawto.obj=spritefortheobject.
angle,cx,cydefinethecameraposition.
*/
voiddraw_object(BITMAP*bmp,BITMAP*obj,fixedangle,fixedcx,fixedcy,MODE_7_PARAMSparams)
{
intwidth,height;
intscreen_y,screen_x;
//Theobjectinthiscaseisatafixedpositionof(160,100).
//Calculatethepositionrelativetothecamera.
fixedobj_x=itofix(160)cx;
fixedobj_y=itofix(100)cy;
//usearotationtransformationtorotatetheobjectbythecamera
//angle
fixedspace_x=fmul(obj_x,fcos(angle))+fmul(obj_y,fsin(angle));
fixedspace_y=fmul(obj_x,fsin(angle))+fmul(obj_y,fcos(angle));
//calculatethescreencoordinatesthatgowiththesespacecoordinates
//bydividingeverythingbyspace_x(thedistance)
screen_x=bmp>w/2+fixtoi(fmul(fdiv(params.scale_x,space_x),space_y));
screen_y=fixtoi(fdiv(fmul(params.space_z,params.scale_y),space_x))params.horizon;
//thesizeoftheobjecthastobescaledaccordingtothedistance
height=fixtoi(obj>h*fdiv(params.obj_scale_y,space_x));
width=fixtoi(obj>w*fdiv(params.obj_scale_x,space_x));
//drawtheobject
stretch_sprite(bmp,obj,screen_xwidth/2,screen_yheight,width,height);
}

Screenshot:

Conclusion
InthisarticleIsetouttoanswersomeofthemostcommonquestionsonsineandcosine,ortrigonometryin
general.Icouldgiveyouamoremathematicalexplanationofsineandcosine,butIwantedthisarticletobe
ofpracticalusetogameprogrammers,especiallytoAllegrogameprogrammers,nottogivean
encyclopedicdescriptionofabstractmathematics.Ihopethatthishasbeenofsomeusetoyou,mydear
reader.Pleasesendmeanemailifyouhavesomethingtosayaboutthisarticle,whetheryoulikeit,dislike
it,findituseful,orjustwanttosayhi.Ifyouhaveanyquestionsyoucanaskthemontheforumsat
http://www.allegro.cc.ItisverylikelythatI'llseeitthere.Andifyoueverwriteaneffectinademoor
gameusingtheexplanationsinthisarticle,Iwouldverymuchliketoseetheresult.
Amarillion
Email:amarillion@yahoo.com
Homepage:http://www.helixsoft.nl/

Anda mungkin juga menyukai