How to make falling object hit certain object in Java Processing - java

How do you get a certain object to hit another object?
I am making a game where trash is falling from the sky, you are meant to drag and drop the trash into the correct bins. How do I make it so when you drag it into the regional area of the bin is, it gives you a score and resets again. I have made the score & lives function but how do you get it to hit the bin and give you a score?
Here's the progress I have made so far:
PImage scene, can, brownB, blueB, greenB;
int can_x = -100; //CAN IS HIDDEN FOR NOW
int can_y = 500;
int canX = 100;
int canY = 50;
int canwidth = width/2;
int canheight = height/2;
int score = 0;
int lives = 3;
int can_count = 0;
int quit = 0;
boolean mouseInCan = false;
void setup() // Entry point (start of program), runs once
{
size(800,600,P2D); // Create a Window, must be same size as scene
scene = loadImage("backround.png"); // load image and data into scene data structure
can = loadImage("can.png"); // load image of can into the GPU
greenB = loadImage("green_bin.png");
textureMode(NORMAL); // Scale texture Top right (0,0) to (1,1)
blendMode(BLEND); // States how to mix a new image with the one behind it
noStroke(); // Do not draw a line around objects
canX = 0 + (int)random(800); // Choose drop starting position
canY = 0;
}
//check if the mouse is in the can
void mousePressed() {
if (mouseX > canX && mouseX < canX + canwidth && mouseY > canY && mouseY < canY + canheight) {
mouseInCan = true;
}
}
//if the mouse is in the can, then move it when the mouse is dragged
void mouseDragged() {
if (mouseInCan) {
float deltaX = mouseX - pmouseX;
float deltaY = mouseY - pmouseY;
canX += deltaX;
canY += deltaY;
}
}
//when we let go of the mouse, stop dragging the can
void mouseReleased() {
mouseInCan = false;
}
void draw()
{
background(scene); // Display background image referenced by scene
image(can, canX, canY, canwidth, canheight); //Can is drawn at position
image(greenB, 100, 450, width/8, height/4);
pushMatrix(); // Store current location of origin (0,0)
translate(can_x,can_y); // Change origin (0,0) for drawing to (drop_x,drop_y)
beginShape(); // Open graphics pipeline
texture(can); // Tell GPU to use drop to texture the polygon
vertex( -20, -20, 0, 0); // Load vertex data (x,y) and (U,V) texture data into GPU
vertex(20, -20, 1, 0); // Square centred on (0,0) of width 40 and height 40
vertex(20, 20, 1, 1); // Textured with an image of a drop
vertex( -20, 20, 0, 1);
endShape(CLOSE); // Tell GPU you have loaded shape into memory.
popMatrix();
canY += 1.5; // Make "drop" move down the screen (two pixels at a time)
if (canY>600) // If y value is entering the bottom of screen
{
canX = 0+(int)random(800); // Restart the drop again in the cloud.
canY = 0;
lives--;
}
if ((canY>100)&&(canY<450)) // If drop is on same level as player
{
if(abs((canX+100)-(canX+450))<25) // And drop is near player
{
score++; // Increase drop count by one (caught)
canX=0+(int)random(800); // Restart a new drop in the cloud
canY=0;
}
}
textSize(18); // Display score information on the screen
fill(0,0,0);
text("Lives: "+lives, 540, 20);
textSize(18);
fill(0,0,0);
text("Score: "+score, 640, 20);
if(quit==1) // Wait five seconds before exiting
{
delay(5000);
exit();
}
if (lives<1) // All lives lost so game over but
{ // return to draw one more time to
quit = 1; // allow "Game Over to be displayed.
}
}
I have tried doing it where the comment with 'If drop is on same level as player' but no luck, The code is a mess right now but I will clean it up, still new to processing.

