Advertisement
  1. Game Development
  2. Java

Úvod do JavaFX pro vývoj her

Scroll to top
Read Time: 15 min

Czech (Čeština) translation by Tereza Foretová (you can also view the original English article)

JavaFX je multiplatformní GUI toolkit pro Java a je nástupcem knihovny Java Swing. V tomto kurzu budeme zkoumat funkce JavaFX, které je snadno ovladatelný, jak začít programovat hry v Javě.

Tento kurz předpokládá, že už víte, jak se kód v jazyce Java. Pokud ne, podívejte se na další Java pro Android, Úvod do programování počítače s Java: 101 a 201, hlava první Java, Greenfoot nebo naučit Java tvrdý způsob, jak začít.

Instalace

Pokud jste již vyvíjet aplikace Java, pravděpodobně nebudete muset vůbec nic stahovat: JavaFX byla součástí standardní sady JDK (Java Development Kit) od JDK verze 7u6 (srpen 2012). Pokud jste neaktualizovali své instalace Javy ve chvíli, zamiřte do Java stáhnout webové stránky pro nejnovější verzi.

Základní rámec tříd

Vytvoření programu JavaFX začíná třída aplikace, z něhož jsou rozšířeny všechny aplikace JavaFX. Hlavní třída by měla zavolat metodu launch(), která pak zavolá metodu init() a poté vyčkejte na dokončení aplikace metodu start() a poté zavolejte metodu stop(). Z těchto metod metoda start() je abstraktní a musí být přepsána.

Třída Stage je nejvyšší úroveň JavaFX kontejner. Když se spustí aplikace, počáteční fázi je vytvořen a předán do aplikace start metody. Fáze řízení základní okno Vlastnosti, například titul, ikony, viditelnost, resizability, celoobrazovkový režim a dekorace; Ta je nakonfigurován pomocí StageStyle. Další fází může být vytvořena podle potřeby. Po fázi je nakonfigurován a přidávání obsahu, je volána metoda show().

Věděl to všechno, můžeme napsat minimální příklad, který otevře okno v JavaFX:

1
import javafx.application.Application;
2
import javafx.stage.Stage;
3
4
public class Example1 extends Application 
5
{
6
    public static void main(String[] args) 
7
    {
8
        launch(args);
9
    }
10
11
    public void start(Stage theStage) 
12
    {
13
        theStage.setTitle("Hello, World!");
14
        theStage.show();
15
    }
16
}

Strukturování obsahu

Obsah v JavaFX (např. text, obrázky a UI prvky) je uspořádán pomocí stromu jako datové struktury známé jako grafu scény, která seskupí a uspořádá prvky grafické scény.

JavaFX Scene GraphJavaFX Scene GraphJavaFX Scene Graph
Znázornění grafu JavaFX scény.

Obecným prvkem graf scény v JavaFX se nazývá uzel. Každý uzel ve stromu má jeden "nadřazený" uzel, s výjimkou speciální uzel, označené jako "root". Skupina je uzel, který může mít mnoho prvky uzlu "dítě". Grafické transformace (překlady, rotace a měřítko) a efekty, které aplikujete na skupinu platí také pro jeho děti. Uzly může být navržen pomocí JavaFX kaskádových stylů (CSS), podobně jako CSS pro formátování dokumentů HTML.

Třída Scene obsahuje veškerý obsah pro graf scény a vyžaduje kořenový uzel nastavit (v praxi to je často skupina). Můžete nastavit velikost scény konkrétně; jinak velikost scény bude automaticky vypočtou na základě jeho obsahu. Scénu objekt musí být předán do fáze (metodou setScene()) Chcete-li zobrazit.

Vykreslování grafiky

Vykreslování grafiky je obzvlášť důležité programátorům her! V JavaFX plátno objekt je obrázek na kterém získáme text, tvary a obrázky, pomocí jeho přidruženého objektu GraphicsContext. (Pro ty vývojáři, kteří znají Java Swing toolkit, to je obdobou Graphics objektu předaný metodě paint() třídy JFrame.)

