Recursive Collision Detection for Rectangles not quite working - java

I have a bunch of variable sized rectangles, which are laid out randomly in an area.
I am attempting to do (recursive) collision detection that ensures that they do not collide, by shifting their positions.
But, something is still wrong (some of the rectangles still collide), and I can't figure out what.. probably my inability to do recursion right. I would be grateful if someone checked this out.
Here's the code, just copy & paste & run it, and you'll see the result instantly. Requires JavaFx:
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.*;
public class Example extends Application {
public static void main(String[] args) {
launch(args);
}
private Pane root = new Pane();
#Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(root, 800, 600);
stage.setTitle("Collision Problem");
stage.setScene(scene);
stage.setOnCloseRequest(e -> System.exit(0));
stage.show();
run();
}
private static class Node2D {
private double x, y, w, h;
public Node2D() {
w = randInt(40, 80);
h = randInt(20, 40);
}
public void setPosition(double x, double y) {
this.x = x;
this.y = y;
}
public double getWidth() {
return w;
}
public double getHeight() {
return h;
}
}
private static class LayoutEntity {
private Node2D obj = new Node2D();
private double x, y;
public void setLocationInLayout(double x, double y) {
this.x = x;
this.y = y;
}
public double getXInLayout() {
return x;
}
public double getYInLayout() {
return y;
}
}
public void run() {
LayoutEntity[] entitiesToLayout = new LayoutEntity[100];
for (int i = 0; i < entitiesToLayout.length; i++)
entitiesToLayout[i] = new LayoutEntity();
randomizePositions(entitiesToLayout);
collisionDetection(entitiesToLayout);
for (LayoutEntity entity : entitiesToLayout) {
Node2D node = entity.obj;
node.setPosition(entity.getXInLayout(), entity.getYInLayout());
}
// Print possible collisions
displayNodes(entitiesToLayout);
}
private void displayNodes(LayoutEntity[] all) {
for (LayoutEntity entity : all) {
Node2D node = entity.obj;
Rectangle rect = new Rectangle(node.x, node.y, node.w, node.h);
rect.setStroke(Color.BLACK);
rect.setFill(null);
root.getChildren().add(rect);
}
}
private void randomizePositions(LayoutEntity[] entities) {
for (LayoutEntity entity : entities) {
entity.setLocationInLayout(randInt(0, 512), randInt(0, 512));
}
}
private void collisionDetection(LayoutEntity[] entities) {
collisionDetection(Arrays.asList(entities));
}
private void collisionDetection(Collection<LayoutEntity> c) {
List<LayoutEntity> collisions = new ArrayList<>();
for (LayoutEntity e1 : c) {
for (LayoutEntity e2 : c) {
if (e1 == e2)
continue;
boolean collides = checkAndResolveCollision(e1, e2);
if (collides)
collisions.add(e1);
}
}
checkRecursively(collisions, c);
}
private void checkRecursively(List<LayoutEntity> collisions,
Collection<LayoutEntity> all) {
if (collisions.isEmpty())
return;
for (LayoutEntity e1 : all) {
for (int i = 0; i < collisions.size(); i++) {
LayoutEntity e2 = collisions.get(i);
if (e2 == e1)
continue;
boolean collides = checkAndResolveCollision(e1, e2);
if (collides) {
if (collisions.contains(e1))
continue;
collisions.add(e1);
checkRecursively(collisions, all);
}
}
}
}
private boolean checkAndResolveCollision(LayoutEntity e1, LayoutEntity e2) {
Node2D n1 = e1.obj;
// Ideally, I also want to add a gap around the boxes
double extraSpace = 0;
double w1 = n1.getWidth() + extraSpace * 2;
double h1 = n1.getHeight() + extraSpace * 2;
Rectangle2D b1 = new Rectangle2D(e1.getXInLayout() - extraSpace,
e1.getYInLayout() - extraSpace, w1, h1);
Node2D n2 = e2.obj;
double w2 = n2.getWidth() + extraSpace * 2;
double h2 = n2.getHeight() + extraSpace * 2;
Rectangle2D b2 = new Rectangle2D(e2.getXInLayout() - extraSpace,
e2.getYInLayout() - extraSpace, w2, h2);
if (b1.intersects(b2)) {
Point2D trans = getMinimumTranslation(b1, b2);
double x = e1.getXInLayout() + trans.getX();
double y = e1.getYInLayout() + trans.getY();
e1.setLocationInLayout(x, y);
return true;
}
return false;
}
private Point2D getMinimumTranslation(Rectangle2D source, Rectangle2D target) {
double mtdx, mtdy;
Point2D amin = new Point2D(source.getMinX(), source.getMinY());
Point2D amax = new Point2D(source.getMaxX(), source.getMaxY());
Point2D bmin = new Point2D(target.getMinX(), target.getMinY());
Point2D bmax = new Point2D(target.getMaxX(), target.getMaxY());
double left = (bmin.getX() - amax.getX());
double right = (bmax.getX() - amin.getX());
double top = (bmin.getY() - amax.getY());
double bottom = (bmax.getY() - amin.getY());
if (left > 0 || right < 0)
return Point2D.ZERO;
if (top > 0 || bottom < 0)
return Point2D.ZERO;
if (Math.abs(left) < right)
mtdx = left;
else
mtdx = right;
if (Math.abs(top) < bottom)
mtdy = top;
else
mtdy = bottom;
// zero the axis with the largest mtd value.
if (Math.abs(mtdx) < Math.abs(mtdy))
mtdy = 0;
else
mtdx = 0;
return new Point2D(mtdx, mtdy);
}
private static Random rand = new Random();
public static int randInt(int min, int max) {
return rand.nextInt((max - min) + 1) + min;
}
}

