Rotating the sprite using mouse

Hello! I want my sprite’s front to rotate in the direction of the mouse pointer. But it isn’t doing so properly (as you can see in the pic). The pointer is behind the car, but I want it to be in front of the car. I’ve tried changing different parameters but no proper outcome. Can anyone help me with this or tell what should be changed?

move

The code I have as of now:

  pushMatrix();
  translate(width/2, height/2);
  angle = atan2(mouseX-width/2, mouseY-height/2);
  rotate(angle*-1);
  image(car, -30, -50, 60, 100);
  popMatrix();

Hi! The way you are calculating the angle is wrong. You should first calculate the vector that points from your car to the mouse

The way to calculate the angle which I am going to suggest does not require any translate call

Let’s say your car has a position which is of type PVector
Then what you need to do is to create the pointer vector (which points from your car to the mouse pointer)

PVector mouse = new PVector(mouseX, mouseY);
PVector pointer = PVector.sub(mouse, position);
float angle = pointer.heading();

[Note: you might need to add or subtract PI/2 or PI] depending on the sprite’s normal rotation (how the sprite looks without any rotation)

1 Like

hi,
if mouvements are fine ,but just oposite direction, i guess
rotate(PI+angle*-1);
can solve it no?

1 Like

Yes, this works. Thanks a lot!

Is there a way such that the car moves in the mouse direction only by just pressing “W” key instead of using “WASD” keys for directions?

Yes you can:

PVector pos;
float speed;

boolean wIsPressed = false;

void setup() {
  size(600, 600);
  pos = new PVector(width/2, height/2);
  speed = 5;
}
void draw() {
  background(0);
  fill(255);
  noStroke();
  ellipse(pos.x, pos.y, 50, 50);
  if (wIsPressed) {
    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);
    

    //skip the rest when it's close to the mouse so it doesn't look weird
    if (vel.mag() < speed) {
      return;
    }

    vel.normalize();
    vel.mult(speed);
    pos.add(vel);
  }
}
void keyPressed() {
  if (key == 'w' || key == 'W') {
    wIsPressed = true;
  }
}
void keyReleased() {
  wIsPressed = false;
}

Okay then how do I use it with my car? cuz when I use this code, the car doesn’t rotate

I don’t know exactly what your code looks like, but I would have done it like this:

PVector pos;
float speed;

boolean wIsPressed = false;
float angle = 0;
void setup() {
  rectMode(CENTER);
  size(600, 600);
  pos = new PVector(width/2, height/2);
  speed = 5;
}
void draw() {
  background(0);
  pushMatrix();
  translate(pos.x, pos.y);
  rotate(angle);
  fill(255);
  noStroke();
  rect(0, 0, 50, 20);
  if (wIsPressed) {
    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);

    if (vel.mag() < speed) {
      return;
    }

    vel.normalize();    
    vel.mult(speed);
    pos.add(vel);
  }
  popMatrix();
  
  PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);
  angle = atan2(vel.y, vel.x);
}
void keyPressed() {
  if (key == 'w' || key == 'W') {
    wIsPressed = true;
  }
}
void keyReleased() {
  wIsPressed = false;
}

Oh yes I got the idea. Now it works great! Thank you :slight_smile:

1 Like

Also, how do I keep the car in the center of the canvas and move the background instead (seems as though the car moves, but not actually)?. Kind of like zoomed into a bigger background.

modified code of yours

pushMatrix();
  scale(2.0);
  // track background to be moved
  image(tracktest2, 0, 0);
  popMatrix();
  pushMatrix();
  translate(pos.x, pos.y);
  rotate(angle*-1);
  fill(255);
  noStroke();
  image(car, -30, -50, 60, 100);
  if (wIsPressed) {
    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);

    if (vel.mag() < speed) {
      return;
    }

    vel.normalize();    
    vel.mult(speed);
    pos.add(vel);
  }
  popMatrix();

  PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);
  angle = atan2(vel.x, vel.y);

Because you only have one key for controlling, you could also say:

if (keyPressed && (key == 'w' || key == 'W')) {
  //The same as before with less code
}

but if you have multiple inputs such as w, a, s, d movement for example, then I would use the method shown above…

I don’t know if you’ve already dealt with classes.

If not, I would recommend this to you:

Here is an example of how I would do it:

Click to see the Code
PImage background;
PVector backgroundPos;
Car car;

