• No results found

Utvärdering av utvecklingsmiljön HTML5 vid utveckling av 2d-spel med fysikmotor

N/A
N/A
Protected

Academic year: 2021

Share "Utvärdering av utvecklingsmiljön HTML5 vid utveckling av 2d-spel med fysikmotor"

Copied!
31
0
0

Loading.... (view fulltext now)

Full text

(1)

Institutionen för datavetenskap

Department of Computer and Information Science

Examensarbete

Utvärdering av utvecklingsmiljön HTML5 vid

utveckling av 2d-spel med fysikmotor

av

Christofer Ärleryd

LIU-IDA/LITH-EX-G--14/006--SE

2014-03-20

Linköpings universitet SE-581 83 Linköping, Sweden

Linköpings universitet 581 83 Linköping

(2)
(3)

Upphovsrätt

Detta dokument hålls tillgängligt på Internet – eller dess framtida ersättare – under en

längre tid från publiceringsdatum under förutsättning att inga extra-ordinära

omständigheter uppstår.

Tillgång till dokumentet innebär tillstånd för var och en att läsa, ladda ner, skriva

ut enstaka kopior för enskilt bruk och att använda det oförändrat för ickekommersiell

forskning och för undervisning. Överföring av upphovsrätten vid en senare tidpunkt

kan inte upphäva detta tillstånd. All annan användning av dokumentet kräver

upphovsmannens medgivande. För att garantera äktheten, säkerheten och

tillgängligheten finns det lösningar av teknisk och administrativ art.

Upphovsmannens ideella rätt innefattar rätt att bli nämnd som upphovsman i den

omfattning som god sed kräver vid användning av dokumentet på ovan beskrivna sätt

samt skydd mot att dokumentet ändras eller presenteras i sådan form eller i sådant

sammanhang som är kränkande för upphovsmannens litterära eller konstnärliga

anseende eller egenart.

För ytterligare information om Linköping University Electronic Press se förlagets

hemsida

http://www.ep.liu.se/

Copyright

The publishers will keep this document online on the Internet - or its possible

replacement - for a considerable time from the date of publication barring exceptional

circumstances.

The online availability of the document implies a permanent permission for

anyone to read, to download, to print out single copies for your own use and to use it

unchanged for any non-commercial research and educational purpose. Subsequent

transfers of copyright cannot revoke this permission. All other uses of the document

are conditional on the consent of the copyright owner. The publisher has taken

technical and administrative measures to assure authenticity, security and

accessibility.

According to intellectual property law the author has the right to be mentioned

when his/her work is accessed as described above and to be protected against

infringement.

For additional information about the Linköping University Electronic Press and

its procedures for publication and for assurance of document integrity, please refer to

its WWW home page:

http://www.ep.liu.se/

(4)
(5)

Examensarbete

Utvärdering av utvecklingsmiljön HTML5 vid

utveckling av 2d-spel med fysikmotor

av

Christofer Ärleryd

LIU-IDA/LITH-EX-G--14/006--SE

2014-03-20

Handledare: Erik Berglund Examinator: Erik Berglund

(6)
(7)

Sammanfattning

Det här examensarbetet utvärderar spelutveckling i HTML5 teknologin vilket innebär utveckling i JavaScript, CSS och HTML med utnyttjande av teknologin tillagd i HTML5 märkningen. I detta fall är teknologin canvas-element som ritar ut 2-D grafik.

Två implementationer av ett motorcykelspel har utvecklats med fysikmotorerna Box2D respektive Chipmunk.

Målet med arbetet var att utvärdera bästa val av fysikmotor vid utveckling av ett spel i 2-D med krävande fysik i HTML5 till mobil.

Examensarbetet har utförts självständigt genom IDA på Linköpings Universitet med handledning från Erik Berglund.

(8)
(9)

Innehållsförteckning

1. Inledning 1.1 Bakgrund 1.2 Syfte 1.3 Metod 1.4 Avgränsningar 2. HTML5 2.1 Beskrivning

2.2 World Wide Web Consortium(W3C) 2.3 Utveckling i HTML5

2.4 Canvas-element 2.5 WebView

2.6 Från HTML5 till mobil applikation 3. Spelets krav på utvecklingsmiljön

3.1 Bakgrund 3.2 Spelbeskrivning

4. Fysikmotorer: Chipmunk och Box2D 4.1 Beskrivning

4.2 Val av fysikmotor 4.3 Fysikmotorn Chipmunk 4.4 Fysikmotorn Box2D 4.5 Bindings och Portning

5. Teknisk specifikation: Chipmunk och Box2D 5.1 Beskrivning 5.2 Världen 5.3 Motorcykel: Chipmunk 5.4 Motorcykel: Box2D 6. Benchmarking 6.1 Beskrivning

6.2 Test: Maximalt antal anrop av huvudfunktion tick() 6.3 Reflektion

7. Resultat

7.1 Portning till JavaScript 7.2 Spelet

7.3 Fysikmotorer 7.4 Optimering 8. Ordlista

(10)
(11)

1. Inledning

1.1 Bakgrund

Att utveckla applikationer till mobiler har exploderat de senaste åren. Google Play och App Store bröt just gränsen på en miljon appar på deras marknader. Andra operativsystem som Windows Phone ligger över 200,000.

Detta betyder inte att det inte finns chans för nya appar att bli populära. År 2013 såldes appar för tio miljoner dollar på App Store och enligt Gartners prognoser kommer detta att fortsätta många år framåt[1][2][3][4].

Som utvecklare finns det så många utvecklingsmetoder att välja bland. Multiplattformslösningar har blivit väldigt populära eftersom marknadsandelen av potentiella kunder är så splittrad bland

operativsystemen.

1.2 Syfte

Utveckla en app i HTML5 med potentialen att kunna köras på de största mobila operativsystemen. Appen ska vara ett spel i 2d-plattformsmiljö och använda sig av passande fysikmotor. Syftet är att avgöra bäst val av fysikmotor i denna miljö genom jämförande av egna implementationer.

1.3 Metod

Undersök vilka fysikmotorer som är bäst lämpade att använda för mitt typ av spel och jämför dessa mot varandra genom att skapa grundliga implementationer av spelet i dessa valda fysikmotorer.