Objekt GraphicsContext obsahuje spoustu vlastních úprav schopnosti. Chcete-li vybrat barvy pro kreslení text a tvary, můžete nastavit výplň (interiér) a tah (hranice) barvy, které jsou objekty, malování: to může být jediný plnou barvou, přechodovou definované uživatelem (LinearGradient nebo RadialGradient) nebo dokonce ImagePattern. Můžete také použít jeden nebo více účinek styl objektů, například osvětlení, stín nebo GaussianBlur a změnit výchozí písma pomocí třídy písmo.

Image třída usnadňuje načítat obrázky z různých formátů souborů a jejich kreslení pomocí třídy GraphicsContext. Je to snadné konstrukci procedurálně generované obrazy pomocí třídy WritableImage spolu s PixelReader a PixelWriter třídy.

Pomocí těchto tříd, můžeme napsat mnohem více hoden "Hello, World"-styl příklad následovně. Pro stručnost, uvedeme jen metodu start() (budeme přeskočit příkazy pro import a main() metody); Nicméně kompletní funkční zdrojový kód naleznete v GitHub repo, který doprovází tento návod.

1
public void start(Stage theStage) 
2
{
3
    theStage.setTitle( "Canvas Example" );
4
        
5
    Group root = new Group();
6
    Scene theScene = new Scene( root );
7
    theStage.setScene( theScene );
8
        
9
    Canvas canvas = new Canvas( 400, 200 );
10
    root.getChildren().add( canvas );
11
        
12
    GraphicsContext gc = canvas.getGraphicsContext2D();
13
        
14
    gc.setFill( Color.RED );
15
    gc.setStroke( Color.BLACK );
16
    gc.setLineWidth(2);
17
    Font theFont = Font.font( "Times New Roman", FontWeight.BOLD, 48 );
18
    gc.setFont( theFont );
19
    gc.fillText( "Hello, World!", 60, 50 );
20
    gc.strokeText( "Hello, World!", 60, 50 );
21
    
22
    Image earth = new Image( "earth.png" );
23
    gc.drawImage( earth, 180, 100 );
24
        
25
    theStage.show();
26
}

Herní smyčky

Dále potřebujeme, aby naše programy dynamické, což znamená, že v průběhu času mění stav hry. Jsme budete implementovat herní smyčky: nekonečnou smyčku, která aktualizuje herní objekty a vykresluje scénu na obrazovku, v ideálním případě ve výši 60 krát za sekundu.

Nejjednodušší způsob, jak dosáhnout tohoto v JavaFX používá AnimationTimer třídy, kde metoda (pojmenované handle()) může zapisovat, který bude volat ve výši 60 krát za sekundu, nebo jako blízko k této sazbě jako je možné. (Tato třída nemusí být použity výhradně pro účely animací, dokáže mnohem víc.)

Pomocí třídy AnimationTimer je trochu složitější: protože to je abstraktní třída, nelze vytvořit přímo – třída musí být prodloužena dříve, než mohou být vytvořena instance. Však naše jednoduché příklady, jsme se rozšířit třídu napsáním anonymní vnitřní třídy. Tato vnitřní třída musí definovat abstraktní metodu handle(), který bude předán jeden argument: aktuální systémový čas v nanosekundách. Po definování vnitřní třída, jsme okamžitě vyvolat metodu start(), která začíná smyčku. (Smyčky může být zastavena voláním metody stop().)

Pomocí těchto tříd můžeme upravit náš příklad "Hello, World" vytváření animace sestávající z zemi obíhající kolem slunce proti hvězdné pozadí obrazu.

1
public void start(Stage theStage) 
2
{
3
    theStage.setTitle( "Timeline Example" );
4
5
    Group root = new Group();
6
    Scene theScene = new Scene( root );
7
    theStage.setScene( theScene );
8
9
    Canvas canvas = new Canvas( 512, 512 );
10
    root.getChildren().add( canvas );
11
12
    GraphicsContext gc = canvas.getGraphicsContext2D();
13
14
    Image earth = new Image( "earth.png" );
15
    Image sun   = new Image( "sun.png" );
16
    Image space = new Image( "space.png" );
17
18
    final long startNanoTime = System.nanoTime();
19
20
    new AnimationTimer()
21
    {
22
        public void handle(long currentNanoTime)
23
        {
24
            double t = (currentNanoTime - startNanoTime) / 1000000000.0; 
25
26
            double x = 232 + 128 * Math.cos(t);
27
            double y = 232 + 128 * Math.sin(t);
28
29
            // background image clears canvas

30
            gc.drawImage( space, 0, 0 );
31
            gc.drawImage( earth, x, y );
32
            gc.drawImage( sun, 196, 196 );
33
        }
34
    }.start();
35
36
    theStage.show();
37
}

