Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon


Home > >

28. February 2013

I have decided to take a closer look at the Loom game engine that I mentioned recently, as I am an absolute sucker for new game engines, especially ones that are currently free.  You can consider these posts a cross between a diary and a review as I dive in to what it's like using Loom.  Of course, I am also documenting the process, so if you are just getting started with Loom hopefully these posts prove useful for you.

 

Alright, let's jump right in.

 

Quick summary to those that didn't read the above link.  Loom is a 2D game engine, based around a custom scripting language Loomscript, which is a bastard love child of ActionScript and C#. Behind the scenes it is written in C++ over the Cocos2D-x library (and others) and you can get full access to the source code. Full disclosure, I've never actually used ActionScript before, but I have a fair bit of JavaScript and C# experience as well as a copy of the book The ActionScript 3.0 Bible so hopefully I can puzzle things out as I go.

 

Getting Started -- Installation and creating your first application

 

First things first head on over to TheEngine.co site sign up and download Loom.  Be sure to use a valid email address and password, you are going to need them later.  Log in and download the version most applicable to you.

Loom1

 

In this particular case, I'm using MacOS, but it should simply be a matter of substituting the words "command prompt" wherever you see "Terminal".

 

Now that it's installed, welcome to an interesting part of Loom.  It is command line driven.  During installation it will have installed itself to somewhere in your systems path (/usr/bin/loom in my case on Mac OS), simply open a terminal/command prompt and change to the directory where you want to create a Loom project.

Now type loom login

When prompted for credentials, use the username and password you signup with.  If all went well you should see:

Loom2

 

If you don't get Login successful! well, good luck finding out if TheEngine.co has good support or not. :)

 

Now we actually want to create a project.  From the same terminal window type:

loom new ProjectName

like so:

Loom3

 

Voila, a new project should have just been created for you.

 

Now simply change into your project directory and type loom run

Loom will now download the latest SDK and compile your code like so:

Loom4

 

And assuming everything went well, your skeleton application will run:

Loom5

And you've just created and run your first loom application in about 4 minutes work.

 

Now for the first annoyance… for some reason, after calling loom run once you close your application window, the script is hung.  You need to CTRL+C to stop execution of the script in your terminal window.  Small oversight, but kinda irritating… at first it just seemed like it was simply hung up, especially as my LoomTest application didn't get focus on execution, so I wasn't even aware it was running at first!

 

Anyways, that's a pretty small beef and something easily fixed in time.

 

 

Setting up an Editor

 

Command line tools are all nice and good, but I am spoiled from years of Visual Studio development… I like working in an IDE or at least an editor.  Fortunately Loom has configurations for a very good one available, Sublime Text.

 

This part is completely optional, but I am now going to configure Sublime Text to work with Loom.

Initially all I am going to do is add LoomScript syntax highlighting, I don't really mind switching to a terminal window to build/run my application.  To do so, head over to this thread and download the linked bundle zip file.  I assume you have already installed SublimeText, if not, do so now.

Extract the zip file.  Inside the extracted folder is another folder called LoomScript.  Copy it.

(The following is MacOS specific)

Open Finder then hit Command Key + Shift + G

In the field enter ~/Library/Application\ Support/Sublime\ Text\ 2/Packages/

Paste the folder LoomScript in this directory.

Now start Sublime Text and select File->Open… and navigate to the folder you created your Loom project in.  If should look like this with complete syntax highlighting.

Loom6

 

This mostly just gives pretty syntax, if you want closer to a full IDE experience, be sure to read this post!

 

TheEngine.co apparently have a full IDE in the works, but for now, IDE should prove a usable solution.  I am going to continue investigating getting code completion working though, a feature I really like.

 

Learning Loom

 

This part may not seem immediately obvious… so now you have your game created, how do you actually go about learning Loom?  

Well, there are two ways.  First off, there are a series of examples you can download and dissect from right here.

Second, in your terminal, change to your project directory and simply type loom docs 

The documentation will then be loaded in your default browser like so:

Loom7

 

Well, we are now setup and running with our first primitive app.  That's enough for now, in the next part I will get down to writing some actual code.

 

