+ All Categories
Transcript
  • GraphicswithlibGDXDr.AndrewVardy

    Adaptedfromthefollowingsources:libGDX slides byJussi Pohjolainen (TampereUnversity ofAppliedSciences)

    transformationslidesbyDr.PaulGillard(MemorialUniversity)

    ENGI5895SoftwareDesign

    MemorialUniversity

    Introduction

    • Thegoalhereistoillustratesomeconceptsincomputergraphics• ThetoolwewilluseislibGDX,across-platformgamedevelopmentenvironment• libGDX libraryprovidessixinterfacestoabstractawayplatformdetails

    • Application,Files,Input,Net,Audio,Graphics• ThegraphicslibrarywrapsOpenGLESorWebGL

    • OpenGLhasemergedasastandardlibraryforgraphics;ES=EmbeddedSystems• OpenGLESavailableonAndroidandiOS• WebGL isaJavascript APIthatconformstoOpenGLES

    Cross-platform

    • libGDX targetsDesktop,Android,HTML5,andiOS• DesktopviaLWJGL(LightweightJavaGameLibrary)• AndroidviaAndroidSDK• HTML5viaGWT(GoogleWebToolkit)

    • Java->Javascript• iOSviaRoboVM

    • Java->Objective-C

    • ForanalternateintrotolibGDX try“2DGameDevelopmentwithlibGDX”fromUdacity

    interfaceApplication

    • AccordingtothelibGDX API:

    “AnApplicationisthemainentrypointofyourproject.Itsetsupawindowandrenderingsurfaceandmanagesthedifferentaspectsofyourapplication,namelyGraphics,Audio,InputandFiles.ThinkofanApplicationbeingequivalenttoSwing'sJFrame orAndroid'sActivity.”

    • Applicationisaninterfacewhichisimplementedbyoneofthefollowing:• JglfwApplication (Desktop)• AndroidApplication (Android)• GwtApplication (HTML5)• IOSApplication (iOS)

  • • TheApplicationinterfaceandthecorrespondingXXXApplication (e.g.AndroidApplication)classesexistanddon’tneedtobemodified• CreateyourownappbyimplementingApplicationListener

    App(Lifecycle(

    Thisiswhatyouhavetoimplementtomakeyourowngame/app

  • ListenersandAdapters(JavaConcept)

    • Usuallya“Listener”inJavarespondstoevents• e.g.inSwinginterfaceMouseListener definesthefollowingmethods:

    mouseClicked,mouseEntered,mouseExited,mousePressed,mouseReleased

    • ThisisreallyjustanotherflavouroftheObserverpattern• Butwhatifyouonlycareabout“mouseClicked”events?YourconcreteListenerhastodefineall5ofthemethodsabove• ToavoidthistheabstractclassMouseAdapter isdefinedwhichprovidesemptymethodsforallofthese• NowyourconcreteListenercanextendMouseAdapter insteadofimplementingMouseListener andyoudefineonlythemethodsyouwant

    About(Starter(Classes(

    •  For(each&plaVorm&(iOS,(Android,(Desktop(..)(a(starter(class(must(be(wri_en(

    •  Starter(classes(are(plaTorm(dependent(•  We(will(focus(on((– Desktop((LWJGL)(– Android&

    Starter(Classes:(Desktop(// This is platform specific: Java SEpublic class DesktopStarter { public static void main(String[] argv) { LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); config.title = “…”; config.width = 480; config.heigth = 320; new LwjglApplication(new MyGame(), config); }}

  • Starter(Classes:(Android(import android.os.Bundle;import com.badlogic.gdx.backends.android.AndroidApplication;import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;

    // This is platform specific: Android// No mainpublic class AndroidLauncher extends AndroidApplication { @Override protected void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); AndroidApplicationConfiguration config = new AndroidApplicationConfiguration(); MyGame game = new MyGame(); initialize(game, config); if(this.getApplicationListener() == game) { this.log("test", "success"); } }}

    Android(Manifest(

    Android(Permissions(

    •  Add(permissions(if(your(android(app(requires(certain(funcAonality(–  – 

    – 

    •  Add(these(to(manifest(file •  See(–  http://developer.android.com/guide/topics/manifest/manifest-intro.html#perms

    ProjectSetup

    • RatherthancraftyourownStarterClasses,Irecommendusingtheprojectgenerator(gdx-setup.jar)referredtohere:• https://github.com/libgdx/libgdx/wiki/Project-Setup-Gradle

    • Torunonthedesktop:• ClickRun->“EditConfigurations...”• Click”+”inupper-leftcorner• Select“Gradle”(thebuildsystem)

    • Change“Name”toDesktop• Set“Tasks”todesktop:run

    • With“Desktop”selected,hittheplaybutton• Ifeverythingworks,thisshouldappear:

  • • Thefollowingclasswillbedefinedincore/src/com.mygdx.game

    publicclassMyGdxGame extendsApplicationAdapter {SpriteBatchbatch;Textureimg;

    @Overridepublicvoidcreate(){batch=newSpriteBatch();img =newTexture("badlogic.jpg");

    }

    @Overridepublicvoidrender(){Gdx.gl.glClearColor(1,0,0,1);Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);batch.begin();batch.draw(img,0,0);batch.end();

    }

    @Overridepublicvoiddispose(){batch.dispose();img.dispose();

    }}

    Locatedinandroid/assets(otherplatforms linktothisdir)

    TwolinesofactualOpenGL;Colour specifiedasred,green,blue,alpha(opacity)

    Entities(hereanimage)drawninabatchtooptimize themforprocessingbytheGPU

    Isn’tJavasupposed tohavegarbagecollection(GC)? Unfortunately,GCisunpredictableandcostly.Iflargeresources(e.g.images)weresubjecttoGCitcouldcausegamelag.Also,objectsallocatedoutside theJVM(e.g.bycallingC++code)arenotGC’d.

    Example:“ASimpleGame”

    • Thisexamplewasadaptedfrom

    https://github.com/libgdx/libgdx/wiki/A-simple-game

    • Asimplezen-likegamewithnoend:• Catchraindropswithabucketonthebottomofthescreen.• Raindropsspawnrandomlyatthetopofthescreeneverysecondandacceleratedownwards.

    • Playerdragsthebuckethorizontallyviathemouse/touchorbythekeyboardusingleftandrightcursorkeys.

    packagecom.mygdx.game;

    import/*NOTSHOWN*/

    publicclassDropextendsApplicationAdapter {privateTexturedropImage;privateTexturebucketImage;privateSpriteBatch batch;privateOrthographicCamera camera;privateSpritebucket;privateArrayraindrops;privatelonglastDropTime;

    //Thewidthandheightofthescreen--- assumednot//tochange(otherwisedefineresize).privateint width,height;

    ...

    Imagestobeloadedfromfiles

    Havingacameraenablesmanipulating theviewindependent oftheworld.Twochoices:- PerspectiveCamera:Distantobjectswillappear

    smaller.Good for3D.- OrthographicCamera:Thesceneisprojected

    ontoaplane.Good for2D.

    @Overridepublicvoidcreate(){//loadtheimagesforthedropletandthebucket,64x64pixelseachdropImage =newTexture(Gdx.files.internal("droplet.png"));bucketImage =newTexture(Gdx.files.internal("bucket.png"));

    width=Gdx.graphics.getWidth();height=Gdx.graphics.getHeight();

    //createthecameraandtheSpriteBatchcamera=newOrthographicCamera();camera.setToOrtho(false,width,height);batch=newSpriteBatch();

    //createaSpritetologically representthebucketbucket=newSprite(bucketImage);bucket.setX(width/2- bucket.getWidth()/2);//centerthebuckethorizontallybucket.setY(20);//bottomleftcornerofthebucketis20pixelsabovethebottomscreenedge

    //createtheraindropsarrayandspawnthefirstraindropraindrops=newArray();spawnRaindrop();

    }

  • privatevoidspawnRaindrop(){Spriteraindrop=newSprite(dropImage);raindrop.setX(MathUtils.random(0,width-raindrop.getRegionWidth()));raindrop.setY(height);raindrops.add(raindrop);lastDropTime=TimeUtils.nanoTime();

    }

    ThisSpriteiscreatedwithnew.GCwillstillhappenasnormalandaslongastheobjectscreatedaresmall,thereshouldn’t beabigimpact.

    @Overridepublicvoidrender() {//clearthescreenwithadarkbluecolor.TheargumentstoglClearColor arethered,greenblueandalphacomponent//intherange[0,1]ofthecolor tobeusedtoclearthescreen.Gdx.gl.glClearColor(0, 0,0.2f,1);Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    //tellthecameratoupdateitsmatrices.camera.update();

    //telltheSpriteBatch torenderinthecoordinatesystemspecifiedbythecamera.batch.setProjectionMatrix(camera.combined);

    //beginanewbatchanddrawthebucketandalldropsbatch.begin();bucket.draw(batch);for(Spriteraindrop: raindrops){

    raindrop.draw(batch);}batch.end();

    ...

    Notreallynecessaryheresincethecameraisnotchanging

    rendermethod:part1/3

    ...

    //processuserinputif(Gdx.input.isTouched()) {Vector3touchPos =newVector3();touchPos.set(Gdx.input.getX(), Gdx.input.getY(),0);camera.unproject(touchPos);bucket.setX(touchPos.x - bucket.getWidth()/2);

    }if(Gdx.input.isKeyPressed(Keys.LEFT)) bucket.translateX(-400*Gdx.graphics.getDeltaTime());if(Gdx.input.isKeyPressed(Keys.RIGHT)) bucket.translateX(400*Gdx.graphics.getDeltaTime());

    //makesurethebucket stayswithinthescreenboundsif(bucket.getX()<0)bucket.setX(0);if(bucket.getX()>width- bucket.getWidth())bucket.setX(width- bucket.getWidth());

    //checkifweneedtocreateanewraindropif(TimeUtils.nanoTime() - lastDropTime >1000000000) spawnRaindrop();

    ...

    rendermethod:part2/3...

    //movetheraindrops, removeanythatarebeneath thebottomedgeof//thescreenorthathitthebucket.Iteratoriter =raindrops.iterator();while(iter.hasNext()){Spriteraindrop=iter.next();raindrop.translateY(-200*Gdx.graphics.getDeltaTime());if(raindrop.getY()+raindrop.getHeight()<0)iter.remove();if(raindrop.getBoundingRectangle().overlaps(bucket.getBoundingRectangle())){iter.remove();

    }}

    }

    rendermethod:part3/3

  • @Overridepublicvoiddispose(){//disposeofallthenativeresourcesdropImage.dispose();bucketImage.dispose();batch.dispose();

    }}

    Whatneedstobedisposedof?AnyclassesthatimplementDisposable.

  • Example:“ZenGarden”

    • ”ASimpleGame”• Rainfallsrandomlyfromthesky• Usercontrolsabuckettocatchraindrops

    • Hereweinvertthissetup• Usercontrolsacloudinthesky,fromwhichraindropsfall• Ifrainfallsonatree,thetreegrows

    • Downloadthecodeforthisexample(seenotespage)• Themainfiles:• ZenGardenGame (extendsApplicationAdapter)• Tree(interface)• SimpleTree (implementsTree)• RecursiveTree (implementsTree)

    • First,considerSimpleTree’s drawmethod…

    publicvoiddraw(SpriteBatch batch){//Anaffine transformisusedtorepresenttranslation, rotation,andscalingoperations.Affine2 transform=newAffine2();

    //Initialtranslationandrotation,bringingustothebaseofthetree,pointedupwards.transform.translate(baseX,baseY);transform.rotate(90.0f);

    //Storethecurrenttransformstateforusebelow.Affine2savedTransform =newAffine2(transform);

    drawBranch(batch,transform);

    ….privatevoiddrawBranch(SpriteBatch batch,Affine2 transform){//Drawthecurrentbranch.Wedrawitastwohalvesbecausetransformationssuchas//rotationaremadewithrespecttothelowerleftcorneroftheimage.batch.draw(stickLeft,stickLeftWidth,stickLeftHeight,transform);transform.scale(1f,-1f);batch.draw(stickRight,stickRightWidth,stickRightHeight,transform);transform.scale(1f,-1f);

    }

    drawmethod:part1/2

  • //Translatetothefirstbranchingpointtransform.translate(stickLeftWidth *0.86f,0);

    //Drawthefirstbranchtransform.rotate(30.0f);transform.scale(FIRST_BRANCH_SCALE,FIRST_BRANCH_SCALE);drawBranch(batch,transform);

    //Repositiontosecondbranchingpointbyrestoringthesavedtransform.transform=savedTransform;transform.translate(stickLeftWidth *0.55f,0);

    //Drawthesecondbranchtransform.rotate(-30.0f);transform.scale(SECOND_BRANCH_SCALE,SECOND_BRANCH_SCALE);drawBranch(batch,transform);

    }

    drawmethod:part2/2

    • OnthepreviousslideweareusingtheSimpleTree classwhichdrawsthebasictrunkofthetreeandtwobranches.• RecursiveTree addsthefollowing:• Treegrowsinresponsetowaterdropsfallingonit• Treegrowsfractally byrecursivebranching• Whenatthemaximumgrowthlevel,berriesemerge!

    • Pleaseseetheattachedcodefordetails…


Top Related