vue3+threejs新手从零开发卡牌游戏(十六):初始化对方手牌

news2024/11/15 7:04:43

添加对方手牌区时注意位置调整,以及手牌应该是背面朝上,加个rotateX翻转即可,其他代码和p1.vue代码一致,game/hand/p2.vue代码如下:

<template>
  <div></div>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
import { useCommonStore } from "@/stores/common.ts"
import { Card } from "@/views/game/Card.ts"
import { CARD_DICT } from "@/utils/dict/card.ts"
import { transPos } from "@/utils/common.ts"

// 引入threejs变量
const {proxy} = getCurrentInstance()
const THREE = proxy['THREE']
const scene = proxy['scene']
const camera = proxy['camera']
const renderer = proxy['renderer']
const TWEEN = proxy['TWEEN']

const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();

const commonStore = useCommonStore()

// 手牌区group
const handGroup = new THREE.Group()
handGroup.name = "p1_handGroup"
scene.add(handGroup)

const _width = ref()

const init = () => {
  setHandPos()
}

// 设置手牌区位置
const setHandPos = () => {
  nextTick(() => {
    let plane = scene.getObjectByName("地面")
    let point1 = transPos(window.innerWidth * 0.35, 10) // 手牌区起始位置的屏幕坐标
    let point2 = transPos(window.innerWidth - 10, 10) // 手牌区结束位置的屏幕坐标
    let x1 = 0 // 手牌区起始位置的世界x坐标
    let x2 = 0 // 手牌区结束位置的世界x坐标
    // 
    raycaster.setFromCamera( point1, camera );
    const intersects1 = raycaster.intersectObject( plane );
    if (intersects1.length > 0) {
      let point = intersects1[0].point
      // 由于卡牌几何体大小设置的是(1, 0.005, 1.4),所以我们对应进行偏移
      // handGroup.position.set(point.x, point.y, point.z)
      handGroup.position.set(point.x + 0.5, point.y, point.z + 0.7)
      x1 = handGroup.position.x
    }
    // 
    raycaster.setFromCamera( point2, camera );
    const intersects = raycaster.intersectObject( plane );
    if (intersects.length > 0) {
      let point = intersects[0].point
      x2 = point.x + 0.5
    }

    // 用绝对值相加得到手牌区长度
    _width.value = Math.abs(x1) + Math.abs(x2)

  })
}

// 添加手牌
const addHandCard = (obj: any, origin: any) => {
  let position = origin.position
  // console.log(666, deckGroupPos)
  let cardObj = CARD_DICT.find((v: any) => v.card_id === obj.card_id)
  if (cardObj) {
    let card = new Card(cardObj)
    let mesh = card.init()
    mesh.name = obj.name
    mesh.position.set(position.x, position.y, position.z)
    mesh.rotateX(180 * (Math.PI / 180)) // 弧度
    mesh.material.forEach((v: any) => {
      // v.alphaHash = true
      v.transparent = true
    })
    scene.add( mesh );
    updateCardPos(mesh)
  }
}

// 移除手牌
const removeHandCard = (mesh: any) => {
  handGroup.remove(mesh)
  // 计算叠放间距
  let space = ((_width.value - 1) / (handGroup.children.length - 1)) <= 1 ? (_width.value - 1) / (handGroup.children.length - 1) : 1
  handGroup.children.forEach((v: any, i: any) => {
    v.position.set(i * space, 0.005 * i, 0)
    v.userData = {
      ...v.userData,
      oriPos: new THREE.Vector3(i * space, 0.005 * i, 0)
    }
  })
}

// 回到手牌原位置
const backPosition = (mesh: any) => {
  let oriPos = mesh.userData.oriPos
  const tw = new TWEEN.Tween({
    x: mesh.position.x,
    y: mesh.position.y,
    z: mesh.position.z,
    mesh
  })
  tw.to({
    x: oriPos.x,
    y: oriPos.y,
    z: oriPos.z,
  }, 200)
  tw.easing(TWEEN.Easing.Quadratic.Out)
  tw.onUpdate((obj: any) => {
    obj.mesh.position.set(obj.x, obj.y, obj.z)
  })
  tw.onComplete(function() {
    TWEEN.remove(tw)
  })
  tw.start();
}

