Processing - Add mousePressed to an animation with 100 balls - java

I want to create an ArrayList of ball objects, which should be in a loop until there are 100 pieces.
Now my problem: I must implement a function hitTest, so that when you click on a ball it gets removed. In the same position, there should appear two balls then, which go into a different direction. I included mousePressed and a private boolean but it doesn't work and I don't know why.
Can someone help me? I am so lost...
Here's my code so far:
Tab 1
ArrayList<Ball> balls;
void setup()
{
size(800,800);
balls = new ArrayList<Ball>();
for(int i = 0; i<100; i++)
{
drawBall();
}
}
void draw()
{
background(255);
//b.update();
for(int i= 0; i<balls.size(); i++)
{
balls.get(i).update();
}
}
void drawBall()
{
Ball b = new Ball();
balls.add(b);
}
void mousePressed()
{
for(int i = balls.size()-1; i>=0; i--)
Ball ball = balls.get (i);
if (ball.hitTest())
{
balls.get(i).hitTest();
balls.remove(ball);
Ball b1 = new Ball(mouseX, mouseY);
Ball b2 = new Ball(mouseX, mouseY);
balls.add(b1);
balls.add(b2);
}
}
/*Tab 2:*/
class Ball
{
private float x;
private float y;
private float ballSize;
private float dirX;
private float dirY;
private boolean moving = true;
Ball()
{
this.x = width/2;
this.y = height/2;
this.ballSize = random(10.0,30.0);
this.dirX = random(-3.0,3.0);
this.dirY = random(-3.0,3.0);
if(this.dirX<1.0 && this.dirX>1.0)//1 statt -1 macht zufälliger
{
this.dirX = 1.0;
}
if(this.dirY<1.0 && this.dirY>1.0)
{
this.dirY = 1.0;
}
}
public void update()
{
stroke(255);
fill(random(255),random(255),random(255), random(255));
ellipse( this.x, this.y, this.ballSize, this.ballSize);
if(this.moving == true)
{
this.x += this.dirX;
this.y += this.dirY;
}
if(this.x+ this.ballSize/2> width ||this.x- this.ballSize/2<0)
{
this.dirX= dirX*-1;
}
if(this.y+ this.ballSize/2> height ||this.y- this.ballSize/2<0)
{
this.dirY= dirY*-1;
}
}
private boolean hitTest()
{
float d = dist(this.x, this.y, mouseX, mouseY);
if (d < ballSize)
{
println("h");
return true;
}
else
{
return false;
}
}
}

