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

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

javascript 复制代码
<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添加手牌方法:

javascript 复制代码
<!-- 手牌区 -->
<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的初始化手牌方法:

javascript 复制代码
// 初始化手牌
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)
    })
  })
}

页面效果如下:

相关推荐
浮华似水15 分钟前
简洁之道 - React Hook Form
前端
幻狐boke42 分钟前
【游戏模组】星际争霸1代模组燃烧之地,泰伦帝国对决UED。特效华丽兵种巨多特别好玩
游戏
正小安2 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch4 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光4 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   4 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   4 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web4 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常4 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇5 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器