How to start then stop movement

Josiah Parrott :

My code stops a red square when it touches a blue square, but I don't want the red square to stop indefinitely. How can I fix that?

I've created the collision detection, and it makes speed = 0 if the red square touches the blue square.

//r=red
float rx = 200;
float ry = 375;
float rw = 50;
float rh = 50;

//b=blue
float bx = 550;
float by = 375;
float bw = 50;
float bh = 50;

float speed = 2;
//float cspeed = 2;

void setup() {
  size(800, 800);
  noStroke();
  noSmooth();
  noCursor();
}

void draw() {
  surface.setTitle(mouseX + ", " + mouseY);
  background(50);
  collision();
  move();
  display();
}

void collision() {
  //Check if blue and red are touching
  boolean hit = rectRect(rx, ry, rw, rh, bx, by, bw, bh);
  if (hit) {
    speed = 0;
  } else {
    speed = 2;
  }
}

void display() {

  fill(0, 0, 255);  //Blue square
  rect(bx, by, bw, bh);

  fill(255, 0, 0);  //Red square
  rect(rx, ry, rw, rh);
}



void move() {
  //Red square movement
  if (keyPressed) {
    if (key == 'd' || key == 'D') {
      rx = rx + speed;
      if (rx > width) {
      }
    }
  }


  if (keyPressed) {
    if (key == 'a' || key == 'A') {
      rx = rx - speed;
      if (rx > width) {
      }
    }
  }

  if (keyPressed) {
    if (key == 'w' || key == 'W') {
      ry = ry - speed;
      if (ry > width) {
      }
    }
  }

  if (keyPressed) {
    if (key == 's' || key == 'S') {
      ry = ry + speed;
      if (ry > width) {
      }
    }
  }
}




boolean rectRect(float rx, float ry, float rw, float rh, float bx, float by, float bw, float bh) {

  //is red touching blue?

  if (rx + rw >= bx && 
    rx <= bx + bw &&   
    ry + rh >= by &&    
    ry <= by + bh) { 
    return true;
  }
  return false;
}

I don't want the red square and the blue square to overlap, but I want to continue playing if they do touch. Right now the red square stops indefinitely.

laancelot :

In void collision() you set up that the square's speed will lower to 0.01 when there is a collision.

You check for collision after your square moved.

Once your squares collides, they cannot move anymore, so the speed is stuck on 0.01. Forever.

There are many ways you can solve this. Since the blue square never move, you can check for collision one move before they overlap, or "bounce" the red square when there is a collision.

A fast/easy fix would be to modify this function:

void move() {
  //Red square movement
  // You only need to check for input once, then for which input interests you
  if (keyPressed) {
    // I think this would look better in a switch statement, but we'll keep it an If for simplicity
    if (key == 'd' || key == 'D') {
      // checking if the square will collide BEFORE it does
      // notice that the square won't move if it detects that this move will be a collision
      if (!rectRect(rx + speed, ry, rw, rh, bx, by, bw, bh)) {
        rx = rx + speed;
      }
    }
    else if (key == 'a' || key == 'A') {
      if (!rectRect(rx - speed, ry, rw, rh, bx, by, bw, bh)) {
        rx = rx - speed;
      }
    }
    else if (key == 'w' || key == 'W') {
      if (!rectRect(rx, ry - speed, rw, rh, bx, by, bw, bh)) {
        ry = ry - speed;
      }
    }
    else if (key == 's' || key == 'S') {
      if (!rectRect(rx, ry + speed, rw, rh, bx, by, bw, bh)) {
        ry = ry + speed;
      }
    }

    if (ry > width) {
      // insert whatever you were planning with this condition
    }
  }
}

Now, void collision() has been taken over by void move(), so you can erase void collision. It will literally never happen, because we stop the square before any collision can happen.



Now... you want to add some more squares. Easy.

Sure, you can write a move function for every square. But this is tiring, and for sure there must be a better way.

