20260419
终于实现联机逻辑了,虽然代码很烂(非常烂)而且只有简单的移动逻辑并且bug也很多,但后续我还会修复和补充完整的。
第一次看到其他设备上的小方块会跟随本机上的小方块移动时真的太激动了,这一刻感觉自己无所不能,这是使用ai写得不到的成就感
下面依旧是代码环节:
这是前端vue中的一个组件:RectangleMove.vue
1 <template> 2 <div class="CanvasContainer"> 3 <canvas width="1000" height="800" ref="backgroundCanvas"></canvas> 4 </div> 5 </template> 6 7 <script setup> 8 import axios from "axios" 9 import { onMounted, ref } from "vue" 10 11 const backgroundCanvas = ref(null) 12 let ctx = null 13 let ws = null 14 //我的名字 15 let myName = Date.now() 16 //玩家集合 17 const players = [] 18 //子弹集合 19 const bullets = [] 20 //按键字典 21 const keys = { 22 w: false, 23 a: false, 24 s: false, 25 d: false, 26 mouseX: 0, 27 mouseY: 0, 28 mouseDown: false, 29 mouseLatestDown: 0 30 } 31 //按下按键 32 function keyDown(e) { 33 if (keys.hasOwnProperty(e.key)) { 34 keys[e.key] = true 35 } 36 } 37 //松开按键 38 function keyUp(e) { 39 if (keys.hasOwnProperty(e.key)) { 40 keys[e.key] = false 41 } 42 } 43 //鼠标移动 44 function mouseMove(e) { 45 keys.mouseX = e.clientX - backgroundCanvas.value.getBoundingClientRect().left; 46 keys.mouseY = e.clientY - backgroundCanvas.value.getBoundingClientRect().top; 47 } 48 //鼠标按下 49 function mouseDown(e) { 50 const ts = Date.now() 51 if (ts - keys.mouseLatestDown >= 1000) { 52 //新增子弹 53 const me = players.find(player => player.name == myName) 54 55 //计算方向 56 const dx = keys.mouseX - me.x 57 const dy = keys.mouseY - me.y 58 const dist = Math.sqrt(dx * dx + dy * dy) 59 const speed = 5 60 61 //保证所有方向速度一致 62 const vx = (dx / dist) * speed 63 const vy = (dy / dist) * speed 64 65 bullets.push(new Bullet(me.x, me.y, 10, 10, "green", vx, vy, null, myName)) 66 67 keys.mouseDown = false 68 keys.mouseLatestDown = Date.now() 69 } 70 71 } 72 //画鼠标准心 73 function drawMouse() { 74 const cx = keys.mouseX 75 const cy = keys.mouseY 76 const len = 8 77 const gap = 3 78 79 ctx.strokeStyle = '#fff' 80 ctx.lineWidth = 2 81 82 ctx.beginPath() 83 ctx.moveTo(cx, cy - gap - len) 84 ctx.lineTo(cx, cy - gap) 85 ctx.moveTo(cx, cy + gap) 86 ctx.lineTo(cx, cy + gap + len) 87 88 ctx.moveTo(cx - gap - len, cy) 89 ctx.lineTo(cx - gap, cy) 90 ctx.moveTo(cx + gap, cy) 91 ctx.lineTo(cx + gap + len, cy) 92 ctx.stroke() 93 } 94 95 //更新位置 96 function updatedPosition() { 97 //自己移动 98 players.forEach(player => { 99 if (player.name == myName) { 100 if (keys.w || keys.a || keys.s || keys.d) { 101 if (keys.w) player.y -= player.ySpeed 102 if (keys.s) player.y += player.ySpeed 103 if (keys.a) player.x -= player.xSpeed 104 if (keys.d) player.x += player.xSpeed 105 //发送自己新位置 106 ws.send(JSON.stringify({ type: "update", player: player })) 107 } 108 } 109 }) 110 //子弹移动 111 bullets.forEach((bullet, index) => { 112 bullet.x += bullet.xSpeed 113 bullet.y += bullet.ySpeed 114 if (bullet.x < 0 || bullet.x > backgroundCanvas.value.width || bullet.y < 0 || bullet.y > backgroundCanvas.value.height) { 115 bullets.splice(index, 1) 116 console.log("已移除子弹") 117 } 118 }) 119 //ws 120 } 121 //更新事件 122 function updateEvent() { 123 124 } 125 //画 126 function draw() { 127 //清空画布 128 ctx.clearRect(0, 0, backgroundCanvas.value.width, backgroundCanvas.value.height) 129 130 players.forEach(player => { 131 player.draw() 132 }) 133 134 bullets.forEach(bullet => { 135 bullet.draw() 136 }) 137 drawMouse() 138 } 139 //动画 140 function animate() { 141 updatedPosition() 142 updateEvent() 143 draw() 144 requestAnimationFrame(animate) 145 } 146 //初始化 147 onMounted(() => { 148 const canvas = backgroundCanvas.value 149 ctx = canvas.getContext("2d") 150 151 ws = new WebSocket("ws://192.168.174.97:5000") 152 153 ws.onopen = () => { 154 const player1 = new Player(0, 0, 60, 60, "red", 2, 2, myName) 155 players.push(player1) 156 ws.send(JSON.stringify({ type: "join", player: player1 })) 157 //绑定按键事件 158 window.addEventListener('keydown', keyDown) 159 window.addEventListener('keyup', keyUp) 160 window.addEventListener('mousemove', mouseMove) 161 window.addEventListener('mousedown', mouseDown) 162 //启动 163 animate() 164 } 165 166 ws.onmessage = (res) =>{ 167 const data = JSON.parse(res.data) 168 const type = data.type 169 const player = new Player(data.player.x,data.player.y,data.player.width,data.player.height,data.player.color,data.player.xSpeed,data.player.ySpeed,data.player.name) 170 if(type == "join"){ 171 const index = players.findIndex(p => p.name == player.name ) 172 if(index!=-1){ 173 players[index] = player 174 }else{ 175 players.push(player) 176 } 177 }else if(type == "update"){ 178 const index = players.findIndex(p => p.name == player.name ) 179 if(index!=-1){ 180 players[index] = player 181 }else{ 182 players.push(player) 183 } 184 } 185 } 186 187 }) 188 //玩家类 189 class Player { 190 constructor(x, y, width, height, color, xSpeed, ySpeed, name) { 191 this.x = x 192 this.y = y 193 this.width = width 194 this.height = height 195 this.color = color 196 this.xSpeed = xSpeed 197 this.ySpeed = ySpeed 198 this.name = name 199 } 200 201 draw() { 202 ctx.fillStyle = this.color 203 ctx.fillRect(this.x - this.width / 2, this.y - this.height / 2, this.width, this.height) 204 } 205 206 } 207 //子弹类 208 class Bullet { 209 constructor(x, y, width, height, color, xSpeed, ySpeed, angle, from) { 210 this.x = x 211 this.y = y 212 this.width = width 213 this.height = height 214 this.color = color 215 this.xSpeed = xSpeed 216 this.ySpeed = ySpeed 217 this.angle = angle 218 this.from = from 219 } 220 221 draw() { 222 ctx.fillStyle = this.color 223 ctx.fillRect(this.x - this.width / 2, this.y - this.height / 2, this.width, this.height) 224 } 225 } 226 </script> 227 228 <style scoped> 229 .CanvasContainer { 230 display: flex; 231 width: 100vw; 232 height: 100vh; 233 background-color: #000; 234 align-items: center; 235 justify-content: center; 236 } 237 238 canvas { 239 background-color: #000333; 240 border: red solid 2px; 241 cursor: none; 242 } 243 </style>
这是node.js后端:app.js
1 const express = require('express'); 2 const WebSocket = require('ws') 3 const cors = require('cors'); 4 const app = express(); 5 6 app.use(express.json()); 7 app.use(cors()); 8 9 const server = app.listen(5000, () => { 10 console.log(`服务运行在 http://localhost:5000`); 11 }); 12 13 const wss = new WebSocket.Server({server}) 14 15 let players = [] 16 let bullets = [] 17 18 wss.on('connection', ws => { 19 console.log("新玩家加入游戏") 20 21 ws.on('message',res => { 22 const data = JSON.parse(res); 23 const player = data.player 24 const type = data.type 25 //判断类型后操作 26 if(type == "join"){ 27 //新添玩家 28 players.push(player) 29 }else if(type == "update"){ 30 //寻找符合条件的玩家 31 const index = players.findIndex(p => p.name == player.name) 32 if(index != -1) { 33 //更新玩家 34 players[index] = player 35 } 36 } 37 //广播玩家信息 38 if(type =="join"||type == "update"){ 39 wss.clients.forEach(client => { 40 if(client.readyState == WebSocket.OPEN) { 41 if(type == "join"){ 42 client.send(JSON.stringify({type:"join",player:player})) 43 }else if(type == "update"){ 44 client.send(JSON.stringify({type:"update",player:player})) 45 } 46 } 47 }) 48 } 49 }) 50 })
接下来的几天也再接再厉

浙公网安备 33010602011771号