What you are looking for is some kind of collision detection. There are very complex algorithms out there that can do an accurate detection with complex shapes, at the cost of CPU time, but in your case, just checking the intersection of two rectangles will probably be enough, at least for a start.
First you need to have your bin and can's positions and dimensions stored somewhere. We'll use the enclosing rectangle of each as a collision box.
PImage scene, can, brownB, blueB, greenB;
int canX = 100, canY = 50, canwidth = 80, canheight = 80;
int binX = 100, binY = 450, binW = 100, binH = 150;
You can then adjust the dimensions according to your screen size, but it has to be done after you have initialized it in setup:
void setup() // Entry point (start of program), runs once
{
size(800, 600, P2D); // Create a Window, must be same size as scene
scene = loadImage("backround.png"); // load image and data into scene data structure
can = loadImage("can.png"); // load image of can into the GPU
greenB = loadImage("green_bin.png");
//...
canwidth = width / 10;
canheight = width / 10;
binW = width / 8;
binH = height / 4;
binY = height - binH; //Set the bin at the bottom of the screen
}
Now your elements can be drawn according to the stored values, how and where you intend them, and you can do your collision detection inside your draw loop:
void draw()
{
background(scene); // Display background image referenced by scene
image(can, canX, canY, canwidth, canheight); //Can is drawn at position
image(greenB, binX, binY, binW, binH);
//...
/* If the top of the can is below the top of the bin and the can is
* horizontally within the bin, then we count score
*/
if (canY > binY && canX > binX && canX + canwidth < binX + binW) {
score++; // Increase drop count by one (caught)
canX=0+(int)random(800); // Restart a new drop in the cloud
canY=0;
}
//...
}
I've neglected the check for bottom coordinates of the can relative to the bin, as the bin is at the very bottom, and hitting the bottom kills you.

As was said already, you need some kind of collision detection. I would abstain though from trying to implement any algorithms myself and would instead use a professional library for that like:
https://github.com/locationtech/jts
That way you can also test against the real geometry and the overhead is minimal because these tests are first run againt the bounding boxes and only when that is positive they are refined to the real geometry. In addition you could also use a spatial index to speed up the search.

Related

Processing not detecting dead flies

