/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.H7-fW46yzsG/rev.588
*
* authors:
* Wolfe
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
// Car turning and driving according to tires angle
// http://engineeringdotnet.blogspot.com/2010/04/simple-2d-car-physics-in-games.html helped alot but I had to modify it
// Try to look latest revision of this animation, maybe I added something new
// TODO: compute turning point and turning radius of vehicle
// Controls:
// arrows to steer and change speed
// + and - to change size (lenghth of wheelbase = lengeth between front and back wheels)
// space to reset
// A to lock steering angle (won't turn back)
PVector carLocation;
float carHeading;
float carSpeed, maxSpeed;
float steerAngle;
float maxSteerAngle = PI/4;
float wheelBase, minWB, maxWB; // the distance between the two axles
boolean plus, minus, up, down, left, right, steerLock;
PVector frontWheel;
PVector backWheel;
void setup()
{
size(600, 600);
up = down = left = right = steerLock = false;
carLocation = new PVector(width/2, height/2);
carHeading = PI;
carSpeed = 0;
maxSpeed = 10;
steerAngle = 0;
wheelBase = 50;
minWB = 30;
maxWB = 150;
}
void draw()
{
background(#89FF81);
frontWheel = new PVector(carLocation.x+(wheelBase/2)*sin(carHeading), carLocation.y+(wheelBase/2)*cos(carHeading));
backWheel = new PVector(carLocation.x-(wheelBase/2)*sin(carHeading), carLocation.y-(wheelBase/2)*cos(carHeading));
// body of vehicle
pushMatrix();
translate(carLocation.x, carLocation.y);
rotate(-carHeading);
rectMode(CENTER);
fill(255);
rect(0, 0, wheelBase/3, wheelBase+wheelBase/2, 3, 3, wheelBase/(wheelBase/20), wheelBase/(wheelBase/20));
fill(200);
rect(0, 0, wheelBase/5, wheelBase/1.9, 3, 3, wheelBase/(wheelBase/15), wheelBase/(wheelBase/15));
popMatrix();
// end: body of vehicle
// front axle
pushMatrix();
translate(frontWheel.x, frontWheel.y);
rotate(-carHeading);
strokeWeight(2);
line (-wheelBase/7, 0, wheelBase/7, 0);
strokeWeight(1);
popMatrix();
// end: front axle
// front left wheel
pushMatrix();
translate(frontWheel.x+sin(carHeading+PI/2)*wheelBase/7, frontWheel.y+cos(carHeading+PI/2)*wheelBase/7);
// sin(carHeading+PI/2) and cos(carHeading+PI/2) helps to place wheel to correct position shifted to side by (wheelBase/7)
// otherwise it would circle adound front regarding to a heading
// I don't know to work well with these Processing matrix translate/rotate things
rotate(-steerAngle-carHeading);
rectMode(CENTER);
fill(150);
rect(0, 0, wheelBase/7, wheelBase/3, wheelBase/(wheelBase/10));
popMatrix();
// end: front left wheel
// front right wheel
pushMatrix();
translate(frontWheel.x-sin(carHeading+PI/2)*wheelBase/7, frontWheel.y-cos(carHeading+PI/2)*wheelBase/7);
rotate(-steerAngle-carHeading);
rectMode(CENTER);
fill(150);
rect(0, 0, wheelBase/7, wheelBase/3, wheelBase/(wheelBase/10));
popMatrix();
// end: front right wheel
// back wheels
pushMatrix();
translate(backWheel.x, backWheel.y);
rotate(-carHeading);
rectMode(CENTER);
fill(150);
strokeWeight(2);
line(-wheelBase/7, 0, wheelBase/7, 0);
strokeWeight(1);
rect(-wheelBase/7, 0, wheelBase/7, wheelBase/3, wheelBase/(wheelBase/10));
rect(wheelBase/7, 0, wheelBase/7, wheelBase/3, wheelBase/(wheelBase/10));
popMatrix();
// end: back wheels
frontWheel.add(carSpeed*sin(carHeading+steerAngle), carSpeed*cos(carHeading+steerAngle), 0);
backWheel.add(carSpeed*sin(carHeading), carSpeed*cos(carHeading), 0);
carLocation.set(frontWheel.x+backWheel.x, frontWheel.y+backWheel.y, 0) ;
carLocation.div(2);
if (carLocation.x<0) carLocation.x=width;
if (carLocation.x>width) carLocation.x=0;
if (carLocation.y<0) carLocation.y=height;
if (carLocation.y>height) carLocation.y=0;
carHeading = atan2( frontWheel.x - backWheel.x, frontWheel.y - backWheel.y );
fill(0);
if(carSpeed<0) text("®",5,height-40);
text("Speed: "+carSpeed, 20, height-40);
text("Heading: ("+round(degrees(carHeading))+"º) "+carHeading, 20, height-30);
text("Steer angle: ("+round(degrees(steerAngle))+"º) "+steerAngle, 20, height-20);
text("Wheel base: "+wheelBase, 20, height-10);
// prcessing.js messes text so I separated it
text("Instructions:", 2, 10);
text("↑",2, 25); text("accelerate", 32, 25);
text("←",2, 35); text("steer left", 32, 35);
text("→",2, 45); text("steer right", 32, 45);
text("↓",2, 55); text("deccelerate", 32, 55);
text("+",2, 65); text("lengthen wheelbase", 32, 65);
text("-",2, 75); text("shorten wheelbase", 32, 75);
text("A",2, 85); text("lock steering", 32, 85);
text("spacebar RESET", 2, 100);
// ⭕⭕⭕
if (up) text("◉",16,25);
if (left) text("◉",16,35);
if (right) text("◉",16,45);
if (down) text("◉",16,55);
if (plus) text("◉",16,65);
if (minus) text("◉",16,75);
if (steerLock) text("◉",16,85);
// ⭕⭕⭕
fill(255);
// LEFT
if (left)
{
if (steerAngle < maxSteerAngle) steerAngle += 0.08;
if (steerAngle>maxSteerAngle) steerAngle = maxSteerAngle;
}
else
{
if (!steerLock) if (steerAngle > 0) steerAngle -= 0.08;
}
// RIGHT
if (right)
{
if (steerAngle > -maxSteerAngle) steerAngle -= 0.08;
if (steerAngle<-maxSteerAngle) steerAngle = -maxSteerAngle;
}
else
{
if (!steerLock) if (steerAngle < 0) steerAngle += 0.08;
}
// UP
if (up)
{
if (carSpeed<maxSpeed) carSpeed += 0.05;
}
else
{
}
// DOWN
if (down)
{
if (carSpeed>0) carSpeed -= 0.15; //brake
else
if (abs(carSpeed)<maxSpeed) carSpeed -= 0.05; // reverse
}
else
{
}
// PLUS
if (plus)
{
if (wheelBase<maxWB) wheelBase += 1;
}
else
{
}
// MINUS
if (minus)
{
if (wheelBase>minWB) wheelBase -= 1;
}
else
{
}
if (abs(steerAngle)<0.08) steerAngle = 0;
if (carSpeed>0) carSpeed -= 0.01; //friction for forward
if (carSpeed<0) carSpeed += 0.01; //friction for backward
if ((!up && !down) && (abs(carSpeed)<0.01)) carSpeed=0;
}
void keyPressed()
{
if (keyCode==32) // spacebar
{
carLocation.set(width/2, height/2, 0);
steerAngle = 0;
carSpeed = 0;
carHeading = PI;
wheelBase = 50;
}
if (keyCode==65) // A
{
steerLock = !steerLock;
}
// for processing.js I had to add keycode 187 and 189
if ((keyCode==61) || (keyCode==187)) // +
{
plus = true;
}
if ((keyCode==45) || (keyCode==189)) // -
{
minus = true;
}
if (key == CODED)
{
switch(keyCode) {
case LEFT:
left=true;
break;
case RIGHT:
right=true;
break;
case UP:
up=true;
break;
case DOWN:
down=true;
break;
}
}
}
void keyReleased()
{
// for processing.js I had to add keycode 187 and 189
if ((keyCode==61) || (keyCode==187)) // +
{
plus = false;
}
if ((keyCode==45) || (keyCode==189)) // -
{
minus = false;
}
if (key == CODED)
{
switch(keyCode) {
case LEFT:
left=false;
break;
case RIGHT:
right=false;
break;
case UP:
up=false;
break;
case DOWN:
down=false;
break;
}
}
}
//???????????????????? THE END ????????????????????//