Mini Quests

Very cool! Have you thought about posting your progress strings via HTTP? I think you’ll get a lot more data.

Thanks! I did toy with the idea of logging all the player’s key presses (together with the initial random seed) and sending that data back so I could watch a perfect replay of the game. But that sort of thing feels a bit too much like cyberstalking. I went with a string encoding some crude stats because it seems simple and transparent.(*)

But you’re right - I would definitely have got a lot more data. I’ve not exactly been overwhelmed by strings so far.

Simon

(*) And because I’ve not dabbled in that kind of web programming before.

I’ve done it a few times. It’s a bit sneaky, but it’s harmless so I doubt it matters. Users who might care should have a firewall! :stuck_out_tongue:

I just did this for collecting errors, so I have some code handy. Here’s some code for posting data:

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

public class Errors {
	static public void install () {
		Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
			public void uncaughtException (Thread thread, Throwable ex) {
				submit("Uncaught", ex);
			}
		});
	}

	static public void submit (String message) {
		submit(message, null);
	}

	static public void submit (String message, Throwable ex) {
		try {
			System.out.println("Error: " + message);
			if (ex != null) ex.printStackTrace();

			URL url = new URL("http://example.com/something");
			HttpURLConnection conn = (HttpURLConnection)url.openConnection();
			conn.setDoInput(true);
			conn.setDoOutput(true);
			conn.setUseCaches(false);
			conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			conn.setRequestMethod("POST");
			conn.connect();

			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()), 1024);
			writer.write("m=");
			writer.write(URLEncoder.encode(message, "UTF-8"));
			if (ex != null) {
				StringWriter buffer = new StringWriter(1024);
				ex.printStackTrace(new PrintWriter(buffer));
				String stacktrace = buffer.toString();

				writer.write("&e=");
				writer.write(URLEncoder.encode(stacktrace, "UTF-8"));
			}
			writer.close();

			BufferedInputStream input = new BufferedInputStream(conn.getInputStream(), 256);
			byte[] buffer = new byte[256];
			while (input.read(buffer) != -1)
				;
			input.close();

			conn.disconnect();
		} catch (Throwable ex2) {
			System.out.println("Unable to submit exception!");
			ex2.printStackTrace();
		}
	}
}

Now you just need somewhere to post it. If you already have PHP/mysql somewhere that is pretty easy. If you want to use Java you can use a servlet container like Tomcat. Here’s a servlet that stuffs the posted data into a mysql table:

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

public class ErrorServlet extends HttpServlet {
	protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Connection connection = getConnection();
		try {
			String message = request.getParameter("m");
			if (message == null) message = "";
			String exception = request.getParameter("e");
			if (exception == null) exception = "";
			PreparedStatement statement = connection.prepareStatement("INSERT INTO errors SET date=NOW(), message=?, exception=?;");
			statement.setString(1, message.trim());
			statement.setString(2, exception.trim());
			statement.executeUpdate();
			statement.close();
		} catch (Exception ex) {
			throw new RuntimeException("Error writing database entry.", ex);
		} finally {
			try {
				connection.close();
			} catch (SQLException ignored) {
			}
		}
	}

	private Connection getConnection () {
		try {
			Context envContext = (Context)new InitialContext().lookup("java:comp/env");
			DataSource dataSource = (DataSource)envContext.lookup("jdbc/arcanetactics");
			return dataSource.getConnection();
		} catch (Exception ex) {
			throw new RuntimeException("Error opening database connection.", ex);
		}
	}
}

You’ll need servlet-api.jar so you can compile that. Then you’ll Tomcat and mysql, then setup your servlet like this:
http://www.mulesoft.com/tomcat-mysql
Basically you will have:
META-INF/context.xml (describes db connection)
WEB-INF/web.xml (describes servlet and references db connection)
WEB-INF/classes/com/example/YourServlet.class
Throw that in a subdirectory of your tomcat webapps dir, start tomcat, and you’re good. Indeed, it is a bit of a PITA to setup. :slight_smile:

That article describes the db setup in your web.xml, but leaves out how to define the servlet mapping. Your web.xml should look like this:


<web-app>
	<servlet>
		<servlet-name>somename</servlet-name>
		<servlet-class>com.example.YourServletClass</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>somename</servlet-name>
		<url-pattern>/somepath</url-pattern>
	</servlet-mapping>

	<resource-ref>
		<res-ref-name>jdbc/YourResourceNameFromContextXml</res-ref-name>
		<res-type>javax.sql.DataSource</res-type>
		<res-auth>Container</res-auth>
	</resource-ref>
</web-app>

Then you would hit your servlet using… http://yourdomain:8080/somepath
You could use something like dyndns.com to have your stuff posted to your home machine (just forward the port on your router).
Note the servlet code I pasted above uses HTTP POST and won’t respond to GET. You can change doPost to doGet for easier hitting of your serlvet.

Thanks! One of these days I’ll accept that the internet’s here to stay, and I’ll give all that a try. I’ve mentally bookmarked the post.
Simon

This game is really nice!

My opinion might be tainted by my new found excitement with android. Have you considered porting it?
It seems like a simple enough game to release on android, and would be really awesome to see on a portable device.

;D
A couple of chapters more of BadLogic’s book and I’ll make a start.
I don’t know how well it will work with on-screen buttons but it’ll be fun to try.

It seems all that is needed is arrows and a select button.

So I think you could use the dpad without problems, and remove dependency on the touch screen by default. As for touch screen devices have no extra buttons on screen, if people touch around in the corner areas, the character moves in that direction, or touches the center he shoots/selects.

Most of the work would just be to setup a view to display a bitmap with the current pixels values.

I want to see your game on Android! You will have a much larger audience I think. Your game is really cool, it deserves it! Here’s the libgdx code to draw a bitmap full screen:

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Blending;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;

public class MiniQuests extends ApplicationAdapter {
	private SpriteBatch batch;
	private Pixmap pixmap;
	private Texture texture;
	private Color color = new Color(1, 1, 1, 1);

	public void create () {
		Pixmap.setBlending(Blending.None);

		batch = new SpriteBatch();
		pixmap = new Pixmap(40, 30, Format.RGB565);
		texture = new Texture(MathUtils.nextPowerOfTwo(pixmap.getWidth()), MathUtils.nextPowerOfTwo(pixmap.getHeight()),
			Format.RGB565);

		Gdx.input.setInputProcessor(new InputAdapter() {
			public boolean touchDown (int x, int y, int pointer, int button) {
				// Do something!
				return true;
			}
		});
	}

	public void render () {
		for (int x = 0; x < pixmap.getWidth(); x++) {
			for (int y = 0; y < pixmap.getHeight(); y++) {
				color.r = MathUtils.random(0, 1);
				color.g = MathUtils.random(0, 1);
				color.b = MathUtils.random(0, 1);
				pixmap.setColor(color);
				pixmap.drawPixel(x, y);
			}
		}
		texture.draw(pixmap, 0, 0);

		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

		batch.disableBlending();
		batch.begin();
		batch.draw(texture, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), 0, 0, pixmap.getWidth(), pixmap.getHeight(),
			false, false);
		batch.end();
	}

	public static void main (String[] args) throws Exception {
		new LwjglApplication(new MiniQuests(), "", 800, 480, false);
	}
}

To run on Android, remove the main method and use:


public class MiniQuestsAndroid extends AndroidApplication {
	public void onCreate (Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		initialize(new MiniQuests(), false);
	}
}

To setup your dev environment for libgdx:
http://code.google.com/p/libgdx/wiki/ProjectSetup

Note running the above code shows random pixels at 60fps and may give you a seizure, so don’t do more than glance at it (seriously). It is usually not advised to upload to a texture every frame, as this is slow, but since your texture is tiny and that is pretty much all your game is doing, you’ll be fine. You only need to draw the pixmap to the texture after you change the pixmap. I stuck in the bit of code you need to handle input. Just stick in your game and go! :smiley:

Thanks! I’m hoping I’ll have the chance to install assorted dev kits later today. “Hello World” here we come!

Something I’ve been meaning to ask: Is there a good argument either way for using libgdx rather than dealing with Android directly for this project? As you say, there’s not much more to it than drawing a bitmap and reading inputs. I’ll almost certainly use libgdx for any future Android stuff, but I’m wondering whether it’s overkill in this case.

Any opinions?

(Actually, whichever approach let’s me lock to 30 frames-per-sec most easily probably wins. :wink: )

Simon

I’ve been scratching my head over this. Apart from the PlayStation phone, I didn’t know that any had a dpad. And I’ve only seen a few that have hardware keyboards. There’s not some kind of onscreen dpad support built into the OS is there?!
???
How embarrassing. Android has turned me back into a newbie.
Simon

My opinion in regards to android Dev:

Because Im used to using LWJGL, I program in android directly. I find that the OpenGL handling is relatively similar. Also because android handles ogg natively, It means that you dont really need to learn any 3rd party libraries.

Its easy enough to handle touchscreen events (Activity class), as its not that different to mouse events in java (Applet class).

One other thing that may surprise a new developer is how android resets that application on orientation change, you just need to setup a few lines of code in the xml file (there is heeps of examples on the net) to make your application work like a solid game without any surprising behaviour.

Impressive game! I love it!

Some feedback in return. I found damn complicated (almost rage-infuriating-quitting complicated) fourth screen in second dungeon. It took me a while to figure out what to do.