been a lurker for a while, first time posting :)
I'm currently working on a little game for uni. I have the the majority of it done, but the "problem" that I'm running into is getting lost to tunnel vision. I was hoping someone would be able to point me in the right direction.
I have a program (code below) that prints an image of a fly to the canvas. A fly swatter follows the mouse and, when a fly is within range of the swatter and the mouse is pressed, the fly gets rekt.
The issue that I'm encountering is that the program is still recognising dead flies as having collision detection, and so whether I click on a dead fly of a live fly, the collision is still detected and a new fly is spawned.
Any help would be appreciated :) Thanks!
PImage fly, flybye, swatter, swatted;
float[] fX, fY; // fly locations array
float[] swat; // fly swatted binary boolean array, 1 = swatted, 0 = not swatted
int score = 0; // increments when swatted.
void setup(){
size(800,400);
fX = new float[0];
fY = new float[0];
swat = new float[0];
fly = loadImage("fly.png");
flybye = loadImage("flybye.png");
swatter = loadImage("swatter.png");
swatted = loadImage("swatted.png");
fX = append(fX, random(1, 800)); //first fly - random location
fY = append(fY, random(1, 400));
swat = append(swat, 0); // used as a boolean and matches to each individual fly, 0 = fly not
swatted, 1 = swatted.
}
void populate(){ // draw the flies in memory to the screen.
for(int i = 0; i < fX.length; i++){
if(swat[i] == 1){ // if swatted
flybye.resize(30, 30);// resize the fly image and place based on fx/fy array values
image(flybye, fX[i], fY[i]);
} else { // not swatted
fly.resize(30, 30);
image(fly, fX[i], fY[i]);
}
}
}
void collisionDetect(){ //collision detection - detect collision between swatter and fly
for(int i = 0; i < swat.length; i++){ // bounding box detection
if(dist(fX[i], fY[i], mouseX, mouseY) < 50){ // condition should look at location of mouse
and individual coordinates in fX and fY
swat[i] = 1; // swatted
fX = append(fX, random(1, 800)); //new fly placed in random location when old fly dies.
fY = append(fY, random(1, 400));
swat =append(swat, 0); // new fly not swatted
score++; //increment score
}
}
}
void draw(){
background(255);
populate(); // draw flys to screen.
fill(0);
textSize(20);
text(score, 5, 20);// set a text size and location for the score.
if(mousePressed) { // image swap
collisionDetect();
image(swatted, mouseX - 30, mouseY - 30); //draw swatter image to around mouse locaiton - might want to play with this to get it to look right.
}else{
image(swatter, mouseX - 30, mouseY - 30); // if not pressed then alternative image.
}
}
Shouldn't you test if the fly is swatted in your collision detection:
if(swat[i] == 0 && dist(fX[i], fY[i], mouseX, mouseY) < 50){

Processing - Zoom on clicked area then click again to zoom back out

I have a picture then used a flashlight type of light to only show where the mouse is hovering over. That part of the code works, but now I want to use if/else statements to zoom in on the selected area and then click again to zoom back out. Any other way to zoom in on specific area then back out of that area also helps. Really any help will be appreciated!
PImage ispy;
void setup () {
size(1024,768);
ispy = loadImage("ispy2.jpeg");
}
void draw () {
loadPixels();
ispy.loadPixels();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int loc = x+y*width;
float r = red(ispy.pixels[loc]);
float g = green(ispy.pixels[loc]);
float b = blue(ispy.pixels[loc]);
float d = dist(mouseX, mouseY, x, y); //
float factor = map(d, 0, 200, 2, 0);
pixels[loc] = color(r*factor, g*factor, b*factor);
}
}
updatePixels();
}
Here is my interpretation of what you are talking about. We store a isClicked boolean to store the state of whether we should zoom in or not. When we are going to draw the image, we translate() to the mouse, then we scale(), then we translate() back the same amount that we moved before, but in the opposite direction. What this does is it does the scale transform around the mouse position.
One thing that I couldn't find a way around way your way of updating the pixels directly from the image and the flashlight effect. What the program is doing instead is using your method to make a mask image and applying that to a PGraphics object. Another thing that I noticed is that when just rendering straight to the screen, there is considerable lag from the scaling. Instead, I have moved the drawing to a PGraphics object. This improves the performance.
In the end, to render, the program is drawing everything on the PGraphics object, then applying the mask to that object to get the flashlight effect.
Here is the code that I have:
PImage ispy, distMask;
boolean isClicked = false;
PGraphics renderer;
void createDistanceMask(PImage distMask){ //Takes image and changes its pixels to "alpha" for the PGraphics renderer
distMask.loadPixels();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int loc = x+(height-y-1)*width;
float d = dist(mouseX, mouseY, x, y); //
int factor = int(map(d, 0, 200, 400, 0)); //Pixel data will be between 0 and 255, must scale down later.
if (factor > 255)
factor = 255;
distMask.pixels[loc] = color(factor,factor,factor);
}
}
distMask.updatePixels();
}
void setup () {
size(1024,768, P2D);
ispy = loadImage("ispy2.jpeg");
distMask = new PImage(width,height);
renderer = createGraphics(width,height,P2D);
mouseX = width/2; //Not necessary, but will have black screen until mouse is moved
mouseY = height/2;
}
void draw () {
background(0);
pushMatrix();
createDistanceMask(distMask);
renderer.beginDraw(); //Starts processing stuff into PGraphics object
renderer.background(0);
if(isClicked){ //This is to get the zoom effect
renderer.translate(mouseX, mouseY);
renderer.scale(2);
renderer.translate(-mouseX, -mouseY);
}
renderer.image(ispy,0,0); //Render Image
renderer.endDraw();
renderer.mask(distMask); //Apply Distance mask for flashlight effect
image(renderer,0,0); //Draw renderer result to screen
popMatrix();
}
void mouseClicked(){
isClicked = !isClicked;
}
In my comment, I asked about having the screen move to the mouse, which is what this is doing. If you want to "freeze" the screen in one position, what you can do is store a lastMouseClickPosition PVector or simply just ints. Then, when translating, translate to the position instead of the PVector.
Here's the code that would change:
PVector lastClickPos = new PVector(); //Make the position
if(isClicked){ //When Rendering
renderer.translate(lastClickPos.x, lastClickPos.y);
renderer.scale(scalingFactor);
renderer.translate(-lastClickPos.x, -lastClickPos.y);
}
void mouseClicked(){ //At the bottom
isClicked = !isClicked;
lastClickPos.set(mouseX, mouseY);
}

