Wednesday, January 30, 2013

Spaceship Warrior Pt 2 (Level 1)

Quest Log:

Sweat drips from your brow, the stifling heat threatening to derail your concentration.  You have glanced through the code, but it is frustratingly unclear where to start.  You decide to go to your roots.  Start with the smallest pieces.

Set up a two Java Projects as we discussed before, GameXYZ and GameXYZ-desktop.  In GameXYZ's buildpath, add gdx.jar and artemis-xxxxxxx.jar.  Don't forget to hit the "Order and Export" tab in check gdx.jar.  In GameXYZ-desktop's add gdx-natives, gdx-backend-lwjgl, and gdx-backend-lwjgl-natives.  Also, make sure you hit the Build Path -> Projects tab and reference GameXYZ.  Spaceship Warrior uses more .jar's than we have included, but we'll build up to them.

In GameXYZ-desktop, create a Launcher.java class in new package com.gamexyz.
package com.gamexyz;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;

public class Launcher extends Game {
 public static final int FRAME_WIDTH = 1280;
 public static final int FRAME_HEIGHT = 900;
 
 @Override
 public void create() {
  setScreen(new GameXYZ(this));
 }
 
 public static void main(String[] args) {
  LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
  cfg.width=FRAME_WIDTH;
  cfg.height=FRAME_HEIGHT;
  cfg.useGL20=true;
  cfg.title = "GameXYZ";
  new LwjglApplication(new Launcher(), cfg);
 }
}


In GameXYZ create a GameXYZ.java class in new package com.gamexyz.
package com.gamexyz;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;

public class GameXYZ implements Screen {

 OrthographicCamera camera;

 public GameXYZ(Game game) {

     camera = new OrthographicCamera();
     camera.setToOrtho(false, 1280,900);
 }
 
 @Override
    public void render (float delta) {
     
     Gdx.gl.glClearColor(0,0,0.2f,1);
     Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
     
     camera.update();         
 }
}
Eclipse Tip:  You don't have to put in your own import commands.  Once you have typed the code without them, you can press ctrl+shift+o, and they will automatically get added.  If there is any ambiguity, you have to select which package you meant to import from.


Eclipse will now warn you that because class GameXYZ implements Screen, it must implement a series of methods.  It can automatically add them, leaving them empty.  I like to add reporters to them, so my list looks like this:

 @Override
    public void resize (int width, int height) {
     System.out.println("Resize");
    }

 @Override
    public void pause () {
     System.out.println("Pause");
    }

 @Override
    public void resume () {
     System.out.println("Resume");
    }

 @Override
    public void dispose () {
     System.out.println("Dispose");
    }

 @Override
 public void show() {
  System.out.println("Show");
 }

 @Override
 public void hide() {
  System.out.println("Hide");
 }

Cold dread washes over you as you realize just how different things seem.  Now our Launcher extends something called Game.  And our GameXYZ implements something called Screen.  You check the Javadocs for them, and see that Game is an ApplicationListener, like what you used in Drop, but that it can delegate to screens, allowing multiple screens to be created.  This strikes you as a powerful tool, because someday you plan on having splash screens, menus, inventory screens, and more.  Perhaps this Game/Screen feature will be useful.

You decide that it's time to start pursuing Artemis, you want to create a character for your Spaceship Warrior.  You check the demo code and decide that you should start by making Components for Position and Sprite.  In GameXYZ, you create a new package called com.gamexyz.components and add these two classes to it:

package com.gamexyz.components;

import com.artemis.Component;

public class Position extends Component {
 
 public Position(float x, float y) {
  this.x = x;
  this.y = y;
 }
 
 public Position() {
  this(0,0);
 }
 
 public float x, y;
}
package com.gamexyz.components;

import com.artemis.Component;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;

public class Sprite extends Component {
 
 public Sprite(String path) {
  sprite = new Texture(Gdx.files.internal(path));
 }
 
 public Sprite() {
  this("textures-original/fighter.png");
 }
 
