vue3+threejs新手从零开发卡牌游戏(二十一):添加战斗与生命值关联逻辑
首先将双方玩家的HP存入store中,stores/common.ts代码如下:
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'export const useCommonStore = defineStore('common', () => {const _font = ref() // 字体const p1HP = ref(4000) // 己方HPconst p2HP = ref(4000) // 对方HPconst p1Deck = ref([] as any) // 己方卡组const p2Deck = ref([] as any) // 对方卡组const p1Hand = ref([] as any) // 己方手牌const p2Hand = ref([] as any) // 对方手牌const p1SiteCards = ref([] as any) // 己方场上卡牌const p2SiteCards = ref([] as any) // 对方场上卡牌// 加载字体function loadFont(data: any) {_font.value = data}// 更新己方HPfunction updateP1HP(data: any) {p1HP.value = data}// 更新对方HPfunction updateP2HP(data: any) {p2HP.value = data}// 更新己方卡组function updateP1Deck(data: any) {p1Deck.value = data}// 更新对方卡组function updateP2Deck(data: any) {p2Deck.value = data}// 更新己方手牌function updateP1Hand(data: any) {p1Hand.value = data}// 更新己方手牌function updateP2Hand(data: any) {p2Hand.value = data}// 更新己方场上卡牌function updateP1SiteCards(data: any) {p1SiteCards.value = data}// 更新对方场上卡牌function updateP2SiteCards(data: any) {p2SiteCards.value = data}return {_font,p1HP,p2HP,p1Deck,p2Deck,p1Hand,p2Hand,p1SiteCards,p2SiteCards,loadFont,updateP1HP,updateP2HP,updateP1Deck,updateP2Deck,updateP1Hand,updateP2Hand,updateP1SiteCards,updateP2SiteCards,}
}, {persist: true
})
然后思考下血量相关逻辑,这里只做两个简单的处理:
1.卡牌战斗后的攻击力差值导致的血量削减
2.卡牌直接攻击玩家导致的血量削减
所以我们修改game/index.vue中的fight方法,补充了直接攻击和卡牌战斗后差值导致的血量变化逻辑:
// 战斗
const fight = () => {if (selectedCard.value && selectedTargetCard.value) { // 如果selectedCard和selectedTargetCard都存在let _selectedCard: any = selectedCard.valuelet _selectedTargetCard: any = selectedTargetCard.valueif (selectedCard.value.name === "攻击力") {_selectedCard = _selectedCard.value.parent}if (selectedTargetCard.value.name === "攻击力") {_selectedTargetCard = _selectedTargetCard.value.parent}// 移除卡牌const removeCard = async (card: any) => {if (card.children && card.children.length > 0) {card.children.forEach((v: any) => {card.remove(v)})}let areaType = card.userData.areaTypelet isP1 = areaType.indexOf("己方") > -1let graveyardGroup = nulllet graveyardGroupPos = new THREE.Vector3(0, 0, 0)let cards = []card.material.forEach((v: any) => {v.transparent = falsev.opacity = 1v.alphaTest = 0.1;})card.rotateX(180 * (Math.PI / 180)) // 弧度if (isP1) {card.userData.areaType = "己方墓地"graveyardGroup = scene.getObjectByName("p1_graveyardGroup")graveyardGroup.getWorldPosition(graveyardGroupPos)card.position.set(graveyardGroupPos.x, graveyardGroupPos.y, graveyardGroupPos.z)cards = scene.children.filter((v: any) => v.userData?.areaType === "己方墓地")} else {card.userData.areaType = "对方墓地"graveyardGroup = scene.getObjectByName("p2_graveyardGroup")graveyardGroup.getWorldPosition(graveyardGroupPos)card.position.set(graveyardGroupPos.x, graveyardGroupPos.y, graveyardGroupPos.z)cards = scene.children.filter((v: any) => v.userData?.areaType === "对方墓地")}// 修改墓地let position = new THREE.Vector3(0, 0.005 * cards.length, 0)await editGraveyardCard(graveyardGroup, card, "remove")await renderGraveyardText(graveyardGroup, `${cards.length}`, commonStore.$state._font, position)if (isP1) {let sitePlane = scene.getObjectByName("己方战域Plane")let mesh = sitePlane.children.find((v: any) => v.name === areaType)if (mesh) {mesh.userData.empty = true}} else {let sitePlane = scene.getObjectByName("对方战域Plane")let mesh = sitePlane.children.find((v: any) => v.name === areaType)if (mesh) {mesh.userData.empty = true}}}cardAttack(_selectedCard, _selectedTargetCard, () => {console.log(888, Number(_selectedCard.userData.ATK), Number(_selectedTargetCard.userData.ATK))if (Number(_selectedCard.userData.ATK) > Number(_selectedTargetCard.userData.ATK)) {cardDestroy(_selectedTargetCard, () => {removeCard(_selectedTargetCard)let p2HP = JSON.parse(JSON.stringify(commonStore.$state.p2HP))p2HP -= (Number(_selectedCard.userData.ATK) - Number(_selectedTargetCard.userData.ATK))if (p2HP < 0) {p2HP = 0}commonStore.updateP2HP(p2HP)})} else if (Number(_selectedCard.userData.ATK) === Number(_selectedTargetCard.userData.ATK)) {cardDestroy(_selectedCard, () => {removeCard(_selectedCard)})cardDestroy(_selectedTargetCard, () => {removeCard(_selectedTargetCard)})} else {cardDestroy(_selectedCard, () => {removeCard(_selectedCard)let p1HP = JSON.parse(JSON.stringify(commonStore.$state.p1HP))p1HP -= (Number(_selectedTargetCard.userData.ATK) - Number(_selectedCard.userData.ATK))if (p1HP < 0) {p1HP = 0}commonStore.updateP1HP(p1HP)})}})selectedCard.value = nullselectedTargetCard.value = null} else if (selectedCard.value) { // 如果只存在selectedCard// 直接攻击let _selectedCard: any = selectedCard.valueconsole.log(333, _selectedCard)if (selectedCard.value.name === "攻击力") {_selectedCard = _selectedCard.value.parent}let areaType = _selectedCard.userData.areaTypelet isP1 = areaType.indexOf("己方") > -1let cards = []if (isP1) {cards = scene.children.filter((v: any) => v.userData?.areaType?.indexOf("对方怪兽区") > -1)} else {cards = scene.children.filter((v: any) => v.userData?.areaType?.indexOf("己方方怪兽区") > -1)}if (cards.length > 0) {return}cardDirectAttack(scene, _selectedCard, () => {if (isP1) {let p2HP = JSON.parse(JSON.stringify(commonStore.$state.p2HP))p2HP -= _selectedCard.userData.ATKif (p2HP < 0) {p2HP = 0}commonStore.updateP2HP(p2HP)} else {let p1HP = JSON.parse(JSON.stringify(commonStore.$state.p1HP))p1HP -= _selectedCard.userData.ATKif (p1HP < 0) {p1HP = 0}commonStore.updateP1HP(p1HP)}})}
}
然后在utils/common.ts中添加一个卡牌直接攻击特效的方法,和卡牌战斗动效差不多,修改下移动终点的位置即可:
// 卡牌直接攻击特效
const cardDirectAttack = (scene: any, card: any, callback: any) => {// 获取card1世界坐标let pos1 = new THREE.Vector3(0, 0, 0)card.getWorldPosition(pos1)// 获取card2世界坐标let pos2 = new THREE.Vector3(0, 0, 0)let isP1 = card.userData.areaType.indexOf("己方") > -1let mesh = nullif (isP1) {let sitePlane = scene.getObjectByName("对方战域Plane")mesh = sitePlane.children.find((v: any) => v.name === "对方战术2")mesh.getWorldPosition(pos2)} else {let sitePlane = scene.getObjectByName("己方战域Plane")mesh = sitePlane.children.find((v: any) => v.name === "己方战术2")mesh.getWorldPosition(pos2)}// 动画1:移动到对方卡面前const twA = new TWEEN.Tween({x: pos1.x,y: pos1.y,z: pos1.z,card,})twA.to({x: pos2.x,y: pos2.y + 0.1,z: pos2.z,}, 300)twA.easing(TWEEN.Easing.Quadratic.Out)twA.onUpdate((obj: any) => {obj.card.position.set(obj.x, obj.y, obj.z)})twA.onComplete(function() {//动画结束:关闭允许透明,恢复到模型原来状态TWEEN.remove(twA)callback && callback()})// 动画2:退回到原位置const twB = new TWEEN.Tween({x: pos2.x,y: pos2.y + 0.1,z: pos2.z,card,})twB.to({x: pos1.x,y: pos1.y,z: pos1.z,}, 400)twB.easing(TWEEN.Easing.Quadratic.In)twB.onUpdate((obj: any) => {obj.card.position.set(obj.x, obj.y, obj.z)})twB.onComplete(function() {//动画结束:关闭允许透明,恢复到模型原来状态// TWEEN.remove(twA)// callback && callback()})twA.chain(twB)twA.start();
}
export { cardDirectAttack }
页面效果如下:

相关文章:
vue3+threejs新手从零开发卡牌游戏(二十一):添加战斗与生命值关联逻辑
首先将双方玩家的HP存入store中,stores/common.ts代码如下: import { ref, computed } from vue import { defineStore } from piniaexport const useCommonStore defineStore(common, () > {const _font ref() // 字体const p1HP ref(4000) // 己…...
Linux内核err.h文件分析
在阅读和编写内核相关的代码时,经常会看到IS_ERR、ERR_PTR等函数。这些函数在内核头文件的err.h中。以我服务器的代码为例,内核版本为5.15。 这个文件的代码如下: /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_ERR_H #define _L…...
Qt 富文本处理 (字体颜色大小加粗等)
Qt中支持HTML的控件有textEdit 、label 、textBrowser 。 接口:setHtml("Qt"); toHtml(). 文本样式设置 : 可分字设置 ,主要使用QTextCharFormat类进行文本样式设置。 示例: QTextCharFormat fmt; //粗体 fmt.setFontWeight…...
消息队列的七种经典应用场景
在笔者心中,消息队列,缓存,分库分表是高并发解决方案三剑客。 在职业生涯中,笔者曾经使用过 ActiveMQ 、RabbitMQ 、Kafka 、RocketMQ 这些知名的消息队列 。 这篇文章,笔者结合自己的真实经历,和大家分享…...
uniapp 微信小程序 canvas 手写板文字重复倾斜水印
核心逻辑 先将坐标系中心点通过ctx.translate(canvasw / 2, canvash / 2) 平移到canvas 中心,再旋转设置水印 假如不 translate 直接旋转,则此时的旋转中心为左上角原点,此时旋转示意如图所示 当translate到中心点之后再旋转,此…...
JavaScript如何制作轮播图
在JavaScript中实现轮播图可以通过多种方式,但最常见的方式是使用数组来存储图片,然后使用setInterval函数定期更改显示的图片。下面是一个简单的例子: 首先,你需要在HTML中设置一些用于显示图片的<img>标签,以…...
【面试经典150 | 动态规划】零钱兑换
文章目录 Tag题目来源解题思路方法一:动态规划 写在最后 Tag 【动态规划】【数组】 题目来源 322. 零钱兑换 解题思路 方法一:动态规划 定义状态 dp[i] 表示凑成总金额的最少硬币个数。 状态转移 从小到大枚举要凑成的金额 i,如果当前…...
什么是防火墙,部署防火墙有什么好处?
与我们的房屋没有围墙或界限墙一样,没有防护措施的计算机和网络将容易受到黑客的入侵,这将使我们的网络处于巨大的风险之中。因此,就像围墙保护我们的房屋一样,虚拟墙也可以保护和安全我们的设备,使入侵者无法轻易进入…...
学习鸿蒙基础(10)
目录 一、轮播组件 Swiper 二、列表-List 1、简单的List 2、嵌套的List 三、Tabs容器组件 1、系统自带tabs案例 2、自定义导航栏: 一、轮播组件 Swiper Entry Component struct PageSwiper {State message: string Hello Worldprivate SwCon: SwiperControl…...
阿里云对象存储OSS入门
阅读目录 一、阿里云OSS的使用 1、OSS是什么?2、OSS的使用 二、阿里云OSS的使用三、图床的搭建四:图床绑定阿里云OSS 编写不易,如果我的文章对你有帮助的话,麻烦小伙伴还帮忙点个赞再走! 如果有小伙伴觉得写的啰嗦&a…...
[幻灯片]软件需求设计方法学全程实例剖析-03-业务用例图和业务序列图
DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 pdf已上传至本号的CSDN资源,或到以下地址下载: http://umlchina.com/training/umlchina_03_bm.pdf...
ctfshow-web入门-xxe
什么是xxe? XXE,全称XML External Entity Injection,即XML外部实体注入。这是一种针对应用程序解析XML输入类型的攻击。当包含对外部实体的引用的XML输入被弱配置的XML解析器处理时,就会发生这种攻击。这种攻击通过构造恶意内容&…...
Docker数据卷挂载
一、容器与数据耦合的问题: 数据卷是虚拟的,不真实存在的,它指向文件中的文件夹 ,属主机文件系统通过数据卷和容器数据进行联系,你改变我也改变。 解决办法: 对宿主机文件系统内的文件进行修改,会立刻反应…...
QT_day4:对话框
1、完善对话框,点击登录对话框,如果账号和密码匹配,则弹出信息对话框,给出提示”登录成功“,提供一个Ok按钮,用户点击Ok后,关闭登录界面,跳转到其他界面 如果账号和密码不匹配&…...
矢量数据库:连接人工智能应用程序的数据复杂性与可用性的桥梁
关注我的公众号:Halo咯咯 简介 矢量数据库是一种专门设计的数据库,专注于高效地存储、管理和操作矢量数据。与传统数据库处理标量值(如数字、字符串、日期)不同,矢量数据库针对的是那些表现为多维数据点的向量…...
docker:can’t create unix socket /var/run/docker.sock: is a directory
docker:can’t create unix socket /var/run/docker.sock: is a directory 原因:docker.sock不能创建 解决方式: rm -rf /var/run/docker.sock 然后重新启动docker Docker是一种相对使用较简单的容器,我们可以通过以下几种方式获取信息&…...
Qt 图形视图 /图形视图框架坐标系统的设计理念和使用方法
文章目录 概述Qt 坐标系统图形视图的渲染过程Item图形项坐标系Scene场景坐标系View视图坐标系map坐标映射场景坐标转项坐标视图坐标转图形项坐标图形项之间的坐标转换 其他 概述 The Graphics View Coordinate System 图形视图坐标系统是Qt图形视图框架的重要组成部分…...
视频号小店类目资质如何申请?新手看一遍就懂了!
我是电商珠珠 大家在视频号小店后台新增商品的时候,需要先完成类目资质的申请,通过后才可以上架相关商品。 而类目资质分为普通类目和特殊类目,如果你所上架的商品属于开放类目,那么就去按照普通类目资质去申请。 如果是定向准…...
整合SpringSecurity+JWT实现登录认证
一、关于 SpringSecurity 在 Spring Boot 出现之前,SpringSecurity 的使用场景是被另外一个安全管理框架 Shiro 牢牢霸占的,因为相对于 SpringSecurity 来说,SSM 中整合 Shiro 更加轻量级。Spring Boot 出现后,使这一情况情况大有…...
C# Onnx Yolov9 Detect 物体检测
目录 介绍 效果 项目 模型信息 代码 下载 C# Onnx Yolov9 Detect 物体检测 介绍 yolov9 github地址:https://github.com/WongKinYiu/yolov9 Implementation of paper - YOLOv9: Learning What You Want to Learn Using Programmable Gradient Information…...
避坑指南:Dify知识库数据清洗的5个常见错误与正则表达式优化技巧
避坑指南:Dify知识库数据清洗的5个常见错误与正则表达式优化技巧 在企业级知识库构建过程中,数据清洗环节往往成为影响LLM问答质量的关键瓶颈。许多团队投入大量资源进行知识库建设后,仍面临"清洗了数据但召回率低"的困境。本文将揭…...
告别Keil!用VSCode+EIDE插件打造你的STM32开发环境(附ST-LINK V2避坑指南)
从Keil到VSCode:打造高效STM32开发环境的完整指南 在嵌入式开发领域,Keil MDK长期以来一直是STM32开发的主流工具,但它的封闭性、高昂的授权费用和略显陈旧的用户界面让越来越多的开发者开始寻找替代方案。Visual Studio Code(VSC…...
FastJson内存泄漏实战:我是如何用MAT工具定位到IdentityHashMap这个坑的
FastJson内存泄漏深度剖析:从MAT工具实战到IdentityHashMap陷阱破解 凌晨三点,手机突然响起刺耳的告警声——生产环境某核心服务的堆内存使用率突破95%。作为值班工程师,我瞬间清醒过来。这不是普通的OOM,而是一场持续增长的内存…...
NaViL-9B效果实测:支持中英文混排表格图像的行列结构识别与内容提取
NaViL-9B效果实测:支持中英文混排表格图像的行列结构识别与内容提取 1. 模型介绍 NaViL-9B是新一代原生多模态大语言模型,专为处理复杂视觉-语言任务设计。与常规视觉模型不同,它不仅能够理解图片内容,还能精准解析表格、文档等…...
深度解析ViGEmBus:Windows虚拟游戏手柄驱动实战指南
深度解析ViGEmBus:Windows虚拟游戏手柄驱动实战指南 【免费下载链接】ViGEmBus Windows kernel-mode driver emulating well-known USB game controllers. 项目地址: https://gitcode.com/gh_mirrors/vi/ViGEmBus ViGEmBus是一款专业的Windows内核模式驱动&a…...
告别硬编码路径:手把手教你用Go cgo优雅集成第三方C库(Windows/MinGW环境)
告别硬编码路径:用Go cgo优雅集成第三方C库的工程实践 在混合编程的世界里,Go与C/C的联姻既带来了性能红利,也伴随着路径管理的噩梦。当项目需要引用多个第三方库时,硬编码的绝对路径会让构建脚本变得脆弱不堪,团队协作…...
保姆级教程:在Jeecg-Vue3项目中快速集成SuperQuery高级查询组件(含完整配置代码)
Jeecg-Vue3项目实战:SuperQuery高级查询组件深度集成指南 在后台管理系统开发中,高效的数据筛选功能直接影响用户体验和操作效率。Jeecg-Vue3作为企业级快速开发框架,其内置的SuperQuery组件能够帮助开发者快速构建复杂的多条件查询面板。本文…...
CRaxsRat v7.4隐藏功能挖掘:用自定义脚本实现批量设备自动化运维
CRaxsRat v7.4隐藏功能实战:JSON脚本引擎在企业级自动化运维中的高阶应用 在企业IT运维领域,效率提升往往隐藏在工具的高级功能层。CRaxsRat v7.4的脚本模块就像瑞士军刀的隐藏刀片——90%的用户只停留在远程桌面和文件管理的基础功能,却不知…...
F_Record:让Photoshop绘画过程录制变得简单高效的轻量级插件
F_Record:让Photoshop绘画过程录制变得简单高效的轻量级插件 【免费下载链接】F_Record 一款用来录制绘画过程的轻量级PS插件 项目地址: https://gitcode.com/gh_mirrors/fr/F_Record 在数字艺术创作领域,每一笔笔触都承载着创作者的灵感与思考。…...
Vitis新手避坑:自定义IP编译报错?先检查这个Makefile路径!
Vitis新手避坑指南:自定义IP编译报错的核心排查思路 第一次在Vitis中集成自定义IP时遇到编译报错,那种挫败感我至今记忆犹新。明明硬件描述文件(XSA)已经正确生成,软件工程却莫名其妙地报出"xxx.h: No such file …...
