Sie sind auf Seite 1von 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
A
sprite
inGameMakerreferstoanimagethatrepresentanobjectinyourgame.
Withoutsprites,alloftheobjectsinthegamewouldbeinvisible.

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

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

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

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

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

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

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

Fonts
Youcanusethisresourcetocreatedifferent
fonts
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
A
view
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
A
comment
inGameMakerLanguageisasectionoftextthatiscompletelyignoredby
the
compiler
(thepartofGameMakerthatturnsyourcodeintoagame)inotherwords,
commentsdontdoanythingforyourgame.Commentsareusefultoprogrammers
becausetherearetimeswhenitisbeneficialtodescribeapieceofcode.

AddingCommentstoCode
TherearethreewaystodeclareacommentinGameMakerLanguage.Thefirstisto
placetwoforwardslashes,
//
,beforethelineofcode.Thiswill
commentout
(wesay
commentouttoremindourselvesthatthe
comment
is
out
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

Thisiswhatthedescriptionofthe
Code Action
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
why
behindthemorecomplicatedsectionsandnotjustthe
how
.

Hopefully,thesefewpageshaveconvincedyouofthebenefitsofusingcomments,
whilealsopersuadingyoutotakeadvantageofthosebenefits.

Literals
A
literal
isavaluethatappearsdirectlyinyourcode.Herearesomeexamplesofwhat
literalslooklikeinsideGameMakerLanguage:

true
false
3
3.5
"Game Maker!"
'Language'

// The boolean value true


// The boolean value false
// A real number
// A decimal number
// A double-quote string
// A single-quote string

Identifiers
Identifiers
arenamesgiventovariables.Hereisanexampleofhowtouseanidentifier:

age
=
25;

Doyourecognizewhatpartofthiscodeistheidentifier?Itsthewordage.The
number25representsthe
literal

value
ofthevariableandthewordageisthe
name
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
(e.g.,
obj_player
)

IdentifierTips
GameMakerStudiousesanamingpracticecalled
snakecase
formostofitsbuiltin
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
called
HP
.

hp
=
100
;
HP
=
200
;

Thesetwovariablesare
different
.Ifyoutrytousetheidentifier
HP
toaccess
hp
,you
willeithergetthewrongvalue(if
HP
hasbeendefined)orgetanerror(if
HP
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

Iwould
highly
recommendgettingintothehabitofplacingsemicolonsafterevery
statementbecausemanyothermajorprogramminglanguagesrequireit.

23

Chapter2

DataTypesandValues
NumberLiterals
A
numberliteral
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
);
round
(
5.3
);
floor
(
5.6
);
ceil
(
5.3
);
abs
(-
4
);

/ Rounds 5.6 up to 6
/
// Rounds 5.3 down to 5

// Rounds 5.6 down to 5

// Rounds 5.3 up to 6

// 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
A
stringliteral
isalistofzeroormorecharacterssurroundedbysingleordouble
quotationmarks.Herearesomeexamplesofstringliterals:

"
Ben
"
"
3
"
'
GameMaker
'
'
'

Notethatthestringliteral
3
isdifferentfromthenumberliteral
3
.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
A
booleanvalue
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
Variables
inGameMakerLanguage(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
scope
ofavariabledescribesthelocationsinthecodewhereyouhaveaccessto
thatvariable.InGameMakerLanguage,therearethreemainvariablescopes.Thereis
the
globalscope
,the
instancescope
,andthe
localscope
.

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
.
Itisbesttodefinethesevariablesinthe
Create Event
oftheobjectinordertoavoid
errorscausedbyundefinedvariables.Thereisnosecrettodefiningthesevariables
yousimplynamethevariablewithanidentifierandthenassignavaluetoit.

name
=
"Benjamin Anderson";

Afterdeclaringtheminanobject,youcanaccessinstancevariableselsewhereinside
theobject.

InolderversionsofGameMaker,instancevariableshadtobedefinedinthe
Create
Event
ofanobjectorthecompilerwouldthrowanerror.

LocalVariables
Ifavariableiscreatedinthelocalscope,itcanonlybeaccessedwithintheactionor
scriptinwhichitwasdefined.Variableslocatedinthelocalscopearecalledlocal
variables.Definingalocalvariableisjustlikedefininganinstancevariable,onlyyou
placethe
var
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
);

Itshouldalsobenotedthat
macrosfallundertheglobalscope
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
}

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

Inalaterchapter,Iwillshowyousomewaystouseenumsincombinationwitharrays
togiveyourcodebetterstructureandalsomakeiteasiertoread.

31

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

32

Chapter4

ArraysandDataStructures
Arrays
An
array
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
';

Ifyouimaginethatthearray
names
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
containswhatiscalledthearrays
index
.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.

