上班最强摸鱼游戏-多人联机小游戏 (一)
highlight: a11y-dark
多人在线射击游戏、最强摸鱼游戏
在想体验地址====>
github地址:
开发不易,多谢大哥大姐们点个start吧,点个小爱心吧
技术栈
- canvas、socket
初始 Canvas
画布
<template><div class="main-wrap" id="main-wrap"><canvas class="canvas" ref="canvasRef" id="canvas"></canvas></div>
</template><script setup lang="ts">
const canvasRef = ref();
import { onMounted, ref } from 'vue';
// 获取可是窗大小
const { innerWidth, innerHeight } = window;
onMounted(() => {// 赋值画布大小canvasRef.value.width = innerWidth;canvasRef.value.height = innerHeight;
})
</script><style lang="scss" scoped>
.main-wrap {height: 100%;width: 100%;
}
</style>
定义玩家的模型
工厂模式走起,每出现一个玩家就通过创建一个实例就行了
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: anypublic ctx: anyconstructor(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();}......
}
看看效果 玩家已经动起来了
边缘计算 ,上下左右可视窗,不能超出
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 < innerWidth;case 40:return y + speed < innerHeight;default:return false;}}......
}
那在什么时候调用呢,在执行update之前就得判断是否移动外面
/*** 初始化操作监听*/
const initOperate = () => {// 键盘事件 只控制状态值window.onkeydown = function (e: KeyboardEvent) {if (player?.verifyPosition(e.keyCode)) {renderElements(e.keyCode);}};
};
搞定
玩家的移动算是搞定了
子弹
鼠标点击的方向就是子弹出来的地方
那首先就得监听鼠标点击的位置
/*** 初始化操作监听*/
const initOperate = () => {。。。。。。// 玩家点击创子弹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轴正半轴的夹角大小。
然后根据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 <= 0 || y >= innerHeight || y <= 0) {allBullet.delete(item.options.id);}item.update();});
};
timingTask();
看看效果怎么样
完美!!!!
下个帖子重点聊联机
上班最强摸鱼游戏-多人联机小游戏 (一)相关推荐
- 最强摸鱼神器:开着IDEA看股票,看小说...
这周很多公司都开始复工了,不出意外下周应该大部分都要开始上班了吧.今天TJ冒着被各公司老板追杀的风险,给大家推荐一个上班摸鱼神器:Thief-Book. **项目名称:**Thief Book **项 ...
- 【转】多人联机射击游戏中的设计模式应用(一)
为了方便大家更加系统地学习和掌握各种常用的设计模式,下面通过一个综合实例--"多人联机射击游戏"来学习如何在实际开发中综合使用设计模式. 反恐精英(Counter-Strike, ...
- 不用 H5,闲鱼 Flutter 如何玩转小游戏?
简介: 最近APP游戏化成为了一个新的风口,把在游戏中一些好玩的.能吸引用户的娱乐方式或场景应用在应用当中,以达到增加用户粘性,提升DAU的效果,成本较低.同时在一些需要对用户有引导性的场景,游戏化还 ...
- flutter能开发游戏吗_不用 H5,闲鱼 Flutter 如何玩转小游戏?-阿里云开发者社区...
什么是Candy引擎? Candy 是闲鱼技术团队设计开发的一款引擎: APP嵌入式的.轻量级的.易于开发.性能稳定的互动引擎: 绘制系统高度融合Flutter体系,游戏场景和Flutter UI支持 ...
- Unity Networking开发多人联机射击游戏
UNet开发多人联机射击游戏 引言: Networking作为Unity官方的用于开发多人在线游戏的网络模块,开发者可以不用自己搭建网络模块的底层,通过使用Unity提供的一些相关组件,可以轻松实现简 ...
- 技术人玩小游戏,如何“不战而胜”
虽然迟了一天,但还是祝各位小伙伴端午安康. 最近因为端午节到来,物业举办了一个网页小游戏,得分最高的前 N 名可以拿到奖品. 闲来无事的我参加了一下,发现自己实在是太菜了,总是玩不过别人,于是转变思路 ...
- 微信火柴人html5小游戏,20个好玩的微信小游戏推荐!你玩过几个?
50000+游戏爱好者已加入我们! 每天推荐好玩游戏! 加入我们,沐沐带你发现好游戏! 只有你想不到, 没有我找不到的好游戏! 「良心好游戏推荐」 搜罗了好玩的微信小游戏大全, 模拟经营游戏.恐怖游戏 ...
- 多人联机射击游戏中的设计模式应用
转:http://blog.csdn.net/lovelion/article/details/8262987 反恐精英(Counter-Strike, CS).三角洲部队.战地等多人联机射击游戏广受 ...
- 多人联机射击游戏中的设计模式应用(一)
为了方便大家更加系统地学习和掌握各种常用的设计模式,下面通过一个综合实例--"多人联机射击游戏"来学习如何在实际开发中综合使用设计模式. 反恐精英(Counter-Strike, ...
最新文章
- 使用Redis分区将数据分割到多个Redis实例
- android 属性动画 弧形,CSS分层动画可以让元素沿弧形路径运动
- Django之MVC框架与MTV框架详解
- c语言编译器_学C语言写自己的K语言:编译器词法分析。
- java语言程序设计全国考试题,2019年12月全国计算机等级考试[Java语言程序设计]复习题及答案...
- 【EXLIBRIS】二十唯识白话译本【ZZ】
- Codeforces Round #556 (Div. 1)
- mysql group set,Mysql--group_concat()、group by、find_in_set()使用笔记
- RHEL 5 rpm包安装bind
- 双线性插值算法实现和opencv、matlab结果不一致问题
- uniapp引入阿里巴巴矢量图标库
- unity 控制人物模型移动
- 尺度、空间异质性、干扰、景观多样性、景观连接度,对其概念的理解
- 用手机打开word图表位置很乱_干货 | 论文格式调半天?Word攻略帮你统统都搞定...
- 画出属于你的最漂亮的数字时序图—WaveDrom
- 电脑计算机为什么不是有效程序,电脑提示“不是有效的win32应用程序”是什么原因【解决方法】...
- 计算机组成原理例题4.2,4.2.2 例题解析(1)
- java接口自动化监控_java接口自动化(三) - 手工接口测试到自动化框架设计之鸟枪换炮...
- 关于insight数据库价格与价值的双重选择
- 基于JSP的网上书城