// 更新卡牌位置
const updateCardPos = (mesh: any) => {
  const tw = new TWEEN.Tween({
    x: mesh.position.x,
    y: mesh.position.y,
    z: mesh.position.z,
    opacity: 0.9,
    mesh
  })
  tw.to({
    x: handGroup.position.x,
    y: handGroup.position.y,
    z: handGroup.position.z,
    opacity: 0
  }, 200)
  tw.easing(TWEEN.Easing.Quadratic.Out)
  tw.onUpdate((obj: any) => {
    obj.mesh.position.set(obj.x, obj.y, obj.z)
    obj.mesh.material.forEach((v: any) => {
      v.opacity = obj.opacity
    })
  })
  tw.onComplete(function() {
    //动画结束:关闭允许透明,恢复到模型原来状态
    TWEEN.remove(tw)
    scene.remove( mesh );
    
    mesh.material.forEach((v: any) => {
      v.transparent = false
      v.opacity = 1
    })
    handGroup.add(mesh)
    // 计算叠放间距
    let space = ((_width.value - 1) / (handGroup.children.length - 1)) <= 1 ? (_width.value - 1) / (handGroup.children.length - 1) : 1
    handGroup.children.forEach((v: any, i: any) => {
      v.position.set(i * space, 0.005 * i, 0)
      v.userData = {
        ...v.userData,
        oriPos: new THREE.Vector3(i * space, 0.005 * i, 0)
      }
    })
  })

  tw.start();
}

defineExpose({
  init,
  addHandCard,
  removeHandCard,
  backPosition
})
</script>

<style lang="scss" scoped>
</style>

修改game/hand/index.vue,添加p2添加手牌方法:

<!-- 手牌区 -->
<template>
  <P1 ref="p1Ref"/>
  <P2 ref="p2Ref"/>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
import P1 from "./p1.vue"
import P2 from "./p2.vue"

const p1Ref = ref()
const p2Ref = ref()

onMounted(() => {
  // init()
})

const init = () => {
  p1Ref.value.init()
  p2Ref.value.init()
}

const addP1HandCard = (obj: any, origin: any) => {
  p1Ref.value.addHandCard(obj, origin)
}

const addP2HandCard = (obj: any, origin: any) => {
  p2Ref.value.addHandCard(obj, origin)
}

const removeHandCard = (mesh: any) => {
  p1Ref.value.removeHandCard(mesh)
}

const backPosition = (mesh: any) => {
  p1Ref.value.backPosition(mesh)
}

defineExpose({
  init,
  addP1HandCard,
  addP2HandCard,
  backPosition,
  removeHandCard
})
</script>

<style lang="scss" scoped>
</style>

然后修改下game/index.vue的初始化手牌方法:

// 初始化手牌
const initHand = () => {
  let p1 = new Promise((resolve, reject) => {
    let cardNumber = 4
    let _number = 0
    let p1Deck = JSON.parse(JSON.stringify(commonStore.$state.p1Deck))
    let deckGroup = scene.getObjectByName("p1_deckGroup")
    let position = new THREE.Vector3(0, 0.005 * p1Deck.length, 0)
    let _interval = setInterval(async() => {
      // console.log(123, p1Deck)
      if (_number < cardNumber) {
        let obj = p1Deck[p1Deck.length - 1]
        p1Deck.splice(p1Deck.length-1, 1)
        commonStore.updateP1Deck(p1Deck)
        // 修改卡组
        await editDeckCard(deckGroup, obj, "remove")
        await renderDeckText(deckGroup, `${commonStore.$state.p1Deck.length}`, commonStore.$state._font, position)
        // 手牌区添加手牌
        handRef.value.addP1HandCard(obj, deckGroup)
      } else {
        clearInterval(_interval)
        resolve(true)
      }
      _number++
    }, 200)
  })

  let p2 = new Promise((resolve, reject) => {
    let cardNumber = 4
    let _number = 0
    let p2Deck = JSON.parse(JSON.stringify(commonStore.$state.p2Deck))
    let deckGroup = scene.getObjectByName("p2_deckGroup")
    let position = new THREE.Vector3(0, 0.005 * p2Deck.length, 0)
    let _interval = setInterval(async() => {
      // console.log(123, p1Deck)
      if (_number < cardNumber) {
        let obj = p2Deck[p2Deck.length - 1]
        p2Deck.splice(p2Deck.length-1, 1)
        commonStore.updateP2Deck(p2Deck)
        // 修改卡组
        await editDeckCard(deckGroup, obj, "remove")
        await renderDeckText(deckGroup, `${commonStore.$state.p2Deck.length}`, commonStore.$state._font, position)
        // 手牌区添加手牌
        handRef.value.addP2HandCard(obj, deckGroup)
      } else {
        clearInterval(_interval)
        resolve(true)
      }
      _number++
    }, 200)
  })
  return new Promise((resolve, reject) => {
    Promise.all([p1, p2]).then((res: any) => {
      resolve(true)
    })
  })
}

