使用konvajs三步实现一个小球游戏

简介: 使用konvajs三步实现一个小球游戏

记得以前玩过一个很简单的小游戏,一个球在动,然后底下一个板来接,如果没接住撞到底边游戏就结束了,虽然忘了它叫什么名字,但一直心心念念,魂牵梦萦,刚好最近临过年需求不饱和、刚好之前一直想做但是没动手,刚好这两天在学习konvajs,刚好就有了这篇小文章,很简单,只要三步,包你三分钟学会。


konvajs简介


konvajs就像jquery之于DOMSnap.svg之与svg一样之于canvas,可以方便的实现canvas的图形交互效果,使用它,你可以快速绘制常用图形,并能方便的给它添加样式、各种事件、动画效果等等,妈妈再也不用担心我自己来检测鼠标位置了。文档:konvajs.org/


使用konvajs基本上也分为三步,第一步是创建一个"舞台":


import { Stage } from 'konva'
const stage = new Stage({
    container: 'container',// 容器元素的id
    width: 500,
    height: 500
})


第二步是创建一个“图层”,添加到“舞台”里:


import { Layer } from 'konva'
const layer = new Layer()
stage.add(layer)


一个layer对应着一个canvas元素。


第三步就是创建各种图形添加到“图层里”。


第一步


来一个小球和挡板。


直接复制官方文档上的圆和矩形的示例代码,小球:


import { Circle } from 'konva'
createCircle () {
    // 小球的中心坐标
    this.centerX = this.stage.width() / 2
    this.centerY = 100
    this.circle = new Circle({
        x: this.centerX,
        y: this.centerY,
        radius: this.radius,
        fill: 'red',
        stroke: 'black',
        strokeWidth: 4
    })
    this.layer.add(this.circle)
    this.layer.draw()// 重新绘制,每次修改后都需要调用该方法
}


挡板:


import { Rect } from 'konva'
createRect () {
    this.rect = new Rect({
        x: (this.stage.width() - 100) / 2,
        y: this.height - 50,
        width: 100,
        height: 10,
        fill: 'green',
        stroke: 'black',
        strokeWidth: 4,
        draggable: true,// 允许拖动
        dragBoundFunc: function (pos) {// 控制只能水平拖动
            return {
                x: pos.x,
                y: this.absolutePosition().y// 获取y的绝对位置
            }
        }
    })
    this.layer.add(this.rect)
    this.layer.draw()
}


image.png



第二步


让球动起来,给球一个初始速度,一个加速度,撞墙后速度反向,速度逐渐加快,用来增加游戏难度:


this.speedX = Math.random() * 3
this.speedY = Math.random() * 3
this.speedStep = 0.01
runCircle () {
    // 修改小球位置
    this.centerX += this.speedX
    this.centerY += this.speedY
    this.circle.x(this.centerX)
    this.circle.y(this.centerY)
    // 撞墙检测
    if (this.centerX - this.radius <= 0 || this.centerX + this.radius >= this.width) {// 左侧和右侧的墙
        this.speedX = -this.speedX// 水平速度反向
    }
    if (this.centerY - this.radius <= 0) {// 只判断顶部的墙,底部的墙用来结束游戏
        this.speedY = -this.speedY
    }
    // 撞板检测,第三步再说
    this.collisionCheck()
    // 游戏结束检测
    if (this.centerY + this.radius >= this.height) {// 撞到底部的墙游戏就结束了
        return this.gameOver()
    }
    // 加速度
    // 还没达到最大速度则增加速度
    if (Math.abs(this.speedX) < this.maxSpeed) {
        this.speedX > 0 ? this.speedX += this.speedStep : this.speedX -= this.speedStep
    }
    if (Math.abs(this.speedY) < this.maxSpeed) {
        this.speedY > 0 ? this.speedY += this.speedStep : this.speedY -= this.speedStep
    }
}


然后使用requestAnimationFrame来不断刷新:


update () {
    window.requestAnimationFrame(() => {
        this.runCircle()
        this.update()
        this.layer.draw()
    })
}


image.png


第三步


检测小球和挡板的是否碰撞,撞到了则速度反向,原理是找到矩形四周离小球的圆心最近的点,然后判断这个点和小球圆心的距离是否小于小球半径,这个点怎么确定也很简单,如果圆心在矩形的左侧,那么这个点一定在矩形的左侧边上,点x的值也就是rect.x,如果在矩形的右侧,点x的值一定在矩形的右侧边上,也就是rect.x + rect.width,如果在矩形之间的话,因为最近的点和圆心的连线肯定是重置于矩形的边,所以点x的值就是圆心的x,点y的计算和x一样。


image.png