1.4 Avgränsningar

Som avgränsningar undviker jag användning av ramverk för JavaScript. Med PhoneGap kan en implementation som fungerar i webbläsare på PC och Mac lätt paketeras och köras på mobil med Android genom endast tilläggning av input från mobilens touch och accelerometer.

Andra alternativ tas upp i uppsatsen men används ej i test då målet var att jämföra fysikmotorer vilket ej kräver optimala prestandalösningar.

(12)

2. HTML5

2.1 Beskrivning

HTML5 beskriver den femte revisionen av HTML. Tidigare fanns HTML4 och kom så tidigt som 1993. Sedan dess har väldigt mycket ändrats på internet och mycket funktionalitet som länge saknats blir eftersom införd i HTML5 revisionen.

All ny funktionalitet kom inte helt plötsligt eftersom HTML5 inte kom helt plötsligt. HTML5 är en samling funktionalitet som organisationen World Wide Web Consortium(W3C) bestämt ska ingå i denna samling. Webbläsare lägger eftersom in funktionalitet. Allt detta i hopp om att det ska finnas en bestämmelse om vad som bör stödjas och därmed göra det lättare för utveckling av webben[5].

2.2 World Wide Web Consortium(W3C)

W3C är en internationell organisation som har som mål att sätta långsiktiga riktlinjer för utvecklingen av World Wide Web(WWW eller W3). Konsortiet har vid dagsläget 383

medlemsföretag och upprätthåller en personal på cirka 50-60 personer som arbetar heltid med utveckligen av den femte revisionen av HTML i vilket de kommer överens om vilka API som ska ingå i denna.

HTML5 är klassificerad som en ”candidate recommendation” av W3C. Detta betyder att den är förbi stadiet som ”working draft” vilket innebär att den tar in åsikter från i stort sett vem som helst, och är istället inne i stadiet där gruppen som arbetar med standarden är nöjda med innehållet och tar in information från utvecklare.[6]

2.3 Utveckling i HTML5

Utveckling i HTML5 utförs likadant som i tidigare upplagor. Det består av utveckling med märkspråket HTML med vanligtvis CSS som stilmall samt JavaScript som skriptspråk. Det finns dock alternativ till CSS samt JavaScript[7].

2.4 Canvas-element

Canvas-element används på webbsidor vid rendering av 2D grafik och används vid spelutveckling i HTML5. De kan skapas på en webbsida i HTML, CSS eller JavaScript och är endast en ram där grafik kan ritas ut och utför ingenting förrän ett skript begär att något ska renderas.

Canvas-element tillkom i HTML5 och stöds av Firefox(3.0+), Safari(3.0+), Chrome(3.0+), Opera(10.0+), iPhone(1.0+), Android(1.0+) samt Internet Explorer(9+)[8].

2.5 WebView

En WebView är som namnet antyder, en sida som visar hemsidor. Det är som att använda sig av en webbläsare fast utan all funktionalitet förutom möjligheten att visa upp en sida given en

fördefinierad adress.

2.6 Från HTML5 till mobil applikation

Applikationer utvecklade i HTML5 till webben kan med verktyg paketeras till att skapa mobila appar. Det finns många verktyg som åstadkommer detta genom att använda en kombination av mobilens WebView samt dess native API. På så vis kan en majoritet av utveckling ske precis som till en PC eller Mac och samtidigt dra nytta av de inbyggda API som mobilen har men inte en webbläsare har tillgång till, vilket kan vara kamera, gps, etc.

(13)

Verktyg som PhoneGap och Trigger.io fungerar på detta vis. Utveckla i HTML5 miljön precis som till webben, paketera som en helt vanlig applikation till vald plattform och sedan körs den som vilken annan app som helst.

Detta låter som det självklara valet för alla utvecklare då verktyg som PhoneGap kan skapa körbara appar på i stort sett alla plattformar. Så är självklart inte fallet. Prestandaproblem plågar fortfarande HTML5 på mobiler och många APIs är fortfarande inte implementerade av mobilens webbläsare. För vissa är det dock redan ett alternativ att snabbt utveckla i ett verktyg till alla plattformar men för många är det fortfarande inte.

Vid spelutveckling då appen inte kräver att få tillgång till annat än touch och möjligen

deviceorientation finns det verktyg som CocoonJS och Ejecta som skapar färdiga appar fast till skillnad från PhoneGap och Trigger.io inte använder sig av en WebView.

Det som enligt koden ska rita ut grafik på canvas-elementet binds istället om av verktyget och kallar på OpenGL ES vilket gör det hårdvaruaccelererat och mycket mer kraftfullt. Jag tar upp mina erfarenheter med CocoonJS senare[9][10].

(14)

3. Spelets krav på utvecklingsmiljön

3.1 Bakgrund

Val av utvecklingsmiljö är fullständigt beroende av vilket slags spel man vill utveckla och till vilka plattformar man vill utveckla det till. Spelet jag utvecklar är ett motorcykelspel där grafiken är 2-D och det finns fyra input för styrning; gasa, bromsa, luta framåt, luta bakåt. Detta går att utveckla till alla slags mobila plattformar.

3.2 Spelbeskrivning

Spelet består av en motorcykel som användaren på så kort tid som möjligt ska styra från start till mål genom terräng. På grund av komplexiteten hos motorcykeln krävdes en komplex fysikmotor som innehöll funktionalitet för att möjliggöra detta. Kraven som sattes på fysikmotorn var att den:

• Ska vara väldokumenterad

• Ska vara populär så att hjälp lätt går att hitta på internet

• Ska innehålla funktionalitet för att skapa en motorcykel med motor och stöddämpning samt så skulle kroppar i den här världen ha studs, friktion, fjädring samt gravitation

Ytterligare krav som ställdes på spelet var att det skulle kunna köras på mobiltelefon i

operativsystemet Android som en .apk fil. Det ska gå att få input från mobilens accelerometer och touch input. Detta möjliggjordes genom att paketera spelet i PhoneGap.

