Vehicle-Like Movement

I have a rectangle, which I want to move like a car. I can get it to move in cardinal directions, and can get it to rotate. However, I can’t seem to get it to work for me when I try and make it move in the direction it is facing.

Car mycar;

void setup() {
  fullScreen();
  mycar = new Car();
}

void draw() {
  background(0);
  if (keyPressed == true) {
    mycar.move();
  }
  mycar.show();
}

class Car {
  color c;
  float xpos;
  float ypos;
  float angle;

  Car() {
    c = color(255, 0, 0);
    xpos = 900.0;
    ypos = 500.0;
    angle = 0.0;
  }

  void move() {
    if (keyCode == LEFT) {
      angle -= 8.0;
    } else if (keyCode == RIGHT) {
      angle += 8.0;
    }
    if (keyCode == UP) {
      xpos += (4*sin(radians(angle)));
      ypos -= (4*cos(radians(angle)));
    } else if (keyCode == DOWN) {
      xpos -= (4*sin(radians(angle)));
      ypos += (4*cos(radians(angle)));
    }
  }

  void show() {
    pushMatrix();
    rectMode(CENTER);
    translate(xpos, ypos);
    rotate(radians(angle));
    fill(c);
    rect(0, 0, 30, 50);
    popMatrix();
  }
}

Using 3.5.3. The canvas generates, but the box now does not move.
If I comment out the following section, the forward and backward motion works fine:

if (keyCode == LEFT) {
      angle -= 8.0;
    } else if (keyCode == RIGHT) {
      angle += 8.0;
    }

Or, if I comment out the section for forward/backward movement, the rotation works fine. Is there something I’m missing here? Why do the two not work together?
@marcholman23 Could you explain a solution to this?

1 Like

OK I have no idea what difference it made but I changed it so that only one operation could be done at a time:

    if (keyCode == LEFT) {
      angle -= 8.0;
    } else if (keyCode == RIGHT) {
      angle += 8.0;
    } else if (keyCode == UP) {
      xpos += (4*sin(radians(angle)));
      ypos -= (4*cos(radians(angle)));
    } else if (keyCode == DOWN) {
      xpos -= (4*sin(radians(angle)));
      ypos += (4*cos(radians(angle)));
    }
2 Likes

nice idea,
might take a look at little bit different

coding style
Car mycar= new Car();

void setup() {
  fullScreen();
}

void draw() {
  background(0);
  mycar.go();
}

class Car {
  color c = color(255, 0, 0);
  float xpos=900;
  float ypos=500;
  float angle = PI/2;
  float steer = 0.05;  // change angle from 360 to 2PI
  float speed = 4;
  float dir   = 1;

  Car() {
  }

  void move() {
    if ( keyPressed && key == CODED ) {
      if      (keyCode == LEFT)       angle -= steer;
      else if (keyCode == RIGHT)      angle += steer;
      else if (keyCode == UP)         dir = 1;
      else if (keyCode == DOWN)       dir = -1;
      xpos += dir * speed * sin(angle); // movement different, even move when steer
      ypos -= dir * speed * cos(angle);
    }
  }

  void go() {
    move(); //_ we can do here, not need extra call from main
    pushMatrix();
    rectMode(CENTER);
    translate(xpos, ypos);
    rotate(angle);
    fill(c);
    rect(0, 0, 50, 80, 10); // car
    fill(200, 100, 200);
    rect(0, -10, 33, 20, 5); // windshield
    popMatrix();
  }
}

1 Like

The problem lies with how draw uses the key / keyCode variables.

While in keyPressed (and similar) you can press multiple keys at the same time and still get the Input from all, this does not work in draw.

If you press up and left at the same time in keyPressed, and Print the result, you get (“Up, Left”), but if you do the same thing in draw, you get (“Left”).

This is because the key variable for draw is set before the loop and can only contain one value.
The key for keyPressed is also set before keyPressed and can also only contain one value, but keyPressed is called once for each key you press (in this case Up and Left), while draw is only called using the latest keyPress event (In this case Left).

As for wether the keyPressed method only calls once on key press or repeats while the key is held, that depends on your System.

If it repeats, that‘s probably what you‘d want. If it doesn’t, you can simply set a boolean in the keyPressed method and then use the move function in draw, adding only to the variables whose respective boolean is true and set it back to false, once keyReleased is called.

Something like this :

boolean up, down, left, right;

void keyPressed() {
   if (keyCode == UP) {
      up = true;
   } else if (keyCode == DOWN) {
      down = true;
   } else if (keyCode == LEFT) {
      left = true;
   } else if (keyCode == RIGHT) {
      right = true;
   }
}

void keyPressed() {
   if (keyCode == UP) {
      up = false;
   } else if (keyCode == DOWN) {
      down = false;
   } else if (keyCode == LEFT) {
      left = false;
   } else if (keyCode == RIGHT) {
      right = false;
   }
}

void draw() {

   //you can also put this in .move() and just pass the booleans as argument

   //don’t use else!!!
   if (up) {
      y += 1;
   }
   if (down) {
      y += -1; //i prefer this way
   }
   if (left) {
      angle += 0.1; //and i prefer to avoid radians if it‘s not needed
   }
   if (right) {
      angle += -0.1;
   }
}
1 Like

@Lexyth you mean the second KeyPressed as keyReleased

yes, might be a good second step from above code
but actually UP DOWN is not ±y here,
it is forward/backward…

i think that is a special usage what needs
that you try your idea after test his and my code.

for the usual X/Y RUNNER your idea is valid / compared to the WALKER
( sorry i always try to find EXPRESSIONS for the functionalities we try to do here. )

Uhm, no i meant the keyReleased would stop the value from increasing. If you hold a key, it starts when you press and stops when you release it, so i used keyPressed to start the motion change and keyReleased to stop it. And that for each key individually.

Also, my Code was just an example on how to do the keyHolding in case his System doesn‘t do multiple calls for one press, not copy paste solution :sweat_smile: to implement this Code, he‘ll have to change these parts accordingly.

It sounds like you are modeling a vehicle like a graphics turtle. The built-in PVector is already design for this. https://processing.org/reference/PVector.html

class Car {
  PVector head;
  PVector loc;
  Car(float x, float y, float h){
    loc = new PVector(x, y);
    head = new PVector(5, 0);
    head.rotate(h);
  }
  void show(){
    // ...
  }
}

Now display the car by translating to the current location, then rotating to the heading. Turn the car with car.head.rotate(). Move the car by shifting its absolute location in the direction of its heading – car.loc.add(car.head). Note that the magnitude of head is the speed of the car, and you could change it with car.head.setMag() – for example, making it negative if you want to shift into reverse. All of this is built into vectors.

Note also that you don’t necessarily need to create many methods on the car – those methods already exist on its heading and location objects, so you can manipulate them directly (if you wish) from keyPressed() – or create wrapper methods for them.

4 Likes