20260509
歪日,早知道一帧就只发一个大包了,现在小包满天飞...
目前依旧是半成品,前端就写了个大概,后端也只写个大概,目前还不能控制移动...
不过也学到了挺多,比如渲染模块单独写到渲染器里,能让代码耦合度更低
依旧展示进度
后端
app.js
import Player from './entity/PLayer.js'
import Bullet from './entity/Bullet.js';
import express from 'express';
import {WebSocketServer} from 'ws';
import cors from 'cors';
const app = express();
app.use(express.json());
app.use(cors());
const server = app.listen(5000, () => {
console.log('服务器运行在 http://localhost:5000');
});
const wss = new WebSocketServer({server});
let players = []
let bullets = []
let config = {
FPS: 1000 / 60,
shootGap: 1000,
}
wss.on('connection', ws => {
console.log('有玩家连接服务器');
ws.on('message', message => {
const msg = JSON.parse(message);
const type = msg.type;
const data = msg.data;
const tsp = Date.now();
let index;
//接受玩家消息
switch (type) {
//玩家加入
case 'pJoin':
players.push(new Player(data))
//这里有点屎了,为了解决新玩家不能同步老玩家的问题:一有玩家加入就给所有玩家广播现已有全部玩家
wss.clients.forEach((client) => {
players.forEach(player => {
client.send(JSON.stringify({type:"pJoin",data:player}))
})
})
console.log(data.name + " 加入游戏")
break;
//玩家移动
case 'pMove':
index = players.findIndex(player => player.id === data.id)
if (index > -1) {
players[index].up = data.up;
players[index].down = data.down;
players[index].left = data.left;
players[index].right = data.right;
}
break;
//玩家射击
case 'pShoot':
index = players.findIndex(player => player.id === data.id)
if ((index > -1) && (tsp - players[index].latestShoot > config.shootGap)) {
//新建子弹
bullets.push(new Bullet({
id: bullets.length,
from: players[index].name,
color: players[index].color,
at: players[index].at,
x: players[index].x,
y: players[index].y,
w: 10,
h: 10,
a: 0,
v: 5,
angle: players[index].angle
}));
}
break;
//玩家死亡
case 'pDelete':
index = players.findIndex(player => player.id === data.id)
if (index > -1) {
players.splice(index, 1)
console.log(data.name + " 离开游戏")
}
break;
}
})
})
//广播定时器
setInterval(() => {
players.forEach(player => {
//更新玩家位置
player.playerMove();
//广播玩家位置
wss.clients.forEach(client => {
client.send(JSON.stringify({type: "pPosition", data: player}));
})
})
bullets.forEach(bullet => {
//更新子弹位置
bullet.bulletMove();
//广播子弹位置
wss.clients.forEach(client => {
client.send(JSON.stringify({type: "bPosition", data: bullet}));
})
})
//碰撞检测
}, config.FPS)
entity/Player.js
//玩家类
class Player {
constructor({
name,
id,
color,
maxHp,
hp,
at,
latestShoot,
latestHurt,
x,
y,
w,
h,
a,
v,
up,
down,
left,
right,
angle
}) {
this.name = name;
this.id = id;
this.color = color;
this.maxHp = maxHp;
this.hp = hp;
this.at = at;
this.latestShoot = latestShoot;
this.latestHurt = latestHurt;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.a = a;
this.v = v;
this.up = up;
this.down = down;
this.left = left;
this.right = right;
this.angle = angle;
}
}
export default Player;
entity/Bullet.js
//子弹类
class Bullet {
constructor({ id, from, color, at, x, y, w, h, a, v, angle }) {
this.id = id;
this.from = from;
this.color = color;
this.at = at;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.a = a;
this.v = v;
this.angle = angle;
}
}
export default Bullet;
前端
RectangleMove.vue
<template>
<div class="CanvasContainer">
<canvas width="1000" height="800" ref="backgroundCanvas"></canvas>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue"
import Player from "../entity/Player.js"
import Bullet from "../entity/Bullet.js"
import { drawPlayer, drawBullet } from "../renderer/renderer.js"
const backgroundCanvas = ref(null)
let ctx = null
let ws = null
//我的名字
let myName = Date.now()
//玩家集合
const players = []
//子弹集合
const bullets = []
//按键字典
const keys = {
w: false,
a: false,
s: false,
d: false,
mouseX: 0,
mouseY: 0,
mouseDown: false,
mouseLatestDown: 0
}
//按下按键
function keyUp() {
}
//松开按键
function keyDown() {
}
//鼠标按下
function mouseDown(e) {
}
//鼠标移动
function mouseMove(e) {
keys.mouseX = e.clientX - backgroundCanvas.value.getBoundingClientRect().left;
keys.mouseY = e.clientY - backgroundCanvas.value.getBoundingClientRect().top;
}
//画鼠标准心
function drawMouse() {
const cx = keys.mouseX
const cy = keys.mouseY
const len = 8
const gap = 3
ctx.strokeStyle = '#fff'
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(cx, cy - gap - len)
ctx.lineTo(cx, cy - gap)
ctx.moveTo(cx, cy + gap)
ctx.lineTo(cx, cy + gap + len)
ctx.moveTo(cx - gap - len, cy)
ctx.lineTo(cx - gap, cy)
ctx.moveTo(cx + gap, cy)
ctx.lineTo(cx + gap + len, cy)
ctx.stroke()
}
//画
function draw() {
//清空画布
ctx.clearRect(0, 0, backgroundCanvas.value.width, backgroundCanvas.value.height)
players.forEach(player => {
drawPlayer(ctx, player)
})
bullets.forEach(bullet => {
drawBullet(ctx, bullet)
})
drawMouse()
}
//动画
function animate() {
draw()
requestAnimationFrame(animate)
}
//初始化
onMounted(() => {
myName = prompt("输入你的昵称")
const canvas = backgroundCanvas.value
ctx = canvas.getContext("2d")
ws = new WebSocket("ws://10.99.121.4:5000")
ws.onopen = () => {
let x = Math.random() * canvas.width
let y = Math.random() * canvas.height
const player1 = new Player({ name: myName, id: Date.now(), color: "red", maxHp: 100, hp: 100, at: 10, latestShoot: 0, latestHurt: 0, x: x, y: y, w: 60, h: 60, a: 0, v: 2, up: false, down: false, left: false, right: false, angle: 0 })
players.push(player1)
ws.send(JSON.stringify({ type: "pJoin", data: player1 }))
console.log(player1)
//绑定按键事件
window.addEventListener('keydown', keyDown)
window.addEventListener('keyup', keyUp)
window.addEventListener('mousemove', mouseMove)
window.addEventListener('mousedown', mouseDown)
//启动
animate()
}
ws.onmessage = (message) => {
const msg = JSON.parse(message.data)
const type = msg.type
const data = msg.data
if (type === "pJoin") {
if (data.name === myName)
return
players.push(data)
console.log(data.name + "加入了游戏")
} else if (type === "pDelete") {
const index = players.findIndex(p => p.id === data.id)
if (index !== -1) {
players.splice(index, 1)
console.log(data.name + "离开了游戏")
}
} else if (type === "pPosition") {
const index = players.findIndex(p => p.id === data.id)
if (index !== -1) {
players[index] = data
}
} else if (type === "pShoot") {
bullets.push(data)
console.log(data.from + "发射了一颗子弹")
}
}
})
</script>
<style scoped>
.CanvasContainer {
display: flex;
width: 100vw;
height: 100vh;
background-color: #000;
align-items: center;
justify-content: center;
}
canvas {
background-color: #000333;
border: red solid 2px;
cursor: none;
}
</style>
Player.js和Bullet.js内只有和后端一样属性,没函数,就不写了
renderer.js
//渲染器
export const drawPlayer = (ctx, player) => {
//画玩家
ctx.fillStyle = player.color;
ctx.fillRect(player.x - player.w / 2, player.y - player.h / 2, player.w, player.h);
//画血条
const barWidth = player.w;
const barHeight = 4;
const barY = player.y - player.h / 2 - 10;
ctx.fillStyle = "#ff3333";
ctx.fillRect(player.x - barWidth / 2, barY, barWidth, barHeight);
const currentWidth = (player.hp / player.maxHp) * barWidth;
ctx.fillStyle = "#33ff33";
ctx.fillRect(player.x - barWidth / 2, barY, currentWidth, barHeight);
//画名字
ctx.fillStyle = "#fff";
ctx.font = "14px Arial";
ctx.textAlign = "center";
ctx.fillText(player.name, player.x, player.y + player.h / 2 + 5);
};
export const drawBullet = (ctx, bullet) => {
ctx.fillStyle = bullet.color;
ctx.fillRect(bullet.x - bullet.w / 2, bullet.y - bullet.h / 2, bullet.w, bullet.h);
};

浙公网安备 33010602011771号