Existují alternativní způsoby, jak implementovat herní smyčky v JavaFX. Trochu delší (ale pružnější) přístup zahrnuje třída časové osy, která je animační sekvence sestávající z řady objektů, klíčový snímek. Chcete-li vytvořit herní smyčky, je třeba nastavit časové osy na opakovalo, a jen jediný klíčový snímek je požadováno, s jeho trvání nastavena na 0.016 sekund (k dosažení 60 cyklů za sekundu). Tato implementace lze nalézt v souboru Example3T.java v GitHub repo.

Založené na rámečku animace

Další běžně potřebné herní programování komponenta je založené na rámečku animace: zobrazení pořadí snímků v rychlém sledu k vytvoření iluze pohybu.

Za předpokladu, že všechny animace opakovat a všechny snímky zobrazovat stejný počet sekund, základní provedení by mohlo být stejně jednoduché následovně:

1
public class AnimatedImage
2
{
3
    public Image[] frames;
4
    public double duration;
5
    
6
    public Image getFrame(double time)
7
    {
8
        int index = (int)((time % (frames.length * duration)) / duration);
9
        return frames[index];
10
    }
11
}

Chcete-li integrovat tuto třídu do předchozího příkladu, bychom mohli vytvořit animovaný UFO, inicializaci objektu pomocí kódu:

1
AnimatedImage ufo = new AnimatedImage();
2
Image[] imageArray = new Image[6];
3
for (int i = 0; i < 6; i++)
4
    imageArray[i] = new Image( "ufo_" + i + ".png" );
5
ufo.frames = imageArray;
6
ufo.duration = 0.100;

... a v rámci AnimationTimer, přidáním jednoho řádku kódu:

1
gc.drawImage( ufo.getFrame(t), 450, 25 ); 

.. .at příhodné místo. Kompletní příklad pracovní kódu naleznete v souboru Example3AI.java v GitHub repo.

Zpracování vstupu uživatele

Zjišťování a zpracování vstupu uživatele v JavaFX je jednoduché. Uživatelské akce, které mohou být detekovány v systému, například stisknutí kláves a klepnutí myší, se nazývají události. V JavaFX tyto akce automaticky způsobit generování objektů (například KeyEvent a MouseEvent), které ukládají přidružená data (například skutečný klíč stisknutí nebo umístění ukazatele myši). JavaFX třídy, která implementuje EventTarget třídy, například scénu, může "poslouchat" události a zpracování v následujících příkladech vám ukážeme, jak nastavit scénu zpracovat různé události.

Pohlédl přes dokumentaci pro třídu scénu, existuje mnoho metod, které naslouchání pro zpracování různých typů vstupů z různých zdrojů. Například metodu setOnKeyPressed() můžete přiřadit EventHandler, která se aktivuje při stisknutí klávesy, setOnMouseClicked() Metoda přiřadit EventHandler, který se aktivuje, když je stisknuto tlačítko myši, a tak dále. EventHandler třída slouží jednomu účelu: k zapouzdření metoda (tzv. handle()), který se nazývá při výskytu příslušné události.

Při vytváření EventHandler, je nutné zadat typ události, která zpracovává: můžete deklarovat EventHandler<KeyEvent> nebo EventHandler<MouseEvent>, například.</MouseEvent> </KeyEvent> Také obslužné rutiny jsou často vytvořeny jako anonymní vnitřní třídy, jako jsou obvykle použity pouze jednou (když jsou předávány jako argument do jedné z výše uvedených způsobů).

Zpracování události klávesnice

Vstup uživatele je často zpracovány do hlavní herní smyčky, a tedy musí být vedeny záznamy, které klávesy jsou momentálně aktivní. Jedním ze způsobů jak toho dosáhnout je tím, že vytvoří ArrayList String objektů. Když zpočátku stisknutí klávesy, přidáme řetězcovou reprezentaci KeyEvent KeyCode do seznamu; Když klíč je propuštěn, odstraníme ji ze seznamu.

