xxxxxxxxxx
168
// made 4/19/23 @ RC creative coding
// rfong, kevan hollbach, rodrigo espinosa de los monteros
let car;
let rotInc, accInc;
let arena = {w:400,h:400};
let canvas;
let arrowKeys = [];
let started = false;
function setup() {
canvas = createCanvas(600, 600);
rectMode(CENTER);
textAlign(CENTER, CENTER);
arrowKeys = [LEFT_ARROW, RIGHT_ARROW, DOWN_ARROW, UP_ARROW];
car = new Car(300,400);
rotInc = PI/48;
accInc = 0.05;
}
function draw() {
background(220); // grey
// draw arena
stroke(0,0,0);
fill(255,255,255);
rect(300,300, arena.w,arena.h);
if (!started) {
push();
noStroke(); fill(200); textSize(30);
text('up/down to change speed\nright/left to rotate',
canvas.width/2,
canvas.height/2);
pop();
}
car.draw();
}
// only on initial keypress
function keyPressed() {
if (keyCode === UP_ARROW) {
// accelerator
car.accel += accInc;
} else if (keyCode === DOWN_ARROW) {
// brakes
car.accel -= accInc;
}
if (arrowKeys.includes(keyCode)) {
started = true;
}
}
function keyReleased() {
if (keyCode === UP_ARROW) {
// accelerator
car.accel -= accInc;
} else if (keyCode === DOWN_ARROW) {
// brakes
car.accel += accInc;
}
}
class Car {
constructor(x,y) {
// x and y are the center of the rect
this.x = x;
this.y = y;
this.width = 30;
this.height = 50;
// p5 angle: 0 = pointing up, positive goes clockwise
this.dir = PI/12; // direction
this.vel = 0; // internal
this.accel = 0; // public
}
update() {
this.vel += this.accel;
this.x += this.vel * sin(this.dir);
this.y -= this.vel * cos(this.dir);
// if key is held down
if (keyIsDown(LEFT_ARROW)) {
car.dir -= rotInc;
} else if (keyIsDown(RIGHT_ARROW)) {
car.dir += rotInc;
}
let corner = this.checkBounds();
if (corner) {
// TODO: how much to bounce back by?
this.vel *= -0.9; // bounce back
// NOTE: if we get stuck past the boundary, this naive sign flip lets us continue pushing slowly through the arena wall
}
// TODO implement floor friction
// TODO don't let us rotate a corner into the wall
}
draw() {
this.update();
push(); // start new draw state
// draw car body
stroke(0,0,0);
noFill();
translate(this.x, this.y);
rotate(this.dir);
rect(0,0, this.width, this.height);
// draw indicator for front of car
fill(255,0,0);
triangle(
0,-this.height/2,
-5,-this.height/2+10, 5,
-this.height/2+10
);
pop(); // end draw state
}
getCorners() {
const center = new p5.Vector(this.x, this.y),
w = this.width,
h = this.height,
dir = this.dir;
// half the diagonal of the full rect
const d = sqrt(Math.pow(w/2, 2) + Math.pow(h/2, 2));
// angle off dir to the corner in the +x,+y direction
const theta = atan(w/h)+PI/2;
// calculate the corners relative to the center
return [
new p5.Vector(d*cos(dir+theta), d*sin(dir+theta)).add(center),
new p5.Vector(d*cos(dir-theta), d*sin(dir-theta)).add(center),
new p5.Vector(d*cos(dir+theta+PI),
d*sin(dir+theta+PI)).add(center),
new p5.Vector(d*cos(dir-theta+PI),
d*sin(dir-theta+PI)).add(center),
];
}
checkBounds() {
const corners = this.getCorners();
// draw all corners
for (const c of corners) {
fill(255,0,0);
noStroke();
circle(c.x, c.y, 5);
}
// detect collision
for (const c of corners) {
// if we hit part of the canvas, return the corner that hit
// TODO: calculate arena bounds instead of hardcoding xy
if (c.x >= 100+arena.w || c.x <= 100 ||
c.y >= 100+arena.h || c.y <= 100) {
return c;
}
}
return false;
}
}