 public Texture sprite;
 public float r = 1;
 public float g = 1;
 public float b = 1;
 public float a = 1;
 public float scaleX = 1;
 public float scaleY = 1;
 public float rotation;

}
Your Sprite class is a shadow of the target, but it should be enough to start with.  You also chose to copy the entire "textures-original" folder from Spaceship Warrior, and put it in your GameXYZ-desktop package.  A quick run of Launcher reveals that your code is not broken... yet.  Clearly though, these components alone are not enough to draw your ship to the screen.

Artemis is based on Components and Systems.  Components only hold data, but no logic.  Systems grab all the entities that have the relevant components and process the logic of them.  For instance, a SpriteRenderSystem may be used to draw entities that have both Position and Sprite classes.

Create a new package com.gamexyz.systems and a new class SpriteRenderSystem.java.
public class SpriteRenderSystem extends EntitySystem {
 @Mapper ComponentMapper<Position> pm;
 @Mapper ComponentMapper<Sprite> sm;
 
 private OrthographicCamera camera;
 private SpriteBatch batch;
 
 @SuppressWarnings("unchecked")
 public SpriteRenderSystem(OrthographicCamera camera) {
  super(Aspect.getAspectForAll(Position.class, Sprite.class));
  this.camera = camera;
 }
 
 @Override
 protected void initialize() {
  batch = new SpriteBatch();
 }

 @Override
 protected boolean checkProcessing() {
  return true;
 }

 @Override
 protected void processEntities(ImmutableBag<Entity> entities) {
  
  for (int i = 0; i < entities.size(); i++) {
   process(entities.get(i));
  }
 }
 
 @Override
 protected void begin() {
  batch.setProjectionMatrix(camera.combined);
  batch.begin();
 }
 
 protected void process(Entity e) {
  if (pm.has(e)) {
   Position position = pm.getSafe(e);
   Sprite sprite = sm.get(e);
   
   batch.setColor(sprite.r, sprite.g, sprite.b, sprite.a);
   float posx = position.x;
   float posy = position.y;
   
   batch.draw(sprite.sprite, posx, posy);
  }
 }
 
 @Override
 protected void end() {
  batch.end();
 }
}

As with Sprite.java, you decided to start smaller, with more familiar tools.  You notice that SpriteRenderingSystem extends EntitySystem, and you look up its code in the Google code repository.  You notice that it has a process() method that looks like this:
public final void process() {
  if(checkProcessing()) {
    begin();
    processEntities(actives);
    end();
  }
}

As such you learned that by switching checkProcessing() to false, you can disable the system.  You also learned the order our child methods are called in.

Using @Mapper instead of getComponent speeds execution up, apparently.  getAspectForAll() gets all the entities which have EVERY one of the argument Components.  You can also use getAspectForOne to get entities that only have at least one of the listed Components.  process() is where most of the magic happens, but begin() and end() call batch.begin() and batch.end(), which we know is important from the drop demo.

You review the demo code to see how to start processing your new system.  In GameScreen.java, you see they have defined a "World", and they registered their systems to this world.  They also have a special object for SpriteRenderSystem, though not all of their systems got this same treatment.  Also, when they add the SpriteRenderSystem, it gets a second argument of true.  Reading more documentation you learn that this is to stop SpriteRenderSystem from running automatically, you must call it manually.

You study the code that shows initializing the world, creating a new Entity, and adding Components to it.  You also look down in the render() method, and see the use of world.setDelta(), world.process(), and spriteRenderSystem.process().  You include all these in your new GameXYZ.java:
public class GameXYZ implements Screen {

 private OrthographicCamera camera;
 private Game game;
 private World world;
 
 private SpriteRenderSystem spriteRenderSystem;

 public GameXYZ(Game game) {
  
     camera = new OrthographicCamera();
     camera.setToOrtho(false, 1280,900);
     
     this.game = game;
     
     world = new World();
     spriteRenderSystem = world.setSystem(new SpriteRenderSystem(camera),true);
     
     world.initialize();
     
     Entity e = world.createEntity();
     e.addComponent(new Position(150,150));
     e.addComponent(new Sprite());
     e.addToWorld();
 }
 