Theotherfunction(onethatI,personally,usemoreoften)is
array_length_1d
.
vara;
a
[
0
]=
0;
a
[
1
]=
0;
show_message
('
The size of the array is:
'+
string
(
array_length_1d
(
a
));

Thisfunctionreturnsthearrayssize(asanumber).Youmaywonderwhythefunction
has
_1d
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
codewillcreateastackandthenassignitsidtothe
my_stack
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,adda
Create 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
toitanddragoveran
Execute 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
.Withthisone,wewillusethe
Mouse, Left Released Event
.

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
and
mouse_y
positions.Afterthat,wecheckto
seeifthemouseishoveringoverthedeck.Ifitishoveringoverthedeck,wepushthe
global.card
spriteindextothestack.Forthisexample,wereallyonlyneedtoknow
whatspritetheobjecthad.Afterpushingtothestack,wedestroythecardobject.Once
allofthesestepshavebeenexecuted,wesetthe
global.card
backto
noone
andset
thedepthbacktozero.Ofcourse,ifthemouseisnthoveringoverthedeck,thenwewill
ONLYset
global.card
to
noone
andsetthedepthtozero.Thiswilldropthecardon
theground,wherewecanpickitupagainlater.

Greatjobsofar!Thereisonlyonemorethingforustoaddtogetourgameworking.
Weneedtoallowtheplayertotakeacardoffthetopofthedeck.Openupthedeck
objectagainandadda
Mouse, Left Pressed Event
toit.Draga
Code Action
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
andstoreitinalocalvariablecalled
top_card
.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
A
queue
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
The
list
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

The
ds_list_find_value
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
notcodeandyoucantadddatatoa
ds_map
likethis.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
Grids
arethelastdatastructurethatIwilltalkaboutinthisbook.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
thelocalvariable
num
.

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,youwillnoticetheOsubimageinourspritehasan
image_index
of0andtheXsubimageinourspritehasan
image_index
of1.Wewillbeusingthis
facttoouradvantage.

ItstimeforthenextphaseinourTictactoeexample.Addanew
Mouse > Global >
Global Left Pressed Event
anddragovera
Code Action
.Insidethe
Code Action
,
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.The
ds_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
div
operator.
Thisoperatortakestwooperands,dividesthem,andreturnsawholenumberanswer
(meaningthatitdoesnthavetheremainder).The
mod
operatorandthe
div
operator
areverysimilar.Thedifferenceisthatthe
mod
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
one
forloop
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
iteration).Ifitdoesntequal1,wewilldrawaspritewithan
image_index
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
An
expression
isacombinationofoperatorsandoperands.Ifyouhaventlearneda
programminglanguagebefore,thatstatementmightsoundalientoyou.Illexplainitin
thischapter,andyoushouldfindthatitisprettyeasytounderstand.Letmeshowyou
someexamplesofstatements.Youveactuallyseenthembefore.

x
=
10;

Easy,right?Itoldyoutheywouldbefamiliar.Letmedissectthisexpressionforyouso
thatIcanexplainmyearliercomments.The
x
andthe
10
arethe
operands
inthis
statement.Theyrepresentthedatabeing
operated
on.The
=
isthe
operator
.Itis
operating
onthetwooperands.Thesingleequalssigniscalledtheassignment
operator.Thisisbecauseit
assigns
therightoperandtotheleftoperand.Youwillalso
noticetherearetwooperands.Thatmakestheassignmentoperatora
binary

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

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

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

56

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

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

AssignmentOperator
The
assignment
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
The
add
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
Unliketheaddoperator,the
subtract
operatorcannotbeusedwithstringsitwillonlybe
usedwithnumbers.Withnumbers,itdoesthesimplemaththatyouwouldexpect.

/// Use the subtract operator to subtract two numbers


varresult
=
5
-
3
;
// Returns 2

Thisoperatorisstraightforward.Justremembernottouseitwithstrings.

MultiplyOperator
The
multiply
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
The
divide
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
The
not
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
allowsyouto
toggle
thevariablesvaluefromfalsetotrueorfromtruetofalse,andyou
donthavetoknowwhatitwasoriginally.Youjustknowthatitisnowtheoppositeof
whatitwasbefore.

Youcanalsousethenotoperatorinconditionstatements.Youhaveseenthisina
previoussectionthattalkedaboutcombiningtheequalityoperatorwithotheroperators.

DivisionOperator
61

The
division
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
The
modulo
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
The
if
controlstatementisveryimportanttoprogramminginanylanguage.Itmakesit
possibletoexecutecodebasedonaspecifiedcondition.Hereisanexampleofwhen
youmightusean
if
statementinGameMakerLanguage:

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

Thiscodewillchecktoseeifthekeyboardsrightarrowkeyisbeingpressedifitis,
thenthecodewilladd4tothecurrentxpositionoftheobjectthatisrunningthecode.
Adding4tothecurrentxpositionwillmovetheobjecttotherightonthescreen.