From reading your code, what I can predict is, the problem is in the logic. Inside checkAndResolveCollision(), when you assigning a new co-ordinate to the Node using
e1.setLocationInLayout(x, y);
you miss to check whether the new co-ordinate that you have assigned to this node, overlaps any of the other node's which we have already checked against this one.
You will have to generate the logic, as to whenever you are changing the co-ordinate of a Node, it must again be checked against every other Node
Hope it helps

Related

Referring to objects in Java

quick question regarding my program. My program deals with circle objects that bounce around on a JFrame window. When 2 circles collide the smaller circle is consumed by the larger one and smaller one disappears. The method that does this is called:
checkCollsion();
In my program I'm calling it as such:
for ( int i = 0; i < blobs.length - 1; i++ )
{
for ( int j = i + 1; j < blobs.length; j++ )
{
blobs[i].checkCollision( blobs[j] );
}
}
My only question is, is that I do not understand how to reference blobs[i]. I get how to check if something collides, however I don't understand how I am suppose to compare blobs[i] with blobs[j] since in my method I can only pass in blobs[j] object.
Here's code from my blob class:
import java.awt.Color;
import java.util.Random;
#SuppressWarnings("serial")
public class Blob extends BouncingBall {
private int r;
private int g;
private int b;
private Color c;
private BouncingBall bb;
public Blob(int x, int y, int diameter, int windowSize) {
super(x, y, diameter, windowSize);
}
public void setRandomColor(){
Random r1 = new Random();
Random r2 = new Random();
Random r3 = new Random();
r = r1.nextInt(256);
g = r2.nextInt(256);
b = r3.nextInt(256);
c = new Color(r,g,b);
setBackground(c);
}
public void setRandomDirection(){
Random r1 = new Random();
Random r2 = new Random();
int rightOrLeft = r1.nextInt(2);
int upOrDown = r2.nextInt(2);
if (rightOrLeft == 0){
xDelta = LEFT;
}else{
xDelta = RIGHT;
}
if (upOrDown == 0){
yDelta = UP;
}else{
yDelta = DOWN;
}
}
public void checkCollision(Blob blobs){
//setRandomDirection();
//setRandomColor();
}
}
You have two Blob instances in checkCollision. The parameter blobs and this. For example,
public void checkCollision(Blob blobs) {
// this is blobs[i] and blobs is blobs[j]
if ((this.x + this.diameter) == (blobs.x + blobs.diameter)
&& ((this.y + this.diameter == blobs.y + blobs.diameter))) {
// ...
}
}