Processing - rendering shapes is too slow

I have been doing a small little project using Processing, and the effect I wanted to achieve was a kind of "mountains" forming and moving, using Perlin Noise with the noise() function, with 2 parameters.
I was originally using a image for the background, but for illustrational purposes, I made the background black, and it's basically the same effect.
My issue is that I want to have a "history" of the mountains because they should fade away after some time, and so I made a history of PShapes, and draw the history and update it each frame.
Updating it is no issue, but drawing the PShapes seems to take a lot of time, reducing the frame rate from 60 to 10 when the length of the history is 100 elements.
Below is the code I used :
float noise_y = 0;
float noise_increment = 0.01;
// increment x in the loop by this amount instead of 1
// makes the drawing faster, since the PShapes have less vertices
// however, mountains look sharper, not as smooth
// bigger inc = better fps
final int xInc = 1;
// maximum length of the array
// bigger = less frames :(
final int arrLen = 100;
int lastIndex = 0;
PShape[] history = new PShape[arrLen];
boolean full = false;
// use this to add shapes in the history
PShape aux;
void setup() {
size(1280, 720);
}
void draw() {
background(0);
// create PShape object
aux = createShape();
aux.beginShape();
aux.noFill();
aux.stroke(255);
aux.strokeWeight(0.5);
for (float x = 0; x < width + xInc; x = x + xInc) {
float noise = noise(x / 150, noise_y) ;
// get the actual y coordinate
float y = map(noise, 0, 1, height / 2, 0);
// create vertex of shape at x, y
aux.vertex(x, y);
}
aux.endShape();
// push the current one in the history
history[lastIndex++] = aux;
// if it reached the maximum length, start it over ( kinda works like a queue )
if (lastIndex == arrLen) {
lastIndex = 0;
full = true;
}
// draw the history
// this part takes the MOST TIME to draw, need to fix it.
// without it is running at 60 FPS, with it goes as low as 10 FPS
if (full) {
for (int i = 0; i < arrLen; i++) {
shape(history[i]);
}
} else {
for (int i = 0; i < lastIndex; i++) {
shape(history[i]);
}
}
noise_y = noise_y - noise_increment;
println(frameRate);
}
I have tried to use different ways of rendering the "mountains" : I tried writing my own class of a curve and draw lines that link the points, but I get the same performance. I tried grouping the PShapes into a PShape group object like
PShape p = new PShape(GROUP);
p.addChild(someShape);
and I got the same performance.
I was thinking of using multiple threads to render each shape individually, but after doing some research, there's only one thread that is responsible with rendering - the Animation Thread, so that won't do me any good, either.
I really want to finish this, it seems really simple but I can't figure it out.
One possible solution would be, not to draw all the generated shapes, but to draw only the new shape.
To "see" the shapes of the previous frames, the scene can't be cleared at the begin of the frame, of course.
Since the scene is never cleared, this would cause, that the entire view is covered, by shapes over time. But if the scene would be slightly faded out at the begin of a new frame, instead of clearing it, then the "older" shapes would get darker and darker by time. This gives a feeling as the "older" frames would drift away into the depth by time.
Clear the background at the initlization:
void setup() {
size(1280, 720);
background(0);
}
Create the scene with the fade effect:
void draw() {
// "fade" the entire view
blendMode(DIFFERENCE);
fill(1, 1, 1, 255);
rect(0, 0, width, height);
blendMode(ADD);
// create PShape object
aux = createShape();
aux.beginShape();
aux.stroke(255);
aux.strokeWeight(0.5);
aux.noFill();
for (float x = 0; x < width + xInc; x = x + xInc) {
float noise = noise(x / 150, noise_y) ;
// get the actual y coordinate
float y = map(noise, 0, 1, height / 2, 0);
// create vertex of shape at x, y
aux.vertex(x, y);
}
aux.endShape();
// push the current one in the history
int currentIndex = lastIndex;
history[lastIndex++] = aux;
if (lastIndex == arrLen)
lastIndex = 0;
// draw the newes shape
shape(history[currentIndex]);
noise_y = noise_y - noise_increment;
println(frameRate, full ? arrLen : lastIndex);
}
See the preview:

Making a moving circle disappear after being clicked on, Processing

I have written a program in which a UFO (in essence, a gray ellipse) appears from the center of the screen and flies to the edge. There is a laser that appears when the mouse is pressed, and disappears when the mouse is released. I want to make it so that the UFO disappears when the mouse clicks on it/the laser touches it.
I've made it as far as to make the UFO class and create variables that determine its movements and speed, and I was able to get the laser to appear directly on the cursor. I thought of making an 'if' statement to check if the cursor is within the radius (or diameter) of the UFO, and placing it inside of the for loop I created for the UFOs. However, I am not sure how to achieve the proper syntax to make it happen.
Note: You may need to wait a few seconds for the first circle to appear after you play the sketch.
UFO[] ufos = new UFO[3];
void setup() {
size(700, 700);
for (int j = 0; j < ufos.length; j++) {
ufos[j] = new UFO();
}
}
//UFO class
//Class setup ends on line 61
class UFO {
float a;
float b;
float c;
float sa;
float sb;
float d;
UFO() {
//declare float a/b/c value
a = random(-width/2, width/2);
b = random(-height/2, width/2);
c = random(width);
}
//UFO movement
void update() {
//float c will serve as a speed determinant of UFOs
c = c - 1;
if (c < 5) {
c = width;
}
}
//UFO setup
void show() {
//moving x/y coordinates of UFO
float sa = map(a / c, 0, 1, 0, width);
float sb = map(b / c, 0, 1, 0, height);
float d = map(c, 0, width, 50, 0);
//UFO drawing shapes
//ellipse is always sa (or sb) / c to allow UFO to appear
//as if it is moving through space
fill(200);
ellipse((sa / c), (sb / c), d + 5, d+5);
//Hides UFO way off the screen
//and replaces it with a black-filled ellipse until
//it forms into a full circle
//When radius d becomes 50, the UFO flies from the
//center of the screen to off of the screen
if (d < 50) {
fill(0);
ellipse(-5, -10, 90, 90);
sa = 10000;
sb = 10000;
}
}
}
void draw() {
//Background
background(0);
//Translated the screen so that the UFOs appear to fly from
//the center of the screen
translate(width/2, height/2);
//UFO draw loop, make UFO visible on screen
for (int j = 0; j < ufos.length; j++) {
ufos[j].update();
ufos[j].show();
//mouse-click laser
if (mousePressed == true) {
fill(200,0,0);
ellipse(mouseX - 352,mouseY - 347,50,50);
}
}
}
Like I said on the Happy Coding forum:
Basically, if your UFO is a series of circles, then you just need to use the dist() function to check whether the distance from the mouse to the center of the circle is less than the radius of the circle. If it is, then the mouse is inside the circle. Here's a small example:
float circleX = 50;
float circleY = 50;
float circleDiameter = 20;
boolean showCircle = true;
void draw(){
background(0);
if(showCircle){
ellipse(circleX, circleY, circleDiameter, circleDiameter);
}
}
void mousePressed(){
if(dist(mouseX, mouseY, circleX, circleY) < circleDiameter/2){
showCircle = false;
}
}
If your UFO is multiple circles, then you need to apply this logic to each circle. Please try something and post a small example like this one (not your whole sketch) if you get stuck. Good luck.

Android - Canvas onDraw with zoomed camara following character movements