Bilden ovan visar spelet köras på en Nexus 4. Med hjälp av PhoneGap kan man luta motorcykeln genom att luta på mobiltelefonen och gasa och bromsa genom att trycka på höger, respektive vänster del av skärmen.

(15)

4. Fysikmotorer: Chipmunk och Box2D

4.1 Beskrivning

Fysikmotorer används i spel då man vill undvika att programmera komplex fysik och på så vis minska på utvecklingstiden och samtidigt kunna använda sig av redan välbeprövade metoder. Ofta handlar det om simpel vanlig fysik som friktion, fjädring och studs tillsammans med gravitation. I sådana fall finns det många fysikmotorer med öppen källkod för spel i både 2-D och 3-D.

Det finns två typer av fysikmotorer, den ena används vid behöv av hög precision och den andra då man vill kunna påverka aktiviteten i realtid. Den förstnämnda används framförallt inom

vetenskapliga experiment och datoranimerade filmer där hög precision är av största vikt. Den andra används framförallt inom datorspel där man värderar möjligheten att kunna påverka händelser i realtid över precision.

4.2 Val av fysikmotor

Då jag påbörjade detta arbete i början av 2013 så försökte jag framförallt hitta forumtrådar och diskussioner kring vilka fysikmotorer som fanns tillgängliga i JavaScript. Då dessa ofta inte är källor man fullständigt kan lita på så var detta ett så pass nytt ämne och jag valde därför det här tillvägagångssättet.

Konsensus var att Chipmunk och Box2D var de mest populära. Det var dessa fysikmotorerna man kunde finna mest hjälpmedel och guider till då de använts i många år.

Att avgöra vilken utav dessa två som var bäst var inte särskilt lätt. Denna artikel[12] och

diskussioner på detta forum[11] tyder på att Chipmunk är det bättre alternativet, dock så är dessa inte särskilt starka källor.

Skaparen av Chipmunk har på sin webbsida[13] gjort tester som pekar på att hans fysikmotor är snabbare än Box2D, då denna kan ses som en bättre källa än forumtrådar så är dessa tester ej utförda av en opartisk källa.

Boken ”Learn cocos2D”[32] tar upp diskussionen om vilken fysikmotor som lämpar sig bäst för utveckling till mobiler i spelmotorn Cocos2D och påstår att de inte är särskilt olika från varandra och beslutet om vilken man använder i slutändan har att göra med vilket utvecklingsspråk man föredrar; C++ för Box2D eller C för Chipmunk.

I slutändan ligger dock mycket tyngd i vilken port av fysikmotorn man använder vilket jag, i brist på annat, har valt utifrån mindre pålitliga källor vilket jag beskriver i kapitel 7.1.

4.3 Fysikmotorn Chipmunk

Chipmunk SDK är en open-source fysikmotor skriven i C av Scott Lembcke och använts till att skapa fysik i hundratals 2-D spel. Spelmotorer som Cocos2D till iOS och Marmalade för multiplattform använder sig av Chipmunk.

Fysikmotorn används antingen genom C versionen av Chipmunk SDK eller genom någon av de flera portar och bindings som skapats av andra personer[14].

4.4 Fysikmotorn Box2D

Box2D SDK är skriven i C++ av Erin Catto och används i spelutveckling till att skapa fysik i 2-D spel. Fysikmotorn är open-source och har använts i populära spel som Limbo, Angry Birds, Tiny Wings med mera[15].

(16)

4.5 Bindings och Portning

De flesta fysikmotorer är skrivna i ett systemprogrammeringsspråk som C eller C++. De görs sedan användbara i andra språk genom att porta eller skapa en binding till det språket.

Portning av en SDK skriven i C till JavaScript innebär att alla funktioner skrivs om i JavaScript. En binding från C till JavaScript är en koppling skapad i JavaScript till C funktionerna. Fördelen med en binding är att den går mycket snabbare att skapa än en port och fördelen med en port är att det blir ren kod skriven för språket du använder vilket ofta gör den pålitligare och effektivare[16][17] [18][19].

(17)

5. Teknisk specifikation: Chipmunk och Box2D

5.1 Beskrivning

Här beskriver jag de funktionerna jag använt från fysikmotorerna. Eftersom målet var att skapa två så likartade implementationer i Chipmunk respektive Box2D som möjligt så ska jag gå igenom funktionalitet jag använt samt skillnaderna mellan fysikmotorerna.

5.2 Världen

Miljön som först måste skapas innan man kan lägga in objekt som får fysik och kollision med varandra är ett objekt som i Chipmunk kallas ”space” och i Box2D kallas ”world”. De definieras på följande vis:

space = new cp.Space(); space.gravity = cp.v(0, 700);

world = new box2d.b2World(new box2d.b2Vec2(0, 50), false);

Parametrarna som skickas till b2World är i vilken riktning gravitationen ska agera och hur stark samt ifall man ska tillåta objekt att ”somna” vilket innebär att de inte längre behöver simuleras.

Bild tagen från Chipmunk-versionen av spelet körandes i Google Chrome version 33 på Mac.

Nivån man spelar består endast av smala rektanglar. Antingen långa för att skapa mark eller korta för att skapa hopp, i detta fall måste de roteras. Dessa är färgade svarta och syns i bilden ovan. Det undre och längsta markblocket skapas på följande vis:

Chipmunk

floor = this.space.addShape(

new cp.SegmentShape(this.space.staticBody, cp.v(0, 650), cp.v(900, 650), 5)); floor.setFriction(1.0); floor.group = 2;

Box2D

(18)

new box2d.b2Vec2(0/SCALE, 650/SCALE), new box2d.b2Vec2(2500/SCALE, 650/SCALE)); this.world.CreateBody(bodyDef).CreateFixture(fixDef);

Chipmunk använder pixlar som mått och Box2D meter vilket är varför jag använder variabeln ”SCALE” så att jag kan hantera objekten i pixlar.

Motorcykeln består av två hjul vilka är skapade som cirklar, ett chassi som är en rektangel. Dessa sitter ihop med varandra genom olika typer av så kallade ”constraints” vilket jag går djupare in på i kapitel 5.3.

