GameMakerLangAnIn DepthGuide PDF

You might also like

You are on page 1of 192

GameMaker

Language:
AnInDepthGuide

Thisbookisdedicatedtomybeautifulwife,mydaughter,
andeveryaspiringgamedeveloper.

TableofContents
Preface:Introduction
Book:Contents
Chapter1:LexicalStructure
Chapter2:DataTypesandValues
Chapter3:Variables
Chapter4:ArraysandDataStructures
Chapter5:ExpressionsandOperators
Chapter6:Statements
Chapter7:ScriptsandAudio
Chapter8:ObjectsandSprites
Chapter9:Events
Chapter10:GameAudio
Chapter11:DevelopmentPatternsandTricks
Chapter12:DrawingontheGUILayer
Chapter13:ParticlesandSurfaces
Chapter14:Physics
Chapter15:OnlineMultiplayer
Chapter16:ArtificialIntelligence
ContactandKickstarter

Preface

Introduction
TheAuthor
Hey,guys!Goodmorning,afternoon,or
evening,whereverandwheneveryouare!My
nameisBenjaminAnderson.Youmayknow
mefrommyYouTubechannel,HeartBeast,
whereIuploadfreeGameMakertutorialvideos.
TeachingGameMakerismypassion.

Letmetellyoualittlemoreaboutmyself.Iwas
raisedinVernal,Utah(wheretherearemore
cowsthanpeople).Igrewuponafarm,but
spentmostofmydaysbuildingsmallcomputer
gamesinGameMaker.Ifinishedmyfirst
recognizedgame, DeepMagic ,attheageof15
andpublisheditforfreeontheYoYoGames
sandboxwebsite.There,itreceivednearly
18,000downloads.Soonafterfinishing Deep
Magic,Ifinishedmysecondpopulargame,
AncientWar ,whichwasalsopublishedforfree
ontheYoYoGameswebsiteandreceivedover
27,000downloads.Youcanfindbothofthese
gamesonGameJolt,butremember,Imadethemalmosttenyearsago,sodontexpect
anythingtoofancy.

Duringthenextpartofmylife,IleftforRiodeJaneiro,Brazil,whereIlivedforacouple
ofyearstolearnPortuguese,makenewfriends,andhaveanadventure.Afterreturning
fromBrazil,Idecidedtopursueacareerincomputerscience.

Myfavoritethingtodoisprogramgamesandteachotherpeopletodothesame.I
startedasuccessfulYouTubechannelinJanuaryof2014.Iplantocontinuelearning
moreaboutGameMakerandsharingmyknowledgewithanyonewhoiswillingtolisten.

Outsideofthecomputerworld,Ienjoywakeboarding,playingchess,reading,writing,
doingmath,playingguitar,playingpiano,singing,playingPokmon,learning
languages,andwatchingPsychorCastlewithmywife.

Welcome,GameDesigner.
Thereissomethingmagicalaboutgamedevelopmentthatcantbefoundinanyother
creativemedium.Everyonehastheirownreasonsforenjoyingthisactivity.Forme,its
thegodlikefeelingIgetwhenIcreatemyownworldandthesatisfactionthatcomes
fromknowingmycreatedworldwillfollowtherulesIveset.Itsthesenseof
accomplishmentIfeelafterspendinghoursonafrustratingproblemandfinallygettingit
toworkanditsthejoythatfillsmewhenIseeasmileonthefaceofsomeoneexploring
mycreation.ThesearethereasonsIspendmostofmyfreetimelearninghowto
programandteachingwhatIvelearnedtoanyonewillingtolearn.

Gamecreationinadigitalformisarelativelyunexploredmedium.Whereasartand
musichavebeenaroundforaslongashumanshave,peopleofthecurrentgeneration
arethepioneersofthisamazingnewmedium.Itiseasiertogetstartednowthanever
before.Ourtimeisnow.

GameMakerComponents
Welcometosquareone.IfyouhaventeverusedGameMakerbefore,thiswillbethe
mostimportantsectionofthisbook.IfyouhaveusedGameMaker,thenyoumaywant
toskipthissection,asmuchofitwillbereview.Below,Iwillbrieflyexplainthebasic
resourcesthatyouwillusewhilebuildingagame.

Sprites
sprite
A inGameMakerreferstoanimagethatrepresentanobjectinyourgame.
Withoutsprites,alloftheobjectsinthegamewouldbeinvisible.



Origin
Everyspritehasan origin.Theoriginofaspriteisareferencepointthatdeterminesthe
locationatwhichthespriteisdrawn,relativetotheobjectitisassignedto.Quiteoften,
theoriginofaspritewillbesettocentered,resultinginasymmetricalflipifthespriteis
mirrored.

Mask
mask
A isusedforcollisiondetectionbytheobjectassociatedwiththesprite.Imost
oftenusesimplerectangularcollisionmasksbecausetheyaretheeasiesttocontroland
generallyresultingoodlookingcollisions.Ifyouareabeginner,thenIsuggestnot
clickingtheprecisecollisionchecking checkboxuntilyoufullyunderstandhowit
worksitcancauseglitchesinyourgamescollisioncheckingandcanalsoslowyour
gamedownbecauseitishardertocompute.



Subimages
Spritescanbemadeupofmultipleimages,eachoftheseimagesiscalleda subimage .
Whenrunningagame,thespritewillcyclethroughthesesubimagestocreatean
animation.Thedefaultspeedofthespriteissetto1frameperstep,butyoucanalso
adjustthespeedofthisanimation.Thespeedisdependentonthespeedofthegame,
thatis,theroomspeed,whichcanalsobechanged.



Objects
ObjectsarecentraltotheprogrammingarchitectureofaGameMakergame.Objectsin
GameMakerinterpretalloftheeventsandrunthemajorityofthecode.Mostobjects
haveaspriteassignedtothem.Whenyouplaceanobjectinyourlevel,thethingyou
willseedisplayedinthegamewillbethe(firstsubimageofthe)spriteassociatedwith
thatobject.

Events
Events
controlthebehaviorofeachobject.Aneventisaconditionthatismetduringthe
gameeacheventcantriggeranaction(moreonactionsbelow).Anexampleofan
eventwouldbetheGame Start Event .Thiseventistriggeredattheverymomentthe
gamestartsrunning.Itcanruncodeinanactionthatisrelevanttothestartofthe
game,suchasshowingastartscreen.

Actions
Actionsarewhathappenwhenaneventoccurs.GameMakerhasalargevarietyof
draganddropactionsthatcanbeused.Inthisbook,Iwillnotbetalkingabout
draganddropactionsverymuchtherearealreadysomeexcellentbooksavailablethat
teachyouhowtousethem.Thereisonedraganddropactionthatyouwillbeusing
quiteoften:theExecute Code Action .ThisistheactionthatrunsGameMakers
scriptinglanguage,GML(shortforGameMakerLanguage).Allofthecodeexamplesin
Execute Code Action
thisbookwilleitherberuninan orinsideascript(moreon
Execute Code Action
scriptslater).The islocatedintheControltabunderCodein
anobjectsproperties.

Timelines
timeline
A isalistofmoments(pointsintime)inagame.Ateachmoment,youcanset
anactiontobeexecuted.Timelinesareusefulforcontrollingsequencesofactions.Ilike
tousethemforartificialintelligencesequences.Forexample,youhaveanenemythat
runsuptoyou,jumpsforwardtoattack,andjumpsback.Thisshortsequencecouldbe
definedusingatimeline.

Fonts
fonts
Youcanusethisresourcetocreatedifferent foryourgame,changingthewaythat
textappears.Youcansetthesizeandstyleforyourfonts.Fontscanbeaccessedin
yourgameusingthefontresourcesname.

Sounds
Sounds,justlikesprites,canaddveryimportantfeedbacktoyourgame.Soundscanbe
playedonalooporplayedonlyasingletime.Addinghighqualitysoundswillmakejust
asmuchofadifferencetothefeelofyourgameasaddinghighqualitysprites.

10

Rooms
Rooms containthelevelsofyourgame.Eachroomcanbefilledwithobjectsatdifferent
locations.Youcanalsouseroomstocreatemenus,statscreens,andinventory
screens.Roomshaveaheightandwidthandcanalsoemploybackgroundsandviews
(moreonthesebelow).

Backgrounds
Backgrounds aresimilartospriteshowever,abackgroundisassociatedwitharoomin
thegame,notanyspecificobject.Youcanaddabackgroundtoaroomintheroom
properties.

11

12

Views
Aview
isasectionofaroomthatcanbedisplayedonthescreen.Therearemany
timesingameswhenyoudontwanttheplayertoseetheentirelevelyoucancreatea
viewinsidetheroomthatrestrictswhattheplayercansee.Viewshaveaheightand
widththatsethowmuchoftheroomisvisible.Viewsalsohaveaportheightandport
widththatdefinethesizeofthegamewindowonyourscreen.

13

Book

Contents
Whatisinthisbook?
Thisbookasamixtureofprose,simplecodeexamples,images,andactualgame
examples.Atthestartofthebook,Iusemainlyimages,prose,andsimplecode
examplestogetyoustartedonlearninghowGameMakerLanguageworks.Later,Istart
togivemorecomplicatedexamplesthatinvolvebuildingminigamesorbasicgame
engines.

Chapter1:LexicalStructure
InChapter1,youwilllearntounderstandcomments,literals,identifiers,andsomeof
therulesthatmustbefollowedwhilewritingcodeinGameMakerLanguage.

Chapter2:DataTypes
Chapter2coversthedifferentdatatypesandvaluesthatyoucanuseinGameMaker
Language.Thisinformationwillbecomemoreimportantasyoulearnboththedifferent
functionsavailableinGameMakerandthetypesofdatayoushouldpassintoeach
function.

Chapter3:VariablesandScope
Chapter3willgiveyouasolidunderstandingofvariables,includingtypeandscope.
YouwilllearnthatGameMakerisaweaklytypedlanguageandhowthatinfluencesthe
wayyouprogram.

Chapter4:DataStructuresandArrays
InChapter4,yourbrainwillprobablyexplodeasyouattempttoabsorballthe
informationaboutdatastructuresandarrays.Chapter4isatrickychapter,butitisalso
veryrewardingbecauseitwillprepareyoufornetworkinginGameMakerLanguage.

Chapter5:ExpressionsandOperators
Chapter5willteachyouthedifferentexpressionsandoperatorsthatyouhaveatyour
disposalwhenwritinginGML.Manyoftheseoperatorsarecommontoother
programminglanguages,andasolidunderstandingofhowtheyworkisessentialto
writingpowerfulcodeinGameMakerLanguage.

14

Chapter6:Statements
WhenyoureachChapter6,youwillbereadytouseallthatyouhavelearneduptothat
pointtocreatedifferentstatementsinGameMakerLanguage.Thesestatementswill
helpyoudefinethelogicofyourgame.Statements,likeoperators,arenecessaryfor
anylargeGameMakerproject.

Chapter7:Scripts
InChapter7,youwillquicklylearnofthepowerofGameMakerscripts.Scriptsexecute
blocksofcodeandareusedtocutoutredundanciesthatmaymakeyourcodebloated
andhardtomaintain.Writingthesametypeofcodeinallofyourseparateobjectscan
becomedifficulttomaintainandwasteyourtime.Scriptswillgiveyouthepowertowrite
codeinoneplaceandthencallthatcodeinmultiplelocations(asneeded).

Chapter8:ObjectsandSprites
AsyoureadthroughChapter8,youwilllearnallaboutobjectsinGameMakerandtheir
relationshipswithsprites.Objectsandspritesaretwoofthemainbuildingblocksofany
gamebothhavemanydifferentbuiltinvariablesandpropertiesthatyouwillwantto
becomefamiliarwith.

Chapter9:Events
Chapter9willcoverthebasicsofGameMakerevents.Youwilllearnhoweventscontrol
theexecutionofcodeandwhicheventsareusedmostoften.Youwillalsolearnhowto
createandruncustomizedevents.

Chapter10:GameAudio
Chapter10getsyoustartedwithplayingsoundsinyourgame.Illteachyouthebasic
functionsyouneedforaudioandafewotherfunctionsyouwillusetocreateaudio
emittersthatcanmodifyyoursoundswhileyourgameisrunning.

Chapter11:Patterns,Tricks,andTips
Chapter10isfullofbasicpatterns,functions,tips,andtricks,includingtricksforgetting
inputfromtheplayer,howtocontrolzoomedviews,howtomakeanobjectfollowthe
mouse,andhowtomakeanobjectpointtowardsthemouse(amongmanyothers).

Chapter12:SurfacesandParticles
InChapter12,youwilllearnhowtocreateamazinggraphiceffectsinyourgameusing
thepowerofsurfacesandparticles.Illshowyouhowtocreateandmanipulate
surfaces.Inthesectiononparticles,Illteachyouhowtocreateyourownparticle
systems,particletypes,andemitters(andwellfigureoutwhatthesetermsevenmean).

15

Chapter13:HUDandGUI
Inthischapter,youwilllearnhowtodrawinformationsuchashealth,lives,andscore
onthescreen.Youwilllearnmorespecificallyaboutthe Draw GUI Event inside
GameMakerandhowitworksdifferentlyfromallotherdrawevents.

Chapter14:Physics
ThischapterisallaboutGameMakersbuiltinphysicsengineandhowyoucanuseitto
startbuildingsomeveryrealisticgames.Illshowyouhowtobuildasimple
boxesundergravityexampleandthenmoveontoamorecomplicated
truckandrandomlygeneratedterrainexample.Thischapterwasloadsoffuntowrite
andImsureyouwillenjoyreadingit.

Chapter15:OnlineMultiplayer
Inthischapter,Istartbyexplainingsomecommontermsthatyouwillneedto
understandinordertobuildyourfirstonlinemultiplayergame.Next,Illwalkyou
throughacoupleofexamples.Thefirstexampleissimpleandcantreallybeclassified
asacompletegame,butitisgreatforlearningthebasicsofnetworkcommunication.
Afterthat,Illshowyouhowtobuildasimple,turnbasedboardgame.

Chapter16:ArtificialIntelligence
Thischapterisdedicatedtoallthedifferenttypesofartificialintelligence.Illcoverbasic
topdownartificialintelligence,someoptionsforplatformartificialintelligence,some
basicpathfinding,andafewotherconsiderationswhenyouareprogrammingthose
baddies.

16

Chapter1

LexicalStructure
Youmightthinkthatlexicalisabigword.Well,Iguessitisntreallythatbig,butit
definitelydoesntcomeupoftenineverydayconversation. Lexical
referstothe
vocabularyofalanguage.Inthischapter,Iwillteachyouthebasicvocabularyof
GameMakerLanguage.

Comments
comment
A inGameMakerLanguageisasectionoftextthatiscompletelyignoredby
compiler
the (thepartofGameMakerthatturnsyourcodeintoagame)inotherwords,
commentsdontdoanythingforyourgame.Commentsareusefultoprogrammers
becausetherearetimeswhenitisbeneficialtodescribeapieceofcode.

AddingCommentstoCode
TherearethreewaystodeclareacommentinGameMakerLanguage.Thefirstisto
placetwoforwardslashes, // commentout
,beforethelineofcode.Thiswill (wesay
commentouttoremindourselvesthatthe comment out
is oftheconsiderationofthe
compiler)anycharactersonthelineafterthetwoslashes.

// This is a comment on its own line


vari
=
0
;
// This is a comment after some code

Thesecondwaytocommentallowsyoutocommentoutmultiplelinesofthecode.This
/*
kindofcommentstartswithaforwardslashthatisfollowedbyanasterisk, ,and
*/
finisheswithanasteriskthatisfollowedbyaforwardslash, .

/* Here is a comment
that spans a
few lines */

Thethirdwaytoaddacommentissimilartothefirst,butithassomefunctionalitythatis
specifictoGameMakerLanguage.Thistypeofcommentisdeclaredbyplacingthree

17

///
forwardslashes, ,beforethelineofcode.Itworkssimilartothefirsttype,but
when
thiscommentisplacedonthethefirstlineofacode,ithasadditionalbenefits .

/// This is a comment with added benefits.

Theaddedbenefitsyougetdependonwhetherthecommentisonthefirstlineofan
Execute Code Action oronthefirstlineofascript.

Ifthecommentisonthefirstlineofan Execute Code Action


,itwillbeshownasthe
descriptiontextofthataction.Youcanusethiskindofcommenttolabelcodeactions
andmakeiteasiertonavigateandmaintainyourcode.Letmegiveyouascreenshot
example.Ihavethiscommentonthefirstlineofmycode.

/// Initialize all the data

Code Action
Thisiswhatthedescriptionofthe lookslikebecauseofthatcomment:

Ifthecommentisonthefirstlineofascript,thecommentwillbeshownas
helpertext
(apopupthattellsyouwhichelementsareneeded)whencallingthescriptelsewhere
inyourgame.Hereisanexampleofwhatthecommentmightlooklikeinsidethescript
itself:

/// scr_add(number1,number2)

HereiswhatyouwillseewhilecallingthescriptinGameMakerbecauseofthe
commentatthetopofthescriptyougetdescriptivecodecompletionforthescript,like
this:

18

And,ontopofthat,yougetargumenthelpertextdownatthebottomofthecodeeditor
whileyouaretypinginthearguments(justlikeforallotherGameMakerfunctions).

Ifthatdoesntmakesense,youareprobablyjustnewtoscripts.Thatsokay!Justcome
backandrereadthissectionafteryouhavefinishedthechapteronscripts(Chapter7).

BestPracticesforComments
Intheworldofcommentingcode,therearetwoextremes.Ontheoneextreme,there
areprogrammerswhocommenteverylineofcodetodescribeexactlywhatthatlineof
codedoes.Forexample:

// Set health to 100


health
=
100
;

Becausethecodeisalreadyprettyclear,thesetypesofcommentsare,atbest,
redundantand,atworst,awasteofeffort.Ontheotherextreme,thereareprogrammers
19

whoneverusecomments.Thisisverycommonamongnewprogrammersbecauseat
thetimeofwritingtheircode,itallmakesperfectsense.Theproblemis,lateron,
uncommentedcodeismoredifficulttomaintain.

ThebestadviceIthatcangiveistowriteyourcommentsasiftherewereanother
programmerlookingthroughyourcode(andmaybetherewillbe).Keepthecodeclean,
buttrytoexplainthe whybehindthemorecomplicatedsectionsandnotjustthehow
.

Hopefully,thesefewpageshaveconvincedyouofthebenefitsofusingcomments,
whilealsopersuadingyoutotakeadvantageofthosebenefits.

Literals
Aliteral
isavaluethatappearsdirectlyinyourcode.Herearesomeexamplesofwhat
literalslooklikeinsideGameMakerLanguage:

true // The boolean value true


false // The boolean value false
3 // A real number
3.5 // A decimal number
"Game Maker!" // A double-quote string
'Language' // A single-quote string

Identifiers
Identifiers
arenamesgiventovariables.Hereisanexampleofhowtouseanidentifier:

age
=
25;

Doyourecognizewhatpartofthiscodeistheidentifier?Itsthewordage.The
number25representsthe literalvalue
name
ofthevariableandthewordageisthe or
identifier
.Identifiersareafairlysimpleconceptand,forthemostpart,youcanchoose
whateverdescriptivewordyouwantfortheidentifier.However,therearesome
restrictions.Ialsohaveafewwordsofadviceforchoosingidentifiers.

20

IdentifierRestrictions
Therearefivemainrestrictionsonwhatyoucanuseasanidentifier.Dontworryabout
memorizingthem.Asyoustartcreatingmoreandmorevariables,youwillstarttogeta
feelforwhatoptionsyouhave.Hereisthelist:

1. Identifierscannotexceed64characters
2. Identifierscannotcontainspecialcharacters
3. Identifierscannotcontainspaces
4. Identifierscannotbeginwithanumber(Theycancontainthem,though.)
5. IdentifierscannotsharethesamenameasanotherresourceinGameMaker
obj_player
(e.g., )

IdentifierTips
GameMakerStudiousesanamingpracticecalled snakecaseformostofitsbuiltin
variablesandfunctions.Thepatternfornamingavariableinsnakecasehaseasyrules.
Heretheyare:

1. Everyletterbelowercase
2. Replaceeveryspacebereplacedwithanunderscore

Hereisanexampleofanidentifierinsnakecase:

my_name
="
Ben
";

Simple,right?Ioftenusesnakecasewhennamingmyvariablesandresources
becauseIwanttobeconsistentwiththepatternsusedinthenativeGameMaker
variablesandfunctions.Thisdoesnotmeanthatthisistheonlywaytonamethings.In
fact,manypeopleonlyuseothernamingconventionssothattheycanmoreeasily
separatetheirvariablesandfunctionsfromthebuiltinones.

Anothercommonnamingconventioniscalled camelcase .Thereareonlythreerulesfor


camelcaseandtheymightalreadybefamiliartoyou.

1. Removeallspaces
2. Makesurethefirstwordbeginswithalowercaseletter
3. Makesureallotherwordsbeginwithanuppercaseletter

21

Hereisanexample:

myName
="
Ben
";

Regardlessofwhatstyleyouchoose,the mostimportant thingisthatyouare


consistent !
Imserious.Ifyouarenotconsistent,youwillgetmesseduplaterwhen
youaretryingtorememberwhatyounamedcertainvariables.

CaseSensitivity
GameMakerLanguageisacasesensitivelanguage.Twoidentifierswithdifferentcases
aredifferentidentifiers.Youmayhaveavariablecalled hp andadifferentvariable
HP
called .

hp
=
100
;
HP
=
200
;

Thesetwovariablesare different HP
.Ifyoutrytousetheidentifier hp
toaccess ,you
HP
willeithergetthewrongvalue(if hasbeendefined)orgetanerror(ifHP
hasnot
beendefined).

ReservedWords
InGameMaker,therearesomewordsthatcantbeusedasidentifiersbecausetheyare
reservedforbuiltinvariables.Someofthecommonreservedwordsare:

gravity
x
y
health
exp
direction
speed

22

Thevariablesaffecttheplayerobjectinawaythatyoumaynotwantincertaincases.
Becauseofthis,manyprogrammersusemodifiedversionstogetaroundthereserved
words.

grav
player_x
player_y
hp
expr
dir
spd

OptionalSemicolons
InGameMakerLanguage,placingasemicolonattheendofyourstatementisoptional.
Inmostprogramminglanguages,however,thisisnotthecase.InGameMaker
Language,thesetwostatementsareequivalent:

x
=
0
;
// A statement with a semicolon
x
=
0
// A statement without a semicolon

highly
Iwould recommendgettingintothehabitofplacingsemicolonsafterevery
statementbecausemanyothermajorprogramminglanguagesrequireit.

23

Chapter2

DataTypesandValues
NumberLiterals
Anumberliteral
isanumberinsideyourcode.Herearesomeexamplesofnumber
literals:

35
3.75555
-3

WorkingwithNumbers
WorkingwithnumbersinGameMakerLanguageissimilartoworkingwithnumbersona
calculator.Youcanperformallofthestandardoperationssuchasaddition,subtraction,
multiplication,anddivision.Therearealsosomeotheroperationsandfunctionsthatyou
canperformonnumbers.Hereareafewexamplesofsomecommonoperationsand
functions:

18
+
7 // Adds 7 to 18
30
-
5 // Subtracts 5 from 30
5
*
5
// Multiplies 5 by 5
100
/
4
// Divides 100 by 4
20
%
3 // Returns the remainder of 20 divided by 3,
which is 2

round
(
5.6
); / Rounds 5.6 up to 6
/
round
(
5.3
);
// Rounds 5.3 down to 5
floor
(
5.6
);
// Rounds 5.6 down to 5
ceil
(
5.3
);
// Rounds 5.3 up to 6
abs
(-
4
);
// Returns the absolute value of -4

sign
(-
4
); / Returns a -1 for a negative number and a
/

// +1 for a positive number

random
(
10
); // Returns a random number between 0 and 10

24

irandom
(
10
);
// Returns a random integer from 0 to 10
random_range
(
5
,
10
); // Returns a random number from 5 to 10
irandom_range
(
5
,
10
);
// Returns a random integer from 5 to 10

StringLiterals
stringliteral
A isalistofzeroormorecharacterssurroundedbysingleordouble
quotationmarks.Herearesomeexamplesofstringliterals:

"
Ben
"
"
3
"
'
GameMaker
'
'
'

3
Notethatthestringliteral 3
isdifferentfromthenumberliteral.Laterinthischapter,
Iwilltalkabouttheimportanceofknowingthedifferencebetween(andhowtoconvert
between)thetwoliteraltypes.

WorkingwithStrings
Knowinghowtoworkwithstringscanbeuseful,butitcanalsobealittleconfusingat
first.Unlikenumbervalues,stringsbehavedifferentlythanmightbeexpected.Letslook
atsomeexamples.

'
Benjamin
'+'
Anderson
';
// gives BenjaminAnderson
'
4
'
+
'
8
';// gives 48, NOT 12 or 12

ConvertingbetweenRealsandStrings
InGameMakerLanguage,itiscriticaltounderstandconversionbetweenreals
(numbers)andstringsbecauseitwonthappenautomatically.Thisisaverycommon
issuefornewdevelopers.Letslookathowyoucanconvertbetweenthetwosothat
youneverhaveaproblemwithit.

string
(
3
);
// Converts number 3 into the string '3'

25