V následujícím příkladu plátna obsahuje dva obrazy klávesy se šipkami; při každém stisknutí klávesy odpovídající obraz se zabarví zeleně.


Zdrojový kód je obsažen v souboru Example4K.java v GitHub repo.

1
public void start(Stage theStage) 
2
    {
3
        theStage.setTitle( "Keyboard Example" );
4
5
        Group root = new Group();
6
        Scene theScene = new Scene( root );
7
        theStage.setScene( theScene );
8
9
        Canvas canvas = new Canvas( 512 - 64, 256 );
10
        root.getChildren().add( canvas );
11
12
        ArrayList<String> input = new ArrayList<String>();
13
14
        theScene.setOnKeyPressed(
15
            new EventHandler<KeyEvent>()
16
            {
17
                public void handle(KeyEvent e)
18
                {
19
                    String code = e.getCode().toString();
20
21
                    // only add once... prevent duplicates

22
                    if ( !input.contains(code) )
23
                        input.add( code );
24
                }
25
            });
26
27
        theScene.setOnKeyReleased(
28
            new EventHandler<KeyEvent>()
29
            {
30
                public void handle(KeyEvent e)
31
                {
32
                    String code = e.getCode().toString();
33
                    input.remove( code );
34
                }
35
            });
36
37
        GraphicsContext gc = canvas.getGraphicsContext2D();
38
39
        Image left = new Image( "left.png" );
40
        Image leftG = new Image( "leftG.png" );
41
42
        Image right = new Image( "right.png" );
43
        Image rightG = new Image( "rightG.png" );
44
45
        new AnimationTimer()
46
        {
47
            public void handle(long currentNanoTime)
48
            {
49
                // Clear the canvas

50
                gc.clearRect(0, 0, 512,512);
51
52
                if (input.contains("LEFT"))
53
                    gc.drawImage( leftG, 64, 64 );
54
                else
55
                    gc.drawImage( left, 64, 64 );
56
57
                if (input.contains("RIGHT"))
58
                    gc.drawImage( rightG, 256, 64 );
59
                else
60
                    gc.drawImage( right, 256, 64 );
61
            }
62
        }.start();
63
64
        theStage.show();
65
    }

Zpracování událostí myši

Nyní Podívejme se na příklad, který se zaměřuje na třídy MouseEvent spíše než KeyEvent třídy. V této minihře hráč nasbírá bod při každém klepnutí na cíl.


Vzhledem k tomu, obslužné rutiny jsou vnitřní třídy, musí být proměnné, které používají finále nebo "efektivně finální", což znamená, že proměnné nemůže být reinicializována. V předchozím příkladu byla data předána EventHandler prostřednictvím ArrayList, jejichž hodnoty mohou být změněny bez reinitializing (prostřednictvím metody add() a remove()).

Nicméně v případě základní datové typy, hodnoty nelze změnit jednou inicializována. Pokud chcete EventHandler pro přístup k základní datové typy, které jsou změněny jinde v programu, můžete vytvořit souhrnný třídu, která obsahuje veřejné proměnné nebo vlastnosti getter/setter metody. (V následujícím příkladu IntValue je třída, která obsahuje veřejné int proměnnou s názvem hodnotu.)

1
public void start(Stage theStage) 
2
{
3
    theStage.setTitle( "Click the Target!" );
4
5
    Group root = new Group();
6
    Scene theScene = new Scene( root );
7
    theStage.setScene( theScene );
8
9
    Canvas canvas = new Canvas( 500, 500 );
10
11
    root.getChildren().add( canvas );
12
13
    Circle targetData = new Circle(100,100,32);
14
    IntValue points = new IntValue(0);
15
16
    theScene.setOnMouseClicked(
17
        new EventHandler<MouseEvent>()
18
        {
19
            public void handle(MouseEvent e)
20
            {
21
                if ( targetData.containsPoint( e.getX(), e.getY() ) )
22
                {
23
                    double x = 50 + 400 * Math.random(); 
24
                    double y = 50 + 400 * Math.random();
25
                    targetData.setCenter(x,y);
26
                    points.value++;
27
                }
28
                else
29
                    points.value = 0;
30
            }
31
        });
32
33
    GraphicsContext gc = canvas.getGraphicsContext2D();
34
35
    Font theFont = Font.font( "Helvetica", FontWeight.BOLD, 24 );
36
    gc.setFont( theFont );
37
    gc.setStroke( Color.BLACK );
38
    gc.setLineWidth(1);
39
40
    Image bullseye = new Image( "bullseye.png" );
41
42
    new AnimationTimer()
43
    {
44
        public void handle(long currentNanoTime)
45
        {
46
            // Clear the canvas

47
            gc.setFill( new Color(0.85, 0.85, 1.0, 1.0) );
48
            gc.fillRect(0,0, 512,512);
49
50
            gc.drawImage( bullseye, 
51
                targetData.getX() - targetData.getRadius(),
52
                targetData.getY() - targetData.getRadius() );
53
54
            gc.setFill( Color.BLUE );
55
56
            String pointsText = "Points: " + points.value;
57
            gc.fillText( pointsText, 360, 36 );
58
            gc.strokeText( pointsText, 360, 36 );
59
        }
60
    }.start();
61
62
    theStage.show();
63
}