Fysikmotorerna hanterar kollision mellan kroppar i ”space” respektive ”world”. Denna hantering går att i både Chipmunk och Box2D modifiera genom att gruppera kroppar i olika grupper. Alla kroppar som ingår i en viss grupp kan kollidera med kroppar från andra grupper men ej med kroppar tillhörande samma grupp.

Jag drar nytta av detta genom att gruppera in alla kroppar tillhörande motorcykeln i en grupp samt resten av världen i en annan.

5.3 Motorcykel: Chipmunk

Chassis, rightWheel och leftWheel är fysiska objekt i space skapade liknande floor, vilket beskrevs i 5.2. De betydande skillnaderna är att dessa objekt studsar vid kollision, att friktionen är satt högre samt att kroppar använder kollisionsgrupp 1 istället för grupp 2.

leftWheel.shape = this.space.addShape(

new cp.CircleShape(leftWheel.body, radius, cp.v(0.0, 0.0))); leftWheel.shape.setFriction(5.0);

leftWheel.shape.setElasticity(0.3); leftWheel.shape.group = 1;

För att leftWheel samt rightWheel ska fästas med chassis skapas en DampedSpring samt en GrooveJoint mellan dessa.

var leftWheelDampedSpring = this.space.addConstraint( new cp.DampedSpring(

chassis.body, leftWheel.body, cp.v(-80.0, -10.0), cp.v(0.0, 0.0), 55.0, 100.0, 5.0)); var leftWheelGrooveJoint = this.space.addConstraint(

new cp.GrooveJoint(

chassis.body, leftWheel.body, cp.v(-80.0, 0.0), cp.v(-80.0, 60.0), cp.v(0.0, 0.0)));

På detta sätt fästs bakre delen av chassis med leftWheel.

DampedSpring tar sju parametrar, först är det vilka två kroppar som ska sitta ihop med varandra. Sedan på vilka punkter på dessa kroppar den ska sitta på de två kropparna. Den femte är en längd vilket DampedSpring strävar efter att nå. De sista två parametrarna bestämmer hur stark den är på att sträva till att nå denna längd.

På bilden representerar den röda pilen cp.v(-80.0, 0.0) vilket är punkten från mitten på chassis till punkten där den fästs i chassis.

(19)

Punkten på leftWheel sätts i dess mittpunkten därmed cp.v(0.0, 0.0). De grå linjerna representerar en Damped Spring och är i spelet osynliga.

Utan den andra mekanismen leftWheelGrooveJoint skulle hjulet endast sträva efter att hålla sig till en viss längd och tillåtas snurra runt de svarta punkterna vilket resulterar i att motorcykeln bara faller ihop på marken. En Groove Joint är nödvändig både för att ge leftWheel en stabil position relativt chassis samt göra så att hjulet inte dämpas inåt motorcykeln utan istället dämpas i Y-led. På leftWheel är det endast ett fäste på punkten cp.v(0.0, 0.0), alltså mitten av hjulet. På chassis sida är det ett långt fäste där punkten tillåts ”glida” mellan, utan någon som helst kraft som bestämmer vart. Detta fäste går mellan cp.v(-80.0, 0.0) och cp.v(-80.0, 60.0) på chassis, vilket är färgat lila på bilden ovan.

Givet den här kombinationen av mekanismer kan motorcykelns fram- och bakhjul dämpas i Y-led givet en Damped Spring samt en Groove Joint mellan hjul och chassis

Motorcykelns motor skapas på följande vis

this.leftWheelMotor = this.space.addConstraint(

new cp.SimpleMotor(this.leftWheel.body, this.chassis.body, 0));

Den tredje parametern bestämmer motorns hastighet, den initieras till 0 och ändras då input ges för att gasa.

För att luta motorcykeln används en Damped Rotary Spring. Den håller två kroppar i samma vinkel med varandra.

tiltDampedRotarySpring = space.addConstraint(new cp.DampedRotarySpring( bike.chassis.body, levelSelector.floor.body, 0, 500000.0, 10000.0));

De två första parametrarna representerar de två kropparna som ska hålla en viss vinkel relativt varandra, vilket är motorcykelns chassi och golvet som skapades i kapitel 5.3. Den tredje

parametern är i vilken vinkel de ska hålla relativt varandra, initierad till 0. Parameter fyra och fem är hur kraftigt tiltDampedRotarySpring ska anstränga sig för att hålla dessa två i samma vinkel samt hur vilket är fasthet samt gungning i förhållande till varandra då den ena kroppen rör sig.

Det som sker när input för lutning är given är att gradtalet för lutning, som är initierat till 0, ökas samt sänks. Därför har jag satt dessa två värden väldigt högt då jag vill att motorcykeln ska ge efter kvickt och stabilt då detta sker. Detta skapar illusionen, då bättre textur är tillagd, att chauffören lutar sig bakåt samt framåt för att luta motorcykeln.

5.4 Motorcykel: Box2D

Box2D använder sig av fixture definition samt body definition vid skapandet av fysiska kroppar. Kroppar kan skapas helt oberoende av fysik och det är tilläggningen av en fixture på ett body objekt som gör att den hanteras av fysikmotorn efter de givna specifikationerna från fixture.

Chassis, rightWheel samt leftWheel skapas alla med delvis samma fixture värden. Även vissa body värden är de samma, därför kan de alla initieras innan skapandet av motorcykeln börjar.

(20)

fixDef.density = 1; fixDef.friction = 100; fixDef.restitution = 0.2;

bodyDef.type = box2d.b2Body.b2_dynamicBody; fixDef.filter.groupIndex = -8;

Värdena beskriver till stor del sig själva; densitet, friktion samt ”restitution” vilket är hur mycket ett objekt studsar vid kollision.

Eftersom kroppar tillhörande motorcykeln ej ska kollidera med varandra, dock fortfarande kollidera med kroppar i världen, så ges de ett groupIndex. Två kroppar som har samma groupIndex samt är negativt, kolliderar inte med varandra vid kontakt.

Motorcykelns hjul skapas på detta vis

fixDef.shape = new box2d.b2CircleShape(20/SCALE);

bodyDef.position.Set(400/SCALE - 67.5/SCALE, 550/SCALE); leftWheel = world.CreateBody(bodyDef);

