Let the AI play the game in the browser side, where the AI plays the game, which is broken down into AI and game, so share the main two relatively complete parts of the game and neural network. We first used JS to play games on atari in browser. It is estimated that even the post-80s may only hear about this game machine and have never seen the real one. I did see the real one.

A little request for you

  • Familiar with Web development
  • Understand the new features of HTML5, especially the Canvas API, which is frequently used here
  • Familiar with neural networks

The above is a little request for you, but also to fully understand the premise of this article

Js implementation game

Start building environment

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <canvas id=gameCanvas" width="700" height="500"></canvas>
    <script>
        
    </script>
</body>
</html>
Copy the code
  • Under the project, the so-called project is to create a folder, and then create index.html, the project is completed, so JS development is so popular, because it is easy to get started, intuitive. Unlike other languages, just setting up the environment is exhausting.
  • Create a canvas element and single-handedly terminate the Flash canvas. Now all the drawing work on HTML is completed on this canvas. Therefore, he is also given the task of drawing the game today

Write javascript code inside the Script tag

/ * *@type {HTMLCanvasElement} * /
        var canv = document.getElementById("gameCanvas");
        var ctx = canv.getContext("2d");
Copy the code
  • Get the canvas by id, and then get the 2D context of the canvas, that is, the interface that canvas provides for drawing on the canvas.
  • Some of the new features in javascript also address the jQuery setup. Think about how you might not have known about jQuery when you started web front-end development recently
    const FPS = 30; // Set frames per second

    / * *@type {HTMLCanvasElement} * /
    var canv = document.getElementById("gameCanvas");
    var ctx = canv.getContext("2d");

    / / set
    setInterval(update, 1000 / FPS);

    function update(){
        // Draw the background

        / / draw the ship

        / / rotate the ship

        / / move the ship
    }
Copy the code

Update the picture

  • No matter animation or game, a series of pictures are continuously presented in front of us at a certain time interval, which is also realized by using the function of our eyes to make us feel the continuity of the picture
  • Here the setInterval method is a method that can repeatedly execute the code fragment at the specified delay time, which is exactly what we need, so the update screen will be put inupdateFunction, which is then executed at intervals by setInterval

Draw the background

    ctx.fillStyle = "black";
    ctx.fillRect(0.0, canv.width, canv.height);
Copy the code

Draw the ship

The shape of ship is very simple, that is, a triangle is used to represent the ship. The ship state includes the position of the center point X and Y, the size of the ship and the Angle of the ship. Therefore, the state of the ship is determined by the position and rotation Angle at time T.

var ship = {
    x: canv.width / 2.y: canv.height / 2.r: SHIP_SIZE / 2.a: 90 / 180 * Math.PI //
}
Copy the code

The following code is the principle behind drawing a ship on the canvas, which is the geometry, so adding 4/3 and 2/3 coefficients is to put x and y in the ship to adjust compensation

    // Draw the ship role
    ctx.strokeStyle = "white";
    ctx.lineWidth = SHIP_SIZE / 20;
    ctx.beginPath();
    
    // Canvas's origin is in the upper-left corner
    // The coordinate Angle should be rotated counterclockwise with the right half of the x axis as the starting axis to get Angle A
    ctx.moveTo(
        ship.x + 4/3 * ship.r * Math.cos(ship.a),
        ship.y - 4/3 * ship.r * Math.sin(ship.a)
    );
    
    ctx.lineTo(
        ship.x - ship.r * (2/3 * Math.cos(ship.a) + Math.sin(ship.a)),
        ship.y + ship.r * (2/3 * Math.sin(ship.a) - Math.cos(ship.a))
    );

    ctx.lineTo(
        ship.x - ship.r * (2/3 * Math.cos(ship.a) - Math.sin(ship.a)),
        ship.y + ship.r * (2/3 * Math.sin(ship.a) + Math.cos(ship.a))
    );

    ctx.closePath();
    ctx.stroke();
Copy the code

So ship drawing is made up of three lines that are joined end to end, so using the Canvas line API just to make it simple, imagine a pen beginPath is to take a pen and say to the Canvas I’m going to draw a line on it moveTo and we put the pen in a position that is moveTo, Then, step by step, closePah moves the stroke to draw a line. Finally, closePah closes the beginning and end of the stroke, and finally executes the stroke to draw the line in the specified line.

The event processing

// Set the event handler
document.addEventListener("keydown",keyDown);
document.addEventListener("keyup",keyUp);

function keyDown(/ * *@type {keyboardEvent} * / ev){
    console.log("keydown");
}

function keyUp(/ * *@type {keyboardEvent} * / ev){
    console.log("keyup");
}
Copy the code

Let the ship rotate

    var ship = {
        x: canv.width / 2.y: canv.height / 2.r: SHIP_SIZE / 2.a: 90 / 180 * Math.PI, //
        rot: 0
    }
Copy the code

First add a property to the ship, which is the rotation Angle of the ship. Default is 0. When the left arrow key is pressed by the keyboard, the ship is rotated at an Angle const TRUN_SPEED = 360; This is the amount of rotation each time you press left or right ship.

As for the action here, the strategy mode replaces the switch function, in fact, it may complicate the problem, so it is better to use switch directly. The strategy mode about how to achieve code will not be explained too much here, and it is estimated that everyone can understand it at a glance.

class ShipAction {
        constructor(actionId, handler){
            this._actionId = actionId;
            this._handler = handler
        }

        doAction(){
            this._handler(); }}class ShipActionManager{
        constructor(){
            this._actions =[];
        }

        addAction(action){
            this._actions = [...this._actions,action]
        }
        getAction(actionId){
            return this._actions.find(action= >action._actionId == actionId); }}const shipKeyDownActionManager = new ShipActionManager();
    const shipKeyUpActionManager = new ShipActionManager();
    
    const leftAction = new ShipAction(KEYCODE.LEFT,() = >{
        ship.rot = TRUN_SPEED / 180 * Math.PI /180;
    });
    const rightAction = new ShipAction(KEYCODE.RIGHT,() = >ship.rot = -TRUN_SPEED / 180 * Math.PI /180);
    const stopLeftAction = new ShipAction(KEYCODE.LEFT,() = >ship.rot = 0);
    const stopRightAction = new ShipAction(KEYCODE.RIGHT,() = >ship.rot = 0);

    shipKeyDownActionManager.addAction(leftAction);
    shipKeyDownActionManager.addAction(rightAction);

    shipKeyUpActionManager.addAction(stopLeftAction);
    shipKeyUpActionManager.addAction(stopRightAction);


    function keyDown(/ * *@type {keyboardEvent} * / ev){
    
        shipAction = shipKeyDownActionManager.getAction(ev.keyCode);
        if(shipAction ! =undefined){
            shipAction.doAction()
        }
    }

    function keyUp(/ * *@type {keyboardEvent} * / ev){
        shipAction = shipKeyUpActionManager.getAction(ev.keyCode);
        if(shipAction ! =undefined){
            shipAction.doAction()
        }
        
    }
Copy the code

So at this point, we’re done controlling the ship rotation.

Add thrusters for ship

const SHIP_THRUST = 5;
const FRICTION = 0.7;
Copy the code

Two constants, thrust, power, and FRICTION, were added to allow the ship to slow to a halt when the button is released.

Add in the update function

//thrust the ship
if (ship.thrusting){
    ship.thrust.x += SHIP_THRUST * Math.cos(ship.a) / FPS;
    ship.thrust.y -= SHIP_THRUST * Math.sin(ship.a) / FPS;
}else{
    ship.thrust.x -= FRICTION * ship.thrust.x / FPS;
    ship.thrust.y -= FRICTION * ship.thrust.y / FPS;
}
Copy the code

When the UP button is pressed, the SHIP thruster starts to accelerate. When the UP button is released, FRICTION exists due to resistance.

ship.x += ship.thrust.x;
ship.y += ship.thrust.y;
Copy the code

