slick
Slick2D is a simple set of tools wrapped around the LWJGL OpenGL binding for Java
Authored by:
aalmiray@yahoo.com
Provides integration with Slick2D, a simple set of tools wrapped around the LWJGL OpenGL binding for Java, used mainly for games.
Usage
This plugin enables two modes of operation: basic and state-based.
Basic Mode
In the first mode a single slick View provides the entry point for the application. It relies on a custom implementation of org.newdawn.slick.BasigGame to launch and execute the game. The behavior of every method can be overridden by setting a closure that matches a naming convention: on<MethodName>. These are all the possible closures that can be set on the game delegate:
onInit, onRender, onUpdate, acceptingInput, onCloseRequested, onControllerButtonPressed, onControllerButtonReleased, onControllerDownPressed, onControllerDownReleased, onControllerLeftPressed, onControllerLeftReleased, onControllerRightPressed, onControllerRightReleased, onControllerUpPressed, onControllerUpReleased, onInputEnded, onInputStarted, onKeyPressed, onKeyReleased, onMouseClicked, onMouseDragged, onMouseMoved, onMousePressed, onMouseReleased, onMouseWheelMoved, onSetInput.
Note acceptingInput is the only closure whose name does not follow the convention.
Events
The following events will be triggered by this basic mode
- SlickInit[app, gc] - triggered when the game inits itself
- SlickUpdate[app, gc, delta] - triggered when the game has been updated
- SlickRender[app, gc, g] - triggered when the game has completed a render pass
State Based Mode
In state-based mode the game is built around a set of states. Each state is defined in its own MVC group. A custom implementation of org.newdawn.slick.state.StateBasedGame is used to connect each state. The behavior of every method can be overridden by setting a closure that matches a naming convention: on<MethodName>. These are all the possible closures that can be set on the game delegate:
onInit, onRender, onUpdate, acceptingInput, onCloseRequested, onControllerButtonPressed, onControllerButtonReleased, onControllerDownPressed, onControllerDownReleased, onControllerLeftPressed, onControllerLeftReleased, onControllerRightPressed, onControllerRightReleased, onControllerUpPressed, onControllerUpReleased, onInputEnded, onInputStarted, onKeyPressed, onKeyReleased, onMouseClicked, onMouseDragged, onMouseMoved, onMousePressed, onMouseReleased, onMouseWheelMoved, onSetInput, onEnter, onLeave.
Note acceptingInput is the only closure whose name does not follow the convention.
Events
The following events will be triggered by this basic mode
- SlickInit[app, gc, game] - triggered when the game inits itself
- SlickUpdate[app, gc, game, delta] - triggered when the game has been updated
- SlickRender[app, gc, game, g] - triggered when the game has completed a render pass
- SlickInitStates[game] - triggered when states should be added to the game
- SlickPreRenderState[container, g] - triggered before rendering a state
- SlickPostRenderState[container, g] - triggered after a state has been rendered
- SlickPreUpdateState[container, delta] - triggered before updating a state
- SlickPostUpdateState[container, delta] - triggered after a state has been updated
Scripts
- create-game-state - creates a new GameState class in griffon-app/slick-states
- convert-view - transforms a View script into a View that can be used with BasicSlickGriffonApplication
Example
Here's how the Basic Slick sample from the Tutorials can be implemented with Griffon.
Create a new application
griffon create-app sample cd sampleInstall the slick plugin
griffon install-plugin slickConvert the generated View into a Slick View.
griffon convert-view sample.SampleView
It should now look like this
griffon-app/views/sample/SampleView.groovy
package sample
application(title: 'sample',
size: [800, 600],
icon: 'griffon-icon-48x48.png',
icons: ['griffon-icon-48x48.png',
'griffon-icon-32x32.png',
'griffon-icon-16x16.png']) {
app.game.onRender = { container, g ->
// render game contents
}
}
- Update the view code so that it renders a plane and the map. Where do the plane and map references come from? Don't worry, we'll setup those references in the next step.
griffon-app/views/sample/SampleView.groovy
package sample
application(title: 'sample',
size: [800, 600],
icon: 'griffon-icon-48x48.png',
icons: ['griffon-icon-48x48.png',
'griffon-icon-32x32.png',
'griffon-icon-16x16.png']) {
app.game.onRender = { container, g ->
model.with {
land.draw(0, 0)
plane.draw(x, y, scale)
}
}
}
- Add a plane and map properties to the Model.
griffon-app/models/sample/SampleModel.groovy
package sample
import org.newdawn.slick.Image
class SampleModel {
Image plane
Image land
float x = 400f
float y = 300f
float scale = 1f
void load() {
land = new Image('data/land.jpg')
plane = new Image('data/plane.png')
}
}
Pay close attention to the load() method. It must be called once the game has been initialized, which will do in the Controller.
Replace the Controller with a Java artifact. By default the Controller will be Groovy based. Why do we need to replace the Controller? Just to demonstrate how legacy code can be reused.
griffon replace-artifact --file-type=java --type=controller sample.SampleControllerEdit the Controller by filling the missing game logic
griffon-app/controllers/sample/SampleController.java
package sample;
import griffon.core.GriffonApplication;
import org.newdawn.slick.Input;
import org.newdawn.slick.Image;
import org.newdawn.slick.GameContainer;
import org.codehaus.griffon.runtime.core.AbstractGriffonController;
public class SampleController extends AbstractGriffonController {
private SampleModel model;
public void setModel(SampleModel model) { this.model = model; }
public void onSlickInit(GriffonApplication app, GameContainer container) {
model.load();
}
public void onSlickUpdate(GriffonApplication app, GameContainer container, int delta) {
Input input = container.getInput();
Image plane = model.getPlane();
if(input.isKeyDown(Input.KEY_A)) {
plane.rotate(-0.2f * delta);
}
if(input.isKeyDown(Input.KEY_D)) {
plane.rotate(0.2f * delta);
}
if(input.isKeyDown(Input.KEY_W)) {
float hip = 0.4f * delta;
float rotation = plane.getRotation();
double x = model.getX() + (hip * Math.sin(Math.toRadians(rotation)));
double y = model.getY() - (hip * Math.cos(Math.toRadians(rotation)));
model.setX((float) x);
model.setY((float) y);
}
float scale = model.getScale();
if(input.isKeyDown(Input.KEY_Z)) {
scale += (scale >= 5.0f) ? 0 : 0.1f;
plane.setCenterOfRotation(plane.getWidth()/2.0f*scale, plane.getHeight()/2.0f*scale);
}
if(input.isKeyDown(Input.KEY_O)) {
scale -= (scale <= 1.0f) ? 0 : 0.1f;
plane.setCenterOfRotation(plane.getWidth()/2.0f*scale, plane.getHeight()/2.0f*scale);
}
model.setScale(scale);
}
}
Notice we take advantage of the onSlickInit event to initialize the Model at the right time.
Locate and download the
plane.pngandland.jpgassets from Basic Slick sample. Place them undergriffon-app/resources/data.Run the application
griffon run-app
The following keys are enabled
- A - rotates the plane counter clock-wise.
- D - rotates the plane clock-wise.
- W - moves the plane forward.
- Z - increases the zoom level of the plane sprite.
- O - increases the zoom level of the plane sprite.
Be careful when moving the plane forward; this game is so simple it does not perform collision detection, chances are that when the plane leaves the viewport you won't see it again unless you manage to rotate it to the correct angle.
Griffon