 @Override
    public void render (float delta) {
     
     Gdx.gl.glClearColor(0,0,0.2f,1);
     Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
     
     camera.update();
     
     world.setDelta(delta);
     world.process();
     spriteRenderSystem.process();
 }
    (...)
}

You expertly use ctrl+shift+o to clean up any needed imports, and congratulate yourself on a job well done.  You have a ship.

You gain 50 XP.  Progress to Level 2: 100/400

Spaceship Warrior Pt 1 (Level 1)

Adrenaline rushes through your veins as the Drop Beast's blood drips from your bare hands.  You have tasted victory, and now have a hunger which can never truly be satisfied.  Back in town, you hear whispers about a new creature plaguing the outlying farms, each of it's 200 teeth the size of a broadsword, harder than diamond, and sharper than a sharp pair of scissors.  The creature is known as Spaceship Warrior, and rumors say that it possesses the power of both libgdx AND Artemis.  You let a slow smile creep into your face: fate has favored those farmers today.  You're on the job now.

You ask around for any information you can find, and discover that a magical scroll is required to summon the monster at will: MercurialEclipse.  You've heard of it before.  This scroll can summon forth anything, you will have to be very careful in its application.

Once it has been properly installed within you Eclipse spellbook, begin the summoning ritual.

On the Google code page, click the "Checkout" link.  Where it says "Command-line access" copy the link, starting with https://...  Back in Eclipse, click File->Import.  In the list of options, find Mercurial->Clone Existing Mercurial Repository, and click Next.  In the URL field, paste the checkout URL from Google and select finish.  In the Project Explorer on the left side of Eclipse you should see a new project called "spaceship-warrior".

Eager to test your mettle against its creations, you browse its source through a file-tree and find com.gamadu.spaceshipwarrior.SpaceshipWarrior.java, which resembles the launch file you created for Drop.  You tell Eclipse to run it, but, alas.  The selection cannot be launched.  Confusion and rage twist your body as you let out a blood curling scream!  AAAAAAGGGGGGHGHGHGHGHHHHH!!!!!

Once you calm down, you realize the problem.  Somehow, Eclipse does not recognize this as a Java Project, and has no clue what to do with these files.  You quickly form a plan.

WARNING: My solution is probably a pretty stupid workaround.  There must be an easier way, and you may want to find a better solution.

Create a new Java Project, called spaceship-warrior2, then copy the "lib", "resources", and "textures-original" folders into your new project, leaving "src" alone for a moment.  Then, clicking into the "src" folder, you copy the "com" folder (which will come with all its subfolders) into the "src" folder of your new project.  It instantly converts the file folder structure into a package structure like we expected.  Unfortunately our new project is still riddled with errors.

Our problem is that we still haven't included the appropriate .jar files.  Look in the "libs" folder and you will find a collection of "kryonet" jars and "libgdx" jars.  I don't know what they "kryonet" jars do, but they aren't important to include.  Add the libgdx ones + sources.

The beast comes into focus, but there are still errors in the project.  What haven't you done?!?!?!  You recall that the creature also harnesses the power of Artemis, and though no Artemis jars were included in the download, you know exactly where to find them.  You see there is only one jar, and its associated source.  You quickly add them to the build path and watch all the errors fizzle away.  You are eager to begin.

Proceeding to launch com.gamadu.spaceshipwarrior.SpaceshipWarrior.java, the beast phases into existence, but before you can strike it, disappears again.  Righteous anger flows through your veins.  There were no errors!  What could have gone wrong now?!  You check the console, and find that textures\pack could not be found.  But you personally have no trouble finding it within the "resources" folder.

I'm not yet sure why this happens, but the problem is that the "resources" folder hasn't been added to the "classpath" yet.  Go back to the Build Path under "Libraries" (where you added all the .jars) and click "Add Class Folder".  Add the "resources" folder and run it again.