Moving Squares Freeze Tag

For my project we have to have four square moving and bouncing off the sides and when it hits a side it changes color. When you click on one of the squares it freezes and turns red. So far i have the four squares and when I click them it says it freezes them. However, I'm not positive how to get them to move and bounce off the sides. Below is the code i have written so far.
MAIN:
import java.util.Random;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
StationarySquare[] squares = new StationarySquare[4];
Random rng = new Random();
for (int i = 0; i < squares.length; i++) {
squares[i] = new StationarySquare(rng.nextDouble(), rng.nextDouble());
}
while (true) {
StdDraw.clear();
int count = 0;
for (int i = 0; i < squares.length; i++) {
squares[i].draw();
if (StdDraw.mousePressed()
&& squares[i].containsPoint(StdDraw.mouseX(), StdDraw.mouseY())) {
squares[i].freeze();
}
if (squares[i].isFrozen()) {
count++;
}
}
StdDraw.text(0.5, 0.5, "Frozen: " + count);
StdDraw.show(25);
}
}
}
My other class
import java.util.Random;
public class StationarySquare {
private double x;
private double y;
private double halfLength;
private int red;
private int green;
private int blue;
private boolean isFrozen;
public StationarySquare(double x, double y) {
this.x = x;
this.y = y;
halfLength = 0.1;
isFrozen = false;
randomColor();
}
public void draw() {
if (isFrozen) {
StdDraw.setPenColor(StdDraw.RED);
} else {
StdDraw.setPenColor(red, green, blue);
}
StdDraw.filledSquare(x, y, halfLength);
}
public void randomColor() {
Random rng = new Random();
red = rng.nextInt(256);
green = rng.nextInt(256);
blue = rng.nextInt(256);
}
public void freeze() {
isFrozen = true;
}
public boolean containsPoint(double a, double b) {
return a > x - halfLength &&
a < x + halfLength &&
b > y - halfLength &&
b < y + halfLength;
}
public boolean isFrozen() {
return isFrozen;
}
}

finding the maximum number of points that lie on the same straight line in a 2D plane

