So I can load a 2D map using tiles using a text file, which is great and all. However, one issue I have met with this method is that I can't add objects/actors to my map since the file is a 2D grid. (The game is similar to games like zelda and pokemon.) I've tried creating an object layer so I can overlap images, but it doesn't seem to work for me. To give an example of what I want, have objects such as trees to be solid and on top of the background grass.
I am also looking for better methods to creating these tile based maps if you want to pitch some ideas to me.
**Note: I am about beginner/intermediate at Java.
Here is my constructor for the GameState class that calls the Map.
public GameState(Game game) {
super(game);
player = new Player(game, 0, 0, 64, 64);
map = new Map(game, "res/saves/save1.txt");
}
Here is the Map class (which works) that also calls the object (2nd) layer.
private int width, height;
public static int spawnX, spawnY;
private int[][] mapTiles;
MapObjects mapObjects;
Game game;
public Map(Game game, String path) {
this.game = game;
mapObjects = new MapObjects(game, "res/saves/save1_obj.txt", width, height);
loadMap(path);
}
private void loadMap(String path) {
String file = Utils.loadFileAsString(path);
//Token is which number it is out of the total
String[] tokens = file.split("\\s+");
//Sets what is what
width = Utils.parseInt(tokens[0]);
height = Utils.parseInt(tokens[1]);
spawnX = Utils.parseInt(tokens[2]);
spawnY = Utils.parseInt(tokens[3]);
mapTiles = new int[width][height];
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
// (x+y*width) : calculates the nth token (+4) : The 4 prior tokens before the graph
mapTiles[x][y] = Utils.parseInt(tokens[(x + y *width) + 4]);
}
}
}
public void render(Graphics g) {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
//Only renders what is seen.
getMapTile(x, y).render(g, (int)(x*Tile.TILE_WIDTH-game.getCamera().getxOffset()), (int)(y*Tile.TILE_HEIGHT-game.getCamera().getyOffset()));
}
}
}
public void tick() {
}
//Gets the specific tile at specific coordinates.
private Tile getMapTile(int x, int y) {
Tile t = Tile.tiles[mapTiles[x][y]];
if(t == null) {
return Tile.grassTile;
}
return t;
}
And lastly, the object layer that doesn't work. It does not give an error, just the overlapping objects aren't visible. I've made sure to load the object layer before the Map layer, but that doesn't seem to be the issue.
private int width, height;
private int[][] objTiles;
Game game;
public MapObjects(Game game, String path, int width, int height) {
this.game = game;
loadObjects(path, width, height);
}
public void loadObjects(String path, int width, int height) {
this.width = width;
this.height = height;
String file = Utils.loadFileAsString(path);
String[] tokens = file.split("\\s+");
objTiles = new int[width][height];
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
objTiles[x][y] = Utils.parseInt(tokens[(x + y *width)]);
}
}
}
public void render(Graphics g) {
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
//Only renders what is seen.
getObjTile(x, y).render(g, (int)(x*Tile.TILE_WIDTH-game.getCamera().getxOffset()), (int)(y*Tile.TILE_HEIGHT-game.getCamera().getyOffset()));
}
}
}
public void tick() {
}
//Gets the specific object tile at specific coordinates.
private Tile getObjTile(int x, int y) {
Tile t = Tile.tiles[objTiles[x][y]];
if(t == null) {
return Tile.nothingTile;
}
return t;
}
We may need a bit more info from you.
Do you use a different "container/component" to draw map tiles than you do for your objects? Because if you render objects first then they will disappear as soon as you render the map. You should draw the map first, and then do objects like so:
public Map(Game game, String path) {
this.game = game;
//swapped the order of the lines below so the map loads first:
loadMap(path);
mapObjects = new MapObjects(game, "res/saves/save1_obj.txt", width, height);
}
From what you have said then this does not work either, however if you use the same component to draw your map and objects then one will always override the other, and something will always be missing. To fix this you need to crease two separate panes, one for the map, and a transparent one that sits on top of the map that you can use to render your objects.
See this illustration as an example:
You basically need to add a new transparent "content plane" similar to the way that glass pane shown above.
Related
I am using a tutorial to understand how sprites work using a draw() method as well as using a gameloop. I adjusted the code as far as I understand it for my own project.
The question I have is how can I access a different row of my sprite sheet besides the second row. My sprite sheet has 9 columns and 20 rows.
public class Sprite implements Drawable {
private static final int BMP_COLUMNS = 9;
private static final int BMP_ROWS = 20;
private int x = 0;
private int y = 0;
private int xSpeed = 5;
private Bitmap bmp;
float fracsect = 30;
private GameContent gameContent;
private int currentFrame = 0;
private int width;
private int height;
public Sprite(GameContent gameContent, Bitmap bmp) {
this.gameContent = gameContent;
this.bmp = bmp;
this.width = bmp.getWidth() / BMP_COLUMNS;
this.height = bmp.getHeight() / BMP_ROWS;
}
#Override
public void update(float fracsec) {
if (x > gameContent.getGameWidth() - width - xSpeed) {
xSpeed = -5;
}
if (x + xSpeed < 0) {
xSpeed = 5;
}
x = x + xSpeed;
currentFrame = ++currentFrame % BMP_COLUMNS;
}
#Override
public void draw(Canvas canvas) {
update(fracsect);
int srcX = currentFrame * width;
int srcY = 1*height - 41;
Rect src = new Rect(srcX +20 , srcY,srcX + width,srcY + height-38); // Generates
Rect dst = new Rect(x,y,x+width -30, y+height-30); // Scales
canvas.drawBitmap(bmp, src, dst, null);
}
}
How do I gain access to the second row and how can I change it for instance to the third or 4th row ?
What I understand so far is that using a sprite as an object as a bitmap instead via imageview the code implementation of the code works differently. Is there any advice on how to access a different row for the sprite sheet ? I used the android documentation as well as the tutorial to understand this process as far as I can.
Here is the tutorial also:
http://www.edu4java.com/en/androidgame/androidgame4.html
From what I can see you are iterating through the first row, drawing each sprite in turn, perhaps to create an animation.
If you want to draw the animation for the second row, you can simply add 'height' to 'srcY'. It looks like you had this idea but you substracted instead of adding. Remember that Y-coordinates on computers go from top to bottom (i.e. (0,0) is top-left, (0, 10) is 10 pixels lower).
Likewise for the third and fourth rows, just add 'height' to 'srcY' again.
I've been trying to create a maze using swing.
For now, I just want to create a 10x10 grid using lines, which would be all around my cells (a 40x40 square, from the "w"). I'm trying to create each lines of my square depending of my boolean[] walls tab ( walls[0] is top, walls[1] is right, walls[2] is bottom and walls[3] is left).
If the value is true, then we have a wall, if not, the passage is opened and no line on this side.
I'm using an ArrayList<Cell> for collecting the differents cells of my grid.
All of this is working (it seems) but I've came across a problem.
Indeed I would like to use my object's (Cell) attributes in paintComponent(Graphics g) for doing custom dimensions of cells.
But, I don't know really how to do this. I've tried to separate my Cell class and do another class for my graphic interface but it didn't worked out as well.
public class Cell {
int i,j;
int w = 40;
ArrayList<Cell> grid = new ArrayList<Cell>();
boolean[] walls = new boolean[4];
boolean visited;
public Cell () {
this.setup();
}
public Cell(int i, int j) {
this.i = i;
this.j = j;
this.walls[0] = true;
this.walls[1] = true;
this.walls[2] = true;
this.walls[3] = true;
this.visited = false;
}
public int getI() {
return i;
}
public int getJ() {
return j;
}
public void setup() {
JFrame f = new JFrame("Maze");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(400, 400);
f.setVisible(true);
JPanel panel = new JPanel();
f.getContentPane().add(panel);
int cols = (int) f.getWidth()/w;
int rows = (int) f.getHeight()/w;
for (int j = 0; j < rows; j++)
{
for (int i = 0; i < cols; i++)
{
Cell cell = new Cell(i,j);
grid.add(cell);
}
}
}
public void paintComponent(Graphics g) {
int x = i*w;
int y = j*w;
g.setColor(Color.black);
if (this.walls[0])
g.drawLine(x , y , x + w, y );
if (this.walls[1])
g.drawLine(x + w, y , x + w, y + w);
if (this.walls[2])
g.drawLine(x + w, y + w, x , y + w);
if (this.walls[3])
g.drawLine(x , y + w, x , y );
}
public static void main(String[] args) {
new Cell();
}
}
You can't just add a paintComponent(...) method to a class and expect that method to be invoked.
To do custom painting you override the paintComponent() method of a JPanel. So you need a custom panel. The panel would contain the ArrayList of Cell objects and in the paintComponent() method you iterate through each Cell object and paint it.
So first you need to start by reading the section from the Swing tutorial on Custom Painting to learn the basics of painting a single object.
Once you understand that you can check out Custom Painting Approaches which shows how to paint from a List of objects.
I am attempting to draw a checkerboard pattern in java using nested for loops, but I am having trouble doing it with two different colors. I know this question has been asked before, but it hasn't been asked with two different colors on the board that are not just using a background color. I plan on using the individual squares as an array to hold checker positions, so I do need each individual square made. Would it be better to drop the ice of a nested for loop to create each square, or should i stick with that shortcut? And if I were to stick with it, how would the nested loop be formatted (one for each color)?
When creating checker tiles, I would pass in an int for the x coordinate, and y coordinate such as:
import java.awt.Color;
import java.awt.Graphics;
public class CheckerTile {
public static final int WIDTH = 100; //width of each tile
public static final int HEIGHT = 100; //height of each tile, most likely same as width so its a square
public static int currentId = 0; //variable to reference unique id for each tile
private int id; //current id of tile
private int x; //x coordinate
private int y; //y coordinate
private int width; //width of tile
private int height; //height of tile
//Default constructor to take x and y coordinate
public CheckerTile( int x, int y ) {
this.id = currentId++;
this.x = x;
this.y = y;
width = WIDTH;
height = HEIGHT;
}
public int getId()
{
return id;
}
//draws the tile on the panel.
public void draw(Graphics g)
{
//if the checkerTile's id is divisible by 2, draw it red, otherwise draw it black.
g.setColor( id % 2 == 0 ? Color.RED : Color.black);
g.fillRect(x, y, width, height);
}
}
That way we have a way to draw the tile on the board. Now, when creating each object, we increment a currentId variable so that we can color each one individually using the modulus operator later.
I am assuming you are using Swing so I decided to add a draw(Graphics g) method so when repainting in java it would use that Graphics object. If you are using a different library, then you will have to do some research in to how to draw it on the board.
Now in your JPanel, it would look something like this:
//Creates the JPanel, which needs to be added to JFrame object in main
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CheckerBoard extends JPanel {
CheckerTile[][] checkerTiles; //2-dimension array of checkerTiles
public CheckerBoard() {
super();
this.setSize(800,800);
checkerTiles = new CheckerTile[9][9];
//This creates the checkerTiles.
for(int i = 0; i < 9; i++)
{
for( int j = 0; j < 9; j++)
{
checkerTiles[i][j] = new CheckerTile( j * CheckerTile.WIDTH, i * CheckerTile.HEIGHT );
}
}
this.setVisible(true);
//Repaint right away to show results.
repaint();
}
//We need to override this to paint the tiles on the board.
#Override
public void paintComponent(Graphics g)
{
for(int i = 0; i < checkerTiles.length; i++)
{
for(int j = 0; j < checkerTiles[i].length; j++)
{
//call the draw method on each tile.
checkerTiles[i][j].draw(g);
}
}
}
//A demo of adding the panel to a frame and showing the tiles.
public static void main(String[] args)
{
//Create the JFrame and add the CheckerBoard we made to it.
JFrame frame = new JFrame();
frame.setSize(800,800);
frame.setLayout(new BorderLayout());
frame.add(new CheckerBoard(), BorderLayout.CENTER);
frame.setVisible(true);
}
}
I'm trying to make online game. The server sends data of surrounding "chunks" to client, which then saves the chunks and when drawing it checks the saved data of those chunks and draws them. So the data that is drawn is saved on Array on the client. Even if the data is saved on that array, if I connect to server using ip, the graphics seem to be flickering but when connectin as a localhost, the graphics are just fine. I've also tried to use double buffering (when running with localhost it works just fine without double buffer too) but when using ip it still flickers (even if I use double buffering (not too sure if it's working though..)) So what I'm asking is:
1) Why it flickers when using ip instead of localhost? (it can't be because of too much traffic over socket)
2) How could I do better double buffering (I've used multiple guides from youtube and internet like this)
None of guides I've used so far haven't helped with flickering though..
Drawing method:
public class Area implements Serializable{
/**
*
*/
private static final long serialVersionUID = -7079895937774492131L;
private Tile[][] tiles;
private Object[][] objects;
private int x, y;
private static Chunk[] chunks;
//public static Area currentArea;
private static Area[][] areasave = new Area[512][512];
public Area(Tile[][] tiles, Object[][] objects, int x, int y){
this.tiles = tiles;
this.objects = objects;
this.x = x;
this.y = y;
}
public Area(Tile[][] tiles, Object[][] objects){
this.tiles = tiles;
this.objects = objects;
}
public static Tile[][] getTiles(){
return tiles;
}
public static Object[][] getObjects(){
return objects;
}
public static boolean addArea(Area area){
areasave[area.x][area.y] = area;
return true;
}
public static void Draw(Graphics g) {
if(chunks != null){
for(int i = 0; i < chunks.length; i++){
if(areasave[chunks[i].getX()][chunks[i].getY()] != null){
Tile[][] tiles = getTiles(areasave[chunks[i].getX()][chunks[i].getY()]);
Object[][] objects = getObjects(areasave[chunks[i].getX()][chunks[i].getY()]);
g.setColor(new Color(255, 255, 255));
if(tiles != null){
for(int y = 0; y < tiles.length; y++){
for(int x = 0; x < tiles[0].length; x++){
if(tiles[x][y] != null){
if(new File(Frame.dpath +"TileImages/" +Tile.getID(tiles[x][y]) +".png").exists()){ //if we have tile for that ID
g.drawImage(tileImages[Integer.parseInt(Tile.getID(tiles[x][y]))], ((Integer.parseInt(Tile.getX(tiles[x][y]))-Player.x)+GameLayout.tilesWidth/2)*(Frame.Width/GameLayout.tilesWidth), ((Player.y-Integer.parseInt(Tile.getY(tiles[x][y])))+GameLayout.tilesHeight/2)*(Frame.Height/GameLayout.tilesHeight), Frame.Width/GameLayout.tilesWidth, Frame.Height/GameLayout.tilesHeight, null);
}
else{
g.drawImage(tileImages[0], ((Integer.parseInt(Tile.getX(tiles[x][y]))-Player.x)+GameLayout.tilesWidth/2)*(Frame.Width/GameLayout.tilesWidth), ((Integer.parseInt(Tile.getY(tiles[x][y]))-Player.y)+GameLayout.tilesHeight/2)*(Frame.Height/GameLayout.tilesHeight), Frame.Width/GameLayout.tilesWidth, Frame.Height/GameLayout.tilesHeight, null);
}
}
}
}
}
if(objects != null){
for(int y = 0; y < objects.length; y++){
for(int x = 0; x < objects[0].length; x++){
if(objects[x][y] != null){
if(new File(Frame.dpath +"ObjectImages/" +Object.getID(objects[x][y]) +".png").exists()){ //if we have object for that ID
g.drawImage(objectImages[Integer.parseInt(Object.getID(objects[x][y]))], ((Integer.parseInt(Object.getX(objects[x][y]))-Player.x)+GameLayout.tilesWidth/2)*(Frame.Width/GameLayout.tilesWidth), ((Player.y-Integer.parseInt(Object.getY(objects[x][y])))+GameLayout.tilesHeight/2)*(Frame.Height/GameLayout.tilesHeight), Frame.Width/GameLayout.tilesWidth, Frame.Height/GameLayout.tilesHeight, null);
//System.out.println(Integer.parseInt(Object.getID(objects[x][y])) +", " +Object.getX(objects[x][y]) +"-" +Player.x +"," +((Player.y-Integer.parseInt(Object.getY(objects[x][y])))+GameLayout.tilesHeight/2)*(Screen.Height/GameLayout.tilesHeight));
}
else{
g.drawImage(objectImages[0], ((Integer.parseInt(Object.getX(objects[x][y]))-Player.x)+GameLayout.tilesWidth/2)*(Frame.Width/GameLayout.tilesWidth), ((Integer.parseInt(Object.getY(objects[x][y]))-Player.y)+GameLayout.tilesHeight/2)*(Frame.Height/GameLayout.tilesHeight), Frame.Width/GameLayout.tilesWidth, Frame.Height/GameLayout.tilesHeight, null);
}
}
}
}
}
}
}
}
}
the Draw method above did flicker even if I tried to use bufferStrategy (I'm not too sure if I did it all right though..) I'm more than happy to give more code/describe more etc. if needed
I am having a big problem trying to figure out how to use a variable in one overridden method, and also use it in another. Specifically, I am using libgdx, and want to use the variable map in the create method in the render method.
Here is my code:
#Override
public void create() {
batch = new SpriteBatch();
background = new TextureAtlas(
Gdx.files.internal("data/background/background.pack"),
Gdx.files.internal("data/background/"));
bg = background.findRegion("Background");
t = TMXLoader.readTMX("data/world/World.tmx");
ArrayList<Image> map = TMXLoader.drawMap();
camera = new OrthographicCamera(WIDTH, HEIGHT);
camera.position.set(WIDTH / 2, HEIGHT / 2, 0);
camera.update();
}
#Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
BufferedImage image = new BufferedImage(20, 20,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) image.getGraphics();
batch.begin();
for (int i = 0; i < WIDTH; i += bg.getRegionWidth()) {
for (int j = 0; j < HEIGHT; j += bg.getRegionHeight()) {
batch.draw(bg, i, j);
}
}
for (int x = 0; x < t.width; x++) {
for (int y = 0; y < t.height; y++) {
for (Image tile : map) {
float[] scale = getScale();
Image scaledTile = tile.getScaledInstance(
(int) (t.tilewidth * scale[0]),
(int) (t.tileheight * scale[1]), Image.SCALE_FAST);
g2d.drawImage(scaledTile, x, y, null);
}
}
}
batch.end();
}
As it is, I can't access map in the render method. I can't create my own method, because it would have to be called in each method create and render. I am stuck, and can't figure it out on my own. I have a feeling I could use a get/set to do this, but I'm not quite sure how I'd try to do that...
Declare map as a global variable.
private ArrayList<Image> map;
Inside create method, do the assignment as you did-
map = TMXLoader.drawMap();
Now in render method, you can use the map.