void setup() {
  imageMode(CENTER);
  size(600, 600);
  background = loadImage("https://2.bp.blogspot.com/-n_JHPmowrJE/UrZcro0trpI/AAAAAAAAFV0/16-SsjIJy-Q/s320/000036-01-25-34.jpg");
  int size = 3;
  background.resize(background.width*size, background.height*size);

  car = new Car(width/2, height/2);

  backgroundPos = new PVector(background.width/2, background.height/2);
}
void draw() {
  background(0);
  pushMatrix();
  translate(backgroundPos.x, backgroundPos.y);
  image(background, 0, 0);
  popMatrix();
  car.show();
  car.update();
}
void moveBackground(PVector vel) {
  vel.x = -vel.x;
  vel.y = -vel.y;

  backgroundPos.add(vel);
}
class Car {
  PImage carImage;
  PVector pos;
  float angle;
  float speed;
  Car(float x, float y) {
    carImage = loadImage("car.png");
    //link to the Picture: https://toppng.com/uploads/preview/aston-martin-one77-01-top-down-car-sprite-1156302814774vtgthfmb.png

    carImage.resize(0, 40);

    pos = new PVector(x, y);
    angle = 0;
    speed = 10;
  }
  void show() {
    pushMatrix();
    translate(pos.x, pos.y);
    rotate(angle);
    image(carImage, 0, 0);
    popMatrix();
  }
  void update() {
    updateAngle();
    //Just try both and decide which one you like more

    updateMovementFirstExample();
    //updateMovementSecondExample();
  }
  void updateAngle() {
    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);
    angle = atan2(vel.y, vel.x) + PI / 2;
  }
  void updateMovementFirstExample() {
    if (!keyPressed) return;
    if (key != 'w' && key != 'W') return;

    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);

    if (vel.mag() < speed) return;

    vel.normalize();
    vel.mult(speed);
    pos.add(vel);

    float distToMid = dist(pos.x, pos.y, width/2, height/2);
    if (distToMid > min(width, height)/5) {
      pos.sub(vel);
      moveBackground(vel);
    }
  }
  void updateMovementSecondExample() {
    if (!keyPressed) return; //I think its better to read with return instead of all the curly braces
    if (key != 'w' && key != 'W') return;

    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);

    if (vel.mag() < speed) return;

    vel.normalize();
    vel.mult(speed);
    moveBackground(vel);
  }
}

1 Like

Hey I dont know why you deleted your post but here is your answer:

I actually figured it out myself, but thanks anyways!

1 Like

Okay so I could actually implement the rotation of the car using arduino’s potentiometer (like a steering). But the issue is when I rotate the car with potentiometer, it doesn’t drive in that direction, but instead drives in mouseX mouseY direction.

I feel this is one of the lines that’s causing the issue.

PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);

So, what do I need to change in the code to make it work?

Full code here:

import processing.serial.*;

Serial myPort; // serial port
PImage carImage;
PImage background;
PVector backgroundPos;
Car car;
boolean move = false;
float speed;

/* STEERING */
float mapval;
int val;


void setup() {
  imageMode(CENTER);
  size(1200, 675);
  carImage = loadImage("car.png");
  background = loadImage("https://www.clipartkey.com/mpngs/m/33-331548_clip-art-oval-oval-race-track-clipart.png");
  int size = 3;
  background.resize(background.width*size, background.height*size);

  car = new Car(width/2, height/2);

  backgroundPos = new PVector(background.width/2, background.height/2);

  myPort = new Serial(this, Serial.list()[0], 9600);
  myPort.bufferUntil('\n');
}


