I'm trying to develop the game Othello using Java and I'm struggling with the implementation of finding the available moves the player has(not computer).
For example I'm player 1, and I'm playing with the white pieces,
Check if the button is empty or not. (I'm using buttons as tiles)
Check if there're any neighbors of the opposite color.
If there is, continue checking every direction there's an opposite color until
If we reach a boundary - return false.
If we reach our color - turn all the pieces to my color.
I'm struggling implementing 3. and 5.
How can I Iterate through all the directions ( maximum of 8 directions if I'm in the inner part of the board ), and how can I can advance on checking the colors in each direction?
I thought about implementing all the 8 directions for the inner board, and then implementing all the possibilities in the outer board and checking edge options which is VERY not efficient and I don't want to code like that.
You don't have to look on the code, I'm trying to figure out how to approach it (thinking about 2 for loops), but here's the function and the whole code below: (every button has an icon - black/white piece)
private void checkLegalPlay(int row, int col) {
if(playerNum == 0){ //Black player
if(row > 0 && row < 7 && col > 0 && col < 7){ //Inner board -
//not good, i want from any point of the board
for(int i = col-1; i != 0; i--)
if(squares[row][i].getIcon() != null){
if(squares[row][i].getIcon() == blackPiece){
//Advance until boundary - return false
//Advance if there're black pieces
//If we get to white piece, turn all to
// white pieces
}
}
}
}
}
It's already almost 300 lines of code, so I prefer to give a link if you really want to see what I've done so far: -deleted-
Software development is an art of abstraction. You should try to develop a skill to see similarities between pieces of logic and to abstract them away. For example, to check if the move is legal you have to iterate from the cell in different directions applying the same check logic. Moreover, check of a move and applying move (flipping pieces) share the same iteration logic. So let's abstract it away, i.e. let's separate iteration from logic we do inside iteration:
private static final int SIZE = 8;
static boolean isValidPos(int pos) {
return pos >= 0 && pos < SIZE;
}
static class Point {
public final int row;
public final int col;
public Point(int row, int col) {
this.row = row;
this.col = col;
}
}
private static final Point[] ALL_DIRECTIONS = new Point[]{
new Point(1, 0),
new Point(1, 1),
new Point(0, 1),
new Point(-1, 1),
new Point(-1, 0),
new Point(-1, -1),
new Point(0, -1),
new Point(1, -1),
};
interface CellHandler {
boolean handleCell(int row, int col, Icon icon);
}
void iterateCells(Point start, Point step, CellHandler handler) {
for (int row = start.row + step.row, col = start.col + step.col;
isValidPos(row) && isValidPos(col);
row += step.row, col += step.col) {
Icon icon = squares[row][col].getIcon();
// empty cell
if (icon == null)
break;
// handler can stop iteration
if (!handler.handleCell(row, col, icon))
break;
}
}
static class CheckCellHandler implements CellHandler {
private final Icon otherIcon;
private boolean hasOtherPieces = false;
private boolean endsWithMine = false;
public CheckCellHandler(Icon otherIcon) {
this.otherIcon = otherIcon;
}
#Override
public boolean handleCell(int row, int column, Icon icon) {
if (icon == otherIcon) {
hasOtherPieces = true;
return true;
} else {
endsWithMine = true;
return false;
}
}
public boolean isGoodMove() {
return hasOtherPieces && endsWithMine;
}
}
class FlipCellHandler implements CellHandler {
private final Icon myIcon;
private final Icon otherIcon;
private final List<Point> currentFlipList = new ArrayList<Point>();
public FlipCellHandler(Icon myIcon, Icon otherIcon) {
this.myIcon = myIcon;
this.otherIcon = otherIcon;
}
#Override
public boolean handleCell(int row, int column, Icon icon) {
if (icon == myIcon) {
// flip all cells
for (Point p : currentFlipList) {
squares[p.row][p.col].setIcon(myIcon);
}
return false;
} else {
currentFlipList.add(new Point(row, column));
return true;
}
}
}
private boolean checkLegalPlay(int row, int col) {
ImageIcon otherIcon = (playerNum == 0) ? whitePiece : blackPiece;
Point start = new Point(row, col);
for (Point step : ALL_DIRECTIONS) {
// handler is stateful so create new for each direction
CheckCellHandler checkCellHandler = new CheckCellHandler(otherIcon);
iterateCells(start, step, checkCellHandler);
if (checkCellHandler.isGoodMove())
return true;
}
return false;
}
ALL_DIRECTIONS represents all 8 directions you can navigate. iterateCells method accepts some direction and navigates through it till it hits either empty cell or a border. For each non-empty cell handleCell of the passed CellHandler is called. So now your checkLegalPlay becomes simple: implement CheckCellHandler and iterate through all possible directions to see if we can flip in that direction. Implementing actual flip logic is actually very similar: just implement FlipCellHandler and use it similarly. Note that you can also abstract "current player" away by explicitly passing myIcon and otherIcon to handlers.
Related
I understand the basis of collision detection and have developed my way (as far as I know) for collision detection. However, it does not seem to be working. What I have done is instead of drawing full rectangles around sprites, shapes, and other objects, I simply made methods that draw lines (very thin rectangles) on all sides of the object, left, right, top, and bottom.
The methods that draw the thin rectangles on the left,right,top, and bottom of the specified sprites. This is probably not the best way to do collision, but this is what I have, and am open to all ideas and different solutions!
Note: The(int)(x-Game.getCamera().getX()),(int)(y + height -Game.getCamera().getY()), is simply for the camera, so when the player moves, the trees (in my case the object I want to collide with) do not move with him
public Rectangle getBounds() {
return new Rectangle((int)(x-Game.getCamera().getX()),
(int)(y + height - Game.getCamera().getY()),width,height-height);
}
public Rectangle getBoundsTop() {
return new Rectangle((int)(x-Game.getCamera().getX()),
(int)(y- Game.getCamera().getY()),width,height-height);
}
public Rectangle getBoundsRight() {
return new Rectangle((int)(x + width -Game.getCamera().getX()),
(int)(y - Game.getCamera().getY()),width - width,height);
}
public Rectangle getBoundsLeft() {
return new Rectangle((int)(x -Game.getCamera().getX()),
(int)(y- Game.getCamera().getY()),width - width,height);
}
Actual collision CHECKING (in player class)-
if(getBounds().intersects(treeTile.getBoundsTop())) {
//What happens when they collide
//When the bottom of the player collides with the top of the tree.
}
if(getBoundsTop().intersects(treeTile.getBounds())) {
//What happens when they collide
//When the top of the player collides with the bottom of the tree
}
if(getBoundsRight().intersects(treeTile.getBoundsLeft())) {
//What happens when they collide
//when the right side of the player collides with the left of the
//tree
}
if(getBoundsLeft().intersects(treeTile.getBoundsRight())) {
//What happens when they collide
//When the left of the player collides with the right of the tree
}
I appreciate all the help I can get
Here is collision and movement code-
for (int i = 0; i < handler.object.size(); i++) {
Square handle = handler.object.get(i);
if (getBounds().intersects(treeTile.getBoundsTop())) {
handle.setvelY(-1);
}
if (getBoundsTop().intersects(treeTile.getBounds())) {
handle.setvelY(0);
}
if (getBoundsRight().intersects(treeTile.getBoundsLeft())) {
handle.setvelX(0);
}
if (getBoundsLeft().intersects(treeTile.getBoundsRight())) {
handle.setvelX(0);
}
}
Movement-
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
for (int i = 0; i < handler.object.size(); i++) {
Square handle = handler.object.get(i);
if (handle.getId() == ObjectId.Zelda) {
if (code == KeyEvent.VK_D) {
handle.right();
}
if (code == KeyEvent.VK_A) {
handle.left();
}
if (code == KeyEvent.VK_W) {
handle.up();
}
if (code == KeyEvent.VK_S) {
handle.down();
}
}
My new collision idea -
boolean walkRight = true;
boolean walkLeft = true;
boolean walkDown = true;
boolean walkUp = true;
public void tick() {
if(walkRight == true) {
velX = 2;
}if(walkLeft == true) {
velX = -2;
}if(walkDown == true) {
velY = 2;
}if(walkUp == true) {
velY = -2;
}
if (getBounds().intersects(treeTile.getBoundsTop())) {
walkDown = false;
}
if (getBoundsTop().intersects(treeTile.getBounds())) {
walkUp = false;
}
if (getBoundsRight().intersects(treeTile.getBoundsLeft())) {
walkRight = false;
}
if (getBoundsLeft().intersects(treeTile.getBoundsRight())) {
walkLeft = false;
}
Something like this, not exactly, it is just an example.
Have you tried using 1 instead of width - width and height - height ?
I think these evaluating to 0 for the width / height is causing the intersects function to fail.
EDIT
I think you're over complicating this. If you just have one big collision box, instead of four small ones, then where ever you're moving your player, you can
// Square previousPosition = currentPosition
// do move player code, which presumably updates the currentPosition
// loop through all the trees you have
// if the player is colliding with a tree
// currentPosition = previousPosition
This should handle the player colliding with any side of the tree, since we just move them back to where they were previously.
Wanna simulate the next move for my AI in my othello game, but instead of just returning the next move, it makes all the moves on the original board instead of just simulating on a clone and the game ends.
public class GameState implements Cloneable{
private Node[][] board; // Game board
private int scorePlayer, scoreAI; // Player scores ( will start at 0 )
private ArrayList<Node> validNodes; // List holding all nodes possible to add pieces to
/**
* Creates the game state
*/
public GameState(){
// create size x size board
this.board = new Node[Setting.BOARD_SIZE][Setting.BOARD_SIZE];
validNodes = new ArrayList<>();
scorePlayer = 0;
scoreAI = 0;
protected GameState clone() {
return new GameState(this);
}------------------------ CLONE METHOD----------------
public int search(GameState board, Player player, int alpha, int beta, int depth, ScoreEval function) {
int record = Integer.MIN_VALUE;
Node maxMove = null;
int result;
GameState subBoard = board.clone();
if (depth <= 0 || board.getValidMoves().size()==0) {
record = function.evaluate(board, player);
} else {
ArrayList<Node> possibleMoves = board.getValidMoves();
if (!possibleMoves.isEmpty()) {
for (int i =0; i<possibleMoves.size();i++) {
Node nod = possibleMoves.get(i);
subBoard = board.clone();
subBoard.setPiece(nod.x,nod.y, player.type);
if(player.type==Setting.TILE_AI){
result = -search(subBoard, aiAss1.Controller.pHum, alpha, beta, depth - 1, function);
}
else{
result = -search(subBoard, aiAss1.Controller.pAI, alpha, beta, depth - 1, function);
}
if (result > record) {
record = result;
maxMove = nod;
}
}
} else {
record = -search(subBoard, player, alpha, beta, depth - 1, function);
}
}
bestMove = maxMove;
return record;
}
Try breaking down you logic into parts, putting each in a separate method. Then write a unit test for each part, and check that each part does what you want.
This is how you should write software.
I am working on a tile based game, where various tiles have different states.
I am now looking to add a player object, this object will continually check the states of the tiles in order to move.
My Tile class:
class Tile {
int x, y;
boolean empty = true;
boolean baseCell = true;
boolean active = false;
public static final int EMPTY = 0;
public static final int BASE = 1;
public static final int ACTIVE = 2;
Tile(int x_, int y_, boolean baseCell_) {
x=x_;
y=y_;
baseCell=baseCell_;
}
void display() {
if (baseCell) {
fill(0, 0, 255);
} else {
if (empty) {
fill (255);
} else if (active) {
fill(255, 100, 50);
}
}
stroke(0);
rect(x, y, 50, 50);
}
}
When the game starts, 4 baseCells are drawn and cannot be changed. The user is able to click on other cells to change their values from empty to active or active to empty.
Currently I am unsure how to assign booleans the static values I have set - or perhaps this is not the best approach.
Any help or guidance would be much appreciated.
First of all, given that you want to assign to boolean values, the values of the static variables, I would say that it would be impossible since ACTIVE = 2. This value cannot be used as a boolean since it is not 0 nor 1.So if you want to use boolean variables all static final variable values should be 0 or 1.
Secondly, you should consider gathering all the constants (static final variables) in an interface which will be implemented by each class that needs one of those constants. It is a better programming practice and this will help you organize your project better.
Finally, if you want to change the value of the variables empty,active,baseCell, you should either include them in the constructor like so:
Tile(int x_, int y_, boolean baseCell_, boolean active_, boolean empty_)
{
x=x_;
y=y_;
baseCell=baseCell_;
active = active_;
empty = empty_;
}
and/or you should implement getters and setters for each one of them like so:
//setter
protected void setEmpty(boolean empty_)
{
empty = empty_;
}
//getter
public boolean getEmpty()
{
return empty;
}
And the way to change the respective values is by simply calling either the constructor, when the object is first constructed, and the setters after that like so:
(..) new Tile( x, y, BASE, ACTIVE, EMPTY); //constructor
tile1.setEmpty(EMPTY); //tile1 is an instance of the Tile class
Final notes:
Regarding the visibility of the setter you should be careful, because if you make it public, anyone can access it and change its' value, and this affects the security of your application. The way to tackle this is through the hierarchy.
I suggest you read this: http://docs.oracle.com/javaee/6/tutorial/doc/gjbbp.html (basic info on getters/setters)
and this: https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
(required knowledge to tackle the setter problem)
As I was mentioning in the commend, rather than using a boolean for each state, you can use a single integer to keep track of the cell state.
This will also allow you to use a switch statement. There's nothing wrong with if/else, you can achieve the same thing, but it may help things tidy.
Here's a basic proof of the above concept based mostly on your existing code:
//a few test tiles
int numTiles = 3;
//prepare an array to store each tile
Tile[] tiles = new Tile[numTiles];
void setup(){
size(150,150);
//initialize each tile
for(int i = 0 ; i < numTiles; i++)
tiles[i] = new Tile(50*i,50,random(1) > .5);//50% pseudo-random change of getting a BASE cell
}
void draw(){
background(255);
//render tiles (they'll handle their own states internally)
for(int i = 0 ; i < numTiles; i++){
tiles[i].display();
}
}
void mouseReleased(){
//update each tile on click
for(int i = 0 ; i < numTiles; i++)
tiles[i].click(mouseX,mouseY);
}
class Tile {
int x, y;
int size = 50;
int state = 0;
public static final int EMPTY = 0;
public static final int BASE = 1;
public static final int ACTIVE = 2;
Tile(int x_, int y_, boolean baseCell_) {
x=x_;
y=y_;
if(baseCell_) state = BASE;
else state = EMPTY;
}
void click(int mx,int my){
//check if the mouse coordinates are within the tile's rectangle
boolean isOver = (mx >= x && mx <= x + size) && (my >= y && my <= y + size);
if(isOver){//if so, update states
switch(state){
case EMPTY:
state = ACTIVE;
break;
case ACTIVE:
state = EMPTY;
break;
case BASE:
println("BASE cell clicked, change what happens here");
break;
}
}
}
void display() {
switch(state){
case EMPTY:
fill(255);
break;
case ACTIVE:
fill(255, 100, 50);
break;
case BASE:
fill(0,0,255);
break;
}
stroke(0);
rect(x, y, size, size);
}
}
Later on you may choose to call functions from within the switch statements to keep the code easier to manage (especially if you might use many states)
I'm working on a Mario game and am in need of assistance and suggestions on how to go about creating hit detection for a tilemap.
Currently, the player has the ability to walk/jump through the blocks.
I added in a fixed detection to the ground for now which I am hoping to replace with regular hit detection.
I understand that there are four sides to each block and the player. Only some blocks need hit detection and some things you might need to know is that the player stays at 300px(middle of screen) 98% of the time.
The only thing that moves is the map
The map is rendered from a .txt file and is rendered like so:
for(int y=0;y<map.length;y++) {
for(int x=0;x<map[y].length;x++) {
int index = map[y][x];
int yOffset = 0;
if(index>(tileSheet.getWidth() / Engine.TILE_WIDTH) -1) {
yOffset++;
index = index - (tileSheet.getWidth() / Engine.TILE_WIDTH);
}
g.drawImage(tileSheet,
((x * Engine.TILE_WIDTH)*scale)+position,
((y * Engine.TILE_HEIGHT)*scale),
(((x * Engine.TILE_WIDTH) + Engine.TILE_WIDTH )*scale)+position,
(((y * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT)*scale),
index * Engine.TILE_WIDTH,
yOffset * Engine.TILE_HEIGHT,
(index * Engine.TILE_WIDTH) + Engine.TILE_WIDTH,
(yOffset * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT,
null
);
}
}
//This code is actually longer(included file later on)
Colour hit detection is too slow and inconsistent for multi coloured tiles
Since the map is moving I suppose I need to move the hit detection boxes with it. As for selecting the boxes that it should detect might be difficult. Maybe it would be a better idea to make the code NOT hit detect certain tiles.
My attempts have ended in obfuscation of code. Can anyone suggest the easiest way to implement the hit detection? (keep in mind I have jumping).
The important codes are listed below:
Board.java(The panel where everything is drawn)
package EvilMario; //Include this class in the EvilMario game package
import java.awt.*; //Imported to allow use of Image
import java.awt.event.*; //Imported to allow use of ActionListener
import javax.swing.*; //Import swing
public class Board extends JPanel implements ActionListener { //Class Board
private TileLayer l; //Instance of TileLayer class
private Menu m; //Instance of menu class
private Player p; //Instance of player class
Timer time; //A timer
public static enum STATE {MENU,GAME}; //The game states
public static STATE State = STATE.MENU; //Set the first state to menu
//END
//GLOBAL
//DECLARATIONS
public Board() {
l = TileLayer.FromFile("D:/ICS3U1/EvilMario/map.txt"); //Tile map data from .txt file
this.addMouseListener(new MouseInput()); //Listen for mouse input
this.addKeyListener(new AL()); //Listen for key input
p = new Player(); //Start running Player class
m = new Menu(); //Start running Menu class
setFocusable(true); //Allows movement
time = new Timer(20,this); //Timer set to update "this" class every 20 milliseconds(Approximately 50fps)
time.start(); //Actually start the timer
}
public void actionPerformed(ActionEvent e) {
p.move(); //Call the move method from the player class
repaint(); //Repaint
}
public void paintComponent(Graphics g) { //Graphics method
super.paintComponent(g); //Super hero?
Graphics2D g2d = (Graphics2D) g; //Cast 2D graphics
if(State==STATE.GAME) {
if(p.distanceTraveled<300)l.DrawLayer(g,0);else l.DrawLayer(g, -(p.distanceTraveled-300)); //Draw the tile map
g2d.drawImage(p.getImage(), p.getX(), p.getY(), 48, 48, null); //Draw the player
if(p.distanceTraveled==3488) System.out.println("You have won the game!"); //Draw the end game screen
} else {
m.render(g); //Render the menu
}
}
private class AL extends KeyAdapter { //Action Listener extends key adapter
public void keyPressed(KeyEvent e) { //On key press
p.keyPressed(e); //Send whatever key was pressed TO the keyPressed method in the player class
}
public void keyReleased(KeyEvent e) { //On key release
p.keyReleased(e); //Send whatever key was released TO the keyReleased method in the player class
}
}
}
Player.java(player logic)
package EvilMario; //Include this class in the EvilMario game package
import java.awt.Image;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
public class Player {
int x, dx, y, distanceTraveled; //x coordinate,change in x coordinate,y coordinate,1st rep bg,2nd rep bg,dist traveled
Image player; //The player variable
ImageIcon walk_L_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_L_anim.gif");
ImageIcon walk_L_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_L_idle.png");
ImageIcon walk_R_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_R_anim.gif");
ImageIcon walk_R_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/walk_R_idle.png");
ImageIcon jump_L_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_L_anim.gif");
ImageIcon jump_L_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_L_idle.png");
ImageIcon jump_R_anim = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_R_anim.gif");
ImageIcon jump_R_idle = new ImageIcon("D:/ICS3U1/EvilMario/images/animatedMario/jump_R_idle.png");
boolean holdingLeft = false;
boolean holdingRight = false;
static boolean jumping = false;
static boolean falling = false;
static int jumpingTime = 350;
public Player() {
player = walk_R_idle.getImage(); //Give the player the image
x = 75; //The original x position of the player
y = 277; //The original y position of the player
distanceTraveled = 75; //Original distance traveled
}
public void move() {
if(x>=0 && x<=300) { //If the player is within the moving area
x = x+dx; //The x position is updated to become itself+the amount you moved
}
if(x<0) //If the player has reached he very left side of the screen(0px)
x=0; //Move him up a pixel so he can move again
if(x>300) //If the player has reached the center of the screen(300px)
x=300; //Move him down a pixel so he can move again
distanceTraveled=distanceTraveled+dx; //Calculate distanceTraveled
if(distanceTraveled<0) //Make sure distanceTraveled isn't a negative
distanceTraveled=0; //Make sure distanceTraveled isn't a negative
if(distanceTraveled>=300) //Keep player at center position once past 300 mario meters
x=300; //Keep player at center position once past 300 mario meters
if(holdingLeft && !holdingRight) {
if(distanceTraveled<300)dx=-5; else dx=-4;
if(jumping && !falling) {
player = jump_L_anim.getImage();
y-=8;
} else {
player = walk_L_anim.getImage();
if(y<277)
y+=8;
}
} else if(holdingRight && !holdingLeft) {
if(distanceTraveled<300)dx=5; else dx=4;
if(jumping && !falling) {
player = jump_R_anim.getImage();
y-=8;
} else {
player = walk_R_anim.getImage();
if(y<277)
y+=8;
}
} else if(!holdingRight && !holdingLeft) {
dx = 0;
if(jumping && !falling) {
player = jump_R_anim.getImage();
y-=8;
} else {
if(y<277)
y+=8;
}
}
if(y==277) {
falling = false;
}
System.out.println("LEFT: "+holdingLeft+" JUMP: "+jumping+" RIGHT: "+holdingRight+" FALLING: "+falling+" Y: "+y);
}
public int getX() { return x; } //This method will return the x. Is used by other classes
public int getY() { return y; } //This method will return the y. Is used by other classes
public Image getImage() { return player; } //This method will return the player. Is used by other classes
public void keyPressed(KeyEvent e) { //Called from the board class, the argument is whatever key was pressed
int key = e.getKeyCode(); //The key originally sent from the board class
if(key == KeyEvent.VK_LEFT && !holdingLeft)
holdingLeft = true;
if(key == KeyEvent.VK_RIGHT && !holdingRight)
holdingRight = true;
if(key == KeyEvent.VK_UP && !jumping && !falling)
new Thread(new JumpThread(this)).start();
}
public void keyReleased(KeyEvent e) { //Called from the board class, the argument is whatever key was released
int key = e.getKeyCode(); //The key originally sent from the board class
if(key == KeyEvent.VK_LEFT) { //If the left or right key was released
dx = 0; //Stop moving
holdingLeft = false;
player = walk_L_idle.getImage();
}
if(key == KeyEvent.VK_RIGHT) {
dx = 0;
holdingRight = false;
player = walk_R_idle.getImage();
}
}
}
TileLayer.java (Rendering of the tile layer)(Probably most important part relating to the question)
package EvilMario; //Include this class in the EvilMario game package
import java.awt.Graphics; //
public class TileLayer {
private int[][] map; //2D array
private BufferedImage tileSheet; //The tile sheet
public TileLayer(int[][] existingMap) { //
map = new int[existingMap.length][existingMap[0].length]; //map initialized
for(int y=0;y<map.length;y++) { //Loop through all boxes
for(int x=0;x<map[y].length;y++) { //Loop through all boxes
map[y][x] = existingMap[y][x]; //Update the map
}
}
tileSheet = LoadTileSheet("D:/ICS3U1/EvilMario/images/tilemap.gif"); //Load the tilesheet
}
public TileLayer(int width, int height) {
map = new int[height][width];
}
public static TileLayer FromFile(String fileName) {
TileLayer layer = null;
ArrayList<ArrayList<Integer>> tempLayout = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
String currentLine;
while((currentLine = br.readLine()) !=null) {
if(currentLine.isEmpty())
continue;
ArrayList<Integer> row = new ArrayList<>();
String[] values = currentLine.trim().split(" ");
for(String string: values) {
if(!string.isEmpty()) {
int id = Integer.parseInt(string);
row.add(id);
}
}
tempLayout.add(row);
}
} catch(IOException e) {
System.out.println("ERROR");
}
int width = tempLayout.get(0).size();
int height = tempLayout.size();
layer = new TileLayer(width,height);
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
layer.map[y][x] = tempLayout.get(y).get(x);
}
}
layer.tileSheet = layer.LoadTileSheet("D:/ICS3U1/EvilMario/images/tilemap.gif");
return layer;
}
public BufferedImage LoadTileSheet(String fileName) {
BufferedImage img = null;
try {
img = ImageIO.read(new File(fileName));
} catch(Exception e) {
System.out.println("Could not load image");
}
return img;
}
int scale = 2;
public void DrawLayer(Graphics g, int position) {
for(int y=0;y<map.length;y++) {
for(int x=0;x<map[y].length;x++) {
int index = map[y][x];
int yOffset = 0;
if(index>(tileSheet.getWidth() / Engine.TILE_WIDTH) -1) {
yOffset++;
index = index - (tileSheet.getWidth() / Engine.TILE_WIDTH);
}
g.drawImage(tileSheet,
((x * Engine.TILE_WIDTH)*scale)+position,
((y * Engine.TILE_HEIGHT)*scale),
(((x * Engine.TILE_WIDTH) + Engine.TILE_WIDTH )*scale)+position,
(((y * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT)*scale),
index * Engine.TILE_WIDTH,
yOffset * Engine.TILE_HEIGHT,
(index * Engine.TILE_WIDTH) + Engine.TILE_WIDTH,
(yOffset * Engine.TILE_HEIGHT) + Engine.TILE_HEIGHT,
null
);
}
}
}
}
Engine.java (Not as important)(Simple variables for tile sizes)
package EvilMario;
public class Engine {
public static final int TILE_WIDTH = 16;
public static final int TILE_HEIGHT = 16;
}
If you need other pieces of code, just ask for them. I am not asking you to give me a specific answer to the question but simply a method that would work with my following code.
A specific answer would be nice though :)
I also believe the answer to this question will be useful to others because this method was explained in a popular java 2d game tutorial video(They never showed hit detection).
Methods I tried:
Creating a new java file called HitDetectionLayer with the exact code in TileLayer.java that stored positions in arrays. It failed :(
Ok, I'm not entirely sure what you are doing, if you throw up some images it would be more clear.
At any rate, 'hit detection' aka collision detection is a very complex topic, but it depends on what you want to do. If you want everything to be boxes or circles, then it is quite easy. If however you want things to rotate or you want collision for complex shapes it becomes extreme difficult.
Most games use circles or spheres for collision. You put the majority of your graphics (it may not fit perfectly either leaving part of your images in or out of the circle but that's life). Now lets say you have your mario sprite and one of those turtles. Well, you have circles around them both and once the circles touch you trigger your event.
The math for this is very easy because circles are by definition a perimeter around a constant length. Look at this:
You probably already know this, and it may seem obvious, but if you think about it this is what a circle really is: a consistent length in every fathomable direction. The directions are measured in degrees and from there you move on to trigonometry but you don't need that. What you need is coordinance aka vectors. So look at this:
All you need to determine circle collision is the distance between the circles. No matter what angle the circles collide from it does not matter because the distances from the circle's centre are consistent all the way around. Even if the circles are different sizes, it doesn't matter, just account for the radii difference.
Too compute all of this, you would write a method like this:
public boolean testDistanceBetween( float radius1, float radius2,
float x1, float x2, float y1, float y2 ){
double distanceBetween = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
if(distanceBetween < (radius1+radius2) ){
return true;
}
return false;
}
The moral of the story is that circles are just good that way. If you want to do rectangle collision you take the bottom-left and top right point and you test if other rectangles are in between those points. This should be pretty straight forward, each point is a vector, each rectangle has 4 points. If any of the 4 points of one rectangle are between points on the other rectangle, there is collision.
You can use this system to handle ground and walls also. For example, if ground is at Y=300, then if your sprite's y coordinance are == 300, you suspend your gravity.
The main thing I wanted to explain is that if you intend to have rotating rectangles or polygons and you want to detect collision on them... good luck. It can be done yes, but you should understand you are implementing complex physics, especially when/if you implement gravity.
So my answer is cautionary: there is NO easy way to detect collision of rotating rectangles or polygons. Circles and static rectangles are the limits. If you really want to do rotating rectangles/polygons get a physics engine. Box2d is pretty good and has a Java version Jbox2d.
I'm writing a code which changes the colour of an array cell to either black or white depending on what the original colour is. If the cell is white, it changes to black and vice versa. I'm working with the below code:
public class Grid {
boolean[][] grid;
private int Height;
private int Width;
public White;
public Black;
public Grid(int height, int width) {
Height = height;
Width = width;
}
public int getHeight() {
return Height;
}
public int getWidth() {
return Width;
}
public boolean isWhite(int i, int j) {
boolean [][] Grid = new boolean [Height][Width];
for ( i = 0; i<Height ; i++);{
for (j = 0; j<Width ; j++){
if (Grid[i][j] ?? //iswhite) {
??//setBlack
}
else (??)//setWhite
}
}
}}
protected void setWhite(int i, int j) {
??
}
protected void setBlack(int i, int j) {
??
}
}
I believe I'm supposed to create an instance for Grid[i][j] depending on the boolean value however I'm not sure how to actually relate 'public boolean isWhite' to 'setWhite' and 'setBlack'. How should I go about it?
You have already defined boolean grid[][] as a class variable. You'll need to set it up in your constructor with something along the lines of grid = new boolean[height][width]; I would recommend initializing all the values, just for sanity's sake. (i.e. set them all to true or false or some predefined pattern.)
Once you've done that, you can check grid[i][j] for its value, which will either be true or false. (Whichever of those you wish to represent black and white.)
For instance, your isWhite function is as simple as this (assuming white == true)
public boolean isWhite(int i, int j) {
return grid[i][j];
}
Similarly, the set functions are also relatively minimal:
void setWhite(int i, int j) {
grid[i][j] = true;
}
void setBlack(int i, int j) {
grid[i][j] = false;
}
Additional:
You would probably want to create some code that does the appropriate thing when flipping colors rather than placing it inside the isWhite function. (It should be testing for color, not changing the values, right?)
void changeColorAtYX(int y, int x){
if(isWhite(y, x)){
setBlack(y, x);
} else {
setWhite(y,x);
}
}
1) You should probably initialize your array in your constructor. Since it is an array of primitives it will default to false in every position. If this is not what you want, set to true as needed.
2) isWhite, judging from the name, should probably just return the value at the given indices.
3) For the set methods, assuming that false == black, just set the value at the appropriate position to true or false depending on whether you want it white or black.