real
('
3
');
// Converts the string 3 it into the number 3

Whywoulditbeimportanttousethesefunctions?Oneofthemostcommonreasonsis
todrawnumbervalues,suchastheplayersstats,onthescreen.

hp
=
100;
// This is incorrect and will give an error
draw_text
(
x
,
y
-
32
,
HP:
+ hp
);

Ifyouweretoputthiscodeinyourgame,itwouldgiveanerror.The
draw_text
functionneedsastringvaluetobeabletodrawtothescreen,butourvariable
hp
currentlyholdsanumbervalue.Togetthistowork,youwillneedtoconvertthenumber
toastringlikethis:

hp
=
100;
// This is correct
draw_text
(
x
,
y
-
32
,
HP:
+
string
(
hp
));

BooleanValues
booleanvalue
A hasonlytwostates:booleansareeithertrueorfalsetheycantbe
anythingelse.Hereisanexampleofaboolean:

in_air
=
false;
moving
=
true;

Simple,right?Thenamecanoftenbeintimidating,buttherereallyisntmuchtothem.
Hereisaningameexampleofhowyoumightusethe in_air boolean:

26

if
(
in_air
==
true
)
{
sprite_index
=
spr_player_jumping;
}

Thiscodechecksthebooleanvariable in_air tofindoutiftheplayerisjumping.Ifthe


playerisjumping,itchangestheobjectsspritetoajumpingsprite.

TherearentverymanybasicdatatypesinGameMakerbecausethelanguageis
weaklytyped,andso,thereareimplicittypeconversionshappening(almostmagically)
behindthescenes,whereyoudonthavetoworryaboutthem.Thisisnice,butagood
understandingofthefewbasictypesthatGameMakeruseswillhelpimproveyour
abilitytoprogramerrorfreeinGameMakerLanguage.

27

Chapter3

Variables
Variables
VariablesinGameMakerLanguage(andinotherprogramminglanguages)areusedto
storeinformationinmemory.Onceapieceofinformation(orvalue)isstored,itcanlater
beaccessedandmanipulatedthishelpscreatecodethatiseasiertounderstandand
maintain.Skilleduseofvariablescreatespowerfulcode.

VariableTyping
Variabletypingshowsupinmanyprogramminglanguages.Generally,whenyoucreate
anewvariable,thecompilerwillwanttoknowifthatvariableisgoingtobeusedtohold
astringvalue,anumbervalue,abooleanvalue,oranyothertypeofvalueavailablein
thelanguage.Thiscanbeconfusingfornewprogrammers,sovariablesinGameMaker
Languageweredesignedtobeweaklytyped.Thismeansthatyoudontneedtodeclare
thetypeofavariablewhenyoucreateit.Italsomeansthatavariablethatwasonce
usedtoholdanumbervaluecan(atanytimeinyourcode)betoldtoholdastring
value.

Eventhisbasicunderstandingofvariabletypingwillhelpyoutoavoiderrorsasyouuse
GameMakerLanguage.

VariableDeclaration
DeclaringavariableinGameMakeriseasy.Thereareafewdifferentwaystodeclare
themandIwilltalkaboutthesedifferentwaysinthenextsection.Forthemostpart,all
youhavetodoisnamethevariablewithanidentifierusetheassignmentoperator,a
singleequalssign,=
andthenincludeavalue.Hereisanexampleofsomeoftheways
thatyoucandefineavariable.

name
=
"Benjamin"
;
// String variable type
age
=
25
;
// Number variable type
happy
=
true
;
// Boolean variable type

Thenextsectionwilltalkmoreaboutvariabledeclarationanddescribehowtodeclare
variablesindifferentscopes.

28

VariableScope
The scopeofavariabledescribesthelocationsinthecodewhereyouhaveaccessto
thatvariable.InGameMakerLanguage,therearethreemainvariablescopes.Thereis
theglobalscope instancescope
,the localscope
,andthe .

GlobalVariables
Ifavariableiscreatedintheglobalscope,itcanbeaccessedfromanywhereinthe
code.Variableswithintheglobalscopearecalled globalvariables big
( surprisethere).
Thesevariablesaredefinedbyplacingthekeyword global .
andadot, ,beforethe
variableidentifier.

global
.
name
=
"Benjamin Anderson";

Aftertheglobalvariablehasbeencreated,youcanaccessitfromwithinanyscriptor
object.

InstanceVariables
Ifavariableiscreatedintheinstancescope,itcanonlybeaccessedwithinthecodeof
asingleinstance.Variableslocatedintheinstancescopearecalled instancevariables .
Create Event
Itisbesttodefinethesevariablesinthe oftheobjectinordertoavoid
errorscausedbyundefinedvariables.Thereisnosecrettodefiningthesevariables
yousimplynamethevariablewithanidentifierandthenassignavaluetoit.

name
=
"Benjamin Anderson";

Afterdeclaringtheminanobject,youcanaccessinstancevariableselsewhereinside
theobject.

InolderversionsofGameMaker,instancevariableshadtobedefinedinthe Create
Event ofanobjectorthecompilerwouldthrowanerror.

LocalVariables
Ifavariableiscreatedinthelocalscope,itcanonlybeaccessedwithintheactionor
scriptinwhichitwasdefined.Variableslocatedinthelocalscopearecalledlocal
variables.Definingalocalvariableisjustlikedefininganinstancevariable,onlyyou
placethevar keywordbeforetheidentifier.
29

varname
=
"Benjamin Anderson";

LocalvariablesdidntexistinolderversionsofGameMaker.Ifyoutrytocreatealocal
variableinanolderversion,youwillgetanerror.

Macros(FormerlyConstants)
Amacroissimilartoavariablebecauseitcancontainavaluethatisaccessibleinthe
code.Amacroisdifferentfromavariablebecauseitcanonlyhaveonevalue:onceyou
setthevalueofamacro,itcannotbemodifiedduringthegame.Inmanyprogramming
languages,theidentifierforthemacroiswritteninallcaps.Thishelpstheprogrammer
todistinguishitfromothervariables,primarilyasareminderthatitcannotbealtered.

Todefineamacro,youwillneedtoclickonthemacrosnodeintheresourcetree.
Therearetwonodesthatyoucanchoosefrom.IusethemacronodelabeledDefault
Oncethemacrowindowcomesup,youcanusetheaddbuttontocreateasmanyor
fewasyouwillneed.Afterthemacrohasbeendefined,youcanuseitinsideyourcode
justlikeanyothervariable.

draw_text
(
x
,
y
,
COMPANY_NAME
);

macrosfallundertheglobalscope
Itshouldalsobenotedthat andcanbeused
anywhereinthecode.

Enums
Enums arearathernew(andveryuseful)partofGameMakerLanguage.Theword
enumisshortforenumeratoranenumenumerates(orlists)asetofkeyvaluepairs.
Letslookathowyoucancreateone.

enumbasestat {
hp
=
50,
att
=
20,
def
=
18,

30

spd
=
24
}

Easyenough.Afteryouhavecreatedtheenum,youcanaccessthevaluesinitlikethis.

varbasehp
=
basestat
.
hp;
// Returns 50

Itsimportanttoknowthat
enums,likemacros,areglobalinscope,sotheycanbe
accessedanywhereinyourcode .Theyareconstants,meaningthattheycannothold
valuesthatwillchange.Enumsalsohavedefaultvalues,soyoudonthavetoassigna
valuetothem.Youcouldcreateanenumwithdefaultvalueslikethis:

enummonths {
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
}

month.January
Thedefaultvaluesstartat1andcountup.Inthisexample, nowhasa
valueof1andmonth.December hasavalueof12.

Inalaterchapter,Iwillshowyousomewaystouseenumsincombinationwitharrays
togiveyourcodebetterstructureandalsomakeiteasiertoread.

31

UndefinedVariables
Anundefinedvariableisonethathasneverhadavalueassignedtoit.Thesetypesof
variableswillshowupquiteoftenwhenyouarefirstlearningtoprogrambecauseyou
willtrytocallavariablethathasnotyetbeendefinedandthecompilerwillthrowan
error.Ifyoureadtheerrormessagecarefully,itwilltellyoutheobject(andoften,the
exactlineofcode)wheretheerroroccurred.

32

Chapter4

ArraysandDataStructures
Arrays
array
An isakindofvariablethatcanholdmorethanonevalue.Easy,right?Letme
seeifIcangiveyouanideaofwhatImtalkingabout.Hereisanexampleofsome
ordinary(thatis,singlevalued)variablesandthenanexampleofanarray:

// Ordinary variables
varname1
='
Ben
';
varname2
='
Charly
';
varname3
='
Dalin
';

// Array
varnames
;
names
[
0
]='
Ben
';
names
[
1
]='
Charly
';
names
[
2
]='
Dalin
';

names
Ifyouimaginethatthearray referstoarowofsmallboxesinwhicheachboxhas
itsownlabel(anindex)anditsowncontent(somedata),thenyouareimaginingwhat
anarraymightlooklikeinphysicalform.Hereisanimagethatcanhelpyoutovisualize
it:


Ifyouhaveneverusedanarraybefore,then,atthispoint,youareprobablywondering
howanarrayisbetterthanmultiplevariables.Themainreasonthatarraysarebetteris
33

thatyoucanloopthroughthem.Iwilltalkaboutloopsmorelater,butfornow,letme
explainjustthebasics.

Whatifyouwantedtodisplaythesenamesonthescreen?Hereishowyouwoulddoit
withthelistofordinaryvariables:

draw_text
(
32
,
32
,
name1
);
draw_text
(
32
,
64
,
name2
);
draw_text
(
32
,
96
,
name3
);

Nottoocomplicatedright?Inthiscase,itisntaproblembecausethereareonlythree
names,butwhatiftherewere100names?Thecodingwouldbecomeprettytedious.
Now,letslookathowyoucandisplaythenameswithanarrayandaloop.

vari
=
0;
repeat
(
3
){
draw_text
(
32
,
32
*(
i
+
1
),
names
[
i
]);
i
++;
}

Iwontgointogreatdetailexplainingtheloop,butbutknowthatthiscodedoesexactly
whatthecodeabovedoes,onlyitusesanarray.Thebenefitofthiscodeisthat,ifthere
are100names,allyouhavetodoischangethenumberin repeat() ,likeso:

vari
=
0;
repeat
(
100
){
draw_text
(
32
,
32
*(
i
+
1
),
names
[
i
]);
i
++;
}


Supercool,huh?Thiscodeiswaybetterthantyping100statementswithonlyslight
differences.

34

Arraysareextremelypowerful.Thevaluewithinthesquarebrackets, [ ]
,ofthearray
containswhatiscalledthearraysindex.Thisnumberindicateswhatlocationinthe
arraytoreaddatafrom(orwritedatato).Theindexofthearraystartsat0andcounts
upfromthere. Itisimportanttorememberthatthefirstindexofanarraywillbeat
position0,notposition1.

ArrayRelatedFunctions
InGameMakerLanguage,thereareafewdifferentfunctionsthatletyoumanipulate
arrays.Therearentverymany,soIwillcoverthemallhere.Thefirstfunction,
is_array ,allowsyoutochecktoseeifavariableisactuallyanarray.

vara;
a
[
0
]=
0;
a
[
1
]=
0;

if
(
is_array
(
a
)){
show_message
('
The variable is an array
');
}else{
show_message
('
The variable is NOT an array
');
}

Thisfunctionreturnsaboolean(trueorfalse)valueindicatingwhetherornotthe
variablepassedtoitisanarray.

array_length_1d
Theotherfunction(onethatI,personally,usemoreoften)is .

vara;
a
[
0
]=
0;
a
[
1
]=
0;

show_message
('
The size of the array is:
'+
string
(
array_length_1d
(
a
));

Thisfunctionreturnsthearrayssize(asanumber).Youmaywonderwhythefunction
_1d
has attheendofit.ThereasonisthatGameMakersupportsbothonedimensional

35

(1d)arraysandtwodimensional(2d)arrays.Sofar,youhaveonlyseen1darrays
however,2darraysareatleastaspowerfulas1darrays(andcansometimesbeeven
morepowerful).Letsmoveonandlearnalittleabout2darrays.

TwoDimensionalArrays
A2darrayisanarraywithtwoindexes.Ifa1darraycanbethoughtofasarowofdata,
witheachindexrepresentingapositionintherow,a2darrayislikeagridofdata,
whereeachindexindexpairrepresentsalocationinthegrid.

DifferentDataStructuresinGameMaker
GameMakerStudiohassomeamazingdatastructuresavailablehereisalistofthem:

stacks
queues
lists
maps
priorityqueues
grids

Eachofthesedatastructuresisauniquetool,andeachcanbeappliedtodifferentdata
storagescenarios.Ihaveusedmanyoftheminmyowngameprojects.Thissectionof
thechapterisnecessarytounderstandthechapteronmultiplayergames.Makesureto
paycloseattentionifyouareinterestedinmakingmultiplayergames.

Stacks
Stacks aredatastructuresthatcanbefoundinmanyprogramminglanguages.Astack
isalastinfirstout(LIFO)datastructure.Ifyouremoveonesliceofdatafromthestack,
itwillbethemostrecentlyaddedsliceofdatathatgetsremoved.Imagineadeckof
cardswhereyoucanonlyaddorremovecardsfromthetop.Hereisanimagethat
mighthelpyoutoimaginehowastackworks:

36

LastIn,FirstOut(LIFO)


ThecodebelowshowsyouhowtocreateastackinGameMakerLanguage.Thislineof
my_stack
codewillcreateastackandthenassignitsidtothe variablesothatyoucan
accessitlaterthroughthatvariable.

my_stack
=
ds_stack_create
();

Itisimportanttodestroydatastructureswhenyouaredoneusingthem,becausethey
cantakeupalotofmemory,eventothepointofcrashingyourgame.Hereishowyou
candestroythestackwhenyouarefinishedwithit:

ds_stack_destroy
(
my_stack
);

Therearethetwomethodsusedtoaddorremovedatafromastack.Whenworking
withstacks,addingdataisreferredtoaspushingandremovingdataisreferredtoas
popping.Usingourcardanalogy,pushingislikeaddingacardtothetopofthedeck,
andpoppingislikeremovingacardfromthetopofthedeck.Hereisthefunctionyou
usetopushavaluetothestack:

ds_stack_push
(
my_stack
,
3
);

37

Irecommendmakingsurethatthestackhasdatabeforeattemptingtocallapop
method.Hereishowyoupopthetoppieceofdatafromthestack(ifandonlyifitisnot
empty):

if
(!
ds_stack_empty
(
my_stack
)){
varnumber
=
ds_stack_pop
(
my_stack
);
}

Sometimes,youmaywanttocheckthevalueonthetopofthestackbeforeyoupopit
(callingthepopfunctionwillremovethedatafromthedatastructure,butthisfunction
willleavethedataalone,alittlelikejustpeekingatthetopcardofthedeck).

varnumber
=
ds_stack_top
(
my_stack
);

Toclearthestackofallofitsvalues,simplycallthisfunction:

ds_stack_clear
(
my_stack
);

Youcancopystacksaswell.

my_newstack
=
ds_stack_create
();
ds_stack_copy
(
my_newstack
,my_stack
);

Lastly,youcanfindouthowmanydataelements(cards)thereareinthestack.

varstack_size
=
ds_stack_size
(
my_stack
);

UsingaStackforaCardGame
Nowthatyouveseenthebasicfunctionsusedformanipulatingastack,Imgoingto
showyouhowwecanuseastacktobuildacardgame.Ihavesimplifiedthisexample
asmuchasIcansothatwecanfocusonthestackdatastructure.
38

Wewillneedtocreateafewspritesforthisexample.Theycanbeassimpleasdifferent
coloredsquares.Imademyspritesrectangulartomakethemlooklikecards,butyou
cancreatethemhoweveryoulike.

spr_card_red
spr_card_green
spr_card_blue
spr_deck

Imademydeckanoffwhitecolor,representingthebackofacard.Wecankeepthese
reallysimple,butusingdifferentcolorswillhelpusvisualizehowstacksworkinagame.

Nowthatwehavethesprites,itistimetocreatetheobjects.Wewillneedthreeobjects.
HereiswhatIchosetonamethem:

obj_deck
obj_card
obj_drag_controller

Oncewehavecreatedthesethreeobjects,wearereadytostartprogramming.Open
thedeckobject,addaCreate Event toit,anddragoveran
Execute Code Action
fromthecontroltabontheright(thisiswherewewillcreateourstack).

obj_deck: Create Event

/// Create the stack data structure


deck
=
ds_stack_create
();

Thislineofcodecreatesanewstackandassignsittothevariable deck.

Now,wearegoingtoleavethedeckobjectforabitandopenupthedragcontroller
object.Oncewehavethedragcontrollerobjectopen,wecanaddanew Create Event
toitanddragoveranExecute Code Action .Inthisaction,wearegoingtocreatea
variableforcontrollingourdraganddropcardmovement.

39

obj_drag_controller: Create Event

/// Create a global variable to keep track


// of the card we are drawing
global
.
card
=
noone;

Thisglobalvariablewillholdthevalueofthecardinstancethatwearedraggingwiththe
mouse.Oncewehaveareferencetotheinstance,wecanmakeitfollowthemouseina
Step Event .

obj_drag_controller: End Step Event

/// Move the card


if
(
global
.
card!=
noone
){
with
(global
.
card
){
x
=
mouse_x;
y
=
mouse_y;
}
}

Thiscodeperformsanimportantcheckbeforetryingtoaccessthe global.card
instance.Itfirstcheckstomakesurethatweactuallyhaveacard.Ifwedo,thenitsets
thexandypositionsofthatcardequaltothexandypositionsofthemouse.Thiscode
isplacedinthe End Step Event fordrawtimingreasons.Ifweplaceitinthe
Step
Event ,thecardwillappeartolagbehindthemousebecausethepositionwillbe
updatedafterdrawingthesprite.Tryit,ifyouwant!Maybeyouwillliketheeffect.

Ourobjectdragcontrollerisnowfinished,butwemaynoticethat,currently,
global.card willalwaysholdthevalue noone
.Weneedtoaddsomecodethatallows
ustopickupacardbyassigningitsidtothe global.card variable.Butfirst,weneed
toaddsomecodethatassignsarandomcardcolortoeachcardinstance.Letshandle
thatcodeinsideofourcardobject.Openupthecardobjectandaddanew Create
Event
andan Execute Code Action .

obj_card: Create Event

/// Initialize the card object and choose a sprite

40

sprite_index
=
choose
(
spr_card_red
,
spr_card_green, spr_card_blue
);

Thislineofcodechoosesarandomcardspritefromtheonesthatwehavecreatedand
assignsittoourcard.Withthiseventoutoftheway,wearereadytoaddthecodethat
picksupthecard.Addanewevent.Thistimewearegoingtochoosethe Mouse, Left
Pressed Event .

obj_card: Left Pressed Event

/// Pick up the card


global
.
card
=
id;
depth
=-
1;

Nowwecanpickupthecards!Itisaseasyasassigningtheidofthecardinstancethat
weclickedontotheglobalvariablethatwecreatedinthedragcontrollerobject.Once
wehavedonethat,thedragcontrollerobjectthatwealreadycodedtakescareofthe
restandmovesthecardaroundforus.Wearealsosettingthedepthto1tobringthe
cardinfrontofothercardsthatmaybeintheroom.

Itstimetowritethecodethatwillallowustodropcards.Addanewmouseeventwitha
Code Action Mouse, Left Released Event
.Withthisone,wewillusethe .

obj_card: Left Released Event

/// Drop the card on the ground or on the deck


varmx
=
mouse_x;
varmy
=
mouse_y;
if
(
position_meeting(
mx
,
my
,
obj_deck
)){
// Add the card to the deck stack
with
(obj_deck
){
ds_stack_push(
deck
,
global
.
card
.
sprite_index
);
}

// Destroy the card instance


with
(
global
.
card
){
instance_destroy
();

41

}
}
global
.
card
=
noone;
depth
=
0;

Thatisoneofthemostcomplicatedsectionsofthisexample,soletmeexplainit.First,
wegetlocalreferencestothe mouse_x andmouse_y positions.Afterthat,wecheckto
seeifthemouseishoveringoverthedeck.Ifitishoveringoverthedeck,wepushthe
global.card spriteindextothestack.Forthisexample,wereallyonlyneedtoknow
whatspritetheobjecthad.Afterpushingtothestack,wedestroythecardobject.Once
allofthesestepshavebeenexecuted,wesetthe global.card noone
backto andset
thedepthbacktozero.Ofcourse,ifthemouseisnthoveringoverthedeck,thenwewill
ONLYset global.card noone
to andsetthedepthtozero.Thiswilldropthecardon
theground,wherewecanpickitupagainlater.

Greatjobsofar!Thereisonlyonemorethingforustoaddtogetourgameworking.
Weneedtoallowtheplayertotakeacardoffthetopofthedeck.Openupthedeck
objectagainandadda Mouse, Left Pressed Event Code Action
toit.Draga over
intotheeventandwritethiscode:

obj_deck: Left Pressed Event

/// Remove the card from the deck


if
(
ds_stack_size
(
deck
)>
0
){
vartop_card
=
instance_create
(
x
,
y
,
obj_card
);
varcard_sprite
=
ds_stack_pop
(
deck
);
top_card
.
sprite_index
=
card_sprite;
global
.
card
=
top_card;
global
.
card
.
depth
=-
1;
}

Thislastblockofcodeisprobablythesecondmostcomplicated.Thefirstthingwedois
checktomakesurethatthestackisntempty.Ifthestackisemptyandwetrytoremove
informationfromit,wewouldgetbadinformation.Onceweknowforsurethatthestack
hasinformation,wecancontinue.Thenextthingwedoiscreateanewcardinstance
top_card
andstoreitinalocalvariablecalled .Afterthat,wepopthespriteinformation

42

thatwestoredinthestackandassignittoalocalvariablecalled card_sprite .Now,


wehavealloftheinformationweneed.Weassign card_sprite tothenewcard
instancethatwecreated,makethatnewcardinstance global.card ,andsetitsdepth
to1tosimulatepickingitup.

Congratulations!Youjustbuiltasimplecardgameusingastackdatastructure.Asyou
playaroundwiththegame,paycloseattentiontotheorderinwhichcardsareremoved
fromthedeck.Ifyouareperceptive,youwillnoticethatcardsareremovedinthe
reverseorderthattheywereplacedinthedeck.Thisexampleshowsthemain
principlesofhowastackworks.

Queues
Aqueue isafirstinfirstout(FIFO)datastructure.Thefirstdataelementthatyouaddto
itwillbethefirstonethatyoucanremovefromit.Theeasiestwaytounderstanda
queueisjusttothinkofalineatthesupermarket.Thefirstpersoninlinewillbethefirst
persontobehelped.Hereisanimagetohelpyouimaginehowaqueueworks:

43


FirstIn,FirstOut(FIFO)


Letscreateaqueue.Itssimilartohowyouwouldcreateastack.

my_queue
=
ds_queue_create
();

Createthequeueandassignitsidtoavariablesothatyoucanaccessitlater.

Youcandestroythequeuelikethis:

ds_queue_destroy
(
my_queue
);

Toaddanitemtothequeue,youusethisfunction:

ds_queue_enqueue
(
my_queue
,
3
);

Toremoveanitemfromthequeue,youcanusethisfunction.Itsagoodideatomake
surethequeueisnotemptyfirst.

if
(!
ds_queue_empty
(
my_queue
)){

44

varnumber
=
ds_queue_dequeue
(
my_queue
);
}

Queueshavetwoendstheyhavewhatiscalledtheheadofthequeueandthetailof
thequeue.Theheadisthenextvaluetoberemovedwhenusingthe
ds_queue_dequque function.Thetailisthelastvalueaddedusingthe
ds_queue_enqueue function.Youcanpeekattheheadorthetailwithoutremoving
themusingthesefunctions:

varnumber
=
ds_queue_head
(
my_queue
);
varnumber
=
ds_queue_tail
(
my_queue
);

Youcanclearqueues,copyqueues,andfindouthowmanyvaluesaqueuecontains,
similartohowyoudidwiththestackdatastructure.

ds_queue_clear
(
my_queue
);

my_newqueue
=
ds_queue_create
();
ds_queue_copy
(
my_newqueue
,
my_queue
);

varqueue_size
=
ds_queue_size
(
my_queue
);

Lists
Thelist
datastructureinGameMakerhasquiteafewdifferentfunctionsthattheother
structuresyouhaveseensofardonothave.Creatingalistisverysimilartocreatinga
1darray.However,oneofthedifferencesbetweenlistsandarraysisthatyoudonot
needtoknowhowlongalistisgoingtobe.Usingthesameanalogyusedwitharrays,
boxesofdataareaddedtothelistdynamically,andso,youdonthavetoworryabout
thelistssizewhenyoucreateit.

my_list
=
ds_list_create
();

45

Justliketheotherdatastructures,alistshouldbedestroyedwhenitisnolongerbeing
used.Theyareeasytodestroy.Usethissimplefunction:

ds_list_destroy
(
my_list
);

Belowisthefunctionthatyoucanusetoaddavaluetothelist.Thevaluewillbeadded
totheendofthelist.Thefirstargumentinthisfunctionindicatesthelistthatyouare
addingto,andthesecondargument(inthiscase,thenumber3)isthevaluebeing
addedtothelist.

ds_list_add
(
my_list
,
3
);

Thefunctionbelowwilldeleteavaluefromthelist.Unlikewithaddingavalue,the
secondargumentistheindexofthevaluethatyouwouldliketodelete,notthevalue
itself.Justlikearrays,whenyouaretryingtoaccessavalueinalist,youwillneedto
usetheindexofthevalue.

ds_list_delete
(
my_list
,
0
);
// This function requires an index

Rememberhowwhenyouaddavaluetoalist,thenewboxisdynamicallyadded?
Well,thesameistrueinreverse.Whenyoudeleteanitemfromthelist,theboxis
dynamicallyremovedandanyboxesaftertheoneremovedwillshifttotakeupthe
emptyspace.Forexample,ifyouremovethe6thitem,andthereisa7thitem,the7th
itemwillbecomethenew6thitem.

Usingthe ds_list_find_index function,youprovideavalueandfinditsindex(below,
3isthevalue,nottheindex).Ifthereareseveralitemsinthelistthatarethesame,then
thisfunctionwillreturntheindexofoneofthem,butyoucannotknowwhichonethatit
willreturn.Ifthevaluedoesntexist,thisfunctionwillreturnavalueof1.

index=
ds_list_find_index
(
my_list
,
3
);

46

ds_list_find_value
The functionwillreturnthevalueforthegivenindex.This
functionwillnotremovethevaluefromthelist(whichthepopfunction will
dofora
stack).

value=ds_list_find_value
(
my_list
,
0
);

Oneofthecoolthingsaboutthelistdatastructureisthatyoucaninsertavalueintothe
listandtheothervalueswillmoveoutoftheway,insteadofbeingreplaced.Belowis
thefunctionyoucanuseforthat.Thesecondargumentistheindexandthethird
argumentisthevalueyouwouldliketoinsert.

ds_list_insert
(
my_list
,
0
,
5
);

Ifreplacingthevalueisyouractualgoal,youcanusethefunctionbelow.Onceagain,
thesecondargumentistheindexandthethirdisthevalue.

ds_list_replace
(
my_list
,
0
,
3
);

Listdatastructureshaveaneasytouse,builtinshufflefunction.Thisfunctioncouldbe
usefulforacardgame.

ds_list_shuffle
(
my_list
);

Youalsohavetheabilitytosortthevaluesinalist.Thesecondargumentisaboolean
valuethatdetermineswhetherthelistshouldbesortedinascendingorder.

ds_list_sort
(
my_list
,
true
);

Herearethestepsyouwouldtaketocreateacopyofalistthatyouhavealready
created:

47

my_newlist
=
ds_list_create
();
ds_list_copy
(
my_newlist
,
my_list
);

Intheend,listsareverysimilarto1darrays,buttheyhavesomeextrafunctionsand,
therefore,arebettersuitedtocertainprogrammingsituations.

Maps
Mapsareaverypowerfuldatastructure.Theyaresimilartoanobjectthathasno
eventsorscripts.Mapscontainkeyvaluepairs,whichmeansthatyouhavealistof
keyswhereeachkeyhasavalueassignedtoit.Hereisawayyoumightvisualizeakey
valuepair:

key:value

Letstakeaquicklookatsomedatathatyoumightstoreina ds_map .Note,thatthisis


notcodeandyoucantadddatatoads_maplikethis.Thisisjustanexampletohelp
youvisualizehowthedataisorganized.

class:wizard
attack:25
speed:18
mana:37

Creatingamapiseasythiskindoffunctionshouldlookfamiliartoyoubynow.

my_map
=
ds_map_create
();

Itiseasytoclearamapofallofitskeyvaluepairs.Beawarethatthisfunctiondoesnt
actuallydestroythemap.Itjustclearsthemapofeverykeyvaluepair.

ds_map_clear
(
my_map
);

48

Thecodebelowshowshowtoaddanewkeyvaluepairtothemap.Besuretocheck
outtheeasierwaytodothisjustalittlelaterinthechapterwhereItalkaboutaccessors.

ds_map_add
(
my_map
,'
hp
',
25
);

Thefunctionbelowshowshowyoucanactuallydestroythemapthatwascreated.This
shouldbedoneonceyouaredoneusingamap.

ds_map_destroy
(
my_map)

Mapsareagreatwaytostorealargeamountofdatainanorganizedway.Theyare
necessarywhenusingthenetworkingfunctionsinGameMakerLanguage,sobesureto
studythemwell.

Grids
GridsarethelastdatastructurethatIwilltalkaboutinthisbook.Gridsareexactlywhat
theysoundlike.Imagineachessboardwhereeachsquarecanholdsomepieceof
information.Youcancreategridsofanysizeandmanipulatespecificareasorsquares
insidethegrid.Gridsaresimilarto2darrays,buttheyhaveafewuniquefunctions.

Thefunctionbelowshowshowyoucancreateagrid.Thefirstargumentisthenumber
ofcolumns,andthesecondargumentisthenumberofrows.Letscreateagridwith
only9totalcells.

my_grid
=
ds_grid_create
(
3
,
3
);

And(reallyquickly)Imgoingtoshowyouhowyoucandestroyagridaswell.Notethat
whenyouaredoneusingthegrid,youshouldcallthisfunction.

ds_grid_destroy
(
my_grid
);

49

Therearedozensoffunctionsthatyoucanusewithgrids.Theyareallsupercool,andI
wouldhighlyrecommendlearningaboutthem.Imgoingtocoverafewofthemainones
here,butImnotgoingtotalkaboutallofthem,becausetheGameMakerHelpfile
explainsthemallverywell.Aftershowingyouthefunctions,wewillbuildanexample
gameusingagridsothatyoucanlearnaboutthepracticalapplicationsthatthisdata
structureprovides.

Letsstartwiththebasics.Afteryouhavecreatedthegrid,youneedtobeabletoadd
valuestothecells.Belowisthefunctionyouwillusetosetthevaluesofindividualcells.
Thefirstargumentistheidofthegrid,thesecondisthexpositioninthegrid,thethird
istheyposition,andthefourthisthevaluetobeadded.

ds_grid_set
(
my_grid
,
0
,
0
,
3
);

Itisalsopossibletosetcellvaluesonmultiplecells.Belowisthefunctionyoucanuse
tosetthecellvaluesforarectangularregion.Thefirstargumentisthegrididagain,the
secondandthirdarethexandyposition,respectively,fortheleftcornerofthe
rectangle.Thethirdandfourthargumentsarethexandyposition,respectively,forthe
bottomrightcorner.Finally,thefifthargumentisthethevaluethatwillbesetforeach
cellinsidetheregion.

ds_grid_set_region
(
my_grid
,
0
,
0
,
2
,
2
,
3
);

Itisalsopossibletosetaregionthathasacircularshape(calledadiskby
GameMaker).Youwillhavetocheckthehelpfileformoreinformationonthatone
though.

Next,Imgoingtoshowyouhowyoucanretrieveavaluefromthegrid.Belowisthe
functionthatyouwilluse.Thefirstargumentistheid,thesecondisthexpositionofthe
cell,andthethirdistheypositionofthecell.

varnum
=
ds_grid_get
(
my_grid
,
0
,
0
);

50

Thispieceofcodewillretrievethevaluefromtheupperleftcellinthegridandstoreitin
num
thelocalvariable .

Nowthatyouhaveasolid,basicunderstandingofthisdatastructure,letscreateareal
gameexampletogethertohelpyougetmorecomfortablewiththesefunctions.

TicTacToewithaGridDataStructure
Letsstepthroughaverysimpleexampleofhowwecansetupanduseagridtomake
aTictactoegameinGameMaker.

Thefirstthingthatweneedtodoforthisexampleiscreateanewspriteandnameit
spr_char .Thisspritewillhaveawidthof160,aheightof160(thesetwonumberswill
setthesizeofeachcellinthegrid),anxoriginofzero,andayoriginofzero.Itwillalso
havetwosubimages.ThefirstsubimagewillbeagiantcircleorOandthesecond
subimagewillbeagiantcrossorX.

Thesecondthingwewillneedisanewbackgroundcalled bg_tiles .Wearegoingto


usethisbackgroundtocreatethe9squaresneededineveryTictactoegame.Give
thisbackgroundawidthandaheightof160.Leavethecenterofthebackground
transparent,butdrawawhiteoutlinearoundallfouredges.

Aftercreatingthespriteandthebackground,createanewobjectandnameit
obj_game .Thiswillbetheonlyobjectinourexample.Itwillcontainourgriddata
structureandallofthecodethatisrequiredtorunthegame.Addanew Create Event
totheobject.

obj_game: Create Event

/// Create the ds_grid and initialize the


// game object
grid
=
ds_grid_create
(
3
,
3
);
ds_grid_set_region
(
grid
,
0
,
0
,
2
,
2
,-
1
);

Thiscodecreatesanewgridandthensetseverygridsquareequalto1.The
argumentsfor ds_grid_set_region shouldbeexplained.Thefirstargumentistheid
ofthegrid.Thenexttwoargumentsarethexandyposition,respectively,forthe
upperleftcorneroftheregion,followedbythetwoargumentsforthexandyposition,
respectively,forthelowerrightcorneroftheregion.Thelastargumentisthevaluethat

51

shouldbesetforeachsquareorcellintheregion.Forourexample,wewilluse1to
representanemptycell,0torepresentanOcell,and1torepresentanXcell.Ifyou
areextraperceptive,youwillnoticetheOsubimageinourspritehasanimage_index
of0andtheXsubimageinourspritehasan image_index of1.Wewillbeusingthis
facttoouradvantage.

ItstimeforthenextphaseinourTictactoeexample.Addanew Mouse > Global >


Global Left Pressed Event anddragoveraCode Action Code Action
.Insidethe ,
typethiscode:

obj_game: Global Left Pressed

/// Set an O
vargridx
=
mouse_x div
160;
vargridy
=
mouse_y div
160;
ds_grid_set
(
grid
,
gridx
,
gridy
,
0
);

ThissmallcodewillsetanOattheclickedcelllocationinthegrid.Theds_grid_set
functiontakesagrididasitsfirstargument,thexvalueasitssecondargument,they
valueasitsthirdargument,andthevaluethatthecellwillbesettoasitsfourth
argument.Here,wearesettingthevalueto0,whichisourdigitalrepresentationofO.

Beforewemoveon,itisimportanttohaveasmalldiscussionaboutthemathinvolved
here.Wehavedecidedthatourroomshouldbedividedupinto9squares,eachsquare
being160pixelswideand160pixelstall.Theupperleftofoursquareintheroomcould
havexvaluesandyvaluesanywherefrom0to159,butinourgrid,thexandyvalues
oftheupperleftsquarewillbothbe0.Ourmiddlesquareintheroomcouldhavexand
yvaluesanywherefrom160to319,butinourgrid,thexandyvalueswillbothbe1.

Weneedsomewaytoconvertfromtherangeofvaluesintheroomtothesinglevalue
inthegrid.Onewaytodothisistodividethexandymousepositionsfromtheroomby
thegridcellsize(160)andthenroundthemdown(usingthefloorfunction).Thisworks
well,butthereisaneasierway.GameMakerhasanoperatorcalledthe divoperator.
Thisoperatortakestwooperands,dividesthem,andreturnsawholenumberanswer
(meaningthatitdoesnthavetheremainder).The modoperatorandthediv operator
mod
areverysimilar.Thedifferenceisthatthe operatorreturnsonlytheremainder,
withoutthewholenumberanswer.Usingthiscoolmathtrick,wecancalculatethegridx

52

andyvaluesofthemousesxandypositionsintheroom.Justasasidenote,wecan
alsousethismethodtomakeobjectssnaptoagrid.

Nowthatthemathisoutoftheway,weneedtoaddawayforourplayertoplaceanX
intheroom.ThecodewillbesimilartothatforsettinganO,butwewillputitina
Mouse > Global Mouse > Global Right Pressed Event .

obj_game: Global Right Pressed

/// Set an X
vargridx
=
mouse_x div
160;
vargridy
=
mouse_y div
160;
ds_grid_set
(
grid
,
gridx
,
gridy
,
1
);

Wearesettingthegridvalueto1,becausethatisthedigitalrepresentationthatwe
choseforX.

ThelastpiecethatweneedinordertofinishoursmallTictactoegridexampleisthe
codethatwilldrawourgame.Thiscodecangetalittletricky,becauseweneedtohave
forloop
one nestedinsideanotherforloop.Illdomybesttoexplainit.

obj_game: Draw Event

/// Draw the grid


vargridw
=
ds_grid_width (
grid);
vargridh
=
ds_grid_height (
grid);
for
(
vari
=
0
;i
<gridw;i
++){
for
(
varj=
0
;j<
gridh;j
++){
if
(
ds_grid_get (
grid,
j
,
i
)==-
1
){
continue;
}
varsubi=
ds_grid_get (
grid
,
j
,
i
);
draw_sprite (
spr_char,subi
,
j
*
160
,
i
*
160
);
}
}

53

Thetwoforloopsinthiscodeareusedtocyclethroughthedifferentcellsinourgrid. j
representsthexvaluesand i
representstheyvalues.Ineachcell,wecheckitsvalue.
Ifthevalueisequalto1,wecontinue(jumpoutofthiscurrentloopandstartthenext
image_index
iteration).Ifitdoesntequal1,wewilldrawaspritewithan ofthevalue
foundinthegrid.Wewillneedtomultiplythexandypositionsinthegridby160to
convertthemfromgridvaluestoroomxandypositions.

Now,createanewroom,giveitaname,awidthof480,andaheightof480.Clickthe
backgroundstab,checkVisiblewhenroomstarts,choose bg_tiles asthe
background,andmakesureTileHor.andTileVert.arechecked.Weshouldseethe
classicTictactoeboardinourroom.Clicktheobjectstabandplace obj_game inthe
room.Now,runthegameandtestit.WeshouldbeabletoplaceOsbyleftclicking
andXsbyrightclicking.Now,grabafriendandenjoyagameofTictactoe!

DataStructureAccessors
Thesetofdifferentfunctionsusedtosetandgetvaluesfromdatastructuresisrather
largeand,inmyopinion,kindofapaintouse.Luckilyforus,thenewerversionsof
GameMakerhaveaneatlittletrickcalled accessors .Accessorsallowyoutomanipulate
thevaluesofadatastructureinamoreintuitiveway.Letslookatsomecode.

stats
=
ds_map_create
();
ds_map_add
(
stats
,"
health
",
100);
ds_map_add
(
stats
,"
mana
",
50
);

Thisbitofcodecreatesamapwithtwokeyvaluepairs:oneforthehealthandonefor
themana.Itsagiantmessofcodeforwhatlittleitactuallydoes.Letmeshowyouhow
todotheexactsamethingusingtheaccessorformapdatastructures.

stats
=ds_map_create
();
stats
[
?
"
health
"]
=
100;
stats
[
?
"
mana
"]
=
100;

Ofcourse,theothercoolthingaboutaccessorcodeisthatitcanbeusedtobothset
andgetvaluesfromthedatastructure.

54

Youmaybethinkingthatthecodeabovelookskindoflikeanarraywithafunny
questionmarkatthestartofit,right?Thatquestionmarktellsthecompilerwhattypeof
datastructureitisdealingwith.Eachdatastructurehasitsownsymboltobeusedin
theaccessor.Herearethedifferentsymbolsandtheircorrespondingdatastructure
types:

list
[
|
]
map
[
?
]
grid
[
#]

Accessorsareagreatwaytomakeyourcodeeasierbothtounderstandandtowrite.
AsfarasIknow,thereisnowaytochainaccessorsinGameMakerLanguagerightnow
(ifyouhadamapinsideanothermapandtriedtochaintheaccessorstogetavalue
twolevelsdown),buthopefullythisfunctionalitywillbeaddedinalaterversion.

55

Chapter5

ExpressionsandOperators
Expressions
Anexpressionisacombinationofoperatorsandoperands.Ifyouhaventlearneda
programminglanguagebefore,thatstatementmightsoundalientoyou.Illexplainitin
thischapter,andyoushouldfindthatitisprettyeasytounderstand.Letmeshowyou
someexamplesofstatements.Youveactuallyseenthembefore.

x
=
10;

Easy,right?Itoldyoutheywouldbefamiliar.Letmedissectthisexpressionforyouso
thatIcanexplainmyearliercomments.The x
andthe10aretheoperandsinthis
statement.Theyrepresentthedatabeing operated on.The = operator
isthe .Itis
operatingonthetwooperands.Thesingleequalssigniscalledtheassignment
operator.Thisisbecauseit assignstherightoperandtotheleftoperand.Youwillalso
noticetherearetwooperands.Thatmakestheassignmentoperatora binary

operator.
Inmostprogramminglanguages,thereare unary binary
, ,andsometimes ternary
operators.

EqualityvsAssignment
Thisisnotadifficultconcept,butitisoneofthemostfrequentmistakesnew
programmersmakeinGameMaker.(Ivemadeitbefore)Luckily,GameMaker
Languageisveryforgiving.Bestpracticeistoknowthedifferencebetweenthesetwo
operatorsandusetheminthecorrectplacesthisis veryimportantifyouwanttolearn
othermajorprogramminglanguagelater.Theeasywaytorememberthemistoknow
thattheequalityoperatoristhe question andtheassignmentoperatoristhe
statement .

EqualityOperator
Theequality operatoristhequestion,Isthisvariableequaltothisvalue?Thisoperator
isrepresentedbytwoequalssigns, ==,andiscommonlycombinedwithanifstatement
(formoreinformationonifstatements,seeChapter6).Hereisanexample:

56

if
(
health
==
0
){
instance_destroy
();
}

GameMakerissmartenoughtoknowthatifyou(mistakenly)useasingleequalssign
(theassignmentoperator),thecodewillstillworkhowever,thiswillgetyouintobig
troublelaterifyouevermoveontootherprogramminglanguages.Iwouldhighly
recommendgettinginthehabitofusingthisoperatorcorrectly.

AssignmentOperator
Theassignment operatoristhestatement,Thisvariablenowhasthisvalue.This
operatorisrepresentedbyasingleequalssignandiscommonlyusedwheninitializing
avariable.Hereisanexample:

health
=
100;

GameMakerwillNOTallowyoutouseadoubleequalssign(theequalityoperator)
here.Youwillgetanerrorifyoutrythis.

EqualitywithotherOperations
Theequalityoperatorcanbecombinedwithotheroperators.Theoperatorscommonly
combinedwithitarethegreaterthan,lessthan,andnotoperators.Belowaresome
examplesofhowthesecanbecombined.

Thefollowingexamplecheckstoseeiftheplayershealthislessthanorequaltozero.
Thiscanbeveryusefulwhentheplayertakesdamagethatwouldputhishealthinthe
negative.

if
(
health
<=
0
){
// Do something
}

Thisexamplecheckstoseeiftheplayershealthisgreaterthanorequalto100.This
canbeusefulformakingsureavariablehasnotpassedsomemaximumvalue.

57

if
(
health
>=
100
){
// Do something
}

Thisexamplecheckstomakesuretheplayershealthisnotequalto100.

if
(
health
!=
100
){
// Do something
}

AddOperator
add
The operatorhastwomainfunctionsinGameMaker.Thefirstisdoingthemathof
addingnumberstogether.Hereisasimpleexample:

/// Add two numbers using the add operator


varresult
=
5
+
3;

Canyouguessthevalueofresult?Prettyeasy,right?

Thesecondfunctionoftheaddoperatoristoconcatenate(concatenatejustmeansto
link)stringvaluestogether.Youveseenthisbeforeinapreviouschapter,butImgoing
tothrowdownaquickexamplehereaswell.

/// Create a full name using the add operator


varspace
="
";
varfull_name
="
Benjamin
"+
space
+"
Anderson
";

Canyouimaginetheresultofthisoperation?Theaddoperatoroneofthemost
commonoperatorsinGameMakerLanguage.Itissimpletounderstandbutvery
important.

58

SubtractOperator
subtract
Unliketheaddoperator,the operatorcannotbeusedwithstringsitwillonlybe
usedwithnumbers.Withnumbers,itdoesthesimplemaththatyouwouldexpect.

/// Use the subtract operator to subtract two numbers


varresult
=
5
-
3
;
// Returns 2

Thisoperatorisstraightforward.Justremembernottouseitwithstrings.

MultiplyOperator
multiply
The operatordoesjustwhatyouwouldexpect.Tomultiply,simplyusethe
*
asterisk,,symbol.Hereisanexample:

/// Use the multiply operator to get the product of two numbers
varresult
=
5
*
3
;
// Returns 15

Justaseasyasthesubtractoperator,butstillveryuseful.

DivideOperator
Thedivide
operator,justlikethemultiplyoperator,worksjustasyouwouldexpect.It
/
usesasingleslash, ,symbol.Hereisasimpleexample:

// Use the multiply operator to get the answer to 15/3


varresult
=
15
/
3
;
// Returns 5

Soon,Iwillexplainthedivisionoperator.Dontgetthesetwoconfused!Thedivision
operatorworksdifferentlyfromthedivideoperator!

AssignmentwithOperation
Theassignmentoperatorcanalsobecombinedwiththeotheroperatorsinthisway:

hp
+=
10;

59

hp
-=
10;
hp
*=
2;
hp
/=
2;

Eachofthepreviousstatementsareequivalenttothefollowingcorresponding
statements.

hp
=
hp
+
10;
hp
=
hp
-
10;
hp
=
hp
*
2;
hp
=
hp
/
2;

Thesimpleoperatorcombinationscanbeusedasshortcuts.Youwillseethemused
moreoftenthantheirwrittenoutcounterparts.

IncrementandDecrementOperator
Theincrementanddecrementoperatorsarealsoshortcuts.Theyareverycommonly
usedinforloops,buttheycanbeusedoutsideforloopsaswell.

level
++;
level
--;

Thesetwopreviousstatementsare(moreorless)equivalenttothesecorresponding
statements:

level
+=
1;
level
-=
1;

60

Youcanactuallychangetheorderoftheoperationlikethis:

++
level;
level
++
;

Thislastversiongetsalittlemorecomplicated,andIrarelyusethemlikethis.The
differenceisthatifyouputtheoperatorbefore,thenitwillreturntheincrementedvalue.
Ifyouputtheoperatorafter,itwillreturnthevalueandthenincrementit.

TheNotOperator
Thenot
operatorislikeaswitchinGameMakerLanguage.Itworksbasicallythesame
inmanyotherprogramminglanguages.Ifyouhaveavaluethatisequaltotrueanduse
youthenotoperator,thenthatvaluewillbecomefalse.Hereisanexample:

varfalling
=
false;
falling
=!
falling;

Inthisexample,thefallingvariablestartsoutasfalse.Thesecondlineusesthenot
operatortoflipitfromfalsetotrue.Youmightaskwhyyoucantjustsetittotrue,like
this:

falling
=
true;

Well,theansweristhatyoucan,butwhatifthevalueisalreadytrue?Ifthevalueis
alreadytrue,thenusingthenotoperatorwillswitchittofalse.Basically,theabovecode
allowsyoutotoggle
thevariablesvaluefromfalsetotrueorfromtruetofalse,andyou
donthavetoknowwhatitwasoriginally.Youjustknowthatitisnowtheoppositeof
whatitwasbefore.

Youcanalsousethenotoperatorinconditionstatements.Youhaveseenthisina
previoussectionthattalkedaboutcombiningtheequalityoperatorwithotheroperators.

DivisionOperator

61

division
The operatorisdifferentfromthedivideoperatorbecauseitreturnsawhole
number(thatis,withnoremainder).Thisisconvenientbecauseyoucanusethis
anytimethatyouneedtocalculatesnappingtoagrid.

End Step Event

/// Make an object follow the mouse while snapping to a 32x32 grid
x
=(
mouse_x div
32
)*
32;
y
=(
mouse_y div
32
)*
32;

Inthiscode,youareusingthedivisionoperatortogetthewholenumbervalueofthe
mousepositiondividedby32.Ifthemousepositionis64,thentheanswerwillbe2.If
themousepositionis67,thenanswerwillstillbe2.Afteryougetthevalue,youmultiply
youranswerby32.Asyoucansee,bothcases(valuesof64and67)willreturn64.

Thedivisionoperatorisalittletrickyatfirst,butonceyoufigureitout,itcanbevery
useful.

ModuloOperator
Themodulo operatoristhecomplementtothedivisionoperator:insteadofreturningthe
wholenumberanswer,themodulooperatorreturnstheremainderafterperformingthe
division.Onecommonuseforthisistodiscoverwhetheranumberisoddoreven.

/// Use the modulo operator to find if a number is odd or even


varnum
=
irandom
(
10
);
varis_even
=((
num mod
2
)==
0
);

Asyoucansee,youusethemodulooperatortodivide num by2.Ifthereturnedwhole


numberremainder(theresultoftheoperation)isequalto0,thenyouknowthatthe
numberiseven.Ifnot,thenumbermustbeodd.

Themodulooperatoriseventrickierthanthedivisionoperator,butitisalsowellworth
thefewminutesthatitwilltakeyoutolearnhowtouseit.

62

Chapter6

Statements
ControlStatements
Inprogramming,controlstatementsareusedtobranchyourcode,meaningtorun
differentsectionsofcodebasedonaspecificcondition.Themostbasicformisan
if
statement .

If
Theif
controlstatementisveryimportanttoprogramminginanylanguage.Itmakesit
possibletoexecutecodebasedonaspecifiedcondition.Hereisanexampleofwhen
youmightusean if
statementinGameMakerLanguage:

if
(
keyboard_check
(
vk_right
)){
x
+=
4
;
}

Thiscodewillchecktoseeifthekeyboardsrightarrowkeyisbeingpressedifitis,
thenthecodewilladd4tothecurrentxpositionoftheobjectthatisrunningthecode.
Adding4tothecurrentxpositionwillmovetheobjecttotherightonthescreen.

Nowthatyouhaveseenaningameexampleofwhereyoumightuseanifstatement,
letmeshowyouagenericexample.

ifcondition
( )
{
statement
;
}

Ifstatementsarefairlyeasytounderstand,evenfornewprogrammers.Thethingyou
needtocheckcarefullyiswhereyouwantyourcurlybracestoend.Beingconsistent
withyourformattingstylewillhelpyoutoreducebugsandmakeyourcodeeasierto
read.Herearetwocommonwaystoformatstatements,startingwiththeonethatIuse
mostoften.

63

FirstExample

ifcondition
( ){
statement
;
}

SecondExample

ifcondition
( )
{
statement
;
}

Itreallydoesntmatterwhichmethodyouchoosetouse(thecompilertreatstheseas
equivalent),orifyouchoosetouseonethatIdonthavelistedhere.What really
mattersisthatyouchooseonestyleandthenstickwithit.Youwillhave way
fewer
bugsifyouareconsistent.

Else
The elsecontrolstatementgoesrightalongwiththeifcontrolstatement.Theelseis
whathappensiftheconditioninsidetheifevaluatestofalse.

ifcondition
( ){
// True? Do this statement
}
else{
// False? Do this statement
}

Elseif
elseif
The controlstatementisalsocombinedwiththeifcontrolstatement.Youcan
chainelseifasmanytimesasisnecessarytobuildthelogicalstructurethatyouwantto
use.

ifcondition1
( ){

64

// Condition1 is true? Do this


// statement
}
else
ifcondition2
( ){
// Condition2 is true? Do this
// statement
}
else
ifcondition3
( ){
// Condition3 is true? Do this
// statement
}
else{
// None are true? Do this statement
}

Switch
switch
The controlstatementisanalternativetotheif/elseif/elsecombination.

switchexpression
( ){
case
value1:
// Does expression evaluate to
// value1? Do this
break;
case
value2:
// Does expression evaluate to
// value2? Do this
break;
default:
// Do this if none are true
break;
}

Thisoneisalittletrickytounderstandwithagenericexamplelikethis.Aningameuse
wouldbetterexplainit.

switch
(
direction
){
case
0:

65

// We are moving to the right


sprite_index
=
spr_player_right;
break;
case
90:
// We are moving up
sprite_index
=
spr_player_up;
break;
case
180:
// We are moving to the left
sprite_index
=
spr_player_left;
break;
case
270:
// We are moving down
sprite_index
=
spr_player_down;
break;
default:
// We are moving at an angle
// Do nothing
break;
}

Thisswitchstatementchecksifanobjectismovingright,left,up,ordown,andchanges
thespriteaccordingly.Thebreak statementattheendofeach case preventsthe
switch statementfromcheckingtheothercasesaswell.

With
WithisapowerfulcontrolstatementinsideGameMakerthatallowsyoutorunsome
codeinthecontextofanotherobject.Anexamplewillexplainthisbest.

// Code inside the enemy object


with
(
obj_player
){
hp
-=
10;
}

66

Eventhoughthiscodeisrunfrominsidetheenemyobject,itisactuallysubtracting
healthfromtheplayerobjectandnotfromitself.Youcanalsodothisusingthedot
operator.

// Code inside the enemy object


obj_player
.
hp
-=
10;

Thisisbasicallythesamethingbutthewithcontrolstatementisusefulwhenyouneed
toperformtonsofchangesontheotherobject,becauseyoudonthavetokeep
referencingtheobjectyouonlyreferenceitonce.Oneotherdifferenceisthatifyou
wanttocallafunctioninsidetheplayerobject,youmust usethewithstatementyou
cantusethedotoperator.

LoopStatements
Loop statements
inGameMakerLanguageallowyoutorunalineofcodemultipletimes
withslightdifferences.Thisisimportantformanykindsofgames.Therearefourmain
loopstatementsthatcanbeusedinGameMaker,andtherearedifferentreasonsfor
choosingeachone.

Repeat
Repeat istheshortestandthesimplestofalloftheloops.Itallowsyoutorunthesame
sectionofcodemultipletimesinasingleStep Event (whichhappensbasically
instantaneouslyduringruntime).

varrand_x
=random
(
room_width
);
varrand_y
=random
(
room_height
);

repeat
(
10
){
instance_create
(
rand_x
,
rand_y
,
obj_enemy
);
}

Becarefulwiththiscode.Thereisactuallyonlyonelineofcodeinsidetherepeatblock,
butithastowrapbecauseitissuchalongline.Thiscodewillcreatetenenemiesin
randomlocationsthroughouttheroom.Ofcourse,instead,youcouldtypethe
instance_create() functionouttentimes,butthatwouldbeapain.

67

While
Thewhile loopisalittlemorecomplicatedthantherepeatloop,butitsnottoobad
either.Thewhilelooptakesacondition,and,iftheconditionistrue,willrepeatallthe
codewithinitscurlybraces.Oncetheconditionisfalse,thewhileloopwillexit.

For
Theforloopisprobablythehardesttounderstandoutofallofthecontrolstatements,
butitispowerful,andyouneedtoknowhowitworks.

for
(
vari
=
0
;i
<
10
;i
++){
draw_line
(
32
*
i
,
0
,
32
*
i
,
room_height
);
}

Letmefirsttellyouwhatthiscodedoes,andthenIwillexplainhowitdoesit.Thiscode
drawsverticallinesstretchingtheentireheightoftheroom.Thefirstlineisdrawnatan
xpositionof32,andeachlineafterthataddsanother32tothexposition.

Thefirstpartoftheforloopyouneedtounderstandisthepartwrappedinparenthesis.

for
(
vari
=
0
;i
<
10
;i
++)

Thispartismadeupof3sections.Thefirstsectionistheinitializationsection,the
secondistheconditionsection,andthethirdistheiterationsection.

forinitialize
( condition
; iterate
; )

Theinitializesectionisonlyrunthefirsttime.Itisgenerallyusedtocreatethevariable
thatyouwillbeusingtoiterateandcheckagainstinthecondition.Theconditionsection
ischeckedeveryloop.Iftheconditionevaluatestotrue,thentheloopwillrunagain.If
theconditionevaluatestofalse,thentheloopwillnotrunagain.Theiterationsectionis
runeverytimetheconditionsectionevaluatestotrue,butafterthecodeitselfhasbeen
run.

68

Iknowthiswillbealittlebitconfusingifforloopsarenewtoyou.Letsstepthrougha
shortloop.

for
(
vari
=
0
;i
<
2
;i
++){
show_message
("
Hi
");
}

Thisforloopwillfirstcreatethevariable iandsetitto0.Afterthat,itwillchecktoseeif
iislessthan2becauseitislessthan2,the show_message
codewillberun.Then,the
iterationsectionwillberunand i++willadd1to i,makingitnowequalto1.The
conditionsectionwillmakesurethat
iisstilllessthan2.Because i isequalto1,the
loopwillevaluateastrueandthe show_message codewillberunagain.Now,the i++
codewillrunagain,setting i
to2.Theconditionsectionwillcheckif i
islessthan2
sinceitisnolongerlessthan2,the show_message
codewillNOTberunagain,andthe
programwillexittheloop.

Hopefully,thatsmallexamplehelped.Besuretowatchmyvideoaboutforloopsifyou
arestillstrugglingwiththem.

ExpressionStatements
Hereareafewcommonexpressionstatementsthatyouwillfinduseful.

Return
Return statementsaremostusefulinscripts.Forexample,youmightcreateascriptlike
this:

///scr_add(n1,n2);
returnargument
[
0
]+
argument
[
1
];

Youmightnotunderstandallofwhatisgoingonhere,butknowthatthisscripttakes
twoarguments(values)andthenreturnstheirsum.Aftercreatingthisscript,youcould
callitlikethis:

69

sum
=
scr_add
(
5
,
7
);
// sum now holds the value 12

Asyoucansee,thescriptreturnsthenumberthathasbeenaddedtogetherinsidethe
script.ManyGameMakerfunctionsreturnvalues,anditsveryimportanttounderstand
whatvaluetypesarereturnedfromthem.

Break
Break iscommonlyusedinswitchstatements,butitcanalsobeusedinotherloop
statementstobreakoutoftheloop.

for
(
vari
=
0
;i
<
100
;i
++)
{

// Some code
if
(
i
==
9
)
break;
}

Thatisasimpleforloop.Inanormalsituation,itwillrunthecodeinsidetheloop100
timeshowever,once
i break
reaches9,itwillcallthe statementandexittheloop,
runningonly10times.

Breakstatementselsewherewillalsoexitthelooponcetheyarereachedbythe
compiler.Thismeansthateverylineofcodebeforethebreakwillberuninsidethat
loop,andeverylineafteritwillnotberun.

Breakstatementswillexitoutofforloops,whileloops,repeatloops,andwith
statements.

Continue
Thecontinuestatementworkssimilarlytothebreakstatement,butinsteadofexitingout
oftheloopcompletely,itsimplyskipsoneiteration.Basically,theremaybetimeswhen
youwishtoskiponespecificstepintheloopbutcontinuetoexecutetherestofthe
stepsintheloop.

70

Exit
Exitworkslikethebreakstatement,butitappliestotheentiresectionofcode.Ifyou
placeanexitinascript,itwillexitthescriptcompletely.Ifyouputoneina
Code
Action,itwillexitthatentireaction(nottheevent,buttheaction).

71

Chapter7

ScriptsandAudio
CreatingScripts
Scriptsareamazing!Theyarealsoeasytouse!Ifyouhaveeverprogrammedina
differentlanguage,a script
issimilartoafunction,inthatitallowsyoutowriteablockof
codeonceandthenrunthatblockofcodemultipletimesbyexecutingthescriptitis
containedin.Oneoftheinterestingthingsaboutscriptsisthattheyareglobalinscope.
WhenIsayglobal,Imeanthatifyoucreateascript,anyobjectinyourgame couldcall
thatscript.Ifyouseelotsofrepeatedcodeinyourgame,thenitmightbeagoodideato
createascriptforthatcodeandthenjustcallthescriptwheneverneeded.

Creatingascriptisprettyeasy.Youcanusetheshortcutkeysshift+ctrl+ctoaddanew
scripttoyourgame,oryoucanclickthepageiconwiththegreenplaysymbolonit.
Afteraddinganewscript,youwillneedtogivethescriptitsownidentifier.Scripts,just
likevariables,haveanidentifier.Letscreateascriptcalled scr_move_right .Imgoing
toputthename(identifier)ofthescriptattheupperlefthandsideofthecodeblockso
thatyouknowitisascriptandyouknowwhatitiscalled.

scr_move_right

/// scr_move_right()
x
+=
4;

Asstatedbefore,the scr_move_right ontheupperlefthandsideofthecodeblockis


theactualidentifierforthescript.InGameMaker,youwillplacetheidentifierforthe
scriptinthislocation.Thetripleslashcommenthasaspecialmeaningatthetopofa
scriptfile,asIdescribedinChapter1.Thiscommentprovidestheautocomplete
functionalityinGameMakerandcanalsobeusedtotellyoutheorderofargumentsthat
needtobepassedtothescript.Thelastlineinthescriptsimplymovestheplayerfour
pixelstotheright.Thisbasicscriptisntsuperuseful,butletmeshowyouonethatIuse
quiteofteninmyprojects.

72

scr_get_depth

///scr_get_depth()
depth
=-
y
;

Thisisaneatlittlescriptthatwillgiveobjectsthatareclosertothebottomofthescreen
alowerdepth(meaningthattheywillbedrawnontopofobjectsthatareclosertothe
topofthescreen).Thisgivesthegameasenseofdepthand,whenusedcorrectly,can
createacoolvisualeffect.

ScriptArguments
Justlikefunctions,scriptscanbepassedarguments.Letsupgradethescriptwewrote
inthelastsectionfrom scr_move_right scr_move
to andpassitsomearguments.

scr_move

/// scr_move(xmov,ymov)
x
+=
argument
[
0
];
y
+=
argument
[
1
];

argument
Thereisanarrayinthelocalscopeofeachscriptcalled .Itcontainsthe
valuesoftheargumentsthatwerepassedtothescriptintheorderinwhichtheywere
passed.Thefirstvaluepassedinwillbeassignedtoargument[0] ,thesecondto
argument[1] ,andsoon.

Iusuallyassignthesevaluestolocalvariablesinordertohelpwiththereadabilityofthe
script.

scr_move

/// scr_move(xmov,ymov)
varxmov
=
argument
[
0
];
varymov
=
argument
[
1
];

x
+=
xmov;
y
+=
ymov;

73

Itmayseemalittleredundantinthisparticularscript(andmaybeitis),butinascript
wheretheargumentisbeingusedinmultipleplaces,itiseasiertounderstandanamed
argument[0]
variablethanthegeneric .

ScriptsReturnValues
Scriptscanalsoreturnavalue.Thismeansthatyoucanpassdatatothescript,do
somethingwiththatdatatogetaresult,andthenhavethatresultpopbackout.For
example,youmightcreateascriptlikethis.

scr_add

///scr_add(n1,n2);
returnargument
[
0
]+
argument
[
1
];

Youmightnotunderstandallofwhatisgoingonhere,butknowthatthisscripttakes
twoarguments(values)andthenreturnsthesum.Aftercreatingthisscript,youcould
callitlikethis:

obj_add_button: Mouse Pressed Event

sum
=
scr_add
(
5
,
7
);
// sum now holds the value 12

Asyoucansee,thisscriptwillactuallyreturntheresultofthetwoargumentsadded
togetherbythescript.

ExecutingScripts
Letmeshowyouhowyoucancallascriptinsideyourcode.Therearetwowaystodo
so.Theeasiestwayistowritetheidentifierforthescriptfollowedcloselybytwo
parenthesis.Thesetwoparenthesisarecalledthe scriptcalloperator
theywillcause
thescripttobeexecuted.

scr_get_depth
();

Letslookatthesecondwayyoucancallascript.Thiswayinvolvesanewfunction
insideGameMakerLanguagecalledthe execute_script function.Makesurethat
whenyoupassthescriptintothisfunctionyoudonotaddthebracketstotheend.

74

execute_script
(
scr_get_depth
);

Now,youmightbewonderingwhyonearthyouwouldeverusethiswhenittakes
longertotype.Thebenefitofthismethodisthatyoucanrunascriptthathasbeen
assignedtoavariable.

varmy_script
=scr_get_depth
;
execute_script
(
my_script
);

Thisisusefulwhenyouhaveaparentobjectthatrunsdifferentscriptsforeachofits
childobjects(seechapter8formoreinformationonthechild/parentrelationshipin
objects)dependingonwhatyouwantthemtodo.Makesurethatyoudontusethe
scriptcalloperatorwhenassigningthescripttoavariableorexecutingitinsidethe
execute_script function.

Also,notethatwhenyouassignascripttoavariable(asseeninthecodeabove)you
donotusethebracketsontheendofthescript.Thebracketsrepresentsthescriptcall
operatororfunctioncalloperatortherefore,theywouldcauseGameMakertoexecute
thescriptandattempttoassignitsreturnedvaluetothevariable,insteadofassigning
thescriptsidtothevariable.

Thischapteronscriptshasbeenshortandsweet,buthopefullyyoulearnedsomething
fromitthatyoucanapplyinyourfutureprojects.Iloveusingscriptstohelporganizemy
code,andIhopethatyoucanfindthemusefulaswell.

75

Chapter8

ObjectsandSprites
CreatingObjectInstances
SomethingthatIdidntpickuponrightawaywhenIfirststartedusingGameMakeris
thedifferencebetweeninstancesandobjects.

AnobjectinGameMakeriskindoflikeacookiecutter,whereastheinstanceissortof
likethecookie.Objectsareusedasthemoldsforcreatinginstances.

TherearetwowaystocreateaninstanceofanobjectinGameMaker.Thefirstwayisto
simplyplacetheinstanceinaroom.Thiscreatesanewinstanceoftheobjectthatyou
haveselected.

Theotherwaytocreateanewinstanceofanobjectisbyusingthe instance_create
function.Itisasimplefunctionthattakesonly3arguments.

instance_create
(
32
,
32
,
obj_player
);

Thislineofcodewillcreateaninstanceoftheplayerobjectatxposition32,yposition
32.

ObjectProperties
Everyinstancecreatedfromanobjectstartsoffwithsomebasicproperties.These
propertiesarebuiltinvariables,whichhavetheirowndefaultvalues,andidentifiers.
Therearequiteafewoftheseproperties,butletsstartoffwiththebasics.

BasicInstanceProperties

id

Aninstancesid
isrepresentedbyarealvalue,inotherwords,anumber.Youcanstore
thisnumberinavariablethatcanlaterbeusedtorefertotheinstance.Youwillfindan
instancesidcanbeyourbestfriend.

76

solid

EventhoughGameMakerhasabuiltin solidbooleanproperty,Iwouldntrecommend
usingit.ThisvariableaffectsthewayGameMakerhandlesthe Collision Event ,and
often,theeffectisnotagreatone.ThebestwayIvefoundtohandlecollisionsisusing
aparentobject.Illshowyoumoreaboutthislater.

visible

Thevisible instancepropertyisasimplebooleanpropertythatdetermineswhether
theinstanceisdrawnonthescreenornot.

persistent

Thepersistent propertyisanotherbooleanpropertythatdetermineswhetherthe
instanceiscarriedoverbetweenrooms.Ifpersistentissettotrue,thentheinstancewill
notbedestroyedwhenchangingroomsitwillalsoretainthevaluesofallofitsother
properties.

depth

The
depth

propertyisnecessary.Thispropertycanbeanyrealintegervalue.Itcontrols
whichobjectsaredrawninfrontofotherobjects.Lowervaluesarebroughttothefront
andhighervaluesarepushedtotheback.Ifyouhaveatreeobjectthatyouwanttobe
inthebackground(behindtheplayer),youmightgiveitadepthvalueof5andyour
playeradepthvalueof10.Thiswoulddrawtheplayerobjectinfrontofthetreeobject
because10islower
than5.

alarm

77

alarm
The
propertyisanarraythatisusedinconjunctionwithalarmevents.The
alarm[0]
property wouldreturnthevalueoftheAlarm0event.

object_index


Theobject_index propertyreturnsarealvaluerepresentingtheidofthatobject(not
theinstance).Thispropertycanbeusedtocompareinstancestoseeiftheywere
createdfromthesameobject.

x
Thepropertyrepresentsthecurrentxpositionoftheinstance.

y
Thepropertyrepresentsthecurrentypositionoftheinstance.

xstart

xstart
The
propertyisthexpositionoftheinstanceatthemomentwhenitwas
created.

ystart

ystart
The
propertyistheypositionoftheinstanceatthemomentwhenitwas
created.

xprevious

78

xprevious
The
propertyisthexpositionoftheinstanceinthepreviousstep.

yprevious

yprevious
The
propertyistheypositionoftheinstanceinthepreviousstep.

direction

direction
The propertyisthedirectioninwhichtheinstancewillmoveifthe
speed
propertyisset.Itusesdegrees,anditdefaultsto0.

speed

speed
The
propertyisthespeedatwhichtheinstancewillmove.Ifthe
direction
propertyisnotset,thentheinstancewillmovetotheright.

friction

Thefriction speed
propertyisavaluethatgetssubtractedfromthe
propertyatevery
step.Ifyouhaveaspeedof10andafrictionof1,thenitwilltake10stepsforthespeed
tofalltozero.

hspeed

The hspeed

propertyrepresentsthehorizontalspeedoftheinstance.Apositivenumber
right
willmovetheinstancetothe ,andanegativenumberwillmovetheinstanceto
theleft
.

vspeed

79

Thevspeed
propertyrepresentstheverticalspeedoftheinstance.Apositivenumber
dow
willmovetheinstance n
,

andanegativenumberwillmovetheinstance up
.

SpriteInstanceProperties
Thespriteinstancepropertiesareasetofpropertiesthatareusedtocontrolthesprite
associatedwithaparticularinstance.

sprite_index

Thesprite_index
propertycanbeusedtogetorsetthespriteassociatedwithan
instance.Itisgenerallyusedtochangethespriteofaninstance.

image_index

Theimage_index
representstheanimationsubimageindex.Ifyouhaveananimation
withfivesubimages,andyouwanttosetittothefirstone,youwouldset
image_index
to0.

image_speed

image_speed
The
propertycontrolsthespeedatwhichthespriteanimates,ormore
specifically,thespeedatwhichitcyclesthroughitssubimages.

image_count

The image_count
propertyreturnsthenumberofsubimagesinthespriteassociated
withtheinstance.Notethatthisreturnsthenumber,notthevalue.Thelastsubimage
image_count
willactuallybe1lessthan becausethesubimageindexstartsat0.

80

ObjectInheritance
ObjectinheritanceisoneofmyfavoritefeaturesofGameMaker.Asolidunderstanding
ofthissectionisveryhelpfulifyoueverwanttobuildalargescaleprojectlikea
roleplayinggamebecauseitallowsyoutoreusecodeinobjectsthatwillhavesimilar
behaviors.Thefirstthingthatyouneedtoknowishowtosetupobjectinheritanceand
whatthecommontermsare.

SettingupInheritance
Youcanmakeoneobjectinheriteventsandcertainpropertiesfromanotherwith
GameMakersinheritancesystem.GameMakerusesthetermsparentandchildto
describetheinheritancerelationshipbetweenobjects.The child
objectwillinheritevents
andcertainpropertiesfromits parentobject.Illdescribethespecificsofhowthatworks
alittlelater,butfornow,letslookathowyoucansetupthisrelationship.

Whenyouopenuponeofyourobjectsfromtheresourcetree,youwillseeaproperty
nexttothemaskpropertylabelledparent.Clickonthatpropertyandselecttheobject
thatyouwanttobecomethisobjectsparent.Youcannotselectthesameobject,asthis
wouldcreatealoop.

Oncetheparent/childrelationshiphasbeenset,thechildobjectwillinheriteventsfrom
theparentobject.Therearesomeimportantrulesthatyoushouldbemadeawareof
regardinghowtheinheritanceactuallyworks.

1. Childobjects will
inheriteventsfromgrandparents,greatgrandparents,andso
on(seesecondruleforexceptions).
2. Childobjects willnotinheriteventsthattheypossessthemselves.
3. Itispossibleforchildeventstohavetheirowneventandstillruntheparents
eventusingthe events_inherited() function.

Letstalkaboutrulethreealittle,becauseitisespeciallyimportant.Therewillbecases
whenyouwanttocreateabaseparentobjectandhavechildrenexecutethebase
eventsoftheparent,butalsocontaintheirownuniqueadditionstotheevent.

Generally,whenIstartcreatinganewgame,Itrytothinkaboutthemainobjectsinmy
gameandwhatactionstheywillbeperforming.AfterIfigurethesetwothingsout,Istart
separatingthoseactionsintotwocategories, actionsthatwillbethesame between
objectsand actionsthatwillbedifferent .Letmegiveyouaquickexample.

81

Youarebuildingaplatformgame.Youhavetwomaintypesofobjects.Youhaveyour
playerobject,andyouhaveyourenemyobjects.Youwanttheplayerobjectandthe
enemyobjectstobeabletointeractwiththeworldinthenormalplatformertypeway
(falling,running,jumping,andsuch).Youwantyourplayerobjecttobeabletoshoot
arrowsattheenemies,andyouwanttheenemiestoshootfireballsattheplayer.

Steponeistoseparatetheactionsthatwillbethesameandtheactionsthatwillbe
different(therearemanygoodwaystosetthisup,Imonlygoingtoshowyouoneof
them).

Actionsthatwillbethesame
1. Gravity
2. Jumping
3. Collisions

Actionsthatwillbedifferent
1. Input(usercontrolsplayer,AIcontrolsenemy)
2. Attacking(enemyshootsfire,playershootsarrows)

Onceyouhavetheactionsseparated,itistimetocreateyourobjects.Youwillcreatea
parent actionsthatarethesame
objectthatcontrolsthe andthentwochildobjects
thatwilladdtotheparentsactionsandcontroltheactionsthataredifferent .

Ifyouarenewtoprogramming,youmightbeaskingyourselfwhyyouwoulddothis.
Theansweristhatitwillmakeyourlifeeasierlater.Letssaythatinsteadofusinga
parentobject,youjustcodedtheactionsthatwillbethesame
twice,onceforeachchild
object.Thecodewouldworkexactlythesame,right?Also,youwouldnthavetoworry
aboutparentobjectlogic.Youwouldberighttomaketheseassumptions.Butwhatif,
insteadofhavingtwochildren,youhave20?Then,youwouldhavetowritethesame
code20times.Whatifyoustilldecidedtojustpushthroughandwritethecode20
times,butthenlater,youwantedtochangeit?Youwouldhavetochangeyourcodein
20differentplaces.Thiscouldcauseerrorsanddiscrepancies.Ifyouuseaparent
system,thenyouneedtowritethecodeonlyonceandthen,ifneedbe,changeitin
onlyoneplace.

GettingusedtotheinheritancesysteminGameMakercanbetrickysometimes,butthe
effortiswellworththebenefits.Onceyougetitdown,itwillbecomeoneofyourfavorite
featuresofGameMakerLanguage.

82

IdentifyingInstances
Wevealreadytalkedaboutthedifferencesbetweenobjectsandinstances.Thereare
timesinyourprogrammingwhereyouwillwanttoworkwithaspecificinstance.One
waytodothisistocreateareferencetotheinstanceatthetimeitscreated.Acommon
placeforthisiswhenanenemyfiresabulletattheplayer.

instance_create
The functionreturnstheidoftheinstancecreated.Becauseofthis,
weareabletocreateareferencetotheinstancelikethis:

// Creates a bullet instance


varbullet
=
instance_create
(
x
,
y
,
obj_bullet
);

Afterwehavecreatedtheinstanceandwehaveareferencetoit,wecansetits
propertiesandhaveitcallfunctions.

// Set the direction of the bullet instance


bullet
.
direction
=
dir;

// Call a script inside the bullet instance.


with
(
bullet
){
scr_move
();
}

Thisisallmadepossiblebecausewehaveavariableasareferencetotheinstance.

Iftheinstancehasalreadybeencreated,itisstillpossibletogetareferencetoitusing
somedifferentfunctions.Letslookatafewofthesedifferentmethods.

TheNearestObjectInstance
ThereisausefullittlefunctioninGameMakerthatcanbeusedtogettheidofthe
nearestinstanceofanobject.Thisfunctioniscalledinstance_nearest .Hereisan
exampleofhowitworks:

83

obj_player:DrawEvent

// Get a reference to the id of the nearest enemy object


varnear_enemy
=
instance_nearest
(
x
,
y
,
obj_enemy
);
draw_sprite
(
spr_target
,
0
,
near_enemy
.
x
,
near_enemy
.
y
);

The instance_nearest functionreturnstheidoftheinstancethatisnearesttothex


andypositionpassedintothefunction.Youcanusethistostoretheidinavariableand
thenaccessitlater.Asyoucanseeinthecodeabove,weusethevariablethat
containstheidofthenearestenemyobjecttodrawatargetspriteonit.Thisisoneof
themanyusesthatthisfunctionhas.

TheFurthestObjectInstance
ThereisalsoafunctioninGameMakerthatisquitetheoppositeof instance_nearest .
instance_furthest
itiscalled .Ittakesthesamearguments,butitwillreturntheidof
theinstancethatisfarthestfromthepointgiven,insteadofthenearest.

obj_player:DrawEvent

// Get a reference to the id of the farthest enemy object


varfar_enemy
=
instance_furthest
(
x
,
y
,
obj_enemy
);
draw_sprite
(
spr_target
,
0
,
far_enemy
.
x
,
far_enemy
.
y
);

spr_target
Thislineofcodewilldraw atthepositionoftheenemyobjectthatisthe
farthestfromtheplayersxandyposition.

InstanceataPosition
Thisisoneofthemostusefulfunctionsforgettingareferencetoaninstance.Youonly
needtoknowwherethatinstanceis,andyoucanusethatinformationtogettheidof
theinstance.Hereisanexample:

obj_player:StepEvent

// Check to see if the left mouse button is being pressed


if
(
mouse_button_check
(
mb_left
)){

// Find the enemy object at the mouse's position

84

vari
=
instance_position
(
mouse_x
,
mouse_y
,
obj_enemy
);

// Destroy the found instance


with
(
i
){
instance_destroy
();
}
}

Thisbitofcodecheckstoseeiftheuserispressingthemousebutton.Iftheyare,then
itcheckstoseeifthereisaninstanceoftheenemyobjectatthatlocation.Ifthereis,
thenitstorestheidofthatinstanceinthelocalvariable i.Afterthat,itdestroysthe
instancefound.

InstanceataPlace
ThereisonelastfunctionthatIthinkisworthmentioninghere.Iusethisonelessoften,
instance_place
butitisalsouseful.Itiscalledthe function.Thisfunctionworksalmost
instance_position
exactlylikethe function.Ittakesthesameargumentsandwillalso
returntheidoftheinstancefound.Theonemajordifferenceisthatthisfunction uses
themaskoftheobjectcallingandchecksforacollisionatthepositionpassed ,
whereas instance_position onlycheckstheonepixelpointthatispassed.Thiscan
causedrasticdifferencesinresults.Makesurethatyouunderstandthisdistinctionwhen
choosingbetweenthesetwofunctions.Igenerallyuse instance_position moreoften.

obj_player:StepEvent

// Check to see if the left mouse button is being pressed


if
(
mouse_button_check
(
mb_left
)){

// Find the enemy object that would collide with the


// player object if
it were at the mouse's position
vari
=
instance_place
(
mouse_x
,
mouse_y
,
obj_enemy
);

// Destroy the found instance


with
(
i
){
instance_destroy
();
}

85

Hereisthesamecodeasabove,butusing instance_place instead.Iveaddedextra


commentstohelpyoutounderstandthedifferencebetweenthetwo.

NoInstanceFound
Nowisagoodtimetomentionwhatthesefunctionswillreturniftheycantfindan
instanceoftheobjectyouarelookingfor.ThereisaspecialconstantinGameMaker
noone
called .Thisconstantrepresentsnoobject.Ithasanactualvalueof4.Thecode
belowshowshowyoumightusethisconstant.Iliketouseitbecauseitmakesthecode
easiertoreadandunderstand.

vari
=
instance_position
(
mouse_x
,
mouse_y
,
obj_enemy
);

// Make sure we actually found an instance before we


// attempt to destroy it.
if
(
i
!=
noone){
with
(i
){
instance_destroy
();
}
}

Onceyougetthehangofusingallofthesegreatpropertiesandfunctionswithobjects
andinstances,youwillseehowsweettheyare.Thereisalottolearnhere,butitisfun
whenyoustarttousethesetoaddfeaturestoyourgame.IntheEventschapter,Iwill
talkaboutonemorewaythatyoucangetaccesstoaspecificinstanceintheroom,but
youhavelearnedquiteabitalready.

86

Chapter9

Events

Events
InGameMakerStudio,eachobjecthasalistofeventsthatcanbefired(triggered)by
instancesofthatobjectduringthegame.

CreateEvent
The Create Event isonlyrunwhenaninstanceisfirstcreatedandthenisneverused
againduringthelifeofthatinstance.Itisanimportanteventforsettingupdefault
propertiesandvariables.

StepEvents
The Step Event isrunonceateveryframeofthegame.Ifyourgameissettorunat30
fps(framespersecond),thenyour Step Event willbeexecutedthirtytimesper
second.Thiseventismainlyusedforcontrollingthestatesofyourobjects.

BeginStepEvent
The Begin Step Event isalmostexactlyliketheStep Event theonlydifferenceis
Begin Step Event
thatthe runsfirst.Ifyouneedtorunaspecificbitofcodeevery
gameframebutwanttorunitbeforeyour Step Event ,thenthisistheeventforyou.

EndStepEvent
The End Step Event isalsoalmostexactlylikethe Step Event thedifferencehereis
End Step Event
thatthe runsafterthe Step Event .Thiscanbeespeciallyuseful
whenhavinganobjectfollowthemouseoranotherobject.Youwillnoticethatifyou
makeanobjectfollowthemouselikethisinthe Step Event ,thereseemstobesome
lag.

Step Event

x
=
mouse_x;
y
=
mouse_y;

End Step Event


Ifyouplacetheabovecodeinthe ,therewillbenolag.Thisisdueto
End Step Event
thetimingofthe Draw Event
comingafterthe .
87

AlarmEvents
TheAlarm Event worksjustlikeanyalarm:yousetalengthoftimeforthealarmto
waitandattheendofthattime,somethinghappens.Alarmsarealmostnecessaryin
everygame.Understandingofhowalarmsworkshouldbeobtainedasquicklyas
possible.Iamgoingtogiveyouseveralexamplesofhowalarmsworkandhowyoucan
usetheminagame.Letsstartwiththemostbasicexample.

Create Event

/// Set an alarm


alarm
[
0
]=
120;

Here,wehavea Create Event ,andwearesettingthealarmtoavalueof120.Alarms


automaticallysubtractfromtheirvalueateverystep.Ifyourgameisrunningat30fps
(framespersecond),thenthe Alarm Event willfireafter4seconds(120/30=4).You
willnoticethatalarmsarepartofanalarmarray.Thisgivesyoutheoptionofusing
multiplealarms. alarm[0] isthefirstalarm.Currently,nothingwillhappenwhenthis
alarmreachestheendofits120stepsthisiswherethe Alarm Event comesin.Inside
theAlarm Event ,wecandefinetheactionsthatwewanttohappenwhenthealarm
reacheszero.

Alarm 0 Event

/// Execute some action at the end of the alarm


show_message
("
Wakeup
,sleepy head
.");

Alarm Event
Inthe ,wecandragoveraCode Action andexecuteanykindofcode
thatwewant.Asasimplejoke,thiscodeonlyshowsapopupmessagetellingtheuser
towakeup.Thisalarmwillonlygooffonce.Ifyouwantthealarmtocycleandshowthis
popupmessageevery120steps,thenweneedtosetthealarmagaininsidethe Alarm
Event likethis.

Alarm 0 Event

/// Execute some action at the end of the alarm


show_message
("
Wakeup
,sleepy head
.");

88

alarm
[
0
]=
120;

Asyoucansee,theonlydifferencebetweenthis Alarm Event andtheotheroneisthat


thisonewillresetitsowntimer.Thiswillcausethealarmtorunmultipletimesata
120stepinterval.

CollisionEvent
Thereareseveraldifferentwaystosimulateacollisioningames.Thesimplestwayisto
checkthedistancefromtheobjectthatyouaretryingtocollidewith.Oneofthenice
thingsaboutGameMakeristhatithasabuiltineventforcollisionsthathandlesallof
thetrickycollisioncodebehindthescenes.BeforeIgiveyouanexample,youshould
knowthatifyoucheckthePrecisecollisioncheckingcheckboxinyoursprite,the
collisioncheckingforanyobjectusingthatspritewillbequiteabitslowerthanifyou
weretoleavethatcheckboxunchecked.ThereasonforthisslowingisthatPrecise
collisioncheckinghastospendextratimetocheckeachpixelofthespriteandseeifit
iscollidingwiththeotherobject.Youmaythinkthatthisiswhatyouwantforyourgame,
but99%ofthetime,youwillactuallywanttoleavePrecisecollisioncheckingturned
off,asitcancauseglitchesinyourgamewhenyourimagecyclesthroughitsdifferent
subimages.Forthemajorityofthegamesyoubuild,usingasquarecollisionboxis
actuallygoingtoworkbetter.

Oneotherthingthatyoushouldknowisthatthe Collision Event hasabuiltin


referencetotheobjectbeingcollidedwith.Thisreferencehasanidentifierof other .
Let'slookataquickexamplethatillustrateswhatImean.

Collision Event

/// Destroy both objects in the collision


instance_destroy
();
With
(
other
){
instance_destroy
();
}

Thiscodeexamplewilldestroybothobjectsinthecollision.First,wedestroytheobject
Collision Event
callingthe ,andthen,wedestroytheotherobjectinthecollision
usingtheother
reference.

89

Well,nowyouknowbasicallyallthatyouneedtoknowinordertousecollisionevents
inyourgame.

KeyboardEvents
AKeyboard Event
isfiredwhenaspecifickeyispressedandwillcontinuetofireeach
stepthattheuserholdsthatkeydown.Therearequiteafewdifferentkeysthatyoucan
choosefrom.Hereisanexampleofhowyoumightusekeyboardeventstocreate
movementinagame.

Keyboard Up Event

/// Move the object up


y
-=
4;

Keyboard Right Event

/// Move the object to the right


x
+=
4;

Keyboard Down Event

/// Move the object down


y
+=
4;

Keyboard Left Event

/// Move the object to the left


x
-=
4;

Step Event
Youcouldalsowritesimilarcodeinthe keyboard_check
using .Which
methodyouchoosetouseismoreamatterofstylethananythingelse.Mostly,Iusethe
Step Event keyboard_check
withthe function.

90

KeyPressEvent
The Key Press Event Key Event
workssimilarlytothe ,exceptthatitonlyfiresinthe
singlestepinwhichthekeyboardkeyispresseddown.

KeyReleaseEvent
The Key Release Event Key Event
alsoworkssimilarlytothe ,butitwillonlyfirein
thesinglestepinwhichthekeyboardkeyisreleased.

MouseEvents
AMouse Event
isfiredwhenthecursorishoveringoverthecollisionboxfortheobject
callingtheeventandaspecificmousebuttonispressed,or,inotherwords,whenthe
userclicksontheobjectcontainingthe Mouse Event .TheMouse Event willcontinueto
fireeverystepthattheuserholdsdownthemousebuttonthatwaspressed.

Create Event

/// Set the size of the object


size
=
1;

Mouse Left Event

/// Increase the size of the object


image_xscale
=
size;
image_yscale
=
size;
size
+=
0.25;

Inthisexample,wehavebotha Create Event Mouse Left Event


anda .First,weset
thesizeoftheobjectto1intheCreate Event .Then,weusethe
Mouse Left Event
toincreasethesizeoftheobject.Thesizewillonlyincreaseifthecursorishovering
overtheobjectscollisionboxandtheleftmousebuttonisbeingpressed.

MousePressedEvents
TheMouse Pressed Event Mouse Event
issimilartothe ,

exceptthatitwillonlyfire
duringthestepinwhichthemousebuttonispresseddown.Hereisasimpleexample:

91

Mouse Left Pressed Event

/// Destroy the instance


instance_destroy
();

Thisbitofcodedestroystheinstancethatisclickedon.

MouseReleasedEvents
The Mouse Released Event Mouse Event
isalsosimilartothe ,

exceptthatitwillonly
fireduringthestepinwhichthemousebuttonisreleased.Hereisanexamplesimilarto
theoneinthelastsection.Youwillnoticethatinthisexample,youcanclickonthe
instance,butitwontbedestroyeduntilyoureleasethemousebutton.

Mouse Left Released Event

/// Destroy the instance


instance_destroy
();

Onceagain,inthisbitofcode,wedestroytheinstanceclickedon,butonlywhenthe
mousebuttonisreleased.

GlobalMouseEvents
Global Mouse Event
A isfiredwhenamousebuttonispressed,regardlessofwhere
thecursorislocatedinthegame.Theeventalsocontinuestofireineachstepaslong
astheuserholdsdownthemousebutton.Hereisaslightlylargerexamplethatshows
howtousethe Alarm Event Global Mouse Left Pressed Event
withthe foraneat
bulletfiringsysteminagame.

AlarmsandGlobalLeftPressedforFiringBullets
Hereisacombinationofthe Global Left Pressed Event Alarm Event
andan to
createabulletfiringsystem.Acommonuseforalarmsistohelpwiththetimingoffiring
bulletsinagame.Inmostgames,wewanttheplayertobeabletoholdthefirebutton
downwithfullyautomaticweapons,butwedonotwantthegametofireabulletatevery
step,asthiswouldbewaytoomanybullets.Wecanlimitthefirerateofourgunsusing
alarms.

92

Create Event

/// Initialize the alarm


alarm
[
0
]=-
1;
fire_delay
=
8;

Alarm 0 Event

/// We only have this here so that the compiler doesn't remove the
// alarm event

Global Left Pressed Event

/// Fire the gun


if
(
alarm
[
0
]==-
1
){
instance_create
(
x
,
y
,
obj_bullet
);
alarm
[
0
]=
fire_delay;
}

Imthrowingallthecodedownatonce,andIllexplainitallherebecauseitisntvery
muchcodetoexplain.First,weadda Create Event wherewesetthealarmto1(its
endingvalue),createthe fire_delay variableandsetitto8.Next,wecreatethe
Alarm 0 Event andaddacommentinittomakesurethatthecompilerdoesntremove
theevent.Finally,weadda Global Left Pressed Event wherewecheckthevalue
ofthealarm,andthen,createthebulletobjectifthevalueisequalto1.Afterthat,we
makesuretoresetthealarmtothe fire_delay valuesothatthebulletwillwaittobe
createduntilthealarmreachesavalueof1again.

GlobalMousePressedEvents
TheGlobal Mouse Pressed Event Global Mouse Event
workssimilarlytothe ,
exceptthatitonlyfiresinthesinglestepinwhichthemousebuttonispresseddown.

GlobalMouseReleasedEvents
TheGlobal Mouse Released Event Global Mouse Event
alsoworkssimilarlytothe ,
exceptthatitonlyfiresinthesinglestepinwhichthemousebuttonisreleased.

93

OtherEvents
Thereareseveraldifferenteventsthathavebeenplacedinthe Other Event
submenu.
Iwillcoverthemhere.Mostwillbeeasyforyoutounderstandataglance,andso,will
haverathershortexplanations.

OutsideRoomEvent
The Outside Room Event firesonceanobjectleavestheboundariesoftheroom.It
continuestofireeverystepaslongastheobjectstaysoutsidetheroom.

IntersectBoundaryEvent
The Intersect Boundary Event fireswhenanobjectintersectstheboundaryofthe
room.Thismeansthatiftheobjectleavestheboundariesoftheroom,theeventwillfire
andiftheobjectreturnstotheboundariesoftheroom,theeventwillfire.Inotherwords,
thiseventfiresoncewhenitleavesandoncewhenitreturns.

OutsideViewEvent
The Outside View Event fireswhenanobjectleavestheboundariesoftheviewand
continuestofireeachstepinwhichtheobjectremainsoutsidetheboundariesofthe
view.

BoundaryViewEvent
The Boundary View Event fireswhenanobjectleavestheboundariesofaviewand
whentheobjectreturnstowithintheboundariesofaview.Itworkslikethe Intersect
Boundary Event butwithviewsinsteadoftheroom.

GameStartEvent
The Game Start Event onlyfireswhenthegameislaunchedandatnoothertime
duringthegame.

GameEndEvent
The Game End Event onlyfireswhenthegameendsandatnoothertimeduringthe
game.

RoomStartEvent
The Room Start Event willfireatthestartofeachroom.

RoomEndEvent
The Room End Event willfireattheendofeachroom.

94

NoMoreLivesEvent
TheNo More Lives Event willfireifyouareusingGameMakersbuiltin,globallives
variableandthevariablereaches0.

NoMoreHealthEvent
TheNo More Health Event willfireifyouareusingGameMakersbuiltin,global
healthvariableandthevariablereaches0.

AnimationEndEvent
TheAnimation End Event fireswhenanobjectsspriteanimationloopreachesthe
lastsubimage.

EndofPathEvent
TheEnd of Path Event firesifanobjectismovingalongapathanditreachesthe
endofthatpath.

UserDefinedEvent
Userdefinedeventsfirewhentheuserfiresthem.Theycanbefiredanywhereinyour
codeusingtheevent_user function.Hereisanexampleofhowyoucandothis.

User Defined Event 0

/// Show a message


show_message(This is my own event);

Create Event

/// Execute the user-defined event


varevent_number
=
0;
event_user
(
event_number
);

User Defined Event


Westartbycreatingthe .Forthesakeofthisexample,Imjust
showingamessage.After,wecalltheeventusingevent_user .Wechosetouseevent
event_user
number0,andso,wepassthatintothe functionsothatGameMaker
knowswhichusereventtofire.Thereare16usereventsthatcanbeused.

95

DrawEvent
TheDraw Event Step Event
fireseachstepofthegame,justlikethe .Itisagoodidea
toputaslittlecodeinthe Draw Event aspossible.Youshouldonlyputcodeinthe
Draw Event thatactuallydrawssomething.Anotherthingtonoteisthatthe Draw
Event
willfireforeveryinstanceinyourroom,unlessthatinstancehastheVisible
objectpropertyunchecked.Ifyouaddyourown Draw Event
toanobject,beawarethat
youwilloverridethedefault Draw Event ifyoudontplacecodeinyourDraw Event
thatactuallydrawstheobjectssprite,thenyourinstancewillnotshowup.Theeasiest
waytotellGameMakertodrawanobjectistousethe draw_self functionlikethis:

Draw Event

/// We added our own draw event that will override the default
// draw event so we have to make sure to draw the object
// ourselves.
draw_self
();

Theorderinwhichyoudrawthedifferentelementsinyour Draw Eventwillaffecthow


theylayerinyourgame.Itemsthatyoudrawfirstwillshowupbehindanyitemsdrawn
afterwards.

Draw Event

/// Draw a textbox


draw_rectangle
(
x
-
48
,
y
-
24
,
x
+
48
,
y
+
24
,
false
);

// Draw some text. This will be drawn on top of the rectangle


varcol
=
c_white;
draw_set_halign
(
fa_center
);
draw_set_valign
(
fa_middle
);
draw_text_colour
(
x
,
y
,"
Hello
GameMaker
",
col
,
col
,
col
,
col
,
1
);

Westartbydrawingarectangle.Becausethisrectangleisdrawnfirst,itwillbebelow
anyitemsthatwedrawafterwards.The draw_rectangle functiontakestwoxandy
coordinatepairs.Thefirstpairistheupperleftcornerandthesecondpairisthe
lowerrightcorner.Thelastargumentasksiftherectangleshouldbejustanoutlineorif
itshouldbefilled.Thisrectanglewillbedrawninblackbecausethatisthedefaultcolor.

96

Afterdrawingtherectangle,wecreateatemporaryvariablecalled col thatholdsthe


colorforthetextthatwewilldraw.Wesetthehorizontalalignmenttoacenteralignment
usingdraw_set_halign .Then,wesettheverticalalignmenttoamiddlealignment
usingdraw_set_valign .Onceoursetupisdone,wedrawthetextusingthe
draw_text_color function.Thefirsttwoargumentsarethepositionofthetext,the
thirdisthetexttodraw,thenextfourarethecolorstouse(oneforeachcornerofthe
text,asthisfunctioncanbeusedtocreateagradientinthetext),andthefinalisthe
alphavalueofthetext.

DrawGUIEvent
TheDraw GUI Event existsonaseparateplanefromthe Draw Event ,

butworks
similarlytoit.Everythingdrawninthe Draw GUI Event
willbedrawnontopofthe
itemsdrawninthe Draw Event .TheDraw GUI Event mayalsohaveadifferent
resolutionthanthe Draw Event
dependingonhowyousetupyourviews.Iwontgive
anyexampleshereonthe Draw Gui Event
becauseChapter11hasamoredetailed
explanationofthe Draw GUI Event
andsomeexamples.

97

Chapter10

GameAudio
Soundsareeasiesttoexplainbygoingthroughasimpleexample.Inthisexample,we
willcreateabuttonthattheusercanclickon.Whenthebuttonispressed,itwillplaya
sound.Thisexamplewillalsobedividedupintotwoparts.Inthefirstpartofthe
example,thesoundwonthaveanyeffectshowever,inthesecondpart,wewillusea
soundemittertoalterthepitch(howhighorlowthesoundisonthemusicalscale)and
thegain(theloudnessofthesound).

Thefirstthingthatweneedtodoforthisexampleistoaddanewsprite.Ivenamedmy
spr_button
sprite .Itis32x32pixelsinsize,itsoriginiscentered,anditisasimple
blackbox.

spr_button

Hereisanimageofmysprite:

Oncethespritehasbeencreated,weneedtocreateitsaccompanyingobject.Ive
namedmyobject obj_button .Fornow,wewontaddanyeventstothisobject.Lets
firstcreatethesoundwewillbeusing.

98

obj_button

Addanewsoundtothegame.Ifyoulike,youcanuse bfxr.net
tocreateasound.Ive
namedmysound snd_click .

snd_click

IalsomadethesoundaStereosoundusingthedropdownoptionintheTarget
Optionssectionofthesoundproperties.Thisgivesthesoundtwoaudiochannels.You
couldusethistoplayasoundonlyintherightspeakerifthesoundiscomingfromthe
rightinthegameortoplayitonlyintheleftspeakerifthesoundiscomingfromtheleft.
Hereisascreenshotofmysoundsproperties:

Thesoundisaddedandreadytogo!Openupthebuttonobjectandaddanew Mouse
> Left Pressed Event
toit.Insidethisevent,wewillbeexecutingthefollowingcode.

obj_button: Left Pressed Event

/// Play the sound


audio_play_sound
(
snd_click
,
10
,
false
);

99

audio_play_sound
Thisshortbitofcodewillusethe functiontoplayourclicksound
whentheuserclicksonthebutton.Thisfunctiontakesthreearguments.Thisfirst
argumentistheidofthesoundtoplay.Thesecondargumentisthepriorityofthe
sound.Theprioritycanbefrom0to100ithelpsGameMakerdecidedwhichsoundsto
playiftherearetoomanyplayingatthesametime.Highernumbershaveahigher
priority.ThefinalargumenttellsGameMakerwhetherthesoundshouldloopornot.We
dontwantoursoundtoloop,sowepassavalueoffalse .

Createasimpleroom,addthebuttonobjecttotheroom,runthegame,andtestthe
project.Thesoundshouldplaywheneverthebuttonisclicked.

Sofar,sogood.Nowthatyouknowhowtocreateasoundandplayitwhenanevent
occurs,wehaveaproblem:Ifyoupressthebuttonmultipletimesinarow,thesound
cangetannoying.Thisisnormal.Nomatterhowgoodyoursoundis,ifyouhearittoo
manytimesinarow,itwillbecomeannoying.Imgoingtoteachyouatrickthatyoucan
usetohelpmitigatethisproblem.

Openupthebuttonobjectagain,andthistime,addanew Create Event toit.

obj_button: Create Event

/// Create a sound emitter


audio_em
=
audio_emitter_create
();

Inthecodeabove,wearecreatinganewaudioemitterandassigningitsidtothe
audio_em variable.

Youprobablyrememberfromtheintroductiontothischapterthataudioemitterscanbe
usedtoalterthepropertiesofasoundthatisbeingplayed.Thetwomostcommon
propertiesthatarealteredarethepitchandthegain.Wewillbealteringbothofthese
propertiesofoursoundthiswillcreateanicevarianceandpreventthesoundfrom
becomingannoying.Firstthough,weneedtomakesuretodestroytheemitterwhenthe
gameends.Addanew Game End Event tothebuttonobject.

100

obj_button: Game End Event

/// Destroy the sound emitter


audio_emitter_free
(
audio_em
);

audio_em
Inthiscode,wepassthe audio_emitter_free
variableintothe functionin
ordertodestroyouraudioemitter.

Now,weneedtojumpbacktoour Mouse > Left Pressed Event andchangetheway


thatweplayoursound.Makesurethatyoucompletelyremovethecodethatwas
previouslyintheeventbeforeaddingthisnewcode.

obj_button: Left Pressed Event

/// Play the sound


audio_emitter_pitch
(
audio_em
,
random_range
(.
5
,
1.5
));
audio_emitter_gain
(
audio_em
,
random_range
(.
1
,
1
));
audio_play_sound_on
(
audio_em
,
snd_click
,
false
,
10
);

Westartbycallingtheaudio_emitter_pitch function.Wetellitwhatemitterwewant
touseandthepitchthatwewantouremittertobesetto.Wepassinarandomrange
from.5 1.5
to .Anormalpitchissetat1.Next,weusetheaudio_emitter_gain
function.Wealsopassintheemitterandusearandomrangeof .1 1
to.Again,the
normalgainforasoundis 1
.Finally,weplaythesoundusingthe
audio_play_sound_on function.Thefirstargumentforthisfunctionistheemittertobe
used,thesecondistheidofthesound,thethirdiswhetherornotthesoundshould
loop,andthefourthisthepriorityofthesound.Itisunfortunatethatthelasttwo
argumentsareswappedinthisfunctionfromhowtheyareorderedinthe
audio_play_sound
function,butifyouknowthat,itmakesthisfunctioneasierto
manage.

Runthegameagainandlistentothedifference.Itriedtousefairlydrasticnumbersin
thisexamplesothatyouwillbeabletohearthedifference.Ifyoustilldontnoticea
difference,youmighttrychangingtherandomrangesuntilyoudo.

101

Greatjob!Now,youknowhowtousesoundsinGameMaker,andyouevenknowhow
toaltertheirpitchandgainduringthegametocreateabetteraudiofeedback
experience.

102

Chapter11

DevelopmentPatternsand
Tricks
States
Settingupastatesysteminanobjectissmartifyouwantittohavedifferentbehaviors.
Letssayyouwantyourplayerobjecttobeabletorun,swingasword,rolloutofthe
wayofenemyattacks,andthencontinuerunning.Thesethreebehaviorscouldhave
theirownstateswithintheobject.Therearedifferentwaysofimplementingthiskindof
system,butIcanshowyousomeofthewaysthathavebeenhelpfultomeinthepast.

WhenIwasfirstlearningtosetupstatesystems,Itriedcreatingadifferentobjectfor
eachstateandthenIwoulduse instance_change tochangestates.Thisworked,but
instance_change
notverywell. isgoodforsomethings,butIwouldntrecommendit
forastatesystem.Aftertryingthat,IrealizedthatIcouldjustuseastatevariable
combinedwithaswitchstatementinthe Step Event .Letmeshowyou.

Create Event

/// Create the state variable


state
='
idle
';

Step Event

/// Control the state


switch
(
state){
case
'idle
'
:
scr_idle
();
break;

case
'
move
':
scr_move
();
break;

103

case
'
attack
':
scr_attack
();
break;

case
'
roll
':
scr_roll
();
break;
}

Inordertohelpmetoorganizemycodebetter,Ichosetocreateascriptforeachstate
andthenjustrunthescriptsforeachcase.

Ivelearnedseveraldifferentwaysofdoingthis,andIdiscoveredonethatIlikequitea
bit.Ofcourse,itisntperfecteitherandcouldbeimproved,butitsprettysimpleand
easytounderstand.Thismethodusesacombinationofmacrosand script_execute .
Letmeshowyouasimpleexampleofhowitworks.

OneofthecoolfeaturesofGameMakeristhatyoucanassignanexpressiontoa
macro.Youcanalsoassignascripttoamacro.Forthesakeofthisexample,Illuse
somefakecodewiththeassignmentoperatorbutinreality,youwillsimplyusethe
GameMakeruserinterfacetocreatethemacro.

PLAYER_WALK
=
scr_player_walk

Create Event
Onceyouhavethisasyourmacro,youcansetyourstateinthe like
this:

state
=
PLAYER_WALK;

Youcanusethesamemethodtochangethestateofyourobjectinsideotherevents.
Afterdoingthesefirsttwosteps,youstillneedtoactuallyrunthecodeassociatedwith
thatstate.Todoso,youwouldplacethisintheStep Event ofyourobject.

104

execute_script
(
state
);

Itsthatsimple!Inthefirststep,youassignthescriptsidtothemacro/constant.Inthe
secondstep,youassignthatmacro/constant(whichnowcontainsthescriptsid)tothe
statevariable.Inthelaststep,youexecutewhateverscriptisassignedtothestate.Its
asimplemethod,butitcanworkreallywellforseparatingthecodeofeachstate.

Becauseenumsareglobalinscope,youcanuseenumsinsteadofmacros.Eitherway
worksfine.SometimesIfindthatmacrosarebetter,becauseyougetsyntaxhighlighting
onmacros,butyoudontonenums.

Hereisanexampleofhowyoumightsetupyourstateswithanenum,insteadofusing
macros:

enumstate {
walk
=
scr_player_walk;
}

Youcansetthestateinalmostthesameway.

state
=
state
.
walk;

Executingthestatewillbedoneinexactlythesameway.Inthechapteronartificial
intelligence,Igothroughabasicexampleofhowyoucanuseenumsandstatesto
createartificialintelligenceforyourenemiesifthissectionisalittleconfusingtoyou,be
suretocheckthereforaningameexample.

CreatingPseudoObjectsusingArraysandEnums
Whiledevelopingthisbook,Ivebeenworkingonamethodforcreatingarraysthatare
similartoobjects.Therearemanysituationsinwhichyouneedtostoresome
informationinanorganizedwayandaccessitlater,butwherecreatinganactualobject
inyourgamewouldbeoverkill,becausethebuiltinobjectsinGameMakercomewitha
lotofextrabaggage.Ofcourse,itispossibletouse ds_maps forthis,butmapscan
becomebloatedanddifficulttomanageifyoutrytogothreelevelsdown(forexample,if

105

youtrytoaccessamapdatastructurethatisnestedinsideanothermapdatastructure).
Icansimulatethethreeleveldepthusinga2darrayandafewenums.Letmeshow
youwhatImean:

enum
base{
name
=
0,
hp
=
1,
att
=
2,
def
=
3,
spd
=
4
}

enum
class{
wizard
=
0,
knight
=
1
,
}

Youcanuseenumstomakethecodemorereadableandavoidmagicnumbers.A
magicnumberisanumberinyourcodethathaslittleobviousmeaningtotheperson
reading/writingthecode.Onceyouhavesetuptheenums,youcanstartcreatingthe
2darrays.

stats
[
class
.
wizard
,
base
.
name
]="
Merlin
";
stats
[
class
.
wizard
,
base
.
hp
]=
25;
stats
[
class
.
wizard
,
base
.
att
]=
3;
stats
[
class
.
wizard
,
base
.
def
]=
1;
stats
[
class
.
wizard
,
base
.
spd
]=
3;

stats
[
class
.
knight
,
base
.
name
]="
Arthur
";
stats
[
class
.
knight
,
base
.
hp
]=
35;
stats
[
class
.
knight
,
base
.
att
]=
5;
stats
[
class
.
knight
,
base
.
def
]=
2;
stats
[
class
.
knight
,
base
.
spd
]=
1;

106

Iwouldrecommendcreatingascript(wherethevaluesbeingpassedarethe
arguments)tosettheseup.Thenicethingaboutthismethodisthatlaterinyourgame,
youcanaccessthisdatainawaythatisveryreadable.Letmeshowyou.

vardamage
=
stats
[
class
.
wizard
,
base
.
att
];
enemy
.
hp
-=
damage;

Thisexampleassumesthatyouhaveareferencetotheenemythatyouareattacking,
butyougettheidea.Ifyoudidntuseenums,itwouldlooksomethinglikethis:

stats
[
0
,
1
]="
Merlin
";
stats
[
0
,
2
]=
25;
stats
[
0
,
3
]=
3;
stats
[
0
,
4
]=
1;
stats
[
0
,
5
]=
3;

DoyouseewhatImeanaboutmagicnumbers?Nobodywouldbeabletounderstand
thiscode.Itwouldstillbeeasytoaccess,butyouwouldhavetomemorizethenumber,
stat,andnameassociations.

MakeanObjectFollowtheMouse
Itsaprettysimpletasktogetanobjecttofollowthemouse,butsomereadersmightnot
knowhowtodothis,soImgoingtoputaquickexamplein.Thetrickwiththis
functionalityismakingsurethatyouusethe End Step Event .

End Step Event

x
=
mouse_x;
y
=
mouse_y;

Thiscodesetsthexpositionoftheobjecttothecurrentxpositionofthemouseatthe
endofeverygamestep.Ifyouusethenormal Step Event ,thetimingofthemovewill
beoff,andtheobjectwillappeartolagbehindthemouse.

107

MakeaGunAimTowardstheMouse.
Codingthisfunctionisalsoasimpletask.Thetrickhereisknowinghowtousethe
image_angle
builtin point_direction
propertyandthe function.

vardir
=
point_direction
(
x
,
y
,
mouse_x
,mouse_y
);
image_angle
=
dir;
direction
=
dir;

Youshouldalsomakesurethatyourspritehasitsoriginwhereyouwantthepivotpoint
tobe,andthatthespritestartsoutfacingexactlytotheright(or0degrees).

UsingMedianorClamp
Oneneattrickthatyoucandoisusethe median
functionortheclamp
function.Bothof
thesefunctionscanbeusedtolimitavariableorpropertytoaspecificrange.One
commonuseforthesefunctionsislimitingtheplayersmovementtotheroom.

obj_player: Step Event

/// Limit the player's movement


x
=
clamp
(
x
,
0
,
room_width
);
y
=
clamp
(
y
,
0
,
room_height
);

Thisisasimplepieceofcodethatclampstheplayersxandycoordinatestotheroom
boundaries.Alternatively,youcoulddothisusingmedian.

x
=
median
(
0
,
x
,
room_width
);
y
=
median
(
0
,
y
,
room_height
);

Youcanuseeithermethod,butIfeelthatclampisamorespecificwordandthatclamp
makesthecodemorereadable.

Colors
GameMakerhassomebuiltincolorsthatyoucanusewhendrawingspritesorshapes.
Thesecolorsareconvenient,butIwouldntrecommendusingtheminmostcases
becausetheyareverybland.Someofthemworkokayforparticles,buteveninthis
108

case,Igenerallyavoidthem.Inthenextsegment,Illshowyouhowyoucancreateyour
owncustomcolors,butfirst,letmelistthebuiltincolors.

c_white
c_ltgray
c_gray
c_dkgray
c_black

c_fuchsia
c_purple

c_yellow
c_orange

c_red
c_maroon

c_lime
c_green

c_teal
c_agua
c_blue
c_navy

c_silver
c_olive

CustomColors
Makingyourowndrawcolorsisimportantforhelpingyourgamesgraphicsmatchyou
maywantbuttonsortexttoshareacommoncolorwithacharacterorbackground.
Thereareafewsimplefunctionsthatyoucanusetomoresubtlyadjustcolors.

my_green
=
make_colour_rgb
(
105
,
205
,
85
);

109

make_colour_rgb
The functionusedintheabovecodetakesthreearguments:ared
value,agreenvalue,andabluevalue(orRGB).Inmostgraphicsprograms,these
RGBvaluesaredisplayedwhenyoucreateacustomcolor.

my_red
=
make_colour_hsv
(
0
,
58
,
80
);

Themake_colour_hsv functionalsotakesthreearguments,ahue,asaturation,anda
value.Thesevalueswillalsotypicallyshowupwhileeditingcolorsinanygraphics
program.Igenerallyusepaint.net(gotogetpaint.nettodownloadacopy).Paint.netis
freeandquitepowerfulfortheprice.

my_orange
=
merge_colour
(
c_red
,
c_yellow
,.
5
);

Themerge_colour functiontakestwocolorsandblendsthemtogethertomakeanew
color.Thefirsttwoargumentsarethecolorstobeused.Thelastargumentisanumber
between0and1thatrepresentstheblendvalueforthecolor.Ablendvalueof0will
returnthefirstcolorentered(c_red).Ablendvalueof1willreturnthesecondcolor
(c_yellow ).Ablendvalueof.5willuse50%ofeachcolor.

UserInput
InGameMakerStudio,therearemanydifferentwaysthatyoucangetinputfromthe
user.Inthischapter,Iwillbecoveringthethreemostcommonmethods:mouseinput,
keyboardinput,andgamepad(controller)input.

MouseInput
Keyboardinputandmouseinputarearguablythemostcommonformsofinputon
computers.MouseinputinGameMakerStudioissimpleandeasytolearn.Youcanuse
thebuiltinobjectevents,butImalsogoingtoshowyouhowtocheckthoseeventsin
code.

Letsbeginbyshowinghowtocheck,incode,ifamousebuttonisbeingpressed.

if
(
mouse_check_button
(
mb_left
)){

110

// The left mouse button is being pressed


}

mb_leftisaconstantinGameMakerLanguagethatreferstotheleftmousebutton.
Thereareconstantsforeachoftheothermousebuttons.

mb_none // No mouse button


mb_left // Left mouse button
mb_right // Right mouse button
mb_middle // Middle mouse button
mb_any // Any mouse button

mouse_check_button() isafunctionthatreturnstrueifthebuttonisbeingpressedand
returnsfalseifitisnot.Atthispoint,Ishouldclarifythedifferencebetweenthecasein
whichthemousebuttonis beingpressedandthecaseinwhichthemousebutton is
(now) pressed.Thefirstcaseimpliesthatiftheuserisholdingthebuttondown,itwould
stillreturntrue.Thesecondimpliesitthefunctionwillonlyreturntrueinthestepin
whichthemousebuttonis(initially)presseddown.Afterthat,itwillreturnfalse,evenif
theusercontinuestoholdthebuttondown.Letslookathowyoucanchecktoseeifthe
mouseispressed.

if
(
mouse_check_button_pressed
(
mb_left
)){
// The left mouse button was pressed.
}

MousePosition
InGameMakerLanguage,therearetwovariablesthatyoucanusetoaccessthe
mouseposition.

mouse_x
mouse_y

111

Thesetwovariablesareglobal,andassuch,canbeaccessedinsideanyobject.Ifyou
wantedtomakeanobjectfollowthemouseposition,youcouldusethissimplecode.

End Step Event

x
=
mouse_x;
y
=
mouse_y;

Asdiscussedabove,becauseofeventtimingissues,youwillmostoftenwanttomake
End Step Event
anobjectfollowthemouse(oranotherobject)inthe .

KeyboardInput
Keyboardinputisprettyeasytolearnhowtouseifyoualreadyknowhowtousemouse
input,asthetwoareverysimilar.

if
(
keyboard_check
(
vk_right
)){
// The right arrow key is being pressed
}

vk_rightisaconstantthatissimilartothemb_leftconstantthatthemouseinput
uses.Therearequiteafewdifferentvk_ prefixedconstantsthatyoucanuse.

vk_right // Right arrow key


vk_left // Left arrow key
vk_up // Up arrow key
vk_down // Down arrow key
vk_nokey // No key
vk_anykey // Any key
vk_enter // Enter key
vk_escape // Escape key
vk_space // Space key
vk_shift // Shift key
vk_control// Control key
vk_alt // Alt key

112

Therearemanymorekeyconstantsthanthis.Mostofthemareratherobvious.Ifyou
wantthefulllist,youcansearchforthemintheGameMakerhelpfile.

Theimportantthingtonoteisthatyoucannotuseletterkeyslikethis:

if
(
keyboard_check
(
vk_a
))
{
// Causes an error
}

Ifyouwanttocheckwhethertheplayerhaspressedanyoftheletterkeys,youneedto
useaspecialfunction.

if
(
keyboard_check
(
ord
('
A
')){
// The "A" key is being pressed
}

Maybeyouwanttocheckforanykeyandthendisplaythekeypressedtothescreen.
Youcandothatlikethis:

if
(
keyboard_check
(
vk_any
)){
show_message
(
keyboard_lastchar
);
}

keyboard_lastchar willreturnastringofthelastkeyboardkeypressed.Thiswillonly
workwithlettersandnumbersthoughitreturnsanemptystringforanyotherkeys.If
youwanttocheckforakeythatisntanumberoraletteryoucanuse keyboard_key .
ThisglobalvariablereturnstheASCIIvalueofthekeypressed.

if
(
keyboard_check
(
vk_any
)){
show_message
(
string
(
keyboard_key
));
}

113

Therewillbetimeswhenyouwanttohavetwokeysdothesameexactthing.For
example,manygamesallowtheplayertouseeitherthearrowkeysortheWASDletter
keystomovethecharacter.Youcanmapkeystoeachotherlikethis.

// Map the ASDW keys to the arrow keys


keyboard_set_map
(
ord
(
'D'
),vk_right
);
keyboard_set_map
(
ord
(
'A'
),vk_left
);
keyboard_set_map
(
ord
(
'W'
),vk_up
);
keyboard_set_map
(
ord
(
'S'
),vk_down
);

Youonlyneedtocallthesefunctionsonce,forexample,inacharacterobjects Create
Event .Youwouldcontinuetoprogramyourgameandcharacterusingthearrowkeys,
buttheWASDkeyswouldworkaswell,becausetheyaremappedtothearrowkeys.

GamePadInput
Usinggamepadinputisalittleharderthankeyboardinput,butitisverysimilar.Mostof
thetime,youwillwantyourgametobecompatiblewithseveralinputdevices.Youmay
haveitsetupsothattheplayercanusethekeyboard(bydefault),butiftheyplugina
gamepad,thenthekeyboardwillbedisabledandthegamepadwillbeusedforcontrol.
Letmeshowyouasimpleexamplethatallowsyoutodothis.

vardevice=
0;
if(
gamepad_is_connected
(
device
)){
// Get gamepad input
}else{
// Get keyboard input
}

th
Thiscodecheckstoseewhetherthefirst(0 )gamepadisconnected.Thedevice
numberistheassignedplayernumber.Ifyourcontrollerslightindicatesthatyouare
playernumberone,thenthedevicenumbershouldbe0.Ifitshowsthatyouareplayer
numbertwo,thenthedevicenumbershouldbe1.Thedevicenumberisimportant,
becauseitisusedinmostfunctionsforgettinggamepadinputletslookatsomeof
them.Assumethatthedevicenumberisstill0.

114

if(
gamepad_button_check
(
device
,gp_face1
))
{
// Do something cool
}

Thisfunctioncheckstoseewhethertheuserispressingabuttononthegamepad.The
buttonbeingcheckedis gp_face1 onanXboxcontroller,thisistheAbutton.Thereis
anentirelistofbuttonconstantsthatcanbeusedwiththesefunctions.Youcancheck
theGameMakerhelpfileforthelistofcontrollerconstants.

MethodsofControllingInput
ThemethodthatIgenerallyuseforcontrollinginputistoseparatetheinputfromthe
actualmovementwithvariables.Thisallowsmetomoveacharacterorenemywiththe
variablescreated.Imgoingtosetupasimpleexampleofthis.

Firstletscreatethevariables.

Create Event

xaxis
=
0;
yaxis
=
0;
abtn
=
false;

Nowletssettheminourplayer,basedontheinput.

Step Event
if
(
gamepad_is_connected
(
0
))
{
// Get gamepad input
xaxis
=
gamepad_axis_value
(
0
,
gp_axislh
);
yaxis
=
gamepad_axis_value
(
0
,
gp_axislv
);
abtn
=
gamepad_button_check
(
0
,
gp_face1
);

}
else{
// Get keyboard input
varright
=
keyboard_check
(
vk_right
);
varleft
=
keyboard_check
(
vk_left
);

115

varup
=
keyboard_check
(
vk_up
);
vardown
=
keyboard_check
(
vk_down
);

xaxis
=
right
-
left;
yaxis
=
down
-
up;
abutton
=
keyboard_check
(
ord
('
A
'));
}

Onceyouhavecreatedthevariablesandsetthemaccordingtotheinputtheplayerhas
givenus,youcandecidehowtomovetheplayer.Thiscodeassumesthatyouare
obj_solid
using asyourwalls.

Step Event
// Set the speed
varspd
=
4;

// Horizontal movement
if
(!
place_meeting
(
x
+
xaxis
*
spd
,
y
,
obj_solid
)){
x
+=
xaxis
*
spd
;
}

// Vertical movement
if
(!
place_meeting
(
x
,
y
+
yaxis
*
spd
,
obj_solid
)){
y
+=
yaxis
*
spd
;
}

if
(
abtn
){
// Shoot a bullet
instance_create
(
x
,
y
,
obj_bullet
);
}

Asyoucansee,itiseasytoseparatetheinputfromthecontroloftheobject,which
allowsustopassanyinputtypeintothisobjectandstillhavethesameplayerbehavior.

116

Chapter12

DrawingontheGUILayer
DrawGUIEvent
GUIstandsforgraphicaluserinterface.The Draw GUI Event isdifferentfromthe
Draw
Event becausetheDraw GUI Event drawseverythingrelativetothe GUIlayer
andnot
relativetotheactualgameroom.Often,theGUIlayerseemstoberelativetotheview,
andmostofthetimeitis,butbeawarethatthisisntalwaystrue.

Often,whenpeoplestartusingthe Draw GUI Event inconjunctionwithviews,theyrun


intoscalingissues.Thereisahandlylittlefunctionthatyoucanusetoresolvethis.

display_set_gui_size
(
640
,
360
);


display_set_gui_size takesawidthandaheight.Youwanttomakesurethatthe
widthandtheheightoftheGUIarethesameasthewidthandheightofyourviewso
thattheGUIsurfacewillhavethesameresolutionastheview.Bydefault,theGUIlayer
seemstobethesameresolutionastheportonthescreen.

HereisagoodwaytosettheGUIsdisplaysizetothesizeoftheview,nomatterwhat
Create Event
theactualsizeoftheviewis.Placethiscodeinthe ofyourobject:

/// Set the GUI to the size of the view


varview_width
=
view_wview
[
view_current
];
varview_height
=
view_hview
[
view_current
];
display_set_gui_size
(
view_width
,
view_height
);

Onceyouhavethecorrectheightandwidth,the Draw GUI Event isusedjustlikethe


Draw Event .Theoutputisdifferent,butyoucanstilluseallthenormaldrawfunctions
inthisevent.Letsquicklydrawsometextusingthe Draw GUI Event .

draw_text
(
32
,
32
,"
Hello
Draw
GUI
!");

117

ThissimplecodewilldrawthetextHelloDrawGUIatposition(32,32)oftheview.It
wontmatteriftheplayermovesinsidetheroom,thetextwillstillbedrawnrelativeto
theview.Asyoucansee,the draw_text functionisusedexactlylikeitwouldbeina
normalDraw Event ,butitworksdifferently.Youwillalsonoticethatthetextisontopof
theotherobjectsintheroom,regardlessofthedepthoftheobjectthatactuallydrawsit.
ThisisbecausetheGUIlayeris,bydefault,drawnontopoftherestoftheroom.

AsyouaredrawingontheGUIlayer,youmayneedtogetinformationabouttheGUI
layer.Herearesomefunctionsthatyoucanusetogetthisinformation:

vargui_width
=
display_get_gui_width
();
vargui_height
=
display_get_gui_height
();
draw_text
(
gui_width
-
32
,
gui_height
-
32
,"
Hello
Draw
GUI
!");

Thiscodedoesthesamethingasthecodeaboveit,butitdrawsthetextatthelower
righthandcornerofthedisplay.WegetreferencestothewidthandheightoftheGUI
layerinordertodrawourtextrelativetothosedimensions.

CreatingButtonsontheGUILayer
OneofthemostdifficultthingstodealwithontheGUIlayerisdrawingbuttons.Thisis
becausetheGUIlayerisindependentoftheapplicationsurface.Thecalculationsfor
determiningifthemouseishoveringoverabuttoncanbetricky.LetmeshowyouhowI
havesolvedthisproblemwithanexample.

Createanewspriteandnameit spr_player .

spr_player

Ivemademyspriteablack32x32square.Nowcreatetwonewobjects.Nameoneof
themobj_player andtheotheroneobj_gui_button .

obj_player
obj_gui_button

Createanewroomandnameit rm_test.Givetheroomawidthof640andaheightof
360.Clickontheviewstab,clickEnabletheuseofViews,clickVisiblewhenroom

118

starts,settheviewwidthandheightto320x180,andsettheportwidthandheightto
640x360.SettheObjectfollowingtoobj_player ,thehorizontalborder(Hbor)to160
andtheverticalborder(Vbor)to90.Thiswillforcetheviewtofollowourplayerobject.
Now,placeboththeplayerobjectandtheGUIbuttonintheroom.Makesurethatthe
GUIbuttonfallsinsidetheview.Wearegoingtouseitsstartingpositiontodetermineits
positionontheGUILayer.Hereisascreenshotoftheroomanditsviewproperties.

YoucanseethatIhavealsoplacedabasicgreenbackgroundinmyroom.Itisnt
necessary,butyoucanaddabackgroundaswell,ifyouwant.

Openuptheplayerobjectandadda Step Event toit.Thiseventwillholdthecodethat


movestheplayerintheroom.Weneedtheplayerobjecttomovesothatwecanmake
surethattheGUIbuttonobjectfollowstheviewcorrectly.Hereisthecodethatwillgoin
Step Event
the .

obj_player: Step Event

// Move the player


varspd
=
4;
varup
=
keyboard_check
(
vk_up
);

119

vardown
=
keyboard_check
(
vk_down
);
varleft
=
keyboard_check
(
vk_left
);
varright
=
keyboard_check
(
vk_right
);

x
+=(
right
-
left
)*
spd;
y
+=(
down
-
up
)*
spd;

Thisissomeshortcutcodethatmovestheplayerusingthearrowkeys.

Now,openuptheGUIbuttonobjectandaddanew Create Event .Dragovera


Code
Action andplacethiscodeblockinit.

obj_gui_button: Create Event

/// Initialize the GUI button


text
='
Click
Me
'
;
width
=
96;
height
=
28;
hover
=
false
;
scale
=
2;

// This is the button object's position relative to the GUI layer.


display_x
=
xstart
*
scale
;
display_y
=
ystart
*
scale
;

Westartbycreatingsomeinstancevariablesthatwilldefinehowourbuttonlooksand
behaves.The text variableholdsastringvalueofthetextthatwillbeshownonthe
button.Afterthat,wehavethe widthand height.Thenwehaveavariablecalled
hover .Ourhovervariablewillbetrueifthemouseisoverthebuttonandfalseifitis
not.Wealsocreateavariablecalled scale andsetitto2.Ourscaleissetto2because
theGUIlayerdefaultstothesizeoftheportonthescreenandtheportonthescreenis
twicethesizeoftheview.Aftercreatingtheinitialinstancevariables,wecreatetwo
newinstancevariablesthatwillbeusedtopositionthebuttonontheGUIlayer.These
twovariableswillgrabthebuttonobjectsstartingpositionintheroomandmultiplyitby
scale
our .

120

AddanewEnd Step Event .Inthisevent,wewilldothemathnecessarytocalculate


whetherthemouseishoveringoverourbutton.

obj_gui_button: End Step Event

/// Check to see if the mouse is above the button


// Find the edges
varleftx
=
display_x
-
width
/
scale
;
varrightx
=
display_x
+
width
/
scale
;
vartopy
=
display_y
-
height
/
scale
;
varbottomy
=
display_y
+
height
/
scale
;

// Get the window position of the mouse


varwmx
=
window_mouse_get_x
();
varwmy
=
window_mouse_get_y
();

// Check to see if the mouse is inside the edges


hover
=
point_in_rectangle
(
wmx
,
wmy
,
leftx
,
topy
,
rightx
,
bottomy
);

First,wegettheedgesofthebutton.Next,wegetthemouseswindowposition.Ifthe
viewhasmoved,thewindowpositionofthemousewillbedifferentfromthemouses
positionintheroom.Lastly,weusethepoint_in_rectangle functiontodetermine
whetherthemouseishoveringovertheGUIbutton.The point_in_rectangle
function
takessixarguments.Thefirsttwoarethepointtocheck,thenexttwoaretheupperleft
corneroftherectangle,andthefinaltwoarethelowerrightcorneroftherectangle.

Adda Mouse > Global Mouse > Global Left Pressed Event totheGUIbutton
Code Action
object.Dragovera andtypethiscodeinit.

obj_gui_button: Global Left Pressed Event

/// Click the button


if
(
hover
){
show_message
("
Youclicked the button
.");
}

121

Thiscodecheckstomakesurethatweareoverthebuttonwhenthemousebuttonis
pressed.Ifweare,itshowsamessagesayingthatthebuttonwasclicked.Wecantuse
the Left Pressed Event becausethisbuttonobjectdoesnthaveacollisionbox.Even
ifitdid,weareworkingontheGUIlayer,andthecollisionboxwouldbeinadifferent
locationfromthedrawnbutton.

ThelasteventthatweneedtoaddtoourGUIbuttonobjectistheeventthatwilldraw
thebuttononthescreen.Weareusingthe Draw GUI Event .

obj_gui_button: Draw GUI Event

/// Draw the GUI button


if
(
hover
){
draw_set_alpha
(.
2
);
}
else{
draw_set_alpha
(.
5
);
}

varleftx
=
display_x
-
width
/
scale
;
vartopy
=
display_y
-
height
/
scale
;
varrightx
=
display_x
+
width
/
scale
;
varbottomy
=
display_y
+
height
/
scale
;

// Draw the button using a rectangle


draw_rectangle
(
leftx
,
topy
,
rightx
,
bottomy
,
false
);
draw_set_alpha
(
1
);

// Set the horizontal and vertical alignments for text


draw_set_halign
(
fa_center
);
draw_set_valign
(
fa_middle
);

// Draw the button text


varcol
=
c_white;
draw_text_colour
(
display_x
,
display_y
,
text
,
col
,
col
,
col
,
col
,
1
);

hover
Usingthe variablethatwecreatedearlier,wesetthedrawalphatoeither0.2or
0.5.Thismakesiteasiertoseethebuttonifthemouseishoveringoverit.Wecreate
sometemporaryvariablestohelpusfindthecornersofthebuttonandusethe

122

draw_rectangle functiontodrawourbutton.Afterdrawingthebutton,wemakesureto
setthedrawalphabackto1.Wesetthetextalignmenttocenterandmiddle,andthen,
wedrawthetextoverthebutton.

TestingtheGame
Runthegameandmakesurethat,nomatterwheretheplayerisintheroom,thebutton
respondstobeinghoveredoverandclickedon.

WorkingwiththeGUIlayercanbealittletrickysometimes,butoften,itiswellworththe
work.Keepinmind,youdontalwaysneedtousetheGUIlayerforHUDs,menus,and
buttons.Sometimesyoucanmakeitworkjustbydrawingrelativetotheview.
Personally,Idowhateverisgoingtotaketheleastamountofcodeandstilllookgood.
Forgamestartmenus,Iusenormalobjects,butforHUDitemsthatdontneedany
interaction(likehealthandlives),IwillusetheGUIlayerbecauseIdontneedto
calculatethepositionusingtheview.Messaroundwiththepreviousexamplesandfind
outwhatworksbestforyou.

123

Chapter13

ParticlesandSurfaces
Creatingparticlesystems
particlesystem
A isaworldthattheparticleslivein.InGameMaker,itisimpossibleto
createparticleswithoutfirstcreatingaparticlesystem.Creatingaparticlesystemis
actuallyeasierthancreatingtheparticlesthemselves.Thinkofitthisway.Forobjects,
youmustfirstcreatearoomtoputtheminortheobjectscantbeusedorseen.Particle
systemsareliketheroomandtheparticlesareliketheobjects.Inmostcases,youwill
onlyneedtocreateoneparticlesystemtocontainallofyourparticles.Becausethereis
generallyonlyone,Iliketocreateanobjectforcontrollingtheparticlesandtheparticle
system.Inthisobject,Icreateaglobalvariableandassigntheparticlesystemtoitlike
this.IwouldplacethiscodeintheGame Start Event ofanobjectcreatedspecifically
tomanagetheparticles.

global
.
ps
=
part_system_create
();

Aftercreatingtheparticlesystemandassigningittoaglobalvariable,youcan
referenceitatanytime.Youarenowreadytocreateyourfirstparticletype.

Creatingparticletypes
Theparticleisgoingtobeusedbymultipleobjects,soImgoingtomakeitglobal,just
liketheparticlesystem.Thisfunctionjustcreatestheparticletype.Remember,though,
thisfunctiondoesnotactuallycreateparticles,itjustdefinestheparticletype!Later,you
willusethisparticletypetoactuallycreatesomeparticlesinsideofyourparticlesystem,
whichwilldisplaythemintheroom.

global
.
pt_blood
=
part_type_create
();

Nowthatyouhavecreatedtheparticletype,youneedtosetthedifferentproperties.
Thepropertiesthatyousetwilldeterminethelookandbehaviorofthisparticletype.
Therearelotsofdifferentpropertiestoset.Iwillexplaineachoneasitgetsset.

124

Ialsoliketocreatealocalreferencetotheparticletypeforeasieraccesswhilesetting
properties.

varpt
=
global
.
pt_blood;

Thelastlineallowsustousept
astheidfortheparticletype,whichislesstyping.Lets
settheshapeoftheparticle.

part_type_shape
(
pt
,
pt_shape_disk
);

pt
Thefirstargumentistheidoftheparticletype.Iuse ,whichisareferencetothe
particletypealreadycreated.Thesecondargumentistheparticleshape.Thereare
quiteafewdifferentbuiltinshapes.Hereisaquickreferencelistofthem:

Particle Shape Types

pt_shape_disk
pt_shape_pixel
pt_shape_line
pt_shape_star
pt_shape_sphere
pt_shape_flare
pt_shape_spark
pt_shape_explosion
pt_shape_snow
pt_shape_smoke
pt_shape_cloud
pt_shape_square
pt_shape_circle
pt_shape_ring

Trythemallouttoseewhattheylooklike,andfindtheonesthatwillbestsuityour
style!Youmayhavetoadjustthesizesandcolorstogetagoodideaofwhattheylook
like.Illshowyouhowtodothatnow.

125

part_type_size
(
pt
,
0.1
,
0.2
,
0
,
0
);

Thisbitofcodesetsthesize.Therearefivearguments:thefirstistheid,thesecondis
theminimumsize,thethirdisthemaximumsize,thefourthisthesizeincrease,andthe
fifthisthesizewiggle.Imnotgoingtoexplainexactlywhateachargumentdoes.Ifyou
wanttoknowmoreaboutthat,youcancheckouttheGameMakerhelpfileorjustmess
aroundwiththevalues.

part_type_type_color2
(
pt
,
c_red
,
c_maroon
);

Thisbitofcodewillchangetheparticlefromredtomaroonalongthespanofitslife.
Letssettheparticlespeednow.

part_type_speed
(
pt
,
2
,
5
,
-0.1
,
0
);

Thefirstargumentisobviouslytheidagain.Afterthat,itistheminimumspeedofthe
particle,themaximumspeed,theamounttoaddtothespeedateachstep(youwill
noticethatIamactuallysubtractingfromthespeedeachstep),andfinallytheamount
towiggle.

part_type_direction
(
pt
,
0
,
360
,
0
,
0
);

Thisfunctionsetsthedirectioninwhichtheparticlewilltravel.Onceagain,younotice
thatthefirstargumentistheid.Aftertheid,thereisaminimumdirection,amaximum
direction,adirectionincrease,andadirectionwiggle.Youmaybenoticingapattern
withthesefunctions.Forthemostpart,theyareallverysimilar.

part_type_gravity
(
pt
,
0.2
,
270
);

126

Nowletssethowgravityinfluencestheseparticles.Aftertheidargumentatthestart,
thereisagravityamountargumentfollowedbyagravitydirectionargument.Asyou
probablyknowbynow,270degreesisthedownwarddirection.

Youarefinishedcreatingtheparticletypeandsettingallofthedifferentattributesof
yournewparticletype.Nowitsalmosttimetoactuallycreatesomeparticles.Before
youmoveon,youneedtomakesuretodestroytheparticlesystemandparticletypesin
Game End Event
the sothatyoudontcreateamemoryleak.Itisprettyeasytodo.

Game End Event

part_type_destroy
(
global
.
pt_blood
);
part_system_destroy
(
global
.
ps
);

Andyouredone!

Creatingparticles
Thispartiswhereitgetsfun.Letscreatesomeactualparticles.First,Imgoingtoshow
youhowtocreateparticleswithoutusinganemitter.Youmightnotknowwhataparticle
emitteris,andthatsokay.Iwillexplainitafterthis.Ifyouarejusttestingyourparticles,
youcanplacethisnextbitofcodeina Global Mouse, Left Pressed Event .

Global Mouse, Left Pressed Event

var mx=mouse_x;
var my=mouse_y;
part_particles_create
(
global
.
ps
,
mx
,
my
,
global
.
pt_blood
,
10
);

Thiscodewillcreatetenbloodparticlesatthemousesposition.Thefirstargumentis
thesystemthatyouwanttocreatetheparticlesinthesecondandthirdarethexandy
positionsoftheparticles,respectivelythefourthistheparticletypethatyouwantto
useandthelastargumentisthenumberofparticlesthatyouwanttocreate.

Creatingparticleemitters
Usingpart_particles_create isagoodwayforbeginnerstogetsomepracticeusing
particles,butgenerally,youwillwanttouseanemittertocreateyourparticles.Emitters
arespeciallittleobjectsthatletyoucreateparticlesinbursts,instreams,acrosslarge

127

areasoftheroom,andindifferentdefinedareas.Thefirstthingyouneedtodoinorder
touseanemitteriscreateone.

em
=
part_emitter_create
(
global
.
ps
);

Theonlyargumentforthisfunctionistheidoftheparticlesystemthatyouwantthis
emittertolivein.Igenerallycreateemittersusinganinstancevariable(instancescope),
becauseonlytheobjectthatcreatestheemitterisgoingtouseit.Placingitintheglobal
scopewouldgenerallybepointless.

Aftercreatingtheemitter,youneedtosettheregionthattheemitterwillcreateparticles
in.Thisisasimplefunctionthatlookslikethis:

var mx=mouse_x;
var my=mouse_y;
var shape=ps_shape_ellipse;
var distr=ps_distr_gaussian;
part_emitter_region
(
global
.
ps
,
em
,
mx
-
4
,
mx
+
4,
my
-
4
,
my
+
4
,
shape,distr
);

Thereareafewstrangethingsthatyoumightnoticeabouttheargumentspassedinto
thisfunction.Letsgooverthem.Thefirstargumentistheparticlesystemid.Afterthat,
wehavetheemitterid,thexminimum,thexmaximum,theyminimum,theymaximum,
theemittershape,andtheparticledistributiontype.

The emittershapeisaglobalconstantdefinedbyGameMaker.Hereisalistofthe
differentemittershapesthatyoucanuse:

Particle Distribution Shape Types

pt_shape_rectangle
pt_shape_ellipse
pt_shape_diamond
pt_shape_line

128

Theparticledistributiontype
isalsoaglobalconstantdefinedbyGameMaker.There
areonlythreedifferenttypesthatyoucanuse.Heretheyare:

Particle Distribution Types

ps_distr_linear
ps_distr_gaussian
ps_distr_invgaussian

Thelineardistribution
isanevendistributionacrosstheregionoftheemitter.The
gaussiandistribution createsmoreparticlesnearthecenteroftheshapeandfewer
particlesclosertotheoutsideoftheshape.The invgaussianisaninverseofthe
gaussian,creatingmoreparticlesneartheedges.

CreatingParticlesUsinganEmitter
Nowthattheemitterisallsetup,youarereadytocreatesomeparticlesusingit.There
areafewdifferentfunctionsthatyoucanusenowwithyouremitter.Illshoweachone
toyouandteachyouwhattheydo.

part_emitter_stream
(
global
.
ps
,
em
,
global
.
pt_blood
,
2
);

Onceagain,theargumentsstartwiththeparticlesystemid.Then,youhavetheemitter
id,theparticletype,andthenumberofparticlestocreate.Thefunctionwillcreatea
streamofparticles.Everystep,itwillcreatetwobloodparticles.Thisfunctioncanbe
placedinthe Create Event ofanobjectandwillcontinuetostreamparticlesevenafter
theCreate Event hasended.Ifyouwanttostopthestreamofparticles,youwillneed
tocallthisfunctionagainandsetthenumberofparticlescreatedtozero.

Streamingiscoolforwaterfountains,snow,rain,andsuch.Forblood,ontheother
hand,ourcharacterwouldbeconstantlybleedingout.Hereishowyoucancreatea
burstofbloodparticles:

part_emitter_burst
(
global
.
ps
,
em
,
global
.
pt_blood
,
10
);

129

Theargumentsareallexactlythesameasthoseforthestreamingfunction.The
differenceisthatthisisaonetimecallthatwillstopcreatingparticlesonceithasbeen
run.Igenerallylikeburstingmoreoftenthanstreaming,butyoucanmessaroundwith
bothofthem.

Greatjob!Youhavenowlearnedtocreateaparticlesystem,particletypestoliveinthe
system,andemitterstocreatethoseparticletypes.Thebestwaytolearnmoreabout
particlesistocreateasimpleparticlegameandmessaroundwiththedifferentvalues
foryourparticleemittersandparticletypesuntilyougetsomethingyoulike.Itcanbea
greatwaytocomeupwithnewparticletypes,andyouwillhaveloadsoffunwhiledoing
it.

Surfaces
IfyouhaveeverusedGameMaker,youhavealreadyuseda surface .Sprites
associatedwithobjectsaredrawonwhatiscalledtheapplicationsurface.Thisisthe
room/viewcombinationthatcomesupwhenwerunourgame.Itisevenpossibleto
manipulatetheapplicationsurface,butfornow,wewontdothat.Theonlydifference
betweentheapplicationsurfaceandanothersurfacethatwemightcreateisthatthe
applicationsurfaceisdrawnonthescreenautomaticallyandyoursurfaceisnt.Infact,if
wedontdrawoursurfacemanually,wewillneverbeabletoseeit.Oncewehave
createdoursurfaceanddrawnsomethingtoit,weneedtoactuallydrawthesurface
itself(orpiecesofit)ontotheapplicationsurfacesothatthepersonplayingyourgame
willseeit.

SurfacesareanamazingfeatureofGameMakerLanguage,butifnotusedcorrectly,
theyarealsoalittlevolatile(evenunstable).Thereisonlyonesurfacethatisnt
volatiletheapplicationsurface.Theapplicationsurfaceisthesurfacethatnormaldraw
eventsdrawto.Therearebuiltinfunctionsthatwecanusetoworkaroundthevolatility
ofsurfaces.Usingthesefunctions,wecancreateourownsurfacesandmanipulate
them.Surfacescanbeusedforlotsofdifferentthings,buttheyaremostcommonly
usedforlightingeffectsandshadows.

Creatingasurfaceisveryeasyandcanbedonewiththissimplefunction.Thetwo
argumentsarethewidthandheightofthesurface.

Create Event

my_surface
=
surface_create
(
640
,
360
);

130

Becausetheyarevolatile,surfacescanberemovedfrommemoryatanytime.This
meansthatthereisnoguaranteethatthesurfacewillstillexistthenexttimethatwetry
toaccessit.Forthisreason,GameMakerLanguageprovidesafunctiontocheckforthe
existenceofasurfacebeforeittriestodoanythingwithit.

Draw Event

if(
surface_exists
(
my_surface
)){
// Work with surface
}else{
// Create the surface again
my_surface
=
surface_create
(
640
,
360
);
}

First,wechecktomakesurethatthesurfaceexists.Ifitdoes,wecanuseit.Ifnot,we
willneedtocreatethesurfaceagain.

Onceweknowthatthesurfaceisthere,wecanstartdrawingtoit.Todoso,weneedto
tellGameMakerthatwewanttodrawon our
surfaceandnottotheapplicationsurface.

Draw Event

if
(
surface_exists
(
my_surface
)){
// Tell GameMaker to draw on your surface
surface_set_target
(
my_surface
);

// Now we can draw on the surface


draw_circle_colour
(
32
,
32
,
16
,
c_red
,
c_red
,
false
);

// Tell GameMaker to reset to the application surface


surface_reset_target
();

// Make sure to draw the new surface in the room


draw_surface
(
my_surface
,
0
,
0
);

131

Afterwehavedrawntooursurface,wewillwanttoactuallydrawthesurfaceinthe
room.Remember,surfacesarehiddenuntilweactuallyshowthemtotheplayer.We
candothisusingthedraw_surface function.Thisfunctiontakesasurfaceasitsfirst
argument,withxandycoordinatesasthesecondandthirdarguments,respectively.

Now,wewillwanttomodifythewaythatwecreatethesurface.Thesurfaceshould
startasacleanslate.Wecandothisbydrawingablackbackgroundacrosstheentire
surface.

Draw Event

// Create the surface again


my_surface
=
surface_create
(
640
,
360
);

// Tell GameMaker to draw on your surface


surface_set_target
(
my_surface
);

// Now we can draw on the surface


draw_clear_alpha
(
c_black
,
0
);

// Tell GameMaker to reset to the application surface


surface_reset_target
();

Greatjob!Thosearethebasicsofusingasurface.Thereisonelastthingabout
surfacesthatweneedtodiscuss:Asurfacemustbedestroyedattheendofthegame
toavoidmemoryleaks.Thisisthefunctionwecancalltodothat:

Game End Event

if
(
surface_exists
(
my_surface
)){
surface_free
(
my_surface
);
}

SurfacesExampleGame
Forourveryfirstuseofsurfaces,Imgoingtoshowyouhowtocreateabasicdrawing
game.Thisisasimplewaytodealwithsurfaces,anditwillhelpyougetyourhands

132

dirtyinsomecodewithoutovercomplicatingthings.Youwontneedanyspritesor
backgroundstocreatethesimplegamejustoneroomandoneobject.Letsget
started!

Createanewobjectandnameitobj_paper .Thiswillbetheobjectweusetocontrol
everythinginourgame.AddaCreate Event totheobjectanddragovera
Code
Action .Whenthenewcodewindowcomesup,writethis:

Create Event

surface
=
noone
;
mouse_xprevious
=
mouse_x
;
mouse_yprevious
=
mouse_y
;

Itshouldbeeasytorecognizewhatthiscodedoes.Wearecreatingthreenew
variables.Surfaceissettonoone (becausewehaventcreatedthesurfaceyet)andthe
mousesprevioustwovariablesaresetthemousescurrentposition.Wewillusethese
fordrawingourlineslater.

NowthattheCreate Event Draw Event


isfinished,adda anddragoveranew,empty
Code Action .Thisisgoingtobethemostcomplicatedsection,butIllexplainitall.

Draw Event

// Local variables to shorten code


varmx
=
mouse_x
;
varmy
=
mouse_y
;
varmxp
=
mouse_xprevious
;
varmyp
=
mouse_yprevious
;

if
(
surface_exists
(
surface
)){
if
(
mouse_check_button(
mb_left
)){
surface_set_target
(
surface
);
draw_circle
(
mx
,
my
,
3
,
false
);
draw_line_width
(
mxp
,
myp
,
mx
,
my
,
8
);
surface_reset_target
();
}

133

draw_surface
(
surface
,
0
,
0
);
mouse_xprevious
=
mouse_x;
mouse_yprevious
=
mouse_y;
} else{
// We need to create it
surface
=
surface_create
(
640
,
360
);
surface_set_target
(
surface
);
draw_clear_alpha
(
c_white
,
1
);
surface_reset_target
();
}

First,wechecktoseeifthesurfaceexists.Remember,wedothisbecausetheyare
volatile(itmightnotexist).Ifitdoesntexist,thenwecreateitanddrawasolidwhite
colortotheentiresurface.Ifitdoesexist,wechecktoseeifthemousebuttonisbeing
pressed.Ifthemousebutton is
beingpressed,wedrawacircleatthecurrentmouse
positionandalinefromthelastmousepositiontothecurrentmouseposition.

Afterallthatisdone,wecall draw_surface toactuallydrawthesurfaceintheroom(on


theapplicationsurface)andupdatethemousespreviouspositions.Therearethree
argumentsthat draw_surface needs.Thefirstistheidofthesurface,andtheother
twoarethexandypositions,respectively,wherethesurfacewillbedrawn.Thatwasnt
toobad,wasit?

Lastly,wewillneedtodestroythesurfacewhenthegameends.Addanew Game End


Event andplacethiscodeintoit:

Game End Event

if
(
surface_exists
(
surface
)){
surface_free
(
surface
);
}

Thegameisnowreadyfortesting!Createaroom,putaninstanceof obj_paperinthe
room,andrunthegame.Weshouldhaveawhitepaperthatwecandrawonusingthe
mouse.Iftheroomislargerthanthesurfacesizeof640by360,thesurfacedoesnt
takeuptheentireroom,andthereisagrayareawherewecantdraw.Ifyouwant,you

134

canchangeyourroomsizetomatchthesurfacesize,butitisgoodforyoutoseethat
wearedrawingthesurfaceatposition(0,0)andthatitdoesnttakeupthewhole
screen.

135

Chapter14

Physics
UsingPhysicsinyourGame
TherearecasesinwhichyouwillnotwanttouseGameMakersbuiltinphysicssystem.
Youshouldntassumethatbecausethephysicssystemcanbereallycoolandfunthat
youshoulduseitinyourgame.Asageneralrule,youprobablywontwanttoadda
physicssystemtoyourgameunlessitwilladdtothegameplayinsomefundamental
way.Thatbeingsaid,usingphysicsisagreatwayforevennewGameMakerusersto
createlifelikeandamazingworlds.Letsgetstarted.

MakingaPhysicsWorld
Thefirstthingsthatweneedtodotousethephysicssystemiscreatearoom,clickthe
physicstabintheroomproperties,andcheckthelittleboxthatsaysRoomisPhysics
World.Ifweforgettocheckthisbox,ourphysicssystemwillnotwork.

Weshouldalsonoticethreeotherinputfieldsunderneaththecheckbox.Whatare
those?ThefirstistheGravityXvalue.Thiscanbesettocreatehorizontalgravityin
ourroom.Apositivevaluewouldbegravitytotherightandanegativevaluewouldbe
gravitytotheleft.ThenextistheGravityYvalue.Thiscanbesettocreatevertical
gravityinourroom.Apositivevaluewouldbegravityinthedownwardsdirectionanda
negativevaluewouldbeintheupwardsdirection.Iusuallysetmygravitytoabout60in
theGravityYvalue.ThelastinputfieldallowsustochangethePixelstoMeters
conversionrate.Ipersonallyleavethatalone.

PhysicsObjectTypes
Inthephysicssystem,therearethreemaintypesofobjectsthatwewillbecreating:
staticobjects,dynamicobjects,andkinematicobjects.Hereisashortdescriptionof
eachtype:

StaticObjects
Staticobjectswillmakeupthewallsofourphysicsgame.Theseobjectsaresolidand
cannotbemovedbycollisionswithotherobjects.Theywillalsobeunaffectedby
gravity.

136

DynamicObjects
Dynamicobjectswilllikelybethemostcommoninourgame.Thesearenormalphysics
objectsthatcanbepushed,pulled,androtatedbasedonforces.Ifwehaveaplayer
object,itshouldprobablybeadynamicobject.

KinematicObjects
Kinematicobjectsarelikestaticobjects,onlywecanmovethemaroundintheworld
usingtheirxandycoordinates.Anexampleofakinematicobjectwouldbeamoving
platformorelevator.

MakingOurFirstPhysicsObject
Nowthatourfirstphysicsworldisready,itstimetocreateourphysicsobject.Thisis
actuallyeasierthanyoumightthink.Quicklycreateasquarespriteandcenteritsorigin.
Afterdoingthat,createanewobjectandassignthesquarespritetoit.Intheobjects
properties,rightundertheSolidcheckbox,thereisaUsesPhysicscheckboxcheck
thatbox.Anadditionalpropertieswindowwillshowup.Imgoingtoexplaineach
additionalpropertyindetail.

CollisionShape
The collisionshape oftheobjectisacollectionofpointsandedgesthatmakeupthe
collisiondetectionboundaryfortheobject.Therearethreetypesofshapesthatwecan
Circle
setitto: Box
, ,andShape.CircleandBoxareselfexplanatory.Shapeisnttoo
complicated:wecandrawourowncollisionshape.Thisisverypowerfulandeasytodo.
JustclickthebuttonthatsaysModifyCollisionShapetostartcreatingacustomshape.
WemaywanttoclicktheModifyCollisionShapebuttonevenfortheCircleandBox
shapesGameMakertriestobesmartwhencreatingthem,butitoftenseemstoget
themwrong.Ifitdoesgetthemwrongandwedontchecktheshapeourselves,wemay
getstrangecollisionerrors.

Density
Now,letstalkaboutthesecondproperty,the densityoftheobject.Thisvalueisusedto
calculatethemassoftheobject,whichisusedbythephysicssystemformomentum
calculations.Alargervalueherewillmakeourobjectheavier.Whenwesetthatvalueto
zero,itgivestheobjectaninfinitedensity(whichishowthesystemcreatesstatic
objects).

Restitution
Restitution isthethirdpropertywecanset.ThisvalueisalittlehardertoexplainIt
representshowbouncyourphysicsobjectis.

137

CollisionGroup
Asourfourthproperty,wehave collisiongroups .Collisiongroupscanbeusedto
preventcertainphysicsobjectsfromcollidingwithotherphysicsobjects.

LinearDamping
Nowforpropertynumberfive.ThephysicsengineinGameMakerisattemptingto
simulatetherealworldthe lineardamping valuehelpsachievethis.Intherealworld,
objectsintheairexperienceairresistance,andthisslowsthemdown(forexample,
whileinflight).InGameMakersphysicsengine,thereisnoairresistance.Thisvalue
simulatesairresistance.Ifweweretosetthisvaluetozero,theobjectwouldbehaveas
ifitweremovingaroundinavacuum.

AngularDamping
Oursixthproperty, angulardamping ,workslikelineardamping,onlyitoperatesonthe
rotationofanobject.Ifwesetthistozeroandtheobjectstartsrotatingandnever
interactswithanotherobject,itwillrotateforever.Ifwegivethispropertyavalue,the
rotationoftheobjectwillslowdownovertime.Generally,wewillwanttohavesome
valuehere,becauseintherealworld,airresistancewouldslowthespinningofan
object.

Friction
Lastly,wehave friction
.Thisoneisprettyobvious.Thisvaluecontrolsthefrictionthatis
subtractedfromanobjectsmotionwhenitistouchinganotherobject.

ForcevsTorque
InGameMakersphysics,thereareafewdifferentwaysthatwecanmoveanobjectin
theroom.Twocommonwaysaretoapplya force torque
ora toit.Itisimportantto
understandthedifferencebetweenthesetwomethods.Imaginethatwebuildacarin
ourgameroom.Wecouldapplyaforcetothecartomoveit,orwecouldapplyatorque
tothetirestomoveit.Whatisthedifference?Well,applyingaforcetothecarislike
pushingonthecarfrombehind.Thecarwillstarttorollandwemightevenbeableto
movethecarreallyfastifweapplyenoughforce,butwewillneverbeabletospinout
ourtires.Thebetter(andmorerealistic)methodistoapplyatorquetothetires.This
spinsthetiresandmovesthecarforward.Ifwehaveenoughtorquetoovercomethe
frictionthatwehaveset,thenwewillevenbeabletospinourtiresoutandburnsome
rubber!

138

BuildingaSimpleCarGameUsingPhysics
ImreallyexcitedtoteachyouhowtobuildasimplecarusingGameMakersphysics
engine.Forthisfirstexample,wewillavoidmodellingthesuspensionbecausethatwill
complicatethings.Wewilladdsomerandomterraingenerationtomakethingsmore
interesting.

Forthisexample,wewillneedtwosprites,fourobjects,andoneroom.

CreatetheSprites
Forthefirstsprite,createanew8024imageandthenfilltheentireimage.After
creatingtherectanglespritenameit spr_car_body andclickthecenterbuttonto
centertheorigin.Forthesecondsprite,createanew4040imageanddrawasolid
circleusingthecircletool.Startatthetopleftcornerandfinishatthebottomright
corner.Thiswillcreateaperfectcirclethatusesasmuchoftheimageaspossible.
Namethissprite spr_car_tire andclickthecenterbuttontocenteritsorigin.

CreatetheObjects
Thefirstobjecttocreateisthe obj_collision_parent object.Thisobjectallowsusto
havecollisionbeinheriteduniformlyacrossourobjects(wedonthavetoadda
Collision Event toalloftheobjectsinourgame).Thisobjectdoesntneedasprite.
Addanew Collision Event totheobjectandhaveitcollidewithitself
(obj_collision_parent ).Wetechnicallydontneedanycodeinthiseventbecause
thephysicsenginewillhandleallofthecomplicatedbehavior.Wedohavetoputan
actioninhere,though,orGameMakerwilldeletetheevent.Dragovera Code Action
andputthiscommentinit.

/// Detect the collision

ThiscommentjustkeepsGameMakerfromdeletingourcollisionobjectparent.

Now,wecancreatethecarbodyobject.Clicktheaddnewobjectbuttonandname
obj_car_body
thenewobject spr_car_body
.Setthespriteto ,givetheobjectaparent
obj_collision_parent
of ,andclicktheUsesPhysicscheckboxintheobjects
properties.Now,wecansetthecollisionshapetoBoxandmodifythecollisionshape
tomakesurethatitlinesupwiththeassignedsprite.Oncewehavedonethat,use
thesevaluesforthephysicsproperties.

139

Physics Properties

Density: 0.5
Restitution: 0.1
Collision Group: 0
Linear Damping: 0.1
Angular Damping: 0.1
Friction: 0.2

Oncewehavefinishedsettinguptheobjectpropertiesandthephysicsproperties,we
Step Event
arereadytostartaddingsomecodetothisobject.Addanew andplace
thiscodeinit.

obj_car_body: Step Event

/// Force the view to follow the car


view_xview
[
0
]=
x
-
view_wview
[
0
]/
2;
view_yview
[
0
]=
y
-
view_hview
[
0
]/
2;

Thiscodewillforcetheviewthatwecreateintheroomtofollowthecarbody,evenif
thecarbodyactuallyleavestheroom.Wewilladdmorecodetothecarbodyina
minute,butfornow,letsmoveontothecartireobject.

Createanewobjectandnameit obj_car_tire spr_car_tire


.Setitsspriteto ,giveit
obj_collision_parent
aparentof ,andchecktheUsesPhysicscheckboxinthe
objectproperties.Now,wehaveaccesstothephysicsproperties.Setthecollision
shapetoCircle,modifythecollisionshapetomakesurethatitmatchesthesprite,and
then,setthedifferentphysicspropertiesusingthesevalues.

Physics Properties

Density: 0.5
Restitution: 0.1
Collision Group: 0
Linear Damping: 0.1
Angular Damping: 0.1
Friction: 30

140

Makesuretogetthefrictionright.Ifthefrictionissuperlow,ourtireswillspinoutonthe
terrainandwontbeabletogetanytraction.Ourphysicspropertiesarereadynow,and
Create Event
itstimetoadda .Placethiscodeinthe Create Event forourtire
object.

obj_car_tire: Create Event

/// Initialize the tire


spd
=
1000;

Thisjustcreatesaspeedvariablethatwecanuselaterwhenmovingthetires.Notice,I
speed
didntcallthevariable .Thisisbecause speed isreservedbyGameMakerand
shouldnotbeusedwiththephysicssystem.

Now,itstimetoactuallyaddthemovementcodetothetires.Aswediscussedjusta
fewparagraphsago,weneedtodecideifwewanttouseaforceoratorque.Ifweuse
aforce,itwouldsimulatesomeonepushingthecarfrombehind.Ifweuseatorque,it
wouldsimulateanenginespinningthetires.Wewanttouseatorque.Addanew
Keyboard Right Key Event toourtireobjectandaddthiscode:

obj_car_tire: Keyboard Right Key Event

/// Add a clockwise torque to the wheel


physics_apply_torque
(
spd
);

Now,createanew
KeyboardLeftEvent
andaddthiscodetoit.

obj_car_tire: Keyboard Left Key Event

/// Add a counter-clockwise torque to the wheel


physics_apply_torque
(-
spd
);

Thetirewillnowspinwhenwepressleftorright!OneofthethingsthatIloveabout
GameMakersphysicssystemishowitsimplifiesthecodesomuch.Wejustsetthe
rulesfortheworldandthephysicsenginehandlestherest.Ourtiresarenowfinished,

141

Create Event
andwecangobacktothecarbodyobject.Openitupandadda .Inthe
Create Event,addthiscode:

obj_car_body: Create Event

/// Initialize the car


varhalf
=
sprite_width
/
2;

vartire
=
instance_create
(
x
-
half
+
12
,
y
,
obj_car_tire
);
vartx
=
tire
.
x;
varty
=
tire
.
y;
physics_joint_revolute_create
(
id
,
tire
,
tx
,
ty
,
0
,
0
,
0
,
0
,
0
,
0
,
0
);

vartire
=
instance_create
(
x
+
half
-
12
,
y
,
obj_car_tire
);
vartx
=
tire
.
x;
varty
=
tire
.
y;
physics_joint_revolute_create
(
id
,
tire
,
tx
,
ty
,
0
,
0
,
0
,
0
,
0
,
0
,
0
);

Inthiscode,wesometimesusehalfthewidthofourcarbodysprite.Thisnumberwill
helpustoplacethetirecorrectly.Afterdoingthat,wecreatethefirsttireandattachit
usingthe physics_joint_revolute_create function.Thesecondtireiscreatedand
attachedinthesameway,butwepassdifferent tire.x andtire.yvalues.

The physics_joint_revolute_create functionisonethatwehaventdiscussed


before.Thisfunctionattachesphysicsobjectsor(fixtures)toeachotherwitharotatable
joint.Thefirstargumentthatwepassistheidofthecarbody.Thenextistheidofthe
cartire.Becausewestoredtheidofthetireinstanceinthelocaltirevariable,wecan
usethat.Then,wepassthexandypositionsoftheoriginofthejoint.Weareusingthe
xandypositionsofthetirebecausewealreadycreatedthemrightwherewewantthe
jointtobe.Afterthat,thereareatonofzeros.Thesedifferentargumentshavetodo
withlimitingtherotationofthejointoraddingamotorspeedtothejoint.Thelastoneis
whetherornotwewantthetiretocollidewiththecarbody.Wesetthisto0(orfalse)
becausewedonotwantthemtocollide.Theywillbetouching,soacollisionwould
messupourcar.

Oncewehaveaddedthis Create Event ,ourcarisreadyforthephysicsworld!The


onlyproblemisthatourworlddoesnthaveanygroundtodriveon.Itstimetocreate
thegroundobjectthatwillgenerateourworld.Wearealsogoingtohavethisground

142

objectdrawthephysicsworldwithaneatfunctioncalledphysics_world_draw_debug .
Weprobablywouldntwanttousethisfunctionforanactualgame,butitwillworkwellin
thiscase,andwecanuseittobetterunderstandhowdifferentphysicsobjectswork.

Createanewobjectandnameit obj_ground .Givethenewgroundobjectaparentof


obj_collision parent .WedontneedtocheckUsesPhysicsforthisobject,
becausewewillbedoingthatwithcodethistime.Adda Create Eventandplacethis
codeinit.

obj_ground: Create Event

/// Initialize the ground


flags
=
phy_debug_render_shapes |
phy_debug_render_joints |
phy_debug_render_coms |
phy_debug_render_obb;

varxx
=-
100;
varyy
=
0;

varfix
=
physics_fixture_create
();
physics_fixture_set_chain_shape
(
fix
,
false
);
repeat
(
100
){
physics_fixture_add_point
(
fix
,
xx
,
yy
);
xx
+=
50
+
random
(
150
);
yy
+=-
32
+
irandom
(
64
);
yy
=
median
(
64
,
yy
,
room_height
-
64
);
}

physics_fixture_set_density
(
fix
,
0
);
physics_fixture_set_restitution
(
fix
,
0.5
);
physics_fixture_bind
(
fix
,
id
);
physics_fixture_delete
(
fix
);

Atfirstglance,thiscodecanlookdaunting.Illexplainitlinebylinetohelpyou
understanditbetter.Atthetop,wearecreatinganinstancevariablecalled flags
.This
variableholdsthedifferentrenderflagsthatwewantourgroundobjecttousewhenit
drawsthephysicsworld.Asyoucansee,eachflagdescribesadifferentpartofthe

143

physicsworldthatwewantittodraw.Aftercreatingthe flags variable,wecreatetwo


localvariablescalledxxandyy.Thesevariablesholdthestartingpointforourground
generation.Aswecreatethedifferentgroundsegments,wewilladdandsubtractfrom
thesevalues.Wecanseethatthexxvalueis100,whichisprettyfartotheleftof
whereourroomstarts.Thisistohelpmakesurethatwedontcreatethecarobjecttoo
closetotheedgeofourground.

Now,wearereadytocreateourfirstfixture.Well,actually,youhavebeencreating
fixturesalready,youjustdidntdoitincode.WhenyouchecktheUsesPhysicsbutton
inanewobject,ittellsGameMakerthatyouwanttoattachafixturetothatobject.When
yousetthedifferentphysicspropertiesintheobject,youareactuallysettingthe
propertiesofthefixturethatisattachedtothatobject.

Fixturesarethephysicsrepresentationofyourobjectinthegame,sortoflikehow
spritesarethevisualrepresentationofyourobjectinthegame.Thecoolthingisthatwe
canbothcreateafixtureandsetitspropertiesusingcodeinsteadofusingthecheckbox
andfillingoutthephysicspropertiesfields.Thisgivesusmorecontrolovertheshapeof
thefixtureandallowsustogeneratethefixturemanually.Inourcode,youcansee
wherewecreatethelocalvariable fixandwhereweuse physics_fixture_create
andassigntheidofournewfixturetothe fixlocalvariable.Oncewehavecreatedthe
fixture,wecansetitsshapeusing physics_fixture_set_chain_shape .Thechain
shapeallowsustoaddpointstoourfixtureanditwillconnectthedots,creatinga
chainor,inourcase,theground.Thereareotherfixtureshapesthatyoucanusehere
aretheirfunctions:

physics_fixture_set_polygon_shape
physics_fixture_set_circle_shape
physics_fixture_set_box_shape
physics_fixture_set_edge_shape
physics_fixture_set_chain_shape

Ourfixtureshapehasbeensettochain.Thetimehascometoaddpointstothefixture.
Thewayweaddthesepointswilldeterminehowroughthegroundisandhowgoodthe
groundlooks.Asyoucansee,Iusedarepeatloopstatementsothatwecancreate100
pointsalongourgroundfixture.Insidetheloop,wecreatethefixturepointatposition
(xx,yy)andthenwerandomizethenextyypointandaddaslightlyrandomvaluetothe

144

nextxxpoint.Werealsoclampingtheyyvaluewiththemedianfunctiontomakesure
thatourpointsstayinsidetheroomheight.

Afterwecreateallofthepointsforourfixture,wesetthedensityandrestitution.

Thelasttwothingsthatwedoareattachthefixturetoourobjectusing
physics_fixture_bind anddeletethefixturetemplatethatwecreated.Beaware,
deletingthefixturedoesnotdeletetheactualfixturethatweattachedtotheobject.It
justdeletesthefixturetemplatethatwehavecreated.

Now,adda Draw Event andaddthiscodetoit.Italkedalittlebitaboutthisfunctiona


fewparagraphsback.Thisfunctionshouldonlybeusedasamethodtodebugphysics,
butforthisexample,itsimplifiestheconceptandallowsustoseethegroundthatwe
generated.

obj_ground: Draw Event

/// Draw the physics world


physics_world_draw_debug
(
flags
);

Youarenowsecondsawayfromhavingaworkingcarphysicsgamewithrandomly
generatedterrain.Thefinalstepistocreatetheroom.

Createanewroomandnameit rm_carworld .Clickthesettingstabtosettheroom


widthto640andtheheightto360.Clickthephysicstab,checkRoomisPhysics
World,andsettheGravityYvectorto30.0.Now,placethecarobjectandtheground
objectintheroom.Next,clickontheviewstabandcheckEnabletheuseofViews
andVisiblewhenroomstarts.SettheViewInRoomwandhvaluesto640and360,
respectively.ThensetthePortonScreenwandhvaluesto1280and720,
respectively.

Thephysicsgameisnowreadytoplay!Thisisoneofmyfavoritephysicsexamples.
Thereareafewtrickyparts,butbythispointinthebook,youshouldatleasthavean
ideaofwhateachlineofcodedoes.Besuretoshowthisexampleofftoallofyour
friendsandfamily.Youhaveworkedhardandlearnedsomuch.Itstimetoenjoyyour
workalittle.

145

Chapter15

OnlineMultiplayer
Foundation
Thereissomuchtocoverregardingonlinemultiplayer.Thischapterisgoingtobelong,
andeventhen,itwillonlyscratchthesurfaceofmakinganonlinegame.Itwill,
however,teachyouthebasicsandgetyoustartedwithonlinemultiplayer.Wewillnot
bebuildinganyMMOs,butthebaseyoureceiveherewillhelptoprepareyouforlarger
onlinemultiplayerprojects.

Thefirstexampleprojectinthischapteronlycoverstheabsolutebasicsofsettingupa
server,aclient,andthecommunicationbetweenthem.Inordertocompletethisfirst
exampleandlearnfromit,youneedtohaveagoodunderstandingofGameMakers
datastructuresandtheiraccessors(especiallymapsandlists)aswellasthedifferent
componentsofanonlinegame.Iwillcoverthecomponentshere,butifyouare
strugglingwiththedatastructures,besuretorereadChapter4:ArraysandData
Structures.

ComponentsofOnlineGames
Thereareseveraldifferentwaystohaveonlinegamescommunicate.Wewillbeusing
theclientservermodelhere.Alternatively,youcanhavetwoclientsconnecttoeach
other,butIwillnotbecoveringthatstructure.

Server
Theserverisgenerallyacomputer/programsystemthatperformsprocessingforother
computers.InGameMaker,theservercanbeaseparateprogramthatturnsyourlocal
computerintoaminiserver.Thisprogramwillhandleconnections,disconnections,and
datatransferstoandfromthedifferentgameclients.Wecancreateaserverin
GameMakerLanguagelikethis.

vartype
=
network_socket_tcp;
varport
=
8000;
max_clients
=
4;
server
=
network_create_server
(
type
,
port
,max_clients
);

146

Thefirstfewlineshereareusedtocreatedescriptivevariablesthatwecanpassasthe
parametersinthe network_create_server
function.Whenwecallthe
network_create_server function,itwillreturnauniqueidthatcanbeusedtoaccess
thecreatedserver.Asyoucansee,weareassigningthatuniqueidtotheinstance
variableserver .Thereareafewdifferentoptionsfortheservertype.Heretheyare:

network_socket_tcp
network_socket_udp

network_socket_tcp
Inthefirsteditionofthisbook,Iwillonlybecovering .AUDP
socketwillgenerallybefaster(itdoesntspendtimeerrorcheckingthepackets),but
TCPismorereliable.Therearemanyotherdifferences,buttheyarebeyondthescope
ofthischapter.

Client
The client
isacomputerthatconnectstotheserverand(hopefully)communicateswith
it.Often,therewillbemanyclientsconnectedtooneserver.InGameMaker,theclientis
aprogramthatconnectstotheserverandhandlesmessagessentfromtheserver.It
canalsosendmessagesback.ThisishowwecancreateaclientinGameMaker
Languageandattempttoconnectittotheserver.Unfortunately,thiswontconnectyet
becausewehaventhandledtheconnectionontheserverend.Thisishowwecan
attempttoconnectfromtheclientside.

vartype
=
network_socket_tcp;
varip
="
000.000
.
0.000
";
varport
=
8000;
socket
=
network_create_socket
(
type
);
connection
=
network_connect
(
socket
,
ip
,
port
);

Here,wecreatevariablesforthetype,IP,andportinordertomakethecodemore
readable.Ifyouarerunningtheserverfromyourowncomputer,thenyouwillneedto
gettheIPaddressofyourowncomputer.IwilltalkalittlemoreaboutIPaddresses,
ports,andsocketsinjustaminute.First,lookatwherewecallthe
network_connect
function.ThisfunctionwilltrytomakeaconnectionoverasocketusingadefinedIP
addressandport.Youcanchoosetheportyouwouldliketouse,butIjustuse 8000.

147

The network_connect functionwillreturnauniqueidthatcanbeusedtoreferencethe


connection.Ifthefunctionisunabletoestablishaconnection,itwillreturnanumber
lessthan0.

Sockets
Asocket representsatwowayconnectionbetweentheclientandtheserver.The
serverwillactuallyneedtokeeptrackofmultiplesockets(becauseitwillbeconnected
tomultipleclients).Youcanthinkofthesocketasthetubethatconnectstheserverto
theclientandallowsdatatobetransferred.Youhavealreadyseenhowwecancreate
thesocketintheclientusing network_create_socket ,butyoustillneedtoseehow
theservercangetaccesstothissocketaswell.Illtalkmoreabouthowthatisdonein
theactualexamplefile.

IPs
An IPaddress isasetofnumbersthatrepresentsauniqueaddressforacomputer.The
numbersareseparatedbyperiods.AnIPaddressissimilartoyourphysicalhome
address,butusedforcomputers.Ifyouhaveanewfriendthatneedstofindyourhome,
yougiveyourhomeaddress.Ifyourfriendscomputerneedstofindyourcomputer(for
example,toplayagame),yougiveyourIPaddress.

Ofcourse,itisalittlemorecomplicatedthanthat,andinthisexample,theconnection
willonlyworkoveralocalnetwork(youandyourfriendcanplaytogether,butyouboth
needtobeconnectedtothesamelocalnetwork).InordertogetyourcomputersIP
address,youcansearchcmd(whichwillruntheCommandPrompt)andtypethe
commandipconfig.Thiswilllistinformationaboutyourinternetconfigurations,
includingyourIPv4Address.TheIPv4addressistheonethatyouwillwanttousefor
thistutorial.BeawarethatyourroutermayassignyouadifferentIPaddressfromtime
totime,soyoumayneedtoupdatetheIPaddressthatyourclientusestoconnectto
theserver.

Ports
The portnumber isusedinconjunctionwithyourIPaddress.Iused8000inthis
exampleandthatshouldworkforyou.Ifitdoesnt,youcantrysomedifferentnumbers.
Itdoesntmattertoomuch,aslongasyourclientandserverareusingthesamenumber
andthereisntanyotherprogramusingyourchosenportnumber.

Buffers
Thelasttermthatyouneedtohaveanunderstandingofis buffer
.Abufferissimilartoa
variablethatcanbesentthroughthesocket,eitherfromtheservertotheclientorfrom

148

theclienttotheserver.Bufferstakedatainsequentialorderandthatdataisreadback
outofthebufferinthesameorder.

BasicExample:CreatetheStars
Nowthatwehavegoneoverafewofthebasicterms,youarereadyforyourfirstonline
gameexample.ThisisthesimplestexamplegamethatIcouldcomeupwith.
NetworkinginGameMakercanbeverycomplicated,andIwanttomakesureyou
understandthebasics.

CreatingTheServer
Tostartthingsoff,wearegoingtobuildabasicserver.InthisGameMakerexample,
youwillneedtocreatetwonewprojectfiles.Thefirstonethatyoucreateshouldbe
calledServer
andthesecondshouldbecalledClient .Onceallofthecodeisinplace,
youwillruntheserverfirst,andthen,whenyouruntheclient,itwillconnecttothe
server.

Openuptheserverprojectonceyouhavecreateditandaddanewobjecttoit.Name
obj_server
thisobject andaddaCreate Event toit.DragoveraCode Action and
addthiscodetoit:

obj_server: Create Event

/// Initialize the server object


vartype
=
network_socket_tcp;
varport
=
8000;
max_clients
=
1;
server
=
network_create_server
(
type
,
port
,max_clients
);

socket
=
noone;

Goodjob!Thatcodeshouldlookfamiliartoyou.Wearesettingupthenetworktype,the
port,themaximumnumberofclients,andthenwecreatetheserver.Wearealso
creatinganinstancevariablecalled socket thatwillstorethereferencetoour
connectedclient.Inanexamplewithmorethanonesocket,youwillwanttocreatea
ds_list
tostoreyoursockets,butforthissimpleexample,wedontneedtodothat.

Nowthatwehavecreatedtheserver,wewillwanttomakesureitisdestroyedwhen
thegameends.Adda Game End Event toourserverobjectandplacethiscodeinit:

149

obj_server: Game End Event

/// Destroy the server


network_destroy
(
server
);

Okay,uptothispoint,youareprobablyfollowingthetutorialandthinkingthatitispretty
hasbeen
easy.Well,sofarit prettyeasy.Nothingtoofancyandnothingyouhavent
seenbefore.Well,nowthingsareabouttogetalittletricky,sopaycloseattention.This
nextbitofcodewillrequireboththatyouunderstand ds_maps andthatyouunderstand
thetermsthatIwentoverinthestartofthechapter.Dontworrythough,youwillgetit
asIstepyouthroughit.

Addan Asynchronous Event totheserverobjectandselectthe


Networking Event
fromtheasynchronoussubmenu.Thiseventwillkeeptrackofconnections,
disconnections,anddatathatissenttoourserver.Illshowyouhowthisworks.Add
thiscodetotheevent:

obj_server: Asynchronous Networking Event

/// Check for the client


vartype_event
=
async_load
[?
"
type
"];

switch
(
type_event
)
{
casenetwork_type_connect:
// Add the client to the socket
if
(
socket
==
noone
)
{
socket
=
async_load
[?
"
socket
"];
}
break;

casenetwork_type_disconnect:
// Remove the client from the server
socket
=
noone;
break;

casenetwork_type_data:

150

// Grab incoming data and handle it


varbuffer
=
async_load
[?
"
buffer
"];
buffer_seek
(
buffer
,
buffer_seek_start
,
0
);
scr_received_packet
(
buffer
);
break;
}

Thatisabigchunkofcode!Letstakeitonestepatatime.Inthefirstline,weare
gettingareferencetothetypeofAsynchronous Networking Event thatisbeingsent.
The async_load ds_map isaspecialmapcreatedinthiseventonly.Ithasallofthe
informationthatweneedaboutthedatacomingin,butitcannotbeaccessedoutsideof
thisevent.Oncewegettheeventtype,wecanhandlethedataaccordingly.

Therearethreetypesofeventsthatcouldoccur.Thefirstisthe
network_type_connect .Thismeansthataclientisattemptingtoconnecttoour
server.Inthiscase,wewillwanttogetareferencetothatclientwecandothatby
usingthe async_load ds_map again,butinsteadofaccessingthetypekey,weare
goingtoaccessthesocketkey.Thiswillgiveusareferencetotheclient.Wejust
assignthatinformationtooursocketinstancevariableandwearedone.

Thesecondeventtypeisthe network_type_disconnect event.Thiseventfireswhen


aclientdisconnectsfromtheserver.Thisistheeasiesteventtohandle.Wearejust
goingtosetourinstancevariable socket noone
backto .

Thethird,andlast,eventtypeisthenetwork_type_data event.Itisthemostfunofthe
threeandtakesthemostwork,whichyoucanseebythefactthat,attheendofour
casestatement,wearecallingascriptthathandlesthedatawegetfromtheevent.The
firstthingwedoiscreatealocalvariablecalled buffer andassignthedatafromthe
bufferkeyinourasync_load ds_map toit.Rememberthatbuffersarelikepackages
thatcontainthedatathatwillbesentorreceivedoverthenetwork.Aftergettingthe
buffer,wewanttousethe buffer_seek functiontosetourreadlocationtothestartof
thebuffer.Datafromabufferisreadinsequence.Eachtimeyoureaddatafromthe
buffer,thedatareadisactuallyremovedfromthebuffer.Illexplainthisinmoredepth
oncewearereadytotalkaboutthecodecontainedinthescript.Oncewesetthebuffer
readinglocationtothestart,wearereadytosendthatinformationtothescript.
Normally,youwouldsendthesocketinformationaswell,sothatyouknewwhichclient

151

sentthedata,butbecauseweonlyhaveoneclient,wedontneedtoworryaboutthatin
thisexample.

Itstimetowritethecodeinourscript.Createanewscriptandnameit
scr_received_packet .Openupthescriptandtypethisblockofcode.

scr_received_packet

///scr_received_packet(buffer)
varbuffer
=
argument
[
0
];
// buffer is (id,x,y)

varmessage_id
=
buffer_read
(
buffer
,
buffer_u8
);
// After first read buffer is now (x,y)

switch
(
message_id
)
{
case
1:
varmx
=
buffer_read
(
buffer
,
buffer_u32
);
// buffer is now (y)

varmy
=
buffer_read
(
buffer
,
buffer_u32
);
// buffer is now empty ()

//Use the data from the buffer to create the click


instance_create
(
mx
,
my
,
obj_mouse_click
);
break;
}

Iveaddedextracommentstohelpyoutounderstandhowbufferreadingworks.The
firstthingthatwedoiscreatealocalvariablethatgrabsthebufferfromtheargument
thatwepassedtothescript.Bufferscancontainwhateverwewantthemto.Wehavent
actuallysentanybuffersfromtheclientyet,butIdecidedthatourbufferwillcontaina
messageidthatdetermineswhattypeofmessageitis.Then,thexandyvaluesare
readifthemessageidis1.Amessageidof1,inthiscase,meansthatwewantto
createamouseclickobjectatthepositionofthenexttwopiecesofdatareadfromthe
buffer.

152

Afterwehavereadthemessageidfromthebuffer,youcanseeinthecomment
afterwardsthatthebuffernolongercontainsthatinformation.Allthatisleftisthexand
ycoordinatesofthemouseclick.Impassingtheidthroughaswitchsothatlater,ifyou
wantedtoaddothertypesofdatathatdidotherthings,youcouldjustpassadifferentid
throughthebufferandthenaddthatcasetotheswitch.Onceweconfirmthatthe
messageidisactuallysetto1,wereadboththexpositionandtheypositionfromthe
bufferandassignthosevaluestolocalvariablescalled mx
andmy
,respectively.Youcan
alsoseebymycommentsthatonceeachchunkofdataisread,itisremovedfromthe
buffer(aftertheyvalueisread,thereactuallyisntanymoreinformationinourbuffer).
Oncethebufferisempty,wehaveallofthedatathatweneedfromtheclienttocreate
themouseclickobjectandbreak.

Thelastthingthatweneedtodotofinishoursimpleserverprojectiscreatethemouse
clickobject.Addanewobjectandnameit obj_mouse_click .AddaDraw Event toit
andaddtheselinesofcode:

obj_mouse_click: Draw Event

/// Draw the click as a circle


draw_circle_colour
(
x
,
y
,
4
,
c_white
,
c_white
,
false
);

Thiscodewilljustdrawacirclewheretheobjectissothatwecanseeit.Makesureto
createaroomandaddyourserverobjecttoitbeforeyoutestthegame.

YourfirstGameMakerLanguageserverisupandreadytogo!Rememberthatthisis
onlythefirsthalfoftheexample!Eventhoughtheserveriscreated,readytoconnectto
aclient,andreadytoreceivedata,westillhaventcreatedtheclientthatwillconnectto
theserverandthensendthatdatawhensomethinghappens.

CreatingtheClient
Becausethisexampleissobasic,theclientisquiteabitsimplerthantheserver.Ina
morecomplexexample,yourclientmightbealmostascomplicatedastheserver.Ina
laterexample,Iwillshowyouhowyoucanuseoneprojectfileforboththeclientand
theserver.First,though,letsgetstartedonourbasicclient.

Onceyouhavetheclientprojectcreated,addanewobjecttoitandnameit
obj_client Create Event
.Thisclientobjectwillneeda Create Event
.Addthe ,drag
overaCode Action andaddthiscodeblocktoit.

153

obj_client: Create Event

/// Initialize the client


vartype
=
network_socket_tcp;
varip
="
000.000
.
0.000
";
varport
=
8000;
socket
=
network_create_socket
(
type
);
connection
=
network_connect
(
socket
,
ip
,
port
);

varsize
=
1024;
vartype
=
buffer_fixed;
varalignment
=
1;
buffer
=
buffer_create
(
size
,
type
,
alignment
);

Youveseenthefirstpartofthiscodeearlierinthechapter.Westartoffbydefiningour
type,IP,andport.Oncethisisdone,wecreatethesocketandtheconnection.Once
again,ifyoudon'tknowtheIPaddressofyourcomputer,youcanusethecomment
ipconfiginthecommandpromptandlookunderIPv4tofindit.Aftercreatingthe
socketandtheconnection,wecreateabuffertouseforsendingdata.Wegivethe
buffer_fixed
bufferasizeof1024(1kB),atypeof (thiskeepsthesizeofthebuffer
fromchanging),andanalignmentof1.Thealignmentreferstothebytealignmentofthe
data.Thereareotherbuffertypes,butforthisexample,wedontneedtoworryabout
them.

Nowthatourconnectionhasbeeninitializedandwehaveabuffercreatedandreadyfor
communication,thetimehascometoactuallysenddata.Forthisexample,wewillsend
datatotheserverwhentheuserclickstheleftmousebuttonsomewhereintheroom.
Addanew Mouse > Global > Global Left Pressed Event toourclientobjectand
addthisblockofcodetotheevent:

obj_client: Global Left Pressed Event

instance_create
(
mouse_x
,
mouse_y
,
obj_mouse_click
);
buffer_seek
(
buffer
,
buffer_seek_start
,
0
);

// Write the message id

154

buffer_write
(
buffer
,
buffer_u8
,
1
);

// Write the mouse x position


buffer_write
(
buffer
,
buffer_u32
,
mouse_x
);

// Write the mouse y position


buffer_write
(
buffer
,
buffer_u32
,
mouse_y
);

network_send_packet
(
socket
,
buffer
,
buffer_tell
(
buffer
));

Thefirstthingthatwedoiscreatethemouseclickobject.Wewillhaveamouseclick
objectinthisprojectthatisexactlyliketheoneinourclient.Thisisgenerallynotagood
waytodothingsbecauseyouwillhaveduplicatecode,butitsokayherebecausethis
isasimpleexample.Inthefinalexampleinthischapter,Iwillshowyouabetterwayto
dothis.Normally,youwilleitherhaveoneprojectthatworksasboththeserverandthe
client,oryouwillhavemultipleclientsconnectingtothesameserver,whichwilljust
relaytheinformationtoandfromtheclients.Afterwecreatethemouseclickobject,we
setthewritepositionofthebuffertotheverystart.Then,westartwritingtothebuffer.
Thefirstthingthatwewriteisthemessageid.Next,wewritethe mouse_x position.
Finally,wewritethe mouse_y
position.LikeImentionedearlier,wecansendwhatever
datathatwewantandinanyorderinthisbuffer,butourserverneedstoknowhowto
handleit.Wealreadywrotethecodethatallowsourservertohandledatalikethis.After
wehavewrittenthisinformationtothebuffer,wecanusethe network_send_packet
functiontosendittotheserver.Weneedtopassthreeargumentstothisfunction.The
firstisthesockettosendthedataover,thenextisthebuffer,andthelastisthesizeof
thebuffer.Youcanusethe buffer_tell functiontofindthesizeofabuffer.

Ourclientisalmostready,butwestillneedtodotwothings.First,weneedtoadda
Game End Event obj_client
tothe tomakesuretodestroythedynamicdatathatwe
dontneedanymore.Addthe Game End Event andplacethiscodeinit.

obj_client: Game End Event

network_destroy(socket);
buffer_delete(buffer);

155

Afterthat,weneedtocreatethemouseclickobjectintheclientprojectaswell.Youcan
justcopythecodeoverfromtheserverproject,buthereitisagain:

obj_mouse_click: Draw Event

///Draw the click as a circle


draw_circle_colour
(
x
,
y
,
4
,
c_white
,
c_white
,
false
);

TestingtheExample
Now,wearefinallyreadytotestthisexample.Makesurethatthereisaroominthe
clientprojectandthattheclientobjecthasbeenaddedtotheroom.Oncethisisdone,
makesurethatboththeserverandtheclientprojectsareopen.Runtheserverproject
first.Weshouldntbeabletodoanythingintheserverproject,butweshouldntgetany
errors.Nowthattheserverisrunning,wecanruntheclientonthesamecomputer.
Clickaroundinthegameroomoftheclient,andmakesurethattheclicksareappearing
intheserverwindowaswell.Ifeverythingwentwell,youwillgetoneofthemost
excitingfeelingsintheworld:thejoyofseeingamultiplayergamethatyoucoded
running.

Gettingevenabasicexamplelikethisworkingfeelsgreat,andyoushouldbeproudof
howfaryouhavecome.Youwillnoticethatifyourunyourclientonadifferent
computerconnectedtothesamehomenetwork,itwillstillwork.Thisexamplecovers
localareanetworksorLANs.Ifyouwanttousethistoplayagamewithafriendthatis
connectedtoadifferentnetwork,youcanusesomesortofprogramthatallowsyouto
setupafakelocalareaconnection,oryoucoulduseyourexternalIPaddress.

OnlineTicTacToe
Knowinghowtosendinformationbetweentheserverandtheclientisthefirststep,but
itisimportanttoknowthelimitationsofthissystem.Wewanttodoourbesttolimitthe
amountofinformationthatwearesendingbetweentheclientandtheserver.Keeping
thatinformationatamanageablesizewillhelpourgametorunfasterandcanalsohelp
preventlaginourgamewhiletheinformationisbeingsentbackandforth.Haveyou
everbeenplayinganonlinegameandwatchedanenemyoranotherplayerobject
teleportorjumparoundonthescreenbecauseofserverlag?Thisgenerally
happenswhentheclientsxandyinformationonthatparticularobjectfallbehindthe
informationtheserverhas,andtheserverhastoresynchronizetheclienttotheserver.
Itlooksstrangeandisverycommon,eveninlargeonlinetitles.Wewanttoavoidthisas
muchaspossible.

156

ForourfirstonlinemultiplayerGameMakergame,wearegoingtobuildaturnbased
game.Thiswillhelpustolearnhowtolimittheamountofdatathatwearesending,and
wewillusesomeotherneattricksalongtheway.Muchofthecodeinthisexamplewill
lookfamiliar.Thisisbecausewearegoingtouseasystemalmostexactlylikethe
TictactoegameintheDataStructureschapter.Thisshouldmakeiteasierforyouto
separatetheactualgamecodefromtheonlinecode(whichwewilladdontopofit).

spr_mark
Letsstart!Firstoff,addaspritecalled .Givethespriteasizeof6464.Add
twosubimagestothesprite.ThefirstsubimagewillbeourO,andthesecond
subimagewillbeourX.Oncewehavethesprite,addanewbackgroundaswell.
Namethebackground bg_space ,giveitasizeof6464,anddrawaboxoutlinethat
fillsuptheentirebackground.Wewillusenineofthesebackgroundsinourroomto
createtheTictactoeboard.MakesureyoucheckuseastilesetandsetboththeTile
WidthandTileHeightto64.Hereisascreenshotofboththespriteandthe
background:

Sprite: spr_mark

157

Background: bg_space

Nowthatwehavethebackgroundandspriteready,createanewroom.Nametheroom
rm_board .Setitswidthto192anditsheightto224.Now,gotothetilestabandusethe
backgroundwecreatedtomakeyourTicTacToeboard.Whenyouarefinished,your
roomshouldlooksomethinglikethis.Iplacedthetileslowerintheroomtoleavesome
roomforinformationatthetopofthegamewindow.

158

Room: rm_board

Thenextstep,beforeaddingourobjects,istosetupsomemacros.OpenuptheAll
configurationsmacrolistandaddthesemacrosandcorrespondingvalues.

PLACE_MARK: 0
BLANK_MARK: -1
O_MARK: 0
X_MARK: 1
CELL_SIZE: 64

159

Whenwearedone,themacrolistshouldlooklikethis:

All Configurations Macro List

Thetimehascometostartaddingobjectsandtheirevents/actions.Createtwonew
obj_network
objects.Namethefirstone andthesecondone obj_game .Thesetwo
objectsareallthatweneedtocontroltheentiregame.

obj_network
obj_game

Startbyopeningupthenetworkobject,addinga Create Event toit,anddragginga


Code Action Code Action
intotheevent.The willcontainthiscode:

obj_network: Create Event

/// Initialize the network


vartype
=
network_socket_tcp;
varip
="
000.000
.
0.000
";
varport
=
8000;
socket
=
network_create_socket
(
type
);
connection
=
network_connect
(
socket
,
ip
,
port
);
is_server
=
false;
global
.
turn
='
other
';

if
(
connection
<
0
){
global
.
turn
='
mine
';
max_clients
=
1;

160

server
=
network_create_server
(
type
,
port
,max_clients
);
network_destroy
(
socket
);
is_server
=
true;
client
=
noone;
}

Thisbitofcodeiscleverbecauseitallowsustousethesamegameprojectforboththe
serverandtheclient.Westartbycreatingasocketandaconnection.Then,wesettwo
variables.Thefirstvariableisaninstancevariablecalled is_server .Atfirst,we
assumethatthisgameistheclient.Becausewearetheclient,weset global.turn to
true.

network_connect
If isunabletoestablishaconnection(forexample,ifaserverdoesnt
exist),thenthefunctionreturnsanumberlessthan0.Wecanusethistotestifthereis
alreadyaserver.Ifthereisntalreadyaserver(if (connection<0) ),thenweneedto
createone.

Creatingtheserveriseasy.Wesetthe global.turn tomine,setthemax_clients


network_create_server
to1,andcall tocreateourserverwiththeinformationthat
wehavealreadyestablished.Becausewearentaclient,wedontneedthesocketwe
createdanymore,sowedestroyit.Then,weset is_server
totrueand client to
noone.

Weneedtoadda Game End Event tomakesurethatwedestroyoursocketorserver.


Theonewedestroywilldependonwhetherornotweareaserver.Adda Game End
Event andputthiscodeinit:

obj_network: Game End Event

/// Clean up dynamic data


if
(
is_server
){
network_destroy
(
server
);
}
else{
network_destroy
(
socket
);
}

161

Thisisasmallcodeblock.Wechecktoseeifweareaserver.Ifweare,wedestroythe
serverifnot,wedestroythe(client)socket.

Addan Asynchronous > Networking Event .Thiseventwillfireanytimetheserver


picksupamessagefromtheclient.Therearethreetypesofmessagesthatwillbesent:
anattempttoconnect,anattempttodisconnect,andactualdata.Wewillcheckforeach
ofthesetypesandhandlethemaccordingly.Dragovera Code Action andaddthis
codeblocktoit:

obj_network: Networking Event

vartype_event
=
async_load
[?
"
type
"];
switch
(
type_event
){
casenetwork_type_connect:
if
(!
is_server
)
break;

// Get a reference to the client's socket


if
(
client
==
noone
){
client
=
async_load
[?
"
socket
"];
}
break;

casenetwork_type_disconnect:
if
(!
is_server
)
break;

// Remove the reference to the client's socket


client
=
noone;
break;

casenetwork_type_data:
// Handle the data received
varbuffer
=
async_load
[?
"
buffer
"];
buffer_seek
(
buffer
,buffer_seek_start
,
0
);
scr_handle_packet
(
buffer
);
break;
}

162

Thefirstthingthatwedoisgrabareferencetothetypeofeventthatisbeingsent.Ifit
isaconnecteventoradisconnecteventandwearenottheserver,wejustbreakout
andignorethem.Ifwe are
theserver,wemakesuretoaddthenewclientwhenthey
areconnectingandremovethemwhentheyaredisconnecting.Forthedataevent,it
doesntmatterifwearetheserverortheclient,bothcansendandreceivedata.When
weknowthatwearereceivingdata,wecreatealocalvariabletostorethebuffer.The
bufferisthedatastructurethatthedataiscontainedin.Wecall buffer_seek tomake
surethatthebufferstartsreadingfromthestart.Then,wepassthebufferintoascript
thatwillhandlethedata.

Beforewecreatethatscript,wearegoingtofinishupthenetworkobjectbyaddinga
Draw Event Draw Event
toit.This willdrawsomeinformationaboutthegameonthe
screen.Wearegoingtodrawtheturnandsometexttellingtheplayerthattheyarethe
clientortheserver.Dragovera Code Action .Thisisthecodewewilladdtoit:

obj_network: Draw Event

/// Draw the game information


draw_text
(
2
,
0
,"
Turn
:
"+
global
.
turn
);
varplayer_text
='';
if
(
is_server
){
player_text
='
PlayerX
Mark
';
}
else{
player_text
='
PlayerO
Mark
';
}
draw_text
(
2
,
16
,
player_text
);

Thiscodedrawstheturntothescreenandthencheckstoseeiftheplayerisaserver.
Ifso,itsetstheplayertexttotelltheplayerthattheyaretheXmark.Iftheyarentthe
server,itsetstheplayertexttotelltheplayerthattheyaretheOmark.

Nowthatwehavefinishedupwiththenetworkobject,letswritethescriptituses.Adda
newscriptandnameit scr_handle_packet .

scr_handle_packet

Thisistheblockofcodewewillwriteinthescript:

163

scr_handle_packet

///scr_handle_packet(buffer)
varbuffer
=
argument
[
0
];
varmessage_id
=
buffer_read
(
buffer
,
buffer_u8
);

switch
(
message_id
){
casePLACE_MARK:
// Read from the buffer
vargridx
=
buffer_read
(
buffer
,buffer_u8
);
vargridy
=
buffer_read
(
buffer
,buffer_u8
);

// Set the mark


obj_game
.
grid
[
# gridx, gridy]=!is_server
;

// Start my turn
global
.
turn
='
mine
';
break;
}

Itisimportanttoknowhowthebufferisorganized.Forthisgame,Idecidedtoorganize
thebufferlikethis:(messageid,xposition,yposition).Youcanchoosetoorganizethe
bufferinanywayyouwish.Youorganizethebufferwhenyouactuallysendit,andyou
willseethatpartheresoon.Westartbygettingalocalreferencetothebuffer.Then,we
readfromthebuffertogetthemessageid.Weactuallydontneedamessageid(the
messageid isusedtoindicatewhatkindofinformationthebuffercontains)forthis
gamebecauseweareonlysendingonetypeofdata,butIdesigneditthiswaysothatif
youwantedtoaddothersystemstothegame(e.g.,chatfunctionality),itwouldbeeasy
tocreatenewmessageidsandhandlethosemessagetypesaccordingly.

Nowthatwehavethemessageid,wecanswitchthroughthedifferentmessageid
possibilities.Ivestoredtheonlymessageidthatispossibleinthisgameinamacro.
Thismakesthecodemorereadable.Ifthemessageidfromthebuffermatchesthe
messageidinthatmacro,wewillstepintothecodeafterthatcase.Onceweareinthat
sectionofcode,wereadthexpositionandtheypositionfromthebufferandstore
thoseinlocalvariables.

164

Atthispoint,wehavealltheinformationthatweneedfromtheotherplayertoupdate
ourgameboard.Wecanuseashortcutherebecauseofthewaythegridissetup.In
ourgrid(youmayrememberfromthefirsttimewemadeaTictactoegame),Xmarks
arerepresentedbya1andOmarksarerepresentedbya0.TheserveralwaysusesX
marks.Ifwearetheserverandwereceiveamarkfromtheclient,weknowthatthe
markshouldbea0.The is_server willreturna1(true)ifwearetheserver,soifwe
usethenotoperatoronit( !is_server not
),itwillreturna0.Ifweare theserver,then
is_server willreturna0.Ifweusethenotoperatoronit( !is_server ),thenitwill
returna1.Thisisasneakytrickthatallowsustoplacethecorrectmarkregardlessof
whetherwearetheserverortheclient.

Thefinalstepofthiscodechangestheturnbacktothecurrentplayersturn.Ifweare
receivingamarkthisspotmessagefromtheotherplayer,itiscurrentlytheother
playersturn.Oncewemarktheirspot,wecanswitchtheturnbacktous.

Itisfinallytimetowritethegameobjectscode.Openup obj_game andaddanew


Create Event toit.LikeIsaidbefore,muchofthiscodewilllookfamiliar,butweare
goingtochangeafewthings,sopaycloseattention.Dragovera Code Action andadd
thiscodetoit:

obj_game: Create Event

/// Create the ds grid and initialize the game object


grid
=
ds_grid_create
(
3
,
3
);
ds_grid_set_region
(
grid
,
0
,
0
,
2
,
2
,
BLANK_MARK
);

Thisblockofcodeissimpleenough.Allwedoiscreatethegridandsetallofthecells
BLANK_MARK
inthegridtothe BLANK_MARK
value. isamacrothatwecreatedearlierit
containsthevalueof1.

Beforewedothemouseclickeventanditsmessagesendingmagic,letsadda Draw
Event totheobjectandputthiscodeinit:

obj_game: Draw Event

/// Draw the game board


for
(
vari
=
0
;i
<
ds_grid_width
(
grid
);i
++){

165

for
(
varj
=
0
;j
<
ds_grid_height
(
grid
);j
++){
if
(
grid
[
# i, j]==BLANK_MARK) continue;
varsubimage
=
grid
[
# i, j];
varmx
=
i
*
CELL_SIZE;
varmy
=(
i
*
CELL_SIZE
)+
32;
draw_sprite
(
spr_mark
,
subimage
,
mx
,
my
);
}
}

Draw Event
This isalmostidenticaltotheonethatweusedinthepreviousTictactoe
examplegame.Weloopthroughthedifferentcellsinthegrid.Ifthecellisempty,we
usethecontinuestatementtoskipit,becauseweonlyneedtodrawcellsthatcontain
marks.Foralloftheothercells,wecreatethreelocalvariables,oneforthesubimage
(whichconvenientlycorrelatestothevaluethatwestoredinthegrid),oneforthemarks
xposition,andoneforthemarksyposition.Then,wedrawthesprite spr_mark using
thesubimageandpositiondatathatwegathered.

Addanew Mouse > Global > Global Left Pressed Event .Thiseventwillholdthe
codethathandlesthelogicforsettingamarkandsendingthatnewmarkinformationto
theotherplayer.Dragovera Code Action blockandaddthiscode:

obj_game: Global Left Pressed Event

/// Mark the board


if
(
global
.
turn
=='
mine
'){
// Grab the mouse position and convert it to a grid position
vargridx
=
mouse_x div CELL_SIZE;
vargridy
=(
mouse_y
-
32
)div CELL_SIZE;

// Make sure the square isn't already taken


if
(
grid
[
#
gridx, gridy]!=BLANK_MARK) exit;

// Set the mark


grid
[
# gridx, gridy] obj_network.is_server;

// Send the action over the network

166

varbuffer
=
buffer_create
(
1024
,
buffer_fixed
,
1
);
buffer_seek
(
buffer
,
buffer_seek_start
,
0
);
buffer_write
(
buffer
,
buffer_u8
,
PLACE_MARK
);
buffer_write
(
buffer
,
buffer_u8
,
gridx
);
buffer_write
(
buffer
,
buffer_u8
,
gridy
);
varreceiver
=
noone;
if
(
obj_network
.
is_server
){
receiver
=
obj_network
.
client;
}
else{
receiver
=
obj_network
.
socket;
}
network_send_packet
(
receiver
,
buffer
,
buffer_tell
(
buffer
));
buffer_delete
(
buffer
);
global
.
turn
='
other
';
}

Thefirstthingwedoismakesureitisourturn.Ifitisntourturn,thenweshouldntbe
abletoplaceamark.Aftermakingsureitisourturn,wegrabthemousespositionand
convertitintocellcoordinatesonourgridusingthedivisionoperator.

Aftergettingthatinformation,wechecktomakesurethatthereisntalreadyamarkin
thatspotandmarktheboardonoursideoftheconnection.

Now,weneedtosendthisnewinformationtotheotherplayeracrossthenetwork
connection.Westartbycreatingabufferandaddingthemessageid,themarksx
position,andthemarksyposition.Then,weusethe is_server propertyofthe
networkobjecttodeterminewhothereceiverwillbe.Aftergettingallthatready,we
sendthebufferusing network_send_packet ,deletethebufferusing buffer_delete ,
andchangetheturnsothatitistheotherplayersturn.

Thefinalstepistoaddbothyournetworkobjectandyourgameobjecttotheroom.

TestingyourMultiplayerTicTacToeGame
Congratulations!Youfinishedyourveryfirstonlinemultiplayergame.Theeasiestway
totestyourgameistocreateanexecutableofyourprojectbygoingtoFile>Create
Application,thenchangethefiletypetoSingleruntimeexecutableandpressSave.

167

Afteryourprojectbuilds,youcanrunthegametwiceandtwogamewindowswillpop
up.YoushouldbeabletoplayTictactoeagainstyourself.

Ifyouwanttotestitfurther,youcancopytheexecutablefileontoanothercomputerand
makesurethattheconnectionstillworks.

168

Chapter16

ArtificialIntelligence
Enemies
Themaindifferencebetweenartificialintelligence(AI)objectsandplayerobjectsistheir
inputs.Theplayerobjectiscontrolledbythemouse,keyboard,orgamepad.TheAI
objectsareoftencontrolledbasedondistance,collision,andvisibilitychecks.

TopDownGameExample
Letslookatasimplemethodforprogrammingartificialintelligenceinatopdowngame.
Wewilluseasimplestatesystemwithsomedistancecheckstoaccomplishthistask.
First,letsstartbycreatingsomefillersprites.Createtwonewspritesandnamethem
spr_enemy
andspr_player .

spr_enemy
spr_player

Ijustmadethemboth32x32circles.Myplayerisblueandmyenemyisred.After
creatingthesetwosprites,weneedtocreatetwonewobjects.Namethem obj_enemy
andobj_player .

obj_enemy
obj_player

Openuptheplayerobjectandadda Create Event totheobject.Insidethis


Create
Event ,addaCode Action andplacethissimplecodeinside.

obj_player: Create Event

/// Initialize the player


hp
=
1;

Wearesettingthe hpvariableto1forthesakeofsimplicity.Wewillusethe
image_alpha propertyoftheplayerobjecttoshowhowmuchhealththeplayerhas
left.Becausethispropertyonlygoesfrom0to1,itsuitsourpurposestosetthehealth
to1aswell.Afterfinishingupthecodeinthe Create Event Step Event
,adda toour

169

obj_player Code Action


.Draga Step Event
intothe andtypethiscodeinthe
action.

obj_player: Step Event

/// Control the player's movement


varright
=
keyboard_check
(
vk_right
);
varleft
=
keyboard_check
(
vk_left
);
varup
=
keyboard_check
(
vk_up
);
vardown
=
keyboard_check
(
vk_down
);

if
(
right
){
x
+=
4;
}

if
(
left
){
x
-=
4;
}

if
(
up
){
y
-=
4;
}

if
(
down
){
y
+=
4;
}

image_alpha
=
hp;
if
(
hp
<=
0
){
game_end
();
}

Thisblockofcodeallowstheplayertomoveandcausesthegametoendwhenthe
image_alpha
playershpvariableislessthanorequalto0.Italsosetsthe property
equaltothevalueoftheplayershp.Asthehpvaluedecreases,sowillthe
image_alpha
property.Thiswillcausetheplayertobecomemoretransparentas
he/sheloseshealth.

170

Now,theplayerobjectisdone.Therereallywasntanythingtootrickythere,andmost
ofthatcodeshouldlookveryfamiliartoyoubynow.Letsmoveonthethefunpartof
thisexample,creatingtheartificialintelligencefortheenemy.

Openuptheenemyobjectandaddanew Create Event toit.Insidethis


Create
Event Code Action
,wearegoingtoadda andcreatesomevariablesinsidethat
action.Hereisthecodewewilladd:

obj_enemy: Create Event

/// Initialize the enemy


state_text
='
idle
';
state
=
scr_enemy_idle;
sight_range
=
choose
(
96
,
128
,
180
);
attack_range
=
24;
spd
=
3;

Thesevariableswillbeusedtocontrolourartificialintelligence.Thefirstvariable,
state_text ,isonlyusedfordebuggingpurposes.Thiswillallowustodrawthecurrent
stateoftheenemy.Thenextvariable, state ,willstoreascriptthatcontrolsthe
behaviorofeachstate.Asyoucansee,wearesettingthisvariableequalto
scr_enemy_idle .Wedothisbecausewewantallofourenemiestostartoutintheidle
state.Thethirdvariable, sight_range ,willholdeither96,128,or180.Thesenumbers
representthedistanceourenemywillbeabletoseetheplayerfrom.The choose
functionwillselectarandomnumberfromthethreevaluespassedtoit,causingeach
enemytohaveaslightlydifferentsightrange.Thefourthvariable, attack_range ,isthe
distancethatourenemieswillbeabletoattackfrom.Thelastvariable, spd,willcontain
thevalueofthemovementspeedforeachenemy.Wearemakingthevalue3,whichis
slowerthanourplayer.Thisallowsourplayertoescapefromtheenemies.

Nowthatthe Create Event isfinished,itistimetoadda Step Event toourenemy.


Step Event
This willcontainaCode Action withthisbitofcode.

obj_enemy: Step Event

/// Control the states


script_execute
(
state
);

171

Thislineissimpleenough.Weusethe script_execute functiontorunwhateverscript


isassignedtoourstatevariable.Thisisacleanandeasywaytomanagethestatesof
ourenemyobject.

Inordertomakethegamefeelabitcooler,andforyoutobeabletovisualizehowthis
artificialintelligenceworks,wearegoingtoaddsomeneatcodeintotheenemyobjects
Draw Event Draw Event
.Addanew totheenemyobjectanddragovera
Code
Action .Hereisthecodeyouwillplaceinthataction:

obj_enemy: Draw Event

/// Draw self and state


draw_set_halign
(
fa_center
);
draw_set_valign
(
fa_middle
);
draw_self
();
draw_set_alpha
(.
1
);
draw_circle_colour
(
x
,
y
,
sight_range
,
c_red
,
c_red
,
false
);
draw_set_alpha
(
1
);
draw_text
(
x
,
y
,
state_text
);

Nowthatwearedrawingtheenemyanditssightrange,wearereadytowritethe
scriptscontrollingitsdifferentstates.Thefirstscriptthatwewritewillcontroltheidle
state.Addanewscriptandnameit scr_enemy_idle .

scr_enemy_idle

///scr_enemy_idle()
state_text
='
idle
';
vardis
=
point_distance
(
x
,
y
,
obj_player
.
x
,
obj_player
.
y
);
if
(
dis
<=
sight_range
){
state
=
scr_enemy_chase;
}

Thescriptfortheidlestateisnttoocomplicated.Wemakesuretochangethestatetext
variable(fordebugging).Then,wegetthedistancetotheplayer.Ifthatdistancetothe
playerislessthanorequaltooursightrange,thenwecanchangetothechasestate.

172

Weareusingthechasestate,butweactuallyhaventcreatedthescriptforthatstate
scr_enemy_chase
yet.Letsdothatnow.Addanewscriptandnameit .

scr_enemy_chase

///scr_enemy_chase()
state_text
='
chase
';
vardis
=
point_distance
(
x
,
y
,
obj_player
.
x
,
obj_player
.
y
);
vardir
=
point_direction
(
x
,
y
,
obj_player
.
x
,
obj_player
.
y
);

if
(
dis
<=
sight_range
&&dis
>
attack_range
){
motion_set
(
dir
,
spd
);
}
else
if
(
dis
<=
attack_range
){
speed
=
0;
direction
=
0;
state
=
scr_enemy_attack;
}
else{
speed
=
0;
direction
=
0;
state
=
scr_enemy_idle;
}

state_text
Thechasestatescriptstartsliketheidlescript,bysettingthe
variable.
Afterthat,wegettemporaryreferencestothedistancefrom(andthedirectionto)the
player.Next,weaddsomeifstatementstochecktoseewhetherwearewithinthesight
range,withintheattackrange,oroutsidebothrangesforourenemy.Ifwearewithinthe
sightrange,wecontinuemovingtowardstheplayer.Ifwearewithintheattackrange,
westopmovingandswitchtotheattackstate.Ifweareoutsidebothranges,thenwe
stopmovingandswitchbacktotheidlestate.

Lastly,weneedtowritethecodethatwillbeintheattackscript.Addanewscriptand
nameitscr_enemy_attack .

scr_enemy_attack

///scr_enemy_attack()

173

state_text
='
attack
';
vardis
=
point_distance
(
x
,
y
,
obj_player
.
x
,
obj_player
.
y
);

if
(
dis
>
attack_range){
state
=scr_enemy_chase;
}
else{
// Attack
if
(
alarm[
0
]==-
1){
obj_player.
hp
-=
0.1;
alarm
[
0
]=
30;
}
}

Withtheattackscript,wesetthestate_text
variableandgetatemporarydistance
referencefromtheplayer.Afterthat,wecheckthedistance.Ifweareoutsidetheattack
range,weswapbacktothechasestate.Iftheplayeriswithintheattackrange,wecan
subtractfromtheplayershpby0.1wearealsousinganalarmtomakesurethatthe
attackdoesnthappeneverystep.Ifitdid,ourplayerwoulddieinlessthanasecond.
Usingthisalarmforcestheenemytoonlyattackonceeverysecond.

Inordertousethealarm,wehavetoaddthe Alarm Event


totheenemyobject.It
doesntactuallyneedtohaveanycodeintheeventbecausewearecontrollingthe
alarminsideofourattackscript.Wedostillneedtoaddtheeventandadda Code
Action orcommentbecauseGameMakerautomaticallyremoveseventsthatdonthave
anyactionsinthem.Letsaddanew Alarm 0 Event totheobjectenemyanddrag
Code Action
overa .Intheaction,typethis:

obj_enemy: Alarm 0 Event

/// This is just a comment in a code action to prevent


// GameMaker from removing the event.

Now,bothourplayerandourenemyobjectsareready.Createanewroom,addafew
enemiestoit,andaddaplayer.Makesurethatyouhaveassignedthe spr_enemy
spr_player
spritetotheenemyobjectandthe
spitetotheplayerobject.Oncethe

174

objectshavebeenaddedtothenewroom,runthegameandtestit.Watchthebehavior
oftheenemies.

Thissimpleartificialintelligenceexampleteachesthebasicsofusingastatesystemfor
enemyAI.Thisisagreatplacetostart,andwehavealreadycomesofar.Greatwork!
Trytothinkofonemorestatethatwecouldaddtotheseenemiesthatwouldmake
themmoreinteresting.Itmightbefuntotryaddingawanderingstatethatmakesthe
enemieswanderaroundbetweenperiodsintheidlestate.Playaroundwithitandhave
fun!

PlatformArtificialIntelligence
Platformgamescanbetrickyforbeginners,especiallywhenitcomestotheenemies
andtheirartificialintelligence.Iwilldescribetwocommonformsofplatformartificial
intelligenceinthenextsection,andImgoingtoteachyouhowbothofthemwork.

BackandForthEnemies
Manyplatformgameshaveabasicenemythatsimplymovesbackandforthinsome
areaofspace.Theseenemiesdontaggressivelyseekouttheplayerbutactasa
movinghazardthattheplayermustavoidordealwith.Mostofthetime,theseenemies
onlyturnaroundwhentheyencounteraledgeorawall.Therearemanywaysto
programenemieslikethis.Onemethodusesinvisibleobjectsplacedintheroomthat,
whencollidedwith,causetheenemytoturnaround.Thismethodisstraightforwardand
easytoprogram.Itdoesmakeithardertobuildeachlevel,though,becausethe
designerhastoworryaboutplacingtwooftheseobjectsforeverysingleenemythat
theywanttoaddtotheroom.Wearegoingtolookatadifferentwaythatallowsthe
enemiestobeslightlysmarteranddetect(bythemselves)whentheyshouldturn
around.

Beforewestartprogramming,weneedtocreatetwospritesandtwocorresponding
objects.Createtwonewspritesandnamethem spr_enemy spr_solid
and .

spr_enemy
spr_solid

Forthisexample,Imademyenemyspritearedboxwithasizeof32x32andmysolid
spritea32x32graybox.Centertheoriginfortheenemysprite,butnotforthesolid
sprite.Hereisanimageofbothofmyspritesandtheirproperties:

175

Nowcreatetwoobjectsandnamethem obj_enemy obj_solid


and .

obj_enemy
obj_solid

Thesewillbetheonlytwoobjectsthatweneedfornow.Wedontneedtoaddany
eventsorcodeactionstothesolidobject,butyoushouldcreatearoomandbuilda

176

simpleplatformerlevel.Onceyouhavebuiltabasiclevel,youcanaddafewenemy
objectstoit.HereisascreenshotofthelevelthatIbuilt.

Nowthattheroom,objects,andspritesareready,wecanstartaddingtheeventsand
codeactionsneededforourbasicbackandforthplatformenemies.

Openuptheenemyobjectandadda Create Event toit.Nowdragovera


Code
Actionandaddthissmallblockofcodeinit:

obj_enemy: Create Event

/// Set the initial state of the object


state
=
choose
(
scr_enemy_move_right
,
scr_enemy_move_left
);

choose
Inthislineofcode,weareusingthe functiontopickoneofthetwoscriptsand
assignittoourenemyobjectsstatevariable.Wehaventcreatedthesescriptsyet,but
wewillshortly.

177

Nowweneedtoadda Step Event


totheenemythatexecutesourcurrentstate.Here
isthecodethatwewilluse:

obj_enemy: Step Event

/// Execute the state


script_execute
(
state
);

Thetwoeasypartsaredonenow.Itstimetocreatethetwoscriptsthatwewillbeusing
tocontroltheenemysmovements.Createtwonewscripts.Namethem
scr_enemy_move_right scr_enemy_move_left
and .

scr_enemy_move_right
scr_enemy_move_left

Forthiscode,weneedtochecktwothings.Weneedtofindoutwhetherthespaceto
therightisfreeofanysolidobjects.Theotherthingthatweneedtocheckiswhether
thesection under thespacetotherightofusisaledgeornot.Openup
scr_enemy_move_right andaddthiscodetoit.

scr_enemy_move_right

///scr_enemy_move_right
varright_free
=!
place_meeting
(
x
+
2
,
y
,
obj_solid
);
varxpos
=
x
+(
sprite_width
/
2
)+
1;
varypos
=
y
+(
sprite_height
/
2
)+
1;
varno_ledge
=
instance_position
(
xpos
,
ypos
,
obj_solid
);

if
(
right_free
&&no_ledge
){
x
+=
2;
}
else{
state
=
scr_enemy_move_left;
}

Weusethe place_meeting functiontocheckforanyobjectstotherightofus.Ifthere


right_free
arentany,then willbesettotrue.Weuse instance_position tocheck
no_ledge
whetherthereisaledgetotherightofus.Ifthereisntaledge, willbesetto

178

true.Weuseanifstatementtocheckbothofourtemporaryvariables.If right_free is
trueandno_ledge istrue,wecanmovethetheright.Ifnot,wechangethestatetostart
movingtotheleft.

Openscr_enemy_move_left andaddthiscodetoit.Thecodesareverysimilar,the
onlydifferencebeingthedirectionforthechecksandmovement.

scr_enemy_move_left

///scr_enemy_move_left
varright_free
=!
place_meeting
(
x
-
2
,
y
,
obj_solid
);
varxpos
=
x
-
sprite_width
/
2
)-
1;
varypos
=
y
+(
sprite_height
/
2
)+
1;
varno_ledge
=
instance_position
(
xpos
,
ypos
,
obj_solid
);

if
(
right_free
&&no_ledge
){
x
-=
2;
}
else{
state
=
scr_enemy_move_left;
}

Imnotgoingtoexplainthecodeinthisscriptbecauseitworksalmostexactlylikethe
otherscript.

TestYourArtificialIntelligence
Aftercreatingbothofthesescripts,weshouldbeabletorunourgameandwatchthe
enemiesmovebackandforth.

SmarterEnemies
Congratulations!Youhavecreatedyourveryfirstplatformartificialintelligence.The
enemiesyoucreatedarecommoninmanyplatformgames,butoncetheplayerhas
figuredouttheirtiming,theyareeasytoavoid.Foronemoreexample,wearegoingto
addtothepreviousexampleandcreateaplatformenemythatattemptstochasethe
cursoraroundthelevel.

Forthissmarterenemy,wewillneedtoaddbettercollisioncheckingandgravity.The
enemywillonlyhaveonestatebutthatstatewillbemorecomplicatedthanthestatesof
thesimpleenemy.Addanewobjecttothegameandnameit obj_smarter_enemy .

179

obj_smarter_enemy

Wecanusethesamespriteusedforthefirstenemy,orwemightuseaspriteofa
Create Event
slightlydifferentcolor.Adda
tothenewobjectandplacethiscode
insidetheevent.

obj_smarter_enemy: Create Event

/// Initialize the smarter enemy


hspd
=
0;
vspd
=
0;
grav
=
1;
jspd
=
14;
state
=
scr_chase_mouse;

Wecreatequiteafewinstancevariableshere,andwewillbeusingthesedifferent
variablestocontrolourobject.Wehaveahorizontalspeed,averticalspeed,agravity
amount,ajumpamount,andourstate.

Addanew Step Event .Thiseventwillcontrolthestate,thecollisions,andthegravity.

obj_smarter_enemy: Step Event

/// Control the state and collisions


script_execute
(
state
);

// Gravity
if
(!
place_meeting
(
x
,
y
+
1
,
obj_solid
)){
vspd
+=
grav;
}

// Horizontal collisions
if
(
place_meeting
(
x
+
hspd
,
y
,
obj_solid
)){
hspd
=
0;
}

180

// Move horizontally
x
+=
hspd;

// Vertical collisions
if
(
place_meeting(
x
,
y
+
vspd
,
obj_solid
)){
while(!
place_meeting
(
x
,
y
+
sign
(
vspd
),
obj_solid
)){
y
+=
sign(
vspd
);
}
vspd
=
0;
}

// Move vertically
y
+=
vspd;

Thefirstpartofthiscoderunsourcurrentstate.Afterthat,wecheckforasolidunder
theenemyandapplygravityifthereisntsomethingsolidthere.Inthemiddlesection,
wesetourhorizontalspeedto0ifthereisacollisionhorizontally.Afterthecollision
check,weapplythehorizontalmovementspeedtotheenemysxposition.Lastly,we
checkforverticalcollisions.Becausetheverticalmovementhasaspeedthatcould
change(duetogravity),weneedtomakethiscollisionchecksmarter.Weuseawhile
statementtomovetheenemyobjectupagainstsolidobjectsintheverticalaxis.Then
weapplytheverticalspeedtotheyposition.

Thelastthingthatweneedtodoinordertogetoursmarterartificialintelligencemoving
scr_enemy_chase
iswritethe
script.Addanewscriptandaddthiscodetoit.

scr_enemy_chase

///scr_chase_mouse()
if
(
point_distance(
x
,
y
,
mouse_x
,
y
)>
16
){
if
(
x
<mouse_x
){
hspd
=
4;
}
else{
hspd
=-
4;
}
}
else{

181

hspd
=
0;
}

// Set up check variables


varon_ground
=
place_meeting
(
x
,
y
+
1
,
obj_solid
);
varmouse_above
=(
mouse_y
<
y
);
varwall
=
place_meeting
(
x
+
hspd
,
y
,
obj_solid
);
varxpos
=
x
+
sign
(
hspd
);
varypos
=
y
+(
sprite_height
/
2
)+
1;
varledge
=!
instance_position
(
xpos
,
ypos
,
obj_solid
);

// Check for jump


if
(
on_ground
&&mouse_above
&&
(
wall
||ledge
)){
vspd
=-
jspd;
}

Thefirstcheckinourchasescriptisourdistancefromthemouseposition.Afterthat,
weseeifwearetotheleftortherightofthemouse.Ifwearetotheleft,wemoveright,
andifwearetotheright,wemoveleft.Thatpartisrathersimple.

Decidingwhentheenemyobjectneedstojumpisaslightlymorecomplicatedsegment.
Westartbysettingupourdifferentchecks.Weuse place_meeting
toseewhetherwe
areontheground.Weusethemousesypositiontoseeifweareaboveorbelowit.We
useplace_meeting
tocheckourmovementdirectiontoseeifthereisawall.Finally,
weusesomepositionchecksand instance_position
toseeifthereisanedge.Once
allofourcheckvariablesaresetup,wecanwritetheifstatementthatchecksour
differentconditions.Weusethe and operatortomakesurethatweareontheground
andthatthemouseisactuallyaboveus.Ifthosetwocasesaretrue,weusethe or
operatortocheckifthereisawalloranedge.Inbothofthesecases,ourenemywill
jump.

TestingYourSmarterEnemy
Makesureyouhaveaddedthenewenemytotheroom,thenrunthegame.Theenemy
shouldmakeanattempttochasethecursoraroundtheroom.Theartificialintelligence
isntperfect,butitworksquitewellformostsituations.

182

PathfindinginaMaze
OneoftheothertopicsthatIwillteachyouishowtomakeanenemythatissmart
enoughtonavigateamaze.GameMakerdoesagoodjobofsimplifyingthisprocessfor
you,buttherearestillafewthingsthatcanbeconfusingforpeoplewhohavenever
doneitbefore.Imgoingtoshowyouhowtoharnessthisamazingfeatureof
GameMakerStudioandteachyousometipsthatwillhelpyoutoavoidpitfallsthatyou
mightencounter.

TheEnemyinaMazeExample
Letsstarttosetupourpathfinding.OpenupanewGameMakerprojectandaddtwo
newspritestoit.Thesetwospriteswillbenamed spr_enemy andspr_solid.Center
theenemysprite,butleavetheoriginofthesolidspriteat0,0.

spr_enemy
spr_solid

Imadebothofmyspritessimpleboxeswithdimensionsof32x32.Aftercreatingthese
sprites,add3newobjectstothegame.Namethem obj_enemy obj_solid
, ,and
obj_grid .

obj_enemy
obj_solid
obj_grid

Assigntheenemyspritetotheenemyobjectandthesolidspritetothesolidobject
leavethegridobjectwithoutanyassignedsprite.Createabasicroomandgiveita
heightof640andawidthof360.Wecannametheroomwhateverwelike,butInamed
minerm_maze .Addinthegridobject,theenemyobject,andcreateamazeintheroom
usingthesolidobjecttoformthewalls.Besuretokeepthewallsectionobjects
(obj_solid )snappedtoa32x32grid.Hereisascreenshotofmyroom:

183

Mysolidobjectsarethedarkgrayonesandmyenemyobjectistheredone.Oncethe
roomiscomplete,wearereadytostartprogramming.Openupthegridobjectandadd
Create Event
anew Create Event
toit.Placethiscodeinthe :

obj_grid :
Create Event

/// Using an alarm, wait one step, then create the grid
alarm
[
0
]=
1;

Thereasonweareusinganalarmherebeforecreatingthegridistomakesurethatall
ofthesolidinstanceshavebeencreated.Weneedthemtobeintheroombefore
creatingthegridandthenaddingthesolidobjectstothegrid.Thereareotherwaysto
dothis(changingthecreationorderofyourinstancesintheroom),butthismethodwill
workforthisexample.

Now,addthe Alarm 0 Event .Inthisevent,wewillbecreatingthegridthat


GameMakerwilluseforthepathfinding.Oncewehavecreatedthegridandaddedthe
solidobjectstothegrid,GameMakercanusethatinformationtocreateapathbetween
twodefinedpointsinthegrid.

184

obj_grid:Alarm0Event

/// Create the grid


// Create some temporary variables
varcw
=
32;
varch
=
32;
varhc
=
room_width
/
cw;
varvc
=
room_height
/
ch;

// Create the grid


global
.
grid
=
mp_grid_create
(
0
,
0
,
hc
,
vc
,
cw
,
ch
);

// Add the walls to the grid


mp_grid_add_instance
(
global
.
grid
,
obj_solid
,
0
);

First,wesetupsometemporaryvariablesthatwewillusetopasstothe
mp_grid_create cw
function.The ch
and variablesstandforthecellwidthandthecell
height,respectively.Keepingthesenumbersaslargeaspossible(whilestillensuring
thattheenemymovementlooksgood)willreducethelikelihoodofperformanceissues
inyourgame.The hc
andvc
variablesstandforthehorizontalcellcountandthe
verticalcellcount,respectively.Wecalculatethesenumbersbasedontheroom
dimensionsandthecelldimensionsweareusing.Now,wearereadtocreatethegrid.
Thefirsttwoargumentsinour mp_grid_create x
functionarethestartingandstarting
ypositionforthegrid.Thenexttwoarethehorizontalcellcountandtheverticalcell
count.Lastly,wehavethecellwidthandthecellheight.Oncewehavecreatedthegrid,
weneedtoaddthesolidobjectstothegridtomakesurethatGameMakerknowsthat
thoseobjectsshouldbecountedaswalls(whichtheenemycannotwalkthrough).We
usethe mp_gird_add_instance toaccomplishthistask.Ittakesthreearguments:the
gridwecreated,theinstancetoadd,andwhetherornottocheckpreciselyforthat
instance.Aprecisecheckwilluseprecisecollisioncheckingonthespriteoftheobject
passedin.Oursolidobjectisjustabox,sowedontneedtoworryaboutprecise
collisionchecking.

Nowthatthegridhasbeencreated,weneedtomakesurethatwedontendupwith
anymemoryleaks:weneedtoadda Game End Event ,whichwilldestroythegridwhen
thegameisclosed.

185

obj_grid: Game End Event

/// Destroy the grid


mp_grid_destroy
(
global
.
grid
);

Weusethe mp_grid_destroy functionandpassthegridasanargumentinorderto


destroyit.

Goodnews!Yourgridobjectisfinishedandtheglobal.grid variableisreadytobe
usedinourenemyssmartpathfinding.Now,wejustneedtoprogramtheenemy.
Surprisingly,becauseofthewayGameMakerhandlesAIpathfinding,itisactuallyeven
easierthansettingupthegrid.Wewillprogramourenemytomovetoalocationinthe
mazethattheuserclickson.Openuptheenemyobjectandaddanew Create Event
toit.

obj_enemy: Create Event

/// Create the path


path
=
path_add
();

Here,weareusingthepath_add functiontocreateanewpathandassignittothe
pathinstancevariable.Wewillusethislaterwhentheuserclicksalocationinthe
maze.

Nowaddanew Mouse > Global Mouse > Left Pressed Event toourenemy
objectandplacethissegmentofcodeinit:

obj_enemy: Global Left Pressed Event

/// Find the mouse click and move towards it along a path
varmx
=(
mouse_x div
32
)*
32
+
16;
var
my
=(
mouse_y div
32
)*
32
+
16;

if
(
mp_grid_path
(
global
.
grid
,
path
,
x
,
y
,
mx
,
my
,
1
)){
path_start
(
path
,
4
,
path_action_stop
,
false
);
}

186

First,wegetagridsnappedversionofthemouses x
andyposition.Oncewehave
that,weusethe mp_grid_path functioninsideofanifconditiontodetermineifa
suitablepathwasfound.The mp_grid_path functiontakessevenarguments.Thefirst
argumentisthegridtousetocheckforasuitablepath,thesecondisthepaththatthe
functionwillusetocreateamovementsolution,thethirdandfourtharethestarting
positionofthepath(weusetheenemyobjects xandy
position),thefifthandsixthare
theendpositionofthepath(weusethemouses x y
and position),andthefinal
argumentisabooleanvaluethatdeterminesiftheobjectcanmoveat45
anglesinthe
mp_grid_path
path.Wesetthistotrue.Ifthe functionisabletofindasolution,wewill
enterintotheblockofourifstatementandstartmovingalongthepaththatwehave
created.Tomoveonthispathanddeterminewhatactionoccurswhentheobject
reachesitsdestination,weusethe path_start function.Forthefirstargumentofthis
function,wepasstheidofthepathtouse.Next,wepassthespeedatwhichtheenemy
objectshouldmovealongthepath.Afterthat,wepasstheactionthatshouldbe
performedattheendofthepath path_action_stop willstoptheenemywhenit
reachestheend.Finally,wepassabooleanthattellsthefunctionwhetherornotthe
pathisrelativetotheobjectortotheroom.Avalueof false indicatesthatthepathwill
berelativetotheroomandthatworksjustgreatforourexample.

Onceagain,congratulations!Runthegameandtestyourenemy.Ifyouclickonanarea
oftheroomthatispossiblefortheenemytoreach,itshould,verycleverly,movealong
theshortestpathuntilitreachesthatpoint.Ifyouwanttolearnmoreabouthow
GameMakeraccomplishesthistask,youcanresearchtheA*algorithm.

Now,youhaveonemoreweaponinyourarsenalofartificialintelligencetactics,and
youcanmakeyourenemiesevensmarter!

ExperimentwithArtificialIntelligence
Hopefullythischapterhastaughtyouafewdifferenttricksforartificialintelligenceand
younowhavesomeideasofyourownorevenideasonhowtoimprovetheexamples
inthischapter.Keepinmind,smartartificialintelligencedoesntalwaysmakeyour
gamefunsometimes,iftheartificialintelligenceistoogood,thenthegameisnt
enjoyable.Havefunwithyourenemies,butrememberthatyourgameisthemostfun
whentheartificialintelligenceischallenging,notpunishing.Andoften,youcanhave
thembechallengingwithverysimpleartificialintelligence.

187

FinalInfo

ContactandKickstarter
MyContactInformation
Ifyouhaveanyquestionsorconcerns,donthesitatetocontactme!Thebestwayto
contactmeisbyemail,butIwilllistmyotherprofilesaswell.Besuretocheckoutmy
YouTubeChannelforfreetutorialvideosonGameMakerStudio.

Email: heartbeast.studios@gmail.com
Website: heartbeaststudios.com
YouTube: youtube.com/uheartbeast
Twitch: twitch.tv/uheartbeast
FaceBook: facebook.com/heartbeast.studios
Twitter: twitter.com/uheartbeast
Tumblr:
uheartbeast.tumblr.com

Thanks
BeforeIshowtheKickstarterbackerlist,IwanttosaythankstomywifeCharly.She
hasbeenahugesupportformeinthemostfrustratingmoments.Iwanttosaythank
youtomyeditorSlade.HehasbeenapleasuretoworkwithandIlookforwardto
workingwithhimmoreinthefuture.Iwanttosaythankstomyfamilyandallmyfriends.
Iwanttosaythankstoeverybodywhoemailedmewithfeedbackonthisbook,
especiallyChrisSanykforgoingaboveandbeyond.Andfinally,Iwanttosaythanksto
you.Thankyouforbuyingmybook.Yoursupportmeanstheworldtome.

Stories
Ifyoufoundthisbookhelpfulorinspiringinaveryspecificway,thenemailmeyour
story!IwillbecollectingstoriesfrommyreadersandYouTubeviewersandifyourstory
strikesachordinmysoul,itmightbefeaturedinthatcollection,andsentoutwithevery
futurecopyofmybook.

188

TopKickstarterBackers
HerearethetopKickstarterbackers.Thankyouguys!

DavidAlmirall
$148.00
StevenWise
$100.00
GeorgeHopper$82.00
JonathonMcClung$80.00(ForUSAShippedBook)

KickstarterBackerList
HereisalistofalltheotherKickstarterBackers.Youguysandgalsmadethisbooka
reality.Thankyousomuch!

$80+Backers
KyleTrehaeven,FloriahFischer,ChrisAhmad,FrankKristiansen,GeraHmurov,Ingo
Liebig,DavidTill,RobertParry,TylerReddick,RobLyndsey,Jono,MarkusLange,
FrancisFitzgerald,LanceTofsrud,SveinDaniel,Solvenus,JanErikMatz,PaulCook,
DavidWard,KevinScorey,ArthurUiterwijk,MathiasNervik,EleniMerianou,Gustavo
Arantes,Tetruashvili,SteliosPotamitis,CheeLupWan,GilFerrand,DarkCoolEdge,
OmarAlterkait,BrodieHelmore,JoseAntunes,JussiKukkonen

$60Backers
ChristopherM.Bell,ReMeX,LewisAllard,DonovanAnderson,CalebAnderson,Aaron
Freeze,ChinuaWhite,MatthewJarvis,ChrisSanyk,DemetriMallous,QuentinThomas,
MatthewMather,JoshDresner,JamesRyan,JonathanWulff,Azuz,Hugo,Zachary
Mapes,TrevorWilliams,DallasBowland,KennethKline,JonathanBergeron,Joe
Healy,SaadmanRakib,Khan,StephenJolly,UriahMaravilla,Greer,AndrewJeremy,
Goetz,Christopher,Bentley,KyleWrigley,DaeinCho,ScottGoldsmith,EvilLinux,
JeffreyStockton,GopalVithlani,JeanDenisHaas,JoshFields,AnnaMunoz,Raighne
Hogan,Hylyncks,SladeStolar,EndlessMike,GrantCable,NataschaButer,Adrian
Lamar,Nick,DanielCuster,GregoryLeeII,PatrickPolk,JamesKaucher,Hamed
AlRyami

$30Backers
JoelIlett,Alonso,Mikael,EulogioEnamorado,Pallares,TomMason,Matthew
Sylvester,MarvanAlkufai,Timothy,MiguelAngel,GarciaGuerra,JacobDuffy,Sheldon
Sims,ChristopheLiaret,IsraelRN,BryanLumb,AaronParsons,MichaelDailly,Diall
Delmer,MatsWallin,JonathanJohnson,JoeJerkovic,JordanMurphy,William
Richards,GamebotSchool,LLC,DevinKaufman,TyroneSwart,AndrewStrauch,
EduardoAugusto,NicoleImber,RaymondHegge,Jeff,JonathanArdua,Doyle,

189

StephenMaden,Carlos,JoeWilcox,JonBursey,MarcioMattos,Anucha,
Wongkarnkah,Gibo,BobThulfram,JohnGeorge,FreedCastillo,RCMADIAXLLC,
FaisalAlkubaisi,BaronBeckenham,Bloomer,JavierMartinez,JeffWatson,
Pokemonrey6,JohnPeterson,Leonid,JaimeChan,DiegoPiaggio,ArthurChan,Carron
Ohree,Jonathan,Mcillwaine,JeremyLenzo,JonathanTrafton,DafyddFrancis,Blue
MothStudio,BurnenThyme,Victor,AndreasAvoukatos,TylerSease,MiguelCastro,
MarcusMoore,ChristianBelding,ChrisSheldon,Jackie,RodrigoGomes,RobinPlaye,
KrystofHoracek,Abbenano,StuartSulaski,BrianDanforth,DavidConover,Lubin
Hadalgo,ChrisWahl,LauriMarttiKojo,AndrewRodrigue,DavidFarina,Melissa
Musgrove,Hobbyaescala,KarinPortier,JamesClark,LaritzaCastaneda,SvenSowa,
GabrielQuintana,MaikRoseboom,TashaJames,MartinGebhardt

$25Backers
AndrewGasson,DavidJagneaux,Marshall,TimHofstee,Hakun,Matthew,Humphries,
Tehwave,AlexONeill,JesseCall,RaymondSpiteri,ColonelFubar,Ildradil,James
Rozee,JoshHash,FedericoColovini,CHWan,ZpeedTube,KimmoSavilampi,Philippe
AbiSaab,JonathanFoster,ThrynHenderson,Symbios,WilliamABurgess,
Thrashonkel,BillLoguidice,AlexGreenwood,JermainKanhai,BenjaminGemmel,
PlasmaToy,Studios,RemiD.Finjord,TommyWedin,ScyldScefing,LouisGiliberto,
PakoitoWooS,JonTrew,KyleFrick,AlexMaskill,MatthewVine,ThomasHerth,
PhiRune,CarlosDiaz,ColbyRyan,Maulden,CarlosMartinez,BenPledger,Isao
Sasaki,RyanSchuzle,Brian,HyperSloth,CherylHoward,ArturoSanchez,MrNeeNaw,
RobertStockamp,KongNyianSii,Woggos,Toshirolshii,DavidBrittain,Redstart
Industries,BuddRoyce,GabrielJohnson,LazySiege,Cimmarian,DanielRyJek,
Wojcik,JohnHoffer,OscarBagger,DavidBatty,Ronald,AlexandreMoisan,Fabiano
Martino,AlbertoMorales,GustavoDelgado,BenWhittaker,NickLandry,LeongWai
Yin,HunterHarris,KevinPass,c3sk,MarshallNguyen,MikaelMyntti,Josh,Sam
Whillance,KrisKing,Dominic,AndreasSjostrand,Sammywhs,Simone,ClaesWiklund,
Spencer,AlexanderHertzler,Malthe,Falkenstjerne,JorgeG,JoshHenry,Steven
Guzman,GunnarHogberg,JohannMayac,AustinSiagian,Andrew,Wooldridge,Felipe
Nanni,RogueDues,CarolosNavarro,Roman,FranOrellana,MatthewHester,Ludvig,
Cameron,Simwad,DeepraSmith,EthanSwords,PaulBroad,ShikiMatsuri,Rishi
Ilangovan,JuanHernandez,RobbyTheChaotic,JanetteLeis,RafalToczyski,
Metatronaut,AntunKesegic,RomuloPereiradeAraujo,TomAbbott,LeoStefaninos,
RalfZhan,CosimoLattanzio,EkaPramuditaMuharram

190

$5Backers
MelissaAndersonFrancisco,SlimeyJenkins,HelgeSverre,GregMckechnie,
Nazariglez,BlueSocialNetwork(Julian),Camijn,JoseVizcarrondo,ShaneHeres,
MariluAguilar,BrigitFasolinoVucic,Sean

$1Backers
JohnSturgill,LeonardBurnsIV,Duo,AaronZemetres,DavidMathiasSimacek2,
TomaszMichalFilip,Kaczmarek

191

You might also like