You are very very close! Most of the code you posted is pretty much there.
There are a few syntax issues:
The instructions within the for loop in mousePressed() need to be enclosed:
This section here:
for(int i = balls.size()-1; i>=0; i--)
Ball ball = balls.get (i);
if (ball.hitTest())
{
balls.get(i).hitTest();
balls.remove(ball);
Ball b1 = new Ball(mouseX, mouseY);
Ball b2 = new Ball(mouseX, mouseY);
balls.add(b1);
balls.add(b2);
}
should be:
for(int i = balls.size()-1; i>=0; i--){
Ball ball = balls.get (i);
if (ball.hitTest())
{
balls.get(i).hitTest();
balls.remove(ball);
Ball b1 = new Ball(mouseX, mouseY);
Ball b2 = new Ball(mouseX, mouseY);
balls.add(b1);
balls.add(b2);
}
}
This part on it's own is valid syntax:
for(int i = balls.size()-1; i>=0; i--)
Ball ball = balls.get (i);
You can use a for loop without curly braces, but only if you plan to use a single instruction within the loop. In this case it's retrieving the Ball, which isn't very useful on it's own. Assuming you want to check the condition for each ball, that is a list of multiple instructions which need to be enclosed between {}.
(It's a shame Processing only displays this unhelpful error in particular case:
Consider adding "="
)
One other minor detail is the duplicate call to hitTest():
ball is the same as balls.get (i) in the context above, therefore calling balls.get(i).hitTest(); after calling ball.hitTest() is redundant. (also nothing is done with the boolean result it returns):
for(int i = balls.size()-1; i>=0; i--){
Ball ball = balls.get (i);
if (ball.hitTest())
{
balls.remove(ball);
Ball b1 = new Ball(mouseX, mouseY);
Ball b2 = new Ball(mouseX, mouseY);
balls.add(b1);
balls.add(b2);
}
}
Moving forward you would run into this error:
The constructor "Ball(int, int)" does not exist
The new Ball(mouseX, mouseY); constructor is called in mousePressed(), however your Ball class only supplies the default one with no arguments (e.g. Ball(), not Ball(x,y).)
You can easily copy/paste your existing constructor and modify it to add the x, y parameters:
Ball(float x, float y)
{
this.x = x;
this.y = y;
this.ballSize = random(10.0,30.0);
this.dirX = random(-3.0,3.0);
this.dirY = random(-3.0,3.0);
if(this.dirX<1.0 && this.dirX>1.0)//1 statt -1 macht zufälliger
{
this.dirX = 1.0;
}
if(this.dirY<1.0 && this.dirY>1.0)
{
this.dirY = 1.0;
}
}
Another option, avoiding duplicated code (which is not recommended) is to call the first constructor:
Ball(float x, float y){
this();// this() calls Ball()
this.x = x;
this.y = y;
}
Full code listing:
ArrayList<Ball> balls;
void setup()
{
size(800,800);
balls = new ArrayList<Ball>();
for(int i = 0; i<100; i++)
{
drawBall();
}
}
void draw()
{
background(255);
//b.update();
for(int i= 0; i<balls.size(); i++)
{
balls.get(i).update();
}
}
void drawBall()
{
Ball b = new Ball();
balls.add(b);
}
void mousePressed()
{
for(int i = balls.size()-1; i>=0; i--){
Ball ball = balls.get (i);
if (ball.hitTest())
{
balls.remove(ball);
Ball b1 = new Ball(mouseX, mouseY);
Ball b2 = new Ball(mouseX, mouseY);
balls.add(b1);
balls.add(b2);
}
}
}
/*Tab 2:*/
class Ball
{
private float x;
private float y;
private float ballSize;
private float dirX;
private float dirY;
private boolean moving = true;
Ball()
{
this.x = width/2;
this.y = height/2;
this.ballSize = random(10.0,30.0);
this.dirX = random(-3.0,3.0);
this.dirY = random(-3.0,3.0);
if(this.dirX<1.0 && this.dirX>1.0)//1 statt -1 macht zufälliger
{
this.dirX = 1.0;
}
if(this.dirY<1.0 && this.dirY>1.0)
{
this.dirY = 1.0;
}
}
Ball(float x, float y){
this();
this.x = x;
this.y = y;
}
public void update()
{
stroke(255);
fill(random(255),random(255),random(255), random(255));
ellipse( this.x, this.y, this.ballSize, this.ballSize);
if(this.moving == true)
{
this.x += this.dirX;
this.y += this.dirY;
}
if(this.x+ this.ballSize/2> width ||this.x- this.ballSize/2<0)
{
this.dirX= dirX*-1;
}
if(this.y+ this.ballSize/2> height ||this.y- this.ballSize/2<0)
{
this.dirY= dirY*-1;
}
}
private boolean hitTest()
{
float d = dist(this.x, this.y, mouseX, mouseY);
if (d < ballSize)
{
println("h");
return true;
}
else
{
return false;
}
}
}
I've noticed hitTest() is using this condition:
if (d < ballSize)
ballSize is used here: ellipse( this.x, this.y, this.ballSize, this.ballSize);
which means it's the diameter. Normally for a circle hit test you'd check if the distance is smaller than the radius, not the diameter. Perhaps this is not a bug, but a feature :) ? Allowing for twice the size for easier clicks outside the ball area ?
One other minor suggestion: if you don't want the colours to randomly change multiple times a second, you can create a color property to store the random colour once in the Ball constructor, then simply reference that set colour in update()
You've got most of the tasks done. Remember to take it easy, re-read the code after a break and picture how it would run in your head before running it. It might help find issues like these. It may seem counterintuitive to slow down, but doing so and paying attention to detail will actually speed you up on the long run. Good luck

