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) // 己方HP
  const p2HP = ref(4000) // 对方HP

  const 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
  }

  // 更新己方HP
  function updateP1HP(data: any) {
    p1HP.value = data
  }

  // 更新对方HP
  function 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.value
    let _selectedTargetCard: any = selectedTargetCard.value
    if (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.areaType
      let isP1 = areaType.indexOf("己方") > -1
      let graveyardGroup = null
      let graveyardGroupPos = new THREE.Vector3(0, 0, 0)
      let cards = []
      card.material.forEach((v: any) => {
        v.transparent = false
        v.opacity = 1
        v.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 = null
    selectedTargetCard.value = null
  } else if (selectedCard.value) { // 如果只存在selectedCard
    // 直接攻击
    let _selectedCard: any = selectedCard.value
    console.log(333, _selectedCard)
    if (selectedCard.value.name === "攻击力") {
      _selectedCard = _selectedCard.value.parent
    }
    let areaType = _selectedCard.userData.areaType
    let isP1 = areaType.indexOf("己方") > -1
    let 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.ATK
        if (p2HP < 0) {
          p2HP = 0
        }
        commonStore.updateP2HP(p2HP)
      } else {
        let p1HP = JSON.parse(JSON.stringify(commonStore.$state.p1HP))
        p1HP -= _selectedCard.userData.ATK
        if (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("己方") > -1
  let mesh = null
  if (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 }

页面效果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/500912.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

4、Cocos Creator 动画系统

目录 1、Clip 参数 2、动画编辑器 3、基本操作 更改时间轴缩放比例 移动显示区域 更改当前选中的时间轴节点 播放 / 暂停动画 修改 clip 属性 快捷键 4、模拟实验 5、动画事件 6、注意事项 参考 Animation 组件是节点上的一个组件。Clip 动画剪辑就是一份动画的声…

【edge浏览器无法登录某些网站,以及迅雷插件无法生效的解决办法】

edge浏览器无法登录某些网站&#xff0c;以及迅雷插件无法生效的解决办法 edge浏览器无法登录某些网站&#xff0c;但chrome浏览器可以登录浏览器插件无法使用&#xff0c;比如迅雷如果重装插件重装浏览器重装迅雷后仍然出现问题 edge浏览器无法登录某些网站&#xff0c;但chro…

InfoNCE loss

InfoNCE loss是一种用于自监督学习的损失函数&#xff0c;通常用于训练对比学习模型&#xff0c;如自编码器或神经网络。全称是"InfoNCE: Contrastive Estimation of Neural Entropy"&#xff0c;基于对比学习的思想&#xff0c;旨在最大化正样本的相似性&#xff0c…

QSplashScreen

以前打红警的时候进入游戏界面会有一个启动界面&#xff0c;比如美国是有伞兵&#xff0c;英国有狙击手&#xff0c;韩国有黑鹰战机的一些介绍&#xff0c;这些就是启动界面&#xff0c;就是由QSplashScreen这个类来实现的。 QSplashScreen 是 Qt 框架中的一个类&#xff0c;用…

【每日跟读】常用英语500句(300~400)

【每日跟读】常用英语500句 I had to take a shower. 我洗了个澡 Go on in. 赶紧进去吧 Hold up. 等一下 They seem like nice people. 他们看起来像好人 Such a wonderful age. 如此美好的年纪 That’s very impressive. 真厉害 I can see that. 看得出来 You should …

绘制多个box箱型图

1.首先生成随机数据 import random # 创建一个153629行&#xff0c;13列的数据&#xff0c; random_data np.random.randn(153629, 13) #创建数据标签&#xff0c;后续将根据数据标签绘制不同的箱型图 label [0,1,2,3] labels np.asarray(random.choices(label,k 153629))…

U盘文件突然消失:原因分析与恢复策略

U盘遭遇“幽灵”之手&#xff0c;文件不翼而飞 你是否曾遭遇过这样的诡异情况&#xff1a;前一天还好好存放在U盘里的文件&#xff0c;第二天却突然消失得无影无踪&#xff1f;这简直就像是一场无声的灾难&#xff0c;令人措手不及。U盘作为我们日常工作和生活中不可或缺的数据…

Gitea 的详细介绍

什么是 Gitea&#xff1f; Gitea 是一个开源、轻量级的自托管 Git 服务&#xff0c;它允许用户搭建类似于 GitHub 或 GitLab 的代码托管平台。由于采用 Go 语言开发&#xff0c;Gitea 具有高效的性能和跨平台特性&#xff0c;适合个人开发者或小团队使用。 Gitea 的特点 轻量…

java数据结构与算法刷题-----LeetCode34. 在排序数组中查找元素的第一个和最后一个位置

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 二分查找 二分查找 解题思路&#xff1a;时间复杂度O( l o g 2 …

读所罗门的密码笔记05_新的力量平衡

1. 技术发展 1.1. 美国和中国俨然成为人工智能研究、开发和部署方面无可争议的领导者 1.2. 人类从未彻底阻止某种技术的发展&#xff0c;虽然不同国家在不同时间对克隆、化学武器和核武器等技术采取了暂停或禁止措施&#xff0c;但我们仍在继续推进最前…

mysql之MyBatis核心工作原理

MyBatis核心工作原理 一、源码环境 1.手动编译源码 工欲善其事必先利其器。为了方便我们在看源码的过程中能够方便的添加注释&#xff0c;我们可以自己来从官网下载源码编译生成对应的Jar包&#xff0c;然后上传到本地maven仓库&#xff0c;再引用这个Jar。大家可以自行去官…

第十四届蓝桥杯第十题:蜗牛分享

问题描述 输入格式 输出格式 输出共一行&#xff0c;一个浮点数表示答案&#xff08;四舍五入保留两位小数&#xff09;。 样例输入 3 1 10 11 1 1 2 1样例输出 4.20样例说明 蜗牛路线&#xff1a;(0,0)→(1,0)→(1,1)→(10,1)→(10,0)→(11,0)(0,0)→(1,0)→(1,1)→(10,1…

vsqt更改ui,cpp报错(唯二)解决方法,及ui界面布局在cpp文件的运用基本流程

qt的ui布局界面如下 点cpp文件->编译 此时就会自动生成ui_xxx.h 这里是ui文件里面就有类名&#xff1a;Ui_文件名字 下面就有一个类继承于这个类 你所使用的这个ui指针&#xff0c;就这么来的 ***报错解决方法有两种&#xff1a;***第一种&#xff1a;如果改了ui&#x…

Qt+OpenGL入门教程(三)——绘制三角形

通过前两篇文章的学习&#xff0c;我想大家应该有了基本的理解&#xff0c;我们接下来实操一下。 创建Qt OpenGL窗口 QOpenGLWidget QGLWidget是传统QtOpenGL模块的一部分&#xff0c;与其他QGL类一样&#xff0c;应该在新的应用程序中避免使用。相反&#xff0c;从Qt5.4开始…

(五)ROS2学习--创建调用其它包接口的一个发布者

这里写自定义目录标题 一、背景二、构建步骤1. 构建项目包2. 创建消息接口3. 修改“package.xml”4. 修改"src/smart_car/CMakeLists.txt"5. 创建发布者程序 三、编译及验证1. 编译2. 验证 一、背景 主机&#xff1a;Ubuntu20.04 介绍&#xff1a;基于上一篇&#x…

超卖问题的 4 种解决方案来了,太硬核了

大家好&#xff0c;我是路人&#xff0c;最近刚推出的《Java 高并发 & 微服务 & 性能调优实战案例 100 讲》&#xff0c;此课程目前已发布上线&#xff0c;正在连载中&#xff0c;文末有观看方法。 所有案例均源于个人工作实战&#xff0c;均提供原理讲解 & 亲手敲…

手写SpringBoot(三)之自动配置

系列文章目录 手写SpringBoot&#xff08;一&#xff09;之简易版SpringBoot 手写SpringBoot&#xff08;二&#xff09;之动态切换Servlet容器 手写SpringBoot&#xff08;三&#xff09;之自动配置 手写SpringBoot&#xff08;四&#xff09;之bean动态加载 手写SpringBoot…

重构销售话术和知识库,容联云找到了大模型的“钉子”

科技云报道原创。 从ChatGPT诞生起&#xff0c;大模型在营销、客服等场景的落地就被予以众望。然而在经历了一年多的“百模大战”洗礼之后&#xff0c;人们发现无论是算力成本还是内容生成的安全合规问题&#xff0c;都让大模型很难直接应用于机器与人对话的实际业务中。 这其…

linux基础命令篇:Linux基础命令讲解——文件浏览(cat、less、head、tail和grep)

Linux基础命令讲解——文件浏览&#xff08;cat、less、head、tail和grep&#xff09; 本文详细介绍Linux中的cat、less、head、tail和grep命令&#xff0c;这些命令在日常工作中非常实用&#xff0c;以下是关于这些命令的详细介绍&#xff1a; 1. cat命令&#xff1a;用于查看…

光伏发电量预测(Python代码,CNN结合LSTM,TensorFlow框架)

1.数据集&#xff08;开始位置&#xff09;&#xff0c;数据集免费下载链接&#xff1a;https://download.csdn.net/download/qq_40840797/89051099 数据集一共8列&#xff0c;第一列是时间&#xff0c;特征列一共有6列&#xff1a;"WindSpeed" - 风速 "Sunshi…