void draw() {
  background(0);
  pushMatrix();
  //scale(2.0);
  translate(backgroundPos.x, backgroundPos.y);
  image(background, 0, 0);
  popMatrix();
  car.show();
  car.update();
}
void moveBackground(PVector vel) {
  vel.x = -vel.x;
  vel.y = -vel.y;

  backgroundPos.add(vel);
}
float angle;
class Car {
  //PImage carImage;
  PVector pos;
  //float angle;
  //float speed;
  Car(float x, float y) {
    //carImage = loadImage("car.png");
    //link to the Picture: https://toppng.com/uploads/preview/aston-martin-one77-01-top-down-car-sprite-1156302814774vtgthfmb.png

    carImage.resize(0, 100);

    pos = new PVector(x, y);
    angle = 0;
    speed = 20;
  }
  void show() {
    pushMatrix();
    translate(pos.x, pos.y);
    rotate(angle);
    image(carImage, 0, 0);
    popMatrix();
  }
  void update() {
    updateAngle();
    //Just try both and decide which one you like more

    //updateMovementFirstExample();
    updateMovementSecondExample();
  }
  void updateAngle() {
    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);
    angle = atan2(vel.y, vel.x) + PI / 2;
  }

  void updateMovementFirstExample() {
    if (!keyPressed) return;
    if (key != 'w' && key != 'W') return;

    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);

    if (vel.mag() < speed) return;

    //You could do it like this:
    vel.normalize();
    vel.mult(speed);
    pos.add(vel);

    float distToMid = dist(pos.x, pos.y, width/2, height/2);
    if (distToMid > min(width, height)/15) {
      pos.sub(vel);
      moveBackground(vel);
    }
  }
  void updateMovementSecondExample() {
    if (move==false) return;
    //if (key != 'w' && key != 'W') return;

    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);

    if (vel.mag() < speed) return;

    vel.normalize();
    vel.mult(speed);
    moveBackground(vel);
  }
}

