上班最强摸鱼游戏

您所在的位置:网站首页 qq联机小游戏 上班最强摸鱼游戏

上班最强摸鱼游戏

2024-07-09 23:05| 来源: 网络整理| 查看: 265

highlight: a11y-dark

在这里插入图片描述

多人在线射击游戏、最强摸鱼游戏 在想体验地址====>

github地址: 开发不易,多谢大哥大姐们点个start吧,点个小爱心吧

技术栈

canvas、socket 初始 Canvas 画布 const canvasRef = ref(); import { onMounted, ref } from 'vue'; // 获取可是窗大小 const { innerWidth, innerHeight } = window; onMounted(() => { // 赋值画布大小 canvasRef.value.width = innerWidth; canvasRef.value.height = innerHeight; }) .main-wrap { height: 100%; width: 100%; } 定义玩家的模型

工厂模式走起,每出现一个玩家就通过创建一个实例就行了

export class Player { public options: any; public ctx: any; constructor(ctx: any, options: any) { // canvas 2d实例 this.ctx = ctx; // 玩家属性 this.options = options; // 渲染玩家 this.render(); } /** * 渲染 */ render() { // 玩家渲染 } /** * 更新位置 */ update() { // 玩家更新位置 } }

这样就搞定了玩家模型了,接下来是定义子弹的模型

定义子弹的模型

同玩家模型一致

export class Bullet { public options: any public ctx: any constructor(ctx: any, options: any) { this.options = options; this.ctx = ctx; this.render(); } /** * 渲染 */ render() { // 子弹渲染 } /** * 更新位置 */ update() { // 子弹更新位置 } } 工具函数 结下来会用到 /** * 随机id * @params length {number} 长度 * @returns id {string} 随机id */ export const getRandomId = (length?: number) => { return (Math.random() + new Date().getTime()).toString(32).slice(0, length || 13); }; /** * 随机颜色 16进制 * @returns #cccccc */ export const getRandomColor = () => { return `#${random().toString(16)}${random().toString(16)}${random().toString(16)}`; }; /** * 获取0 - 256 的随机数 * @returns 随机数 */ const random = () => { return Math.floor(Math.random() * 256); }; 创建canvas 2d画布 let ctx: CanvasRenderingContext2D; ctx = canvasRef.value.getContext('2d'); // 设置背景颜色 ctx.fillStyle = '#ccc'; // 高宽 ctx.fillRect(0, 0, innerWidth, innerHeight); 创建玩家并渲染 // 所有玩家集合 const allPlayer = new Map(); /** * 创建玩家 */ const createPlayer = () => { // 判断是否存在 if (allPlayer.has(id)) return; const p = new Player(ctx, { // 唯一标识 id: getRandomId(10), // 随机出现的位置 x: Math.round(Math.random() * innerWidth), y: Math.round(Math.random() * innerHeight), // 初始大小 size: 20, // 随机玩家颜色 color: getRandomColor(), // 移动速度 speed: 20, // 可视窗高宽 innerWidth, innerHeight, // 玩家名字 text: 'A' }); allPlayer.set(p.options.id, p); }; console.log(allPlayer);

在这里插入图片描述

玩家搞定了,接下来就是渲染了

把Player类 的render完善一下

export class Player { ...... /** * 渲染 */ render() { const { x, y, size, color, text } = this.options; const { ctx } = this; // 开一个路径 ctx.beginPath(); // 画一个圆 ===> 为了简单,就已圆代替玩家 ctx.arc(x, y, size, 0, 2 * Math.PI, false); // 填充颜色 ctx.fillStyle = color; // 关闭该路径 ctx.fill(); // 设置玩家名称 if (text) { ctx.font = '20px Arial'; ctx.fillStyle = '#000'; ctx.textAlign = 'center'; ctx.fillText(text, x+size/2-10, y+size/2-4); } } ...... }

接下来就是执行render,在 new Player的过程中就有已经执行

export class Player { 。。。。 constructor(ctx: any, options: any) { 。。。。。 this.render(); } /** * 渲染 */ render() { // 玩家渲染 } }

看看效果,果然出现了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g1pfpc3a-1669717448818)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a98178f0d791418ca648025900585cf1~tplv-k3u1fbpfcp-watermark.image?)]

让玩家动起来

通过监听上下左右按键,分别执行不同的操作

keyCode方向37左38上39右40下 onMounted(() => { 。。。。。。 initOperate(); 。。。。。。 }); /** * 初始化操作监听 */ const initOperate = () => { // 键盘事件 只控制状态值 window.onkeydown = function (e: KeyboardEvent) { renderElements(e.keyCode); }; }; /** * 渲染所有的元素 子弹 玩家 。。。。 */ const renderElements = (keyCode?: number) => { // 清空画布 clearRect(); // 更新玩家 allPlayer.get(player?.options.id).update(keyCode); };

接下来实现Player中的update

export class Player { ...... /** * 更新位置信息 * @param keyCode 键盘码值 */ update(keyCode?: number) { const { x, y, speed } = this.options; // 通过keycode 改变xy的坐标信息 switch (keyCode) { case 37: this.options.x = x - speed; break; case 38: this.options.y = y - speed; break; case 39: this.options.x = x + speed; break; case 40: this.options.y = y + speed; break; } // 重新渲染 this.render(); } ...... }

看看效果 玩家已经动起来了 在这里插入图片描述

边缘计算 ,上下左右可视窗,不能超出 export class Player { ...... /** * 边缘计算 * @param keyCode */ verifyPosition(keyCode: number) { const { innerWidth, innerHeight, size, x, y, speed } = this.options; switch (keyCode) { case 37: return x - speed > size; case 38: return y - speed > size; case 39: return x + speed { 。。。。。。 // 玩家点击创子弹 window.onmousedown = function (e: MouseEvent) { createBullet(player as Player, e); }; }; /** * 创建 bullet */ const createBullet = (player: Player, e: MouseEvent) => { const { x, y } = player.options; // 返回原点到点的线段与x轴正方向之间的平面角度 const location = Math.atan2(e.clientY - y, e.clientX - x); const bullet = new Bullet(ctx, { id: getRandomId(), x, y, size: 5, color: 'red', speed: 1, location: { x: Math.cos(location) * 8, y: Math.sin(location) * 8 } }); allBullet.set(bullet.options.id, bullet); return bullet; }; location计算原理

Math.atan2 api方法计算二维坐标系中任意一个点(x, y)和原点(0, 0)的连线与X轴正半轴的夹角大小。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBUKBrQz-1669717448819)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/02ecf38c41134d34886b4bbddd9aca13~tplv-k3u1fbpfcp-watermark.image?)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2H5eAOYN-1669717448819)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eef99aa824fc4a63bc6cfbb82d3ee11d~tplv-k3u1fbpfcp-watermark.image?)]

然后根据cos sin 计算取余弦值、正弦值正数并且 *8(自定义) 得出移动的速度

子弹渲染render函数 export class Bullet { 。。。。。。 /** * 渲染 */ render() { const { x, y, size, color } = this.options; const { ctx } = this; ctx.beginPath(); ctx.arc(x, y, size, 0, 2 * Math.PI, false); ctx.fillStyle = color; ctx.fill(); } /** * 更新位置 */ update() { const { x, y, location } = this.options; this.options.x = x + location.x; this.options.y = y + location.y; this.render(); } }

看看效果

在这里插入图片描述

子弹是有了,但是怎么才能让他动起来了???

requestAnimationFrame 定时器重新渲染玩家和子弹

采用requestAnimationFrame的原因很简单,每一帧执行一次,60赫兹的话 那就是1000/60 = 16.66666 毫秒渲染一次

/** * 定时任务 */ const timingTask = () => { requestAnimationFrame(timingTask); if (!ctx || !canvasRef.value) return; // 清空画布 clearRect(); // 重新渲染 allPlayer.forEach((pl: Player) => { pl.render(); }); // 遍历子弹 allBullet.forEach((item: Bullet) => { const { x, y } = item.options; // 边缘判断 出边界线外删除 if (x >= innerWidth || x = innerHeight || y


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3