I'm trying to make a pinball-style game for a school project for the Android in the SDK in Eclipse.
There's a really weird and super frustrating problem in which objects are moving without any code telling them to. Basically, each Wall instance contains 4 Line objects which are used for collision detection with the Ball. These Lines work the first time, but as soon as the ball collides with them once, then that Line somehow moves to another position on the screen.
I've been debugging it, and wouldn't ask if I hadn't already tried everything, but there is honestly no reason for the Line to shift anywhere. The way I handle a collision is by pushing the Ball to be 1px away from the wall, and then given new dx and dy (velocities) to move away. The code for checking for collisions is below, followed by the function that handles a collision to change the ball's position and velocity. Both are methods in the Ball class.
GameElement[] walls = currLevel.getWalls();
int i, j;
Line[] lines;
Line line;
RectF lineBounds;
boolean hadCollision = false;
for (i = 0; i < walls.length & !hadCollision; i++) {
lines = walls[i].getLines();
for (j = 0; j < lines.length & !hadCollision; j++) {
lineBounds = lines[j].getBounds();
if (lineBounds.intersect(point)) {
paint.setColor(Color.BLUE); // Colour ball blue.
reactToCollision3(lines[j]);
// TEST RESET!!!
//this.x = (float)(648+40);
//this.y = (float)(900-30);
hadCollision = true;
//printWallsLines();
}
}
}
and the function to handle the collision is:
public void reactToCollision3 (Line line) {
float liney = line.sy;
float linex = line.sx;
if (line.rotation == 0.0) { // HORIZONTAL EDGE
if (this.y > liney) { // Ball moving upward hits the bottom of a wall.
this.y = liney + this.radius + 1.0f;
} else { // Ball moving downward hits the top of a wall.
this.y = liney - this.radius - 1.0f;
}
this.dy *= -1.0f;
} else { // VERTICAL EDGE
if (this.x > linex) { // Ball moving leftward hits right edge of a wall.
this.x = linex + this.radius + 1.0f;
} else { // Ball moving rightward hits left edge of a wall.
this.x = linex - this.radius - 1.0f;
}
this.dx *= -1.0f;
}
So when I run this right now, the ball will bounce off a wall the first time it hits it, and then that line (edge of the wall) that it hit will be shifted elsewhere, but is not visible because the Wall is drawn as one unit so the Lines that comprise it don't affect the drawing.
If I comment out the lines for "this.x = ..." and "this.y = ...", then this problem doesn't happen anymore. Also, if I uncomment the test RESET lines for setting the ball's position in the above function, then the line doesn't shift then either. But as soon as I run this, it happens again.
I'm going insane looking for why this would happen. Please give me suggestions.
Thank you!
Did you intend to use bitwise &? (See ** in code) Your test for "!hadCollision" will fail and you will also be masking your wall length. I believe you meant to use &&
for (i = 0; i < walls.length **&** !hadCollision; i++) {
lines = walls[i].getLines();
for (j = 0; j < lines.length **&** !hadCollision; j++) {
lineBounds = lines[j].getBounds();
if (lineBounds.intersect(point)) {
paint.setColor(Color.BLUE); // Colour ball blue.
reactToCollision3(lines[j]);
// TEST RESET!!!
//this.x = (float)(648+40);
//this.y = (float)(900-30);
hadCollision = true;
//printWallsLines();
}
}
}
Related
I have to make a box move zig-zag all the way to the bottom of the screen. I figured out how to have the box start at the top left corner, move to the top right corner, then it moves down (one complete box of (x,y,90,90)). I'm stuck now because I realize that the next if-statement I want to write is fighting with the first if-statement to glide the box to the left end of the screen. I've tried while and for loops to overcome the problem but I honestly don't know what to do or where to do. I need the program to do this all the way down the screen and stop at rest(810,810,90,90) and stop. Any suggestions?
Here's my main code:
WBox v1;
int a = 0;
float x=0;
float y=0;
void setup() {
size(900, 900);
frameRate(1000);
v1 = new WBox();
}
void draw(){
background(255);
fill(0);
rect(x,y,90,90);
if(x<810){
x+=1;
} else if(y < 90){
y+=1;
} else if (x<=0){
x-=1;
}
}
Here's the class:
**class WBox{
PVector wb;
void box(int tempX, int tempY) {
wb = new PVector(tempX, tempY);
wb.mult(a);//
rect(wb.x, wb.y, 90, 90);
}
}
Full disclaimer: it would be better practice to use the WBox class differently to achieve this, but as you seem to be a beginner, I'm going to stick to easy stuff. You can ask me questions if you feel like it, I'll hang around for a while.
Instead of having a hardcoded number for movement speed, let's try it with a variable:
float boxSpeedX = 1;
The we just have to figure out an algorithm to get it to go down. The best way to do this is to use pseudocode.
In other words: write the logic behind the machine code. Something like this:
Rectangle must move horizontally until it reaches a border
When it reaches a border, it must go down 90 pixels
Then it moves horizontally toward the other side
When it reaches (810, 810) it should stop and rest.
Seems easy enough. Let's go closer to the code:
if (reached a border)
box move horizontally toward the other border
else
box move downward 90 pixels
if (box reached (810, 810)
stop
Closer!
// let's start with the stop condition
if (box reached 810, 810)
do nothing
else
if (box is on an horizontal line)
x += speed
if (box reached right side)
speed = -1 // so next time it'll go left
y +=1 // this way the box isn't on an horizontal line anymore
else if (box reached left side)
speed = 1
y += 1
else
y += 1
Now let's elaborate on some of the details:
(box is on an horizontal line):
horizontal lines are multiples of 90 pixels
(box reached right side)
x == 810
(box reached left side)
x == 0
We'll use the modulo operator to know if the y number is a multiple or 90. Take some time to understand this operator, it's really, really useful. In short, it will give you the remains of an integer division (for example, if you do 13/5, instead of having a number with decimals the integer division will say "2" and the modulo will say "3" because there's 2 times 5 in 13, not more "full 5", and there's 3 units left after this operation).
I feel like we're close enough, now. The rest is just translating this in code. I did it for you, and I've left comments in the code. You can copy and paste it into a Processing IDE and it'll run. You can build on this example to get your box to do whatever you want.
WBox v1;
int a = 0;
float x=0;
float y=0;
float boxSpeedX = 1;
void setup() {
size(900, 900);
frameRate(1000);
v1 = new WBox();
}
void draw() {
background(255);
fill(0);
rect(x, y, 90, 90);
moveBox();
}
void moveBox() {
// first check for the stop condition:
if (x != 810 || y != 810) { // the box will move only while the stop condition isn't met
// if the box is on a horizontal line, let's move it
if (y % 90 == 0) { // the '%' operator's name is 'modulo', it's awesome and useful
x += boxSpeedX;
// once the box has moved, let's check if we need to steer it down:
if (x == 0) { // if the box reaches the left side
boxSpeedX = 1; //next time the box moves horizontally, it'll go right
y += 1; // let's move down
} else if (x == 810) {
boxSpeedX = -1;
y += 1;
}
} else { // if the box isn't on an horizontal line, let's move it downward until it reaches a new line
y += 1;
}
}
}
class WBox {
PVector wb;
void box(int tempX, int tempY) {
wb = new PVector(tempX, tempY);
wb.mult(a);//
rect(wb.x, wb.y, 90, 90);
}
}
Have fun!
I am creating a Bouncing Ball Animation with JavaFX similar to the bouncing windows logo screen saver. The code I have now is decent but it will only bounce the ball in a clockwise manner. This is good generally but eventually the ball works itself around to a counter-clockwise rotation in which case it no longer looks realistic. I am stuck trying to find a way to calculate how the ball should bounce; in my mind it really comes down to what angel the ball comes in at. I am Using an AnimationTimer which Translates the ball a set amount each frame. When the Bounds of the ball meet a boundary the translating direction is changed it is at this meeting that I need a suggestion...
BallAnimation is an inner class.
class BallAnimation extends AnimationTimer{
private final Sphere ball;
private double movex = 0;
private double movey = 0;
private double xvariation = 0;
private double yvariation = 0;
private boolean right = true;
private boolean up = false;
private boolean changeColorRandomly = true;
private double rate = 1;
public BallAnimation(Sphere ball){
this.ball = ball;
ball.setLayoutX(200);
ball.setLayoutY(50);
}
public void handle(long now){
move(right,up);
Bounds ballBounds = ball.localToScene(ball.getBoundsInLocal());
if(ballBounds.intersects(rightWall.getBoundsInParent())){
calculateMotion(rightWall);
randomBounceAngle();
setRandomColor();
}
if(ballBounds.intersects(leftWall.getBoundsInParent())){
calculateMotion(leftWall);
randomBounceAngle();
setRandomColor();
}
if(ballBounds.intersects(ceiling.getBoundsInParent())){
calculateMotion(ceiling);
randomBounceAngle();
setRandomColor();
}
if(ballBounds.intersects(floor.getBoundsInParent())){
calculateMotion(floor);
randomBounceAngle();
setRandomColor();
}
}
private void calculateMotion(Line touchedWall){
if(touchedWall.equals(rightWall)){
right = false;
up = false;
}
if(touchedWall.equals(leftWall)){
right = true;
up = true;
}
if(touchedWall.equals(ceiling)){
right = true;
up = false;
}
if(touchedWall.equals(floor)){
right = false;
up = true;
}
}
public void move(boolean right, boolean up){
if(right && !up){
ball.setTranslateX((movex += (getRate() + xvariation)));
ball.setTranslateY((movey += (getRate() + yvariation)));
}
if(right && up){
ball.setTranslateX((movex += (getRate() + xvariation)));
ball.setTranslateY((movey -= (getRate() + yvariation)));
}
if(!right && up){
ball.setTranslateX((movex -= (getRate() + xvariation)));
ball.setTranslateY((movey -= (getRate() + yvariation)));
}
if(!right && !up){
ball.setTranslateX((movex -= (getRate() + xvariation)));
ball.setTranslateY((movey += (getRate() + yvariation)));
}
System.out.println("("+movex+", "+movey+")");
}
public double getRate(){
return rate;
}
public void setRate(double rate){
this.rate = rate;
}
public void randomBounceAngle(){
double ran = Math.random();
if(ran >= .50){
//shallow bounce angle
xvariation = 3;
yvariation = 2;
}else{
//sharp bounce angle
xvariation = 2;
yvariation = 3;
}
}
... The problem is when the ball hits the right boundary it bounces down and away, the bottom it bounces up and left, left boundary: up and right, ceiling: right and down. This is fine most of the time but sometimes it needs to bounce the other way.
Well, in a world of perfect physics, in angle is equal to out angle. If you are using an x/y axis, For reflection off the x-axis, negate the y component of the ball's velocity. For reflection off the y-axis, negate the x component of the ball's velocity.
I re-wrote pong in javascript using layers and detecting keyboard strokes for paddle control (this was in '00 or '01 with Netscape 4.7x). I cheated, and set up functions to move the ball in 8 directions. If the ball was traveling along an axis (straight left/right or up/down) a quick random number provided a different bounce coming out. Otherwise, bounce out at same angle in.
Here is a function to reflect a vector around a normal. It can be used to create a bounce, by reflecting the velocity vector of the ball around the normal of the wall (or the normal of the side of another object) that the ball is bouncing off of.
private Point2D reflect(Point2D vector, Point2D normal) {
return vector.subtract(normal.multiply(vector.dotProduct(normal) * 2));
}
It is part of an implementation for a sample breakout game I created based on the example code in this question.
The code shown for the vector-based reflection uses the formula provided in the answer to this question, which is translated directly to JavaFX classes:
How to get a reflection vector?
𝑟=𝑑−2(𝑑⋅𝑛)𝑛 where 𝑑⋅𝑛 is the dot product and 𝑛 must be normalized.
Please note that, if you search, there are many math tutorials and Stackoverflow questions that talk about functions and methods for performing reflection.
A particular case for balls bouncing off vertical or horizontal surfaces such as walls or bricks in a breakout game is that the lines which the ball is bouncing off of are parallel to the x and y axes of the coordinate system, so the bounce can be performed by negating the x or y values of the velocity vector. See the example code in this question or answers to other questions on reflection for an example of this simplification if it is something you wish to use.
if (topWall) {
dy = dy * -1;
}
if (leftWall || rightWall) {
dx = dx * -1;
}
if(bottomWall) {
dy = dy * -1;
}
I am simply trying to make a generic way to check for collision between rectangles. If you hit the top of a rectangle stop moving. However my rectangles seem to stop regardless of the x coordinate. I have three rectangles falling and one static rectangle that doesn't move, they are supposed to all fall on top of this static rectangle however when I run the code this happens.
Here is the collision handler code
float distanceY, furthestLeft;
public void update() {
for (int i = 0; i < stageObjects.size(); i++) {
iPos = stageObjects.get(i).getPosition();
iDim = stageObjects.get(i).getDimensions();
for(int k = 0; k < stageObjects.size(); k++){
kPos = stageObjects.get(k).getPosition();
kDim = stageObjects.get(k).getDimensions();
if(k == i){
continue;
}
distanceY = assignDy();
furthestLeft = assignDx();
//DistanceY is the subtraction of one objects y coordinate and
// if it equals 0 then they are colliding and the furthest left
// is the bottom right x coord of the furthest left objects x
//coord so it should check if this x is contained within an
// objects left x coord to right x coord
if(distanceY <= 1 && distanceY >= 0 && furthestLeft >= iPos.x && furthestLeft <= iPos.x + iDim.x){
stageObjects.get(i).react(Tuples.HIT_BOTTOM);
stageObjects.get(k).react(Tuples.HIT_FROM_TOP);
System.out.println("Collision: " + stageObjects.get(i).toString() + " with " +
stageObjects.get(k).toString());
}
}
}
}
}
public float assignDy(){
if(kPos.y > iPos.y){
return Math.abs(kPos.y - (iPos.y + iDim.y));
}else
return Math.abs(iPos.y - (kPos.y + kDim.y));
}
public float assignDx(){
if(kPos.x > iPos.x){
return kPos.x + kDim.x;
}else
return iPos.x + iDim.x;
}
The error lies here and here is the react method
public void react(int occurence){
if(occurence == Tuples.HIT_BOTTOM){
velocity.y = 0;
}
}
However, if they are further apart the code works perfectly look.
I have also noticed that the rectangle can fall through other rectangles if it is to the left of another rectangle, but if it further to the right at all it will get hung as if it landed on the rectangle. The only reason the above image worked is because the furthest right fell first, anything to the left to another rectangle will get hung as well if it falls after the rectangle to the left
I just don't see what exactly I am doing wrong any help is greatly appreciated!
Change iPos in the condition to something more generic, assign a variable like assignDx and dy to ccheck if Ipos or kPos is what you need to check for like so
public void assignOx(Vector2 ox){
if(kPos.x > iPos.x){
ox.x = iPos.x;
ox.y = iPos.x + iDim.x;
}else{
ox.x = kPos.x;
ox.y = kPos.x + kDim.x;
}
}
So I understand that I'm not coding this the best way possible at the moment; this is a sort of test run. What I'm trying to do is wall collisions using rectangles and the intersects property (sorry if I'm not using the correct terminology). So far I have 2 rectangles on screen. 1 the player controls and the other which the play is colliding with. When they collide the player stops moving. The problem is that if the player is trying to move into the rectangle while they are already colliding then the player can't move in any direction perpendicular to the movement ie if the player is holding the right arrow key moving into the rectangle, then they cannot move up or down. The game works on the premise that if your x or y coordinates aren't valid, then you will be moved back to the last valid coordinate recorded but I'm having trouble detecting the valid x and y coordinate separately. Here is the code:
public void Collision()
{
if(x < 0)
x = 0;
if(x > 400 - width)
x = 400 - width;
if(y < 0)
y = 0;
if(y > 300 - height)
y = 300 - height;
rect1 = new Rectangle(x, y, 16, 16);
rect2 = new Rectangle(sx, sy, wid, hei);
if(!rect1.intersects(rect2))
{
validX = true;
validY = true;
}
else
{
validX = false;
validY = false;
}
if(validX)
{
lastValidX = x;
}
if(validY)
{
lastValidY = y;
}
if(!validX)
{
x = lastValidX;
}
if(!validY)
{
y = lastValidY;
}
}
The Collision() method in the Guy class is where I'm having the trouble I believe. Yes my code is pretty messy right now but this is only a test.
Thanks, David.
You can implement what you're describing by doing extra logic around here (i.e. detecting cases when one is false and the other is true):
if(!rect1.intersects(rect2))
{
validX = true;
validY = true;
}
else
{
validX = false;
validY = false;
}
However, it seems like maybe you shouldn't be allowing the rectangles to ever be in a "colliding" state in the first place. For example, you can change the Move method to do something like
public void Move()
{
int oldX = x, oldY = y;
x += dx;
y += dy;
if (Collision()) {
x = oldX;
y = oldY;
}
}
So I'm doing a simple collision detection where the player is moved downward 3 every time it's updated and not on the ground, but when it is on the ground it moves the difference of it's position, and the grounds. But I'm getting jitters (It's moving down and up too much, and not being perfectly at a standstill).
My question is: How do I make this code calculate the difference properly?
Vector3f pos = new Vector3f(25,-50,25);
//Vector3f pos = new Vector3f(25,-50,25); isn't actually in the update method,
//but is in the object's constructer.
onGround = false;
Vector3f projPos = new Vector3f(pos);
projPos.y += fallSpeed;
//get a vector of all the triangles touching the player
Vector<Tri> tris = getTrisTouching(pos);
float minY;
//make it so if we don't have a list we don't get tossed into infinity
if(tris.size() > 0) minY = Float.POSITIVE_INFINITY;
else minY = 0;
for(int i = 0; i < tris.size(); i++){
Tri tri = tris.get(i);
if(projPos.y + radius <= tri.max.y){
float difference = tri.min.y - projPos.y;
if(difference < minY) minY = difference;
onGround = true;
}
}
if(onGround){
pos.y = minY;
}
else{
pos.y = projPos.y;
}
Your collision detection code (the 3 lines inside the first 'if' statement) is confusing but let's assume that when you're on the ground, the player doesn't touch any triangles (since it might be 1 pixel above them), so next frame you are moved down, then next frame you are moved back up.
A fix maybe would be to use projPos inside your for-loop instead of the actual position.