Nowthatyouhaveseenaningameexampleofwhereyoumightuseanifstatement,
letmeshowyouagenericexample.

if
(
condition
)
{
statement
;
}

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

63

FirstExample
if
(
condition
){
statement
;
}

SecondExample
if
(
condition
)
{
statement
;
}

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

fewer
bugsifyouareconsistent.

Else
The
else
controlstatementgoesrightalongwiththeifcontrolstatement.Theelseis
whathappensiftheconditioninsidetheifevaluatestofalse.

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

Elseif
The
elseif
controlstatementisalsocombinedwiththeifcontrolstatement.Youcan
chainelseifasmanytimesasisnecessarytobuildthelogicalstructurethatyouwantto
use.

if
(
condition1
){
64

// Condition1 is true? Do this


// statement
}
else
if
(
condition2
){
// Condition2 is true? Do this
// statement
}
else
if
(
condition3
){
// Condition3 is true? Do this
// statement
}
else{
// None are true? Do this statement
}

Switch
The
switch
controlstatementisanalternativetotheif/elseif/elsecombination.
switch
(
expression
){
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.The
break
statementattheendofeach
case
preventsthe
switch
statementfromcheckingtheothercasesaswell.

With
With
isapowerfulcontrolstatementinsideGameMakerthatallowsyoutorunsome
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,you
must
usethewithstatementyou
cantusethedotoperator.

LoopStatements
Loop

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

Repeat
Repeat
istheshortestandthesimplestofalloftheloops.Itallowsyoutorunthesame
sectionofcodemultipletimesinasingle
Step 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
The
while
loopisalittlemorecomplicatedthantherepeatloop,butitsnottoobad
either.Thewhilelooptakesacondition,and,iftheconditionistrue,willrepeatallthe
codewithinitscurlybraces.Oncetheconditionisfalse,thewhileloopwillexit.

For
The
for
loopisprobablythehardesttounderstandoutofallofthecontrolstatements,
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.

for
(
initialize
;
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
i
andsetitto0.Afterthat,itwillchecktoseeif
i
islessthan2becauseitislessthan2,the
show_message

codewillberun.Then,the
iterationsectionwillberunand
i++
willadd1to
i
,makingitnowequalto1.The
conditionsectionwillmakesurethat
i

isstilllessthan2.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)andthen
returns
theirsum.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

reaches9,itwillcallthe
break
statementandexittheloop,
runningonly10times.

Breakstatementselsewherewillalsoexitthelooponcetheyarereachedbythe
compiler.Thismeansthateverylineofcodebeforethebreakwillberuninsidethat
loop,andeverylineafteritwillnotberun.

Breakstatementswillexitoutofforloops,whileloops,repeatloops,andwith
statements.

Continue
The
continue
statementworkssimilarlytothebreakstatement,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
could
call
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
to
scr_move
andpassitsomearguments.

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

Thereisanarrayinthelocalscopeofeachscriptcalled
argument
.Itcontainsthe
valuesoftheargumentsthatwerepassedtothescriptintheorderinwhichtheywere
passed.Thefirstvaluepassedinwillbeassignedto
argument[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
variablethanthegeneric
argument[0]
.

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

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

Youmightnotunderstandallofwhatisgoingonhere,butknowthatthisscripttakes
twoarguments(values)andthen
returns
thesum.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

Aninstances
id
isrepresentedbyarealvalue,inotherwords,anumber.Youcanstore
thisnumberinavariablethatcanlaterbeusedtorefertotheinstance.Youwillfindan
instancesidcanbeyourbestfriend.
76

solid

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

visible

The
visible
instancepropertyisasimplebooleanpropertythatdetermineswhether
theinstanceisdrawnonthescreenornot.

persistent

The
persistent
propertyisanotherbooleanpropertythatdetermineswhetherthe
instanceiscarriedoverbetweenrooms.Ifpersistentissettotrue,thentheinstancewill
notbedestroyedwhenchangingroomsitwillalsoretainthevaluesofallofitsother
properties.

depth

The

depth

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

than5.
alarm

77

The
alarm

propertyisanarraythatisusedinconjunctionwithalarmevents.The
property
alarm[0]
wouldreturnthevalueoftheAlarm0event.
object_index

The
object_index
propertyreturnsarealvaluerepresentingtheidofthatobject(not
theinstance).Thispropertycanbeusedtocompareinstancestoseeiftheywere
createdfromthesameobject.

The
x
propertyrepresentsthecurrentxpositionoftheinstance.
y

The
y
propertyrepresentsthecurrentypositionoftheinstance.
xstart

The
xstart

propertyisthexpositionoftheinstanceatthemomentwhenitwas
created.

ystart

The
ystart

propertyistheypositionoftheinstanceatthemomentwhenitwas
created.

xprevious

78

The
xprevious

propertyisthexpositionoftheinstanceinthepreviousstep.
yprevious

The
yprevious

propertyistheypositionoftheinstanceinthepreviousstep.
direction

The
direction
propertyisthedirectioninwhichtheinstancewillmoveifthe
speed
propertyisset.Itusesdegrees,anditdefaultsto0.

speed

The
speed

propertyisthespeedatwhichtheinstancewillmove.Ifthe
direction
propertyisnotset,thentheinstancewillmovetotheright.

friction

The
friction

propertyisavaluethatgetssubtractedfromthe
speed

propertyatevery
step.Ifyouhaveaspeedof10andafrictionof1,thenitwilltake10stepsforthespeed
tofalltozero.

hspeed

The
hspeed

propertyrepresentsthehorizontalspeedoftheinstance.Apositivenumber
willmovetheinstancetothe
right
,andanegativenumberwillmovetheinstanceto
the
left
.
vspeed

79

The
vspeed

propertyrepresentstheverticalspeedoftheinstance.Apositivenumber
willmovetheinstance
dow
n
,

andanegativenumberwillmovetheinstance
up
.
SpriteInstanceProperties
Thespriteinstancepropertiesareasetofpropertiesthatareusedtocontrolthesprite
associatedwithaparticularinstance.

sprite_index

The
sprite_index

propertycanbeusedtogetorsetthespriteassociatedwithan
instance.Itisgenerallyusedtochangethespriteofaninstance.

image_index

The
image_index

representstheanimationsubimageindex.Ifyouhaveananimation
withfivesubimages,andyouwanttosetittothefirstone,youwouldset
image_index
to0.

image_speed

The
image_speed

propertycontrolsthespeedatwhichthespriteanimates,ormore
specifically,thespeedatwhichitcyclesthroughitssubimages.

image_count

The
image_count

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

80

ObjectInheritance
ObjectinheritanceisoneofmyfavoritefeaturesofGameMaker.Asolidunderstanding
ofthissectionisveryhelpfulifyoueverwanttobuildalargescaleprojectlikea
roleplayinggamebecauseitallowsyoutoreusecodeinobjectsthatwillhavesimilar
behaviors.Thefirstthingthatyouneedtoknowishowtosetupobjectinheritanceand
whatthecommontermsare.

SettingupInheritance
Youcanmakeoneobjectinheriteventsandcertainpropertiesfromanotherwith
GameMakersinheritancesystem.GameMakerusesthetermsparentandchildto
describetheinheritancerelationshipbetweenobjects.The
child
objectwillinheritevents
andcertainpropertiesfromits
parent
object.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
willnot
inheriteventsthattheypossessthemselves.
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
objectthatcontrolsthe
actionsthatarethesame
andthentwo
child
objects
thatwilladdtotheparentsactionsand
controltheactionsthataredifferent
.

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.

The
instance_create
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.Thisfunctioniscalled
instance_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
.
itiscalled
instance_furthest
.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
);

Thislineofcodewilldraw
spr_target
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,
butitisalsouseful.Itiscalledthe
instance_place
function.Thisfunctionworksalmost
exactlylikethe
instance_position
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
called
noone
.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
isalmostexactlylikethe
Step Event
theonlydifferenceis
thatthe
Begin Step Event
runsfirst.Ifyouneedtorunaspecificbitofcodeevery
gameframebutwanttorunitbeforeyour
Step Event
,thenthisistheeventforyou.

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

Step Event
x
=
mouse_x;
y
=
mouse_y;

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

AlarmEvents
The
Alarm 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
the
Alarm Event
,wecandefinetheactionsthatwewanttohappenwhenthealarm
reacheszero.

Alarm 0 Event
/// Execute some action at the end of the alarm
show_message
("
Wakeup
,sleepy head
.");

Inthe
Alarm Event
,wecandragovera
Code 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
callingthe
Collision Event
,andthen,wedestroytheotherobjectinthecollision
usingthe
other
reference.
89

Well,nowyouknowbasicallyallthatyouneedtoknowinordertousecollisionevents
inyourgame.

KeyboardEvents
A
Keyboard 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;

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

withthe
keyboard_check
function.

90

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

KeyReleaseEvent
The
Key Release Event

alsoworkssimilarlytothe
Key Event
,butitwillonlyfirein
thesinglestepinwhichthekeyboardkeyisreleased.

MouseEvents
A
Mouse Event

isfiredwhenthecursorishoveringoverthecollisionboxfortheobject
callingtheeventandaspecificmousebuttonispressed,or,inotherwords,whenthe
userclicksontheobjectcontainingthe
Mouse Event
.The
Mouse 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

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

MousePressedEvents
The
Mouse Pressed Event

issimilartothe
Mouse Event
,

exceptthatitwillonlyfire
duringthestepinwhichthemousebuttonispresseddown.Hereisasimpleexample:

91

Mouse Left Pressed Event


/// Destroy the instance
instance_destroy
();

Thisbitofcodedestroystheinstancethatisclickedon.

MouseReleasedEvents
The
Mouse Released Event

isalsosimilartothe
Mouse Event
,

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

Mouse Left Released Event


/// Destroy the instance
instance_destroy
();

Onceagain,inthisbitofcode,wedestroytheinstanceclickedon,butonlywhenthe
mousebuttonisreleased.

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

AlarmsandGlobalLeftPressedforFiringBullets
Hereisacombinationofthe
Global Left Pressed Event
andan
Alarm Event
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
The
Global Mouse Pressed Event
workssimilarlytothe
Global Mouse Event
,
exceptthatitonlyfiresinthesinglestepinwhichthemousebuttonispresseddown.

GlobalMouseReleasedEvents
The
Global Mouse Released Event
alsoworkssimilarlytothe
Global Mouse Event
,
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
The
No More Lives Event
willfireifyouareusingGameMakersbuiltin,globallives
variableandthevariablereaches0.

NoMoreHealthEvent
The
No More Health Event
willfireifyouareusingGameMakersbuiltin,global
healthvariableandthevariablereaches0.

AnimationEndEvent
The
Animation End Event
fireswhenanobjectsspriteanimationloopreachesthe
lastsubimage.

EndofPathEvent
The
End of Path Event
firesifanobjectismovingalongapathanditreachesthe
endofthatpath.

UserDefinedEvent
Userdefinedeventsfirewhentheuserfiresthem.Theycanbefiredanywhereinyour
codeusingthe
event_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
);

Westartbycreatingthe
User Defined Event
.Forthesakeofthisexample,Imjust
showingamessage.After,wecalltheeventusing
event_user
.Wechosetouseevent
number0,andso,wepassthatintothe
event_user
functionsothatGameMaker
knowswhichusereventtofire.Thereare16usereventsthatcanbeused.

95

DrawEvent
The
Draw Event

fireseachstepofthegame,justlikethe
Step Event
.Itisagoodidea
toputaslittlecodeinthe
Draw Event
aspossible.Youshouldonlyputcodeinthe
Draw Event
thatactuallydrawssomething.Anotherthingtonoteisthatthe
Draw
Event

willfireforeveryinstanceinyourroom,unlessthatinstancehastheVisible
objectpropertyunchecked.Ifyouaddyourown
Draw Event

toanobject,beawarethat
youwilloverridethedefault
Draw Event
ifyoudontplacecodeinyour
Draw 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 Event
willaffecthow
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
using
draw_set_halign
.Then,wesettheverticalalignmenttoamiddlealignment
using
draw_set_valign
.Onceoursetupisdone,wedrawthetextusingthe
draw_text_color
function.Thefirsttwoargumentsarethepositionofthetext,the
thirdisthetexttodraw,thenextfourarethecolorstouse(oneforeachcornerofthe
text,asthisfunctioncanbeusedtocreateagradientinthetext),andthefinalisthe
alphavalueofthetext.

DrawGUIEvent
The
Draw GUI Event
existsonaseparateplanefromthe
Draw Event
,

butworks
similarlytoit.Everythingdrawninthe
Draw GUI Event

willbedrawnontopofthe
itemsdrawninthe
Draw Event
.The
Draw 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
sprite
spr_button
.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

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

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
);