A large window comes up, filled with stars and a ship which you control.  You blast your enemies to smithereens.  Victory at last.

You gain 50 XP.  Progress to level 2: 50/400

You take a few moments to browse through the code.  Some things look similar, some look foreign.  One thing is for sure, Spaceship Warrior will not be such an easy foe as Drop was.


Setting Up Your Work Environment (Level 0)

Quest Log:

A shiver runs down your back as you stare at the Guild work board.  The one job too terrifying for anyone else to touch returns your stare, daring you to accept.  Setting Up Your Work Environment (Level 0).  A quick scan to the bottom reveals artifacts so legendary that their legends are known by... well... at least a lot of people.  Your final quest will be to tame the wild Drop Beast, but you must first procure and master these artifacts:
The shiver keeps shivering.

In general, for the future it's probably best to go with the most up-to-date versions of whatever you can find (except to stick with Java 1.6, instead of 1.7 which is still not as well supported by these libraries... I think).  If my examples don't work for you, my first guess is that incompatible versions are at fault.

You will be on your own to master these ancient tools, though a reliable source has indicated that libgdx installation instructions can be found deep within the Cavern of http://code.google.com/p/libgdx/wiki/ProjectSetup.

So that we are on the same page for the rest of the time, I have created these two projects in keeping with the libgdx instructions:
  1. GameXYZ - This is where we will actually put most of the code for the game
  2. GameXYZ-desktop - This is where we will store the launcher
I won't ever talk about the Android or HTML5 versions, but if you ever wanted to expand, this setup would make it easy to do so.

Once you have assembled your armaments, you must proceed to the lair of the Drop Beast.  It is up to you, and you alone, to conquer whatever perils lie within.  If you do, the reward will be great.  If you fail, then you should practice your Java skills more thoroughly before starting again.

Ding!  For your hard work and dedication in completing your first quest, you are awarded 200 XP, and have officially begun your life as an adventure programmer.  Welcome to Level 1!


As a Level 1 PC, you have gained modest skills in the arts of
  • Making a master project which is referenced by other (launcher) projects
  • Using SpriteBatch.draw() to render sprites to the screen
  • Using Gdx.input to query the keyboard and mouse for user input
Your next quest will bring you face to face with fear itself, as you battle ferocious monsters from the murky depths of... Spaceship Warrior!!!!!

Monday, January 28, 2013

Welcome

Welcome to JaveGameXYZ!  For a long time I have been eager to make a game of my own - the process has looked something like this:
  1. Dream up lots of awesome things I wish were in games, but aren't
  2. Start vigorously designing code and basic 32 x 32 sprites
  3. Try to do anything more complicated than moving a character around a tilemap, and get extremely bogged down in the code and my crappy framework
  4. Research online get bogged down in incomprehensible code and demos that I can't get to work
  5. Shelf the project
  6. Go to step 1
Rinse and repeat through C, C++, Java, C++, and now Java again.  My dream of designing the next groundbreaking Indie RPG now seems a little far-fetched.  BUT, I've realized that fighting through designing a game can be at LEAST as fun as playing somebody else's game, and so I imagine I'll have something someday.

Most recently I wanted to make a game for the Android, because I have a Droid and had gotten bored with all the free RPGs I could find.  Since it was Java I figured it wouldn't be tooooooooooo bad... derp derp derp.  Well thankfully I found the WONDERFUL library libgdx, which makes programming for Android as easy as 1, 2, 3.  ...4.  ...5 ...27...1,000,000,000, ...and on.  It still isn't easy, and I'm sure it never will be.

I also stumbled upon the Entity/Component system of doing things, and found another wonderful library, Artemis.  This blog is my attempt to piece together a bare-bones RPG with libgdx and Artemis.  I will start with the Artemis demo program, StarWarrior, but I don't really understand it all yet.  My hope is that by working through it, and writing about it, it will make more sense to me and and, just perhaps, be useful to the fine women and men of the future who manage to stumble upon this blog.

From there, I hope to be able to piece together the most generic tile based RPG ever, JaveGameXYZ!  God help us.