页面效果如下:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++:关键字(4)

在c中的关键字就是我们各个写的各种代码 这些就是关键字&#xff0c;这些东西是无法当参数的&#xff0c;比如在给变量名设置为int那就不行 这就是个错的 在写其他的参数时候&#xff0c;不可以使用关键词作为参数

Vue 与 React:前端框架对比分析

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

C语言程序编译与链接(拓宽视野的不二之选)

文章目录 翻译环境和运行环境翻译环境预处理编译汇编链接 运行环境 翻译环境和运行环境 1&#xff0c;在ANSI C的任何⼀种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执⾏的机器指 令&#xff08;⼆进制指令&#…

AI Agent(LLM Agent)入门解读

1. 什么是AI Agent&#xff1f; AI Agent可以理解为一个智能体&#xff0c;包括感知模块、规划决策模块和行动模块&#xff0c;类似于人类的五官、大脑和肢体。它能帮助人类处理复杂的任务&#xff0c;并能根据环境反馈进行学习和调整。 五官可以理解为感知模块&#xff0c;大…

2024年上半年数学建模竞赛一览表(附赠12场竞赛的优秀论文+格式要求)[电工、妈杯、数维、五一等12场]

为了帮助大家更好地备战今年上半年十二场数学建模竞赛&#xff0c;我们为大家收集到了这十二场相关竞赛的优秀论文以及格式要求&#xff0c;具体内容如下所示。 资料获取 在文末 文中资料来源 名称竞赛官方网站天府杯https://www.tfmssy.org.cn/认证杯http://www.tzmcm.cn/i…

中国土壤厚度空间分布数据

土壤层次分为覆盖层 林溶层 淀积层 母质层&#xff0c;其中在林溶层中的最上面那层就是我们通常说的土壤厚度在这一层中&#xff0c;这一层也被称为腐殖层&#xff0c;是肥力性质最好的一层&#xff0c;植物根系和微生物也集中在这一层。至于覆盖层在森林土壤中比较常见&#x…

零基础学python之高级编程(6)---Python中进程的Queue 和进程锁,以及进程池的创建 (包含详细注释代码)

Python中进程的Queue 和进程锁,以及进程池的创建 文章目录 Python中进程的Queue 和进程锁,以及进程池的创建前言一、进程间同步通信(Queue)二、进程锁&#xff08;Lock&#xff09;三、创建进程池Poorpool 类方法: End! 前言 大家好,上一篇文章,我们初步接触了进程的概念及其应…

matlab实现神经网络

一、原理 人工神经网络是具有适应性的简单神经元组成的广泛并互连的网络&#xff0c;它的组织能够模拟生物神经系统对真实世界物体作出的交互式反应。人工神经网络具有自学习、自组织、较好的容错性和优良的非线性逼近能力 将神经网络的学习能力引入到模糊系统中&#xff0c;…

Zabbix 配置使用

目录 配置流程 添加组机组 添加模板 添加主机 配置图形 配置大屏 Monitoring 配置地图 最新数据 故障 使用IT服务 使用报表 资产管理 全局搜索 导入导出 用户权限 用户组权限 用户 匿名用户 调试模式 与 LDAP 对接 维护模式 故障确认 批量更新 配置流程…

35.HarmonyOS App(ArkUI)使用父组件@Builder装饰的方法初始化子组件@BuilderParam报错