collisionCheck () {![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0440226e4a95497d8890494d9efda590~tplv-k3u1fbpfcp-watermark.image)
    let minx = 0
    let miny = 0
    let rectX = this.rect.x()
    let rectY = this.rect.y()
    let rectWidth = this.rect.width()
    let rectHeight = this.rect.height()
    // 确定矩形上离小球最近的点的x坐标
    if (this.centerX < rectX) {// 在矩形左侧
        minx = rectX
    } else if (this.centerX > rectX + rectWidth) {// 在矩形右侧
        minx = rectX + rectWidth
    } else {// 在矩形左右之间
        minx = this.centerX
    }
    // 确定矩形上离小球最近的点的y坐标
    if (this.centerY < rectY) {// 在矩形上面
        miny = rectY
    } else if (this.centerY > rectY + rectHeight) {// 在矩形下面
        miny = rectY + rectHeight
    } else {// 在矩形上下之间
        miny = this.centerY
    }
    // 如果距离小于半径则代表发生了碰撞
    if (this.getTwoPointDistance(minx, miny, this.centerX, this.centerY) <= this.radius) {
        // 撞到了左侧或右侧,那么水平速度x反向
        if (minx === rectX || minx === rectX + rectWidth) {
            this.speedX = -this.speedX
        }
        // 撞到了矩形上面则垂直速度y反向
        if (miny === rectY) {
            this.speedY = -this.speedY
        }
    }
}


到这里就结束了,可以愉快的开玩了:


image.png


示例代码:github.com/wanglin2/Ba…。



相关文章
|
JSON 前端开发 数据库
学习nest.js中如何使用jwt进行身份验证,这一篇文章就够
学习nest.js中如何使用jwt进行身份验证,这一篇文章就够
|
XML 安全 IDE
【C/C++ 实用工具】CppCheck:静态代码检测工具,让你的代码更安全
【C/C++ 实用工具】CppCheck:静态代码检测工具,让你的代码更安全
3212 2
|
4月前
|
人工智能 运维 开发工具
10分钟无痛部署!字节Coze开源版喂饭教程
字节跳动开源AI智能体平台Coze(含Studio开发工具+Loop运维系统),仅需2核CPU/4GB内存即可本地运行,48小时GitHub星标破9000。本文提供10分钟极速部署指南,涵盖Docker配置、模型服务调优及Qwen模型切换实战,零成本实现商用级AI开发,彻底降低智能体创作门槛。
|
11月前
|
编解码 人工智能 文字识别
用PDF转换图片的方式弥补通义千问在扫描版PDF支持方面的缺失
当前通义千问Web版和本地版qwen-VL在处理扫描版PDF时均无法直接识别,导致实际应用中处理大量扫描PDF的需求难以满足。为此,通过使用Python的pdf2image库,可将PDF文件转换为图片,再进行OCR处理,实现解决方案。文中提供了具体的代码示例,展示了如何将PDF文件的每一页转换成图片,并保存至指定文件夹,为后续的OCR处理做好准备。
|
移动开发 前端开发
ruoyi-nbcio-plus的Vue3前端升级组件后出现的问题(二)
ruoyi-nbcio-plus的Vue3前端升级组件后出现的问题(二)
366 1
|
机器学习/深度学习 搜索推荐 知识图谱
图神经网络加持,突破传统推荐系统局限!北大港大联合提出SelfGNN:有效降低信息过载与数据噪声影响
【7月更文挑战第22天】北大港大联手打造SelfGNN,一种结合图神经网络与自监督学习的推荐系统,专攻信息过载及数据噪声难题。SelfGNN通过短期图捕获实时用户兴趣,利用自增强学习提升模型鲁棒性,实现多时间尺度动态行为建模,大幅优化推荐准确度与时效性。经四大真实数据集测试,SelfGNN在准确性和抗噪能力上超越现有模型。尽管如此,高计算复杂度及对图构建质量的依赖仍是待克服挑战。[详细论文](https://arxiv.org/abs/2405.20878)。
337 5
|
Kubernetes 监控 API
在K8S中,什么是静态 Pod?
在K8S中,什么是静态 Pod?
|
消息中间件 存储 JavaScript
构建一个基于Node.js的实时数据流处理系统
【5月更文挑战第30天】使用Node.js构建实时数据流处理系统,结合WebSocket实现双向通信,Kafka作为消息队列,Redis做数据存储和缓存,D3.js用于数据可视化。系统包括数据源、传输、处理、存储和可视化五个关键部分,适合高并发、低延迟的实时监控与分析需求。
|
监控 负载均衡 网络协议
|
存储 网络协议 Linux
CentOS 7.6 部署 GlusterFS 分布式存储系统
CentOS 7.6 部署 GlusterFS 分布式存储系统
741 0