LWJGL でゲームを開発しようとしているときに、奇妙なちらつきの問題が発生しています。このチュートリアルで提案されているように、GL_TEXTURE_RECTANGLE_ARB を使用しようとしています: http://youtu.be/Xj_qZ3Cw08Q。
問題の短いビデオを次に示します: http://youtu.be/WUr_8aqoOrk。
カメラが (glTransformf() を介して) 移動すると、各タイルに描画されるスプライトが、ここで問題を引き起こしているのと同じように、隣接するテクスチャを少しだけ表示しているように思えます。ただし、MIN_FILTER と MAG_FILTER を既に GL_NEAREST に設定しているので、これは問題ではないようです。
余分な詳細を省いて問題を示すために、ゲームを可能な限り単純化しました。ここで .zip を取得できます: https://dl.dropbox.com/u/22396951/Copy%20of%20Fezless.zip。
編集:これは、一目でわかるコードです。
Fezless.java
package com.mrmikeanderson.fezless;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import de.matthiasmann.twl.utils.PNGDecoder;
import static org.lwjgl.opengl.ARBTextureRectangle.*;
import static org.lwjgl.opengl.GL11.*;
public class Fezless {
int w, h;
boolean running;
final int FPS = 60;
double t = 0.0;
final double dt = 1.0 / 60.0;
Sprite currentSprite;
Player player;
Map<String, Sprite> spriteMap;
int tiles;
float camX, camY, shakeX, shakeY;
String[] tileGrid = { "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "grass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "air", "air", "tardis", "air", "air", "air", "air", "air", "air", "air", "air", "air", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "air", "air", "air", "air", "dirt", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "air", "air", "air", "air", "deadplant", "dirt", "air", "air", "air", "air",
"air", "air", "plant", "air", "air", "air", "air", "air", "air", "air", "air", "stone", "dirt", "dirt", "stone", "air", "air", "air", "deadplant", "dirt", "dirt", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "air", "air", "air", "air", "air", "air", "air", "stone", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "grass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "grass", "grass", "grass", "air", "air", "air", "air", "air", "air", "deadplant", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone",
"stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "plant", "air", "air", "air", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "grass", "grass", "air", "air", "air", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "air", "air", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "air", "air", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt",
"dirt", "dirt", "air", "air", "air", "air", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "air", "air", "air", "air", "air", "air", "stone", "stone", "stone", "stone", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "stone", "stone", "stone", "dirt", "stone",
"stone", "stone", "stone", "stone", "stone", "stone", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone" };
public Fezless(int w, int h) {
this.w = w;
this.h = h;
}
public void start() {
init();
running = true;
run();
}
void init() {
initDisplay();
initGL();
initSprites();
player = new Player(13f, 4f, this);
// level = new Level(player, this);
camX = 14f;
camY = -6f;
shakeX = shakeY = 0f;
}
void initDisplay() {
try {
Display.setDisplayMode(new DisplayMode(w, h));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
}
}
void initGL() {
glMatrixMode(GL_PROJECTION);
double w = Display.getWidth() / 8;
double h = Display.getHeight() / 8;
glOrtho(-w / 2, w / 2, h / 2, -h / 2, -1f, 1f);
glClearColor(.4f, .769f, 1f, 1f);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_MODELVIEW);
}
void initSprites() {
spriteMap = new HashMap<String, Sprite>();
int texture = glGenTextures();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture);
InputStream in = null;
try {
in = new FileInputStream("res/tiles.png");
PNGDecoder decoder = new PNGDecoder(in);
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * decoder.getWidth() * decoder.getHeight());
decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
buffer.flip();
in.close();
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
tiles = loadSpritesheet();
spriteMap.put("air", new Sprite("air", 0, 0, 8, 8));
spriteMap.put("gonzo", new Sprite("gonzo", 0, 8, 8, 16));
spriteMap.put("grass", new Sprite("grass", 8, 0, 8, 8));
spriteMap.put("plant", new Sprite("plant", 16, 8, 8, 8));
spriteMap.put("deadplant", new Sprite("deadplant", 16, 16, 8, 8));
spriteMap.put("dirt", new Sprite("dirt", 8, 8, 8, 8));
spriteMap.put("stone", new Sprite("stone", 8, 16, 8, 8));
spriteMap.put("platform", new Sprite("platform", 24, 0, 8, 8));
spriteMap.put("obsidian", new Sprite("obsidian", 16, 0, 8, 8));
spriteMap.put("tardis", new Sprite("tardis", 32, 0, 16, 24));
}
void run() {
while (running && !Display.isCloseRequested()) {
shakeX = (float) Math.sin(t + 100);
shakeY = (float) Math.cos(t);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(-camX * 8 + shakeX, camY * 8 + shakeY, 0f);
for (int x = 0; x < 25; x++) {
for (int y = 0; y < 21; y++) {
drawSprite(tileGrid[x + y * 25], x, y);
}
}
player.render();
t += dt;
Display.update();
Display.sync(FPS);
Display.setVSyncEnabled(true);
}
cleanUp(false);
}
public void drawSprite(String name, float x, float y) {
x *= 8;
y *= 8;
currentSprite = spriteMap.get(name);
int u = currentSprite.x;
int v = currentSprite.y;
int u2 = currentSprite.x2;
int v2 = currentSprite.y2;
int w = currentSprite.w;
int h = currentSprite.h;
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tiles);
glBegin(GL_QUADS);
glTexCoord2f(u, v);
glVertex2f(x, y);
glTexCoord2f(u, v2);
glVertex2f(x, y + h);
glTexCoord2f(u2, v2);
glVertex2f(x + w, y + h);
glTexCoord2f(u2, v);
glVertex2f(x + w, y);
glEnd();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
}
int loadSpritesheet() {
int texture = glGenTextures();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture);
InputStream in = null;
try {
in = new FileInputStream("res/tiles.png");
PNGDecoder decoder = new PNGDecoder(in);
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * decoder.getWidth() * decoder.getHeight());
decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
buffer.flip();
in.close();
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
return texture;
}
void cleanUp(boolean error) {
Display.destroy();
System.exit(error ? 1 : 0);
}
public static void main(String[] args) {
Fezless f = new Fezless(800, 600);
f.start();
}
}
Player.java
package com.mrmikeanderson.fezless;
import java.awt.Rectangle;
public class Player {
Fezless f;
float x, y;
boolean falling = false;
String sprite;
Rectangle boundingBox;
public Player(float x, float y, Fezless f) {
this.x = x;
this.y = y;
this.f = f;
sprite = "gonzo";
}
public void render() {
f.drawSprite(sprite, x, y);
}
}
スプライト.java
package com.mrmikeanderson.fezless;
public class Sprite {
String name;
int x, y, x2, y2, w, h;
public Sprite(String name, int x, int y, int w, int h) {
this.name = name;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
x2 = x + w;
y2 = y + h;
}
}