// CSC220F19DemoF_ClassArrayTranslateBB Parson's csc120 recap // This version adds bounding box-based collision detection on 9/3/2019. // Updated evening of 9/3/2019 to support 'f' and 'b' keys for debugging. /** * KEYBOARD COMMANDS: * * 'f' toggles between freeze and unfreeze the display. * 'b' toggles between show and hide bounding boxes for debugging collisions. **/ boolean isFrozen = false ; // 'f' toggles this display freeze boolean isBounding = false ; // 'b' toggles this bounding box display Avatar1 [] av1 = new Avatar1 [5]; // setup() called once at the start of Run void setup() { size(800, 700); // At a minimum set display window size in setup(). frameRate(60); // Must be explicit in newer Macs after setting size. rectMode(CENTER); ellipseMode(CENTER); for (int i = 0 ; i < av1.length ; i++) { av1[i] = new Avatar1(int(random(0,width)), int(random(0, height))); } } // draw() is called in a loop at the frameRate, defaults to once every 60th sec. void draw() { if (isFrozen) { return ; } background(255, 255, 0); // screen color, use Tools -> Color Selector, RGB int i = 0 ; while (i < av1.length) { av1[i].display(); // function call to the display() function to display avatar av1[i].move(); i++ ; } } // Processing calls function keyPressed() whenever a keyboard key is pressed. // See additonal Keyboard functions in the help page. void keyPressed() { if (key == CODED) { // CODED needed only for special characters if (keyCode == RIGHT) { // do the right thing } else if (keyCode == LEFT) { // do the left thing } else if (keyCode == UP) { // do the up thing } else if (keyCode == DOWN) { // do the down thing } } else { // else it is a regular key if (key == 'f') { isFrozen = ! isFrozen ; // invert the value println("isFrozen = " + isFrozen); } else if (key == 'b') { isBounding = ! isBounding ; // invert the value println("isBounding = " + isBounding); } } } /** overlap checks whether two objects' bounding boxes overlap **/ boolean overlap(Avatar1 avatar1, Avatar1 avatar2) { int [] bb1 = avatar1.getBoundingBox(); int [] bb2 = avatar2.getBoundingBox(); // If bb1 is completely above, below, // left or right of bb2, we have an easy reject. if (bb1[0] > bb2[2] // bb1_left is right of bb2_right || bb1[1] > bb2[3] // bb1_top is below bb2_bottom, now reverse them || bb2[0] > bb1[2] // bb2_left is right of bb1_right || bb2[1] > bb1[3] // bb2_top is below bb1_bottom, now reverse them ) { return false ; } // In this case one contains the other or they overlap. return true ; } class Avatar1 { int elx, ely ; // X, Y location int exspeed, eyspeed ; // X, Y speed int ewidth = 75, eheight = 50 ; // w & h of Avatar1 // added in 020 section at 1:30 float scaleFactor, scaleSpeed ; // scale "location" and speed Avatar1(int initialx, int initialy) { // constructor for a class builds an object of that class elx = initialx ; ely = initialy ; exspeed = round(random(1,4)) ; eyspeed = round(random(1,5)) ; ; scaleFactor = random(.1, 2.0); scaleSpeed = .01 ; } void display() { pushMatrix(); translate(elx, ely); scale(scaleFactor); // added in 1:30 class // display my avatar // body stroke(0); fill(255); rect(0, 0, ewidth/2, eheight/2); //head stroke(0, 0, 255); // Sets stroke around shapes, alternatively noStroke() fill(255, 128, 0); // Sets fill within closed shape, alternatively noFill() ellipse(0, -eheight/2, ewidth, eheight); // X, Y, Ewidth, Eheight popMatrix(); if (isBounding) { // Optionally show BB around Avatar for debugging int [] bb = getBoundingBox(); // get "this" object's BB, display it noFill(); stroke(0); strokeWeight(1); rectMode(CORNER); rect(bb[0], bb[1], bb[2]-bb[0], bb[3]-bb[1]); rectMode(CENTER); } } void move() { // move my avatar elx = elx + exspeed ; ely += eyspeed ; if (elx > width+ewidth) { // if horizontal loc is off right side of display exspeed = - exspeed ; // reverse direction } else if (elx < 0-ewidth) { // if horizontal loc is off left side of display exspeed = - exspeed ; } if (ely < 0-eheight || ely > height+eheight) { // if vertical off top or bottom eyspeed = - eyspeed ; } scaleFactor += scaleSpeed ; if (scaleFactor < 0 || scaleFactor > 2) { scaleSpeed = - scaleSpeed ; } // Add collision detection 9/3/2019. // Look at every *other* avatar and see whether we overlap. // On first overlap detected, change directions and break loop. for (Avatar1 other : av1) { if (other == this) { // this is ME! "this" is this Avatar object. continue ; // Go back to top of loop & get the next one. } if (overlap(this, other)) { // If I (this) overlap with the other Avatar. exspeed = - exspeed ; // reverse direction eyspeed = - eyspeed ; // reverse direction break ; // Go past the loop, i.e., break out of the loop. // You only want to change direction because of collision once. } } } int [] getBoundingBox() { int [] result = new int[4]; result[0] = (elx - round (scaleFactor * ewidth/2)) ; // left extreme result[1] = (ely - round (scaleFactor * eheight)); // top result[2] = (elx + round (scaleFactor * ewidth/2)); // right side result[3] = (ely + round (scaleFactor * eheight/4)); // bottom return result ; } }