This "Given n points on a 2D plane, find the maximum number of points that lie on the same straight line."
question from leetcode.com I am trying to solve it but I am not able to pass all test
cases.
What I am trying to do is:- I am using a hashmap whose key is the angle b/w the points which I am getting through tan inverse of the slope and I am storing the values for each slope initially the value of the no of occurrance of that point and then incrementing it.
I am using another hashmap for counting the occurance of points.
I am not getting the correct answer for points like (0,0),(1,0) which should return 2 but it's returning 1.
What am I missing?
My code is:
public class MaxPointsINLine {
int max = 0;
int same;
public int maxPoints(Point[] points) {
int max = 0;
Map<Double, Integer> map = new HashMap<Double, Integer>();
Map<Point, Integer> pointmap = new HashMap<Point, Integer>();
for(Point point: points)
{
if(!pointmap.containsKey(point))
{
pointmap.put(point, 1);
}
else
{
pointmap.put(point, pointmap.get(point)+1);
}
}
if (points.length >= 2) {
for (int i = 0; i < points.length; i++) {
for (int j = i ; j < points.length; j++) {
double dx = points[j].x - points[i].x;
double dy = points[j].y - points[i].y;
double slope = Math.atan(dy / dx);
if (!map.containsKey(slope)) {
map.put(slope, pointmap.get(points[j]));
} else
map.put(slope, map.get(slope) + 1);
}
}
for (Double key : map.keySet()) {
if (map.get(key) > max) {
max = map.get(key);
}
}
return max;
} else if (points.length != 0) {
return 1;
} else {
return 0;
}
}
public static void main(String[] args) {
Point point1 = new Point(0,0);
Point point2 = new Point(0,0);
//Point point3 = new Point(2,2);
MaxPointsINLine maxpoint = new MaxPointsINLine();
Point[] points = { point1, point2};
System.out.println(maxpoint.maxPoints(points));
}
}
class Point {
int x;
int y;
Point() {
x = 0;
y = 0;
}
Point(int a, int b) {
x = a;
y = b;
}
#Override
public boolean equals(Object obj) {
Point that = (Point)obj;
if (that.x == this.x && that.y == this.y)
return true;
else
return false;
}
#Override
public int hashCode() {
// TODO Auto-generated method stub
return 1;
}
}
The general strategy doesn't seem like it can work. Let's suppose that we've successfully populated map with key-value pairs (a, N) where a is an angle and N is the number of pairs of points that are joined by the angle a. Consider the following arrangement of 6 points:
**
**
**
Explicitly, there are points at (0,0), (1,0), (2,1), (3,1), (0,2), and (1,2). You can check that the maximum number of points that lie on any line is 2. However, there are 3 pairs of points that are joined by the same angle: ((0,0), (1,0)), ((2,1), (3,1)), and ((0,2), (1,2)). So map will contain a key-value pair with N = 3, but that isn't the answer to the original question.
Because of arrangements like the above, it's not enough to count slopes. A successful algorithm will have to represent lines in such a way that you can distinguish between parallel lines.
This one worked for me:
/**
* Definition for a point.
* class Point {
* int x;
* int y;
* Point() { x = 0; y = 0; }
* Point(int a, int b) { x = a; y = b; }
* }
*/
public class Solution {
public int maxPoints(Point[] points) {
int result = 0;
for(int i = 0; i<points.length; i++){
Map<Double, Integer> line = new HashMap<Double, Integer>();
Point a = points[i];
int same = 1;
//System.out.println(a);
for(int j = i+1; j<points.length; j++){
Point b = points[j];
//System.out.println(" point " + b.toString());
if(a.x == b.x && a.y == b.y){
same++;
} else {
double dy = b.y - a.y;
double dx = b.x - a.x;
Double slope;
if(dy == 0){ // horizontal
slope = 0.0;
} else if(dx == 0){//eartical
slope = Math.PI/2;
} else {
slope = Math.atan(dy/dx);
}
Integer slopeVal = line.get(slope);
//System.out.println(" slope " + slope + " slope value " + slopeVal);
if(slopeVal == null){
slopeVal = 1;
} else {
slopeVal += 1;
}
line.put(slope, slopeVal);
}
}
for (Double key : line.keySet()) {
Integer val = line.get(key);
result = Math.max(result, val + same);
}
// for all points are same
result = Math.max(result, same);
}
return result;
}
}
The most straightforward solution here seems to be the following: One can consider each pair of points. For each pair, one computes the distance of each other point to the line connecting the two points. If this distance is nearly zero, then the point lies on the line.
When this is done for all pairs, one can pick the pair for which the highest number of points lies on the connecting line.
Of course, this is not very elegant, as it is in O(n^3) and performs some computations twice. But it is very simple and rather reliable. I just implemented this as a MCVE here:
One can set points with left-clicking the mouse. Right-clicks clear the screen. The maximum number of points in one line is displayed, and the corresponding points are highlighted.
(EDIT: Updated to handle points that have a distance of 0, based on the comment)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PointsOnStraightLineTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(new PointsPanel());
f.setSize(600, 600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class PointsOnStraightLine
{
// Large epsilon to make it easier to place
// some points on one line with the mouse...
private static final double EPSILON = 5.0;
List<Point> maxPointsOnLine = new ArrayList<Point>();
void compute(List<Point> points)
{
maxPointsOnLine = new ArrayList<Point>();
for (int i=0; i<points.size(); i++)
{
for (int j=i+1; j<points.size(); j++)
{
Point p0 = points.get(i);
Point p1 = points.get(j);
List<Point> resultPoints = null;
if (p0.distanceSq(p1) < EPSILON)
{
resultPoints = computePointsNearby(p0, points);
}
else
{
resultPoints = computePointsOnLine(p0, p1, points);
}
if (resultPoints.size() > maxPointsOnLine.size())
{
maxPointsOnLine = resultPoints;
}
}
}
}
private List<Point> computePointsOnLine(
Point p0, Point p1, List<Point> points)
{
List<Point> result = new ArrayList<Point>();
for (int k=0; k<points.size(); k++)
{
Point p = points.get(k);
double d = Line2D.ptLineDistSq(p0.x, p0.y, p1.x, p1.y, p.x, p.y);
if (d < EPSILON)
{
result.add(p);
}
}
return result;
}
private List<Point> computePointsNearby(
Point p0, List<Point> points)
{
List<Point> result = new ArrayList<Point>();
for (int k=0; k<points.size(); k++)
{
Point p = points.get(k);
double d = p.distanceSq(p0);
if (d < EPSILON)
{
result.add(p);
}
}
return result;
}
}
class PointsPanel extends JPanel implements MouseListener
{
private final List<Point> points;
private final PointsOnStraightLine pointsOnStraightLine;
PointsPanel()
{
addMouseListener(this);
points = new ArrayList<Point>();
pointsOnStraightLine = new PointsOnStraightLine();
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.BLACK);
for (Point p : points)
{
g.fillOval(p.x-2, p.y-2, 4, 4);
}
pointsOnStraightLine.compute(points);
int n = pointsOnStraightLine.maxPointsOnLine.size();
g.drawString("maxPoints: "+n, 10, 20);
g.setColor(Color.RED);
for (Point p : pointsOnStraightLine.maxPointsOnLine)
{
g.drawOval(p.x-3, p.y-3, 6, 6);
}
}
#Override
public void mouseClicked(MouseEvent e)
{
if (SwingUtilities.isRightMouseButton(e))
{
points.clear();
}
else
{
points.add(e.getPoint());
}
repaint();
}
#Override
public void mousePressed(MouseEvent e)
{
}
#Override
public void mouseReleased(MouseEvent e)
{
}
#Override
public void mouseEntered(MouseEvent e)
{
}
#Override
public void mouseExited(MouseEvent e)
{
}
}

