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 in
update
Function, 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