You can read the next part right here

blog comments powered by Disqus

Month List

Popular Comments

LibGDX Video Tutorial: Scene2D UI, Widgets, Layout and Skins
Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon


3. février 2015

 

In this next part in the ongoing LibGDX Video tutorial series, we take our final look at the Scene2D library.  Specifically we look at using Scene2D to provide a UI, including buttons, windows, layout containers and more.  We also look at how to mix “normal” and Scene2D games together. 

 

The video is available in full HD here.  The source code from the examples is below the video:

 

Example 1

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.viewport.ScreenViewport;

public class Scene2DUiDemo1 extends ApplicationAdapter {
   private Skin skin;
   private Stage stage;
   
   @Override
   public void create () {
      skin = new Skin(Gdx.files.internal("uiskin.json"));
      stage = new Stage(new ScreenViewport());

      final TextButton button = new TextButton("Click Me",skin,"default");
      button.setWidth(200);
      button.setHeight(50);

      final Dialog dialog = new Dialog("Click Message",skin);

      button.addListener(new ClickListener(){
         @Override
         public void clicked(InputEvent event, float x, float y) {
            dialog.show(stage);
            Timer.schedule(new Timer.Task() {
               @Override
               public void run() {
                  dialog.hide();
               }
            }, 10);
         }
      });
      stage.addActor(button);

      Gdx.input.setInputProcessor(stage);
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
      stage.act(Gdx.graphics.getDeltaTime());
      stage.draw();
   }


}

Example 2

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.*;
import com.badlogic.gdx.scenes.scene2d.utils.Align;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.viewport.ScreenViewport;

public class Scene2DUiDemo1 extends ApplicationAdapter {
   private Skin skin;
   private Stage stage;

   private Table table;
   private TextButton startButton;
   private TextButton quitButton;
   
   @Override
   public void create () {
      skin = new Skin(Gdx.files.internal("uiskin.json"));
      stage = new Stage(new ScreenViewport());
      table = new Table();
      table.setWidth(stage.getWidth());
      table.align(Align.center | Align.top);

      table.setPosition(0,Gdx.graphics.getHeight());
      startButton = new TextButton("New Game",skin);
      quitButton = new TextButton("Quit Game",skin);

      table.padTop(30);

      table.add(startButton).padBottom(30);

      table.row();
      table.add(quitButton);

      stage.addActor(table);
      Gdx.input.setInputProcessor(stage);
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
      stage.act(Gdx.graphics.getDeltaTime());
      stage.draw();
   }


}

 

Example 3

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.RepeatAction;
import com.badlogic.gdx.scenes.scene2d.ui.*;
import com.badlogic.gdx.scenes.scene2d.utils.Align;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.viewport.ScreenViewport;

public class Scene2DUiDemo1 extends ApplicationAdapter implements InputProcessor {
   private Skin skin;
   private Stage stage;

   private Table table;
   private TextButton startButton;
   private TextButton quitButton;

   private SpriteBatch batch;
   private Sprite sprite;
   
   @Override
   public void create () {
      skin = new Skin(Gdx.files.internal("uiskin.json"));
      stage = new Stage(new ScreenViewport());
      table = new Table();
      table.setWidth(stage.getWidth());
      table.align(Align.center | Align.top);

      table.setPosition(0,Gdx.graphics.getHeight());
      startButton = new TextButton("New Game",skin);
      quitButton = new TextButton("Quit Game",skin);

      startButton.addListener(new ClickListener() {
         @Override
         public void clicked(InputEvent event, float x, float y) {
            Gdx.app.log("Clicked button","Yep, you did");
            event.stop();
         }
      });

      table.padTop(30);

      table.add(startButton).padBottom(30);

      table.row();
      table.add(quitButton);

      stage.addActor(table);



      batch = new SpriteBatch();
      sprite = new Sprite(new Texture(Gdx.files.internal("badlogic.jpg")));
      sprite.setSize(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());

      Timer.schedule(new Timer.Task() {
         @Override
         public void run() {
            sprite.setFlip(false,!sprite.isFlipY());
         }
      },10,10,10000);


      // ORDER IS IMPORTANT!
      InputMultiplexer im = new InputMultiplexer(stage,this);
      Gdx.input.setInputProcessor(im);
   }