How can I have an object wrap around screen?

I'm working on a program that takes a ship object and it moves it. The trouble I am having is that if it goes past a side, then it is supposed to wrap back around on the other side.
Any help would be great :)
Here is my ship Class: The move method is what I need help with. The code I have doesnt work :/
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import junit.framework.TestCase;
public class Ship {
private BufferedImage _image;
private static final int WIDTH = 50;
private Point location;
private Vector speed = new Vector();
private double facing;
/**
* Generate ship at the given starting location and currently stopped
*
* #param starting
* location to copy for this ship
*/
public Ship(Point starting) {
try {
// Use the RunConfigurations >> Arguments > Working Directory tab so
// that this works. Don't just place the nave.png file in the bin
// directory!
_image = ImageIO.read(new File("nave.png"));
} catch (IOException e) {
System.err.println("Cannot find ship _image: " + e.getMessage());
}
location = starting.clone();
facing = Math.PI;
}
public void accelerate(double force) {
// TODO change the speed (velocity, really) by force in the direction
// the ship is facing.
// add a vector of appropriate magnitude by the facing direction
Vector acc = new Vector(facing);
acc = acc.scale(force);
speed = speed.add(acc);
}
public void rotate(double angle) {
// TODO change the direction the ship is facing. Can accept any angle
// as a parameter but should store it as in [0,2*pi)
while (angle <= 0.0f) {
angle += (Math.PI * 2);
}
while (angle >= Math.PI) {
angle -= (Math.PI * 2);
}
facing += angle;
}
public void move(Dimension bounds) {
// TODO Move the ship its speed. The ship should wrap around
// within its box. (Hint: move the ship by the size of the
// bounding area to wrap it around; you may need to do this
// more than once if the ship is moving fast enough.)
location = speed.move(location);
while (location.getX() > bounds.width) {
Vector v = new Vector(location.getX() - WIDTH);
location = v.move(location);
}
while (location.getX() < -WIDTH) {
Vector v = new Vector(location.getX() + WIDTH);
location = v.move(location);
}
while (location.getY() > bounds.height) {
Vector v = new Vector(location.getY() - WIDTH);
location = v.move(location);
}
while (location.getY() < -WIDTH) {
Vector v = new Vector(location.y() + WIDTH);
location = v.move(location);
}
}
public void draw(Graphics g2d) {
double locationX = _image.getWidth() / 2;
double locationY = _image.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(facing,
locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx,
AffineTransformOp.TYPE_BILINEAR);
// Drawing the rotated image at the required drawing locations
// Code for rotating adapted from StackOverflow.
g2d.drawImage(op.filter(_image, null), location.getX(),
location.getY(), null);
}
And here is my vector class: All this code works :)
public class Vector {
private final double _dx, _dy;
public Vector() {
_dy = 0.0;
_dx = 0.0;
}
public Vector(double x, double y) {
_dx = x;
_dy = y;
}
public Vector(Point a, Point b) {
_dx = b.x() - a.x();
_dy = b.y() - a.y();
}
public Vector(double angle) {
_dx = Math.cos(angle);
_dy = Math.sin(angle);
}
public double dx() {
return _dx;
}
public double dy() {
return _dy;
}
public Point move(Point b) {
double x = b.x();
double y = b.y();
x += _dx;
y += _dy;
return new Point(x, y);
}
public Vector add(Vector a) {
double x = (a._dx + _dx);
double y = (a._dy + _dy);
return new Vector(x, y);
}
public Vector scale(double s) {
double x = _dx * s;
double y = _dy * s;
return new Vector(x, y);
}
public double magnitude() {
double x = Math.pow(_dx, 2);
double y = Math.pow(_dy, 2);
return Math.sqrt(x + y);
}
public Vector normalize() {
double x = _dx / magnitude();
double y = _dy / magnitude();
return new Vector(x, y);
}
public Vector rotate(double rads) {
double theta = angle();
theta += rads;
return new Vector(theta);
}
public double angle() {
double alpha = Math.acos(dx() / magnitude());
if (dy() < 0)
alpha = Math.PI - alpha;
return alpha;
}
#Override
public String toString() {
String vector = "[" + _dx + "," + _dy + "]";
return vector;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Vector) {
Vector vector = (Vector) obj;
if ((Math.abs(_dx - vector._dx) <= (1 / 10000000000f))
&& (Math.abs(_dy - vector._dy) <= (1 / 10000000000f)))
return true;
else
return false;
} else
return false;
}
#Override
public int hashCode() {
return (int) Math.round((angle() * 180) / Math.PI);
}
}
Expanding upon the suggestion to use modulo, you can use it as follows to wrap around easily without loops:
// Assuming move is called for each time frame
// We can update the location of ship using modulo when it exceeds the bounds
public void move(Dimension bounds) {
// TODO Move the ship its speed. The ship should wrap around
// within its box. (Hint: move the ship by the size of the
// bounding area to wrap it around; you may need to do this
// more than once if the ship is moving fast enough.)
location = speed.move(location);
if (location.getX() > bounds.width) {
location.setLocation(location.getX() % bounds.width), location.getY());
}
else if (location.getX() < 0) {
location.setLocation(bounds.width - location.getX(), location.getY());
}
if (location.getY() > bounds.height) {
location.setLocation(location.getX(), location.getY() % bounds.height);
}
else if (location.getY() < 0) {
location.setLocation(location.getX(), bounds.height - location.getY());
}
}
You've provided a lot of code so I may have missed why you need to do this, but rather than create a new delta Vector to move the location, you can alternatively just determine the new wrapped position that the ship should be at set it per setLocation method.
I hope this helps.