leftWheel.CreateFixture(fixDef);

Chassis skapas på detta vis

fixDef.shape = new box2d.b2PolygonShape(); fixDef.shape.SetAsBox(40/SCALE, 10/SCALE); bodyDef.position.Set(400/SCALE, 525/SCALE); chassis = world.CreateBody(bodyDef);

chassis.CreateFixture(fixDef);

Parametern SCALE är satt till 30 vilket är hur många pixlar en meter representerar i Box2D mått. Jag behandlar objektens position likadant som i Chipmunk och delar sedan alla koordinater med SCALE.

Mekanismerna för dämpning i bakdelen av motorcykeln relativt framdelen är olika.

I bakdelen används två Revolute Joint tillsammans med en osynlig rektangel som kopplar ihop leftWheel med chassis.

I framdelen används en liten osynlig cirkel i mitten på hjulet, en Revolute Joint samt en Prismatic Joint som kopplar samman rightWheel med chassis.

Beskrivning av motorcykelns bakre del

Rektangeln färgad grön i bilden representerar kroppen som håller samman leftWheel och chassis, den är definierad ”connection” och jag kommer hädanefter kalla den det. Den kopplas till leftWheel med en Revolute Joint, på bilden färgad lila, på följande vis

var leftWheelRevoluteDef = new box2d.b2RevoluteJointDef();

leftWheelRevoluteDef.Initialize(leftWheel, connection, new box2d.b2Vec2( leftWheel.GetPosition().x, leftWheel.GetPosition().y) );

leftWheelRevoluteDef.enableMotor = true;

(21)

leftWheelRevoluteDef.maxMotorTorque = 10;

leftWheelRevoluteJoint = world.CreateJoint(leftWheelRevoluteDef);

Detta ger leftWheel och connection en fast position relativt varandra tills dess att input för att gasa ges då följande kod exekveras

function rotateRight() {

bike.leftWheelRevoluteJoint.SetMaxMotorTorque(150); bike.leftWheelRevoluteJoint.SetMotorSpeed(-60); }

maxMotorTorque är hur stark en Revolute Joint är. Med rätt motkraft kommer Revolute Joint att sakta ner, stanna, eller till och med tryckas åt motsatt håll.

På samma vis som leftWheel sätts chassis fast i rektangeln med en Revolute Joint. Denna har dock bredare funktionalitet och gasas inte manuellt utan jobbar för att trycka connection motsols i relation till chassis mellan två givna gradtal.

var leftChassisRevoluteDef = new box2d.b2RevoluteJointDef();

leftChassisRevoluteDef.Initialize(chassis, connection, new box2d.b2Vec2(

this.chassis.GetPosition().x - 30/SCALE, chassis.GetPosition().y + 6/SCALE)); leftChassisRevoluteDef.enableMotor = true; leftChassisRevoluteDef.maxMotorTorque = 100; leftChassisRevoluteDef.motorSpeed = -2; leftChassisRevoluteDef.enableLimit = true; leftChassisRevoluteDef.angle = 0 / (180 / Math.PI); leftChassisRevoluteDef.lowerAngle = -20 / (180 / Math.PI); leftChassisRevoluteDef.upperAngle = 20 / (180 / Math.PI); leftChassisRevoluteJoint = world.CreateJoint(leftChassisRevoluteDef);

De givna gradtalen är -20° samt +20° och börjar på 0°. En ganska hög styrka är given samt en låg negativ hastighet. Detta resulterar i att connection, som i sin tur sitter fast med leftWheel, kommer att tryckas nedåt marken relativt chassis och skapa en fjädring då motorcykeln trycks med hög kraft mot marken.

Beskrivning av motorcykelns främre del

Mellan rightWheel och chassis är en fjäder skapad som kallas Prismatic Joint, färgad grå på bilden. Den har som funktion att skapa en stötdämpare i främre delen av motorcykeln.

var rightPrismaticJointDef = new box2d.b2PrismaticJointDef();

rightPrismaticJointDef.Initialize(chassis, rightWheelPivot, new box2d.b2Vec2( chassis.GetPosition().x, chassis.GetPosition().y), new box2d.b2Vec2(0.2, 0.6)); rightPrismaticJointDef.localAnchorA = new box2d.b2Vec2(30/SCALE, -8/SCALE); rightPrismaticJointDef.localAnchorB = new box2d.b2Vec2(0, 0);

rightPrismaticJointDef.lowerTranslation = -1.0; rightPrismaticJointDef.upperTranslation = 1.0; rightPrismaticJointDef.enableLimit = true; rightPrismaticJointDef.maxMotorForce = 1500; rightPrismaticJointDef.motorSpeed = 2;

(22)

rightPrismaticJointDef.enableMotor = true;

rightPrismaticJoint = world.CreateJoint(rightPrismaticJointDef);

De två första parametrarna i rightPrismaticJointDef.Initialize(...) är de två kroppar som blir

påverkade. Den tredje parametern säger vart den ska sitta och den fjärde är en vektor som beskriver hur den ska vara vinklad, 0.2 meter i x-led samt 0.6 meter i y-led(vilket är nedåt).

Värdena för lowerTransition och upperTransition beskriver hur mycket fjädern kan tänjas utåt samt tryckas ihop vilket i min implementation en enhet inåt samt utåt från startläget.

I mitten på rightWheel sitter ett litet cirkulärt objekt, definierat rightWheelPivot. Utan denna skulle rightWheel inte kunna snurra fritt utan låsas av fjädern, rightPrismaticJoint kopplas därför till rightWheelPivot istället för rightWheel.

bodyDef.position.Set(rightWheel.GetPosition().x, rightWheel.GetPosition().y); fixDef.shape = new box2d.b2CircleShape(5/SCALE);

rightWheelPivot = world.CreateBody(bodyDef); rightWheelPivot.CreateFixture(fixDef);

Mellan rightWheelPivot och rightWheel är en Revolute Joint satt.

var rightWheelRevoluteDef = new box2d.b2RevoluteJointDef();