Inthiscode,wepassthe
audio_em
variableintothe
audio_emitter_free
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
);

Westartbycallingthe
audio_emitter_pitch
function.Wetellitwhatemitterwewant
touseandthepitchthatwewantouremittertobesetto.Wepassinarandomrange
from
.5
to
1.5
.Anormalpitchissetat
1
.Next,weusethe
audio_emitter_gain
function.Wealsopassintheemitterandusearandomrangeof
.1
to
1
.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
Settingupa
statesystem
inanobjectissmartifyouwantittohavedifferentbehaviors.
Letssayyouwantyourplayerobjecttobeabletorun,swingasword,rolloutofthe
wayofenemyattacks,andthencontinuerunning.Thesethreebehaviorscouldhave
theirownstateswithintheobject.Therearedifferentwaysofimplementingthiskindof
system,butIcanshowyousomeofthewaysthathavebeenhelpfultomeinthepast.

WhenIwasfirstlearningtosetupstatesystems,Itriedcreatingadifferentobjectfor
eachstateandthenIwoulduse
instance_change
tochangestates.Thisworked,but
notverywell.
instance_change
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

Onceyouhavethisasyourmacro,youcansetyourstateinthe
Create Event
like
this:

state
=
PLAYER_WALK;

Youcanusethesamemethodtochangethestateofyourobjectinsideotherevents.
Afterdoingthesefirsttwosteps,youstillneedtoactuallyrunthecodeassociatedwith
thatstate.Todoso,youwouldplacethisinthe
Step 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
builtin
image_angle
propertyandthe
point_direction
function.

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

