memorial university of newfoundlandav/courses/5895-current/manual_uploads/...import android...
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…