BTW I finished second dungeon. I must be really bad at it because it gave me only one star as a hero :’-(

— LOG —

Completion time: 2139.1 seconds
Number of deaths: 33
“DUm2Jss9RiyGZM5Nb39SjwGZnULew0SlzWXq5eexBpj2Hsp7NzvCTl1JZ97Qj6EXlIJcsZRjxoVq40bxBJh0FtnAL9tHRozOXx5Td2BbjYHfpBNoxzTr1PZy7Pf3DclBJbrFPhxNVn3Rby9Yh1FdnALlvyRpzKXw5Od1BajAHbpDNfvMTn1PZv7Uf7Dbl3JgrAPnxGVs3Lbz9Th6FcnBLgtJRlzNXs5WdyBZjJHepFNlvITu2nZx7Rf6Djl9JfrHPkxLVq3Qbx9Xh1FenKLjtERoziXq6idzBTj2HlpCORvJT11RZb7VfxDdl4JhrGPqxGVt3Lb29Uh5FdnCLdtHRlzMXq5TdyBaj1HfpCNnvJTr1NZ07df3DblAJcrFPkxMVn3Rbx9ZhlFdn9LmtHRpzKXu5Pd5BXjCOIs”

— /LOG —

And also the first dungeon. This time 3/5 hero. Not so bad :slight_smile:

— LOG —

Completion time: 639.6 seconds
Number of deaths: 14
“p6NevITl1RZs7nfzEwl6JbrBPhxIV43Mbt9UhzFYnSLhtTRoznXv5fdwBnj8JEpFPMvMV01Re98ZfjFdnFKmrBP”

— /LOG —

Fantastic! It’s really interesting for me looking at the logs and seeing which rooms you spent your time in. (You clearly had a lot of fun with the sliding pathways near the end of the second dungeon! ;D ) Sorry about the fourth room (the one with the statues). The puzzles have to be challenging, but I hope you didn’t find them unfair.

The way I see it, anyone who completes a dungeon is a hero. 8) The stars ratings are to give people an excuse for playing the dungeons over again if they want to. I would’ve said that it would be pretty difficult for someone to get more than two stars on a dungeon on the first time through, so maybe I need to adjust the scoring on the first dungeon. (Don’t worry, I’ll let you keep your three stars. You earned them. :wink: )

Simon

Sure, you could do it without libgdx and without OpenGL. libgdx also runs on the desktop though, and that alone is enough for me. Deploying to the phone with no hotswap is a huge time sink.

Hey, I was inspired by you isometric view to make my Ludum Dare 22 entry (spam link).

Many people complained about the difficulty of the view point and controls. I guess I still have much (and more) to learn!

cargar: clase knight.MainApplet no encontrada.
java.lang.ClassNotFoundException: knight.MainApplet
	at sun.plugin2.applet.Applet2ClassLoader.findClass(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadClass0(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadCode(Unknown Source)
	at sun.plugin2.applet.Plugin2Manager.createApplet(Unknown Source)
	at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Excepción: java.lang.ClassNotFoundException: knight.MainApplet

So bad, I played this game back in the compo (I tried to pitch in but started too late) and it was fun as hell. I hope you fix it, I want to try the update :slight_smile:

Thanks for trying it. I’m not sure what to do about the exception - the applet is failing before it executes any of my code.

A friend reported the applet not working on Ubuntu with the Sun JRE, but that it was okay with OpenJDK. Could that be the problem in your case? What OS/browser are you using? Can you run other applets okay (and if so, what ones? I’d like to compare my HTML with theirs)?

I’ll upload an executable jar for the game at some stage so that I’m not entirely dependent on applets. (I’m busy at the moment reorganising the code to support Android as well as PC.)

Cheers,
Simon

For anyone feeling brave or reckless, here’s my first attempt at an Android version.

It uses libgdx. I can guarantee that it won’t run on devices prior to Android 2.1 (since that’s what I put in the manifest), but that’s pretty much all I can guarantee. I don’t imagine it will be playable on large devices (tablets) but feel free to try if you like!

It definitely needs work on the user interface and the application lifecycle (in particular it doesn’t save progress yet, which is annoying if you press ‘back’ accidentally), and the frame rate seems a bit cranky on my phone, but it basically seems to be working.

As ever, feedback gratefully received.

Simon

P.S. Trying out the forum’s QR tag…
[qr]http://market.android.com/details?id=com.dishmoth.miniquests[/qr]

Edit (14 Feb): Links now point to Android Market.

I’d be willing to test the Android version, but can’t download the APK file. Is the link broken or is my cell phone?

I’m thinking on moving from LWJGL to libGDX to try making games for Android, is it complicated to port them?