Úplný zdrojový kód je obsažen v GitHub repo; Hlavní třída je Example4M.java.

Vytvoření třídy základní Sprite s JavaFX

Ve videohrách sprite je termín pro jeden vizuální celek. Níže je příklad třídy Sprite, který ukládá obraz a postavení, stejně jako rychlost informace (pro mobilní entity) a šířky/výšky informace při výpočtu ohraničovací rámečky pro účely detekce kolizí. Máme také standardní vlastnosti getter/setter metody pro většinu těchto dat (vynechán pro stručnost) a některých standardních metod potřebných ve vývoji her:

  • Update(): vypočítá novou pozici na základě Sprite je rychlost.
  • Render(): kreslí přidružit obraz na plátno (přes třídu GraphicsContext) pomocí umístění jako souřadnice.
  • getBoundary(): vrátí JavaFX Rectangle2D objekt, který je užitečný při detekci kolizí vzhledem k jeho protíná metoda.
  • intersects(): Určuje, zda ohraničovacího rámečku tohoto Sprite protíná s tím jiného pohyblivého symbolu.
1
public class Sprite
2
{
3
    private Image image;
4
    private double positionX;
5
    private double positionY;    
6
    private double velocityX;
7
    private double velocityY;
8
    private double width;
9
    private double height;
10
11
    // ...

12
    // methods omitted for brevity

13
    // ...

14
15
    public void update(double time)
16
    {
17
        positionX += velocityX * time;
18
        positionY += velocityY * time;
19
    }
20
21
    public void render(GraphicsContext gc)
22
    {
23
        gc.drawImage( image, positionX, positionY );
24
    }
25
26
    public Rectangle2D getBoundary()
27
    {
28
        return new Rectangle2D(positionX,positionY,width,height);
29
    }
30
31
    public boolean intersects(Sprite s)
32
    {
33
        return s.getBoundary().intersects( this.getBoundary() );
34
    }
35
}

Úplný zdrojový kód je součástí Sprite.java v GitHub repo.

Pomocí třídy Sprite

S pomocí třídy Sprite, můžeme snadno vytvořit jednoduchý sběr hra v JavaFX. V této hře můžete převzít roli vnímající aktovky, jehož cílem je shromáždit mnoho pytle s penězi, které byly ponechány ležící kolem neopatrný předchozí majitel. Kláves se šipkami přesuňte hráč po obrazovce.

Tento kód výrazně čerpá z předchozích příkladů: nastavení písma zobrazit skóre, tvorbě tříd obálky pro jednoduché hodnoty, které musí být změněny během ukládání vstup z klávesnice s ArrayList a implementace herní smyčku AnimationTimer herní smyčky.

Jeden segment kódu zvláštního zájmu zahrnuje vytvoření objektu Sprite pro hráče (Aktovky) a ArrayList Sprite objektů pro sběratelství (pytle s penězi):

1
Sprite briefcase = new Sprite();
2
briefcase.setImage("briefcase.png");
3
briefcase.setPosition(200, 0);
4
5
ArrayList<Sprite> moneybagList = new ArrayList<Sprite>();
6
7
for (int i = 0; i < 15; i++)
8
{
9
    Sprite moneybag = new Sprite();
10
  moneybag.setImage("moneybag.png");
11
	double px = 350 * Math.random() + 50;
12
	double py = 350 * Math.random() + 50;          
13
	moneybag.setPosition(px,py);
14
	moneybagList.add( moneybag );
15
}

