So i'm trying to make a top down 2D game using Slick2d.
I have a world object that sores a List of chunks which contain a list of tiles. Those tiles are what is rendered. I have the tile position stored in a Vector2f. Every chunk also has a Vector2f position. Here's my current working code:
main:
public class Main extends BasicGame {
public static Main instance;
private Renderer renderer;
private long lastFrameTime;
private float delta;
public Main() {
super("Test");
}
public static void main(String[] arguments) {
try {
instance = new Main();
AppGameContainer app = new AppGameContainer(instance);
app.setDisplayMode(1240, 720, false);
app.setShowFPS(true);
app.start();
app.setAlwaysRender(true);
} catch (SlickException e) {
e.printStackTrace();
}
}
@Override
public void init(GameContainer container) throws SlickException {
renderer = new Renderer(new World(), new Player());
for(Chunk chunk : renderer.getWorld().getChunks()) {
System.out.println(chunk.getPosition().x + "," +
chunk.getPosition().y + "," + chunk.getTiles().size());
}
}
@Override
public void update(GameContainer container, int delta)
throws SlickException {
long currentFrameTime = getCurrentTime();
this.delta = (currentFrameTime - lastFrameTime)/1000f;
lastFrameTime = currentFrameTime;
renderer.move();
}
public void render(GameContainer container, Graphics g)
throws SlickException {
renderer.update(g);
}
public float getFrameTimeSeconds() {
return delta;
}
private static long getCurrentTime() {
return Sys.getTime()*1000/Sys.getTimerResolution();
}
public Renderer getRenderer() {
return renderer;
}
}
Renderer class:
public class Renderer {
World world;
Player player;
public Renderer(World world, Player player) {
this.world = world;
this.player = player;
}
public World getWorld() {
return world;
}
public void setWorld(World world) {
this.world = world;
}
public Player getPlayer() {
return player;
}
public void setPlayer(Player player) {
this.player = player;
}
public void update(Graphics g) {
for (int i = 0; i < 2; i++) {
for (int i2 = 0; i2 < 2; i2++) {
Vector2f pos = player.getPos();
Chunk chunk = world.getChunk(new Vector2f((int) Math
.floor(pos.x / 16) + i, (int) Math.floor(pos.y / 16)
+ i2));
if (chunk != null) {
for (Tile tile : chunk.getTiles()) {
if (tile.getPos().x < player.getPos().x +
((Display.getWidth() / 2) + 64 + 50) &&
tile.getPos().x > player.getPos().x -
((Display.getWidth() / 2) + 64 + 50) &&
tile.getPos().y < player.getPos().y +
((Display.getHeight() / 2) + 64 + 50) &&
tile.getPos().y > player.getPos().y -
((Display.getHeight() / 2) + 64 + 50))
g.drawImage(tile.getTexture().getScaledCopy(64, 64),
(pos.x - tile.getPos().x)*64+Display.getWidth(),
(pos.y -
tile.getPos().y)*64+Display.getHeight());
}
}
}
}
}
public void move() {
Vector2f pos = player.getPos();
if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
pos.y += 1 * Main.instance.getFrameTimeSeconds();
} else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
pos.y -= 1 * Main.instance.getFrameTimeSeconds();
}
if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
pos.x += 1 * Main.instance.getFrameTimeSeconds();
} else if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
pos.x -= 1 * Main.instance.getFrameTimeSeconds();
}
int width = Display.getWidth();
int height = Display.getHeight();
Vector2f npos2;
Vector2f constant = new Vector2f((int) Math.floor((width / 2 -
pos.x)/256), (int) Math.floor((height / 2 - pos.y)/256));
Vector2f npos = constant;
if (world.getChunk(npos) == null) {
npos2 = npos;
npos2.x += 1;
//Right
if (world.getChunk(npos2) == null) {
for (int i = 0; i < 2; i++) {
System.out.println("Generating new chunk! (1) " + npos2.x +
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " +
player.getPos().y);
npos.x += i;
world.generateChunk(npos);
}
}
npos2.x -= 1;
npos2.y -= 1;
//Down
if (world.getChunk(npos2) == null) {
for (int i = 0; i < 2; i++) {
System.out.println("Generating new chunk! (2) " + npos2.x +
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " +
player.getPos().y);
npos.y -= i;
world.generateChunk(npos);
}
}
}
npos = constant;
if (world.getChunk(npos) == null) {
npos2 = npos;
npos2.x -= 1;
//Left
if (world.getChunk(npos2) == null) {
for (int i = 0; i < 2; i++) {
System.out.println("Generating new chunk! (3) " + npos2.x +
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " +
player.getPos().y);
npos.x -= i;
world.generateChunk(npos);
}
}
npos2.x += 1;
npos2.y += 1;
//Up
if (world.getChunk(npos2) == null) {
for (int i = 0; i < 2; i++) {
System.out.println("Generating new chunk! (4) " + npos2.x +
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " +
player.getPos().y);
npos.y += i;
world.generateChunk(npos);
}
}
}
}
}
That code works decently, but the problem is the frame rate is 4-5 fps. I had an idea to try and stitch the textures together and render them like that, here's my new renderer code:
public class Renderer {
World world;
Player player;
public Renderer(World world, Player player) {
this.world = world;
this.player = player;
}
public World getWorld() {
return world;
}
public void setWorld(World world) {
this.world = world;
}
public Player getPlayer() {
return player;
}
public void setPlayer(Player player) {
this.player = player;
}
public void update(Graphics g) {
for (int i = 0; i < 2; i++) {
for (int i2 = 0; i2 < 2; i2++) {
Vector2f pos = player.getPos();
Chunk chunk = world.getChunk(new Vector2f((int) Math
.floor(pos.x / 16) + i, (int) Math.floor(pos.y / 16)
+ i2));
if (chunk != null) {
Image image = null;
try {
image = new Image(4096, 4096);
} catch (SlickException e1) {
e1.printStackTrace();
}
Graphics ig = null;
try {
ig = image.getGraphics();
} catch (SlickException e) {
e.printStackTrace();
}
for (Tile tile : chunk.getTiles()) {
if (tile.getPos().x < player.getPos().x +
((Display.getWidth() / 2) + 64 + 50) &&
tile.getPos().x > player.getPos().x -
((Display.getWidth() / 2) + 64 + 50) &&
tile.getPos().y < player.getPos().y +
((Display.getHeight() / 2) + 64 + 50) &&
tile.getPos().y > player.getPos().y -
((Display.getHeight() / 2) + 64 + 50))
ig.drawImage(tile.getTexture().getScaledCopy(64, 64),
(pos.x - tile.getPos().x)*64+Display.getWidth(),
(pos.y -
tile.getPos().y)*64+Display.getHeight());
}
ig.flush();
g.drawImage(image, chunk.getPosition().x*256 - pos.x,
chunk.getPosition().y*256-pos.y);
}
}
}
}
public void move() {
Vector2f pos = player.getPos();
if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
pos.y += 1 * Main.instance.getFrameTimeSeconds();
} else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
pos.y -= 1 * Main.instance.getFrameTimeSeconds();
}
if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
pos.x += 1 * Main.instance.getFrameTimeSeconds();
} else if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
pos.x -= 1 * Main.instance.getFrameTimeSeconds();
}
int width = Display.getWidth();
int height = Display.getHeight();
Vector2f npos2;
Vector2f constant = new Vector2f((int) Math.floor((width / 2 -
pos.x)/256), (int) Math.floor((height / 2 - pos.y)/256));
Vector2f npos = constant;
if (world.getChunk(npos) == null) {
npos2 = npos;
npos2.x += 1;
//Right
if (world.getChunk(npos2) == null) {
for (int i = 0; i < 2; i++) {
System.out.println("Generating new chunk! (1) " + npos2.x +
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " +
player.getPos().y);
npos.x += i;
world.generateChunk(npos);
}
}
npos2.x -= 1;
npos2.y -= 1;
//Down
if (world.getChunk(npos2) == null) {
for (int i = 0; i < 2; i++) {
System.out.println("Generating new chunk! (2) " + npos2.x +
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " +
player.getPos().y);
npos.y -= i;
world.generateChunk(npos);
}
}
}
npos = constant;
if (world.getChunk(npos) == null) {
npos2 = npos;
npos2.x -= 1;
//Left
if (world.getChunk(npos2) == null) {
for (int i = 0; i < 2; i++) {
System.out.println("Generating new chunk! (3) " + npos2.x +
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " +
player.getPos().y);
npos.x -= i;
world.generateChunk(npos);
}
}
npos2.x += 1;
npos2.y += 1;
//Up
if (world.getChunk(npos2) == null) {
for (int i = 0; i < 2; i++) {
System.out.println("Generating new chunk! (4) " + npos2.x +
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " +
player.getPos().y);
npos.y += i;
world.generateChunk(npos);
}
}
}
}
}
The problem is that not only does the program crash whenever I move around the tiles too much, but I only get 1 fps which isn't better at all. So my question is, whats the best way to do this with a decent frame rate?
Error when the program crashes with the second method:
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000006996dfce,
pid=10024, tid=0x0000000000001ec8
#
# JRE version: Java(TM) SE Runtime Environment (8.0_161-b12) (build
1.8.0_161-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode windows-
amd64 compressed oops)
# Problematic frame:
# C [atio6axx.dll+0x102dfce]
Update: I have had no luck, but looking at other 2D games which render hundreds of images without dropping many frames, I'd think that there is some way to speed up my code considerably. I don't render off screen tiles, and only attempt to render as much as I need.
Update #2: I switched to libgdx, now it runs like a charm. The batch rendering included in libgdx is better for my purposes.
User contributions licensed under CC BY-SA 3.0