A small change which could make all the difference would be to add a constructor. Constructors can call one another and have different parameters, so each overload can have small differences.
The best way to write these isn't to copy-and-paste your constructor but to write them in a cascade which avoids making twice the same operation.
As an example, here you could use 2 constructors: one which just creates a ball, and one which creates a ball with specific coordinates. Their signatures would be like this:
Ball() // this one creates a ball with pre-choosen coordinates
Ball(float x, float y) // this one creates a ball at these coordinates
In a cascade, always try to keep your signature the same and add stuff, by which I mean do it like this (this is a completely fictive class):
// a correct way to cascade constructors
MyClass()
MyClass(int a, int b)
MyClass(int a, int b, boolean c)
MyClass(int a, int b, boolean c, String d)
Now, there is a prefered order in which you can place your parameters, but I won't go into this as most of the time devs adds them when they need them. The important thing is to avoid doing this:
// a bad way to cascade constructors
MyClass()
MyClass(int a, int b)
MyClass(String d)
MyClass(int a, boolean c, int b)
// etc
I'm saying this because you want the constructors in your cascade to call one another in an orderly manner which won't multiply manipulations. You don't want a constructor to set some coordinates only for another one to set different values to these same variables!
Of course, there can be a good reason to create a constructor like the one that just takes a String as parameter, but when you will have a situation which calls for this kind of modification, you'll have to think it through so your constructors keep some kind of internal logic. You don't want to spaghetti your constructors, that would be worse than having only one constructor which would be very complicated.
To continue with the example, here is how these constructors could call one another:
// a correct way to cascade constructors
MyClass() {
this(0, 0); // a constructor can call another one by using the keyword 'this'
}
MyClass(int a, int b) {
this(a, b, true); // keep it simple: the constructors inside the cascade typically just pass the parameters to more complex constructors
}
MyClass(int a, int b, boolean c) {
this(a, b, c, "defaultName"); // every ignored value is set to their "default value"
}
MyClass(int a, int b, boolean c, String d) {
// at last, the "real" constructor, which will do most of the work
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
Notice that, in the last example, if you call the MyClass() constructor, every parameter of the "real" constructor - the most complex one - will be a default value.
So... to come back to your specific situation, we have a cascade with only 2 different signatures:
Ball()
Ball(float x, float y)
So, to reproduce the logic I just laid out, you could modify your code like this:
Ball() {
// this constructor will produce balls just like you're used to
// I just moved the x/y assignation here
this(width/2, height/2);
}
Ball(float x, float y) {
// this is your "most complex constructor", you can use it to spawn a ball with custom coordinates
this.x = x;
this.y = y;
this.ballSize = random(10.0, 30.0);
this.dirX = random(-3.0, 3.0);
this.dirY = random(-3.0, 3.0);
if (this.dirX<1.0 && this.dirX>1.0)//1 statt -1 macht zufälliger
{
this.dirX = 1.0;
}
if (this.dirY<1.0 && this.dirY>1.0)
{
this.dirY = 1.0;
}
}
Please be careful and considerate when you add a constructor. Always reflect on what you're doing, as many constructors serves no purpose but to clutter the program - for now you probably won't notice this side effect, but keep it in mind for when you'll work on bigger projects, especially with teammates or at work. Projects which development and support lasts for years (like real programs) have a tendency to gather a lot of clutter, especially when devs make "small changes" all the time without regard for the structure of the program.
Have fun!

Related

How do I reflect an angle of a Point2D object

I'm trying to make a pong game using JavaFX and I've decided to use Point2D for the paddle and ball positions.
I created this method to check for wall collisions
public void checkWallCollision(){
boolean ballHitBottom = posBall.getY() > 500;
boolean ballHitTop = posBall.getY() < 50;
boolean ballHitLeft = posBall.getX() < 0;
boolean ballHitRight = posBall.getX() > 725;
if (ballHitTop || ballHitBottom){
ballDirVector = ballDirVector.multiply(-1);
}
if(ballHitLeft || ballHitRight){
ballDirVector = ballDirVector.multiply(-1);
}
}
But with a Point2D object like that, I can't just multiply it like that. I need the angle to be reflected. I guess another thing I could show to help is how I launch the ball. In this method I do have the angle, but I'm not sure what way I should go about accessing it.
public void launchBall(){
// Launch the ball
boolean ballDirection = random.nextBoolean();
// bound to an acute angle on start
double ballAngle = Constants._PADDLE_ANGLES[random.nextInt(5) + 1];
if (ballDirection){
ballAngle *= -1;
}
ballSpeed = Constants._BallStartSpeed;
ballDirVector = new Point2D(Math.cos(ballAngle), Math.sin(ballAngle));
}

I'm trying to create random object using arrays and class with random locations?

I'm trying to create a game using classes, in which objects move to random locations and use arrays in order to add up in a random amount. Can someone help me to code this better as it isn't working? I'm using the software "Processing" by the way.
My Code
My Class
*final color ALIEN_COLOR = color(30, 100, 0);
PImage background;
int x=0; //global variable background location
Superhero hero1;
Alien [] invader1 = new Alien[8];
void setup(){
size(800,400);
background = loadImage("spaceB.jpg");
background.resize(width,height);
hero1 = new Superhero(10, height/2);
for(int i = 0; i < invader1.length; i++){
invader1[i] = new Alien();
invader1 = new Alien(width,300);
}
} // setup ends
void draw ()
{
drawBackground();
hero1.render();
invader1.render();
if(invader1.move() == false){
invader1 = new Alien(width, 500);
}
} // draw ends*
and object as:
***class Alien{
int x;
int y;
Alien(int x, int y){
this.x = x;
this.y = y;
}
void render(){
fill(ALIEN_COLOR);
rect(x, y, 50, 50);
}
boolean move(){
x = x - 1;
return (x >= 0);
}
}***
The error messages that I received are:
the constructor Alien() doesn't exist.
mismatch, Defenders.Alien doesn't match Defenders.Alien[]
You are calling invader1[i] = new Alien(); but you do not have no-arg constructor in Alien class. Declare a no-arg constructor in Alien class as follows to get rid of the issue:
Alien() {
// Put here some initialization code if needed else leave it as it is
}

Processing / How to make selectable object, that moves where i click

that really sucks ... i know how to make classes, objects, interfaces, loops etc . But everytime i try to make a unit or more than one, that is moving (when i select it) to the point where i click i get errors, errors and errors .... Why the hell theres nowhere a tutorial for that ?
My new class looks so atm :
class Unit {
int X;
int Y;
int Breite;
int Laenge;
int ID;
boolean ausgewaelht = false;
Unit() {
}
Unit(int x, int y, int breite, int laenge) {
}
void create(UnitContent function) {
function.form();
}
void move(float geschwindigkeit) {
if(isTriggerd(X,Y,Breite,Laenge) == true){
X = X+(int)geschwindigkeit;
if(X > width) {
X = 0;
}
}
}
void setXandY(int x , int y) {
X = x;
Y = y;
}
void setBreiteandLaenge(int breite, int laenge) {
Breite = breite;
Laenge = laenge;
}
void setID(int id) {
ID = id;
}
int getX() {
return X;
}
int getY() {
return Y;
}
int getBreite() {
return Breite;
}
int getLaenge() {
return Laenge;
}
int getID() {
return ID;
}
boolean isTriggerd(int x, int y, int breite, int laenge) {
if(mouseX > x && mouseX < x+breite && mouseY > y && mouseY < y+laenge ) {
return true;
}
else {
return false;
}
}
}
IS there something i forgot ?
And how do i display 10 or 50 units of them?
sorry for my bad english :) and thx for your help
Some friendly advice: your tone comes off sounding a bit demanding, and your description is very vague. You'll have much better luck if you try to make it easy for other people to help you.
Tell us exactly what errors you're getting.
Post an MCVE with enough code so we can run it, but not any code that isn't directly related to your problem.
Try breaking your problem down into small steps, and only ask one specific question at a time.
Check out the question checklist and make sure you've done everything on the list.
Why the hell theres nowhere a tutorial for that ?
Keep in mind that the people answering questions on Stack Overflow are doing so for free, in their spare time. The people developing Processing are doing so for free, in their spare time. Even so, there are a ton of tutorials on your problems. Have you tried searching on google?
Here is a tutorial that does exactly what you're looking for. These examples come with the Processing editor (go to File -> Examples). The reference is another great resource that you should check out.
All of that being said, I'll walk you through solving this problem, and hopefully how to solve other problems in the future.
Step 0: Break your problem down into smaller steps. This is the golden rule of programming. Whenever you're stuck, go back to this step. This step fuels the rest of the steps, and it should be the first, second, and third thing you do whenever you're stuck.
Step 1: Can you draw a single object? Don't worry about interaction or multiple objects or anything else, just draw a single object. Get that code working first.
Here's code that draws a single circle:
void setup(){
size(500, 500);
ellipseMode(CENTER);
}
void draw(){
background(0);
ellipse(100, 200, 50, 50);
}
Step 2: Can you encapsulate the information needed to draw the object in a class? Again, only worry about the next small step- don't worry about multiple shapes yet. Just get a class working for a single object. Here is how we might encapsulate the data for our circle:
Circle circle;
void setup() {
size(500, 500);
ellipseMode(CENTER);
circle = new Circle(100, 200, 50);
}
void draw() {
background(0);
circle.draw();
}
class Circle {
float x;
float y;
float r;
public Circle(float x, float y, float r) {
this.x = x;
this.y = y;
this.r = r;
}
void draw() {
ellipse(x, y, r, r);
}
}
If you have trouble on this step, then you can post something like this small example with a more specific question, and it'll be much easier to help you than if you post a section of your entire sketch without any specific errors.
Step 3: Can you add some simple user interaction logic to your circle class? Don't worry about clicking yet, just try to change the color of the circle when you move your mouse over it.
Circle circle;
void setup() {
size(500, 500);
ellipseMode(RADIUS);
circle = new Circle(100, 200, 50);
}
void draw() {
background(0);
circle.draw();
}
class Circle {
float x;
float y;
float r;
public Circle(float x, float y, float r) {
this.x = x;
this.y = y;
this.r = r;
}
void draw() {
if(dist(mouseX, mouseY, x, y) < r){
//mouse is inside circle
fill(0, 255, 0);
}
else{
//mouse is outside circle
fill(0, 0, 255);
}
ellipse(x, y, r, r);
}
}
By breaking your bigger problem down into these smaller steps, it becomes much easier to debug your code than if you try to write your entire sketch at one time.
Step 4: Can you improve your interaction code to detect a click? Can you move the circle when a drag is detected?
You should probably break those steps down even further, but for the sake keeping this post short(er), I've combined them into one:
Circle circle;
void setup() {
size(500, 500);
ellipseMode(RADIUS);
circle = new Circle(100, 200, 50);
}
void draw() {
background(0);
circle.draw();
}
class Circle {
float x;
float y;
float r;
public Circle(float x, float y, float r) {
this.x = x;
this.y = y;
this.r = r;
}
void draw() {
if(dist(mouseX, mouseY, x, y) < r){
//mouse is inside circle
if(mousePressed){
//mouse is being dragged
fill(255, 0, 0);
//move the circle to the mouse position
x = mouseX;
y = mouseY;
}
else{
//mouse is not clicked
fill(0, 255, 0);
}
}
else{
//mouse is outside circle
fill(0, 0, 255);
}
ellipse(x, y, r, r);
}
}
Step 5: Can you make it work for multiple objects? If you've done a good job of breaking your problem down into small steps and encapsulating your logic into a class, then this step becomes pretty easy.
ArrayList<Circle> circles = new ArrayList<Circle>();
void setup() {
size(500, 500);
ellipseMode(RADIUS);
for (int i = 0; i < 10; i++) {
circles.add(new Circle(random(width), random(height), random(10, 50)));
}
}
void draw() {
background(0);
for (Circle circle : circles) {
circle.draw();
}
}
class Circle {
float x;
float y;
float r;
public Circle(float x, float y, float r) {
this.x = x;
this.y = y;
this.r = r;
}
void draw() {
if (dist(mouseX, mouseY, x, y) < r) {
//mouse is inside circle
if (mousePressed) {
//mouse is being dragged
fill(255, 0, 0);
//move the circle to the mouse position
x = mouseX;
y = mouseY;
} else {
//mouse is not clicked
fill(0, 255, 0);
}
} else {
//mouse is outside circle
fill(0, 0, 255);
}
ellipse(x, y, r, r);
}
}
To summarize, you need to break your problem down into smaller steps and take on those steps one at a time. If you get stuck on a specific step (or if you don't understand one of the steps in my answer) then you can post a small example MCVE like my examples above, and ask a specific question.
Happy coding!
It really doesn't look like you have any graphical code except for some coordinates. In order to display something in Java you have to use a library that supports it like Swing or JavaFX. Here's a SO question that will give you some idea of what the differences are. Happy coding!

Method not recognizing the call this in JavaFX application

I'm working on a program that displays circles colliding with the wall and with themselves.
I'm having trouble with the method that will compensate for collisions.
public class bouncyFX extends Application {
public ArrayList<Ball> arr = new ArrayList<Ball>();
public static void main(String[] args) {
launch(args);
}
static Pane pane;
#Override
public void start(final Stage primaryStage) {
pane = new Pane();
final Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
pane.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(final MouseEvent event) {
final Ball ball = new Ball(event.getX(), event.getY(), 40, Color.AQUA);
ball.circle.relocate(event.getX(), event.getY());
pane.getChildren().addAll(ball.circle);
arr.add(ball);
final Bounds bounds = pane.getBoundsInLocal();
final Timeline loop = new Timeline(new KeyFrame(Duration.millis(10), new EventHandler<ActionEvent>() {
double deltaX = ball.ballDeltaX;
double deltaY = ball.ballDeltaY;
public void handle(final ActionEvent event) {
ball.circle.setLayoutX(ball.circle.getLayoutX() + deltaX);
ball.circle.setLayoutY(ball.circle.getLayoutY() + deltaY);
final boolean atRightBorder = ball.circle.getLayoutX() >= (bounds.getMaxX()-ball.circle.getRadius());
final boolean atLeftBorder = ball.circle.getLayoutX() <= (bounds.getMinX()+ball.circle.getRadius());
final boolean atBottomBorder = ball.circle.getLayoutY() >= (bounds.getMaxY()-ball.circle.getRadius());
final boolean atTopBorder = ball.circle.getLayoutY() <= (bounds.getMinY()+ball.circle.getRadius());
if(atRightBorder || atLeftBorder)
deltaX *= -1;
if(atBottomBorder || atTopBorder)
deltaY *= -1;
for(int i = 0; i<arr.size(); i++){
for(int j = i+1; j<arr.size()-1; j++){
arr.get(i).collisionMagnitued(arr.get(j));
}
}
}
}));
loop.setCycleCount(Timeline.INDEFINITE);
loop.play();
}
});
}
class Ball{
public Circle circle;
public double ballDeltaX = 3;
public double ballDeltaY = 3;
public void AddBall(Ball b){
arr.add(b);
}
public Ball(double X, double Y, double Rad, Color color) {
circle = new Circle(X, Y, Rad);
circle.setFill(color);
}
private boolean defineCollision(Ball b){
double xd = this.circle.getLayoutX() - b.circle.getLayoutX();
double yd = this.circle.getLayoutY() - b.circle.getLayoutY();
double sumRad = this.circle.getRadius() + b.circle.getRadius();
double squareRad = Math.pow(sumRad, 2);
double distSquare = Math.pow(xd, 2) + Math.pow(yd, 2);
if(distSquare <= squareRad){
return true;
}return false;
}
public void collisionMagnitued(Ball b){
if(this.defineCollision(b)){
double tempDeltaX = ballDeltaX;
double tempDeltaY = ballDeltaY;
if((this.ballDeltaX < 0 && b.ballDeltaX > 0) || (this.ballDeltaX >0 && b.ballDeltaX <0)){
this.ballDeltaX *= -this.ballDeltaX;
b.ballDeltaX *= -b.ballDeltaX;
System.out.println("tredje");
}
if((this.ballDeltaY < 0 && b.ballDeltaY > 0) || (this.ballDeltaY > 0 && b.ballDeltaY < 0)){
this.ballDeltaY *= -this.ballDeltaY;
b.ballDeltaY *= -b.ballDeltaY;
System.out.println("fjärde");
}
else{
System.out.println("Knull");
this.ballDeltaX *= -1;
b.ballDeltaX *= -1;
}
}
}
}
}
The Balls (or circles) are created and are bouncing against the Bounds as expected.
The Collision detection method works as I'm getting print statements inside the last method. However, it seems that there's something wrong with either my ArrayList not being filled with objects or the method trying to compare the parameter Ball and the Ball that calls the method.
Am I way off? Not sure how I'm suppossed to go forth from here.
I see a few issues with your logic:
The first problem is that when the balls "bounce" off the boundaries of the pane, you don't change their ballDeltaX or ballDeltaY values (you just change a local value in the animation loop and use the local value to update the position). So the first time two balls collide, both of their ballDeltaX and ballDeltaY values are equal to +3 (the initial value), which may not represent the actual direction the animation loop is moving them in. In fact, you never actually use any updated values of ballDeltaX or ballDeltaY to compute the new positions; you get the initial values of those variables, copy them into deltaX and deltaY, and then just compute the new positions using deltaX and deltaY. So if you change ballDeltaX and ballDeltaY, the animation loop never sees the change.
The for loops look wrong to me; I don't think they compare the last two elements of the list. (When i = arr.size()-2 in the penultimate iteration of the outer loop, your inner loop is for (int j = arr.size() - 1; j < arr.size() -1; j++) {...} which of course never iterates.) I think you want the bounding conditions to be i < arr.size() - 1 and j < arr.size(), i.e. the other way around.
And then your if/else structure in collisionMagnitued(...) is probably not exactly what you want. I'm not sure what you're trying to implement there, but the else clause only kicks in if the second if is false, and no matter what happens in the first if.
Finally, you are starting a new animation on each mouse click. So, for example, if you have three balls bouncing around, you have three animation loops running, each of which is updating values when the balls collide. You need to start just one loop; it shouldn't do any harm if it refers to an empty list.