HarmonyOS App(ArkUI)使用父组件Builder装饰的方法初始化子组件BuilderParam报错 Type void is not assignable to type () > void. <tsCheck> 去掉括号()就可以了 装饰器&#xff1a; 用于装饰类、结构、方法以及变量&#xff0c;并赋予其特殊的含义。如上述示例中En…

fastadmin学习01-windows下安装部署

下载源代码 官网 安装 解压&#xff0c;然后使用phpstorm打开 修改配置文件 创建数据库 -- drop database fastadmin01; create database fastadmin01;这样fastadmin就部署好了 访问主页也能看到前台页面

基于OneAPI+ChatGLM3-6B+FastGPT搭建LLM大语言模型知识库问答系统

搭建大语言模型知识库问答系统 部署OneAPI部署一个LLM模型部署嵌入模型部署FastGPT新建FastGPT对话应用新建 FastGPT 知识库应用 部署OneAPI 拉取镜像 docker pull justsong/one-api创建挂载目录 mkdir -p /usr/local/docker/oneapi启动容器 docker run --name one-api -d …

LeetCode讲解算法2-数据结构[栈和队列](Python版)

文章目录 一、栈1.1 栈的定义1.2 栈的实现分析步骤1.3 栈的应用匹配圆括号匹配符号模2除法&#xff08;十进制转二进制&#xff09;进制转换 二、队列2.1 单向队列2.2 双端队列2.3 队列的应用验证回文串滑动窗口最大值 一、栈 1.1 栈的定义 栈是一种线性数据结构&#xff0c;栈…

STM32的SPI通信介绍

SPI简介 SPI:串行外设接口,与IIC一样都是通用数据总线。四根通信线&#xff1a;SCK&#xff0c;MOSI&#xff08;DO&#xff09;&#xff0c;MISO&#xff08;DI&#xff09;&#xff0c;SS。同步&#xff08;共用一根时钟线&#xff09;&#xff0c;全双工&#xff08;数据发…

js算法记录

> 更多请前往 https://www.passerma.com/article/86 滑动窗口 1 给定一个矩阵&#xff0c;包含N*M个整数&#xff0c;和一个包含K个整数的数组。现在要求在这个矩阵中找一个宽度最小的子矩阵&#xff0c;要求子矩阵包含数组中所有的整数 function minSubmatrixWidth(mat…

Openstack创建和操作实例,实现与外部网络通信

一、熟悉OpenStack图形界面操作 1、了解Horizon项目 Horizon项目 各OpenStack服务的图形界面都是由Horizon提供的。Horizon提供基于Web的模块化用户界面。Horizon为云管理员提供一个整体的视图。Horizon为终端用户提供一个自主服务的门户。Horizon由云管理员进行管理与控制&a…

SpringBoot学习笔记一、SpringBoot应用初创建以及应用

一、创建SpringBoot的两种方式 1.Spring Initializr方式创建 &#xff08;1&#xff09;第一步在IDEA中选择 File-->NEW-->Project &#xff0c;选择 Spring Initializr &#xff0c;指定Maven坐标、包名、指定 JDK 版本 1.8 &#xff0c;然后点击Next 。如下图&#x…

修改Jupyter Notebook的默认路径,以及在PowerShell中自定义其启动路径

修改Jupyter Notebook的默认路径&#xff0c;以及在PowerShell中自定义其启动路径 设置 Jupyter Notebook 配置文件&#xff0c;修改默认路径要在PowerShell中设置自定义的启动脚本&#xff0c;以确保Jupyter Notebook能够自动定位到当前路径设置后的效果 在使用Jupyter Notebo…

遇到了问题,Firepower 2140配置带外IP时报错 commit-buffer failed

onsite we have a cisco firepower 2140 device which run ASA as we try to modify the 2140 OOB mgmt ip by CLI, we got an error why ? 经过查询发现&#xff0c;需要进入ASA里面打上以下这条命令&#xff0c;并重启ASA 1 修改模式并重启 ciscoasa# configure termina…

C语言例4-15:从键盘输入一个整数,求其绝对值并输出。

代码如下&#xff1a; //从键盘输入一个整数&#xff0c;求其绝对值并输出。 #include<stdio.h> int main(void) {int n;printf("输出一个整数&#xff1a; \n");scanf("%d",&n); //从键盘输入一个整数保存至变量nif(n<0) //…