js_拳皇(上)
文章目录
- 架构设计:一图胜千言
- 绪论
- 不能正常加载动图
- 设计的思路
- 渲染画布
- 开发感想
- 角色抽象为矩形
- ctx 是 canvas 的对象
- 键盘控制角色
- Set
- 键盘事件
- 流程图
- 在 canvas 里面使用 gif 图片
- 继承
- 存储动作
- ReferenceError: gif is not defined
- TypeError: Cannot read properties of undefined (reading 'length')
- 显示贴图
- 从矩形抽象开始
- 拳皇角色矩形抽象视频演示
- 开发小技巧
- gif 工具包
- 创建一个具体的游戏角色
- 关于注释
- 渲染矩形
- 放大+调整帧率
- 竖直方向偏移量
- 使用后退的动画
- 实现攻击的判断
- 修改按键
- 频闪
- 使两名玩家对称
- 修改跳跃的速率
- 修改背景图片
- 结语
架构设计:一图胜千言
绪论
『一个人的自身越是丰富,就越难以忍受世俗常规的安排。』—— 叔本华「人生的智慧」
不能正常加载动图
用注释的最后一行代码不能正常加载图片
#kof {/* 前面是用 id 标记的 div ,所以这里选择出来 */width: 1280px;height: 720px;background-image: url(/static/images/background/0.gif);/* background-image: url('static/images/background/0.gif'); */
}
调整了一下图片的大小
设计的思路
需要有三个对象,背景,左边玩家,右边玩家,这三个对象都需要每秒钟刷新 60 次,实现动态效果
我们的游戏需要读取键盘输入,那么我们需要让我们的 canvas(画布) 能够聚焦
渲染画布
我们需要在每一帧结束的时候把该帧清空,这样下一帧在展示的时候才不会出现重影的现象,但是清空的时间太快了,人眼察觉不到,所以我们把画布渲染为黑色,验证我们是不是可以正确渲染和清空
渲染的函数如下
render() {//表示清空画布的内容,也就是清空一帧的内容//clearRect表示的是清空矩形//参数是左上角的坐标,矩形的宽,矩形的高//this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);//console.log(this.$canvas.width());//把填充的颜色设置为黑色this.ctx.fillStyle = 'black';//填充矩形,参数是左上角的坐标,宽度,高度this.ctx.fillRect(0, 0, this.$canvas.width(), this.$canvas.height());}
开发感想
笔者之前做 django 项目,因为跟着教程做,出现了一些问题,没有很好的解决,现在做这个项目,因为花了很多时间,把这个里面的逻辑搞得非常清楚,所以开发的时候非常开心,因为自己可能确实能够理解这里面每一行代码的具体含义。而且敲击键盘的时候,给自己带来了一些之前没有怎么体会过的成就感
角色抽象为矩形
我们首先把底层的逻辑实现好,等逻辑实现完成之后再去考虑角色的美术优化还有一些动态的效果
ctx 是 canvas 的对象
笔者比较疑惑,canvas 是画布,ctx 表示的是画布的内容,这两者之间的关系是什么
canvas 和 context 的关系 画布和画笔的关系
这个讲的非常清楚,画布相当于背景,画笔相当于画布上面的图案,以这个项目为例,画布就是背景图片,画笔绘制的内容就是两个角色,因为角色要不停地在画布上面移动,表示的意思是,需要不停地在画布上绘制图片
键盘控制角色
//需要用键盘来操作这个游戏//每一个类都需要暴露出来
export class Controller {//需要控制的是角色,所以需要先把画布传进来constructor($canvas) {//需要实现的是,键盘控制当前的角色,按下一个键就会产生相应的反应//把画布存下来this.$canvas = $canvas;//下面是自己手动实现按键盘//Set是一个 js 的数据结构,里面可以存任何数据类型的数据this.pressed_keys = new Set();// 下面是调用初始函数this.start();}//只在开始的时候执行一次start() {//outer 表示的是这个 controller //我们在后面重新定义了一个函数,这个函数会有一个新的 this//this 指向的是类的实例//这里主要是因为之后要用前面定义的这个实例let outer = this;//keydown 表示的是键盘是不是被按住,会连续触发//但是这个不是我们需要的效果,我们需要的效果是,按住就发生变化,松开就结束变化//不需要连续触发这个事件this.$canvas.keydown(function (e) {//Set 添加元素是用 add()outer.pressed_keys.add(e.key);//输出调试一下console.log(e.key);});}
}
我们可以看到控制台可以输出我们键盘按了什么键,这说明可以准确的识别我们操作的是哪一个键位
Set
js 的 Set 里面不能有重复的元素,就是集合的一个概念
add():添加元素
has():返回是否包含某个元素
size:返回元素数量
delete():删除某个元素
clear():删除所有元素
我们项目需要用到的是,往集合里面添加一个键盘输入,再从集合里面删除一个键盘输入,实现的效果是按住某一个键,立即生效,松开某一个键,立即失效
键盘事件
keydown:某个键是否被按住,事件会连续触发
event.code:返回按的是哪个键
event.altKey、event.ctrlKey、event.shiftKey分别表示是否同时按下了alt、ctrl、shift键。
keyup:某个按键是否被释放
keypress:紧跟在keydown事件后触发,只有按下字符键时触发。适用于判定用户输入的字符。
流程图
我们在编辑文章的时候可以多添加一些图片,增强文章的可读性
在 canvas 里面使用 gif 图片
学会把一个函数封装好,给用户使用,而不是把一些底层的东西让用户去使用,这个非常关键,就像是我们需要用一个结果去证明自己,而不是用一个假努力的过程去欺骗自己,欺骗别人,最好用一个努力奋斗的过程取得的一个结果展示给别人看,比如说辛辛苦苦练了几年的肌肉
假设我们写的项目大部分都是市面上已有的函数接口,那么我们的项目没有什么创新点
我们这里就使用一个现有的函数接口帮助我们实现播放动图的功能
继承
class ColorPoint extends Point {constructor(x, y, color) {super(x, y); // 这里的super表示父类的构造函数this.color = color;}toString() {return this.color + ' ' + super.toString(); // 调用父类的toString()}
}
我们在设计一个角色的时候,需要把抽象的矩形变成具体的游戏角色,所以在设计这个角色的时候需要继承玩家这个类,然后设计一些具体的东西,机制其实都是一样的,王者不断新出的英雄,多个地图,等等
存储动作
我们把动作存到一个 Map 里面,保存的是键值对
ReferenceError: gif is not defined
其实在开发的时候我遇到了挺多问题,忘记记录下来了
自己在使用 gif 的时候把 gif 放在了循环外面,索引不到,所以就报错了,解决之后,出现了更多的问题
TypeError: Cannot read properties of undefined (reading ‘length’)
obj.frame_cnt = gif.frame.length;
找到的原因是函数接口里面定义的是 frames ,虽然是这种简单的拼写错误,但是在开发项目的时候,遇到这种问题也是非常无奈的,要是找不出来,项目就无法正常运行
显示贴图
现在控制台没有任何报错,但是显示不了贴纸
笔者认为可能是引用的路径不是绝对路径的原因,也就是不是从 / 开始引用的,现在修改一下试试
把玩家和具体角色的引用路径都改为了绝对路径,没有效果
没有任何问题,但是出现不了贴纸,这是为什么呢
怀疑是 gif 的接口函数没有调用好,重新使用一遍接口函数
- 我在实现两个抽象矩形移动的时候是没有问题的,到贴纸这一步出现的问题,
找了挺久还是没有找到原因,准备从正常移动那里开始看问题出在了哪里,今天先睡觉,明天和它战斗,两个多小时的教程,看了两天了,还没有搞完
从矩形抽象开始
笔者把整个教程完整看了一遍,现在从上一步卡住的地方开始做起
目前初步的构想是,实现几个项目,并且部署到拥有独立域名的网站上面,方便展示,并且绘制几张流程图梳理整个项目的逻辑
多动手多搜索
我们知道,gif 是动图,所以和帧有一定的关系,我们在矩形抽象的时候,和帧有关系的部分只有动画,也有一定的关系,我先把后面的代码都注释掉,让我们的项目可以像之前一样移动矩形
并且现在不涉及到状态机,与状态机有关的代码都可以注释掉
拳皇角色矩形抽象视频演示
视频演示
开发小技巧
因为自己是在 vscode 上面开发的,之前研究了一下怎么通过 git 把项目代码 push 到云端,可以 push 上去,但是不是很流畅,用 vim 会非常流畅的 push 上去,但是没有代码自动补全,没有自动补全,写前端代码其实是非常难受的
所以我的技巧就是多写一点注释,不删除代码,只把需要删除的代码注释掉,及时保存代码
gif 工具包
前面其实用了这个步骤,但是现在需要重新操作一遍,因为我不知道我到底是哪一块出现了问题(debug 的时候试过一次了,没有解决)
这个第三方函数是直接复制过来的,文件夹和文件名字都没有问题,也就是说调用的路径不会有问题
创建一个具体的游戏角色
继承 player 这个基类,写在 player 文件夹下
关于注释
因为自己写的注释比较多,会让人看不清楚代码结构,所以可以多画一些图片来梳理代码结构
通过这些图可以发现其实里面的逻辑关系比较复杂,不同的函数,变量,在不断地调用
架构设计:一图胜千言
渲染矩形
1:25:41 之前是渲染矩形
可以看到目前没有任何地报错信息
现在貌似找到之前地错误的原因了,因为一个单词拼写错了,之前没有报错,是因为没有找到这个变量,所以页面显示不了任何信息
项目能正常跑起来就算成功了一半,真的是真理呀
成功显示了,虽然丑丑的,但是非常开心哈哈哈,后面就是把角色放大一点儿,设置攻击,背景等
放大+调整帧率
很不错
竖直方向偏移量
移动的时候,我们在竖直方向会有一个偏移量,我们需要把这个竖直方向的偏移量消除,因为不同的动图的图片高度是不相等的,换句话说,往右走和静止,两者的高度是不相等的,观察可以发现,移动会比静止低一定的距离
设置一个偏移量的数组,把左右移动加上 22 的偏移量即可,这个偏移量是自己调试出来的
使用后退的动画
目前往右和往左都是同一个动画,假设我们是左边的角色,往右是前进,往左是后退,我们速度方向和 x 轴正方向不一致的时候,设置为后退状态
if (this.status === 1 && this.derection * this.vx < 0) {this.status = 2;
}
实现攻击的判断
在 Player 里面实现
if (space) {//把状态机转变为 4this.status = 4;//攻击的时候不能移动this.vx = 0;//从第零帧开始渲染,也就是渲染攻击的动态效果this.frame_current_cnt = 0;}
修改按键
因为空格键也是网页往下的按键,所以点击空格攻击的时候,网页也会往下,不太好,准备把空格键修改为 k 键,注意 space 这个名称没有修改
space = this.pressed_keys.has('k');
频闪
我们在攻击结束之后,角色会闪一下,下面我们需要解决这个问题
是因为我们图片总共是有 frame_cnt 张,我们播放到 frame_cnt-1 这张图片的时候,下一帧要直接停下来了,这样可以不会发生闪烁,丝滑过渡
if (this.status === 4 && this.frame_current_cnt === obj.frame_rate * (obj.frame_cnt - 1)) {//把状态更新为静止this.status = 0;//重新开始渲染动画//this.frame_current_cnt = 0;}
使两名玩家对称
目前两名玩家是朝向一个方向的,我们需要让这两名玩家面对同一个方向
通过变换坐标系实现翻转,方法是网上搜 api 来实现
目前卡成这样子了,担心出 bug
现在效果还行,就是移动的时候出现了一些问题
现在完全实现了对称,办法就是查一下怎么在 canvas 里面翻转图像,然后把一些偏移量,对称的图形的一些参数改一下,目前的效果是这样子
修改跳跃的速率
写前端就是这样,需要多次修改一些参数,使得用户得到一个比较好的体验
//修改跳跃的参数,其他的是 5 帧播放一张图片,跳跃的时候每 4 帧播放一张图片//一般浏览器是一秒 60 帧,所以就是 0.0167 秒是一帧//5帧就是0.08秒(大概),4帧是0.07秒if (i === 3) {obj.frame_rate = 4;}
修改完之后还行
修改背景图片
感觉还行,主要是这个背景图片占满了整个屏幕,角色无法正常落地,但是不把网页往下翻,其实是没有影响的,目前有两个选择,第一是保持现状,第二是把背景缩小一些,然后调一下参数使得角色可以落地,还是选第一个吧
结语
『这世界太大,勇敢的少年奔赴天涯。』—— 陈可心「荣耀同行」
相关文章:

js_拳皇(上)
文章目录 架构设计:一图胜千言绪论不能正常加载动图设计的思路渲染画布开发感想角色抽象为矩形ctx 是 canvas 的对象键盘控制角色Set键盘事件流程图在 canvas 里面使用 gif 图片继承存储动作ReferenceError: gif is not definedTypeError: Cannot read properties o…...

TCP请求如何获取客户端真实源IP地址
应用场景 在基于TCP的应用程序中,获取客户端真实源IP地址可以用于以下应用场景: 访问控制和安全策略:通过获取客户端真实源IP地址,应用程序可以实施访问控制策略,限制或允许特定IP地址的访问。这可以用于身份验证、防…...

【b站-湖科大教书匠】6 应用层 - 计算机网络微课堂
课程地址:【计算机网络微课堂(有字幕无背景音乐版)】 https://www.bilibili.com/video/BV1c4411d7jb/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 6 应用层 6.1 应用层概述 6.2 客户-服务器方式和对等方…...

QT串口和数据库通信
创建串口 串口连接客户端并向服务器发送消息 client.pro #------------------------------------------------- # # Project created by QtCreator 2024-07-02T14:11:20 # #-------------------------------------------------QT core gui network QT core gui…...

WebKitWebKit简介及工作流程
简介 引擎能够解析HTML、CSS、JavaScript等网页标准,从而将互联网内容呈现给用户。 WebKit的主要特点包括: 开源性:它是一个开源项目,任何人都可以查看、修改和贡献代码。跨平台:WebKit可以在多个操作系统上运行&am…...

架构分析(CPU:ARM vs RISC-V)
ARM N2 ARM V2 对比 N2和V2,整体架构具有一致性。保证 SiFive P870 P870 Pipeline Veyron V1...

使用 Docker Compose 部署 RabbitMQ 的一些经验与踩坑记录
前言 RabbitMQ 是一个功能强大的开源消息队列系统,它实现了高效的消息通信和异步处理。 本文主要介绍其基于 Docker-Compose 的部署安装和一些使用的经验。 特点 成熟,稳定消息持久化灵活的消息路由高性能,高可用性,可扩展性高支…...

前端八股速通(持续更新中...)
1、深拷贝和浅拷贝的区别 浅拷贝:浅拷贝是拷贝一层,引用类型共享地址。 如果属性是基本类型,拷贝的就是基本类型的值。 如果属性是引用类型,拷贝的就是内存地址。 意思是,当进行浅拷贝时,对于对象的每一…...

【语音识别和生成】语音识别和语音合成技术
语音识别和生成:语音识别和语音合成技术 目录 引言语音识别技术 语音识别的基本原理语音识别系统的组成语音识别的关键技术 语音合成技术 语音合成的基本原理语音合成系统的组成语音合成的关键技术 语音识别和生成的应用 智能助理智能家居语音翻译医疗健康教育和学…...

Redis#架构师面试题
1、Redis锁存在哪些问题及如何解决? 1、死锁问题 加过期时间设定 2、原子性问题 通过“set…nx...ex…”命令,将加锁、过期命令编排到一起,它们是原子操作了,可以避免死锁。 3、释放其他线程的锁问题 当过期时间设置小于线程…...

关于#define的使用方法总结
文章目录 #define 预处理指令一、#define宏定义二、查看预处理文件三、#define 的使用方法四、C语言宏中“#”和“##”的用法五、常见的宏定义总结六、常考题目 #define 预处理指令 #define 是 C 和 C 编程语言中的预处理指令,用于定义宏(macro…...

Unity顶点动画(Vertex Animation):创造动态视觉效果
在Unity中,顶点动画(Vertex Animation)是一种强大的技术,它允许开发者直接在顶点级别上操作和变形网格,从而实现各种动态视觉效果。顶点动画不依赖于骨骼绑定,因此非常适合模拟布料、流体、面部表情等复杂的动画效果。本文将探讨顶…...

WSL for Windows
1、安装 超详细Windows10/Windows11 子系统(WSL2)安装Ubuntu20.04(带桌面环境)_wsl安装ubuntu20.04-CSDN博客https://blog.csdn.net/weixin_44301630/article/details/122390018 注意,安装之后首次启动 Ubuntu 时&…...

Matlab freqz 代码简单实现
相关代码打开matlab源码也可以看到,这里做了简单实现,与源码并不完全一样。 实现代码 [h2 w2] freqzfir(data); [h1 w1] freqz(data); h2h2; h12 [h1, h2];[h4 w4] freqziir(b,a, 2001,true); [h3 w3] freqz(b,a, w4, whole); h4 h4; h34 h…...

待办app哪款好?高效待办软件推荐
在快节奏的现代生活中,一款高效的待办事项管理软件对于提升工作效率和个人时间管理至关重要。面对市场上众多的待办app,哪款才是你的最佳选择呢?经过深入体验和对比,我发现敬业签这款高效待办软件是个不错的选择。 敬业签的快速记…...

【OSCP系列】OSCP靶机-BTRsys-2.1(原创)
OSCP系列靶机—BTRsys-2.1 原文转载已经过授权 原文链接:Lusen的小窝 - 学无止尽,不进则退 (lusensec.github.io) 一、主机发现 二、端口扫描 1、快速扫描 2、全端口扫描 3、服务系统探测 4、漏洞探测 80端口扫到了一些目录,有wordpress框…...

攻坚克难岁月长,自主腾飞世界强——回顾近代中国数据库的发展与飞跃
前言 最近看了《中国数据库前世今生》纪录片,感触颇深,也是一直在思考到底该用何种方式起笔来回顾这段筚路蓝缕却又充满民族自豪感的历程。大概构思了一周左右吧,我想,或许还是应该从那个计算机技术在国内刚刚萌芽的年代开始讲起…...

WEB前端12-axios基础
Vue2-axios基础 1.axios基本概念 在现代的前端开发中,处理网络请求是至关重要的一部分。Axios 是一个流行的基于 Promise 的 HTTP 客户端,它可以在浏览器和 Node.js 环境中使用。它的设计简单易用,支持并行请求、拦截器、CSRF 防护等特性&a…...

Ubuntu 防火墙设置
目录 1. 安装防火墙 2. 开启和关闭防火墙 3. 开放端口和服务规则 4. 关闭端口和删除服务规则 5. 查看防火墙状态 1. 安装防火墙 如果已经安装就忽略 # 安装ufw(Uncomplicated Firewall),这是Ubuntu上管理防火墙的一个简单工具 sudo ap…...

JL 跳转指令的理解
一般情况下,JU 和 JC 是最常见的跳转指令;但有时会用到JL 指令,JL 说起来更像是一组指令,类似C,C# 语言中的 switch case 语句,但是有个明显的不同,前者的判断条件可以是任意合理数字,后者范围…...

vue大屏展示组件库datav
主要用于构建大屏数据展示页面,具有多种类型组件可供使用。详情参考 datav官网 一、安装 npm 安装 npm install jiaminghi/data-viewyarn安装 yarn add jiaminghi/data-view二、使用 在main.js中注册为全局组件 import dataV from jiaminghi/data-view Vue.us…...

Vue.js 与 Ajax(vue-resource)的集成应用
Vue.js 与 Ajax(vue-resource)的集成应用 Vue.js 是一款流行的前端JavaScript框架,以其简洁、灵活和高效的特点而受到开发者的喜爱。在实际开发中,与后端服务的通信是不可或缺的,而Ajax技术是实现这一功能的关键。在V…...

【讲解下AI Native应用中的模型微调】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...

【SOC 芯片设计 DFT 学习专栏 -- DFT DRC规则检查】
请阅读【嵌入式及芯片开发学必备专栏】 请阅读【芯片设计 DFT 学习系列 】 如有侵权,请联系删除 转自: 芯爵ChipLord 2024年07月10日 12:00 浙江 文章目录 概述DRC的概念Tessent DRC检查的概述时钟相关检查扫描相关检查BIST规则检查预DFT时钟规则检查 …...

深度学习:如何计算感受野
感受野(Receptive Field)是卷积神经网络(CNN)中的一个重要概念,用于描述输入图像中的一个像素在输出特征图中影响的区域大小。在设计和理解卷积神经网络时,计算感受野有助于理解网络如何对输入数据进行处理…...

【状语从句】
框架 概念,特点主将从现连接词时间条件地点结果方式让步原因目的比较省略倒装 解读 1【概念,特点】 一个完整的句子,去修饰另一个完整句子中的动词,称为状语从句;特点:从句完整,只用考虑连接词是…...

阿里云服务器安装Anaconda后无法检测到
前言 问题如标题所言,就是conda -V验证错误,不过后来发现其实就是虽然安装时,同意了写入环境变量,但是其实还没有写入,需要手动写入。下面也会重复一遍安装流程。 安装 到[Anaconda下载处](Download Now | Anaconda)查…...

在没有源程序的情况时,如何通过控制鼠标按钮控制电脑exe程序?
有时候想控制第三方软件,但是没有源程序,可以控制鼠标键盘自动操作软件达到我们想要的目的 首先建一个功能类包含窗口控制,鼠标控制和输入控制等 csharp using System; using System.Collections.Generic; using System.Linq; using System.…...

如何排查GD32 MCU复位是由哪个复位源导致的?
上期为大家讲解了GD32 MCU复位包括电源复位和系统复位,其中系统复位还包括独立看门狗复位、内核软复位、窗口看门狗复位等,在一个GD32系统中,如果莫名其妙产生了MCU复位,如何排查具体是由哪个复位源导致的呢? GD32 MC…...

【C算法】编程初学者入门训练140道(1~20)
牛客编程初学者入门训练150题 BC1 实践出真知BC2 我是大VBC3 有容乃大BC6 小飞机BC7 缩短二进制BC8 十六进制转十进制BC9 printf的返回值BC10 成绩输入输出BC11 学生基本信息输入输出BC12 字符圣诞数BC13 ASCII 码BC14 出生日期输入输出BC15 按照格式输入并交换输出BC16 字符转…...