Multiple classes in processing

I'm making a game where you're controlling a square, and objects will spawn in random places, which you have to pick up to get points, while also having to dodge big squares going from side to side. Right now I have 2 classes (one for enemies - big squares -, and one for the hero), I haven't done the point system yet with the spawning objects, but that's not what I'm trying to do now.
So my problem right now is that I don't really know how to make the person lose the game/a life when you touch the "enemies" with your hero. I would know how to do it without classes, but I'd like to know how to do it when they are in separate classes.
If someone could explain with code and comments how this would be done, it would help me out a lot :) (I read something about 'extends' for classes but I'm not sure if this is what I should use or not).
Here's a screenshot of what my game looks like at the moment, just to better illustrate it:
Here's the main code page:
Hero myHero = new Hero(400,480,5);
Enemies myEnemies = new Enemies(50,50,10);
Enemies myEnemies2 = new Enemies(50,350,15);
Enemies myEnemies3 = new Enemies(50,650,12);
void setup() {
size(900,800);
frameRate(30);
smooth();
}
void draw() {
background(0);
myHero.keyPressed();
myEnemies.enemyDisplay();
myEnemies.enemyMove();
myEnemies2.enemyDisplay();
myEnemies2.enemyMove();
myEnemies3.enemyDisplay();
myEnemies3.enemyMove();
}
Class 1:
class Enemies {
float xpos, ypos, speed;
Enemies(float x, float y, float s) {
xpos = x;
ypos = y;
speed = s;
}
void enemyDisplay() {
rect(xpos, ypos, 100, 100);
}
void enemyMove() {
xpos += speed;
if((xpos > width - 100) || (xpos < 0)) {
speed *= -1;
}
}
}
Class 2:
class Hero {
float xpos_, ypos_, speed_;
Hero(float x, float y, float s) {
xpos_ = x;
ypos_ = y;
speed_ = s;
}
void keyPressed() {
if (key == CODED) {
if (keyCode == UP) {
ypos_ -= speed_;
}
if (keyCode == DOWN) {
ypos_ += speed_;
}
if (keyCode == LEFT) {
xpos_ -= speed_;
}
if (keyCode == RIGHT) {
xpos_ += speed_;
}
}
rect(xpos_,ypos_,30,30);
}
}
I believe the question you are asking involves basic collision detection and object interactions.
I would first make the Enemies a List and create it / add elements during the setup() call:
List<Enemies> enemies = new List<Enemies>();
enemies.add(new Enemies(50,50,10));
This allows you to store all of your Enemies under one object. So your draw() method would look something like:
void draw(){
background(0);
myHero.keyPressed();
for(Enemies enemy : enemies)
{
enemy.enemyDisplay();
enemy.enemyMove();
if (hero.isCollidingWith(enemy)) // collision method defined in the hero object, but you could define it in the Enemies class as well, it doesn't really matter
{
hero.removeHealth(); // method defined in hero that removes health
}
}
}
This method would be in one of your classes:
public boolean isColliding(Enemies enemy)
{
// check the x and y coordinates of each object
}
I hope this helps to point you in the right direction.
you need to figure out collision detection and when your objects collide with each other, something basic like:
class Enemy
{
//...
public boolean isColliding(Hero hero)
{
//figure out the distance between two objects, if its less than their size, they are colliding..
//...
}
//...
}
then you need a part of your Game Loop that checks if anything is colliding with your hero, pickups, walls, etc...
It seems the first part you need help with is collision detection. The short answer I would give that will almost undoubtedly lead you to more questions is to look at the Area class (specifically Area.intersect). You might also want to look at the classes I've put together for displaying & managing areas in this project here
There are several problems here that touch on both application design as well as conventions. These should be addressed first before trying to tackle the collision detection problem.
The Enemies class only represents a single enemy, so the name of the class should reflect that. Additionally, prefixing the method names with "enemy" is redundant and can be removed. Other changes have been commented in the revised class below.
public class Enemy {
// Instead of hard-coding in the width and height of an enemy, allow the
// instantiating code to specify the enemy's size. This will allow you
// to have different size enemies and prevents you from having "magic numbers"
// in your code.
private float xpos, ypos, width, height, speed;
public Enemy(float x, float y, float s, float w, float h) {
xpos = x;
ypos = y;
width = w;
height = h;
speed = s;
}
/* These getters will be used for collision detection later */
public float getX() {
return xpos;
}
public float getY() {
return ypos;
}
public float getWidth() {
return width;
}
public float getHeight() {
return height;
}
// I've changed `display` to `draw` to be consistent with the method name in
// your main `draw` method.
public void draw() {
rect(xpos, ypos, width, height);
}
// This method now accepts a screenWidth parameter so that the enemy can know
// when they've collided with the left or right wall of the screen without
// having to rely on an global variable.
public void move(int screenWidth) {
xpos += speed;
if ((xpos > screenWidth - width) || (xpos < 0)) {
speed *= -1;
}
}
}
I mention the "magic numbers" in one of the comments above. See this wikipedia article for more on that.
The Hero class contains property names that have an underscore postfix. This ranges from unconventional to inconsistent with respect to all of your other property names in your other classes. The original keyPressed() method mixes the logic for both drawing and moving. These two things have been separated and the methods named like those of the Enemy class for consistency.
public class Hero {
private float xpos, ypos, width, height, speed;
public Hero(float x, float y, float s, float w, float h) {
xpos = x;
ypos = y;
width = w;
height = h;
speed = s;
}
// Change this method name to draw for consistency with the Enemy class
public void draw() {
// Key press functionality has been moved to the `move` method for consistency
// with the Enemy class.
rect(xpos, ypos, WIDTH, HEIGHT);
}
// This method uses the variables key, keyCoded, UP, DOWN, LEFT, and RIGHT. You
// did not include any import statements with your code, so they may be coming
// from there; however, if they are globals, you should pass them to this method
// as arguments whenever you call it.
public void move() {
// If this condition isn't satisfied, return immediately. This prevents
// unnecessary nesting and work below.
if (key != CODED) {
return;
}
if (keyCode == UP) {
ypos -= speed;
}
// Use `else if` here and below to prevent multiple unnecessary
// comparisons of keyCode.
else if (keyCode == DOWN) {
ypos += speed;
}
else if (keyCode == LEFT) {
xpos -= speed;
}
else if (keyCode == RIGHT) {
xpos += speed;
}
}
public boolean isColliding(Enemy enemy) {
// Collision detection is easy since all of your game entities (the hero and
// the enemies) are all rectangles and axis-aligned (not rotated). You can
// use a method known as "bounding box intersection."
return (Math.abs(enemy.getX() - xpos) * 2 < (enemy.getWidth() + width))
&& (Math.abs(enemy.getY() - ypos) * 2 < (enemy.getHeight() + height));
}
}
For more on bounding box intersection, see this gamedev stackexchange question.
Now that your classes are in order, its time to address your main code. We'll need to update the method names and, as #James T suggested, you should make a list of enemies instead of creating a new independent object for each enemy. This will make it easier for you to add or remove enemies in the future and to be able to process all enemies with one block of code without repeating yourself.
// Use constants to remove magic numbers.
private static final int SCREEN_WIDTH = 900;
private static final int SCREEN_HEIGHT = 800;
private Hero myHero = new Hero(400, 480, 30, 30, 5);
private List<Enemy> enemies = new ArrayList<Enemy>();
void setup() {
size(SCREEN_WIDTH, SCREEN_HEIGHT);
frameRate(30);
smooth();
enemies.add(new Enemy(50, 50, 100, 100, 10));
enemies.add(new Enemy(50, 350, 100, 100, 15));
enemies.add(new Enemy(50, 650, 100, 100, 12));
}
void draw() {
hasCollision = false;
background(0);
// I've changed the order of draw->move to move->draw. If you draw first, then
// move, then detect collisions, it will appear to your user that your hero has
// not yet collided with an enemy even though you act as they have (e.g.: they
// will not see the collision until the next time you draw the scene).
myHero.move();
myHero.draw();
for (Enemy enemy : enemies) {
enemy.move();
enemy.draw(SCREEN_WIDTH);
if (!hasCollision && myHero.isColliding(enemy)) {
hasCollision = true;
}
}
if (hasCollision) {
// Handle enemy collision here
}
}
You'll notice that I've also added accessibility modifiers to everything. While it is technically valid to exclude them and use the defaults, it makes your code more readable to include them because it is more obvious. When you're first starting out, the more obvious the better.
Alright got it to work thanks to all you helpful wonderful people!
I did this:
public boolean isColliding(Enemies h){
float distance = dist(x,y,h.x,h.y);
if(distance<100){
return true;
}else{
return false;
}
}
and in my draw(){ I have
if(myHero.isColliding(myEnemies)){
println("You lost!");
}
I had a very similar 'fix' earlier, but the reason I got an error was because I had Hero h instead of Enemies h in the 'if' function, so it was just a very dumb error that I overlooked :P

Categories

Resources