   @Override
   public void render () {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

      batch.begin();
      sprite.draw(batch);
      batch.end();

      stage.act(Gdx.graphics.getDeltaTime());
      stage.draw();
   }


   @Override
   public boolean keyDown(int keycode) {
      return false;
   }

   @Override
   public boolean keyUp(int keycode) {
      return false;
   }

   @Override
   public boolean keyTyped(char character) {
      return false;
   }

   @Override
   public boolean touchDown(int screenX, int screenY, int pointer, int button) {
      sprite.setFlip(!sprite.isFlipX(),sprite.isFlipY());
      return true;
   }

   @Override
   public boolean touchUp(int screenX, int screenY, int pointer, int button) {
      return false;
   }

   @Override
   public boolean touchDragged(int screenX, int screenY, int pointer) {
      return false;
   }

   @Override
   public boolean mouseMoved(int screenX, int screenY) {
      return false;
   }

   @Override
   public boolean scrolled(int amount) {
      return false;
   }
}

 

uiskin.json

// @formatter:off
{
  com.badlogic.gdx.graphics.g2d.BitmapFont: { default-font: { file: Razer.fnt } },
  com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle: {
   default: { down: default-round-down, up: default-round, font: default-font },
  },
  com.badlogic.gdx.scenes.scene2d.ui.Window$WindowStyle: {
    default:  {
      titleFont: default-font
     }
   }
}

Programming , , ,

blog comments powered by Disqus

Month List

Popular Comments

A Closer Look at jMonkeyEngine
Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon


31. August 2015

 

In this Closer Look At we look at take a look at the jMonkeyEngine.  The Closer Look At game engine series is a cross between an overview, a review and a getting started tutorial to help you decide if a game engine is the right fit for you.  The jMonkeyEngine engine is a Java based, open sourced, cross platform 3djMonkeyCloserLook_450px game engine that runs on most Java supported platforms and can target Windows, Linux, Mac and Android, with iOS and Oculus VR support currently being tested.  jMonkeyEngine is available as both a game library, or as a set of tools built on top of the NetBeans IDE.  For this closer look, we will focus on the full SDK experience.

 

 

This closer look is also available in HD video format here.

 

 

Although we are going to focus on the complete set of tools including in the jMonkeyEngine SDK, keep in mind it can be used in library form if you prefer working in Eclipse or IntelliJ.  You will however lose access to some very convenient tools.

 

 

Meet jMonkeyEngine

 

As I mentioned earlier, jMonkeyEngine ships in two forms, as a set of libraries, or as a complete SDK build on top of the Netbeans IDE.  You can download load the SDK for Windows, Mac or Linux right here.  As of writing, 3.0 is the current released version, while 3.1 is available in development on Github.  This version marks the first public release using the Github platform.  jMonkeyEngine has a few prerequisites before installing, but they basically boil down to having an OpenGL 2 compatible video card and JDK 6 or higher installed.

 

Once downloaded and installed simply run the jMonkeyEngine SDK application.   This is jMonkeyEngine:

image

 

As mentioned earlier, this is actually a preconfigured version of the Netbeans IDE with a set of plugins and extensions to support jMonkeyEngine development.  This means in addition to the various jME tools you get a complete modern Java development environment, meaning code completion, project management, refactoring tools, debugging and more.  I won’t be specifically covering Netbeans functionality in this guide.  If you’ve got prior experience in Eclipse or IntelliJ, you should feel right at home.  Personally I rate the Netbeans experience somewhere between the two, with IntelliJ being quite a bit better, while Eclipse is many many many times worse.  That all said, that is purely opinion, each platform has it’s strength and weakness, it’s fans and haters.  If you prefer to use Eclipse or IntelliJ you can.

 

Hello jMonkeyEngine

 

It is often easiest to start with a simple project, so let’s do exactly that.  Select File->New Project

image

 

