I have a chunk of cubes that is generated using a display list, and ever time I come near it, the frame rate drops significantly. Why is this? Here's some code:
public class Chunk implements GameObject {
private int sx, sy, sz, lx, ly, lz, vertID;
private Tile[][][] tiles;
public Chunk(int sx, int sy, int sz) {
this.sx = sx;
this.sy = sy;
this.sz = sz;
this.lx = sx + 16;
this.ly = sy + 16;
this.lz = sz + 16;
init();
}
#Override
public void init() {
this.tiles = new Tile[lx][ly][lz];
vertID = glGenLists(1);
glNewList(vertID, GL_COMPILE);
for (int x = sx; x < lx; x++) {
for (int y = sy; y < ly; y++) {
for (int z = sz; z < lz; z++) {
tiles[x][y][z] = new Tile("grass.jpg");
}
}
}
glEndList();
}
public void rebuild() {
glNewList(vertID, GL_COMPILE);
for (int x = sx; x < lx; x++) {
for (int y = sy; y < ly; y++) {
for (int z = sz; z < lz; z++) {
tiles[x][y][z].getVertices(x, y, z, 16);
}
}
}
glEndList();
}
#Override
public void update() {
}
#Override
public void render() {
glCallList(vertID);
}
#Override
public void dispose() {
}
}
Your problem lies probably herein:
glNewList(vertID, GL_COMPILE);
for (int x = sx; x < lx; x++) {
for (int y = sy; y < ly; y++) {
for (int z = sz; z < lz; z++) {
tiles[x][y][z] = new Tile("grass.jpg");
}
}
}
glEndList();
If I had to make an educated guess your Tile class generate and initialiizes a texture. Well, you see, display lists don't just take on geometry and drawing calls. They also encapsulate the creation and setting of texture data. Before OpenGL-1.1 (i.e. OpenGL-1.0) there were no texture objects (glGenTextures, glBindTexture) and you used display lists as a drop in replacement for that. And that little anachronism is still present today in all compatibility OpenGL profiles. That's also why Windows calls the function to share OpenGL context data wglShareLists, although it goes far beyond lists.
To make a long story short, you probably perform a quite expensive operation right in the middle of a display list. No wonder it's slow. But seriously, why are you allocating a new texture for each tile? You should load your images into your textures only once and then only make references to them.
Related
I am trying to make Space Invaders in Processing. I am currently working on getting the enemy to move correctly. I have got them to be drawn in the right spot but I haven't gotten them to be moved correctly. Here is my code:
PImage mainPlayer;
PImage enemyPlayer;
float Xspeed = 60;
float Yspeed = 60;
float X;
float Y;
Enemy EnemyPlayer = new Enemy("EnemyPlayerSpaceInvaders.png", 10, 10, 6);
void setup() {
size(1400, 800);
//enemyPlayer = loadImage("EnemyPlayerSpaceInvaders.png");
mainPlayer = loadImage("MainPlayerSpaceInvaders.png");
}
void draw() {
background(0);
Enemy[] enemyPlayer = new Enemy[60];
for (int i = 0; i < 5; i += 1) {
for (int j = 0; j < 11; j += 1) {
enemyPlayer[j *i] = new Enemy("EnemyPlayerSpaceInvaders.png", 50 + j * 100, 5 + 75 * i, 6);
}
}
for (int i = 0; i < 5; i += 1) {
for (int j = 0; j < 11; j += 1) {
if(enemyPlayer[j * i].alive){
enemyPlayer[j * i].Draw();
}
enemyPlayer[j *i].moveAndDraw(6);
}
}
}
class Enemy {
boolean alive = true;
float x;
float y;
float speed;
String playerTexFile;
PImage playerTex;
Enemy(String PlayerTexFile, float X, float Y, float Speed){
x = X;
y = Y;
speed = Speed;
playerTexFile = PlayerTexFile;
}
void Draw(){
playerTex = loadImage(playerTexFile);
image(playerTex, x, y);
}
void moveAndDraw(float Speed){
playerTex = loadImage(playerTexFile);
if(alive){
x += Speed;
if (x >= 1300) {
x = 100;
y += 50;
}
}
}
}
Here is my result:
The Draw function works but what you're seeing that is messing it up is the moveAndDraw() function.
And the enemy drawings aren't moving. I have made this before with c++ SFML but in that there is a very basic getPosition function. The reason I want to get position is that right now I'm having to use inaccurate numbers as the X and Y position and for the enemy to move correctly I need to know exactly what it's position is. I have checked multiple pages on processing.org but none of them helped. I haven't found any getPosition void and all the ones I've seen other people using a void to do this I just haven't been able to get it to work. If there is some code that could get me this to work or just some function I've looked over and even a website page I could look at I'd be open to it. Please tell me anything I can do to get this working.
The issue is that you recreate the enemies in every frame at it's initial position:
void draw() {
background(0);
Enemy[] enemyPlayer = new Enemy[60];
for (int i = 0; i < 5; i += 1) {
for (int j = 0; j < 11; j += 1) {
enemyPlayer[j *i] = new Enemy("EnemyPlayerSpaceInvaders.png", 50 + j * 100, 5 + 75 * i, 6);
}
}
// [...]
}
You've to:
Create a global array of enemies Enemy[] enemyPlayer (and delete PImage enemyPlayer).
Create and initialize the enemies in setup.
Use and move the existing enemies in draw:
Further note, that your loops doesn't do what you expect it to do. Create the enemies in 2 nested loops. If i runs from o to 6 and j from 0 to 10, the the index of an enemy is i*10 + j.
The enemies can be moved in a single loop from 0 to enemyPlayer.length.
//PImage enemyPlayer; <--- DELETE
// global array of enemies
Enemy[] enemyPlayer = new Enemy[60];
// [...]
void setup() {
size(1400, 800);
mainPlayer = loadImage("MainPlayerSpaceInvaders.png");
// create enemies
for (int i = 0; i < 6; i += 1) {
for (int j = 0; j < 10; j += 1) {
enemyPlayer[i*10 + j] = new Enemy("rocket64.png", 50 + j * 100, 5 + 75 * i, 6);
}
}
}
void draw() {
background(0);
// move enemies
for(int i = 0; i < enemyPlayer.length; ++i ) {
if(enemyPlayer[i].alive){
enemyPlayer[i].Draw();
}
enemyPlayer[i].moveAndDraw(6);
}
}
I'm writing the drawing system for a roguelike game based on ascii characters (graphics similar to dwarf fortress). I'm using the AsciiPanel from here. My problem is that when I draw entities on my map, they seem to blink, when they should be solid.
In this gif, the r characters in the top row are the entities.
This is the map's draw method that is called every frame.
public void draw(final Display display) {
for (int x = getViewportX(); x < getViewportX() + viewportWidthInTiles; x++) {
for (int y = viewportY; y < viewportY + viewportHeightInTiles; y++) {
final char character = background[x][y].getCharacter();
final Color foreground = background[x][y].getForeground();
final Color backgroundColor = background[x][y].getBackground();
final AsciiCharacterData data = new AsciiCharacterData(
character, foreground, backgroundColor);
display.setCharacterAt(x - getViewportX(), y - viewportY,
background[x][y].getDrawingLayer(), data);
}
}
display.clearLayer(DrawingLayer.PRIMARY);
for (int i = 0; i < entities.size(); i++) {
final Entity e = entities.get(i);
final char character = e.getCharacter();
final Color foreground = e.getForeground();
final Color backgroundColor = e.getBackground();
final AsciiCharacterData data = new AsciiCharacterData(character,
foreground, backgroundColor);
display.setCharacterAt(e.getX() - getViewportX(), e.getY()
- viewportY, e.getDrawingLayer(), data);
}
}
I think I know what causes the problem, because if I write display.clearLayer(DrawingLayer.BACKGROUND); (the layer the tiles are drawn to) before I draw the background tiles, it creates something even more ridiculous.
This is the Display class, where I think I am making some mistake.
public class Display {
private static final char TRANSPARENT_CHARACTER = ' ';
private final AsciiPanel displayPanel;
private final int widthInCharacters, heightInCharacters;
private final static int Z_LEVELS = DrawingLayer.values().length;
private final AsciiCharacterData[][][] characterMap;
public Display(final AsciiPanel panel) {
displayPanel = panel;
widthInCharacters = panel.getWidthInCharacters();
heightInCharacters = panel.getHeightInCharacters();
characterMap = new AsciiCharacterData[widthInCharacters][heightInCharacters][Z_LEVELS];
for (int x = 0; x < widthInCharacters; x++) {
for (int y = 0; y < heightInCharacters; y++) {
for (int z = 0; z < Z_LEVELS; z++) {
characterMap[x][y][z] = new AsciiCharacterData(
TRANSPARENT_CHARACTER,
displayPanel.getDefaultForegroundColor(),
displayPanel.getDefaultBackgroundColor());
}
}
}
}
public void setCharacterAt(final int x, final int y, final DrawingLayer z,
final AsciiCharacterData c) {
if (x < 0 || x >= widthInCharacters || y < 0 || y >= heightInCharacters)
return;
characterMap[x][y][z.layer] = c;
// if z is not the top level
if (z.layer != Z_LEVELS - 1) {
// check all levels above
for (int i = z.layer + 1; i < Z_LEVELS; i++) {
// if there is an opaque character
if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER)
// we dont need to draw anything
return;
}
}
if (c.character == TRANSPARENT_CHARACTER) {
// loop through all characters under the transparent character
for (int i = z.layer - 1; i >= 0; i--) {
// if we find a non transparent character
if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER) {
// display that one instead
displayPanel.write(characterMap[x][y][i].character, x, y,
characterMap[x][y][i].foregroundColor,
characterMap[x][y][i].backgroundColor);
return;
}
}
// if there were no non trasparent characters
displayPanel.write(TRANSPARENT_CHARACTER, x, y);
// if we are a highlighter, we draw the below character and then
// just draw on top
} else {
displayPanel.write(c.character, x, y, c.foregroundColor,
c.backgroundColor);
}
displayPanel.repaint();
}
public AsciiCharacterData getCharacterAt(final int x, final int y,
final DrawingLayer z) {
return characterMap[x][y][z.layer];
}
public int getWidth() {
return widthInCharacters;
}
public int getHeight() {
return heightInCharacters;
}
public void clearLayer(final DrawingLayer layer) {
for (int x = 0; x < widthInCharacters; x++) {
for (int y = 0; y < heightInCharacters; y++) {
setCharacterAt(x, y, layer,
new AsciiCharacterData(TRANSPARENT_CHARACTER,
displayPanel.getDefaultForegroundColor(),
displayPanel.getDefaultBackgroundColor()));
}
}
}
}
Solved! It was one line in the setCharacterAt method. I was repainting every time I set a character which (while inefficient) also creates that flicker.
I've made this code that successfully creates a 16x12 grid by 50x50 squares on a 800x600px board.
As you can see, the player moves to the coordinates of the players mouseclick.
Each square of the grid has an object of Felt (field) on it, which can be laast (locked). If a fields lock attribute is set to 1, the player should not be moved to that position.
How do i detect the field a player tries to move on to achieve this?
public class SimpleGame extends BasicGame{
private Image plane;
private float planeX;
private float planeY;
public SimpleGame()
{
super("SpilTest");
}
#Override
public void init(GameContainer gc) throws SlickException {
plane = new Image("figur.png");
}
#Override
public void update(GameContainer gc, int delta) throws SlickException {
Input input = gc.getInput();
if (input.isMousePressed(input.MOUSE_LEFT_BUTTON)) {
this.planeX = input.getMouseX() - 30;
this.planeY = input.getMouseY() - 50;
}
}
public void render(GameContainer gc, Graphics g) throws SlickException {
Felt board[][] = nytGrid();
int distancex = 0;
int distancey = 0;
int counter = 0;
for (int i=0; i < board.length ; i++) {
for (int j=0; j < board[i].length ; j++) {
if (board[i][j].getLaast() == 1) {
g.setColor(Color.red);
g.fillRect(distancex, distancey, 50, 50);
}
distancex += 50;
counter++;
if (counter == 16) {
distancey += 50;
distancex = 0;
counter = 0;
}
}
}
g.drawImage(plane, planeX, planeY);
}
public static void main(String[] args) throws SlickException {
AppGameContainer app = new AppGameContainer(new SimpleGame());
app.setDisplayMode(800, 600, false);
app.setTargetFrameRate(60);
app.start();
}
public Felt[][] nytGrid() {
Felt [][] board = new Felt[16][12];
for (int i=0; i < board.length ; i++) {
for (int j=0; j < board[i].length ; j++) {
int x = i;
int y = j;
board[i][j] = new Felt(x, y);
if (i == 5 && j == 5) {
board[i][j].setLaast(1);
}
}
}
return board;
}
}
First off, you should probably initialize the board in the init() method instead of render, so it doesn't have to do it every frame, and move the declaration for the grid next to the plane, planeX and planeY declarations in the class.
Now to disable movement into a locked square, first add a method to check if a square at certain coordinates is locked, so something along the lines of:
private boolean isLocked(int x, int y) {
int square = board[x/50][y/50];
if (square == 1) return true;
else return false;
}
Next modify the part of your update() method where you update the plane coordinates, so vaguely something like:
if (input.isMousePressed(input.MOUSE_LEFT_BUTTON)) {
int destX = input.getMouseX() - 30;
int destY = input.getMouseY() - 50;
if (!isLocked(destX, destY)) {
this.planeX = destX;
this.planeY = destY;
}
}
It's easy!
int mx = Mouse.getX();
int my = Mouse.getY();
But, it gives you the world cordinates, and you have to translate it to pixels:
int mx = Mouse.getX();
int my = Mouse.getY() * -1 + (Window.WIDTH / 2) + 71;
Sorry if I'm not explaining good for it's 12:34 am and i'm doing late night programming but I need help. Btw this is in LWJGL Here's my Code:
I keep getting a null pointer error for the addAt() and the draw(); Basicly there is a couple classes that make it so when I click it will run addAt(mousex,mousey); and in the render loop it will keep drawing. The class that is new Block(x,y) is a class that will draw the QUAD.
//beggining
public class Grid {
Block[][] blocks = new Block[25][25];
public Grid(){
for (int x = 0; x < 25 - 1; x++) {
for (int y = 0; y < 16 - 1; y++) {
blocks[x][y] = new Block(x,y);
}
}
}
public void draw(){
for (int x = 0; x < 25;x++){
for (int y = 0; y < 25;y++){
blocks[x][y].draw();
}
}
}
public void addAt(int x,int y){
blocks[x][y] = new Block(x,y);
}
}
//end
basicly the Main is just making a Display and running the draw loop and the input listener.
Then the Block class is just making a quad at the defined x and y.
Sorry if I disobeyed a stack overflow rule. This is my First post and it's at a late time.:) Thanks in advance!
While adding to block array your looping is from 0 to (25-1) and (16-1). While processing the block the looping is from 0 to 25. That would most probably lead to NPE. Try initializing you blocks from 0 to 25 (for both x and y values).
I tried your code and it works fine (I don't get any exception). Here is my code:
public class Test {
private class Block {
int x, y;
private Block(int x, int y) {
this.x = x;
this.y = y;
}
void draw() {
}
}
Block[][] blocks = new Block[25][25];
public Test(){
for (int x = 0; x < 25 - 1; x++) {
for (int y = 0; y < 16 - 1; y++) {
blocks[x][y] = new Block(x,y);
}
}
}
public void draw(){
for (int x = 0; x < 25;x++){
for (int y = 0; y < 25;y++){
blocks[x][y].draw();
}
}
}
public void addAt(int x,int y){
blocks[x][y] = new Block(x,y);
}
public static void main(String[] args) {
Test t = new Test();
t.addAt(4,5);
}
}
=> The problem seems to be the late night programming ;-)
I'm using Processing to divide a large image into a series of smaller, rectangular nodes.
Processing stores the color value for the pixels of a PImage in a pixels array, which I am accessing to break up the image into smaller parts. For some reason, I am getting this output, when my intent was for the entire image to be displayed when the nodes are arranged in draw().
Here is my main class:
ArrayList node = new ArrayList();
PImage grid;
PVector nodeDimensions = new PVector(210, 185);
PVector gridDimensions = new PVector(2549, 3300);
String name = "gridscan.jpeg";
void setup() {
size(500, 500);
grid = loadImage(name);
grid.loadPixels();
fillPixels();
noLoop();
}
void fillPixels() {
int nodeNum = 0;
for (int startX = 0; startX < 2549 - nodeDimensions.x; startX += nodeDimensions.x) {
for (int startY = 0; startY < 3300 - nodeDimensions.y; startY += nodeDimensions.y) {
node.add(new Node());
sendPixels(new PVector(startX, startY), nodeNum);
nodeNum++;
}
}
}
void sendPixels(PVector start, int nodeNum) {
for (int x = int(start.x); x < start.x + nodeDimensions.x; x++) {
for (int y = int(start.y); y < start.x + nodeDimensions.y; y++) {
Node _node = (Node) node.get(node.size() - 1);
_node.fillPixel(new PVector(x, y), grid.pixels[int(y*gridDimensions.x+x)]);
}
}
}
void draw() {
drawNodes();
}
void drawNodes() {
int nodeNum = 0;
for (int x = 0; x < width; x += nodeDimensions.x) {
for (int y = 0; y < height; y += nodeDimensions.y) {
Node _node = (Node) node.get(nodeNum);
_node.drawMe(new PVector(x - (nodeDimensions.x/2), y - (nodeDimensions.y/2)));
nodeNum++;
}
}
}
And here is the Node class:
class Node {
color[] pixel;
Node() {
pixel = new color[int(nodeDimensions.x * nodeDimensions.y)];
}
void fillPixel(PVector pos, color pixelValue) {
if(int(pos.y * nodeDimensions.y + pos.x) < 38850) pixel[int(pos.y * nodeDimensions.y + pos.x)] = pixelValue;
}
void drawMe(PVector centerPos) {
pushMatrix();
translate(centerPos.x, centerPos.y);
for(int x = 0; x < nodeDimensions.x; x++) {
for(int y = 0; y < nodeDimensions.y; y++) {
stroke(getPixelColor(new PVector(x, y)));
point(x,y);
}
}
popMatrix();
}
color getPixelColor(PVector pos) {
return pixel[int(pos.y * nodeDimensions.x + pos.x)];
}
}
Hopefully my code makes sense. I suspect the issue is in the sendPixels() method of the main class.
I used this this page from the Processing reference as a guide for creating that function, and I'm not sure where my logic is wrong.
Any help would be appreciated, and please let me know if I can clarify something.
According to getPixelColor(), it seems that it uses rows.
So if you have a 5x5 square image then 2x2 would be 7.
To get the index you use this formula:
index = (y - 1) * width + x
Explained this way it's look pretty simple, doesn't it?
Alternatively, you may be able to use getSubimage() on the BufferedImage returned by the getImage method of PImage. There's a related example here.