rightWheelRevoluteDef.Initialize(rightWheel, rightWheelPivot, new box2d.b2Vec2( rightWheel.GetPosition().x, rightWheel.GetPosition().y));

rightWheelRevoluteDef.enableMotor = true; rightWheelRevoluteDef.maxMotorTorque = 10;

rightWheelRevoluteJoint = world.CreateJoint(rightWheelRevoluteDef);

Anledningen till att motor är aktiverad är för att hålla motorcykeln från att rulla för långt då gasen släpps.

(23)

6. Benchmarking

6.1 Beskrivning

Under projektet har jag utvecklat två grunder av motorcykelspelet. En för Chipmunk-js samt en för Box2Dweb. Båda implementationer fungerar i princip likadant då fysikmotorerna är väldigt lika varandra.

Det är svårt att utveckla ett spel som definitivt kan säga att en fysikmotor presterar bättre än en annan i en viss utvecklingsmiljö på en viss plattform. De tester jag utfört på mina två

implementationer ska dock jämföra de två fysikmotorernas prestation i just denna typ av spel. Testerna i detta kapitel har utförts i Chrome 32 på en Macbook Air mid 2012 i Mac OS X samt på en Nexus 4 med Android 4.4.2(KitKat) paketerat med PhoneGap.

6.2 Test: Maximalt antal anrop av huvudfunktion tick()

Under spelets gång exekveras en funktion tick() som utför alla uppdateringar av objekt samt ritar ut dem. Ett enkelt sätt att mäta prestandan mellan två implementationer är därför att bara mäta hur många gånger tick() funktionen kan köras under en given tid.

Vanligtvis körs spelet på 60 fps vilket innebär att tick() funktionen kallas på var 1/60 sekund. Genom att istället kalla på nästa tick() direkt efter att den nuvarande är utförd kan man mäta hur många gånger per sekund som funktionen kan utföras.

De funktioner som exekveras är: • draw() - ritar ut all grafik • update() - hanterar input

• space.step(1/60) – fysikmotorn uppdaterar alla objekt som om 1/60 sekund har passerat Funktionen performTasks(...) anropar alla dessa tre funktioner. För varje nytt anrop på funktionen

performTasks(...) så erhåller variabeln elapsed tiden för hur långt det tog för det förra anropet att

utföras. Ifall det tog kortare tid än 1000ms så ökas antalet anrop till performTasks(...) och ifall det tog över 1000ms så ökas variabeln slowCount med ett. Efter att 1000ms överskridits tio

gånger(slowCount == 10) så avslutas programmet och den performLoad, alltså antalet anrop per sekund, som användes innan senaste ökningen antas vara det maximala antalet anrop som klaras av att exekvera under en sekund.

function tick() {

var maximumFrameTime = 1000; currentTime = new Date();

var elapsed = currentTime - previousTime;

if(elapsed < maximumFrameTime || slowCount < maxSlow) { if (elapsed < maximumFrameTime) performLoad += 50; else slowCount++; if(!pause) { performTasks(performLoad, currentTime); requestAnimationFrame(tick); } } else {

// found maximum sustainable load at 60 FPS

(24)

pause = true; }

}

function performTasks(performLoad, currentTime) { for(var i = 0; i < performLoad; i++) {

draw(); update(); space.step(1/60); } previousTime = currentTime; }

För att skapa ett så normalt testscenario som möjligt så behövde motorcyklarna utföra samma arbete. Eftersom spelet gick så extremt mycket snabbare än vanligt gick detta ej att göra själv utan att motorcykeln direkt lämnade plattformarna. Därför skapade jag en cirkel som motorcykeln började inuti vid spelets start och under spelets gång endast accelererade framåt med. De två testscenarion går att se på bilden nedan.

Resultatet av att testa på detta sätt visade på att Chipmunk-js presterade bättre än Box2dweb på båda plattformarna. Resultaten är medelvärden av tio tagna testvärden i plattform och motor:

Anrop per sekund Chrome 32 Android 4.4 WebView

Chipmunk-js 2582.5 123.0

Box2dweb 2131.5 105.2

(25)

Resultatet av detta test pekade på att den implementation med Chipmunk-js var snabbare än den med Box2dweb.

För att lägga ännu mer vikt på fysikmotorns inverkan kommenterade jag ut draw() funktionen ur

performTasks(...). Detta visade på ett betydligt större glapp i prestanda vid utförande av samma test:

Anrop per sekund Chrome 32 Android 4.4 WebView

Chipmunk-js 32170.0 3500.0

Box2dweb 2506.4 193.8

6.3 Reflektion

Värt att överbetona är att de två implementationer jag skapat varierar trots allt betydligt. Chipmunk och Box2D använder liknande mekanismer men ej identiska och framförallt kan det finnas klara felaktigheter i min kod.

Värt att observera är att ration mellan testerna utförda i Chrome 32 på Mac och i WebView på Nexus 4 är närmare 20:1 i första testet men närmare 10:1 i andra testet. Den stora skillnaden förmodar jag uppstår då rendering av canvas-element är så pass krävande i WebView jämfört med Chrome 32 och på så vis drar ner resultaten ytterligare.

I slutstadiet av examensarbetet har jag fått påpekat att mitt skapandet av världen i Box2D ej tillåter objekt att sova. Detta kan möjligen ha betydelse i resultaten jag erhållit dock är jag skeptiskt till detta då jag inte använder särskilt många objekt i mitt spel.

Trots dessa punkter reflekterar mina resultat många åsikter och tester gjorda av andra, bland annat av Chipmunk skaparen Scott Slembcke, vilket jag tog upp i kapitel 4.2.

(26)

7. Resultat

7.1 Portning till JavaScript

Då fysikmotorerna Box2D och Chipmunk är skrivna i C++ respektive C krävs en port eller binding för utveckling i JavaScript. För Box2D finns en rad alternativ:

• Box2DJS – automatiskt konverterad från Box2DFlashAS3_1.4.3.1 med verktyg[20] • Box2Dflash 2.0 portad till JavaScript - ”one big nasty hack that just happens to work”[21] • Box2dweb – en port av Box2DFlash 2.1 till JavaScript. En ActionScript 3 till JavaScript