Youshouldalsomakesurethatyourspritehasitsoriginwhereyouwantthepivotpoint
tobe,andthatthespritestartsoutfacingexactlytotheright(or0degrees).

UsingMedianorClamp
Oneneattrickthatyoucandoisusethe
median

functionorthe
clamp

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

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

my_red
=
make_colour_hsv
(
0
,
58
,
80
);

The
make_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
);

The
merge_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_left
isaconstantinGameMakerLanguagethatreferstotheleftmousebutton.
Thereareconstantsforeachoftheothermousebuttons.

mb_none
mb_left
mb_right
mb_middle
mb_any

// No mouse button
// Left mouse button
// Right mouse button
// Middle mouse button
// Any mouse button

mouse_check_button()
isafunctionthatreturnstrueifthebuttonisbeingpressedand
returnsfalseifitisnot.Atthispoint,Ishouldclarifythedifferencebetweenthecasein
whichthemousebuttonis
being
pressedandthecaseinwhichthemousebutton
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
anobjectfollowthemouse(oranotherobject)inthe
End Step Event
.

KeyboardInput
Keyboardinputisprettyeasytolearnhowtouseifyoualreadyknowhowtousemouse
input,asthetwoareverysimilar.

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

vk_right
isaconstantthatissimilartothe
mb_left
constantthatthemouseinput
uses.Therearequiteafewdifferent
vk_
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
using
obj_solid
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
becausethe
Draw 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
theactualsizeoftheviewis.Placethiscodeinthe
Create Event
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
normal
Draw 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
them
obj_player
andtheotherone
obj_gui_button
.

