In my homework I have to code a 2d parcour-game with javafx. How can I add sensors to my player(gameobject) so it can print out the distance to the next barrier?
To create the barriers/walls I used circles, my player is also an circle.
Can anyone help me with this problem?
I expact when I am controlling the player, that the sensor detects the nearest object in front of him, and prints out the distance and location(x/y) of the object.
This is the main/control class where I build the walls and the player. I hope this helps for understanding my problem.This is how the program looks like for now. I am also happy to get some coding advice.
public class ParkourApp extends Application {
private List<Point2D> points = new ArrayList<>();
private List<Circle> circles = new ArrayList<>();
private Player p1;
public Parent createContent (){
BorderPane root = new BorderPane();
Pane draw = new Pane();
Circle c1 = new Circle(100,0,5);
Circle c2 = new Circle(200,0,5);
Circle c3 = new Circle(300,0,5);
Circle c4 = new Circle(400,0,5);
Circle c5 = new Circle(0,100,5);
Circle c6 = new Circle(0,200,5);
Circle c7 = new Circle(0,300,5);
Circle c8 = new Circle(0,400,5);
addVerticalStraight(100,100,100,50,6);
addHorizontalStraight(150,50,100,50,6);
addVerticalStraight(140,250,50,50,6);
addVerticalStraight(300,150,100,50,6);
addStraight(100,90,140,50,9);
addStraight(100,210,135,250,8);
addStraight(158,205,190,240,8);
addStraight(260,50,330,80,9);
addStraight(340,85,350,140,8);
addStraight(258,105,300,140,8);
addAllCircles();
AnimationTimer at = new AnimationTimer() {
#Override
public void handle(long now) {
update();
}
};
at.start();
for(Circle c : circles){
draw.getChildren().add(c);
}
p1 = new Player(new Rectangle(50,400,11,5));
p1.setGeschwindigkeit(new Point2D(1,0));
System.out.printf("xx:%f YY:%f\n",p1.getNode().getTranslateX(),p1.getNode().getTranslateY());
draw.getChildren().addAll(c1,c2,c3,c4,c5,c6,c7,c8,p1.getNode());
root.setCenter(draw);
return root;
}
private void update(){
for(Circle c : circles){
if(p1.isColliding(c)){
System.out.println("is Colliding..");
}
}
p1.update();
}
private void addAllCircles(){
for(Point2D po : points){
Circle c = new Circle(po.getX(),po.getY(),3, Color.DIMGREY);
circles.add(c);
}
}
private ArrayList<Point2D> addStraight(double startX, double startY, double endX, double endY, double divisor){
ArrayList<Point2D> list = new ArrayList<>();
double length = 0;
double xDist = 0;
double m = 0;
if(startX != endX) {
if(endX < startX){
double temp = startX;
startX = endX;
endX = temp;
temp = startY;
startY = endY;
endY = temp;
}
//upgrade of the straight
m = -(startY-endY)/(endX - startX);
System.out.println(m);
//Distance of the two points
length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
System.out.println(length);
//xDist is the distance of every point of the straight on the x-axis
xDist = endX - startX;
xDist /= divisor;
System.out.println(xDist);
double x = startX;
double y = startY;
//this loop uses all the data that was calculated and creates a
//2D-Point which is stored in class-list of the circles
for(int i=0; i<divisor+1; i++){
list.add(new Point2D(x, y));
points.add(new Point2D(x, y));
y += (xDist * m);
System.out.println(y);
x += xDist;
}
}
return list;
}
private ArrayList<Point2D> addHorizontalStraight(double x, double y, double length, double width, double dist){
int amount = (int)length/(int)dist;
ArrayList<Point2D> list = new ArrayList<>();
for(int i=0; i<amount+1; i++){
list.add(new Point2D(x,y));
list.add(new Point2D(x,y+width));
points.add(new Point2D(x,y));
points.add(new Point2D(x,y+width));
x += dist;
}
return list;
}
private ArrayList<Point2D> addVerticalStraight(double x, double y, double length, double width, double dist){
int amount = (int)length/(int)dist;
ArrayList<Point2D> list = new ArrayList<>();
for(int i=0; i<amount+1; i++){
list.add(new Point2D(x,y));
list.add(new Point2D(x+width,y));
points.add(new Point2D(x,y));
points.add(new Point2D(x+width,y));
y += dist;
}
return list;
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(createContent(),600,600));
primaryStage.getScene().setOnKeyReleased(e ->{
switch (e.getCode()){
case LEFT:
p1.turnLeft();
break;
case RIGHT:
p1.turnRight();
break;
case UP:
p1.faster();
break;
case DOWN:
p1.slower();
break;
}
});
primaryStage.show();
}
}
Related
enter image description here
I am programming a paint editor for my final year project. One of its function is that when an user select one or more lines, these lines can be rotated according to a specific point(one of the vertex of these selected lines). Besides, when a line is chosen, it will be marked as red and both vextices will be marked. The red vertex is rotation pivot chosen by user. To implement this, i first caculate the rotation angle and then use trigonometric function to caculate the rotated lines.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import javax.swing.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class MWE extends JFrame{
MyPanel panel = new MyPanel();
public MWE(){
setTitle("MyFirstFrame");
setSize(1160,830);
setLocation(100,100);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
panel.setBackground(Color.white);
panel.requestFocus();
panel.setVisible(true);
this.getContentPane().add(panel);
}
public static void main(String[] args){
MWE myFrame = new MWE();
}
}
class MyPanel extends JPanel{
private ArrayList selectedLines = new ArrayList<MyLine2D>();
private ArrayList selectedPoints = new ArrayList<MyPoint>();
MyPoint rotationP = new MyPoint(247,309,Color.red);
int lastX = 0, lastY = 0;
public MyPanel(){
this.setBackground(Color.WHITE);
this.requestFocus();
this.setVisible(true);
addMouseListener();
Line2D myLine = new Line2D.Double(247, 309, 344, 197);
selectedLines.add(new MyLine2D(myLine, Color.red));
MyPoint p1 = new MyPoint(247,309,Color.red);
MyPoint p2 = new MyPoint(344,197,Color.black);
selectedPoints.add(p1);
selectedPoints.add(p2);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for(int i = 0; i< selectedLines.size(); i++){
g.setColor(((MyLine2D)selectedLines.get(i)).getColor());
g.drawLine(((MyLine2D)selectedLines.get(i)).getX1(), ((MyLine2D)selectedLines.get(i)).getY1(),
((MyLine2D)selectedLines.get(i)).getX2(), ((MyLine2D)selectedLines.get(i)).getY2());
}
for(int i = 0; i < selectedPoints.size(); i++){
g.setColor(((MyPoint)selectedPoints.get(i)).getColor());
g.drawOval(((MyPoint)selectedPoints.get(i)).getX() - 3, ((MyPoint)selectedPoints.get(i)).getY() - 3, 6, 6);
}
}
private void addMouseListener(){
this.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e){
lastX = e.getX();
lastY = e.getY();
}
});
this.addMouseMotionListener(new MouseMotionAdapter(){
#Override
public void mouseDragged(MouseEvent e){
double vecCross = (lastX - rotationP.getX()) * (e.getX() - rotationP.getX()) + (lastY - rotationP.getY()) * (e.getY() - rotationP.getY());
int c = (Math.abs((lastX - rotationP.getX()) * (lastX - rotationP.getX())) + Math.abs((lastY - rotationP.getY()) * (lastY - rotationP.getY())))
* (Math.abs((e.getX() - rotationP.getX()) * (e.getX() - rotationP.getX())) + Math.abs((e.getY() - rotationP.getY()) * (e.getY() - rotationP.getY())));
double sqrt = Math.sqrt(c);
double cosValue = vecCross/sqrt;
double radian = Math.acos(cosValue);
if(((e.getY() - lastY)*(lastX - rotationP.getX()) < 0) && !Double.isNaN(radian)){
//
for(int i = 0; i < selectedLines.size(); i++){
((MyLine2D)selectedLines.get(i)).rotationLine(rotationP, -radian);
}
for(int i = 0; i < selectedPoints.size(); i++){
((MyPoint)selectedPoints.get(i)).rotationPoint(rotationP, -radian);
}
}
if(((e.getY() - lastY)*(lastX - rotationP.getX()) > 0) && !Double.isNaN(radian)){
for(int i = 0; i < selectedLines.size(); i++){
((MyLine2D)selectedLines.get(i)).rotationLine(rotationP, radian);
}
for(int i = 0; i < selectedPoints.size(); i++){
((MyPoint)selectedPoints.get(i)).rotationPoint(rotationP, radian);
}
}
lastX = e.getX();
lastY = e.getY();
repaint();
}
});
}
}
class MyLine2D {
private Line2D myLine = new Line2D.Double();
private Color color = Color.BLACK;
public MyLine2D(Line2D myLine, Color c){
this.myLine = myLine;
this.color = c;
}
public int getX1(){
return (int)myLine.getX1();
}
public int getY1(){
return (int)myLine.getY1();
}
public int getX2(){
return (int)myLine.getX2();
}
public int getY2(){
return (int)myLine.getY2();
}
public Color getColor(){
return this.color;
}
public void rotationLine(MyPoint pivot, double radians){
double x1=0, y1=0, x2=0, y2=0;
x1 = pivot.getX() + (myLine.getX1() - pivot.getX()) * Math.cos(radians) - (myLine.getY1() - pivot.getY()) * Math.sin(radians);
y1 = pivot.getY() + (myLine.getY1() - pivot.getY()) * Math.cos(radians) + (myLine.getX1() - pivot.getX()) * Math.sin(radians);
x2 = pivot.getX() + (myLine.getX2() - pivot.getX()) * Math.cos(radians) - (myLine.getY2() - pivot.getY()) * Math.sin(radians);
y2 = pivot.getY() + (myLine.getY2() - pivot.getY()) * Math.cos(radians) + (myLine.getX2() - pivot.getX()) * Math.sin(radians);
Line2D rotatedLine = new Line2D.Double(x1,y1,x2,y2);
myLine = rotatedLine;
}
}
class MyPoint {
private int x;
private int y;
private Color color = Color.black;
public MyPoint(int x, int y, Color c){
this.x = x;
this.y = y;
this.color = c;
}
public int getX(){
return this.x;
}
//
//
public int getY(){
return this.y;
}
public Color getColor(){
return this.color;
}
public void rotationPoint(MyPoint pivot, double radians){
double x1 = pivot.getX() + (x - pivot.getX()) * Math.cos(radians) - (y - pivot.getY()) * Math.sin(radians);
double y1 = pivot.getY() + (y - pivot.getY()) * Math.cos(radians) + (x - pivot.getX()) * Math.sin(radians);
x = (int) x1;
y = (int) y1;
}
}
vecCross is dot product of a vector from rotation pivot to original mouse pointer location and a vector from rotation pivot to present mouse pointer location. c is product of the magnitude of these two vectors. the Arraylist, selectedLines, is the lines selected and remain to be rotated. the ArrayList, selectedPoints, is the vertices of selected lines.
in the if arguement, i think '((e.getY() - lastY)*(lastX - rotationP.getX()) < 0 )' can be represented as my mouse moves anticlockwise. because origin of Jpanel is at top left, anticlockwise movement means to reduce its degree.
My Problem:
However, when i select some lines and enter Rotation mode and move my mouse clockwise, these lines dont rotate smoothly, sometimes quick and sometime slow, and those selected points will dettach from vertices, as shown in image. it is really strange because i use the some function to rotate the selected points and lines. can anyone give me some comments?
I am supposed to create a new class SCylinder extended from SObject, which builds a cylinder model. It is expected that the class SCylinder works like the class SSphere provided for rendering the sphere, except that SCylinder is used for rendering a cylinder.
I know I have to change the genData() so that I can create a cylinder but have no idea where to start. Any advice and tips would be much appreciated.
package Objects;
public class SCylinder extends SObject{
private float radius;
private float height;
private int slices;
private int stacks;
public SCylinder(){
super();
init();
update();
}
public SCylinder(float radius){
super();
init();
this.radius = radius;
update();
}
public SCylinder(float radius, float height,int slices, int stacks){
super();
this.radius = radius;
this.height=height;
this.slices = slices;
this.stacks = stacks;
update();
}
private void init(){
this.radius = 1;
this.slices = 20;
this.stacks = 20;
}
#Override
protected void genData() {
int i,j,k;
double deltaLong=PI*2/slices;
double deltaLat= PI/stacks;
// Generate vertices coordinates, normal values, and texture coordinates
numVertices = (slices+1)*(stacks-1)+2;
vertices = new float[numVertices*3];
normals = new float[numVertices*3];
textures = new float[numVertices*2];
//North pole point
normals[0] = 0; normals[1] = 0; normals[2] = 1;
vertices[0] = 0; vertices[1] = 0; vertices[2] = radius;
textures[0]= 0.5f; textures[1] = 1.0f;
k = 1;
//vertices on the main body
for(i=1;i<stacks;i++){
for(j=0;j<=slices;j++){
normals[3*k] = sin(deltaLat*i)*cos(deltaLong*j);
normals[3*k+1] = sin(deltaLat*i)*sin(deltaLong*j);
normals[3*k+2] = cos(deltaLat*i);
vertices[3*k] = radius*normals[3*k];
vertices[3*k+1] = radius*normals[3*k+1];
vertices[3*k+2] = radius*normals[3*k+2];
textures[2*k] = (float) j/slices;
textures[2*k+1] = 1-(float) i/stacks;
k++;
}
}
//South pole point
normals[3*k] = 0; normals[3*k+1] = 0; normals[3*k+2] = -1;
vertices[3*k] = 0; vertices[3*k+1] = 0; vertices[3*k+2] = -radius;
textures[2*k] = 0.5f; textures[2*k+1] = 0.0f; k++;
// Generate indices for triangular mesh
numIndices = (stacks-1)*slices*6;
indices = new int[numIndices];
k = 0;
//North Pole, numElement:slices*3
for(j=1;j<=slices;j++){
indices[k++] = 0;
indices[k++] = j;
indices[k++] = j+1;
}
//South Pole, numElement:slices*3
int temp = numVertices-1;
for(j=temp-1;j>temp-slices-1;j--){
indices[k++] = temp;
indices[k++] = j;
indices[k++] = j-1;
}
//Main body, numElement:(stacks-2)*slices*6
for(i=1;i<stacks-1;i++){
for(j=1;j<=slices;j++){
//each quad gives two triangles
//triangle one
indices[k++] = (i-1)*(slices+1)+j;
indices[k++] = i*(slices+1)+j;
indices[k++] = i*(slices+1)+j+1;
//triangle two
indices[k++] = (i-1)*(slices+1)+j;
indices[k++] = i*(slices+1)+j+1;
indices[k++] = (i-1)*(slices+1)+j+1;
}
}
}
public void setRadius(float radius){
this.radius = radius;
updated = false;
}
public void setSlices(int slices){
this.slices = slices;
updated = false;
}
public void setStacks(int stacks){
this.stacks = stacks;
updated = false;
}
public float getRadius(){
return radius;
}
public int getSlices(){
return slices;
}
public int getStacks(){
return stacks;
}
}
I'm trying to make a honeycomb flow with buttons in JavaFX with so far a FlowPane
So far I got it half working by using a negative VGap on my FlowPane, but as soon as I resize it and make the 3-4 row go to 3-3-1 it obviously goes wrong
I'm trying to find a solution that will keep the honeycomb layout with variable amounts of buttons, but so far I havent had much success. So I was wondering if anyone knows a solution for this.
edit: here is the basic code I have at the moment
FlowPane root = new FlowPane();
root.setHgap(3.0); root.setVgap(-23.0);
root.setAlignment(Pos.CENTER);
Button[] array = new Button[7];
for(int i = 0; i <= 6; i++){
Button button = new Button();
button.setShape(polygon); (made a polygon in the same of a hexagon)
array[i] = button;
}
root.getChildren().addAll(array);
Predetermined columns/ rows
This is a bit more convenient than using a FlowPane to place the fields.
You can observe that using column spans you can place the Buttons in a GridPane: Every field fills 2 columns of sqrt(3/4) times the field height; the odd/even rows start at column 0/1 respectively. Every field fills 3 rows and the size of the column constraints alternate between one quarter and one half of the field height.
Example
public static GridPane createHoneyComb(int rows, int columns, double size) {
double[] points = new double[12];
for (int i = 0; i < 12; i += 2) {
double angle = Math.PI * (0.5 + i / 6d);
points[i] = Math.cos(angle);
points[i + 1] = Math.sin(angle);
}
Polygon polygon = new Polygon(points);
GridPane result = new GridPane();
RowConstraints rc1 = new RowConstraints(size / 4);
rc1.setFillHeight(true);
RowConstraints rc2 = new RowConstraints(size / 2);
rc2.setFillHeight(true);
double width = Math.sqrt(0.75) * size;
ColumnConstraints cc = new ColumnConstraints(width/2);
cc.setFillWidth(true);
for (int i = 0; i < columns; i++) {
result.getColumnConstraints().addAll(cc, cc);
}
for (int r = 0; r < rows; r++) {
result.getRowConstraints().addAll(rc1, rc2);
int offset = r % 2;
int count = columns - offset;
for (int c = 0; c < count; c++) {
Button b = new Button();
b.setPrefSize(width, size);
b.setShape(polygon);
result.add(b, 2 * c + offset, 2 * r, 2, 3);
}
}
result.getRowConstraints().add(rc1);
return result;
}
FlowPane-like behavior
Making the x position depend on the row the child is added is not a good idea in a FlowPane. Instead I recommend extending Pane and overriding layoutChildren method place the children at custom positions.
In your case the following class could be used:
public class OffsetPane extends Pane {
public interface PositionFunction {
public Point2D getNextPosition(int index, double x, double y, double width, double height);
}
private static final PositionFunction DEFAULT_FUNCTION = new PositionFunction() {
#Override
public Point2D getNextPosition(int index, double x, double y, double width, double height) {
return new Point2D(x, y);
}
};
private final ObjectProperty<PositionFunction> hPositionFunction;
private final ObjectProperty<PositionFunction> vPositionFunction;
private ObjectProperty<PositionFunction> createPosProperty(String name) {
return new SimpleObjectProperty<PositionFunction>(this, name, DEFAULT_FUNCTION) {
#Override
public void set(PositionFunction newValue) {
if (newValue == null) {
throw new IllegalArgumentException();
} else if (get() != newValue) {
super.set(newValue);
requestLayout();
}
}
};
}
public OffsetPane() {
this.hPositionFunction = createPosProperty("hPositionFunction");
this.vPositionFunction = createPosProperty("vPositionFunction");
}
#Override
protected void layoutChildren() {
super.layoutChildren();
double width = getWidth();
List<Node> children = getManagedChildren();
final int childSize = children.size();
if (childSize > 0) {
int row = 0;
Node lastRowStart = children.get(0);
Node lastNode = lastRowStart;
lastRowStart.relocate(0, 0);
PositionFunction hFunc = getHPositionFunction();
PositionFunction vFunc = getVPositionFunction();
int index = 1;
int columnIndex = 0;
while (index < childSize) {
Node child = children.get(index);
Bounds lastBounds = lastNode.getLayoutBounds();
Bounds bounds = child.getLayoutBounds();
Point2D pt = hFunc.getNextPosition(columnIndex, lastNode.getLayoutX(), lastNode.getLayoutY(), lastBounds.getWidth(), lastBounds.getHeight());
if (pt.getX() + bounds.getWidth() > width) {
// break row
lastBounds = lastRowStart.getLayoutBounds();
pt = vFunc.getNextPosition(row, lastRowStart.getLayoutX(), lastRowStart.getLayoutY(), lastBounds.getWidth(), lastBounds.getHeight());
child.relocate(pt.getX(), pt.getY());
lastRowStart = child;
row++;
columnIndex = 0;
} else {
child.relocate(pt.getX(), pt.getY());
columnIndex++;
}
lastNode = child;
index++;
}
}
}
public final PositionFunction getHPositionFunction() {
return this.hPositionFunction.get();
}
public final void setHPositionFunction(PositionFunction value) {
this.hPositionFunction.set(value);
}
public final ObjectProperty<PositionFunction> hPositionFunctionProperty() {
return this.hPositionFunction;
}
public final PositionFunction getVPositionFunction() {
return this.vPositionFunction.get();
}
public final void setVPositionFunction(PositionFunction value) {
this.vPositionFunction.set(value);
}
public final ObjectProperty<PositionFunction> vPositionFunctionProperty() {
return this.vPositionFunction;
}
}
double[] points = new double[12];
for (int i = 0; i < 12; i += 2) {
double angle = Math.PI * (0.5 + i / 6d);
points[i] = Math.cos(angle);
points[i + 1] = Math.sin(angle);
}
Polygon polygon = new Polygon(points);
OffsetPane op = new OffsetPane();
double fieldHeight = 100;
double fieldWidth = Math.sqrt(0.75) * fieldHeight;
for (int i = 0; i < 23; i++) {
Button button = new Button();
button.setShape(polygon);
button.setPrefSize(fieldWidth, fieldHeight);
op.getChildren().add(button);
}
// horizontal placement just right of the last element
op.setHPositionFunction((int index, double x, double y, double width, double height) -> new Point2D(x + width, y));
// vertical position half the size left/right depending on index and
// 1/4 the node height above the bottom of the last node
op.setVPositionFunction((int index, double x, double y, double width, double height) -> new Point2D(x + (index % 2 == 0 ? width : -width) / 2, y + height * 0.75));
to start off, I'm making a simple game in Java that involves a blue rectangle that can be moved with arrow keys and seven falling circles of varying color, radius, and falling speed. Essentially, whenever the rectangle comes in contact with one of these circles, the rectangle will "lose" a life, which will be indicated by 3 rectangles on the top right of a JFrame that I haven't drawn yet. Every time the rectangle is hit by one of these circles, one of the rectangles will disappear, and when the blue rectangle is hit once more, a red "Game Over" text will appear in the middle of the frame.
Now then, although I'm having trouble getting the colors and speed to randomize each time the circles hit the bottom, I'll leave those for a future question. My main concern is the hit detection between the circles and the blue rectangle. I know that I need to define a certain method, but I'm unsure on how to go about doing it, and how to test it for each of the seven circles over and over while they're falling and having their Y value constantly change.
How could I go about doing this? Anyway, here's my main and circles classes for this project. I'm aware that there's a lot of junk code that isn't being used in the main class as well. I'll clean it up after I just figure this out.
**Main class (Keyexample)
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.Random;
public class Keyexample extends JPanel implements ActionListener, KeyListener{
Timer t = new Timer(5, this);
private Circle[] Circles = new Circle[7];
private javax.swing.Timer t2;
private Circle newc, c1, c2, c3, c4, c5, c6, c7;
double x = 100, y = 100;
double changeX = 0, changeY = 0;
private int cx = 10, cy = 0;
private int newcx = 0, newcy = 0;
private Random rand = new Random();
private Random colorc = new Random();
private int n = rand.nextInt(8);
public keyExample() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
Random colorc = new Random();
Random radiusc = new Random();
int r1 = radiusc.nextInt(12);
int r2 = radiusc.nextInt(12);
int r3 = radiusc.nextInt(12);
int r4 = radiusc.nextInt(12);
int r5 = radiusc.nextInt(12);
int r6 = radiusc.nextInt(12);
int r7 = radiusc.nextInt(12);
Color cc1 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc2 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc3 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc4 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc5 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc6 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc7 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
//creating the 7 circles and spacing them out
c1 = new Circle(cx, cy, r1, cc1);
c2 = new Circle(cx + 50, cy, r2, cc2);
c3 = new Circle(cx + 100, cy, r3, cc3);
c4 = new Circle(cx + 150, cy, r4, cc4);
c5 = new Circle(cx + 200, cy, r5, cc5);
c6 = new Circle(cx + 300, cy, r6, cc6);
c7 = new Circle(cx + 400, cy, r7, cc7);
Circles[0] = c1;
Circles[1] = c2;
Circles[2] = c3;
Circles[3] = c4;
Circles[4] = c5;
Circles[5] = c6;
Circles[6] = c7;
t2 = new javax.swing.Timer(33, new CircleListener());
t2.start();
}
//painting rectangle and circles
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.fill(new Rectangle2D.Double(x, y, 40, 40));
for (int i = 0; i < Circles.length; i++){
Color circlecolor = new Color(rand.nextInt(255), rand.nextInt(255),
rand.nextInt(255));
//circle color starts spazzing out here. constantly changing while falling
g2.setColor(circlecolor);
Circles[i].fill(g);
}
}
public void createCircle(){
}
public void actionPerformed(ActionEvent e) {
repaint();
x += changeX;
y += changeY;
changeX = 0;
changeY = 0;
}
private class CircleListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Random rand = new Random();
int move = 2 + rand.nextInt(10);
int move2 =2 + rand.nextInt(10);
int move3 =2 + rand.nextInt(10);
int move4 =2 + rand.nextInt(10);
int move5 =2 + rand.nextInt(10);
int move6 =2 + rand.nextInt(10);
c1.move(0, n);
position(c1);
c2.move(0, move);
position(c2);
c3.move(0, move2);
position(c3);
c4.move(0, move3);
position(c4);
c5.move(0, move4);
position(c5);
c6.move(0, move5);
position(c6);
c7.move(0, move6);
position(c7);
repaint();
}
public void position(Circle cp) {
int height = getHeight();
int loc = cp.centerX;
int speed = 3 + rand.nextInt(10);
int radiuss = cp.radius;
Rectangle bound = cp.Bounds();
if (bound.topY + bound.width > height){
cp.centerY = 0;
//moving circle back to the top
cp.move(0, speed);
//randomizing speed of circle after moving to top, not working
cp.radius = 5 + rand.nextInt(20);
//randomizing radius of circle after moving to top, does work
}
}
}
public void up() {
if (y != 0){
changeY = -3.5;
changeX = 0;
}
}
public void down() {
if (y <= 350) {
changeY = 3.5;
changeX = 0;
}
}
public void left() {
if (x >=0) {
changeX = -3.5;
changeY = 0;
}
}
public void right() {
if (x <= 550) {
changeX = 3.5;
changeY = 0;
}
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_UP) {
up();
}
if (code == KeyEvent.VK_DOWN) {
down();
}
if (code == KeyEvent.VK_RIGHT) {
right();
}
if (code == KeyEvent.VK_LEFT) {
left();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
**Circle class
import java.awt.*;
import java.util.Random;
public class Circle{
public int centerX, centerY, radius;
public Color color;
public Circle (int x, int y, int r, Color c){
centerX = x;
centerY = y;
radius = r;
Random random = new Random();
}
public void draw(Graphics g){
Color oldColor = g.getColor();
g.setColor(color);
g.drawOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.setColor(oldColor);
}
public void fill(Graphics g){
Color oldColor = g.getColor();
g.setColor(color);
g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.setColor(oldColor);
}
public boolean containsPoint(int x, int y){
int xSquared = (x - centerX) * (x - centerX);
int ySquared = (y - centerY) * (y - centerY);
int radiusSquared = radius * radius;
return xSquared + ySquared - radiusSquared <=0;
}
public void move(int xAmount, int yAmount){
centerX = centerX + xAmount;
centerY = centerY + yAmount;
}
public Rectangle Bounds(){
int x = centerX - radius;
int y = centerY - radius;
return new Rectangle(x, y, radius * 2, radius * 2, Color.red);
}
}
You want to break the problem into two parts: the first - see if there is "definitely no collision". This happens when the center of the circle is more than a radius away from the edges. It's a very fast test, and will be true most of the time:
if(left > x + radius ||
right < x - radius ||
etc.) { // no collision }
The second - if you fail this test, you may still not hit. That depends on whether both x and y are sufficient for overlap. Within this, there are two situations:
A corner of the rectangle is inside the circle: easy to compute (distance from one corner to center of circle < r)
An edge is inside the circle. This means that in one direction (say X) the center lies between the edges; and in the other direction an edge is less than radius away.
This is general, without actual code. I assume you can write the code from here.
You can detect collisions by simple if else conditions on the circle's and rectangle's co ordinates on screen.
distance(circle, Rectangle) <= circle.radius + Rectangle.radius
You can implement distance helper function using the simple distance formula between two points.
link
I'm working on the physics for my GTA2-like game so I can learn more about game physics.
The collision detection and resolution are working great.
I'm now just unsure how to compute the point of contact when I hit a wall.
Here is my OBB class:
public class OBB2D
{
private Vector2D projVec = new Vector2D();
private static Vector2D projAVec = new Vector2D();
private static Vector2D projBVec = new Vector2D();
private static Vector2D tempNormal = new Vector2D();
private Vector2D deltaVec = new Vector2D();
// Corners of the box, where 0 is the lower left.
private Vector2D corner[] = new Vector2D[4];
private Vector2D center = new Vector2D();
private Vector2D extents = new Vector2D();
private RectF boundingRect = new RectF();
private float angle;
//Two edges of the box extended away from corner[0].
private Vector2D axis[] = new Vector2D[2];
private double origin[] = new double[2];
public OBB2D(float centerx, float centery, float w, float h, float angle)
{
for(int i = 0; i < corner.length; ++i)
{
corner[i] = new Vector2D();
}
for(int i = 0; i < axis.length; ++i)
{
axis[i] = new Vector2D();
}
set(centerx,centery,w,h,angle);
}
public OBB2D(float left, float top, float width, float height)
{
for(int i = 0; i < corner.length; ++i)
{
corner[i] = new Vector2D();
}
for(int i = 0; i < axis.length; ++i)
{
axis[i] = new Vector2D();
}
set(left + (width / 2), top + (height / 2),width,height,0.0f);
}
public void set(float centerx,float centery,float w, float h,float angle)
{
float vxx = (float)Math.cos(angle);
float vxy = (float)Math.sin(angle);
float vyx = (float)-Math.sin(angle);
float vyy = (float)Math.cos(angle);
vxx *= w / 2;
vxy *= (w / 2);
vyx *= (h / 2);
vyy *= (h / 2);
corner[0].x = centerx - vxx - vyx;
corner[0].y = centery - vxy - vyy;
corner[1].x = centerx + vxx - vyx;
corner[1].y = centery + vxy - vyy;
corner[2].x = centerx + vxx + vyx;
corner[2].y = centery + vxy + vyy;
corner[3].x = centerx - vxx + vyx;
corner[3].y = centery - vxy + vyy;
this.center.x = centerx;
this.center.y = centery;
this.angle = angle;
computeAxes();
extents.x = w / 2;
extents.y = h / 2;
computeBoundingRect();
}
//Updates the axes after the corners move. Assumes the
//corners actually form a rectangle.
private void computeAxes()
{
axis[0].x = corner[1].x - corner[0].x;
axis[0].y = corner[1].y - corner[0].y;
axis[1].x = corner[3].x - corner[0].x;
axis[1].y = corner[3].y - corner[0].y;
// Make the length of each axis 1/edge length so we know any
// dot product must be less than 1 to fall within the edge.
for (int a = 0; a < axis.length; ++a)
{
float l = axis[a].length();
float ll = l * l;
axis[a].x = axis[a].x / ll;
axis[a].y = axis[a].y / ll;
origin[a] = corner[0].dot(axis[a]);
}
}
public void computeBoundingRect()
{
boundingRect.left = JMath.min(JMath.min(corner[0].x, corner[3].x), JMath.min(corner[1].x, corner[2].x));
boundingRect.top = JMath.min(JMath.min(corner[0].y, corner[1].y),JMath.min(corner[2].y, corner[3].y));
boundingRect.right = JMath.max(JMath.max(corner[1].x, corner[2].x), JMath.max(corner[0].x, corner[3].x));
boundingRect.bottom = JMath.max(JMath.max(corner[2].y, corner[3].y),JMath.max(corner[0].y, corner[1].y));
}
public void set(RectF rect)
{
set(rect.centerX(),rect.centerY(),rect.width(),rect.height(),0.0f);
}
// Returns true if other overlaps one dimension of this.
private boolean overlaps1Way(OBB2D other)
{
for (int a = 0; a < axis.length; ++a) {
double t = other.corner[0].dot(axis[a]);
// Find the extent of box 2 on axis a
double tMin = t;
double tMax = t;
for (int c = 1; c < corner.length; ++c) {
t = other.corner[c].dot(axis[a]);
if (t < tMin) {
tMin = t;
} else if (t > tMax) {
tMax = t;
}
}
// We have to subtract off the origin
// See if [tMin, tMax] intersects [0, 1]
if ((tMin > 1 + origin[a]) || (tMax < origin[a])) {
// There was no intersection along this dimension;
// the boxes cannot possibly overlap.
return false;
}
}
// There was no dimension along which there is no intersection.
// Therefore the boxes overlap.
return true;
}
public void moveTo(float centerx, float centery)
{
float cx,cy;
cx = center.x;
cy = center.y;
deltaVec.x = centerx - cx;
deltaVec.y = centery - cy;
for (int c = 0; c < 4; ++c)
{
corner[c].x += deltaVec.x;
corner[c].y += deltaVec.y;
}
boundingRect.left += deltaVec.x;
boundingRect.top += deltaVec.y;
boundingRect.right += deltaVec.x;
boundingRect.bottom += deltaVec.y;
this.center.x = centerx;
this.center.y = centery;
computeAxes();
}
// Returns true if the intersection of the boxes is non-empty.
public boolean overlaps(OBB2D other)
{
if(right() < other.left())
{
return false;
}
if(bottom() < other.top())
{
return false;
}
if(left() > other.right())
{
return false;
}
if(top() > other.bottom())
{
return false;
}
if(other.getAngle() == 0.0f && getAngle() == 0.0f)
{
return true;
}
return overlaps1Way(other) && other.overlaps1Way(this);
}
public Vector2D getCenter()
{
return center;
}
public float getWidth()
{
return extents.x * 2;
}
public float getHeight()
{
return extents.y * 2;
}
public void setAngle(float angle)
{
set(center.x,center.y,getWidth(),getHeight(),angle);
}
public float getAngle()
{
return angle;
}
public void setSize(float w,float h)
{
set(center.x,center.y,w,h,angle);
}
public float left()
{
return boundingRect.left;
}
public float right()
{
return boundingRect.right;
}
public float bottom()
{
return boundingRect.bottom;
}
public float top()
{
return boundingRect.top;
}
public RectF getBoundingRect()
{
return boundingRect;
}
public boolean overlaps(float left, float top, float right, float bottom)
{
if(right() < left)
{
return false;
}
if(bottom() < top)
{
return false;
}
if(left() > right)
{
return false;
}
if(top() > bottom)
{
return false;
}
return true;
}
public static float distance(float ax, float ay,float bx, float by)
{
if (ax < bx)
return bx - ay;
else
return ax - by;
}
public Vector2D project(float ax, float ay)
{
projVec.x = Float.MAX_VALUE;
projVec.y = Float.MIN_VALUE;
for (int i = 0; i < corner.length; ++i)
{
float dot = Vector2D.dot(corner[i].x,corner[i].y,ax,ay);
projVec.x = JMath.min(dot, projVec.x);
projVec.y = JMath.max(dot, projVec.y);
}
return projVec;
}
public Vector2D getCorner(int c)
{
return corner[c];
}
public int getNumCorners()
{
return corner.length;
}
public static float collisionResponse(OBB2D a, OBB2D b, Vector2D outNormal)
{
float depth = Float.MAX_VALUE;
for (int i = 0; i < a.getNumCorners() + b.getNumCorners(); ++i)
{
Vector2D edgeA;
Vector2D edgeB;
if(i >= a.getNumCorners())
{
edgeA = b.getCorner((i + b.getNumCorners() - 1) % b.getNumCorners());
edgeB = b.getCorner(i % b.getNumCorners());
}
else
{
edgeA = a.getCorner((i + a.getNumCorners() - 1) % a.getNumCorners());
edgeB = a.getCorner(i % a.getNumCorners());
}
tempNormal.x = edgeB.x -edgeA.x;
tempNormal.y = edgeB.y - edgeA.y;
tempNormal.normalize();
projAVec.equals(a.project(tempNormal.x,tempNormal.y));
projBVec.equals(b.project(tempNormal.x,tempNormal.y));
float distance = OBB2D.distance(projAVec.x, projAVec.y,projBVec.x,projBVec.y);
if (distance > 0.0f)
{
return 0.0f;
}
else
{
float d = Math.abs(distance);
if (d < depth)
{
depth = d;
outNormal.equals(tempNormal);
}
}
}
float dx,dy;
dx = b.getCenter().x - a.getCenter().x;
dy = b.getCenter().y - a.getCenter().y;
float dot = Vector2D.dot(dx,dy,outNormal.x,outNormal.y);
if(dot > 0)
{
outNormal.x = -outNormal.x;
outNormal.y = -outNormal.y;
}
return depth;
}
public Vector2D getMoveDeltaVec()
{
return deltaVec;
}
};
I'm now just unsure how to compute the point of contact when I hit a
wall.
You can represent a wall with a simple plane.
The OBB-vs-plane intersection test is the simplest separating axis test of them all:
If two convex objects don't intersect, then there is a plane where
the projection of these two objects will not intersect.
A box intersects plane only if the plane normal forms a separating axis. Compute the projection of the box center and the projected radius (4 dot products and a few adds) and you're good to go (you also get penetration depth for free).
The condition looks as follows:
|d| <= a1|n*A1| + a2|n*A2| + a3|n*A3|
Here:
d distance from the center of the box to the plane.
a1...a3 the extents of the box from the center.
n normal of the plane
A1...A3 the x,y,z-axis of the box
Some pseudocode:
//Test if OBB b intersects plane p
int TestOBBPlane(OBB b, Plane p)
{
// Compute the projection interval radius of b onto L(t) = b.c + t * p.n
float r = b.e[0]*Abs(Dot(p.n, b.u[0])) +
b.e[1]*Abs(Dot(p.n, b.u[1])) +
b.e[2]*Abs(Dot(p.n, b.u[2]));
// Compute distance of box center from plane
float s = Dot(p.n, b.c) – p.d;
// Intersection occurs when distance s falls within [-r,+r] interval
return Abs(s) <= r;
}
The OBB-vs-OBB intersection test is more complicated.
Let us refer to this great tutorial:
In this case we no longer have corresponding separating lines that are
perpendicular to the separating axes. Instead, we have separating
planes that separate the bounding volumes (and they are perpendicular
to their corresponding separating axes).
In 3D space, each OBB only has 3 unique planes extended by its faces,
and the separating planes are parallel to these faces. We are
interested in the separating planes parallel to the faces, but in 3D
space, the faces are not the only concern. We are also interested in
the edges. The separating planes of interest are parallel to the faces
of the boxes, and the separating axes of interest are perpendicular to
the separating planes. Hence the separating axes of interest are
perpendicular to the 3 unique faces of each box. Notice these 6
separating axes of interest correspond to the 6 local (XYZ) axes of
the two boxes.
So there are 9 separating axes to consider for edges collision in
addition to the 6 separating axes we already have found for the faces
collision. This makes the total number of possible separating axes to
consider at 15.
Here are the 15 possible separating axes (L) you will need to test:
CASE 1: L = Ax
CASE 2: L = Ay
CASE 3: L = Az
CASE 4: L = Bx
CASE 5: L = By
CASE 6: L = Bz
CASE 7: L = Ax x Bx
CASE 8: L = Ax x By
CASE 9: L = Ax x Bz
CASE 10: L = Ay x Bx
CASE 11: L = Ay x By
CASE 12: L = Ay x Bz
CASE 13: L = Az x Bx
CASE 14: L = Az x By
CASE 15: L = Az x Bz
Here:
Ax unit vector representing the x-axis of A
Ay unit vector representing the y-axis of A
Az unit vector representing the z-axis of A
Bx unit vector representing the x-axis of B
By unit vector representing the y-axis of B
Bz unit vector representing the z-axis of B
Now you can see the algorithm behind the OBB-OBB intersection test.
Let's jump to the source code:
2D OBB-OBB: http://www.flipcode.com/archives/2D_OBB_Intersection.shtml
3D OBB-OBB: http://www.geometrictools.com/LibMathematics/Intersection/Intersection.html
P.S: This link http://www.realtimerendering.com/intersections.html will be useful to those, who wish to go beyond planes and boxes.