A New Project wizard will appear.  All of the standard project types supported by Netbeans are available, but also the new jMonkeyEngine templates are available too.  Select BasicGame and click Next.

image

 

Pick a name and location and click Finish.

image

 

Your project will now be created.  You can have several projects open in the IDE at the same time, just be sure to select the right one in the Projects panel:

image

 

The wizard will have automatically created a project hierarchy for you:

image

 

It’s optional to use this layout, but you are making life more difficult for yourself if you do not.  File paths for textures in imported models are absolute, forcing your hand somewhat in how you import your data.  Again, you can code around this design, but you are making your life more complicated.  For the most part I found the layout fairly logical, but the suggestion to import your models into the Textures folder then relocating them to Models ( well discuss this more later ), well that simply a gross kludge.

 

The New Project wizard also generated a default source file for us, Main.java, with the following contents:

 

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;

/**
 * test
 * @author normenhansen
 */
public class Main extends SimpleApplication {

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        geom.setMaterial(mat);

        rootNode.attachChild(geom);
    }

    @Override
    public void simpleUpdate(float tpf) {
        //TODO: add update code
    }

    @Override
    public void simpleRender(RenderManager rm) {
        //TODO: add render code
    }
}

The code is all pretty straight forward.  You game code extends the class SimpleApplication, which in turn implements Application plus implements some “out of the box” behaviour like key mappings for exiting the application and implementing a camera.  These default behaviours can easily be overridden as we will see shortly.  SimpleApplication exposes three critical methods as part of your games life cycle, simpleInitApp(), called when your app is created, then simpleUpdate() and simpleRender() called over and over by the game event loop.  Basically stick your setup code in the init() method, your update code in the update() method and drawing code in the render() method.  If these methods start getting overly complex, you can refactor your design to use States, something we will cover later on.

 

You can run or debug your project using the toolbar:

image

 

Or via the Run menu:

image

 

Once launched you will see a configuration Window.

image

 

Select your preferred configuration and click Continue.  You may be asking, can I get rid of this damned window?  The answer is yes you can, but you have to use code to do it.  I can’t really fathom why there isn’t a “Remember my settings” check box.  Once you click Continue, your first app will run.

FirstApp

 

As you move the mouse cursor around, the camera implemented in SimpleApplication is moving the camera position around.  You may also notice the debug details and of course that startup window.  As said earlier, this can all be override, let’s look at how.

First we can get rid of the configuration window ( which I admit, gets old very quickly ) and set a default resolution using the following code:

    public static void main(String[] args) {
        Main app = new Main();
        
        // Dont show window
        app.showSettings = false;
        
        // Create a new app settings loaded with defaults
        AppSettings appSettings = new AppSettings(true);
        
        // Override resolution
        appSettings.put("Width",720);
        appSettings.put("Height",480);
        
        // Add a title, just because
        appSettings.put("Title", "Super Awesome Megagame 9000!");
        
        app.setSettings(appSettings);
        app.start();
    }

 

Next in our init we add the following logic to disable the camera and debug info. These need to be called after app.start(), thus why they are in init.

    @Override
    public void simpleInitApp() {
                
        // Disable fly cam
        this.flyCam.setEnabled(false);
        
        // Turn off debug info and FPS window
        this.setDisplayFps(false);
        this.setDisplayStatView(false);
        
        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        geom.setMaterial(mat);

        rootNode.attachChild(geom);
    }

 

Now when you run your game, you should no longer see the config window, nor display stats when running.  Instead you should see:

image

 

Importing a 3D Model

 

One of the first things I do when testing a new engine is check to see how hard it is to get a 3D model imported.  In jMonkeyEngine you have a couple of options, you can import to their native format, use a Blender plugin, support an OBJ file, or import files converted using the Ogre XML toolchain, which is also available as a Blender plugin as well as several other packages.

 

I will use the native format (j3o) later, for now, let’s look at the process of importing a Blender model, since jMonkeyEngine has solid Blender integration built in.  In fact, jMonkeyEngine actually ships with a copy of Blender as part of the SDK install, currently version 2.69 (as of writing, 2.75 is the most current version).  When you run Blender from within jMonkeyEngine, this included version is the one that is run.  (Note, for performance, you should always prefer using the native binary format unless you have a very good reason not to).

 

