using keyEvent from different classes - java

im trying to put restrictions on movement if my chicken reaches the corner here is my code for my move method
public void move1(){
if(((x<=350) && (y>=350)) || ((x>=450) && (y>=350))){
if(dy<0){
dy=0;
}
}
if(((x>=350) && (y<=350)) || ((x<=450) && (y<=350))){
if(dx<0 || dx>0){
dx=0;
}
}
if(((y<=250) && (x<=350)) || ((y<=250) && (x>=450))){
if(dy>0){
dy=0;
}
}
if (x >= 750){
x = 750;
}
if (y >= 575){
y = 575;
}
x += dx;y += dy;
}
i used a key adapter for the movement of my chicken but its from a different class
public void keyPressed(KeyEvent e) {
chick.keyPressed(e);
}
the animation is perfectly fine but my chicken wont respond the keyevent

I would strongly recommend you to have a look at KeyBindings, they are more accustomed to such situations, which are more concerned about focus related issues. This post regarding Motion Using the Keyboard, might will surely interest you, on the topic concern :-)
EDIT :
Here a small program from help :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class KeyBindingExample
{
private DrawingBoard contentPane;
/*
* There variables will simply
* decide how much the square
* will move with click key press,
* in this case I have set this to
* 1 (inside the constructor).
* brakes will simply tell whether
* the square will move or not in
* a given direction.
*/
private int speed;
private int brakes;
public KeyBindingExample() {
speed = 5;
brakes = 0;
}
private void displayGUI()
{
JFrame frame = new JFrame("Swing Worker Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
contentPane = new DrawingBoard(10, 10, Color.BLUE.darker());
addBindingsToBoard();
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private void addBindingsToBoard() {
/*
* Since, when UP Arrow is pressed, that means, the square
* can move vertically upwards, hence, the square will move
* along Y-Axis that too in the negative direction of the
* same, though along X-Axis the square will move nowhere,
* hence, we passing 0 and -1, since we want to add the
* current location (say square is at present at 50, 50),
* now after UP Arrow key event, square will be at (50, 49);
*/
putBindingsFor(contentPane, KeyStroke.getKeyStroke("UP"),
"UP Arrow Key", brakes, -speed);
/*
* When RIGHT Arrow is pressed, the square is suppose to
* move horizontally, along the X-Axis, in the positive
* direction towards the RIGHT. Hence +1 change along X-Axis
* and no change along Y-Axis, i.e. from (50, 49), the square
* will now move to (51, 49), that's why we passing (+1, 0)
*/
putBindingsFor(contentPane, KeyStroke.getKeyStroke("RIGHT"),
"RIGHT Arrow Key", speed, brakes);
/*
* When DOWN Arrow is pressed, the square is suppose to
* move vertically, along the Y-Axis, in the positive
* direction towards the BOTTOM. Hence no change along X-Axis
* and +1 change along Y-Axis, i.e. from (51, 49), the square
* will now move to (51, 50), that's why we passing (0, +1)
*/
putBindingsFor(contentPane, KeyStroke.getKeyStroke("DOWN"),
"DOWN Arrow Key", brakes, +speed);
/*
* When LEFT Arrow is pressed, the square is suppose to
* move horizontally, along the X-Axis, in the negative
* direction towards the LEFT side. Hence -1 change along X-Axis
* and no change along Y-Axis, i.e. from (51, 50), the square
* will now move to (50, 50), that's why we passing (-1, 0).
* The square will atlast come to it's initial position.
*/
putBindingsFor(contentPane, KeyStroke.getKeyStroke("LEFT"),
"LEFT Arrow Key", -speed, brakes);
}
private void putBindingsFor(JComponent comp,
KeyStroke keyStroke, String value, final int moveX, final int moveY) {
comp.getInputMap().put(keyStroke, value);
comp.getActionMap().put(value, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
contentPane.setValues(moveX, moveY);
}
});
}
public static void main(String[] args)
{
Runnable runnable = new Runnable()
{
#Override
public void run()
{
new KeyBindingExample().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
class DrawingBoard extends JPanel {
private int x;
private int y;
private int width;
private int height;
private Color rectColor;
public DrawingBoard(int x, int y, Color rColor) {
setOpaque(true);
this.x = x;
this.y = y;
rectColor = rColor;
width = height = 10;
}
public void setValues(int deltaX, int deltaY) {
System.out.format("Firstly X : %d\tY : %d%n", x, y);
repaint(x, y, width, height);
/*
* Whatever values are passed from above, i.e.
* say on Left ARROW, we passing (-1, 0),
* therefore deltaX will be -1 and deltaY will
* be 0. Now whatever the current value of X is
* we are simply adding deltaX to that value
* and the same goes for deltaY as well.
* Now since the value for x and y is updated
* after these two statements below, we checking
* that whether these two updated values lies
* within the bounds of our board or not.
* If they are, then we simply calling repaint,
* to draw the square at this new location, else
* we simply bring back the previous values of
* x and y to their previous state.
*/
x += deltaX;
y += deltaY;
if ((x + width) <= getWidth() && (y + height) <= getHeight()
&& x >= 0 && y >= 0) {
System.out.format("Later X : %d\tY : %d%n", x, y);
repaint(x, y, width, height);
}
else {
x -= deltaX;
y -= deltaY;
System.out.format("Later X : %d\tY : %d%n", x, y);
repaint(x, y, width, height);
}
}
/*
* Make this a customary habbit of overridding
* this method whenever you have to override
* any JComponent, instead of calling
* setPreferredSize(...).
*/
#Override
public Dimension getPreferredSize() {
return (new Dimension(100, 100));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(rectColor);
g.fillRect(x, y, width, height);
}
}

Related

Generating correct co-ordinates of current screen resolution based off of design resolution

I have a problem that I haven't been able to completely understand and thus I am struggling to fix it.
Basically I am busy writing a small game engine for Java Swing, and one of the key components of this engine is the ability to separate design resolution from screen resolution. Meaning if I design a game on a resolution of 400 (w) x 300 (h), and I position an object at the center of the design resolution, then a user can specify the actual resolution they want to play the game at for example 800 (w) x 600 (h) and the object will still be placed correctly at the center of the screen in the current resolution.
This is where I am having trouble, when the design resolution and the current resolution are the same i.e. design resolution 400 x 300 and current resolution is 400 x 300, the object seems to be placed correctly at the center of the screen on start up and the bullet correctly at the center of the player regardless of the players position when moved:
However when the design resolution and current screen resolution are not the same i.e. design resolution 400 x 300 and current resolution is 800 x 600 the object is no longer correctly placed at center of the screen and neither is the bullet centered for the player:
I have a method to generate the center spawn point for all visible objects (the red reference dot, the sprite/player and the bullet) this method is a simple convenience method to help generate a center based coordinate for a Sprite within a container or another Sprite:
public static Point2D getCenterSpawnPoint(int parentWidth, int parentHeight, int childWidth, int childHeight, double childXOffset, double childYOffset) {
double spawnX = ((parentWidth - childWidth) / 2) + childXOffset;
double spawnY = ((parentHeight - childHeight) / 2) + childYOffset;
return new Point2D.Double((int) spawnX, (int) spawnY);
}
The Sprite and bullet render using screen coordinates:
public int getScreenX() {
//return (int) (imageScaler.getWidthScaleFactor() * this.getX());
return (int) ((double) this.getX() / DESIGN_SCREEN_SIZE.width * CURRENT_SCREEN_SIZE.width);
}
public int getScreenY() {
//return (int) (imageScaler.getHeightScaleFactor() * this.getY());
return (int) ((double) this.getY() / DESIGN_SCREEN_SIZE.height * CURRENT_SCREEN_SIZE.height);
}
I am unsure of where I am going wrong, but essentially what Id want to see is the same behavior in my first GIF regardless of the current screen size the game is in, the red reference dot seems to position correctly and it is simply drawn to the JPanel and bypasses the getScreen... calls:
// lets draw a centered dot based on the panels dimensions for a reference
int dotSize = 10;
g2d.setColor(Color.red);
Point2D centeredReferencePoint = getCenterSpawnPoint(getWidth(), getHeight(), dotSize, dotSize, 0, 0);
g2d.fillOval((int) centeredReferencePoint.getX(), (int) centeredReferencePoint.getY(), dotSize, dotSize);
Here is the minaml reproducible example:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
public class ResolutionIndependentLocationIssue {
/**
* uncommenting this and commenting the line below will result in the bullet
* spawning correctly at the center of the sprite/player
*/
private static final Dimension CURRENT_SCREEN_SIZE = new Dimension(800, 600);
//private static final Dimension CURRENT_SCREEN_SIZE = new Dimension(400, 300);
private static final Dimension DESIGN_SCREEN_SIZE = new Dimension(400, 300);
private Scene scene;
private Sprite player;
public ResolutionIndependentLocationIssue() {
try {
createAndShowUI();
} catch (IOException ex) {
Logger.getLogger(ResolutionIndependentLocationIssue.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(ResolutionIndependentLocationIssue::new);
}
private void createAndShowUI() throws MalformedURLException, IOException {
JFrame frame = new JFrame("Resolution Issue");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage bulletImage = resize(ImageIO.read(new URL("https://i.stack.imgur.com/JlSEL.png")), 20, 20);
BufferedImage playerImage = resize(ImageIO.read(new URL("https://icons.iconarchive.com/icons/icons8/windows-8/512/Programming-Java-Duke-Logo-icon.png")), 100, 100);
player = new Sprite(playerImage);
player.setBulletImage(bulletImage);
System.out.println();
// center player according to our design resolution
Point2D spawnPoint = getCenterSpawnPoint(DESIGN_SCREEN_SIZE.width, DESIGN_SCREEN_SIZE.height, playerImage.getWidth(), playerImage.getHeight(), 0, 0);
player.setPosition((int) spawnPoint.getX(), (int) spawnPoint.getY());
System.out.println("ResolutionScalingIssue#createAndShowUI() - Player spawn point (always expressed in design resolution co-ordinates): X: " + spawnPoint.getX() + " Y: " + spawnPoint.getY());
System.out.println("ResolutionScalingIssue#createAndShowUI() - Player Design Resolution X: " + player.getX() + " Y: " + player.getY());
System.out.println("ResolutionScalingIssue#createAndShowUI() - Player Screen X: " + player.getScreenX() + " Screen Y: " + player.getScreenY());
System.out.println("ResolutionScalingIssue#createAndShowUI() - Player Width: " + playerImage.getWidth() + " Height: " + playerImage.getHeight());
System.out.println();
this.scene = new Scene();
this.scene.add(player);
this.addKeyBindings();
frame.add(this.scene);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Thread gameLoop = new Thread(() -> {
while (true) {
this.scene.update();
this.scene.repaint();
try {
Thread.sleep(15);
} catch (InterruptedException ex) {
}
}
});
gameLoop.start();
}
private void addKeyBindings() {
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "A pressed");
this.scene.getActionMap().put("A pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.LEFT = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "A released");
this.scene.getActionMap().put("A released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.LEFT = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "D pressed");
this.scene.getActionMap().put("D pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.RIGHT = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "D released");
this.scene.getActionMap().put("D released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.RIGHT = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "W pressed");
this.scene.getActionMap().put("W pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.UP = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "W released");
this.scene.getActionMap().put("W released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.UP = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "S pressed");
this.scene.getActionMap().put("S pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.DOWN = true;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "S released");
this.scene.getActionMap().put("S released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.DOWN = false;
}
});
this.scene.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "Space pressed");
this.scene.getActionMap().put("Space pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
player.shoot();
}
});
}
public static BufferedImage resize(BufferedImage image, int width, int height) {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
return bi;
}
/**
* Used to calculate the center based spawning point, to ensure calculations
* are the same for the player spawning on the screen and bullet spawning
* from the player
*
* #return
*/
public static Point2D getCenterSpawnPoint(int parentWidth, int parentHeight, int childWidth, int childHeight, double childXOffset, double childYOffset) {
double spawnX = ((parentWidth - childWidth) / 2) + childXOffset;
double spawnY = ((parentHeight - childHeight) / 2) + childYOffset;
return new Point2D.Double((int) spawnX, (int) spawnY);
}
public class Scene extends JPanel {
private final ArrayList<Sprite> sprites;
public Scene() {
this.sprites = new ArrayList<>();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
sprites.forEach((sprite) -> {
sprite.render(g2d);
});
// lets draw a centered dot based on the panels dimensions for a reference
int dotSize = 10;
g2d.setColor(Color.red);
Point2D centeredReferencePoint = getCenterSpawnPoint(getWidth(), getHeight(), dotSize, dotSize, 0, 0);
g2d.fillOval((int) centeredReferencePoint.getX(), (int) centeredReferencePoint.getY(), dotSize, dotSize);
}
#Override
public Dimension getPreferredSize() {
return CURRENT_SCREEN_SIZE;
}
#Override
public boolean getIgnoreRepaint() {
return true;
}
public void add(Sprite sprite) {
sprite.setScence(this);
this.sprites.add(sprite);
}
private void update() {
sprites.forEach((sprite) -> {
sprite.update();
});
}
}
public class Sprite {
protected int x;
protected int y;
protected int speed = 5;
protected final BufferedImage image;
public boolean UP, DOWN, LEFT, RIGHT;
private boolean isFlippedX = false;
private Scene scene;
private BufferedImage bulletImage;
public Sprite(BufferedImage image) {
this.image = image;
}
public void render(Graphics2D g2d) {
// sprite is drawn based on the position of the current screen relative to our design screen size
g2d.setColor(Color.red);
g2d.drawRect(this.getScreenX(), this.getScreenY(), this.getWidth(), this.getHeight());
if (this.isFlippedX) {
// flip horizontally
g2d.drawImage(this.image, this.getScreenX() + this.image.getWidth(), this.getScreenY(), -this.getWidth(), this.getHeight(), null);
} else {
g2d.drawImage(this.image, this.getScreenX(), this.getScreenY(), null);
}
}
public void update() {
if (LEFT) {
setFlippedX(true);
this.x -= this.speed;
}
if (RIGHT) {
setFlippedX(false);
this.x += this.speed;
}
if (UP) {
this.y -= this.speed;
}
if (DOWN) {
this.y += this.speed;
}
}
public void setFlippedX(boolean isFlippedX) {
this.isFlippedX = isFlippedX;
}
/**
*
* #return The current screen x co-ordindate of the sprite relative to
* the design resolution
*/
public int getScreenX() {
//return (int) (imageScaler.getWidthScaleFactor() * this.getX());
return (int) ((double) this.getX() / DESIGN_SCREEN_SIZE.width * CURRENT_SCREEN_SIZE.width);
}
/**
*
* #return The current screen y co-ordindate of the sprite relative to
* the design resolution
*/
public int getScreenY() {
//return (int) (imageScaler.getHeightScaleFactor() * this.getY());
return (int) ((double) this.getY() / DESIGN_SCREEN_SIZE.height * CURRENT_SCREEN_SIZE.height);
}
/**
*
* #return The design resolution x co-ordindate
*/
public int getX() {
return this.x;
}
/**
*
* #return The design resolution y co-ordindate
*/
public int getY() {
return this.y;
}
public int getWidth() {
return this.image.getWidth();
}
public int getHeight() {
return this.image.getHeight();
}
public void setPosition(int x, int y) {
this.x = x;
this.y = y;
}
public void setBulletImage(BufferedImage bulletImage) {
this.bulletImage = bulletImage;
}
public void shoot() {
System.out.println("Sprite#shoot() - Player Design Resolution X: " + this.getX() + " Y: " + this.getY());
System.out.println("Sprite#shoot() - Player Width: " + this.getWidth() + " Height: " + this.getHeight());
/**
* center the bullet according to the players design x and y
* co-ordinates, this is necessary as x and y should the design
* co-ordinates and render method will call getScreenX and
* getScreenY to calculate the current screen resolution
* co-ordinates
*
*/
Point2D spawnPoint = getCenterSpawnPoint(this.getWidth(), this.getHeight(), bulletImage.getWidth(), bulletImage.getHeight(), this.getX(), this.getY());
Bullet bullet = new Bullet((int) spawnPoint.getX(), (int) spawnPoint.getY(), this.bulletImage);
System.out.println("Sprite#shoot() - Bullet spawn point (always expressed in design resolution co-ordinates): X: " + spawnPoint.getX() + " Y: " + spawnPoint.getY());
System.out.println("Sprite#shoot() - Bullet spawn: X: " + bullet.getX() + " Y: " + bullet.getY());
System.out.println("Sprite#shoot() - Bullet spawn: Screen X: " + bullet.getScreenX() + " Screen Y: " + bullet.getScreenY());
System.out.println();
//bullet.LEFT = this.isFlippedX;
//bullet.RIGHT = !this.isFlippedX;
this.scene.add(bullet);
}
public void setScence(Scene scene) {
this.scene = scene;
}
}
public class Bullet extends Sprite {
public Bullet(int x, int y, BufferedImage image) {
super(image);
this.x = x;
this.y = y;
this.speed = 10;
}
}
}
Any help would be greatly appreciated!
UPDATE:
When using the solution by #akuzminykh all seems to work fine, however, now when I set the players position to something like player.setPosition(0,0), expecting it to appear in the top left corner, I get this instead:
which makes sense as I assume we are now positioning via the coordinate being at the center of the sprite, but how would I fix his so both setPosition for the top left corner and center would work, I think I might need to fix the getCenterSpawnPoint?
In your methods getScreenX and getScreenY you are ignoring that getX and getY include the width and height of the sprite. E.g. getX doesn't give you the center position of the sprite in the x-axis, but the position minus half of the sprite's width. When you scale this like you do in getScreenX, then you also scale the offset in x for the sprite. To solve this, simply add the offset initially, do the scaling and subtract the offset finally.
/**
*
* #return The current screen x co-ordindate of the sprite relative to
* the design resolution
*/
public int getScreenX() {
//return (int) (imageScaler.getWidthScaleFactor() * this.getX());
//return (int) ((double) this.getX() / DESIGN_SCREEN_SIZE.width * CURRENT_SCREEN_SIZE.width);
double halfWidth = this.getWidth() / 2.0;
double xCenterDesign = this.getX() + halfWidth;
double xCenterCurrent = xCenterDesign / DESIGN_SCREEN_SIZE.width * CURRENT_SCREEN_SIZE.width;
return (int) (xCenterCurrent - halfWidth);
}
/**
*
* #return The current screen y co-ordindate of the sprite relative to
* the design resolution
*/
public int getScreenY() {
//return (int) (imageScaler.getHeightScaleFactor() * this.getY());
//return (int) ((double) this.getY() / DESIGN_SCREEN_SIZE.height * CURRENT_SCREEN_SIZE.height);
double halfHeight = this.getHeight() / 2.0;
double yCenterDesign = this.getY() + halfHeight;
double yCenterCurrent = yCenterDesign / DESIGN_SCREEN_SIZE.height * CURRENT_SCREEN_SIZE.height;
return (int) (yCenterCurrent - halfHeight);
}
Or more mathematically:
If we take your example with 400x300 in "design" resolution, 800x600 being the "current" resolution and the sprite being 100x100 big: The position of the sprite is (150, 100), which makes sense: (400 / 2 - 100 / 2, 300 / 2 - 100 / 2). Now the formula you've used to bring it in "current" resolution (only for x because I'm lazy): 150 / 400 * 800 = 300. Hm, but half of 800 is 400 and the position should be 400 - 100 / 2? Exactly, the offset 100 / 2 for the sprite got scaled as well, from 50 to 100, which results in .. 400 - 100 = 300.
Therefore, add the offset back initially, so you scale the center. Then it's: (150 + 50) / 400 * 800 = 400. Don't forget to finally subtract the offset: 400 - 50 = 350. Now you have the correct position in the x-axis.
Re: UPDATE:
When you want to put the sprite in the top left corner, you might expect player.setPosition(0, 0) to do the trick. This is not the case. The way you've written it, the coordinates given by getX and getY include the width and height of the sprite, remember? Methods like getScreenX and getScreenY, with my fix, consider that and are used to render the sprite at the correct position. That means the coordinates (0, 0) describe the position of the center to be at (0 + 50, 0 + 50), where 50 is just 100 / 2, the width and height of the sprite divided by two.
To place the sprite in the top left corner, you need to consider the sprite's width and height when setting its position using the method setPosition: In our example, where the sprite is 100x100 big, you need to pass (0 - 100 / 2, 0 - 100 / 2), so the call looks like this: player.setPosition(-50, -50). You can of course make it dynamic by using playerImage.getWidth() and so on and so on.
Suggestion:
I suggest you to let x and y of Sprite to be relative to the center of the corresponding sprite. This will make some changes to the code necessary but it will also simplify other things and make them more intuitive. E.g. the problem with player.setPosition(0, 0) won't exist, it will actually put the sprite at the top left corner, exactly what you'd intuitively expect. This will also simplify getScreenX and getScreenY. Consider the offsets caused by the sprite's width and height just in the render method. This should be enough.

simple KeyListenerDemo.java example: Adding more keystrokes issue

Please excuse the possible simiplicity of this question. I'm revisiting Java to teach it to younger kids and also never had much experience with Graphics in the past. I also don't have an IDE available to run this code or debug at the moment, so I'm wondering if someone could help me understand conceptually what is the correct code I should write to extend the program.
There is a sample program below which should run fine. I just need to extend it by adding a few more keystroke capabilities(like stated in the program instructions/comments) as well as make sure the ball goes to the edge of the screen but is still completely visible.
Please let me know if I'm adding the extra keystroke capabilities correctly or how close or far I am. I was thinking of adding the following lines of code in the keyPressed(KeyEvent e) method.
...
else if(keyCode == KeyEvent.VK_Z)
{
g.fillOval(x + radius, y + radius, 2 * radius, 2 * radius);
}
else if(keyCode == KeyEvent.VK_S)
{
g.fillOval(x - radius, y - radius, radius, radius);
}
else if(keyCode == KeyEvent.VK_B)
{
g.fillOval(x - radius, y - radius, 4 * radius, 4 * radius);
}
else if(keyCode == KeyEvent.VK_C)
{
g.setColor(Color.green);
}
.
I'm not completely sure how the code within the if-else blocks (above and below) should be in order to update the characteristics of the ball. Part of the reason is because I may not have a firm understanding of repaint() and paint(Graphics g). Any insights or tips are much appreciated.
.
import java.awt.*;
import java.awt.event.*; // #1
import javax.swing.*;
/******************************************************************************
*
* KeyListenerDemo.java
* Demonstrates getting keyboard input using the KeyListener interface.
*
* Program 18: Extend this program by adding a few more keystroke commands:
* z (VK_Z) - Cause the ball to jump to a random new location.
* s (VK_S) - Make the ball smaller - multiply its diameter 1/2.
* b (VK_B) - Make the ball bigger - multiply its diameter by 2.
* c (VK_C) - Change the color (in any way you'd like).
*
* In addition, modify the program to ensure the following:
* - The ball goes all the way to the edge of the screen but stays
* completely on the screen.
* - If a doubled diameter doesn't fit, make it as large as possible.
* - Be sure the ball never completely disappears.
*
*****************************************************************************/
public class KeyListenerDemo extends JFrame
implements KeyListener // #2
{
// Class Scope Finals
private static final int SCREEN_WIDTH = 1000;
private static final int SCREEN_HEIGHT = 800;
private static final int START_RADIUS = 25;
private static final int START_X = 100;
private static final int START_Y = 100;
private static final int STEP_SIZE = 10;
// Class Scope Variables
private static int x = START_X; // x at center of the ball
private static int y = START_Y; // y at center of the ball
private static int radius = START_RADIUS; // radius of the ball
// Methods
/**
* Create the window and register this as a KeyListener
*
* #param args
*/
public static void main (String[] args)
{
// Set up the JFrame window.
KeyListenerDemo gp = new KeyListenerDemo();
gp.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
gp.setVisible(true);
gp.addKeyListener(gp); // #3
// If this class had a constructor and you moved this line into
// that constructor it could not refer to gp since that variable
// is local to this method. Instead you would write::
// addKeyListener(this);
}
/**
* Called when a key is first pressed
* Required for any KeyListener
*
* #param e Contains info about the key pressed
*/
public void keyPressed(KeyEvent e) // #4A
{
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT)
{
x = x - STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_RIGHT)
{
x = x + STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_UP)
{
y = y - STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_DOWN)
{
y = y + STEP_SIZE;
}
repaint();
}
/**
* Called when typing of a key is completed
* Required for any KeyListener
*
* #param e Contains info about the key typed
*/
public void keyTyped(KeyEvent e) // #4B
{
}
/**
* Called when a key is released
* Required for any KeyListener
*
* #param e Contains info about the key released
*/
public void keyReleased(KeyEvent e) // #4C
{
}
/**
* paint - draw the figure
*
* #param g Graphics object to draw in
*/
public void paint(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
g.setColor(Color.blue);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
}
That seems correct at a glance (though I also do not work with graphics frequently).
Note that if you were to release the key your ovals may disappear from the screen at the next redraw.
You can create a simple Oval wrapper class to store details like the x, y, width, and height, and put those Ovals into a list that is an instance variable of your KeyListenerDemo.
Your if-blocks would be something akin to:
else if(keyCode == KeyEvent.VK_Z)
{
Oval o = new Oval(x + radius, y + radius, 2 * radius, 2 * radius);
ovals.add(o); // where ovals is an ArrayList<Oval> or LinkedList<Oval>
g.fillOval(o.x, o.y, o.width, o.height);
}
At the public void paint(Graphics g) method you'd loop through the list and (re)draw those ovals.
Edit: I'd implement the oval class like:
public class Oval {
int x;
int y;
int width;
int height;
public Oval(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
The list is simply declared as protected List<Oval> ovals = new LinkedList<>();, right above your main method. To loop over it (in your paint method, where g is available, you can use:
for(Oval o : ovals){
g.fillOval(o.x, o.y, o.width, o.height);
}
Hopefully this helps!

How do I add a keystroke inside of keyPressed(KeyEvent e) method to change color of ball? simple KeyListener demo

I have limited Java experience, especially when it relates to graphics. I've had very good help on this problem so far. It's starter code (below) which works fine as I tested it in Eclipse. I'm using it from a class to teach a high school student. As per the instructions below in comments, would someone know an easy way extend the already simple program to change the color of the Ball after pressing the button C? I'm thinking of adding the following code to the keyPressed(KeyEvent e) method:
else if(keyCode == KeyEvent.VK_C)
{
//Not sure what code to add here
//g.setColor(Color.green); ----> this line says "g can not be resolved".
}
Any tips or ideas to keep the program simple would be very much appreciated. Thank you.
.
import java.awt.*;
import java.awt.event.*; // #1
import javax.swing.*;
/******************************************************************************
*
* KeyListenerDemo.java
* Demonstrates getting keyboard input using the KeyListener interface.
*
* Program 18: Extend this program by adding a few more keystroke commands:
* z (VK_Z) - Cause the ball to jump to a random new location.
* s (VK_S) - Make the ball smaller - multiply its diameter 1/2.
* b (VK_B) - Make the ball bigger - multiply its diameter by 2.
* c (VK_C) - Change the color (in any way you'd like).
*
* In addition, modify the program to ensure the following:
* - The ball goes all the way to the edge of the screen but stays
* completely on the screen.
* - If a doubled diameter doesn't fit, make it as large as possible.
* - Be sure the ball never completely disappears.
*
*****************************************************************************/
public class KeyListenerDemo extends JFrame
implements KeyListener // #2
{
// Class Scope Finals
private static final int SCREEN_WIDTH = 1000;
private static final int SCREEN_HEIGHT = 800;
private static final int START_RADIUS = 25;
private static final int START_X = 100;
private static final int START_Y = 100;
private static final int STEP_SIZE = 10;
// Class Scope Variables
private static int x = START_X; // x at center of the ball
private static int y = START_Y; // y at center of the ball
private static int radius = START_RADIUS; // radius of the ball
// Methods
/**
* Create the window and register this as a KeyListener
*
* #param args
*/
public static void main (String[] args)
{
// Set up the JFrame window.
KeyListenerDemo gp = new KeyListenerDemo();
gp.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
gp.setVisible(true);
gp.addKeyListener(gp); // #3
// If this class had a constructor and you moved this line into
// that constructor it could not refer to gp since that variable
// is local to this method. Instead you would write::
// addKeyListener(this);
}
/**
* Called when a key is first pressed
* Required for any KeyListener
*
* #param e Contains info about the key pressed
*/
public void keyPressed(KeyEvent e) // #4A
{
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT)
{
x = x - STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_RIGHT)
{
x = x + STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_UP)
{
y = y - STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_DOWN)
{
y = y + STEP_SIZE;
}
repaint();
}
/**
* Called when typing of a key is completed
* Required for any KeyListener
*
* #param e Contains info about the key typed
*/
public void keyTyped(KeyEvent e) // #4B
{
}
/**
* Called when a key is released
* Required for any KeyListener
*
* #param e Contains info about the key released
*/
public void keyReleased(KeyEvent e) // #4C
{
}
/**
* paint - draw the figure
*
* #param g Graphics object to draw in
*/
public void paint(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
g.setColor(Color.blue);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
}
private Color currentColor;
...
public void paint(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
g.setColor(currrentColor);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
...
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT)
{
x = x - STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_RIGHT)
{
x = x + STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_UP)
{
y = y - STEP_SIZE;
}
else if (keyCode == KeyEvent.VK_DOWN)
{
y = y + STEP_SIZE;
}else currentColor = Color.BLUE;
repaint();
}
You should probably do something like this.
You dont have the 'g ' graphics variable in the key press event so you get an un resolved variable error from compilor. Need to store the color to draw in a variable and use it in the paint method.
Have a class instance variable that is the color. Initially it will be
colorBall = Color.white
then on clicking C if its white make it green and vice versa.
In paint simply use the variable to color
g.setColor(colorBall );

Beginner Java Question - "Breakout" game. What's my next step?

This is my first post on SO!
I've on my own been working a few weeks through Stanford's "Programming Methodology" class which is an intro to programming with java. I've been doing all the programs so far with little difficulty, researching what I needed with minimal difficulty.
Right now, all I have are a set of bricks, and a ball that I was able to get to bounce off the walls. Currently the ball doesn't do anything but bounce around in the canvas and just passes through the bricks.
There are lots of steps involved, the other ones I am pretty sure I can take care of. The ones I am having a hard time with are...
1) Get the ball to bounce off the bricks.
2) Get the bricks to disappear when the ball bounces off them.
some resources I've been using -
Using the ACM Graphics Package
Stanford PDF with the assignment guidelines
I guess my question is. What do I need to understand in order to be able to solve the problems I listed above. In one of the lectures, the professor talks about using "getElementAt()" But I don't really understand how that method works or how I can use it to get my ball to bounce off the bricks and then after, make them disappear.
Code I wrote so far -
/*
* File: Breakout.java
* -------------------
* Name: Sandi
* Section Leader: I'm learning this online
*
* This file will eventually implement the game of Breakout.
*/
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Breakout extends GraphicsProgram {
/** Width and height of application window in pixels */
public static final int APPLICATION_WIDTH = 400;
public static final int APPLICATION_HEIGHT = 600;
/** Dimensions of game board (usually the same) */
private static final int WIDTH = APPLICATION_WIDTH;
private static final int HEIGHT = APPLICATION_HEIGHT;
/** Dimensions of the paddle */
private static final int PADDLE_WIDTH = 60;
private static final int PADDLE_HEIGHT = 10;
/** Offset of the paddle up from the bottom */
private static final int PADDLE_Y_OFFSET = 30;
/** Number of bricks per row */
private static final int NBRICKS_PER_ROW = 10;
/** Number of rows of bricks */
private static final int NBRICK_ROWS = 10;
/** Separation between bricks */
private static final int BRICK_SEP = 4;
/** Width of a brick */
private static final int BRICK_WIDTH = (WIDTH - (NBRICKS_PER_ROW - 1)
* BRICK_SEP)
/ NBRICKS_PER_ROW;
/** Height of a brick */
private static final int BRICK_HEIGHT = 8;
/** Radius of the ball in pixels */
private static final int BALL_RADIUS = 10;
/** Offset of the top brick row from the top */
private static final int BRICK_Y_OFFSET = 70;
/** Number of turns */
private static final int NTURNS = 3;
/* Method: run() */
/** Runs the Breakout program. */
public void run() {
addBricks();
addBall();
//velocity of the ball's x and y
ballVX = 1;
ballVY = 2;
while (true) {
moveBall();
pause(10);
}
}
private void addBricks() {
int startY = BRICK_Y_OFFSET;
GRect brick;
for (int i = 0; i < NBRICK_ROWS; i++) {
int startX = BRICK_SEP;
for (int j = 0; j < NBRICKS_PER_ROW; j++) {
brick = new GRect(startX, startY, BRICK_WIDTH, BRICK_HEIGHT);
add(brick);
colorBricks(brick, Color.RED, 0, 1);
colorBricks(brick, Color.ORANGE, 2, 3);
colorBricks(brick, Color.YELLOW, 4, 5);
colorBricks(brick, Color.GREEN, 6, 7);
colorBricks(brick, Color.CYAN, 8, 9);
startX += BRICK_WIDTH + BRICK_SEP;
}
startY += BRICK_HEIGHT + BRICK_SEP;
}
}
private void colorBricks(GRect brick, Color color, int fromRowNumber,
int toRowNumber) {
//this will make it so that if the bricks are between two y coordinates
//then the method will set it to a certain color and color it.
if (brick.getY() >= BRICK_Y_OFFSET + fromRowNumber
* (BRICK_HEIGHT + BRICK_SEP)
&& brick.getY() <= BRICK_Y_OFFSET + toRowNumber
* (BRICK_HEIGHT + BRICK_SEP)) {
brick.setColor(color);
brick.setFilled(true);
}
}
private void addBall() {
ball = new GOval(getWidth() / 2, getHeight() / 2, BALL_RADIUS,
BALL_RADIUS);
ball.setFilled(true);
add(ball);
}
private void moveBall() {
double borderX = ball.getX();
double borderY = ball.getY();
if (borderX == 0 || borderX == getWidth() - BALL_RADIUS) {
ballVX = -ballVX;
}
if (borderY == 0 || borderY == getHeight() - BALL_RADIUS) {
ballVY = -ballVY;
}
ball.move(ballVX, ballVY);
}
private GOval ball;
private double ballVX;
private double ballVY;
}
Thanks guys!
There are of course many things that could be done to enhance your program. But to solve your next problem I think you have to understand a little bit of the "magic" of the classes you are depend on.
I havn't studied your API in depth, but it looks like your call add(brick); adds your brick to an array/collection internally stored in GraphicsProgram (if you can get the source code of the classes you depend on it might be helpfull to read and understand the code). You can access the objects you have added later on with getElementAt. So if you add a brick at the location (10,20) and later call getElementAt(10,20) it should return this brick.
You should modify your moveBall method to check if the new position of the ball would contain a brick and, if so, act accordingly. Something like this:
private void moveBall() {
double borderX = ball.getX();
double borderY = ball.getY();
GRect brick = getElementAt(borderX, borderY);
if (brick != null) {
doSomethingWithBall();
} else {
if (borderX == 0 || borderX == getWidth() - BALL_RADIUS) {
ballVX = -ballVX;
}
if (borderY == 0 || borderY == getHeight() - BALL_RADIUS) {
ballVY = -ballVY;
}
}
ball.move(ballVX, ballVY);
}
You might have to fiddle a little bit with the values you pass into getElementAt and what exactly you do when you find a brick. You have to take into account that the bricks are not points but have a height and a width, the ball is moving and not a point, too ...
Edit: Your ball is added to the collection, too. So it might be possible that the ball is returned instead of a brick. You should not pass the actual position of your ball to the getElementAt method but an value where the ball is after it moves half the ball radius when there is no brick in the way.
private GObject getCollidingObject() {
double x = ball.getX(), y = ball.getY();
if (getElementAt(x, y) != null) { return getElementAt(x, y); }
else if (getElementAt(x, y + BALL_RADIUS * 2) != null) {
return getElementAt(x, y + BALL_RADIUS * 2); }
else if (getElementAt(x + BALL_RADIUS * 2, y + BALL_RADIUS * 2) != null) {
return getElementAt(x + BALL_RADIUS * 2, y + BALL_RADIUS * 2); }
else if (getElementAt(x + BALL_RADIUS * 2, y) != null) {
return getElementAt(x + BALL_RADIUS * 2, y); }
else { return null; }
}
... these two methods works perfectly together ...
private void elementsCollisions() {
GObject collider = getCollidingObject();
if (collider == paddle) {
vy = -vy;
}
else if (collider != null) {
remove(collider);
vy = -vy;
}
getElementAt(x, y) - returns object at the specified point. Decompose problem as much as possible if you can't solve fast.
Index the bricks in a way that you can easily check if a brick exists at a certain coordinate.
When you create a brick, you add it to the container, and then you lose it. You don't have a reference to it going forward that you can use to compare to the current ball location.
Based on your design, I believe you should do this through a 2-dimensional array. When you create the bricks, also put the GRect object in a 2-dimensional array. One dimension should be the row, and the other the column. Then you will be able to access these bricks by x and y coordinate using this array. (This is what you will need to do to detect a bounce.)
When the ball moves, the same way that you detect it bouncing off walls, check if the ball's position makes it border a spot where a brick could be. If it does, from any direction, then check if the brick is actually there by looking at your array. If there is a brick there, bounce the ball and remove the brick. Also be sure to set that location in the array to be null to indicate that the brick is gone.
Then you also have your victory condition... when all of the elements of the array have been set to null.

Collision Detection between two images in Java

I have two characters displayed in a game I am writing, the player and the enemy. defined as such:
public void player(Graphics g) {
g.drawImage(plimg, x, y, this);
}
public void enemy(Graphics g) {
g.drawImage(enemy, 200, 200, this);
}
Then called with:
player(g);
enemy(g);
I am able to move player() around with the keyboard, but I am at a loss when trying to detect a collision between the two. A lot of people have said to use Rectangles, but being a beginner I cannot see how I would link this into my existing code. Can anyone offer some advice for me?
I think your problem is that you are not using good OO design for your player and enemies. Create two classes:
public class Player
{
int X;
int Y;
int Width;
int Height;
// Getters and Setters
}
public class Enemy
{
int X;
int Y;
int Width;
int Height;
// Getters and Setters
}
Your Player should have X,Y,Width,and Height variables.
Your enemies should as well.
In your game loop, do something like this (C#):
foreach (Enemy e in EnemyCollection)
{
Rectangle r = new Rectangle(e.X,e.Y,e.Width,e.Height);
Rectangle p = new Rectangle(player.X,player.Y,player.Width,player.Height);
// Assuming there is an intersect method, otherwise just handcompare the values
if (r.Intersects(p))
{
// A Collision!
// we know which enemy (e), so we can call e.DoCollision();
e.DoCollision();
}
}
To speed things up, don't bother checking if the enemies coords are offscreen.
First, use the bounding boxes as described by Jonathan Holland to find if you may have a collision.
From the (multi-color) sprites, create black and white versions. You probably already have these if your sprites are transparent (i.e. there are places which are inside the bounding box but you can still see the background). These are "masks".
Use Image.getRGB() on the mask to get at the pixels. For each pixel which isn't transparent, set a bit in an integer array (playerArray and enemyArray below). The size of the array is height if width <= 32 pixels, (width+31)/32*height otherwise. The code below is for width <= 32.
If you have a collision of the bounding boxes, do this:
// Find the first line where the two sprites might overlap
int linePlayer, lineEnemy;
if (player.y <= enemy.y) {
linePlayer = enemy.y - player.y;
lineEnemy = 0;
} else {
linePlayer = 0;
lineEnemy = player.y - enemy.y;
}
int line = Math.max(linePlayer, lineEnemy);
// Get the shift between the two
x = player.x - enemy.x;
int maxLines = Math.max(player.height, enemy.height);
for ( line < maxLines; line ++) {
// if width > 32, then you need a second loop here
long playerMask = playerArray[linePlayer];
long enemyMask = enemyArray[lineEnemy];
// Reproduce the shift between the two sprites
if (x < 0) playerMask << (-x);
else enemyMask << x;
// If the two masks have common bits, binary AND will return != 0
if ((playerMask & enemyMask) != 0) {
// Contact!
}
}
Links: JGame, Framework for Small Java Games
You don't want to have the collision check code inside the painting code. The painting needs to be fast. Collision can go in the game loop. Therefore you need an internal representation of the objects independent of their sprites.
Here's the main class from my collision detection program.
You can see it run at: http://www.youtube.com/watch?v=JIXhCvXgjsQ
/**
*
* #author Tyler Griffin
*/
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.GraphicsDevice.*;
import java.util.ArrayList;
import java.awt.Graphics;
import java.awt.geom.Line2D;
public class collision extends JFrame implements KeyListener, MouseMotionListener, MouseListener
{
ArrayList everything=new ArrayList<tile>();
int time=0, x, y, width, height, up=0, down=0, left=0, right=0, mouse1=0, mouse2=0;
int mouseX, mouseY;
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice screen = environment.getDefaultScreenDevice();
DisplayMode displayMode = screen.getDisplayMode();
//private BufferStrategy strategy;
JLayeredPane pane = new JLayeredPane();
tile Tile;
circle Circle;
rectangle Rectangle;
textPane text;
public collision()
{
setUndecorated(screen.isFullScreenSupported());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setLayout(null);
setResizable(false);
screen.setFullScreenWindow(this);
width=displayMode.getWidth();
height=displayMode.getHeight();
Circle=new circle(-(int)Math.round((double)height/7*2),-(int)Math.round((double)height/7*2),(int)Math.round((double)height/7*.85),this);
Rectangle=new rectangle(-(int)Math.round((double)height/7*1.5),-(int)Math.round((double)height/7*1.5),(int)Math.round((double)height/7*1.5),(int)Math.round((double)height/7*1.5),this);
Tile=Circle;
Tile.move(mouseX-Tile.width/2, mouseY-Tile.height/2);
text=new textPane(0,0,width,height,this);
everything.add(new circle((int)Math.round((double)width/100*75),(int)Math.round((double)height/100*15),(int)Math.round((double)width/100*10),this));
everything.add(new rectangle((int)Math.round((double)width/100*70),(int)Math.round((double)height/100*60),(int)Math.round((double)width/100*20),(int)Math.round((double)height/100*20),this));
//everything.add(new line(750,250,750,750,this));
/*everything.add(new line(width/700*419,height/700*68,width/700*495,height/700*345,this));
everything.add(new line(width/700*495,height/700*345,width/700*749,height/700*350,this));
everything.add(new line(width/700*749,height/700*350,width/700*549,height/700*519,this));
everything.add(new line(width/700*549,height/700*519,width/700*624,height/700*800,this));
everything.add(new line(width/700*624,height/700*800,width/700*419,height/700*638,this));
everything.add(new line(width/700*419,height/700*638,width/700*203,height/700*800,this));
everything.add(new line(width/700*203,height/700*800,width/700*279,height/700*519,this));
everything.add(new line(width/700*279,height/700*519,width/700*76,height/700*350,this));
everything.add(new line(width/700*76,height/700*350,width/700*333,height/700*345,this));
everything.add(new line(width/700*333,height/700*345,width/700*419,height/700*68,this));
everything.add(new line(width/950*419,height/700*68,width/950*624,height/700*800,this));
everything.add(new line(width/950*419,height/700*68,width/950*203,height/700*800,this));
everything.add(new line(width/950*76,height/700*350,width/950*624,height/700*800,this));
everything.add(new line(width/950*203,height/700*800,width/950*749,height/700*350,this));
everything.add(new rectangle(width/950*76,height/700*350,width/950*673,1,this));*/
everything.add(new line((int)Math.round((double)width/1350*419),(int)Math.round((double)height/1000*68),(int)Math.round((double)width/1350*624),(int)Math.round((double)height/1000*800),this));
everything.add(new line((int)Math.round((double)width/1350*419),(int)Math.round((double)height/1000*68),(int)Math.round((double)width/1350*203),(int)Math.round((double)height/1000*800),this));
everything.add(new line((int)Math.round((double)width/1350*76),(int)Math.round((double)height/1000*350),(int)Math.round((double)width/1350*624),(int)Math.round((double)height/1000*800),this));
everything.add(new line((int)Math.round((double)width/1350*203),(int)Math.round((double)height/1000*800),(int)Math.round((double)width/1350*749),(int)Math.round((double)height/1000*350),this));
everything.add(new rectangle((int)Math.round((double)width/1350*76),(int)Math.round((double)height/1000*350),(int)Math.round((double)width/1350*673),1,this));
addKeyListener(this);
addMouseMotionListener(this);
addMouseListener(this);
}
public void keyReleased(KeyEvent e)
{
Object source=e.getSource();
int released=e.getKeyCode();
if (released==KeyEvent.VK_A){left=0;}
if (released==KeyEvent.VK_W){up=0;}
if (released==KeyEvent.VK_D){right=0;}
if (released==KeyEvent.VK_S){down=0;}
}//end keyReleased
public void keyPressed(KeyEvent e)
{
Object source=e.getSource();
int pressed=e.getKeyCode();
if (pressed==KeyEvent.VK_A){left=1;}
if (pressed==KeyEvent.VK_W){up=1;}
if (pressed==KeyEvent.VK_D){right=1;}
if (pressed==KeyEvent.VK_S){down=1;}
if (pressed==KeyEvent.VK_PAUSE&&pressed==KeyEvent.VK_P)
{
//if (paused==0){paused=1;}
//else paused=0;
}
}//end keyPressed
public void keyTyped(KeyEvent e){}
//***********************************************************************************************
public void mouseDragged(MouseEvent e)
{
mouseX=(e.getX());
mouseY=(e.getY());
//run();
}
public void mouseMoved(MouseEvent e)
{
mouseX=(e.getX());
mouseY=(e.getY());
//run();
}
//***********************************************************************************************
public void mousePressed(MouseEvent e)
{
if(e.getX()==0 && e.getY()==0){System.exit(0);}
mouseX=(e.getX()+x);
mouseY=(e.getY()+y);
if(Tile instanceof circle)
{
Circle.move(0-Circle.width, 0-Circle.height);
Circle.setBounds(Circle.x, Circle.y, Circle.width, Circle.height);
Tile=Rectangle;
}
else
{
Rectangle.move(0-Rectangle.width, 0-Rectangle.height);
Rectangle.setBounds(Rectangle.x, Rectangle.y, Rectangle.width, Rectangle.height);
Tile=Circle;
}
Tile.move(mouseX-Tile.width/2, mouseY-Tile.height/2);
}
public void mouseReleased(MouseEvent e)
{
//run();
}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
//***********************************************************************************************
public void run()//run collision detection
{
while (this == this)
{
Tile.move(Tile.x + ((mouseX - (Tile.x + (Tile.width / 2))) / 10), Tile.y + ((mouseY - (Tile.y + (Tile.height / 2))) / 10));
//Tile.move((mouseX - Tile.width / 2), mouseY - (Tile.height / 2));
for (int i = 0; i < everything.size(); i++)
{
tile Temp = (tile) everything.get(i);
if (Temp.x < (Tile.x + Tile.width) && (Temp.x + Temp.width) > Tile.x && Temp.y < (Tile.y + Tile.height) && (Temp.y + Temp.height) > Tile.y)//rectangles collided
{
if (Temp instanceof rectangle)
{
if (Tile instanceof rectangle){rectangleRectangle(Temp);}
else {circleRectangle(Temp);}//Tile instanceof circle
}
else
{
if (Temp instanceof circle)
{
if (Tile instanceof rectangle) {rectangleCircle(Temp);}
else {circleCircle(Temp);}
}
else//line
{
if (Tile instanceof rectangle){rectangleLine(Temp);}
else{circleLine(Temp);}
}
}
}//end if
}//end for
try {Thread.sleep(16L);}
catch (Exception e) {}
Tile.setBounds(Tile.x, Tile.y, Tile.width, Tile.height);
//Rectangle.setBounds(x, y, width, height);
//Circle.setBounds(x, y, width, height);
repaint();
text.out=" ";
}//end while loop
}//end run
//***************************************special collision detection/handling functions************************************************
void rectangleRectangle(tile Temp)
{
int lapTop, lapBot, lapLeft, lapRight, small, scootX=0, scootY=0;
lapTop=(Temp.y+Temp.height)-Tile.y;
lapBot=(Tile.y+Tile.height)-Temp.y;
lapLeft=(Temp.x+Temp.width)-Tile.x;
lapRight=(Tile.x+Tile.width)-Temp.x;
small=999999999;
if (lapTop<small){small=lapTop; scootX=0; scootY=lapTop;}
if (lapBot<small){small=lapBot; scootX=0; scootY=lapBot*-1;}
if (lapLeft<small){small=lapLeft; scootX=lapLeft; scootY=0;}
if (lapRight<small){small=lapRight; scootX=lapRight*-1; scootY=0;}
Tile.move(Tile.x+scootX, Tile.y+scootY);text.out="collision detected!";
}
void circleRectangle(tile Temp)
{
if((Tile.x+Tile.width/2<=Temp.x+Temp.width && Tile.x+Tile.width/2>=Temp.x)||(Tile.y+Tile.height/2>=Temp.y && Tile.y+Tile.height/2<=Temp.y+Temp.height))
{
rectangleRectangle(Temp);
}
else//push from nearest corner
{
int x,y;
if(Tile.x+Tile.width/2>Temp.x+Temp.width && Tile.y+Tile.height/2<Temp.y){x=Temp.x+Temp.width; y=Temp.y;}
else if(Tile.x+Tile.width/2<Temp.x && Tile.y+Tile.height/2<Temp.y){x=Temp.x; y=Temp.y;}
else if(Tile.x+Tile.width/2>Temp.x+Temp.width && Tile.y+Tile.height/2>Temp.y+Temp.height){x=Temp.x+Temp.width; y=Temp.y+Temp.height;}
else {x=Temp.x; y=Temp.y+Temp.height;}
double distance = Math.sqrt(Math.pow(Tile.x+(Tile.width/2) - x, 2) + Math.pow(Tile.y+(Tile.height/2) - y, 2));
if((int)Math.round(distance)<Tile.height/2)
{
double normY = ((Tile.y+(Tile.height/2) - y) / distance);
double normX = ((Tile.x+(Tile.width/2) - x) / distance);
Tile.move(x-Tile.width/2+(int)Math.round(normX*((Tile.width/2))) , y-Tile.height/2+(int)Math.round(normY*((Tile.height/2))));text.out="collision detected!";
}
}
}
void rectangleCircle(tile Temp)
{
if((Temp.x+Temp.width/2<=Tile.x+Tile.width && Temp.x+Temp.width/2>=Tile.x)||(Temp.y+Temp.height/2>=Tile.y && Temp.y+Temp.height/2<=Tile.y+Tile.height))
{
rectangleRectangle(Temp);
}
else//push from nearest corner
{
int x,y;
if(Temp.x+Temp.width/2>Tile.x+Tile.width && Temp.y+Temp.height/2<Tile.y){x=Tile.x+Tile.width; y=Tile.y;}
else if(Temp.x+Temp.width/2<Tile.x && Temp.y+Temp.height/2<Tile.y){x=Tile.x; y=Tile.y;}
else if(Temp.x+Temp.width/2>Tile.x+Tile.width && Temp.y+Temp.height/2>Tile.y+Tile.height){x=Tile.x+Tile.width; y=Tile.y+Tile.height;}
else {x=Tile.x; y=Tile.y+Tile.height;}
double distance = Math.sqrt(Math.pow(Temp.x+(Temp.width/2) - x, 2) + Math.pow(Temp.y+(Temp.height/2) - y, 2));
if((int)Math.round(distance)<Temp.height/2)
{
double normY = ((Temp.y+(Temp.height/2) - y) / distance);
double normX = ((Temp.x+(Temp.width/2) - x) / distance);
if(Temp.x+Temp.width/2>Tile.x+Tile.width && Temp.y+Temp.height/2<Tile.y){Tile.move((Temp.x+Temp.width/2)-(int)Math.round(normX*((Temp.width/2)))-Tile.width,(Temp.y+Temp.height/2)-(int)Math.round(normY*((Temp.height/2))));text.out="collision detected!";}
else if(Temp.x+Temp.width/2<Tile.x && Temp.y+Temp.height/2<Tile.y){Tile.move((Temp.x+Temp.width/2)-(int)Math.round(normX*((Temp.width/2))),(Temp.y+Temp.height/2)-(int)Math.round(normY*((Temp.height/2))));text.out="collision detected!";}
else if(Temp.x+Temp.width/2>Tile.x+Tile.width && Temp.y+Temp.height/2>Tile.y+Tile.height){Tile.move((Temp.x+Temp.width/2)-(int)Math.round(normX*((Temp.width/2)))-Tile.width,(Temp.y+Temp.height/2)-(int)Math.round(normY*((Temp.height/2)))-Tile.height);text.out="collision detected!";}
else {Tile.move((Temp.x+Temp.width/2)-(int)Math.round(normX*((Temp.width/2))),(Temp.y+Temp.height/2)-(int)Math.round(normY*((Temp.height/2)))-Tile.height);text.out="collision detected!";}
}
}
}
void circleCircle(tile Temp)
{
double distance = Math.sqrt(Math.pow((Tile.x+(Tile.width/2)) - (Temp.x+(Temp.width/2)),2) + Math.pow((Tile.y+(Tile.height/2)) - (Temp.y+(Temp.height/2)), 2));
if((int)distance<(Tile.width/2+Temp.width/2))
{
double normX = ((Tile.x+(Tile.width/2)) - (Temp.x+(Temp.width/2))) / distance;
double normY = ((Tile.y+(Tile.height/2)) - (Temp.y+(Temp.height/2))) / distance;
Tile.move((Temp.x+(Temp.width/2))+(int)Math.round(normX*(Tile.width/2+Temp.width/2))-(Tile.width/2) , (Temp.y+(Temp.height/2))+(int)Math.round(normY*(Tile.height/2+Temp.height/2))-(Tile.height/2));text.out="collision detected!";
}
}
void circleLine(tile Temp)
{
line Line=(line)Temp;
if (Line.x1 < (Tile.x + Tile.width) && (Line.x1) > Tile.x && Line.y1 < (Tile.y + Tile.height) && Line.y1 > Tile.y)//circle may be hitting one of the end points
{
rectangle rec=new rectangle(Line.x1, Line.y1, 1, 1, this);
circleRectangle(rec);
remove(rec);
}
if (Line.x2 < (Tile.x + Tile.width) && (Line.x2) > Tile.x && Line.y2 < (Tile.y + Tile.height) && Line.y2 > Tile.y)//circle may be hitting one of the end points
{
rectangle rec=new rectangle(Line.x2, Line.y2, 1, 1, this);
circleRectangle(rec);
remove(rec);
}
int x1=0, y1=0, x2=Tile.x+(Tile.width/2), y2=Tile.y+(Tile.height/2);
x1=Tile.x+(Tile.width/2)-Line.height;//(int)Math.round(Line.xNorm*1000);
x2=Tile.x+(Tile.width/2)+Line.height;
if(Line.posSlope)
{
y1=Tile.y+(Tile.height/2)-Line.width;
y2=Tile.y+(Tile.height/2)+Line.width;
}
else
{
y1=Tile.y+(Tile.height/2)+Line.width;
y2=Tile.y+(Tile.height/2)-Line.width;
}
Point point=intersection((double)x1,(double)y1,(double)x2,(double)y2,(double)Line.x1,(double)Line.y1,(double)Line.x2,(double)Line.y2);//find intersection
if (point.x < (Line.x + Line.width) && point.x > Line.x && point.y < (Line.y + Line.height) && point.y > Line.y)//line intersects within line segment
{
//if(point!=null){System.out.println(point.x+","+point.y);}
double distance = Math.sqrt(Math.pow((Tile.x+(Tile.width/2)) - point.x,2) + Math.pow((Tile.y+(Tile.width/2)) - point.y, 2));
if((int)distance<Tile.width/2)
{
//System.out.println("hit");
double normX = ((Tile.x+(Tile.width/2)) - point.x) / distance;
double normY = ((Tile.y+(Tile.height/2)) - point.y) / distance;
Tile.move((point.x)+(int)Math.round(normX*(Tile.width/2))-(Tile.width/2) , (point.y)+(int)Math.round(normY*(Tile.height/2))-(Tile.height/2));text.out="collision detected!";
//System.out.println(point.x+","+point.y);
}
}
//new bullet(this, (int)Math.round(tryX), (int)Math.round(tryY));
}
void rectangleLine(tile Temp)
{
line Line=(line)Temp;
if(new Line2D.Double(Line.x1,Line.y1,Line.x2,Line.y2).intersects(new Rectangle(Tile.x,Tile.y,Tile.width,Tile.height)))
{
if (Line.x1 < (Tile.x + Tile.width) && (Line.x1) > Tile.x && Line.y1 < (Tile.y + Tile.height) && Line.y1 > Tile.y)//circle may be hitting one of the end points
{
rectangle rec=new rectangle(Line.x1, Line.y1, 1, 1, this);
rectangleRectangle(rec);
remove(rec);
}
if (Line.x2 < (Tile.x + Tile.width) && (Line.x2) > Tile.x && Line.y2 < (Tile.y + Tile.height) && Line.y2 > Tile.y)//circle may be hitting one of the end points
{
rectangle rec=new rectangle(Line.x2, Line.y2, 1, 1, this);
rectangleRectangle(rec);
remove(rec);
}
if(Line.posSlope)//positive sloped line
{
//first we'll do the top left corner
int x1=Tile.x-Line.height;
int x2=Tile.x+Line.height;
int y1=Tile.y-Line.width;
int y2=Tile.y+Line.width;
Point topPoint=new Point(-99,-99), botPoint=new Point(-99,-99);
double topDistance=0, botDistance=0;
topPoint=intersection((double)x1,(double)y1,(double)x2,(double)y2,(double)Line.x1,(double)Line.y1,(double)Line.x2,(double)Line.y2);//find intersection
topDistance = Math.sqrt(Math.pow(Tile.x - topPoint.x,2) + Math.pow(Tile.y - topPoint.y, 2));
//new let's do the bottom right corner
x1=Tile.x+Tile.width-Line.height;
x2=Tile.x+Tile.width+Line.height;
y1=Tile.y+Tile.height-Line.width;
y2=Tile.y+Tile.height+Line.width;
botPoint=intersection((double)x1,(double)y1,(double)x2,(double)y2,(double)Line.x1,(double)Line.y1,(double)Line.x2,(double)Line.y2);//find intersection
botDistance = Math.sqrt(Math.pow((Tile.x+Tile.width) - botPoint.x,2) + Math.pow((Tile.y+Tile.height) - botPoint.y, 2));
if(topDistance<botDistance)
{
if(new Rectangle(Tile.x,Tile.y,Tile.width,Tile.height).contains(topPoint) && new Rectangle(Line.x,Line.y,Line.width,Line.height).contains(topPoint))
{
Tile.move(topPoint.x,topPoint.y);text.out="collision detected!";
}
}
else
{
if(new Rectangle(Tile.x,Tile.y,Tile.width,Tile.height).contains(botPoint) && new Rectangle(Line.x,Line.y,Line.width,Line.height).contains(botPoint))
{
Tile.move(botPoint.x-Tile.width,botPoint.y-Tile.height);text.out="collision detected!";
}
}
}
else//negative sloped lne
{
//first we'll do the top right corner
int x1=Tile.x+Tile.width-Line.height;
int x2=Tile.x+Tile.width+Line.height;
int y1=Tile.y+Line.width;
int y2=Tile.y-Line.width;
Point topPoint=new Point(-99,-99), botPoint=new Point(-99,-99);
double topDistance=0, botDistance=0;
topPoint=intersection((double)x1,(double)y1,(double)x2,(double)y2,(double)Line.x1,(double)Line.y1,(double)Line.x2,(double)Line.y2);//find intersection
topDistance = Math.sqrt(Math.pow(Tile.x + Tile.width - topPoint.x,2) + Math.pow(Tile.y - topPoint.y, 2));
//new let's do the bottom left corner
x1=Tile.x-Line.height;
x2=Tile.x+Line.height;
y1=Tile.y+Tile.height+Line.width;
y2=Tile.y+Tile.height-Line.width;
botPoint=intersection((double)x1,(double)y1,(double)x2,(double)y2,(double)Line.x1,(double)Line.y1,(double)Line.x2,(double)Line.y2);//find intersection
botDistance = Math.sqrt(Math.pow(Tile.x - botPoint.x,2) + Math.pow((Tile.y+Tile.height) - botPoint.y, 2));
if(topDistance<botDistance)
{
if(new Rectangle(Tile.x,Tile.y,Tile.width,Tile.height).contains(topPoint) && new Rectangle(Line.x,Line.y,Line.width,Line.height).contains(topPoint))
{
Tile.move(topPoint.x-Tile.width,topPoint.y);text.out="collision detected!";
}
}
else
{
if(new Rectangle(Tile.x,Tile.y,Tile.width,Tile.height).contains(botPoint) && new Rectangle(Line.x,Line.y,Line.width,Line.height).contains(botPoint))
{
Tile.move(botPoint.x,botPoint.y-Tile.height);text.out="collision detected!";
}
}
}
}
}
public Point intersection(double x1, double y1, double x2, double y2,double x3, double y3, double x4, double y4)//I didn't write this. got it from http://www.ahristov.com/tutorial/geometry-games/intersection-lines.html (I altered it)
{
double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
int x=(int)Math.round(xi);
int y=(int)Math.round(yi);
return new Point(x, y);
}
//***************************************************************************************
public static void main(String[] args)
{
final collision Collision=new collision();
Collision.run();
}//end main
}//end class
Since Java doesn't have an intersect function (really!?) you can do collision detection by simply comparying the X and Y, Width and Height values of the bounding boxes (rectangle) for each of the objects that could potentially collide.
So... in the base object of each colliding object... i.e. if your player and enemy have a common base you can put a simple Rectangle object called something like BoundingBox. If the common base is a built in Java class then you'll need to create a class that extends the build in class and have the player and enemy objects extend your new class or are instances of that class.
At creation (and each tick or update) you'll need to set the BoundingBox paremeters for both your player and enemy. I don't have the Rectangle class infront of me but its most likely something like X, Y, Width and finally Height. X and Y are that objects location in your game world. The width and height are self explanatory I think. They'll most likely come out from the right of the players location though so, if the X and Y were bothe at 0 and your Width and Height were both at 256 you wouldn't see anything because the character would be at the top left outside of the screen.
Anyways... to detect a collision, you'll want to compare the attributes of the player and enemy BoundingBoxes. So something like this...
if( Player.BoundingBox.X = Enemy.BoundingBox.X && If( Player.BoundingBox.Y = Enemy.BoundingBox.Y )
{
//Oh noes! The enemy and player are on top of eachother.
}
The logic can get sort of complicated but you'll need to compare the distances between each BoundingBox and compare locations.
Here's a useful of an open source game that uses a lot of collisions: http://robocode.sourceforge.net/
You may take a look at the code and complement with the answers written here.
is there a problem with:
Rectangle box1 = new Rectangle(100,100,100,100);
Rectangle box2 = new Rectangle(200,200,100,100);
// what this means is if any pixel in box2 enters (hits) box1
if (box1.contains(box2))
{
// collision occurred
}
// your code for moving the boxes
this can also be applied to circles:
Ellipse2D.Double ball1 = new Ellipse2D.Double(100,100,200,200);
Ellipse2D.Double ball2 = new Ellipse2D.Double(400,100,200,200);
// what this means is if any pixel on the circumference in ball2 touches (hits)
// ball1
if (ball1.contains(ball2))
{
// collision occurred
}
// your code for moving the balls
to check whether youve hit the edge of a screen you could use the following:
Rectangle screenBounds = jpanel.getBounds();
Ellipse2D.Double ball = new Ellipse2D.Double(100,100,200,200); // diameter 200
Rectangle ballBounds = ball.getBounds();
if (!screenBounds.contains(ballBounds))
{
// the ball touched the edge of the screen
}
Use a rectangle to surround each player and enemy, the height and width of the rectangles should correspond to the object you're surrounding, imagine it being in a box only big enough to fit it.
Now, you move these rectangles the same as you do the objects, so they have a 'bounding box'
I'm not sure if Java has this, but it might have a method on the rectangle object called .intersects() so you'd do if(rectangle1.intersectS(rectangle2) to check to see if an object has collided with another.
Otherwise you can get the x and y co-ordinates of the boxes and using the height/width of them detect whether they've intersected yourself.
Anyway, you can use that to either do an event on intersection (make one explode, or whatever) or prevent the movement from being drawn. (revert to previous co-ordinates)
edit: here we go
boolean
intersects(Rectangle r)
Determines whether or not this Rectangle and the specified
Rectangle intersect.
So I would do (and don't paste this code, it most likely won't work, not done java for a long time and I didn't do graphics when I did use it.)
Rectangle rect1 = new Rectangle(player.x, player.y, player.width, player.height);
Rectangle rect2 = new Rectangle(enemy.x, enemy.y, enemy.width, enemy.height);
//detects when the two rectangles hit
if(rect1.intersects(rect2))
{
System.out.println("game over, g");
}
obviously you'd need to fit that in somewhere.
No need to use rectangles ... compare the coordinates of 2 players constantly.
like
if(x1===x&&y1==y)
remember to increase the range of x when ur comparing.
if ur rectangle width is 30 take as if (x1>x&&x2>x+30)..likewise y
It's Java code for collision of two or more ImageViews not rectangles or other,use ImageViews Instead.
1.This code of Collision works every where in any views or layouts.
2.Add a timer to repeat it and to detect collision repeatedly.
3.It only works with views and layout.
if ((getLocationX(_v1) > (getLocationX(_v2) - ((_w2*3) + 40))) && (getLocationX(_v1) < (getLocationX(_v2) + ((_w2*3) +40)))){
if ((getLocationY(_v1) > (getLocationY(_v2) - ((_h2*3) + 40))) && (getLocationY(_v1) < (getLocationY(_v2) + ((_h2*3) +40)))){
showMessage("Collided");
}
}

Categories

Resources