编写NavBar.vue

导入bootstrapcss js 文件

1
2
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap/dist/js/bootstrap"

提示缺少@popperjs/core就去安装对应依赖

NavBar.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<template>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container">
<a class="navbar-brand">King of Bots</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link">对战</a>
</li>
<li class="nav-item">
<a class="nav-link">对局列表</a>
</li>
<li class="nav-item">
<a class="nav-link">排行榜</a>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
aria-expanded="false">
yanxuecan
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">My Bot</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li><a class="dropdown-item" href="#">Log Out</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
</template>


<script>
export default {
name: "NavBar",
setup() {

}
}
</script>

<style scoped></style>

编写router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { createRouter, createWebHistory } from 'vue-router'
import PkIndexView from '../views/pk/PkIndexView'
import RankListIndexView from '../views/ranklist/RankListIndexView'
import RecordIndexView from '../views/record/RecordIndexView'
import UserBotIndexView from '../views/user/bot/UserBotIndexView'
import NotFoundView from '../views/error/NotFoundView'


const routes = [
{
path: '/',
name: 'home',
redirect: "/pk/",
},
{
path: '/pk/',
name: 'pk_index',
component: PkIndexView,
},
{
path: '/ranklist/',
name: 'ranklist_index',
component: RankListIndexView,
},
{
path: '/record/',
name: 'record_index',
component: RecordIndexView,
},
{
path: '/user/bot/',
name: 'user_bot_index',
component: UserBotIndexView,
},
{
path: '/404/',
name: '404',
component: NotFoundView,
},
{
path: "/:catchAll(.*)",
redirect: "/404/"
}
]

const router = createRouter({
history: createWebHistory(),
routes
})

export default router

修改导航栏中的a标签

  • 将a标签改成router-link
  • 添加:to="{name: 'home'}"使得能够跳转在不刷新页面的情况下

编写ContentField.vue公共组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div class="container">
<div class="card">
<div class="card-body">
<slot></slot>
</div>
</div>
</div>
</template>


<script>
export default {
name: "ContentField",
}
</script>


<style scoped>
.card {
margin-top: 20px;
}
</style>

在其他的views中修改

其中一个示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<ContentField>
对战
</ContentField>
</template>

<script>
import ContentField from '../../components/ContentField'

export default {
components: {
ContentField,
}
}
</script>

<style scoped>

</style>

实时聚焦

两个api

  • route
  • computed

在标签中的class改为

1
:class="route_name == 'pk_index' ? 'nav-link active' : 'nav-link'"

造游戏轮子AcGameObjects

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
const AC_GAME_OBJECTS = [];

export class AcGameObjects {
constructor() {
AC_GAME_OBJECTS.push(this);
this.timedelta = 0;
this.has_called_start = false;
}

start() {

}

update() {

}

on_destroy() {

}

destroy() {
this.on_destroy();

for (i in AC_GAME_OBJECTS) {
const obj = AC_GAME_OBJECTS[i];
if (this === obj) {
AC_GAME_OBJECTS.splice(i);
break;
}
}
}
}

let last_timestamp;
const step = (timestamp) => {
for (obj of AC_GAME_OBJECTS) {
if (!obj.has_called_start) {
obj.has_called_start = true;
obj.start();
} else {
obj.timedelta = timestamp - last_timestamp;
obj.update();
}
}

last_timestamp = timestamp;
requestAnimationFrame(step);
}

requestAnimationFrame();

1:08:00

编写GameMap.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { AcGameObjects } from "./AcGameObjects"
import { Wall } from "./Wall";

export class GameMap extends AcGameObjects {
constructor(ctx, parent) {
super();

this.ctx = ctx;
this.parent = parent;
this.L = 0;

this.rows = 13;
this.cols = 13;

this.walls = [];
this.inner_walls_count = 20;
}

check_conectivity(g, sx, sy, ex, ey) {
if (sx == ex && sy == ey)
return true;

g[sx][sy] = true;

let dx = [1, 0, -1, 0];
let dy = [0, 1, 0, -1];

for (let i = 0; i < 4; i++) {
let x = dx[i] + sx, y = dy[i] + sy;
if (!g[x][y] && this.check_conectivity(g, x, y, ex, ey)) {
return true;
}
}

return false;
}

create_walls() {
let g = [];
for (let r = 0; r < this.rows; r++) {
g[r] = [];
for (let c = 0; c < this.cols; c++) {
g[r][c] = false;
}
}

for (let r = 0; r < this.rows; r++) {
g[r][0] = g[r][this.cols - 1] = true;
}

for (let c = 0; c < this.cols; c++) {
g[0][c] = g[this.rows - 1][c] = true;
}

for (let i = 0; i < this.inner_walls_count / 2; i++) {
for (let j = 0; j < 1000; j++) {
let r = parseInt(Math.random() * this.rows);
let c = parseInt(Math.random() * this.cols);
if (g[r][c]) {
continue;
}
if (r == 1 && c == this.cols - 2 || r == this.rows - 2 && c == 1) {
continue;
}
g[r][c] = g[c][r] = true;
break;
}
}

const copy_g = JSON.parse(JSON.stringify(g)); //拷贝
if (!this.check_conectivity(copy_g, this.rows - 2, 1, 1, this.cols - 2))
return false;

for (let r = 0; r < this.rows; r++) {
for (let c = 0; c < this.cols; c++) {
if (g[r][c]) {
this.walls.push(new Wall(r, c, this));
}
}
}

return true;
}

start() {
for (let i = 0; i < 100; i++) {
if (this.create_walls())
break;
}
}

update_size() {
this.L = parseInt(Math.min(this.parent.clientWidth / this.cols, this.parent.clientHeight / this.rows));
this.ctx.canvas.width = this.L * this.cols;
this.ctx.canvas.height = this.L * this.rows;
}

update() {
this.update_size();
this.render();
}

render() {
let color_even = "#AAD781", color_odd = "#A2D149";
for (let r = 0; r < this.rows; r++) {
for (let c = 0; c < this.cols; c++) {
if ((c + r) % 2 == 0) {
this.ctx.fillStyle = color_even;
} else {
this.ctx.fillStyle = color_odd;
}

this.ctx.fillRect(c * this.L, r * this.L, this.L, this.L);
}
}
}
}


1:31:00

编写Wall.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { AcGameObjects } from "./AcGameObjects";

export class Wall extends AcGameObjects {
constructor(r, c, gamemap) {
super();

this.r = r;
this.c = c;
this.color = "#b37226";
this.gamemap = gamemap;
}

start() {

}

update() {
this.render()
}

render() {
const L = this.gamemap.L; //动态取
const ctx = this.gamemap.ctx;

ctx.fillStyle = this.color;
ctx.fillRect(this.c * L, this.r * L, L, L);
}
}


提交代码