Hello fellas,
I am currently working on a 2D real-time strategy game with Java. The purpose is to teach computer science students how to implement standard algorithms and datastructures and how they are used/integrated within a big project. So the game I am creating is actually incomplete, but provides interfaces for the students to implement their sorting-/graph-/whatever- algorithms.
The things I present are just little solutions or interesting snippets that might be useful for you too, if you want to do something with Slick.
It won't be a beginner tutorial, because there are enough of them out there (i.e.
slickwiki)
Cloudy Sky Menu BackgroundHere is a screenshot of my current main menu (graphics are still in progress):
The clouds in the background are generated randomly and float in random speeds from left to right.
How can you realize this?
First I created a private class that holds some data for my clouds. Since they are used like a struct, I make the atributes public:
private class Cloud {
public float x; //x position of the cloud
public float y; //y position
public Image img; //image used to display the cloud
public float speedDif; //the diff from default speed
}
Then I create a certain number of random clouds:
private final List<Cloud> clouds = new LinkedList<Cloud>();
private final Random gen; //initialized in constructor of the class
private Cloud randomCloud() {
Cloud cloud = new Cloud();
cloud.x = -500 + gen.nextInt(GameStateContainer.app.getWidth() + 500);
cloud.y = gen.nextInt(GameStateContainer.app.getHeight());
cloud.speedDif = gen.nextFloat() / 200;
if (gen.nextBoolean()) { //if true cloud will be slowlier than usual
cloud.speedDif = -cloud.speedDif;
}
try {
cloud.img = getRandomCloudImage();
} catch (SlickException e) {
e.printStackTrace();
}
return cloud;
}
I had to experiment a bit with the values.
The x value is the one that shall change, since the clouds are floating from left to right. So I position some of the clouds outside of the screen and some right within for the start -- that's why I used the 500 there.
The y position is right within the screen of course. It won't change at all.
You can only find a good speedDif with experimenting. The gen.nextBoolean() will tell wether the cloud is slowlier or faster than usual.
I made three different cloud images with Gimp and load one of them randomly in here:
private Image getRandomCloudImage() throws SlickException {
return SpriteManager.getInstance().getCloudImage(
gen.nextInt(SpriteManager.MAX_CLOUD_NR));
}
Loading itself is done by the SpriteManager, which manages the instances of all images used in the game. It makes sure that only one instance is created for the same image (for performance reasons).
The SpriteManager loads the clouds here and saves them in an array:
private void initImages() throws SlickException {
clouds = new Image[MAX_CLOUD_NR];
for (int i = 0; i < MAX_CLOUD_NR; i++)
clouds[i] = new Image("sprites/cloud" + (i + 1) + ".png");
}
And getCloudImage just returns one of them:
public Image getCloudImage(int i) {
return clouds[i];
}
Now we have clouds, but still need to update them in every update cycle:
public void update(GameContainer gc, StateBasedGame sbg, int delta)
throws SlickException {
double cloudStep = 0.01;
for (Cloud cloud : clouds) {
cloud.x += (cloudStep + cloud.speedDif) * delta;
if (cloud.x > GameStateContainer.app.getWidth()) { //cloud is out of screen
cloud.x = -200 - gen.nextInt(500); //reset cloud position
cloud.img = getRandomCloudImage();
}
}
}
cloudStep is the default cloud speed and is changed by it's speedDif.
delta is a value that is given from Slick and is the number of milliseconds between frames. Whenever you update, you want to adjust it by the time that actually passed since the last update. So you use delta to get a smooth movement for your clouds.
Once a cloud has floated out of the screen on the right side, I reset the position to somewhere out of the screen on the left side.
cloud.x = -200 - gen.nextInt(500); //reset cloud position
200 should be the max-width of your cloudimage, so the cloud doesn't just pop up in your screen but floats into it. (I actually should have used a constant instead of that magic number - sorry for bad code here)
The cloud image is also randomly set again.
The very last part is the rendering itself:
public void render(Graphics g) {
background.draw(0, 0);
for (Cloud cloud : clouds) {
g.drawImage(cloud.img, cloud.x, cloud.y);
}
}
background is an image with a gradient effect from darkblue to lightblue (also easily done with Gimp). It looks like this:
That is all you need to know I guess. Here is the whole code for the animated cloudy background:
package menu;
import gamestates.GameStateContainer;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.StateBasedGame;
import resourcemanager.SpriteManager;
/**
* A renderable Background that shows randomly floating clouds.
*
* @author Deque
*
*/
public class CloudySkyBackground {
private static final String CLOUD_IMAGE_PATH = "sprites/background.png";
private static final int CLOUD_NUMBER = 15;
private final Image background;
private final List<Cloud> clouds = new LinkedList<Cloud>();
private final Random gen;
public CloudySkyBackground() throws SlickException {
this.gen = new Random();
this.background = new Image(CLOUD_IMAGE_PATH);
initCloudPositions();
}
private void initCloudPositions() {
for (int i = 0; i < CLOUD_NUMBER; i++) {
clouds.add(randomCloud());
}
}
private Cloud randomCloud() {
Cloud cloud = new Cloud();
cloud.x = -500 + gen.nextInt(GameStateContainer.app.getWidth() + 500);
cloud.y = gen.nextInt(GameStateContainer.app.getHeight());
cloud.speedDif = gen.nextFloat() / 200;
if (gen.nextBoolean()) {
cloud.speedDif = -cloud.speedDif;
}
try {
cloud.img = getRandomCloudImage();
} catch (SlickException e) {
e.printStackTrace();
}
return cloud;
}
public void render(Graphics g) {
background.draw(0, 0);
for (Cloud cloud : clouds) {
g.drawImage(cloud.img, cloud.x, cloud.y);
}
}
public void update(GameContainer gc, StateBasedGame sbg, int delta)
throws SlickException {
double cloudStep = 0.01;
for (Cloud cloud : clouds) {
cloud.x += (cloudStep + cloud.speedDif) * delta;
if (cloud.x > GameStateContainer.app.getWidth()) {
cloud.x = -200 - gen.nextInt(500);
cloud.img = getRandomCloudImage();
}
}
}
private Image getRandomCloudImage() throws SlickException {
return SpriteManager.getInstance().getCloudImage(
gen.nextInt(SpriteManager.MAX_CLOUD_NR));
}
private class Cloud {
public float x;
public float y;
public Image img;
public float speedDif;
}
}