Transcript
  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 1/21

    Tutorials Articles Premium About

    Unity2DSnakeTutorial

    Foreword

    ThisTutorialwillexplainhowtomakeasimple2DSnakeGameinUnity.Snakeisanarcadegamethatwascreatedbackinthe1970s.Andlikemostarcadegamesitsstillawholelotoffunandeasytodevelop,whichmakesitagreatgameforaTutorial.

    Requirements

    KnowledgeOurTutorialdoesnotrequireanyspecialskillsexceptsomeknowledgeabouttheUnitybasicslikeGameObjectsandTransforms.Evenifyoudontknowthoseconceptsyet,theTutorialshouldstillbedoable.

    FeelfreetoreadoureasierUnityTutorialslikeUnity2DPongGametogetusedtothisamazinggameengine.

    UnityVersionOurSnakeTutorialwilluseUnity5.0.0f4.Newerversionsshouldwork

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 2/21

    neaswell,olderversionsmayormaynotwork.ThefreeversionofUnity5nowcomeswithalltheenginefeatures,whichmakesittherecommendedversion.

    ProjectSetup

    Letsgettoit.WewillstartUnityandselectNewProject:

    Wewillnameitsnake,selectanylocationlikeC:\,select2DandclickCreateProject:

    IfweselecttheMainCameraintheHierarchythenwecansettheBackgroundColortoblack,adjusttheSizeandthePositionlikeshown

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 3/21

    inthefollowingimage:

    Note:Sizeispreymuchthezoomfactor.

    AddingBorders

    Wewilluseonehorizontalandoneverticalwhitelineimageforourborders:

    border_horizontal.pngborder_vertical.pngNote:rightclickeachlink,selectSaveAs...andsavetheimagesintheprojectsAssetsfolder.

    OncewehavetheminourProjectsAssetsfolder,wecanselecttheminUnitysProjectArea:

    AfterwardswecanchangetheirImportSeingsintheInspectortomakethemappearintherightsizewiththerightlooks:

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 4/21

    Note:PixelsPerUnitistheratiobetweenonepixelintheimageandoneunitintheworld.TheSnakewillhaveasizeof1x1pixel,whichshouldbe1unitinthegameworld.ThisiswhywewilluseaPixelsPerUnitvalueof1forallourtextures.

    Nowwecandrageachborderimageintothescenetwiceandpositionthemsotheyformonerectangularborder:

    Note:thehorizontalborderimageisusedforthetopandboomborders.Theverticalborderimageisusedfortheleftandrightborders.

    LetsrenametheborderstoBorderTop,BorderBoom,BorderLeftandBorderRight.WewillselectoneafteranotherintheHierarchyandtheneitherpressF2orrightclickitandselectRename.Hereistheresult:

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 5/21

    Rightnowthebordersarejustimagesinthegame.Theymaylooklikeborders,buttheyarenotpartofthephysicalworldjustyet.IfwewanttheSnaketocollidewiththeborders,thenwehavetoaddColliderstothem.WecandosobyrstselectingallthebordersintheHierarchy:

    Rightnowthebordersarejustimages,theyarentreallyborders.Thesnakecouldwalkrightthroughthembecausetheyarenotpartofthephysicsworldyet.LetstakealookattheInspectorandselectAddComponent>Physics2D>BoxCollider2D.Andsincewehaveallbordersselectedrightnow,thiswilladdaBoxCollider2Dtoeachofthem:

    Andthatsallthereistoit.Wejustcreatedthebordersforourgame

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 6/21

    withoutwritingasinglelineofcode,thankstothispowerfulgameengine.

    CreatingtheFoodPrefab

    Wedontwantoursnaketogethungry,soletsrandomlyspawnsomefoodinthegame.Asusualwewillstartwithafoodimage,inourcaseacoloredpixel:

    food.pngNote:rightclickonthelink,selectSaveAs...andsaveitintheprojectsAssetsfolder.

    WewillusethefollowingImportSeingsforthefoodimage:

    Alright,letsdragthefoodimageintotheScenetocreateaGameObject:

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 7/21

    TheSnakeshouldreceivesomekindofinformationwheneveritcollideswithfood.Thismeansthatthefoodhastobepartofthephysicsworld,whichcanbedonewithaCollider.

    AGameObjectwithoutaColliderisjustavisualthing,itsnotpartofthephysicsworld.AGameObjectwithaColliderispartofthephysicsworld,justlikeawall.Itwillcauseotherthingstocollidewithit,anditwilltriggertheOnCollisionEnter2Devent.AGameObjectwithaColliderthathasIsTriggercheckedwillnotcauseotherthingstocollidewithit,butitwillstilltriggertheOnTriggerEnter2Devent.

    TheSnakeshouldgetnotiedwhenitwalksthroughfood,butitsnotsupposedtocollidewithitlikeifthefoodwasawall.SoletsselectthefoodintheHierarchyandthenchooseAddComponent>Physics2D>BoxCollider2DintheInspectorandenableIsTrigger:

    Okay,nowwedontwantthefoodtobeintheScenefromthebeginning.InsteadwewantaPrefabsothatwecanInstantiateitwheneverweneedit.InordertocreateaPrefab,allwehavetodoisrenamethefoodtoFoodPrefabandthendragitfromtheSceneintotheProjectArea:

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 8/21

    NowwecandeletetheFoodPrefabGameObjectfromtheHierarchy,becausewedontwantittobeinthegameworldjustyet.

    SpawningFood

    Letsspawnnewfoodatsomerandompositioneveryfewseconds.ThiskindofbehaviorcanbeimplementedwithaScript,soletscreateaSpawnFoodScript.TheScriptshouldbeintheSceneallthetime,sowewillkeepitsimpleandaddittotheMainCamera(becauseitwillbeintheSceneallthetime,too).LetsselecttheMainCameraintheHierarchyandthenclickonAddComponent>NewScript,nameitSpawnFoodandselectCSharpforthelanguage:

    AfterwardswewilldoubleclickitintheProjectAreatoopenit:

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 9/21

    usingUnityEngine;usingSystem.Collections;

    publicclassSpawnFood:MonoBehaviour{

    //UsethisforinitializationvoidStart(){}//UpdateiscalledonceperframevoidUpdate(){}}

    WewontneedtheUpdatefunction,soletsremoveit:usingUnityEngine;usingSystem.Collections;

    publicclassSpawnFood:MonoBehaviour{

    //UsethisforinitializationvoidStart(){}}

    OurScriptneedstoknowwherethefoodPrefabis.WewilladdapublicvariableoftypeGameObject:usingUnityEngine;usingSystem.Collections;

    publicclassSpawnFood:MonoBehaviour{//FoodPrefabpublicGameObjectfoodPrefab;//UsethisforinitializationvoidStart(){}}

    Thefoodshouldbespawnedwithintheborders,notoutside.SowewillalsoneedavariableforeachoftheborderssothatourScriptknowstheirpositions:usingUnityEngine;usingSystem.Collections;

    publicclassSpawnFood:MonoBehaviour{//FoodPrefabpublicGameObjectfoodPrefab;

    //BorderspublicTransformborderTop;publicTransformborderBottom;publicTransformborderLeft;publicTransformborderRight;

    //UsethisforinitializationvoidStart(){

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 10/21

    }}

    Note:theyarealreadyoftypeTransformsowedonthavetowriteborderTop.transform.positionallthetime.InsteadwewillbeabletoaccesstheirpositionslikeborderTop.position.

    LetscreatetheSpawnfunctionthatspawnsonepieceoffoodwithintheborders.Atrstwewillchoosethexpositionsomewhererandomlybetweentheleftandrightborder.Thenwewillchoosetheypositionrandomlybetweenthetopandboomborder.AfterwardswewillinstantiatethefoodPrefabatthatposition://SpawnonepieceoffoodvoidSpawn(){//xpositionbetweenleft&rightborderintx=(int)Random.Range(borderLeft.position.x,borderRight.position.x);

    //ypositionbetweentop&bottomborderinty=(int)Random.Range(borderBottom.position.y,borderTop.position.y);

    //Instantiatethefoodat(x,y)Instantiate(foodPrefab,newVector2(x,y),Quaternion.identity);//defaultrotation}

    Note:xandyareroundedvia(int)tomakesurethatthefoodisalwaysspawnedatapositionlike(1,2)butneveratsomethinglike(1.234,2.74565).

    NowletsmakesurethatourScriptcallstheSpawnfunctioneveryfewseconds.WecandosobyusingInvokeRepeating://UsethisforinitializationvoidStart(){//Spawnfoodevery4seconds,startingin3InvokeRepeating("Spawn",3,4);}

    Hereisourfullscript:usingUnityEngine;usingSystem.Collections;

    publicclassSpawnFood:MonoBehaviour{//FoodPrefabpublicGameObjectfoodPrefab;

    //BorderspublicTransformborderTop;publicTransformborderBottom;publicTransformborderLeft;publicTransformborderRight;

    //UsethisforinitializationvoidStart(){//Spawnfoodevery4seconds,startingin3InvokeRepeating("Spawn",3,4);

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 11/21

    }

    //SpawnonepieceoffoodvoidSpawn(){//xpositionbetweenleft&rightborderintx=(int)Random.Range(borderLeft.position.x,borderRight.position.x);

    //ypositionbetweentop&bottomborderinty=(int)Random.Range(borderBottom.position.y,borderTop.position.y);

    //Instantiatethefoodat(x,y)Instantiate(foodPrefab,newVector2(x,y),Quaternion.identity);//defaultrotation}}

    IfwesavetheScriptandselecttheMainCamerathenwecanseethatallourpublicvariablesarenowshownintheInspector.TheyarestillNone,soletsdragtheFoodPrefabfromtheProjectAreaintotheFoodPrefabvariableandtheBordersfromtheHierarchyintotheircorrespondingslots:

    Note:wecandragsomethingintoaScriptsvariablebyliterallydraggingitwiththemousefromtheHierarchyorProjectAreaintothoseslotthingsthatcanbeseenintheabovepicture.

    Alright,nowitstimetopressPlayandwaitafewseconds.Weshouldbeabletoseesomenewfoodspawninbetweentheborders:

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 12/21

    CreatingtheSnake

    Letsnishthemainpartoftheourgame:theSnake.Asusualwewillstartbydrawingthesnakeimage,whichisjusta1x1pixeltexture:

    snake.pngNote:rightclickonthelink,selectSaveAs...andsaveitintheprojectsAssetsfolder.

    WewillusethefollowingImportSeingsforit:

    NowwecandragthesnakeimageintothemiddleoftheScene:

    SofaritsjusttheSnakeshead,soletsrenameittoHeadtokeepthingsclean:

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 13/21

    Thesnakeshouldbepartofthephysicsworld,whichmeansthatweneedtoaddaCollidertoitagain,soletsselectAddComponent>Physics2D>BoxCollider2D:

    Note:theColliderhasthesize(0.7,0.7)insteadof(1,1)sothatitdoesntcollidewithotherpartsofthesnakethatarerightnexttoit.WesimplywanttogiveUnitysomespace.

    Nowthesnakeisalsosupposedtomovearound.Asaruleofthumb,everythinginthephysicsworldthatissupposedtomove,needsaRigidbody.ARigidbodytakescareofthingslikegravity,velocityandmovementforces.WecanaddonebyselectingAddComponent>Physics2D>Rigidbody2D.Wewillusethefollowingseingsforit:

    Notes:TheRigidbodysGravityScaleis0becausewedontwantthesnaketofalltowardstheboomofthescreenallthetime.TheIsKinematicoptiondisablesthephysicalbehavioroftheRigidbody,soitdoesntreacttogravityorcollisions.Weonlyneedtoknowifthesnakecollidedwithsomething.WedontneedUnitysphysicstopushthingsaroundincaseofcollisions.Moreinfo:Rigidbody2DUnityDocs.

    Thenalsnakewillconsistofmanylileelements.TherewillalwaysbetheHeadatthefrontandthentherewillbeseveralTailelementslikehere:

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 14/21

    ooooox

    TheonlydierencebetweentheTailelementsandtheHeadisthattheheaddoesallthethinking.WewilladdaScripttoitlater.

    LetsdragthesnakeHeadfromtheHierarchyintotheProjectAreatocreateaPrefabandthennameitTailPrefabsowecanloaditwheneverthesnakegrows:

    Note:someUnityversionswillautomaticallyrenameGameObjectintheHierarchytoo,somakesurethattheoneintheHierarchyisstillnamedHead.

    Alright,letsselectthesnakeHeadintheHierarchyagainandclickonAddComponent>NewScript,nameitSnakeandselectCSharpasthelanguage:

    WecanopentheScriptbydoubleclickingitintheProjectArea:usingUnityEngine;usingSystem.Collections;

    publicclassSnake:MonoBehaviour{

    //UsethisforinitializationvoidStart(){}//UpdateiscalledonceperframevoidUpdate(){}}

    LetsmodifythetopoftheScripttoincludesomeListfunctionalitythatwewillneedlateron:usingUnityEngine;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Linq;

    publicclassSnake:MonoBehaviour{

    //UsethisforinitializationvoidStart(){

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 15/21

    }//UpdateiscalledonceperframevoidUpdate(){}}

    TheSnakeshouldalwaysmoveexactlyoneunitintowhateverdirectionitwantstomove.NowifwewouldallowittomoveineveryUpdatecall,thenitwouldbereallyfast.Insteadwewillonlyallowmovementevery300millisecondsbyusingUnitysInvokeRepeatingfunction.ItslikewecreateourownUpdatemethodthatonlygetscalledevery300msinsteadofeveryframe:usingUnityEngine;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Linq;

    publicclassSnake:MonoBehaviour{

    //UsethisforinitializationvoidStart(){//MovetheSnakeevery300msInvokeRepeating("Move",0.3f,0.3f);}//UpdateiscalledonceperframevoidUpdate(){}voidMove(){//DoMovementStuff..}}

    TheSnakeshouldalwaysbemovingintosomedirection,itshouldneverstandstill.SoletsdeneadirectionvariableanduseittomovethesnakeintheMovefunction:usingUnityEngine;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Linq;

    publicclassSnake:MonoBehaviour{//CurrentMovementDirection//(bydefaultitmovestotheright)Vector2dir=Vector2.right;

    //UsethisforinitializationvoidStart(){//MovetheSnakeevery300msInvokeRepeating("Move",0.3f,0.3f);}//UpdateiscalledonceperframevoidUpdate(){}

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 16/21

    voidMove(){//Moveheadintonewdirectiontransform.Translate(dir);}}

    Note:transform.Translatemeansaddthisvectortomyposition.

    ThedirectionvariableisoftypeVector2,whichmeansthatithasanxandyvalue.ThefollowingimageshowsdierentdirectionsforaVector2:

    IfwepressplaythenwecanalreadyseetheSnakemovetotheright:

    Theusershouldbeabletochangethemovementdirectionbypressingoneofthearrowkeys.NowwecouldjustcheckforkeypressesinourMovefunction,butthatwouldmakethegamefeellaggybecausethenwewouldonlydetectkeypressesevery300ms.InsteadwewillusetheUpdatefunctiontodetectkeypressesallthetime://UpdateiscalledonceperFramevoidUpdate(){//MoveinanewDirection?if(Input.GetKey(KeyCode.RightArrow))

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 17/21

    dir=Vector2.right;elseif(Input.GetKey(KeyCode.DownArrow))dir=Vector2.up;//'up'means'down'elseif(Input.GetKey(KeyCode.LeftArrow))dir=Vector2.right;//'right'means'left'elseif(Input.GetKey(KeyCode.UpArrow))dir=Vector2.up;}

    Note:ifyouarenotsurewhyweusedUpdateandMoveinsteadofjustUpdateorjustMove,feelfreetoputthecodefromMoveintoUpdateorthecodefromUpdateintoMove,thenyouwillseethatthesnakemovesrapidlyfastorthatthekeypressesaredetectedonlyrarely.

    IfwepressplaythenwecannowmovetheSnakewiththearrowkeys:

    TheSnakesTail

    LetsthinkabouthowtheSnakestailwillwork.Firstofall,letsassumewehaveasnakewithoneheadandthreetailelements:ooox

    Nowassoonastheheadmovestotheright,theobviousthingwouldbetomoveeverytailelementtowhereitsprevioustailelementwas,likethis:step1:ooox//snakedidn'tmoveyetstep2:ooox//headmovedtotherightstep3:ooox//firsttailfollowsstep4:ooox//secondtailfollowsstep5:ooox//thirdtailfollows

    Thiswouldwork,butitwouldalsobesomecomplicatedcode.Letsuseaneatliletricktomakeourliveseasier.Insteadofmovingonetailelementafteranother,wewillsimplymovethelasttailelementintothegaplikehere:step1:ooox//snakedidn'tmoveyetstep2:ooox//headmovedtotherightstep3:ooox//lasttailelementmovedintothegap

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 18/21

    Nowthatsoundslikeaneasyalgorithm.Ineverymovementcall,allwehavetodoismovethelasttailelementtowheretheheadwasbefore.

    Atrstwewillneedsomekindofdatastructuretokeeptrackofallthetailelementsthatwewilladdlateron://KeepTrackofTailListtail=newList();

    Note:itsreallyimportanttoaddusingSystem.Collections.Generic;andusingSystem.Linq;tothetopinorderforliststowork.

    Letsgettothecodethattakesthelasttailelement,removesitfromthebackandputsitintothegapmentionedabove:voidMove(){//Savecurrentposition(gapwillbehere)Vector2v=transform.position;

    //Moveheadintonewdirection(nowthereisagap)transform.Translate(dir);

    //DowehaveaTail?if(tail.Count>0){//MovelastTailElementtowheretheHeadwastail.Last().position=v;

    //Addtofrontoflist,removefromthebacktail.Insert(0,tail.Last());tail.RemoveAt(tail.Count1);}}

    Note:Translatesimplymeansaddthisvectortomyposition.Afterwardswecheckifthereisanythinginthetaillist,inwhichcasewechangethelasttailelementspositiontothegapposition(wheretheheadwasbefore).Wealsohavetokeepourlistorder,hencetheInsertandRemoveAtcallsattheend.Theymakesurethatthelasttailelementisnowtherstelementinthelist,too.

    AndthatwastheonlyslightlycomplicatedpartofourUnity2DSnakeTutorial.Nowwearealmostdone.

    FeedingtheSnake

    WewillusetheOnTriggerEnter2Dfunctiontoreceivecollisioninformation(whichwillhappenwheneverthesnakewalksintofoodorintoaborder).

    Wheneveritrunsintofood,wewillusetheexactsamemechanicsthatweusedforourmovementabove,exceptthatthistimeinsteadofremovingthelasttailelementandmovingitintothegap,wewillonlyInstantiateanewelementintothegap:ooox//gapoooox//gapfilledwithnewelement

    ItsimportanttounderstandthatwewillnotmaketheSnakelonger

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 19/21

    immediatelyafteriteatssomething.Justlikewithourarrowkeypresses,wewillwaituntilitactuallymoves.ThereforewewillneedanewvariablethatwewillsettotruewhenevertheSnakeatesomething://Didthesnakeeatsomething?boolate=false;

    WewillalsoneedapublicvariablethatletsusassigntheTailPrefablateron://Didthesnakeeatsomething?boolate=false;

    //TailPrefabpublicGameObjecttailPrefab;

    Note:thetwovariablesaredenedatthetopofourSnakescript.

    NowletsgettotheOnTriggerEnter2Dfunction.Thisonewillbestraightforwardagain.WewillndoutiftheSnakecollidedwithfood,inwhichcasewesettheatevariabletotrueanddestroythefood.Ifitdidntcollidewithfood,theniteithercollidedwithitselforwithaborder:voidOnTriggerEnter2D(Collider2Dcoll){//Food?if(coll.name.StartsWith("FoodPrefab")){//GetlongerinnextMovecallate=true;//RemovetheFoodDestroy(coll.gameObject);}//CollidedwithTailorBorderelse{//ToDo'Youlose'screen}}

    Note:weusecoll.name.StartsWithbecausethefoodiscalledFoodPrefab(Clone)afterinstantiatingit.ThemoreelegantwaytondoutifcollisfoodornotwouldbebyusingaTag,butforthesakeofsimplicitywewillusestringcomparisoninthisTutorial.

    Alright,letsmodifyourMovefunctionsoitmakestheSnakelongerwheneverateistrue:voidMove(){//Savecurrentposition(gapwillbehere)Vector2v=transform.position;

    //Moveheadintonewdirection(nowthereisagap)transform.Translate(dir);

    //Atesomething?TheninsertnewElementintogapif(ate){//LoadPrefabintotheworldGameObjectg=(GameObject)Instantiate(tailPrefab,v,Quaternion.identity);

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 20/21

    //Keeptrackofitinourtaillisttail.Insert(0,g.transform);

    //Resettheflagate=false;}//DowehaveaTail?elseif(tail.Count>0){//MovelastTailElementtowheretheHeadwastail.Last().position=v;

    //Addtofrontoflist,removefromthebacktail.Insert(0,tail.Last());tail.RemoveAt(tail.Count1);}}

    Note:allwedidwascheckifateistrue,thenInstantiatethetailprefabatthepositionvwiththedefaultrotation(Quaternion.identity).Afterwardsweaddittothetaillistandresettheateag.Therestofthecodewasalreadythere.

    NowwecanselectthesnakeHeadintheHierarchy,takealookattheInspectoranddragtheTailPrefabfromtheProjectAreaintotheScript:

    IfwepressPlaythenwecannowplayaniceroundofSnake:

    Summary

    SnakeisoneawesomegameforaUnityTutorial.ThereisalotofvalueinunderstandinghowtodopixelexactgameslikethisandhowtoaddmovementwithInvokeRepeating.Yetagainwesawhowamazinglyeasy2DgamesarewithUnitys2Dfeaturesthatwereintroducednotverylongago.

  • 12/06/2015 noobtutsUnity2DSnakeTutorial

    http://noobtuts.com/unity/2dsnakegame 21/21

    Nowitsuptothereadertomakethegamefun!Therearetonsofimprovementstobemadelikeawin/losescreen,abeerlookingsnaketexture,multiplelevels,powerups,increasingspeed,highscores,multiplayerandsoon.

    DownloadSourceCode&ProjectFiles

    TheUnity2DSnakeTutorialsourcecode&projectlescanbedownloadedbyPremiummembers.

    AllTutorials.AllSourceCodes&ProjectFiles.OnetimePayment.GetPremiumtoday!

    20122015noobtuts.com


Recommended