konverterade skapades till att generera koden[22] • box2d.js – JavaScript port av Box2D(Flash) 2.1a[23]

Det jag sökte hos porten var precis det andra söker efter då de letar portar till ett språk, effektivitet. Därför letade jag efter den som var mest populär på senare tid och efter personer som jämfört olika samt forum där konsensus var överens.

Box2DJS har inte uppdaterats sedan 2008-05-15 och bygger ej på den senaste versionen av Box2d Flash så jag ansåg därför att den var föråldrad och uteslöt den.

Porten av Box2Dflash 2.0 till JavaScript gavs ut senare, 2010-04-17, dock så är den som skaparen av porten själv säger inte särskilt välgjord. Den byggde inte heller på den senaste versionen av Box2d Flash.

De två senast skapade portarna box2dweb och box2d.js uppdaterades senast 2011-06-15 respektive 2011-07-22. De är båda baserade på senaste utgåvan av Box2DFlash 2.1a och är därför de bästa kandidaterna.

Slutligen valde jag Box2dweb då det var den populäraste av de två och flera internet källor rekommenderade att det var den porten man bör använda sig av[24][25][26].

Utöver detta så använder böckerna ”Professional HTML5 Game Development”[33] samt ”Pro HTML5 Games”[34] porten Box2dweb.

För Chipmunk var valet mycket enklare då Chipmunk-js är det klara valet. Då det finns alternativ som Chipmunk.js är den enligt skaparen av porten i ett väldigt tidigt alfastadie[27] och är därför till stor del otestad och i behov av optimering. Den var dessutom senast uppdaterad 2012-09-28[28] jämfört med Chipmunk-js som senast uppdaterades 2013-04-13[29].

7.2 Spelet

Spelet blev i slutändan väldigt simpelt men istället testat i många olika miljöer.

Det består av en nivå och några hopp och hinder för att ta sig från start till mål. Då chassi kolliderar med marken förlorar man och får börja om, lyckas man komma i mål får man se sin slutliga tid och sedan börja om.

Styrning sker med piltangenter på PC för att gasa, bromsa samt luta framåt och bakåt. På mobil spelar man i landscape och gasar vid touch på höger sida av skärmen, bromsar vid touch på vänster sida av skärmen samt lutar framåt och bakåt genom att luta mobilen.

(27)

På bilderna ovan kan man se spelet köra på Android med två tillvägagångssätt. Det första är att spelet paketeras med PhoneGap som är ett verktyg som använder sig av mobilens WebView samt drar nytta av delar av mobilens native API. Detta är väldigt enkelt att göra eftersom en WebView

Spelet paketerat i CocoonJS till Android på Nexus 4 Spelet paketerat i PhoneGap till Android på Nexus 4

(28)

fungerar som en hemsida krävs inga modifikationer på koden för att det ska vara körbart.

Det enda som behövde göras var att använda sig av Accelerometer vilket är ett av de native API som mobilen har och som PhoneGap stödjer. Det hade även gått att använda sig av

deviceorientation vilket är en del i HTML5. Dock så stödjer inte alla Android-enheter detta och det är alltid bäst att använda sig av mobilens native API ifall det finns tillgängligt.

Denna lösning fungerade alldeles utmärkt på Nexus 4 tills det att Android 4.4 släpptes och enheten började använda sig av en annan WebView vilket gjorde att mitt spel återigen presterade riktigt dåligt.

Andra bilden visar spelet köra paketerat i CocoonJS. Den är proceduren är mycket mer avancerad men kräver fortfarande inte så väldigt mycket jobb för att åstadkomma.

I CocoonJS presterade spelet utmärkt på Nexus 4 med Android 4.4 vilket inte var särskilt

förvånande då verktyget använder sig av en teknik där grafiken inte ritas ut i WebView utan istället använder OpenGL bibliotek för att på så vis dra nytta av mobilens grafikkort.

Ludei som gör CocoonJS låter dig som utvecklare skicka in din kod till dem och i retur får du en körbar fil till valfri plattform, i mitt fall .apk för Android. Det som krävs är att endast JavaScript exekveras. Inga CSS filer går att använda och endast ett canvas-element bör användas för rendering.

7.3 Fysikmotorer

Efter grundläggande implementation i Chipmunk-js samt Box2Dweb avgjorde jag att Chipmunk-js var det klart effektivaste alternativet vid utveckling till webb. Jag föredrog även Chipmunk för dess funktionalitet vilket var enligt mig lättare att förstå samt så använder sig Chipmunk av pixelmått istället för meter vilket jag föredrog.

7.4 Optimering

Utöver val av fysikmotor så fanns det fler åtgärder som kunde förbättra spelets prestanda. En åtgärd som markant förbättrade FPS var tilläggning av canvas-element. Som beskrivits i kapitel 2.4 så ritar canvas-element ut grafik som bakgrund, terrängen samt motorcykeln och dess beståndsdelar.

Inte nog med det utan varje gång modeller roterar, som t.ex. chassis eller hjul, så måste hela canvas-elementet roteras, rita ut bilden, sedan rotera tillbaka igen. Då detta görs så pass ofta blir det stora och onödiga uträkningar på ett onödigt stort canvas-element då motorcykeln är så pass liten. Därför skapade jag tre separata canvas-element för:

• Bakgrunden som endast behövs ritas ut en gång och är därefter still.

• Terrängen som endast rör sig till vänster och höger och aldrig behöver roteras.

• Motorcykeln som är betydligt mindre än de andra elementen. På så vis kan alla rotationer av chassis och hjul göras på ett mindre canvas-element och därmed inte vara lika krävande.

Denna ändring påverkade inte spelets beteende på något vis dock minskade prestandakraven betydligt.

Något jag aldrig testade men som säkerligen skulle öka prestanda är användning av web workers i JavaScript. Utan web workers kör JavaScript på en enda tråd vilket betyder att flera skript inte kan köras samtidigt. En web worker kan skapas för att exekvera ett visst skript så att det kan exekveras parallellt med resten av programmet.

På denna blogg[30] som länkades av msdn.microsoft.com[31] har Brendan Kenny använt web workers för uträkning av fysik i Box2D. Liknande hade kunnat gjorts för mitt spel och på så vis underlättat arbetet för mainloop.

