I want to make a 10x10 grid and put the robot in position (10,1) (bottom left). I want this robot to be able to move forward, turn left/right and to pick up/put objects in a grid. When put in any position, there should be number in a grid which shows how many objects is put in this position, just like this:
..........
...1......
..2.......
....3.....
..........
..........
......9...
.....4....
.........1
..........
We will not see the robot in a grid. I have two classes. Class Robot:
public class Robot {
private Area area;
private Robot rob;
public Robot(Area area){
this.area = area;
rob = new Robot(area);
}
public void Right(){
}
public void Left(){
}
public void Forward(){
}
public void Put(){
}
public void PickUp(){
}
public (?) getPosition(){ // should return robot's position
}
}
Class Area:
private int numberOfObjects;
private Robot robot;
private static final int X = 10;
private static final int Y = 10;
private Object [][] area; // grid
public Area(){ // defines a grid and robot
area = new Area[X][Y];
for(int a=0;a<X;a++){
for(int b=0;b<Y;b++)
area[a][b]=".";
}
numberOfObjects = 0; // grid is initially empty
Area ar = new Area();
robot = new Robot(ar);
}
public void Put(int x,int y){ // put the object to position (x,y)
area[x][y]=numberOfObjects++;
}
public void PickUp(int x,int y){ // pick up the object in position (x,y)
if(area[x][y]!=null){
area[x][y]=numberOfObjects--;
}
}
public void PrintAGrid(){
for(int r=0;r<X;r++){
for(int c=0;c<Y;c++)
System.out.print(area[r][c]+" ");
System.out.println();
}
System.out.println();
}
}
How can I put the robot in position (10,1)? How can I declare and set its orientation (i.e. on the right)? I guess it will be easy to write other methods, so I do not focus on it.
There are several issues with your code.
Why do you have an instance of Robot inside the class Robot? You have not used that instance at all!
private Object [][] area; should be int[][] area. You always save int in this, right?
If I understand your requirements correctly, Your implementation of pick and put is not correct.
Here is a help how you can solve the problems. I had to think several times if Robot should be in Grid or it should be the other way. I ended up with Grid in Robot.
May be Grid could be a singleton.
Here is our Grid
public class Grid {
private int[][] numberOfObjects = new int[10][10];
public void put(int x, int y) {
numberOfObjects[y][x]++;
}
public void pick(int x, int y) {
numberOfObjects[y][x]--;
}
}
You can replace parameters int x, int y with a Point.
And here is the robot
public class Robot {
private static final int NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3;
private int direction;
private int x, y;
private Grid grid;
public Robot(Grid grid) {
this.x = 0;
this.y = 0;
this.grid = grid;
direction = NORTH;
}
public void right() {
direction++;
if (direction == 4) {
direction = 0;
}
}
public void left() {
direction--;
if (direction == -1) {
direction = 3;
}
}
public void forward() {
if (direction == NORTH) {
y--;
} else if (direction == SOUTH) {
y++;
} else if (direction == EAST) {
x++;
} else if (direction == WEST) {
x--;
}
}
public void put() {
grid.put(x, y);
}
public void pick() {
grid.pick(x, y);
}
}
You need to represent the curent location with a variable and initialize it to the 10 1 postion, though your array goes 0-9 and 0-9 so this may be 9,0. to store this position maybe try a Point object that contains a Point x,y.
If someone is interested in a JavaScript version, you can have a look at this repo right here. In general:
The robot must have a facing direction (left, up, right, down).
The are three possible commands: left, right, move.
With that being said, the algorithm is quite straightforward:
totalScore = 0
Foreach i in input
computeCurrentDirection()
if input != MOVE: continue
totalScore += i
return totalScore
There are several sweet-tricks that someone might do to optimize the functions. Take a look at switchDirection.
const directionArray = [Directions.RIGHT, Directions.DOWN, Directions.LEFT, Directions.UP];
const switchDirection = (currDirection, command) => {
if (command === Commands.MOVE) {
return currDirection
}
const currDirectionIndex = directionArray.indexOf(currDirection);
if (command === Commands.RIGHT) {
return directionArray[(currDirectionIndex + 1) % 4];
}
return directionArray[((currDirectionIndex - 1) + 4) % 4];
}
Instead of an exhaustive approach, someone might use an array to help compute the upcoming direction of the robot. This significantly reduces the amount of needed code.
Note this implementation can be easily expanded to accommodate any new requirements needed for project expansion. When faced with such questions, try to architect your codebase in a testable and expandable way, because it's usually the case where reviewers are interested in your coding organizational skills, rather than whether you are able to solve the problem or not.
Related
I'm trying to do a quite easy boardgame (Carcassonne) but I'm having a lot of troubles with the graphic interface. The problem is that I don't see the way to make a relation between the mouse clicks and the gridpane row and columns.
The first tile is given and it's always added to the 100, 100 gridpanes position. You won't see it in the code, but for each tile added if the adjacents are empty it's added a white tile, so it looks like this:
Then, the player is expected to do a legal move ( we're not controlling cheaters, so yeah, it will be a weak game ) in the positions x = 99 y = 100, x = 101 y = 100, x = 100 y = 99, x = 100 y = 101.
But when i click there using the method play() the e.getSceneX(); method returns me the pixel position, and I need a way to convert it to a valid row index. So this is happening:
This is the console output:
tile XCCCC added at 100 100 // this is always given by the program, it's always the same
tile MFFCF added at 391 380
In this case, I clicked to the x = 100 y = 101 gridpane but the mouse returned me the pixel 391, 380.
Any idea?
This is the structure of my code:
public final class GUI extends Application {
private Game _game;
private GridPane _visualBoard;
#Override
public void start(final Stage primaryStage) {
// some stuff setting the gridpane, which will be inside a scrollpane and the scrollpane will be inside a borderpane which will alse have 2 additional VBoxes with the current turn information
play();
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
public void play() {
_visualBoard.setOnMousePressed((MouseEvent e) -> {
double x = e.getSceneX();
double y = e.getSceneY();
_game.doMove(x, y);
});
}
public void insertTile(myTile r, int x, int y) {
myVisualTile rV = new myVisualTile(r);
_visualBoard.add(rV, x, y);
}
And this is the class game:
public class Game {
private GUI _gui;
private List<Player> _players; // An arraylist of players
private int _currentPlayer; // The index of the current player in the ArrayList
private Board _board; // The logical board, totally separeted from the gridpane
private tileStack _stack;
public void doMove(double x, double y) {
int ax = (int) x;
int ay = (int) y;
if (_stack.isEmpty()) {
System.out.println("Game finnished");
//stuff
}
else {
myTile r = _stack.poll(); // takes the first tile of the stack
_gui.insertTile(r, ax, ay);
}
}
public void play() {
_visualBoard.setOnMousePressed((MouseEvent e) -> {
Node source = (Node)e.getTarget() ;
Integer x = GridPane.getColumnIndex(source);
Integer y = GridPane.getRowIndex(source);
_game.doMove(x, y);
});
}
That actualy worked. Thanks everybody!
I am currently working on a project called Rectangle project in which I am supposed to do the following on Java:
Make the following methods:
setOrigin
area
move
Also make a method that determines if two rectangles intersect and returns a new intersection Rectangle. Test all your methods in the ObjectDemo program for the following rectangles:
A: Origin 0,0: width 10: height 20
B: Origin 5,5: width 15, height 15
C: Origin 20,12: width 10: height 20
What is the area of each? Test if each of them intersect with the other two and what is the intersection area. Move A by 5,5; B by -5,-5: and C by -20, 0. Now give the intersection area of each.
I need to finish this by Monday but I keep getting a ton of errors like unrecognized variables, etc., and I'm not sure how to fix them. Please let me know!
I have three files: Point, RectangleTest, and Rectangle.
Here are their codes:
Point code:
public class Point
{
//Class variables
private int xCoord; //Private (instead of Public) because we are going to use this class in the other file
//We don't want people changing the values unless we let them
private int yCoord; //Variables are not in a function so will maintain their value
//Constructor
Point()
{
xCoord = 0;
yCoord = 0;
}
//Constructor
Point(int startX, int startY)
{
xCoord = startX;
yCoord = startY;
}
public int getX()
{
return xCoord;
}
public int getY()
{
return yCoord;
}
public void setX(int newX)
{
xCoord = newX;
}
public void setY(int newY)
{
yCoord = newY;
}
public void move(int moveX, int moveY)
{
xCoord+=moveX;
yCoord+=moveY;
}
Point(Point p)
{
xCoord = p.getX();
yCoord = p.getY();
}
}
RectangleTest Code:
public class RectangleTest
{
public static void main(String [] args)
{
Rectangle A = new Rectangle(0,0,10,20);
Rectangle B = new Rectangle(5,5,15,15);
Rectangle C = new Rectangle(20,12,10,20);
//Move rectangles
A.moveby(5,10);
B.moveby(-5,-5);
C.moveby(-20,0);
int areaA = A.getarea;
System.out.println("The area of rectangle A is " +areaA);
int areaB = B.getarea;
System.out.println("The area of rectangle B is " +areaB);
int areaC = C.getarea;
System.out.println("The area of rectangle C is " +areaC);
Rectanlge iAB = A.intersect(B);
Rectangle iAC = A.intersect(C);
Rectangle iBC = B.intersect(C);
if(iab != null)
{
System.out.println("The area of intersection rectangle iab = " +iAB.area());
}
if(iac != null)
{
System.out.println("The area of intersection rectangle iac = " +iAC.area());
}
if (ibc != null)
{
System.out.println("The area of intersection area ibc = " +iBC.area());
}
}
}
Rectangle Code:
public class Rectangle
{
Point origin;
int height;
int width;
//Constructor for rectangle object
Public Rectangle(int startX, int startY, int startW, int startH)
{
origin = new Point (startX, startY);
width = startW;
height = startH;
}
//Set origin point for NEW rectangle origins
//FIX
public void setOrigin(int newX, int newY)
{
origin.setX(newX);
origin.setY(newY);
}
public int moveBy(int moveX, int moveY)
{
origin.move(moveX, moveY);
}
public int getArea()
{
int recArea = height*width;
return recArea;
}
public Rectangle intersect(Rectangle testR)
{
int meTRX = origin.getX() + width;
int meTRY = origin.getY() + height;
int testTRX = testR.origin.getX() + width;
int testTRY = testR.origin.getY() + height;
//Boolean to get iTRX
if(meTRX>testTRX)
{
int iTRX = testTRX;
}
else
{
int iTRX = meTRX;
}
//Boolean to get iTRY
if(meTRY>testTRY)
{
int iTRY = testTRY;
}
else
{
int iTRY = meTRY;
}
//Boolean to get iBLX
if(testBLX>meBLX)
{
int iBLX = testBLX;
}
else
{
int iBLX = meBLX;
}
//Boolean to get iBLY
if(testBLY>meBLY)
{
int iBLY = testBLY;
}
else
{
int iBLY = meBLY;
}
//Testing for whether or not there is an intersection rectangle
if(iTRX-iBLX<0 || iTRY-iBLY<0)
{
return null;
}
int iH = iTRY - iBLY;
int iW = iTRX - iBLX;
int intersectArea = iH * iW;
}
}
Please point out any problems! I'm rather new to programming, so I usually make a lot of simple mistakes. Also, I would appreciate if there are no newly introduced commands or anything because my teacher is pretty strict about doing it this way.
Thanks!
P.S. I would appreciate any extra knowledge or info on code improvement (just in general). Thanks!
Couple of Issues:
Java is case sensitive so Public is not same as public in your rectangle class.
When your method doesnt return anything you should use void as return type. So in your method public int moveBy(int moveX, int moveY), you should change it to public void moveBy(int moveX, int moveY)
You need to define variables before using them. So variables like testBLX, meBLX, testBLY, meBLY, iTRX, iTRY, iBLX, iBLY are undefined. I am not sure from where the values will get populated. But you could avoid the compilation error by defining them as int testBLX = 0; and similarly the others.
In your Rectangle class:
In the constructor your wrote Public Rectangle(int startX, int startY, int startW, int startH), but you actually want public Rectangle(int startX, int startY, int startW, int startH). In Java keywords start always with a lower case.
Your method for changing the origin of a rectangle public int moveBy(int moveX, int moveY) has int as a return type, so the compiler wants you to return an integer value. I suppose you did not want to return anything at all so you can change the return type to void.
In your intersect method public Rectangle intersect(Rectangle testR) you declare your variables (iTRX, iTRY, iBLX, iBLY) such as int iTRX = testTRX; only in the scope of your if/else statements which means that after every if/else statement these variables are not available anymore. To learn more about the different scopes of variables: Variable scopes
In your RectangleTest class:
You forgot a part of your task: What is the area of each? Test if each of them intersect with the other two and what is the intersection area.
Some general leads:
The use of more descriptive variable names improves the readability. For example the variable name meTRX does not have any meaning for me as person who did not work on your code or maybe for you if you review your code two months later.
Before you start coding, you could check if Java has built-in classes which you can use. In your case Java provides a Point class in the package java.awt.Point. You do not have to reinvent the wheel.
I would also recommend to read the Java Code Conventions Code Conventions which can bring you and others who read your code on a common denominator in the future.
I have a project to have a red dot inside the first box of a maze i randomly generated and the dot is supposed to follow its way through the boxes and find the end of the maze. Now if it hits a dead end, its supposed to go back to where its path started and not go back down that path, that leads to a dead end. i made it so each box represents the #1, this way when the red dot travels over the box, it increments by 1, so it can realize where its been. its always supposed to go to the lowest number possible so it can never go back to the dead ends its already been to. i am able to reach the end of the maze but i come into 2 problems.
the method i wrote that does all this work is the solve() function. I cant understand why 2 things happen...
1st thing is that when the red dot comes to a branch of dead ends, sometimes itll just go to one dead end, to a different dead end , back to the same dead end.. traveling to the same 'numbers' when im trying to have it only go towards the boxes that have 1's or just the lower numbers.
2nd thing is that once it inevitably reaches the end of the maze.. the red dot goes into the green area, where i specifically say in the while loop, it can not be in a green box.
if M[y][x] = 0, its a green box and if its = 1 its a black box. anything higher than 1 will also be inside the box.
your help is highly appreciated as ive been stuck on this problem for hours and cant seem to find out the problem.
the problem persists in the solve() method
import java.awt.*;
import java.awt.event.*;
import java.awt.Graphics;
import javax.swing.*;
public class mazedfs extends JFrame implements KeyListener
{
/* default values: */
private static int bh = 16; // height of a graphical block
private static int bw = 16; // width of a graphical block
private int mh = 41; // height and width of maze
private int mw = 51;
private int ah, aw; // height and width of graphical maze
private int yoff = 40; // init y-cord of maze
private Graphics g;
private int dtime = 40; // 40 ms delay time
byte[][] M; // the array for the maze
public static final int SOUTH = 0;
public static final int EAST = 1;
public static final int NORTH = 2;
public static final int WEST = 3;
public static boolean showvalue = true; // affects drawblock
// args determine block size, maze height, and maze width
public mazedfs(int bh0, int mh0, int mw0)
{
bh = bw = bh0; mh = mh0; mw = mw0;
ah = bh*mh;
aw = bw*mw;
M = new byte[mh][mw]; // initialize maze (all 0's - walls).
this.setBounds(0,0,aw+10,10+ah+yoff);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try{Thread.sleep(500);} catch(Exception e) {} // Synch with system
this.addKeyListener(this);
g = getGraphics(); //g.setColor(Color.red);
setup();
}
public void paint(Graphics g) {} // override automatic repaint
public void setup()
{
g.setColor(Color.green);
g.fill3DRect(0,yoff,aw,ah,true); // fill raised rectangle
g.setColor(Color.black);
// showStatus("Generating maze...");
digout(mh-2,mw-2); // start digging!
// digout exit
M[mh-1][mw-2] = M[mh-2][mw-1] = 1;
drawblock(mh-2,mw-1);
solve(); // this is the function you will write for parts 1 and 2
play(); // for part 3
}
public static void main(String[] args)
{
int blocksize = bh, mheight = 41, mwidth = 41; // need to be odd
if (args.length==3)
{
mheight=Integer.parseInt(args[0]);
mwidth=Integer.parseInt(args[1]);
blocksize=Integer.parseInt(args[2]);
}
mazedfs W = new mazedfs(blocksize,mheight,mwidth);
}
public void drawblock(int y, int x)
{
g.setColor(Color.black);
g.fillRect(x*bw,yoff+(y*bh),bw,bh);
g.setColor(Color.yellow);
// following line displays value of M[y][x] in the graphical maze:
if (showvalue)
g.drawString(""+M[y][x],(x*bw)+(bw/2-4),yoff+(y*bh)+(bh/2+6));
}
void drawdot(int y, int x)
{
g.setColor(Color.red);
g.fillOval(x*bw,yoff+(y*bh),bw,bh);
try{Thread.sleep(dtime);} catch(Exception e) {}
}
/////////////////////////////////////////////////////////////////////
/* function to generate random maze */
public void digout(int y, int x)
{
M[y][x] = 1; // digout maze at coordinate y,x
drawblock(y,x); // change graphical display to reflect space dug out
int dir = (int)(Math.random()*4);
for (int i=0;i<4;i++){
int [] DX = {0,0,2,-2};
int [] DY = {-2,2,0,0};
int newx = x + DX[dir];
int newy = y + DY[dir];
if(newx>=0 && newx<mw && newy>=0 && newy<mh && M[newy][newx]==0)
{
M[y+DY[dir]/2][x+DX[dir]/2] = 1;
drawblock(y+DY[dir]/2,x+DX[dir]/2);
digout(newy,newx);
}
dir = (dir + 1)%4;}
} // digout
public void solve() // This is the method i need help with.
{
int x=1, y=1;
drawdot(y,x);
while(y!=mh-1 || x!=mw-1 && M[y][x]!=0){
int min = 0x7fffffff;
int DX = 0;
int DY = 0;
if (y-1>0 && min>M[y-1][x] && M[y-1][x]!=0){
min = M[y-1][x];
DX = 0;
DY = -1;
}//ifNORTH
if (y+1>0 && min>M[y+1][x] && M[y+1][x]!=0){
min = M[y+1][x];
DY = 1;
DX = 0;
}//ifSOUTH
if (x-1>0 && min>M[y][x-1] && M[y][x-1]!=0){
min = M[y][x-1];
DX = -1;
DY = 0;
}//ifWEST
if (x+1>0 && min>M[y][x+1] && M[y][x+1]!=0){
min = M[y][x+1];
DX = 1;
DY = 0;
}//ifEAST
M[y][x]++;
drawblock(y,x);
x = x+DX;
y = y+DY;
drawdot(y,x);
}//while
// modify this function to move the dot to the end of the maze. That
// is, when the dot reaches y==mh-2, x==mw-2
} // solve
///////////////////////////////////////////////////////////////
/// For part three (save a copy of part 2 version first!), you
// need to implement the KeyListener interface.
public void play() // for part 3
{
// code to setup game
}
// for part 3 you may also define some other instance vars outside of
// the play function.
// for KeyListener interface
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) // change this one
{
int key = e.getKeyCode(); // code for key pressed
System.out.println("YOU JUST PRESSED KEY "+key);
}
} // mazedfs
////////////
// define additional classes (stack) you may need here.
The issue causing the second problem you are facing (dot moving to green box) lies in the while loop conditiony!=mh-1 || x!=mw-1 && M[y][x]!=0 . The condition evaluates to y!=mh-1 ||(x!=mw-1 && M[y][x]!=0) since && has higher precedence over the || and || just needs one of its operand to be true. In your case, y!=mh-1 is still ture at the end of maze. Hence the loop continues and the dot moves into green area. To fix the issue modify the condition as (y!=mh-1 || x!=mw-1) && M[y][x]!=0. Hope this helps.
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 am trying to add and remove balls into an array, I'm only allowed to use an array (not an arraylist).
I'm having trouble with the add and remove ball methods and keep getting an " insert "AssignmentOperator Expression" " error on length for add and remove ball methods. Not sure how to fix the problem.
Help appreciated.
private final int DIAMETER = 60;
private java.awt.Color _currentColor;
//private SmartEllipse _ball;
SmartEllipse [] myBalls = new SmartEllipse [25];
public BallPanel () {
super();
this.setBackground(java.awt.Color.white);
myBalls = new SmartEllipse[0];
_currentColor = java.awt.Color.red;
this.addMouseListener(new MyMouseListener());
}
//translate the click coordinates to a grid coordinate
public java.awt.Point translatePoint(java.awt.Point aPoint)
{
int x = aPoint.x;
int y = aPoint.y;
aPoint.x = x / 20;
aPoint.y = y / 20;
return aPoint;
}
//find a ball in the array
public int findBall(java.awt.Point p)
{
for (SmartEllipse se : myBalls )
{
java.awt.Point translatedPoint = translatePoint(se.getLocation());
if (translatedPoint.x == p.x && translatedPoint.y == p.y)
return myBalls.length;
}
return -1;
}
// remove a ball from the array
public void removeBall(SmartEllipse p)
{
myBalls.length-1;
this.repaint();
this.revalidate();
}
//add a peg to the array
public void addBall(SmartEllipse p)
{
myBalls.length;
this.repaint();
this.revalidate();
}
public void paintComponent(java.awt.Graphics aBrush)
{
super.paintComponent(aBrush);
java.awt.Graphics2D betterBrush = (java.awt.Graphics2D) aBrush;
for (SmartEllipse se : myBalls )
{
se.draw(betterBrush);
se.fill(betterBrush);
}
}
private class MyMouseListener extends javax.swing.event.MouseInputAdapter
{
public void mouseClicked(MouseEvent e)
{
// get X and Y of click
// translate X and Y to grid
// check peg array for duplicate
// if duplicate, remove ball
// else add ball to ball array
java.awt.Point p = translatePoint(e.getPoint());
// Adjust the coordinates
int index = findBall(p);
for (int i = 0; i< myBalls.length; i++)
{
myBalls[i] = null;
if ( index != -1)
remove(myBalls.length);
else
addBall(new SmartEllipse(_currentColor, p.x, p.y));
}
}
}
}
I'm not sure I understand your question since you don't specify very well where you're getting the error, but it looks as if you are declaring myBalls as an array of 25 elements, but then in the constructor your are assigning it a new array of zero elements. You can't add anything to an array of zero elements.