// to read potentiometer values
void serialEvent(Serial myPort) {
  if (myPort.available() > 0) {
    val = myPort.read();
    mapval = map(val,0,255,0,6.5);
    angle = mapval; // rotate the car
  }

Hey you could use the PVector.fromAngle() function.
Here the Description:
Calculates and returns a new 2D unit vector from the specified angle value (in radians).

So you can say:

PVector vel = PVector.fromAngle(angle);
vel.mult(speed);

@Flolo This is a new topic but I feel you are the right person who can help. I wish to add start and finish point (both same coordinate) to the race track, such that when the car passes it the game stops. I’ve tried doing it, but the coordinates keep changing continuously because of moving background. Could you pls help :slight_smile:

1 Like

Hey you’re right, actually it’s a new topic, but you could change the title to “Questions about a racing game” :slight_smile:

First of all, I now know why the game is lagging.
You have to say in size () that the P2D renderer should be used.
so: size(1200, 675, P2D);

Now we come to a small problem because the start line is rotated.

We could ignore that and instead simply say that we are “testing” the start line imprecisely.
So roughly like this:

You can think about it later. How to do it more precisely.
Here is an interesting link: Collision Detection - point hitting a rotating rectangle - Game Development Stack Exchange

Click to see the code in Processing language
PVector pos;
PVector size;
float angle = 0;
boolean isInside = false;
void setup() {
  rectMode(CENTER);

  size(600, 600);

  pos = new PVector(width/2, height/2);
  size = new PVector(400, 150);
}
void draw() {
  angle += radians(1);

  background(0);
  if (isInside) {
    fill(255);
  } else {
    fill(0);
  }

  stroke(255);
  strokeWeight(2);
  translate(pos.x, pos.y);
  rotate(angle);
  rect(0, 0, size.x, size.y);

  PVector rotatedMousePos = Rotate(new PVector(mouseX, mouseY), angle, pos);

  isInside = isInsideRect(rotatedMousePos, pos, size);
}
boolean isInsideRect(PVector p, PVector rp, PVector rs) {
  if (p.x > rp.x - rs.x/2 && p.x < rp.x + rs.x/2 && 
    p.y > rp.y - rs.y/2 && p.y < rp.y + rs.y/2) {
    return true;
  }
  return false;
}
PVector Rotate(PVector point, float angle, PVector center_of_rotation) {
  angle = -angle;

  float sinus   = sin(angle);
  float cosinus = cos(angle);
  PVector temp = new PVector();

  point  = PVector.sub(point, center_of_rotation);
  temp.x = point.x * cosinus - point.y * sinus;
  temp.y = point.x * sinus   + point.y * cosinus;
  point  = PVector.add(temp, center_of_rotation);

  return point;
}

The problem is, as you said, the background is moving, so we just have to move the starting line position at the same time as the background. And we can just do that in the “moveBackground()” function

So that we know that the player has driven around once, we need a checkpoint. Here it is the same as with the starting line

Click to see the Code
PImage carImage;
PImage background;
PVector backgroundPos;

Car car;

boolean move = false;
float speed;

float angle;

/* STEERING */
float mapval;
int val;

//Start is start and end 
PVector startPos, startSize;
//You could of course make more checkpoints mabye with an array or with an own class :)
PVector checkpointPos, checkpointSize;
boolean checkpointCleared = false;

boolean raceStarted = false;

float raceTime = 0;

void setup() {
  rectMode(CENTER);
  imageMode(CENTER);

  //hey I dont know why but when you use the P2D rederer 
  //then the programm doesnt legg anymore 
  size(1200, 675, P2D);

  carImage = loadImage("car.png");
  background = loadImage("https://www.clipartkey.com/mpngs/m/33-331548_clip-art-oval-oval-race-track-clipart.png");

  int size = 3;
  background.resize(background.width*size, background.height*size);

  car = new Car(width/2, height/2);

  startPos = new PVector(width/2+20, height/2-40);
  startSize = new PVector(150, 300);

  checkpointPos = new PVector(2420, 800);
  checkpointSize = new PVector(300, 150);

  backgroundPos = new PVector(background.width/2, background.height/2);
}
void draw() {
  if (raceStarted) {
    raceTime += 1 / frameRate;
  }
  println("Time: " + raceTime + " seconds");
  println("Race on going: " + raceStarted);
  background(0);

  //Instead of that:
  //pushMatrix();
  //translate(backgroundPos.x, backgroundPos.y);
  //image(background, 0, 0);
  //popMatrix();

  //You could also say:  
  image(background, backgroundPos.x, backgroundPos.y);
  updateStart();
  if (raceStarted) {
    fill(0, 255, 0);
  } else {
    fill(255, 0, 0);
  }
  stroke(255, 0, 0);
  strokeWeight(10);
  rect(startPos.x, startPos.y, startSize.x, startSize.y);

  updateCheckpoint();
  if (checkpointCleared) {
    fill(0, 255, 0);
  } else {
    noFill();
  }
  stroke(255, 0, 0);
  strokeWeight(10);
  rect(checkpointPos.x, checkpointPos.y, checkpointSize.x, checkpointSize.y);

  car.show();
  car.update();
}
void updateCheckpoint() {
  //We dont have to do more than that i think
  if (isInsideRect(car.pos, checkpointPos, checkpointSize)) {
    checkpointCleared = true;
  }
}
void updateStart() {
  if (isInsideRect(car.pos, startPos, startSize)) {
    if (raceStarted == false && checkpointCleared == false) {
      raceStarted = true;
      println("Race Started");
    } else if (checkpointCleared == true) {
      //If raceStarted then test if the checkpoint is clear 
      raceStarted = false;
      println("Race finished");
    }
  }
}
boolean isInsideRect(PVector p, PVector rp, PVector rs) {
  if (p.x > rp.x - rs.x/2 && p.x < rp.x + rs.x/2 && 
    p.y > rp.y - rs.y/2 && p.y < rp.y + rs.y/2) {
    return true;
  }
  return false;
}
void keyPressed() {
  if (key == 'w' || key == 'W' || key == ' ') {
    move = true;
  }
}
void keyReleased() {
  move = false;
}
void moveBackground(PVector vel) {
  vel.x = -vel.x;
  vel.y = -vel.y;
  //move also the start and endpoint
  startPos.add(vel);
  //of course we need to move the checkpoint aswell
  checkpointPos.add(vel);
  backgroundPos.add(vel);
}
class Car {
  //PImage carImage;
  PVector pos;
  //float angle;
  //float speed;
  Car(float x, float y) {
    //carImage = loadImage("car.png");
    //link to the Picture: https://toppng.com/uploads/preview/aston-martin-one77-01-top-down-car-sprite-1156302814774vtgthfmb.png

    carImage.resize(0, 100);

    pos = new PVector(x, y);
    angle = 0;
    speed = 20;
  }
  void show() {
    pushMatrix();
    translate(pos.x, pos.y);
    rotate(angle);
    image(carImage, 0, 0);
    popMatrix();
  }
  void update() {
    updateAngle();
    //Just try both and decide which one you like more

    //updateMovementFirstExample();
    updateMovementSecondExample();
  }
  void updateAngle() {
    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);
    angle = atan2(vel.y, vel.x);
  }
  void updateMovementSecondExample() {
    if (move == false) return;

    PVector vel = PVector.sub(new PVector(mouseX, mouseY), pos);

    if (vel.mag() < speed) return;

    vel.normalize();
    vel.mult(speed);
    moveBackground(vel);
  }
}

I hope I could help you :slight_smile:

Edit:
I removed the Serialport because I dont have an Arduino sorry :sweat_smile: