当前位置:网站首页>3. Realize snake and basic game interface
3. Realize snake and basic game interface
2022-07-24 12:46:00 【Wuhu boy】
1. The overall framework

2. Modify map
thought
If two snakes , Go to the same grid at the same time , It will lead to a draw , This situation will be disadvantageous to the advantaged !
We need to turn the map into even numbers ride Odd number
Modify the code :
x = rows - 1 - x;
y = cols - 1 - y;
Realization way :
The following positions need to be modified :


Success interface :

3. Realize the head of the snake
1. thought
How to draw a snake -> In essence, snakes are a sequence of lattices .
2. Realization
- newly build
Cell.jsStore a sequence of grids --> Storage snake .
export class Cell {
constructor(r, c) {
this.r = r;
this.c = c;
// Convert to canvas Coordinates of
this.x = c + 0.5;
this.y = r + 0.5;
}
}

- newly build
Snake.jsobject , It's convenient for us to operate .
import {
AcGameObject } from "./AcGameObject";
import {
Cell } from "./Cell";
export class Snake extends AcGameObject {
constructor(info, gamemap) {
super();
// Take out the basic id
this.id = info.id;
this.color = info.color;
this.gamemap = gamemap; // Convenient to call functions and parameters
// Store the snake's body ;
this.cells = [new Cell(info.r, info.c)];
}
start() {
}
update() {
this.render();
}
render() {
// Draw the basic snake head
const L = this.gamemap.L;
const ctx = this.gamemap.ctx;
ctx.fillStyle = this.color;
for (const cell of this.cells) {
ctx.beginPath();
ctx.arc(cell.x * L, cell.y * L, L / 2, 0, Math.PI * 2);
ctx.fill();
}
}
}

- stay
GameMap.jsCreate an object with two snakes in .
this.Snakes = {
new Snake({
id : 0, color : "#4876ec", r : this.rows - 2, c : 1}, this),
new Snake({
id : 1, color : "#f94848", r : 1, c : this.cols - 2}, this),
}

- Success interface

4. Realize the movement of the snake
thought
The movement should be coherent .
There are many parts of the body , How to keep continuity ?
Keep still in the middle , Head and tail movement , Create a new node in the header , Move towards your destination . The tail moves towards the destination
When can a snake move ?
Simultaneous acquisition Two people / Two machines The operation can move .
4.1 Basic mobile
- stay
Snake.jsAdd code to , Realize the right movement of snake head .
import {
AcGameObject } from "./AcGameObject";
import {
Cell } from "./Cell";
export class Snake extends AcGameObject {
constructor(info, gamemap) {
super(); // Inherit AcGameObject Methods
this.id = info.id;
this.color = info.color;
this.gamemap = gamemap;
this.cells = [new Cell(info.r, info.c)]; // Store the snake's body , cell[0] Store snakeheads
// new add
this.speed = 5;
}
update_move() {
// To the right
this.cells[0].x += this.speed * this.timedelta / 1000;
// Move up
//this.cells[0].y -= this.speed * this.timedelta / 1000;
}
update() {
this.update_move();
this.render();
}

- The general effect :
The blue ball keeps moving to the right , The red one has moved out of sight .

4.2 Continuous movement
- Because there may be some problems , That is, a certain state in the middle , Not completely moved out , There will be problems with the snake's body .
- Don't move in the middle , Head and tail movement ! The created virtual node moves towards the destination . There are only two inching .
- Consider when the snake moves ? Turn-based game , Both people have input time , To move .
- modify
Snake.js
import {
AcGameObject } from "./AcGameObject";
import {
Cell } from "./Cell";
export class Snake extends AcGameObject {
constructor(info, gamemap) {
super();
this.id = info.id;
this.color = info.color;
this.gamemap = gamemap;
// Store the snake's body ;
this.cells = [new Cell(info.r, info.c)];
this.speed = 5; // The snake walks every second 5 grid
// new add
this.direction = -1; // -1 Indicates that there is no instruction 0 1 2 3
this.status = "idle"; // static , move Move die Death
}
}

- A referee is needed to judge the movement of the two snakes , We put in
GameMap.jsin
check_ready() {
// Judge whether the two snakes are ready for the next round
for (const snake of this.snakes) {
if (snake.status !== "idle") return false;
if (snake.direction === -1) return false;
}
return true;
}

- stay
Snake.jsUpdate the status of snake :
this.next_cell = null; // The next target location
this.dr = [-1, 0, 1, 0]; // That's ok
this.dc = [0, 1, 0, -1]; // Column
this.step = 0;
next_step() {
const d = this.direction;
this.next_cell = new Cell(this.cells[0].r + this.dr[d], this.cells[0].c + this.dc[d]);
this.direction = -1;
this.status = "move";
this.step ++ ;
}

- stay
GameMap.jsIn the update
next_step() {
for (const snake of this.snake) {
snake.next_step();
}
}
update() {
this.update_size();
if (this.check_ready()) {
this.next_step();
}
this.render();
}

4.3 Read the operation of the keyboard :
Get the operation from the keyboard wasd and ↑↓←→ To control two snakes .
- stay
GameMap.vueRevision in China
<canvas ref="canvas" tabindex="0"></canvas>

- The binding event .
stay Snake.js Add an auxiliary function to , Used to get direction .
// Auxiliary function
set_direction(d) {
this.direction = d;
}

stay GameMap.js Revision in China , Add event .
add_listening_events() {
this.ctx.canvas.focus();
const [snake0, snake1] = this.snakes;
this.ctx.canvas.addEventListener("keydown", e => {
if (e.key === 'w') snake0.set_direction(0);
else if (e.key === 'd') snake0.set_direction(1);
else if (e.key === 's') snake0.set_direction(2);
else if (e.key === 'a') snake0.set_direction(3);
else if (e.key === 'ArrowUp') snake1.set_direction(0);
else if (e.key === 'ArrowRight') snake1.set_direction(1);
else if (e.key === 'ArrowDown') snake1.set_direction(2);
else if (e.key === 'ArrowLeft') snake1.set_direction(3);
});
}

- stay
Snake.jsUpdate status in :
update() {
// Execute once per frame
if (this.status === 'move') {
this.uppdate_move()
}
this.render();
}

4.4 Achieve real mobility
stay snake.js Revision in China :
import {
AcGameObject } from "./AcGameObject";
import {
Cell } from "./Cell";
export class Snake extends AcGameObject {
constructor(info, gamemap) {
super();
this.id = info.id;
this.color = info.color;
this.gamemap = gamemap;
this.cells = [new Cell(info.r, info.c)]; // Store the snake's body ,cells[0] Store snakeheads
this.next_cell = null; // The next target location
this.speed = 5; // The snake walks every second 5 Lattice
this.direction = -1; // -1 Indicates that there is no instruction ,0、1、2、3 Indicates top right bottom left
this.status = "idle"; // idle Stand still ,move Indicates that you are moving ,die It means death
this.dr = [-1, 0, 1, 0]; // 4 Offset of rows in two directions
this.dc = [0, 1, 0, -1]; // 4 The offset of the direction column
this.step = 0; // Indicates the number of rounds
this.eps = 1e-2; // Allowable error
}
start() {
}
set_direction(d) {
this.direction = d;
}
next_step() {
// The state of the snake changes to the next step
const d = this.direction;
this.next_cell = new Cell(this.cells[0].r + this.dr[d], this.cells[0].c + this.dc[d]);
this.direction = -1;
this.status = "move";
this.step ++ ;
// Find the length
const k = this.cells.length;
for (let i = k; i > 0; i -- ) {
// The initial element remains unchanged Each element moves back one bit
this.cells[i] = JSON.parse(JSON.stringify(this.cells[i - 1]));
}
}
update_move() {
const dx = this.next_cell.x - this.cells[0].x;
const dy = this.next_cell.y - this.cells[0].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.eps) {
// We have reached the target point
this.cells[0] = this.next_cell; // Add a new snake head
this.next_cell = null;
this.status = "idle"; // It's over , stop
} else {
const move_distance = this.speed * this.timedelta / 1000;
this.cells[0].x += move_distance * dx / distance;
this.cells[0].y += move_distance * dy / distance;
}
}
update() {
// Execute once per frame
if (this.status === 'move') {
this.update_move();
}
this.render();
}
render() {
const L = this.gamemap.L;
const ctx = this.gamemap.ctx;
ctx.fillStyle = this.color;
for (const cell of this.cells) {
ctx.beginPath();
ctx.arc(cell.x * L, cell.y * L, L / 2, 0, Math.PI * 2);
ctx.fill();
}
}
}
4.5 Snake tail moves
- stay
Snake.jsAdd code to , Judge whether the snake tail grows .
check_tail_increasing() {
if (step <= 10) return true;
if (step % 3 === 1) return true;
return false;
}

- modify
Snake.js, Judge whether the snake tail is growing in the next step
this.next_cell = null; // The next target location
this.dr = [-1, 0, 1, 0]; // That's ok
this.dc = [0, 1, 0, -1]; // Column
this.step = 0;
this.eps = 1e-2 // Allowable error
next_step() {
const d = this.direction;
this.next_cell = new Cell(this.cells[0].r + this.dr[d], this.cells[0].c + this.dc[d]);
this.direction = -1;
this.status = "move";
this.step ++ ;
// Find the length
const k = this.cells.length;
for (let i = k; i > 0; i -- ) {
// The initial element remains unchanged Each element moves back one bit
this.cells[i] = JSON.parse(JSON.stringify(this.cells[i - 1]));
}
}
update_move() {
const dx = this.next_cell.x - this.cells[0].x;
const dy = this.next_cell.y - this.cells[0].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.eps) {
// We have reached the target point
this.cells[0] = this.next_cell; // Add a new snake head
this.next_cell = null;
this.status = "idle"; // It's over , stop
if (!this.check_tail_increasing()) {
// Snakes never grow .
this.cells.pop();
}
} else {
const move_distance = this.speed * this.timedelta / 1000;
this.cells[0].x += move_distance * dx / distance;
this.cells[0].y += move_distance * dy / distance;
if (!this.check_tail_increasing()) {
const k = this.cells.length;
const tail = this.cells[k - 1], tail_target = this.cells[k - 2];
const tail_dx = tail_target.x - tail.x;
const tail_dy = tail_target.y - tail.y;
tail.x += move_distance * tail_dx / distance;
tail.y += move_distance * tail_dy / distance;
}
}
}

- The basic effect

4.6 Beautify snakes
modify Snake.js , Let the snake become coherent 、 A little smaller . Add the following code :
render() {
const L = this.gamemap.L;
const ctx = this.gamemap.ctx;
ctx.fillStyle = this.color;
for (const cell of this.cells) {
ctx.beginPath();
ctx.arc(cell.x * L, cell.y * L, L / 2 * 0.8, 0, Math.PI * 2);
ctx.fill();
}
for (let i = 1; i < this.cells.length; i ++ ) {
const a = this.cells[i - 1], b = this.cells[i];
if (Math.abs(a.x - b.x) < this.eps && Math.abs(a.y - b.y) < this.eps)
continue;
if (Math.abs(a.x - b.x) < this.eps) {
ctx.fillRect((a.x - 0.4) * L, Math.min(a.y, b.y) * L, L * 0.8, Math.abs(a.y - b.y) * L);
} else {
ctx.fillRect(Math.min(a.x, b.x) * L, (a.y - 0.4) * L, Math.abs(a.x - b.x) * L, L * 0.8);
}
}
}
- The basic effect :

4.7 Detect illegal logic
- stay
GameMap.jsIn the update
check_valid(cell) {
// Check whether the target location is legal : Didn't hit the body and obstacles of the two snakes
for (const wall of this.walls) {
if (wall.r === cell.r && wall.c === cell.c)
return false;
}
for (const snake of this.snakes) {
let k = snake.cells.length;
if (!snake.check_tail_increasing()) {
// When the snake tail will move forward , Don't judge the snake tail
k -- ;
}
for (let i = 0; i < k; i ++ ) {
if (snake.cells[i].r === cell.r && snake.cells[i].c === cell.c)
return false;
}
}
return true;
}

- stay
snake.jsIn the update
next_step() {
if (!this.gamemap.check_valid(this.next_cell)) {
this.status = "die";
}
}
render() {
if (this.status === "die") {
ctx.fillStyle = "white";
}
}


- Final effect , The red snake hit the wall and died :

4.8 Achieve eye
- modify
snake.js
this.eye_direction = 0;
if (this.id === 1) this.eye_direction = 2;
this.eye_dx = [
[-1, 1];
[1, 1];
[1, -1];
[-1, -1];
];
this.eye_dy = [
[-1, -1];
[-1, 1];
[1, 1];
[1, -1];
];
next_step() {
this.eye_direction = d;
}
render() {
ctx.fillStyle = "black";
for (let i = 0; i < 2; i ++ ) {
const eye_x = (this.cells[0].x + this.eye_dx[this.eye_direction][i] * 0.15) * L;
const eye_y = (this.cells[0].y + this.eye_dy[this.eye_direction][i] * 0.15) * L;
ctx.beginPath();
ctx.arc(eye_x, eye_y, L * 0.05, 0, Math.PI * 2);
ctx.fill();
}
}


effect :

thus , We have achieved the most basic map and the movement of two snakes .
5 Code address
https://git.acwing.com/syy/kob/-/commit/2ee3e35f352dbc104e69491db86856fe5d510ad6
边栏推荐
- 树莓派自建 NAS 云盘之——数据自动备份
- Buckle practice - 27 score after turning the matrix
- The price of domestic flagship mobile phones is nearly 6000, but they can't even beat iphone12. It's clear who users choose
- regular
- Support liuhaiping
- Wechat applet generates QR code
- Summary of recent interviews
- [leetcode]- linked list-3
- SSM在线考试系统含文档
- How to mount NFS shares using autofs
猜你喜欢

Reserved instances & Savings Plans

【功能测试】项目的测试——登录和发布文章功能

6-16 vulnerability exploitation -rlogin maximum permission login

The price of domestic flagship mobile phones is nearly 6000, but they can't even beat iphone12. It's clear who users choose

Behind the rapid growth, Huawei cloud Wulanchabu data center is the green way

Cluster construction based on kubernetes v1.24.0 (I)

Vscode solves the problem of terminal Chinese garbled code

基于Kubernetes v1.24.0的集群搭建(一)
如何用WebGPU流畅渲染百万级2D物体?

Summary of recent interviews
随机推荐
Use abp Zero builds a third-party login module (4): wechat applet development
Wechat applet - drawing dashboard
SSM在线校园相册管理平台
Solutions to problems in IE6 browser
sql的where+or的用法丢失条件
Qt Creator怎样更改默认构建目录
The seventh topic of ape Anthropology
元宇宙更多的功能和作用在于对于传统生活方式和生产方式的深度改造
Video realizes the control of video progress, playback and pause
【Rust】引用和借用,字符串切片 (slice) 类型 (&str)——Rust语言基础12
Getting started with SQL join use examples to learn left connection, inner connection and self connection
Cluster construction based on kubernetes v1.24.0 (I)
[function test] test of the project - login and post function
Reserved instances & Savings Plans
生信识图 之 点图基础
It is difficult for Chinese consumers and industrial chains to leave apple, and iPhone has too much influence
Intent jump pass list set
Buckle practice - sum of 34 combinations
STM32 - Fundamentals of C language
More functions and functions of the metauniverse lie in the deep transformation of the traditional way of life and production