java 2d game -lagging and collision issue

I am designing a simple java 2d game.where an aircraft shoots missiles and they hit alien ships.(pictures are attached for a better understanding).
Now I need to detect when the missile hits the alien ship. So as to count the number of total hits. I used the rectangle1.intersects(rec2)method, but instead of giving me an integer 1 as the answer (after the boolean of course) it gives me some funny answer. I guess like how much the two rectangles intersect...
Also when adding new aliens in an arraylist I use the following: I add new aliens every two seconds, but this slows down the game very much.
So please guide me on these two issues.
There is a game class (contains the main frame), board class (the panel on which I draw) alient, missile and craft class. Below I am giving the the actionPerformed() of the panel class which gets called by a timer every 2ms (the rest of the code is below).
///CODE TO BE FOCUSED ON
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class game extends JFrame {
static long z;
game()
{
add(new board());
setBounds(0, 0, 500, 500);
setVisible(true);
setLayout(null);
setLocationRelativeTo(null);
setTitle("\t\t...................::::~~~~'S GAME~~~~:::::...............");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new game();
z = System.currentTimeMillis();
}
}
class board extends JPanel implements ActionListener
{
Timer t = new Timer(5, this);
public ArrayList alien_list;
craft craft_list = new craft();
Label l = new Label();
int total_hits = 0;
public board() {
setFocusable(true);
setLayout(null);
setDoubleBuffered(true);
setBackground(Color.BLACK);
addKeyListener(craft_list);
l.setBounds(0, 0, 150, 30);
l.setBackground(Color.GREEN);
add(l);
t.start();
alien_list = new ArrayList();
alien_list.add(new alien(0, 100));
alien_list.add(new alien(0, 150));
alien_list.add(new alien(0, 200));
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g1 = (Graphics2D) g;
long z = (System.currentTimeMillis() - game.z) / 1000;
if (z >= 60)
{
remove(l);
g.setColor(Color.red);
g1.drawString("time up", 100, 100);
} else
{
g1.drawImage(craft_list.getImage(), craft_list.getX(),
craft_list.getY(), null);
ArrayList a = craft_list.getmissile();
for (int i = 0; i < a.size(); i++) {
missile m = (missile) a.get(i);
g1.drawImage(m.getImage(), m.getX(), m.getY(), null);
}
l.setText("time elapsed:" + " " + +z + " " + "hits:" + " "
+ total_hits);
for (int i = 0; i < alien_list.size(); i++) {
alien m = (alien) alien_list.get(i);
g1.drawImage(m.getImage(), m.getX(), m.getY(), null);
}
}
}
public void actionPerformed(ActionEvent e) {
ArrayList a = craft_list.getmissile();
for (int i = 0; i < a.size(); i++) {
missile m = (missile) a.get(i);
if (m.visible == true)
m.move();
else
a.remove(i);
}
long z = (System.currentTimeMillis() - game.z) / 1000;
if (z % 3 == 0)
alien_list.add(new alien(-10, 100));
for (int j = 0; j < alien_list.size(); j++) {
alien m = (alien) alien_list.get(j);
if (m.visible == true)
m.move();
else
alien_list.remove(j);
}
craft_list.move();
collison();
repaint();
}
public void collison() {
ArrayList a = craft_list.getmissile();
for (int i = 0; i < a.size(); i++) {
missile m = (missile) a.get(i);
Rectangle r1 = m.getBounds();
for (int j = 0; j < alien_list.size(); j++) {
alien l = (alien) alien_list.get(j);
Rectangle r2 = l.getBounds();
if (r1.intersects(r2)) {
total_hits++;
m.setVisible(false);
l.setVisible(false);
}
}
}
}
}
class craft extends KeyAdapter
{
int x = 250;
int y = 400;
ArrayList m = new ArrayList();
Image i;
int dx, dy;
craft() {
ImageIcon i1 = new ImageIcon("1a.jpg");
i = i1.getImage();
}
public Image getImage() {
return i;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void move() {
x += dx;
y += dy;
if (x < 0)
x = 0;
if (x > 450)
x = 450;
if (y > 420)
y = 420;
if (y < 200)
y = 200;
}
public void keyPressed(KeyEvent k)
{
int key = k.getKeyCode();
if (key == KeyEvent.VK_SPACE) {
fire();
}
if (key == KeyEvent.VK_LEFT) {
dx = -1;
}
if (key == KeyEvent.VK_RIGHT) {
dx = 1;
}
if (key == KeyEvent.VK_UP) {
dy = -1;
}
if (key == KeyEvent.VK_DOWN) {
dy = 1;
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) {
dx = 0;
}
if (key == KeyEvent.VK_RIGHT) {
dx = 0;
}
if (key == KeyEvent.VK_UP) {
dy = 0;
}
if (key == KeyEvent.VK_DOWN) {
dy = 0;
}
}
public void fire() {
m.add(new missile(getX() + 13, getY() - 6));
}
public ArrayList getmissile() {
return m;
}
public Rectangle getBounds() {
return new Rectangle(x, y, i.getWidth(null), i.getHeight(null));
}
}
class missile {
Image i;
int x, y;
public boolean visible;
missile(int x, int y) {
this.x = x;
this.y = y;
visible = true;
ImageIcon i1 = new ImageIcon("1c.jpg");
i = i1.getImage();
}
public Image getImage() {
return i;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void move() {
y--;
if (y < 0)
visible = false;
}
public Rectangle getBounds() {
return new Rectangle(x, y, i.getWidth(null), i.getHeight(null));
}
public void setVisible(boolean t) {
this.visible = t;
}
}
class alien {
Image i;
int x, y;;
public boolean visible;
public alien(int x, int y)
{
this.x = x;
this.y = y;
ImageIcon i1 = new ImageIcon("b.jpg");
i = i1.getImage();
visible = true;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Image getImage() {
return i;
}
public void move() {
x++;
if (x > 500)
visible = false;
}
public Rectangle getBounds() {
return new Rectangle(x, y, i.getWidth(null), i.getHeight(null));
}
public void setVisible(boolean t) {
this.visible = t;
}
}
Ok, your code format is kind of unreadable and invites everybody to oversee otherwise obvious bugs. That is what I have seen so far for your performance issue:
getBounds() creates a new Rectangle instance every time it gets called. You should update the bounds rectangle at the last line of your move() and just return the rectangle instance instead of creating a new one.
Reuse Image or ImageIcon objects. There is no need to load the same jpg file over and over again in a constructor. Make it static or use a image cache.
Instead of o++ in fire() you should use o = m.size(). Mainly because you never call o--, you only remove the rocket from the ArrayList.
And at that point everybody loses track of what o and m means. Name your variables better! o should be amountOfRockets and m should be listOfRockets.
When you use Eclipse, press ctrl + shift + f to format the code which I highly recommend. After that go through your code and name the variables correctly. That means you should give them a descriptive name. And finally: let the name of your classes start with an upper case.
Very likely that this will not yet remove all issues but it will at least help us to understand and read your code easier... which might lead us to a solution...
Update:
You still haven't done 1. and 2. I suggested but you did 3.
Here is what 1. should be as a sample for the Alien class:
private Rectangle bounds
//constructor
Alien() {
// your stuff and the bounds:
bounds = new Rectangle(x, y, i.getWidth(null), i.getHeight(null));
}
public void move() {
bounds.x++;
if (bounds.x > 500)
visible = false;
}
public Rectangle getBounds() {
return bounds;
}
You need to implement that for the Rocket class as well.
What I still don't get is where you remove the old Alien objects. Just setting their visibility is not enough. You should remove them from the list of your Alien objects. Otherwise you will loop through objects that are not there anymore.

Categories

Resources