ive been trying to flip the arrows when the cars are in different side of the road. The image shows two multi-lane roads where forward on the left side goes up and i want forward on the right to go down, and the same for right and left. ive tried my method but it doesnt seem to work so i think im doing something stupid. please hep with a method that can work, heres the code
class Controls
{
constructor(type,laneIndex)
{
this.laneIndex = laneIndex;
this.forward =false;
this.left = false ;
this.foward = false ;
this.reverse =false;
switch (type)
{
case "KEYS":
this.#addKeyboardListeners();
break;
case "DUMMY":
this.forward =true;
break;
}
}
#addKeyboardListeners()
{
if (this.laneIndex <= 3)
{
document.onkeydown=(event)=>
{
switch(event.key)
{
case "ArrowLeft":
this.left =true;
break;
case "ArrowRight":
this.right =true;
break;
case "ArrowUp":
this.forward =true;
break;
case "ArrowDown":
this.reverse =true;
break;
}
}
document.onkeyup=(event)=>
{
switch(event.key)
{
case "ArrowLeft":
this.left = false;
break;
case "ArrowRight":
this.right = false;
break;
case "ArrowUp":
this.forward = false;
break;
case "ArrowDown":
this.reverse =false;
break;
}
}
}
if (this.laneIndex >= 4)
{
document.onkeydown=(event)=>
{
switch(event.key)
{
case "ArrowLeft":
this.right =true;
break;
case "ArrowRight":
this.left =true;
break;
case "ArrowUp":
this.reverse =true;
break;
case "ArrowDown":
this.forward =true;
break;
}
}
document.onkeyup=(event)=>
{
switch(event.key)
{
case "ArrowLeft":
this.right =false;
break;
case "ArrowRight":
this.left =false;
break;
case "ArrowUp":
this.reverse =false;
break;
case "ArrowDown":
this.forward =false;
break;
}
}
}
}
}
This answer shows a method for monitoring the ongoing keyboard state.
Keys of interest are flagged as true when down and false when up.
There is a demo (bottom) to show how this method can be used in an animated app interface.
Keep IO (mouse, keyboards, touch, joysticks etc..) and game logic separate as this help keeps the code simple.
Don't add events via the named event property. Eg document.onkeydown = function
. As it can easily be overwritten by you or 3rd party code.
Use addEventListener
to ensure the event will stay in place
The example creates an object keys
that has a property for each of the keys you want to keep track of. The keyboard event listener will set the state to true when a key is down and false if not.
You can add other keys by adding the KeyboardEvent.code name to keys object. Example I have added Space
to listen for the space key (I will use it to switch tracks in next example.
const keys = (() => {
const keys = { // only names of keys you are interested in
ArrowUp: false,
ArrowRight: false,
ArrowDown: false,
ArrowLeft: false,
Space: false,
};
function listener(e) {
keys[e.code] !== undefined && (keys[e.code] = e.type === "keydown");
}
addEventListener("keyup", listener);
addEventListener("keydown", listener);
return keys;
})();
Once you have the controlling input setup, your app logic need only monitor the state of the input and react as needed.
The example below uses the keys
object to move the active car. W
moves the car forward, and D
moves it in reverse. The function that moves cars is Car.update
To switch cars press the space key. Switching cars can be found in renderLoop
Note Use WASD
for control as arrow keys are not safe in stack overflow snippets
Note you need to focus the snippet for the keyboard events to be seen as stack overflow snippets can not automatically focus.
const keys = (() => {
const keys = { // only names of keys you are interested in
KeyW: false,
KeyS: false,
Space: false,
};
function listener(e) {
keys[e.code] !== undefined && (keys[e.code] = e.type === "keydown");
e.preventDefault(); // to stop stack overflow snippet problems
}
addEventListener("keyup", listener);
addEventListener("keydown", listener);
return keys;
})();
const P2 = (x, y) => ({x, y}); // creates 2D point
const Car = { // common properties for a car
on: false,
col: "#888",
/* Function that inspects keyboard state and moves car */
update() {
if (this.on) { // only if active
let mx = 0, my = 0;
if (keys.KeyW) { /* Check forward */
mx += this.dir.x * 3;
my += this.dir.y * 3;
}
if (keys.KeyS) { /* Check reverse */
mx -= this.dir.x;
my -= this.dir.y;
}
// Keep on canvas
if (this.pos.x + mx > W - 40 || this.pos.x + mx < 40) { mx = 0; }
if (this.pos.y + my > H - 40 || this.pos.y + my < 40) { my = 0; }
this.pos.x += mx;
this.pos.y += my;
}
},
draw() {
ctx.fillStyle = this.col;
const sx = this.size.x, sy = this.size.y;
ctx.setTransform(this.dir.y, this.dir.x, -this.dir.x, this.dir.y, this.pos.x, this.pos.y);
ctx.fillRect(sx * -0.5, sy * -0.5, sx, sy);
ctx.fillStyle = "#9AD";
ctx.fillRect(sx * -0.4, sy * -0.4, sx * 0.1, sy * 0.25);
ctx.fillRect(sx * -0.4, sy * -0.1, sx * 0.8, sy * 0.2);
ctx.fillRect(sx * 0.3, sy * -0.4, sx * 0.1, sy * 0.25);
ctx.strokeStyle = "#EA5";
ctx.lineWidth = 2;
ctx.beginPath();
if (this.on) {
ctx.moveTo(sx * -0.4 , sy * 1.5);
ctx.lineTo(sx * -0.25, sy * 0.5);
ctx.lineTo(sx * -0.1 , sy * 1.5);
ctx.moveTo(sx * 0.4 , sy * 1.5);
ctx.lineTo(sx * 0.25, sy * 0.5);
ctx.lineTo(sx * 0.1 , sy * 1.5);
}
ctx.moveTo(sx * -0.1 , sy * 0.5);
ctx.lineTo(sx * -0.4 , sy * 0.5);
ctx.moveTo(sx * 0.1 , sy * 0.5);
ctx.lineTo(sx * 0.4 , sy * 0.5);
ctx.stroke();
ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default transform incase
// you draw stuff not using setTransform
}
}
const cars = [{
dir: P2(0, 1), // forward direction
size: P2(30, 50),
pos: P2(100, 100),
...Car
}, {
dir: P2(0, -1), // forward direction
size: P2(30, 50),
pos: P2(200, 100),
...Car
},
];
requestAnimationFrame(renderLoop);
const ctx = canvas.getContext("2d"), W = canvas.width, H = canvas.height;
var carOnIdx = 0;
cars[carOnIdx].on = true;
function renderLoop(time) {
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = "#555";
ctx.fillRect(10, 10, W - 20, H - 20);
ctx.fillStyle = "#DDD";
ctx.fillRect(70, 10, 2, H - 20);
ctx.fillRect(130, 10, 2, H - 20);
ctx.fillRect(175, 10, 2, H - 20);
ctx.fillRect(225, 10, 2, H - 20);
// If space key press changed cars
if (keys.Space) {
cars[carOnIdx].on = false;
carOnIdx = (carOnIdx + 1) % cars.length;
cars[carOnIdx].on = true;
// Only want the key down for space to flag it as used
keys.Space = false;
}
for (const car of cars) { car.update(); }
for (const car of cars) { car.draw(); }
requestAnimationFrame(renderLoop);
}
* {
font-family: arial;
}
canvas {
background: #4A6;
}
.info {
position: absolute;
left: 340px;
top: 20px;
font-size: large;
}
ul {
position: absolute;
left: 320px;
top: 30px;
}
<canvas id="canvas" width="300" height="300"></canvas>
<div class="info">Using keyboard input</div>
<ul>
<li>To focus keyboard on snippet, click snippet</li>
<li>Uses WASD rather than arrow keys</li>
<li>Space to switch cars</li>
<li>W to drive forward</li>
<li>D to reverse</li>
<li><b>Note</b> cars can not go outside canvas</li>
</ul>