There is, but this will test your skills. We'll keep things simple to avoid overwhelming you right off the bat, but know that we're going to thread into OOP (Object Oriented Programming) territory. If you're in school, you'll benefit a lot from understanding class (well, even if you're not in school class are still awesome).

First, we'll write a simple class we can use to create and manage the rectangles shapes you use in this program. If you're using the Processing IDE, I suggest you create a "New Tab" so we keep the code tidy. You don't absolutely have to, since the compiler won't see the difference, but as your project become more complex tabs are a great tool to organize your code.

In your new tab, enter this code:

class Rectangle {
  // This should be 'private', but I'm using 'protected' in case I wan to inherit those later, and 'public' to keep things simple
  public float x = 0;
  public float y = 0;
  public float w = 50;
  public float h = 50;
  protected boolean isVisible = true;
  protected color fillColor;

  // This is the constructor. It serves to initialize the class.
  public Rectangle(float xx, float yy, float ww, float hh, color cc) {
    x = xx;
    y = yy;
    w = ww;
    h = hh;
    fillColor = cc;
  }

  public void Render() {
    // I like to let my class draw themselves!
    if (isVisible) {
      fill(fillColor);
      rect(x, y, w, h);
    }
  }

  public void Move(float moveX, float moveY) {
    // This will be used to move. Since the variables are 'protected', they cannot be accessed from outside this class, so I have to create a tool to use them.
    // It is good practice to set 'public' only what's needed, and to manage class variables inside the class.
    // This is for many good reasons. Among those, remember that it's harder to debug 'why is my x position weird' when it can be accessed from anywhere.
    x += moveX;
    y += moveY;
  }

  public boolean Collision(float xx, float yy, float ww, float hh, float moveX, float moveY) {
    // Give coordinates to this function and it will tell you if there's a collision with this rectangle
    return Intersect(x + moveX, y + moveY, w, h, xx, yy, ww, hh);
  }
}

Done? Great! Now you have a Rectangle class which will contain stuff like coordinates, color, etc. of your rectangles.

Now we'll have to make a couple changes to your code.

First, you don't need global variables to track the red and blue square's positions, as this will be dealt by the class you just created. You can erase those. Global variables are useful, but as a general rule the less you use the better you'll fare, as global variables can become a nightmare to track and cause unforeseen mistakes.

Then, you'll need to initialize your new class into a couple nice rectangles. To keep things tidy, we'll have a rectangle named 'red', and an ArrayList of nameless rectangles which will be the obstacles. ArrayLists are a great tool you can learn to use later; for now let's just say that it will contain all those obstacles in a neat list which we'll call upon when needed.

The move function will have to deal with all the new obstacles and also with the fact that we deal with an object and not a bunch of global variables.

And, at last, the rectRect function will have to be updated. For now, it used to check if red and blue were intersected, but from now on it needs to be able to check this for any and all rectangles. We'll make it more neutral and use it differently.

Here's the code after those changes:

//red
Rectangle red;

//all other rectangles
ArrayList <Rectangle> rectList;

float speed = 2;
//float cspeed = 2;

void setup() {
  size(800, 800);
  noStroke();
  noSmooth();
  noCursor();

  // Initializing Red square
  red = new Rectangle(200, 375, 50, 50, color(255, 0, 0));
  // Initializing a bunch of rectangles
  rectList = new ArrayList <Rectangle>();
  rectList.add(new Rectangle(550, 375, 50, 50, color(0, 0, 255)));  // Red
  rectList.add(new Rectangle(300, 500, 50, 50, color(0, 255, 0)));  // Green
  // you can add as many as you want
}

void draw() {
  surface.setTitle(mouseX + ", " + mouseY);
  background(50);
  move();
  display();
}

void display() {
  red.Render();

  // this is a java 'For Each' loop. Research it, it's very useful.
  for ( Rectangle r : rectList) {
    r.Render();
  }
}

void move() {
  //Red square movement
  float moveX = 0;
  float moveY = 0;

  if (keyPressed) {
    // We're going calculate up how much we want to move the red square first
    // Then we'll check for collisions
    // Then we'll move the square (it there are no collisions)
    if (key == 'd' || key == 'D') {moveX += speed;}
    else if (key == 'a' || key == 'A') {moveX -= speed;}
    else if (key == 'w' || key == 'W') {moveY -= speed;}
    else if (key == 's' || key == 'S') {moveY += speed;}

    for (Rectangle r : rectList) {
      if (red.Collision(r.x, r.y, r.w, r.h, moveX, moveY)) {
        // If a collision is detected, we exit this function so there will be no move
        return;
      }      
    }

    red.Move(moveX, moveY);
  }
}

// INTERSECT RECTs
// The old function was nice, but it was really useful only with your specific example. You can re-use this one with any set of rectangles.
// There are many ways to write this function. I wrote this instance back when I was a student, and I still use it to this day.
boolean Intersect(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2)
{
  boolean checkX = false;
  boolean checkY = false;

  if ( (x1<=x2 && (x1+w1)>=x2) || (x1<=(x2+w2) && (x1+w1)>=x2+w2) || (x1>=x2 && (x1+w1)<=(x2+w2)) ) {checkX = true;}
  if ( (y1<=y2 && (y1+h1)>=y2) || (y1<=(y2+h2) && (y1+h1)>=y2+h2) || (y1>=y2 && (y1+h1)<=(y2+h2)) ) {checkY = true;}

  if (checkX && checkY) {return true;}

  return false;
}

Now you can add a bunch of other squares and rectangles of any and all sizes and colors! Try it! But, more importantly, try to understand it. If you have questions, remember that I'm here to help.

Have fun!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=307020&siteId=1