Další segment kódu zájmu je vytvoření AnimationTimer, který je pověřen:

  • Výpočet doby, která uplynula od poslední aktualizace
  • nastavení přehrávače rychlost v závislosti na aktuálně stisknutí kláves
  • detekci střetu mezi přehrávačem a sběratelství a aktualizace skóre a seznam sběratelství, pokud k tomu dojde (iterace se používá spíše než ArrayList přímo k vyhnout souběžné modifikaci výjimku při odstraňování objektů z seznam)
  • vykreslování spritů a text na plátně
1
new AnimationTimer()
2
{
3
    public void handle(long currentNanoTime)
4
	{
5
		// calculate time since last update.

6
		double elapsedTime = (currentNanoTime - lastNanoTime.value) / 1000000000.0;
7
		lastNanoTime.value = currentNanoTime;
8
		
9
		// game logic

10
		
11
		briefcase.setVelocity(0,0);
12
		if (input.contains("LEFT"))
13
			briefcase.addVelocity(-50,0);
14
		if (input.contains("RIGHT"))
15
			briefcase.addVelocity(50,0);
16
		if (input.contains("UP"))
17
			briefcase.addVelocity(0,-50);
18
		if (input.contains("DOWN"))
19
			briefcase.addVelocity(0,50);
20
			
21
		briefcase.update(elapsedTime);
22
		
23
		// collision detection

24
		
25
		Iterator<Sprite> moneybagIter = moneybagList.iterator();
26
		while ( moneybagIter.hasNext() )
27
		{
28
			Sprite moneybag = moneybagIter.next();
29
			if ( briefcase.intersects(moneybag) )
30
			{
31
				moneybagIter.remove();
32
				score.value++;
33
			}
34
		}
35
		
36
		// render

37
		
38
		gc.clearRect(0, 0, 512,512);
39
		briefcase.render( gc );
40
		
41
		for (Sprite moneybag : moneybagList )
42
			moneybag.render( gc );
43
44
		String pointsText = "Cash: $" + (100 * score.value);
45
		gc.fillText( pointsText, 360, 36 );
46
		gc.strokeText( pointsText, 360, 36 );
47
	}
48
}.start();

Jako obvykle kompletní kód naleznete v přiloženém kodexu souboru (Example5.java) v GitHub repo.

Další kroky

  • Zde je sbírka úvodní tutoriály na webu Oracle, který vám pomůže naučit se běžné úkoly JavaFX: Začínáme s JavaFX ukázkové aplikace.
  • Může být zájem o učení, jak používat Scene Builder, vizuální rozložení prostředí pro návrh uživatelského rozhraní. Tento program generuje FXML, což je jazyk založený na XML, který lze použít k definování uživatelského rozhraní programu JavaFX. Za to, viz JavaFX Scene Builder: Začínáme.
  • FX je vynikající blog, pravidelně aktualizován, která obsahuje informace a ukázkové projekty v zájmu JavaFX vývojáři. Mnozí z uvedeny ukázky jsou velmi inspirující!
  • José Pereda má vynikající příklady pokročilejší her s JavaFX v jeho úložiště GitHub.
  • Projekt JFxtras je tvořena skupina vývojářů, které vytvořily zvláštní JavaFX součásti, které poskytují běžně potřebné funkce, které jsou v současné době chybí od JavaFX.
  • JavaFXPorts projektu umožňuje balíček JavaFX aplikace pro nasazení na iOS a Android.
  • Oficiální odkazy by záložku pro JavaFX, zejména společnosti Oracle JavaFX guide a dokumentace API.
  • Některé dobře hodnocené knihy o JavaFX patří 8 Pro JavaFX, JavaFX 8 - Úvod příkladem a zvláštního zájmu pro vývojáře her, vývoj hry Java 8 začátek.

Závěr

V tomto kurzu já jsem představil JavaFX tříd, které jsou užitečné při programování her. Pracovali jsme přes řadu příkladů z rostoucí složitosti, vyvrcholilo sprite-based collection styl hry. Teď jsi připraven buď prozkoumáme některé z prostředků uvedených výše, nebo ponořit a začít vytvářet vlastní hru. Hodně štěstí ve vašem snažení!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.