You can add a new textured Blender cube (you don’t have to by the way), right click the desired location and select File->New->Other…

image

 

Then select Blender->Box prepared for UV texturing.

image

 

Name it and confirm the location, then click Finish.

image

 

This will run a copy of Blender and set up a cube with textures defined for you.

image

 

What’s extremely odd here is the configured cube isn’t actually ready to go.  You still need to UV unwrap the cube, attach a texture and set the UVmap.   You can see the entire process in the video if you need more details.

 

You can confirm that the blend file works fine, right click the blend and select View Model.

image

 

This will open the Viewer.

image

Be sure to click the light icon (top left) to enable lighting in the viewer.  Now that we know the Blender file works, let’s move over to the code to load a Blender file.  there is a bit of a challenge first, Blender support is actually added as a plugin, we need to add it in first.

 

Right click Libraries and select Add Library…

image

 

Select jme3-libraries-blender then click Add Library.

image

 

We need to add a light to the scene or the model isn’t going to show up.  Simply drag and drop SunLight from to the drop of the simpleInitApp() code and it will drop all the code we need.

image

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Spatial;

public class Main extends SimpleApplication {

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        /** A white, directional light source */ 
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun); 
        Spatial blenderModel = assetManager.loadModel("Models/demoBox.blend");
        rootNode.attachChild(blenderModel);
    }

    @Override
    public void simpleUpdate(float tpf) {
        //TODO: add update code
    }

    @Override
    public void simpleRender(RenderManager rm) {
        //TODO: add render code
    }
}

And run it:

image

 

So other than Blender configuration, getting a model into a jMonkeyEngine app is fairly straight forward.

 

Tools in jMonkeyEngine

 

Code Palette

We briefly saw the Palette in action in the previous example.

image

This is a selection of code snippets you can drag and drop into the editor.  One major gotcha however, many of these samples depend on a library, jme3-test-data, that isn’t included by default oddly enough.  We saw earlier when we set up the Blender plugin the process of adding a library.

 

3D File Importer

While jMonkeyEngine supports the Ogre XML format and Blend files, working with a game oriented file format is almost always the best performing option.  Fortunately jMonkeyEngine provides just such a format, j3o.  These files can be created easily using the menu File->Import Model menu:

image

 

Then select the model

image

 

Material/Shader Editor

You can easily create shaders right clicking an Asset folder such as Materials, New->Other…

image

 

Then Material->Empty Material file

image

 

You can then define a shader using a UI tool.  You can also set a template that other materials inherit from.

image

 

3D Scene Composer

image

 

The Scene Composer can be use to assemble and create 3D scenes.  There is also a corresponding scene graph:

image

 

A variety of game nodes can be created here:

image

 

Terrain Editor

In addition to the Scene Composer, there is also a 3d terrain tool:

image

You can create terrain visually.  Easily pull and push terrain into shape, paint with multiple textures.  The generated terrain can be used in the scene composer.

terrain

 

Engine Capabilities

 

We only briefly touched upon the code capabilities of the jMonkeyEngine due to time and space restraints.  jMonkeyEngine is a full functioning engine with the following functionality, excerpted from their website.

image

 

Documentation and Community

jMonkeyEngine is well documented, with a comprehensive collection of tutorials and guides available on the wiki.  I encountered a few entries that were out of date or invalid, but for the most part the document was solid and easy to follow.  There is also a good reference in the form of the JavaDoc.  I may not always be the biggest Java fan, but I almost always love JavaDoc generated references!

Until recently the forums for jMonkeyEngine were pretty terrible, but thankfully they’ve recently transitioned to an improved forum.  There is an active community and questions rarely go unanswered.  They have also recently transitioned the source code to Github.

 

Books

 

There are two books available for jMonkeyEngine.

jm1jm2

 

 

The Video

 

Programming , , ,

blog comments powered by Disqus

Month List

Popular Comments