一款多人,pvp, pve在线平台
“Springboot实战项目”
1.创建公共组件AcGameObject.js 用作前端所有对象的基类,包括start(), update(), 每秒更新60帧 具体代码如下:
const AC_GAME_OBJECT = [];export class AcGameobject { constructor ( ) { AC_GAME_OBJECT .push (this ); this .timedelta = 0 ; this .has_called_satrt = false ; } start ( ) { } update ( ) { } on_destroy ( ) { } destroy ( ) { this .on_destroy (); for (let i in AC_GAME_OBJECT ) { const obj = AC_GAME_OBJECT [i]; if (obj == this ) { AC_GAME_OBJECT .splice (i); break ; } } } } let last_timestamp; const step = timestamp => { for (let obj of AC_GAME_OBJECT ) { if (!obj.has_called_satrt ) { obj.has_called_satrt = true ; obj.start (); } else { obj.timedelta = timestamp - last_timestamp; obj.update (); } } last_timestamp = timestamp; requestAnimationFrame (step) } requestAnimationFrame (step)
2.创建PlayGround.vue用于显示地图,在PkindexView.vue中引用(入口): PkindexView.vue代码如下:
<template> <PlayGround /> </template> <script > import PlayGround from '../../components/PlayGround.vue' export default { components : { PlayGround } } </script > <style scoped > </style >
PlayGround.vue代码如下:
<template> <div class="playground"> <GameMap/> </div> </template> <script> import GameMap from "./GameMap.vue"; export default { components: { GameMap, } } </script> <style scope> div.playground { width: 60vw; height: 70vh; margin: 40px auto; } </style>
2.1在PlayGround中还需要显示地图的区域,PlayGround为map的基层: Map.vue代码如下:
<template> <div ref="parent" class="gamemap"> <canvas ref="canvas"> </canvas> </div> </template> <script> import { GameMap } from "../assets/scripts/GameMap" import { ref, onMounted } from 'vue' export default { setup() { let parent = ref(null); let canvas = ref(null); onMounted(() => { new GameMap(canvas.value.getContext('2d'), parent.value); }) return { parent, canvas } } } </script> <style scoped> div.gamemap { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; } </style>
2.2由上代码可知需要一个对象逻辑GameMap.js来产生地图,以及根据页面大小时时根据canvas的大小来调整正方形地图的大小代码如下: import { AcGameobject } from "./AcGameObject" ;import { Wall } from "./Wall" ;export class GameMap extends AcGameobject { constructor (ctx, parent ) { super (); this .ctx = ctx; this .parent = parent; this .L = 0 ; this .rows = 13 ; this .cols = 13 ; this .inner_walls_count = 20 ; this .walls = []; } check_connectivity (g, sx, sy, tx, ty ) { if (sx == tx && sy == ty) return true ; g[sx][sy] = true ; let dx = [-1 , 0 , 1 ,0 ], dy = [0 , 1 , 0 , -1 ]; for (let i = 0 ; i < 4 ; i++) { let x = sx + dx[i], y = sy + dy[i]; if (!g[x][y] && this .check_connectivity (g, x, y, tx, ty)) { return true ; } } return false ; } create_walls ( ) { const 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++) { for (let c = 0 ; c < this .cols ; c++) { g[r][0 ] = g[r][this .cols - 1 ] = true ; 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 (c == 1 && r == this .rows - 2 || r == 1 && c == this .cols - 2 ) continue ; if (g[r][c] || g[c][r]) continue ; g[r][c] = g[c][r] = true ; break ; } } const copy_g = JSON .parse (JSON .stringify (g)); if (!this .check_connectivity (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 < 1000 ; 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 ( ) { const color_even = "rgb(162, 209, 73)" , color_odd = "rgb(170, 214, 81)" ; for (let r = 0 ; r < this .rows ; r++) { for (let c = 0 ; c < this .cols ; c++) { if ((r + c) % 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 ); } } } }
2.3以上代码中产生了地图,墙,墙后渲染所以覆盖了地图,除此以外还加入了中间的障碍物,为了保证游戏公平性障碍物以对角线对称,并且每秒刷新60词,地图保证连通性通过check_connectivity()中的八皇后算法,中间障碍物需要一个Wall.js逻辑来渲染产生,生成墙函数create_wall()在satart()中调用,通过连同性函数检查一千次,如果超过一千次则无法产生地图。 wall.js代码如下:
import { AcGameobject } from "./AcGameObject" ;export class Wall extends AcGameobject { constructor (r, c, gamemap ) { super (); this .r = r; this .c = c; this .gamemap = gamemap; this .color = "rgb(185, 119, 43)" ; } 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); } }