obj_player
obj_gui_button
Createanewroomandnameit
rm_test
.Givetheroomawidthof640andaheightof
360.Clickontheviewstab,clickEnabletheuseofViews,clickVisiblewhenroom
118

starts,settheviewwidthandheightto320x180,andsettheportwidthandheightto
640x360.SettheObjectfollowingto
obj_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
the
Step Event
.

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
width
and
height
.Thenwehaveavariablecalled
hover
.Our
hover
variablewillbetrueifthemouseisoverthebuttonandfalseifitis
not.Wealsocreateavariablecalled
scale
andsetitto2.Ourscaleissetto2because
theGUIlayerdefaultstothesizeoftheportonthescreenandtheportonthescreenis
twice
thesizeoftheview.Aftercreatingtheinitialinstancevariables,wecreatetwo
newinstancevariablesthatwillbeusedtopositionthebuttonontheGUIlayer.These
twovariableswillgrabthebuttonobjectsstartingpositionintheroomandmultiplyitby
our
scale
.

120

Addanew
End 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,weusethe
point_in_rectangle
functiontodetermine
whetherthemouseishoveringovertheGUIbutton.The
point_in_rectangle

function
takessixarguments.Thefirsttwoarethepointtocheck,thenexttwoaretheupperleft
corneroftherectangle,andthefinaltwoarethelowerrightcorneroftherectangle.

Adda
Mouse > Global Mouse > Global Left Pressed Event
totheGUIbutton
object.Dragovera
Code Action
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
);

Usingthe
hover
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
A
particlesystem
isaworldthattheparticleslivein.InGameMaker,itisimpossibleto
createparticleswithoutfirstcreatingaparticlesystem.Creatingaparticlesystemis
actuallyeasierthancreatingtheparticlesthemselves.Thinkofitthisway.Forobjects,
youmustfirstcreatearoomtoputtheminortheobjectscantbeusedorseen.Particle
systemsareliketheroomandtheparticlesareliketheobjects.Inmostcases,youwill
onlyneedtocreateoneparticlesystemtocontainallofyourparticles.Becausethereis
generallyonlyone,Iliketocreateanobjectforcontrollingtheparticlesandtheparticle
system.Inthisobject,Icreateaglobalvariableandassigntheparticlesystemtoitlike
this.Iwouldplacethiscodeinthe
Game 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;

Thelastlineallowsustouse
pt
astheidfortheparticletype,whichislesstyping.Lets
settheshapeoftheparticle.

part_type_shape
(
pt
,
pt_shape_disk
);

Thefirstargumentistheidoftheparticletype.Iuse
pt
,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
the
Game End Event
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
Using
part_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
emittershape
isaglobalconstantdefinedbyGameMaker.Hereisalistofthe
differentemittershapesthatyoucanuse:

Particle Distribution Shape Types


pt_shape_rectangle
pt_shape_ellipse
pt_shape_diamond
pt_shape_line

128

The
particledistributiontype
isalsoaglobalconstantdefinedbyGameMaker.There
areonlythreedifferenttypesthatyoucanuse.Heretheyare:

Particle Distribution Types


ps_distr_linear
ps_distr_gaussian
ps_distr_invgaussian

The
lineardistribution
isanevendistributionacrosstheregionoftheemitter.The
gaussiandistribution
createsmoreparticlesnearthecenteroftheshapeandfewer
particlesclosertotheoutsideoftheshape.The
invgaussian
isaninverseofthe
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
the
Create 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
candothisusingthe
draw_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!

Createanewobjectandnameit
obj_paper
.Thiswillbetheobjectweusetocontrol
everythinginourgame.Adda
Create Event
totheobjectanddragovera
Code
Action
.Whenthenewcodewindowcomesup,writethis:

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

Itshouldbeeasytorecognizewhatthiscodedoes.Wearecreatingthreenew
variables.Surfaceissetto
noone
(becausewehaventcreatedthesurfaceyet)andthe
mousesprevioustwovariablesaresetthemousescurrentposition.Wewillusethese
fordrawingourlineslater.

Nowthatthe
Create Event
isfinished,adda
Draw Event
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_paper
inthe
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
setitto:
Circle
,
Box
,and
Shape
.CircleandBoxareselfexplanatory.Shapeisnttoo
complicated:wecandrawourowncollisionshape.Thisisverypowerfulandeasytodo.
JustclickthebuttonthatsaysModifyCollisionShapetostartcreatingacustomshape.
WemaywanttoclicktheModifyCollisionShapebuttonevenfortheCircleandBox
shapesGameMakertriestobesmartwhencreatingthem,butitoftenseemstoget
themwrong.Ifitdoesgetthemwrongandwedontchecktheshapeourselves,wemay
getstrangecollisionerrors.

Density
Now,letstalkaboutthesecondproperty,the
density
oftheobject.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
ora
torque
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
thenewobject
obj_car_body
.Setthespriteto
spr_car_body
,givetheobjectaparent
of
obj_collision_parent
,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
arereadytostartaddingsomecodetothisobject.Addanew
Step Event
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
.Setitsspriteto
spr_car_tire
,giveit
aparentof
obj_collision_parent
,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
itstimetoadda
Create Event
.Placethiscodeinthe
Create Event
forourtire
object.

obj_car_tire: Create Event


/// Initialize the tire
spd
=
1000;

Thisjustcreatesaspeedvariablethatwecanuselaterwhenmovingthetires.Notice,I
didntcallthevariable
speed
.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

andwecangobacktothecarbodyobject.Openitupandadda
Create Event
.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
and
tire.y
values.

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

objectdrawthephysicsworldwithaneatfunctioncalled
physics_world_draw_debug
.
Weprobablywouldntwanttousethisfunctionforanactualgame,butitwillworkwellin
thiscase,andwecanuseittobetterunderstandhowdifferentphysicsobjectswork.

Createanewobjectandnameit
obj_ground
.Givethenewgroundobjectaparentof
obj_collision parent
.WedontneedtocheckUsesPhysicsforthisobject,
becausewewillbedoingthatwithcodethistime.Adda
Create Event
andplacethis
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
fix
andwhereweuse
physics_fixture_create
andassigntheidofournewfixturetothe
fix
localvariable.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
The
server
isgenerallyacomputer/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
variable
server
.Thereareafewdifferentoptionsfortheservertype.Heretheyare:

network_socket_tcp
network_socket_udp

Inthefirsteditionofthisbook,Iwillonlybecovering
network_socket_tcp
.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
A
socket
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
called
Server

andthesecondshouldbecalled
Client
.Onceallofthecodeisinplace,
youwillruntheserverfirst,andthen,whenyouruntheclient,itwillconnecttothe
server.

Openuptheserverprojectonceyouhavecreateditandaddanewobjecttoit.Name
thisobject
obj_server
andadda
Create Event
toit.Dragovera
Code 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
easy.Well,sofarit
hasbeen
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
gettingareferencetothetypeof
Asynchronous 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
backto
noone
.

Thethird,andlast,eventtypeisthe
network_type_data
event.Itisthemostfunofthe
threeandtakesthemostwork,whichyoucanseebythefactthat,attheendofour
casestatement,wearecallingascriptthathandlesthedatawegetfromtheevent.The
firstthingwedoiscreatealocalvariablecalled
buffer
andassignthedatafromthe
bufferkeyinour
async_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
and
my
,respectively.Youcan
alsoseebymycommentsthatonceeachchunkofdataisread,itisremovedfromthe
buffer(aftertheyvalueisread,thereactuallyisntanymoreinformationinourbuffer).
Oncethebufferisempty,wehaveallofthedatathatweneedfromtheclienttocreate
themouseclickobjectandbreak.