So I’m going to clean up the code a little bit, and I’m going to put the drawing ship code into a method called drawShip, and then we’re going to have the effect of thruster acceleration back and forth, which is the little triangle that represents the fire behind the ship as it pushes.

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <canvas id="gameCanvas" width="700" height="500"></canvas>
    <script>

        const FPS = 30; // Set frames per second
        const FRICTION = 0.7;
        const SHIP_SIZE = 30;
        const TRUN_SPEED = 360;
        const SHIP_THRUST = 5;

        const KEYCODE = {
            LEFT: 37.UP:38.RIGHT: 39.DOWN:40
            
        }
        / * *@type {HTMLCanvasElement} * /
        var canv = document.getElementById("gameCanvas");
        var ctx = canv.getContext("2d");


        // Set the function to refresh the game screen, that is, draw the game screen on the canvas every second
        setInterval(update, 1000 / FPS);
        

        // Define the SHIP role
        var ship = {
            x: canv.width / 2.y: canv.height / 2.r: SHIP_SIZE / 2.a: 90 / 180 * Math.PI, //
            rot: 0.thrusting: false.thrust: {x: 0.y: 0}}function drawThrustingEffect(ctx,color,size,scale){
           
            ctx.fillStyle = color
            ctx.strokeStyle = "yellow";
            ctx.lineWidth = size;

            ctx.beginPath();

            ctx.moveTo(
                ship.x - ship.r * (2/3 * Math.cos(ship.a) + scale * Math.sin(ship.a)),
                ship.y + ship.r * (2/3 * Math.sin(ship.a) - scale * Math.cos(ship.a))
            );

            ctx.lineTo(
                ship.x - 4/3 * ship.r * Math.cos(ship.a),
                ship.y + 4/3 * ship.r * Math.sin(ship.a)
            );

            ctx.lineTo(
                ship.x - ship.r * (2/3 * Math.cos(ship.a) - scale *Math.sin(ship.a)),
                ship.y + ship.r * (2/3 * Math.sin(ship.a) + scale * Math.cos(ship.a))
            );

            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            

        }


        / / draw the ship

        function drawShip(ctx){
            ctx.strokeStyle = "white";
            ctx.lineWidth = SHIP_SIZE / 20;
            ctx.beginPath();
            
            // Canvas's origin is in the upper-left corner
            // The coordinate Angle should be rotated counterclockwise with the right half of the x axis as the starting axis to get Angle A
            ctx.moveTo(
                ship.x + 4/3 * ship.r * Math.cos(ship.a),
                ship.y - 4/3 * ship.r * Math.sin(ship.a)
            );
            
            // ship.x - ship.r * Math.cos(ship.a) - ship.r * Math.sin(ship.a)
            // Original inscribed triangle, 120 degrees (a + 120)/180 * math.pi
            // ship.x + ship.r * Math.cos(ship.a)
            ctx.lineTo(
                ship.x - ship.r * (2/3 * Math.cos(ship.a) + Math.sin(ship.a)),
                ship.y + ship.r * (2/3 * Math.sin(ship.a) - Math.cos(ship.a))
            );

            ctx.lineTo(
                ship.x - ship.r * (2/3 * Math.cos(ship.a) - Math.sin(ship.a)),
                ship.y + ship.r * (2/3 * Math.sin(ship.a) + Math.cos(ship.a))
            );

            ctx.closePath();
            ctx.stroke();

        }

        function update(){
            // Draw the background
            ctx.fillStyle = "black";
            ctx.fillRect(0.0, canv.width, canv.height);

            //thrust the ship
            if (ship.thrusting){
                ship.thrust.x += SHIP_THRUST * Math.cos(ship.a) / FPS;
                ship.thrust.y -= SHIP_THRUST * Math.sin(ship.a) / FPS;

                drawThrustingEffect(ctx,"red",SHIP_SIZE/10.0.5);
            }else{
                ship.thrust.x -= FRICTION * ship.thrust.x / FPS;
                ship.thrust.y -= FRICTION * ship.thrust.y / FPS;
            }

            // Screen out control
            if (ship.x < 0 - ship.r){
                ship.x = canv.width + ship.r;
            }else if(ship.x > canv.width + ship.r){
                ship.x = 0 - ship.r;
            }

            if (ship.y < 0 - ship.r){
                ship.y = canv.height + ship.r;
            }else if(self.y > canv.height + ship.r){
                ship.y = 0 - ship.r;
            }

            // Draw the ship role

            drawShip(ctx);

            // Rotate ship role
            ship.a += ship.rot

            // Move the SHIP role
            ship.x += ship.thrust.x;
            ship.y += ship.thrust.y;

        }

        // Set the event handler
        document.addEventListener("keydown",keyDown);
        document.addEventListener("keyup",keyUp);

        class ShipAction {
            constructor(actionId, handler){
                this._actionId = actionId;
                this._handler = handler
            }

            doAction(){
                this._handler(); }}class ShipActionManager{
            constructor(){
                this._actions =[];
            }

            addAction(action){
                this._actions = [...this._actions,action]
            }
            getAction(actionId){
                return this._actions.find(action= >action._actionId == actionId); }}const shipKeyDownActionManager = new ShipActionManager();
        const shipKeyUpActionManager = new ShipActionManager();
        
        const leftAction = new ShipAction(KEYCODE.LEFT,() = >{
            ship.rot = TRUN_SPEED / 180 * Math.PI /180;
        });
        const rightAction = new ShipAction(KEYCODE.RIGHT,() = >ship.rot = -TRUN_SPEED / 180 * Math.PI /180);
        const startThrustingAction = new ShipAction(KEYCODE.UP, () = > ship.thrusting = true);

        const stopLeftAction = new ShipAction(KEYCODE.LEFT,() = >ship.rot = 0);
        const stopRightAction = new ShipAction(KEYCODE.RIGHT,() = >ship.rot = 0);
        const stopThrustingAction = new ShipAction(KEYCODE.UP, () = > ship.thrusting = false);

        shipKeyDownActionManager.addAction(leftAction);
        shipKeyDownActionManager.addAction(rightAction);
        shipKeyDownActionManager.addAction(startThrustingAction);
        

        shipKeyUpActionManager.addAction(stopLeftAction);
        shipKeyUpActionManager.addAction(stopRightAction);
        shipKeyUpActionManager.addAction(stopThrustingAction);


        function keyDown(/ * *@type {keyboardEvent} * / ev){
        
            shipAction = shipKeyDownActionManager.getAction(ev.keyCode);
            if(shipAction ! =undefined){
                shipAction.doAction()
            }
        }

        function keyUp(/ * *@type {keyboardEvent} * / ev){
            shipAction = shipKeyUpActionManager.getAction(ev.keyCode);
            if(shipAction ! =undefined){
                shipAction.doAction()
            }
            
        }

    </script>
</body>
</html>
Copy the code