上班最强摸鱼游戏 |
您所在的位置:网站首页 › qq联机小游戏 › 上班最强摸鱼游戏 |
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() { // 玩家渲染 } }看看效果,果然出现了 通过监听上下左右按键,分别执行不同的操作 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(); } ...... }看看效果 玩家已经动起来了 Math.atan2 api方法计算二维坐标系中任意一个点(x, y)和原点(0, 0)的连线与X轴正半轴的夹角大小。 然后根据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 |