Search code examples
javascripthtml5-canvasgame-physics

Arrow Key implementation javascript


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;
                    }
                }
            }   
        }
    } 

two one way multi-lane roads


Solution

  • Basic keyboard input

    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.

    Some pointers

    • 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

    Example of keyboard input

    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;
    })();
    

    Using the input

    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>