Some Context:
I am creating an Android game which draws a maze onto a canvas against a background image, this uses a square block and therefore the maze is always automtically zoomed in to a 5 x 5 square, of a maze that may be 20 x 20. The maze is drawn by simply running through a set of for loops and then drawing lines in the relevent places. This is all working perfectly, however I am having an issue with getting my onDraw to work smoothly. This is occuring due to the fact that everytime I invalidate I have to re-run the for look and run various if statements to check positions (unfortunetly this process cannot be improved).
The Question:
I am looking to re-write the way my maze is drawn onto the canvas, below are the 3 main features I need to implement:
1 - Draw the whole maze onto the canvas (this is easy enough)
2 - Zoom in on the maze so only a 5 x 5 is shown
3 - Move the character (who is always centered on the screen) and draw the next seciton of the maze
Now as mentioned above drawing the whole maze is easy enough and will make the onDraw signifcatly quicker as their is no need to run the for loop on invaldate, it can be done once when the level is first loaded up.
In terms of point 2 & 3, the way I see this working is the charcacter to be drawn in the middle of the canvas then a 2d birdseye view camera to be attached / linked to the characters movement. This camera would also need to be zoomed in to an extent that it only displays a 5 x 5 of the overall maze grid. Then as the charcater moves the camera moves with the character and displays the next section of the maze which has already been drawn. I have tried a few things and done some reasearch however I have never worked with canvas before and no idea really where to start and if this is even possible.
So to sum up the main part of the question is whether their is a way to link a birdseye view camera to a character which is zoomed in and moves with the character image.
Below is a snippet as to how the maze is currently drawn, using 2 sets of for loops checking against 2 sets of boolean arrays[][], which simply store the vertical and horixaonl lines to be drawn.
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawRect(0, 0, width, 100, background);
RectBackground.set(0,0, FullScreenWidth, FullScreenWidth);
canvas.drawBitmap(gameBackground, null, RectBackground, null);
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 5; j++)
{
float x = j * totalCellWidth;
float y = i * totalCellHeight;
indexY = i + currentY;
indexX = j + currentX;
// Draw Verticle line (y axis)
if (indexY < vLength && indexX < vLines[indexY].length && vLines[indexY][indexX])
{
RectOuterBackground.set((int)x + (int)cellWidth, (int)y, (int)x + (int)cellWidth + 15, (int)y + (int)cellHeight + 15);
canvas.drawBitmap(walls, null, RectOuterBackground, null);
}
// Draws Horizontal lines (x axis)
if (indexY < hLength && indexX < hLines[indexY].length && hLines[indexY][indexX])
{
RectOuterBackground.set((int)x, (int)y + (int)cellHeight,(int)x + (int)cellWidth + 15,(int)y + (int)cellHeight + 15);
canvas.drawBitmap(walls, null, RectOuterBackground, null);
}
}
}
}
To make the drawing faster, you can double buffer the canvas by drawing directly into a bitmap and flashing the bitmap into the canvas like this. It's very fast.
private void init()
{
//variables below are class-wide variables
b = Bitmap.createBitmap(cwidth, cheight, Bitmap.Config.ARGB_8888);
bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bitmapPaint.setStyle(Paint.Style.STROKE);
bitmapPaint.setStrokeWidth(3);
myPaint.setColor(0xff000000);
myCanvas = new Canvas(b);
}
protected void onDraw(Canvas c)
{
super.onDraw(c);
c.drawBitmap(b, 0, 0, myPaint);
for(int i = 0; i < number lines; i++)
{
//draw here using myPath
}
if(b != null)
c.drawBitmap(b, 0, 0, myPaint);
myCanvas.drawPath(myPath, bitmapPaint);
}
Then, to "move around" I would suggest using a crop box. What this means is that a 1:1 scale, the image is larger than the viewport displayed on the screen. Really what's happening when someone "moves" is the bitmap is being moved beneath the crop box.
You could use BitmapRegionDecoder and display only the region the character is in (this might be slowish) or by using
public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)
The src parameter here allows you to specify a small region of the bitmap to display. It is effectively a crop box.
To zoom in and out you need to multiply the coordinates by a number( zoom factor)
int x1 = (int)x + (int)cellWidth;
int y1 = (int)y;
int x2 = (int)x + (int)cellWidth + 15;
int y2 = (int)y + (int)cellHeight + 15;
RectOuterBackground.set(x1*zoom, y1*zoom, x2*zoom, y2*zoom);
note that zoom must be a floating point number, using zoom=2 that makes everything twice as big.
if you want to put the camera on top of the cell (xc, yc) you need to do this:
RectOuterBackground.set( (x1-xc)*zoom, (y1-yc)*zoom, (x2-xc)*zoom, (y2-yc)*zoom);
Try first drawing the whole maze, and soon you will figure out how to only draw the bit of the maze that is only inside the screen
I hope this helps and let me know if you have any questions :)

Categories