(29)

8. Ordlista

API – Application Programming Interface WWW – World Wide Web

2-D – Tvådimensionellt

SDK – Software Development Kit FPS – frames per second

(30)

9. Referenser

1. Apple Inc. URL: https://www.apple.com/pr/library/2014/01/07App-Store-Sales-Top-10-Billion-in-2013.html

Hämtad 2014-02-18

2. Wikipedia. URL: http://en.wikipedia.org/wiki/Google_Play

Hämtad 2014-02-18

3. Microsoft Inc. URL: http://blogs.windows.com/windows_phone/b/wpdev/archive/2013/12/13/publish-your-apps-now-to-take-advantage-of-the-biggest-holiday-season-yet.aspx

Hämtad 2014-02-18

4. Techcrunch. URL: http://techcrunch.com/2013/09/19/gartner-102b-app-store-downloads-globally-in-2013-26b-in-sales-17-from-in-app-purchases/

Hämtad 2014-02-26

5. Pilgrim, M. (2010). HTML5: Up & Running. Första utgåvan. O'Reilly Media. 6. W3C. URL: http://www.w3.org/

Hämtad 2014-02-18

7. Pilgrim, M. (2010). HTML5: Up & Running. Första utgåvan. O'Reilly Media. 8. Pilgrim, M. (2010). HTML5: Up & Running. Första utgåvan. O'Reilly Media. 9. Ludei. URL: https://www.ludei.com/cocoonjs/

Hämtad 2014-02-18

10. Impactjs. URL: http://impactjs.com/ejecta

Hämtad 2014-02-18

11. Github, melonJS. URL: https://github.com/melonjs/melonJS/issues/145

Hämtad 2014-02-18

12. Nine Software. URL: http://nine-software.com/2012/12/box2d-vs-chipmunk-html5-games/

Hämtad 2014-02-18

13. Chipmunk physics. URL: http://chipmunk-physics.net/chipmunkPro.php#Benchmarks

Hämtad 2014-02-18

14. Chipmunk physics. URL: http://chipmunk-physics.net/

Hämtad 2014-02-18

15. Box2D. URL: http://box2d.org/

Hämtad 2014-02-18

16. Stackoverflow. URL: http://stackoverflow.com/questions/4546941/how-do-language-bindings-work

Hämtad 2014-02-18

17. Stackoverflow. URL: http://stackoverflow.com/questions/8628326/what-is-the-difference-between-a-wrapper-bindings-and-a-por

t

Hämtad 2014-02-18

18. Wikipedia. URL: http://sv.wikipedia.org/wiki/Portering_(datorteknik) Hämtad 2014-02-18

(31)

Hämtad 2014-02-18

20. Sourceforge, box2d-js. URL: http://box2d-js.sourceforge.net/

Hämtad 2014-02-18

21. 29a.ch. URL: h ttp://29a.ch/2010/4/17/box2d-2-flash-ported-javascript

Hämtad 2014-02-18

22. Google Inc. URL: https://code.google.com/p/box2dweb/

Hämtad 2014-02-18

23. Github, box2d.js. URL: https://github.com/HBehrens/box2d.js

Hämtad 2014-02-18

24. Nine Software. URL: http://nine-software.com/2012/12/box2d-vs-chipmunk-html5-games/

Hämtad 2014-02-18

25. creativeJS. URL: http://creativejs.com/2011/09/box2d-JavaScript-tutorial-series-by-seth-ladd/

Hämtad 2014-02-18

26. Stackoverflow. URL: http://stackoverflow.com/questions/7628078/which-box2d-JavaScript-library-should-i-use

Hämtad 2014-02-18

27. Chipmunk physics. URL: http://chipmunk-physics.net/forum/viewtopic.php?t=1926

Hämtad 2014-02-18

28. Github, Chipmunk.js. URL: https://github.com/Moerphy/Chipmunk.js

Hämtad 2014-09-28

29. Github, Chipmunk-js. URL: https://github.com/josephg/Chipmunk-js

Hämtad 2013-04-13

30. Extremely Satisfactory Totalitarianism URL: http://extremelysatisfactorytotalitarianism.com/blog/?p=932

Hämtad 2014-02-26

31. Microsoft Inc. URL: http://msdn.microsoft.com/en-us/hh549259.aspx

Hämtad 2014-02-26

32. Itterheim, Steffen. (2012). Learn cocos2D 2. Apress Media LLC.

33. Rettig, Pascal. (2012). Professional HTML5 Mobile Game Development. Första utgåvan. Wrox. 34. Aditya Ravi Shankar. Pro HTML5 Games(2012). Första utgåvan. Apress Media LLC.

References

Related documents

Eftersom det bara är en motor så behöver man kontrollera om dess effekt ska gå till vajern eller trumman, vilket kan göras genom att välja vilken av planetbäraren eller ringhjulet

Hall och Cook menar att lärare inte vet själva hur mycket de använder sig utav det egna språket vilket gör att det varierar mellan olika kontexter i undervisningen och detta ger då

Eftersom det i dagsläget tillverkas cirka 50 bakaxelmodeller finns ett flertal olika. Standardutförande vid axeltransport är att använda två stycken xturerna används även ett av

Ultraljud sensorer som undersökts har ett avstånd från givaren, som den inte kan mäta avståndet på, vilket är för långt för ändamålet att mäta steghöjd.. Avståndet det

(Skolverket 2018, s. Detta uppmärksammar mig på hur enormt viktig läsningen är för våra elever då lärare måste ge elever möjlighet att lyssna till läsning för att eleverna

Segmentation results for six different burn wound images: the 1 st column shows the segmentation results using the CIELab coordinates as input of the FCM algorithm with 20

Avseende tillgodoräknande av övrig utbildning antogs att det skulle vara ett fåtal tränare som fått sina utbildningar validerade. Antagandet grundas dels i att tre

Både tidigare forskning (Enochsson m.fl., 2007, s. 29) och vår empiri visar att samverkan mellan skolan och socialtjänsten gällande HVF är viktig för att nå ett helhetsperspektiv då