Thelastthingthatweneedtodotofinishoursimpleserverprojectiscreatethemouse
clickobject.Addanewobjectandnameit
obj_mouse_click
.Adda
Draw 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
.Thisclientobjectwillneeda
Create Event
.Addthe
Create Event
,drag
overa
Code 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
bufferasizeof1024(1kB),atypeof
buffer_fixed
(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
tothe
obj_client
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).

Letsstart!Firstoff,addaspritecalled
spr_mark
.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
objects.Namethefirstone
obj_network
andthesecondone
obj_game
.Thesetwo
objectsareallthatweneedtocontroltheentiregame.

obj_network
obj_game

Startbyopeningupthenetworkobject,addinga
Create Event
toit,anddragginga
Code Action
intotheevent.The
Code Action
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.

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

Creatingtheserveriseasy.Wesetthe
global.turn
tomine,setthe
max_clients
to1,andcall
network_create_server
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
toit.This
Draw Event
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
),itwillreturna0.Ifweare
not
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
inthegridtothe
BLANK_MARK
value.
BLANK_MARK
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
);
}
}

This
Draw Event
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

and
spr_player
.

spr_enemy
spr_player

Ijustmadethemboth32x32circles.Myplayerisblueandmyenemyisred.After
creatingthesetwosprites,weneedtocreatetwonewobjects.Namethem
obj_enemy
and
obj_player
.

obj_enemy
obj_player

Openuptheplayerobjectandadda
Create Event
totheobject.Insidethis
Create
Event
,adda
Code Action
andplacethissimplecodeinside.

obj_player: Create Event


/// Initialize the player
hp
=
1;

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

obj_player
.Draga
Code Action
intothe
Step Event
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
playershpvariableislessthanorequalto0.Italsosetsthe
image_alpha
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
,wearegoingtoadda
Code Action
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.
This
Step Event
willcontaina
Code 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
.Addanew
Draw Event
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
yet.Letsdothatnow.Addanewscriptandnameit
scr_enemy_chase
.

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;
}

Thechasestatescriptstartsliketheidlescript,bysettingthe
state_text

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

Lastly,weneedtowritethecodethatwillbeintheattackscript.Addanewscriptand
nameit
scr_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,wesetthe
state_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
overa
Code Action
.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
spritetotheenemyobjectandthe
spr_player

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
and
spr_solid
.

spr_enemy
spr_solid

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

175

Nowcreatetwoobjectsandnamethem
obj_enemy
and
obj_solid
.

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
Action
andaddthissmallblockofcodeinit:

obj_enemy: Create Event


/// Set the initial state of the object
state
=
choose
(
scr_enemy_move_right
,
scr_enemy_move_left
);

Inthislineofcode,weareusingthe
choose
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
and
scr_enemy_move_left
.

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
arentany,then
right_free
willbesettotrue.Weuse
instance_position
tocheck
whetherthereisaledgetotherightofus.Ifthereisntaledge,
no_ledge
willbesetto
178

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

Open
scr_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
slightlydifferentcolor.Adda
Create Event

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
iswritethe
scr_enemy_chase

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
use
place_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
and
spr_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
mine
rm_maze
.Addinthegridobject,theenemyobject,andcreateamazeintheroom
usingthesolidobjecttoformthewalls.Besuretokeepthewallsectionobjects
(
obj_solid
)snappedtoa32x32grid.Hereisascreenshotofmyroom:

183

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

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
function.The
cw
and
ch
variablesstandforthecellwidthandthecell
height,respectively.Keepingthesenumbersaslargeaspossible(whilestillensuring
thattheenemymovementlooksgood)willreducethelikelihoodofperformanceissues
inyourgame.The
hc
and
vc
variablesstandforthehorizontalcellcountandthe
verticalcellcount,respectively.Wecalculatethesenumbersbasedontheroom
dimensionsandthecelldimensionsweareusing.Now,wearereadtocreatethegrid.
Thefirsttwoargumentsinour
mp_grid_create
functionarethestarting
x
andstarting
y
positionforthegrid.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!Yourgridobjectisfinishedandthe
global.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,weareusingthe
path_add
functiontocreateanewpathandassignittothe
path
instancevariable.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
and
y
position.Oncewehave
that,weusethe
mp_grid_path
functioninsideofanifconditiontodetermineifa
suitablepathwasfound.The
mp_grid_path
functiontakessevenarguments.Thefirst
argumentisthegridtousetocheckforasuitablepath,thesecondisthepaththatthe
functionwillusetocreateamovementsolution,thethirdandfourtharethestarting
positionofthepath(weusetheenemyobjects
x
and
y
position),thefifthandsixthare
theendpositionofthepath(weusethemouses
x
and
y
position),andthefinal
argumentisabooleanvaluethatdeterminesiftheobjectcanmoveat45

anglesinthe
path.Wesetthistotrue.Ifthe
mp_grid_path
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

Das könnte Ihnen auch gefallen