vue3+threejs新手从零开发卡牌游戏(十二):优化手牌上场逻辑

上一节中实现了手牌移到场上的基本逻辑,这节优化下细节:

1.dialog添加取消事件,点击取消时,卡牌回到原手牌区位置

2.卡牌拖放位置不在己方战域内时,卡牌回到原手牌区位置

3.卡牌上场时,按213顺序放置,即中央、左侧、右侧顺序放置

4.卡牌上场时,卡牌从手牌区移除并重新计算手牌区中手牌的叠放位置

首先在game/hand/po1.vue添加一个回到手牌原位置的方法:

// 回到手牌原位置
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();
}

其中oriPos是在计算叠放位置时,将位置存入了userData中,方便退回原位:

// 计算叠放间距
    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)
      }
    })

然后把backPosition方法暴露出去,在game/index.vue中使用:
修改game/index.vue的手牌事件:


// 手牌事件
const onHandEvent = () => {
  let handGroup = scene.getObjectByName("p1_handGroup")
  const dragControls = new DragControls( handGroup.children, camera, renderer.domElement );

  dragControls.addEventListener( 'dragstart', function ( event: any ) {
    event.object.position.y += 0.04
  });

  dragControls.addEventListener( 'drag', function ( event: any ) {
    event.object.position.y += 0.04
    
  });

  dragControls.addEventListener( 'dragend', function ( event: any ) {
    event.object.position.y -= 0.04
    let p1SitePlane = scene.getObjectByName("己方战域Plane")
    let point = transPos(pointer.x, pointer.y)
    // 通过摄像机和鼠标位置更新射线
    raycaster.setFromCamera( point, camera );
    const intersects = raycaster.intersectObjects(p1SitePlane.children);
    if (intersects.length > 0) {
      dialogRef.value.init({
        type: "handToSite",
        obj: event.object,
        message: "是否上场该卡牌"
      })
    } else {
      handRef.value.backPosition(event.object)
    }
  });
}

之后我们给Dialog添加取消事件(这里调用了手牌返回原位置的方法):
components/Dialog.vue代码如下:

<template>
  <el-dialog
    v-model="dialogVisible"
    title="Tips"
    width="75%"
    :show-close="false"
  >
    <span>{{ state.message }}</span>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="onCancel">取消</el-button>
        <el-button type="primary" @click="onConfirm">
          确定
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'

const emits = defineEmits(["handToSite", "onCancel"])

const dialogVisible = ref(false)

const state = reactive({
  type: "",
  message: "",
  obj: ""
})

const init = (data: any) => {
  state.type = data.type
  state.message = data.message
  state.obj = data.obj
  nextTick(() => {
    dialogVisible.value = true
  })
}

// 确定
const onConfirm = () => {
  dialogVisible.value = false
  if (state.type === "handToSite") {
    emits("handToSite", state.obj)
  }
}

// 取消
const onCancel = () => {
  dialogVisible.value = false
  if (state.type === "handToSite") {
    // 返回原位置
    emits("onCancel", state.obj)
  }
}

defineExpose({
  init
})
</script>

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

然后就可以在game/index.vue中使用了:

<template>
  ...
  <!-- 对话框 -->
  <Dialog ref="dialogRef" @handToSite="handToSite" @onCancel="onCancel"/>
</template>

<script setup lang="ts">
...
// 取消
const onCancel = (data: any) => {
  handRef.value.backPosition(data)
}
</script>

然后我们修改下game/index.vue手牌上场逻辑,按213顺序上场:

// 手牌移入己方战域
const handToSite = (data: any) => {
  let sitePlane = scene.getObjectByName("己方战域Plane")
  console.log(data)
  let userData = data.userData
  if (userData.type === "怪兽") {
    let meshes = sitePlane.children.filter((v: any) => v.name.indexOf("己方怪兽区") > -1 && v.userData.empty === true)
    if (meshes.length > 0) {
      let _mesh = null
      let m1 = meshes.find((v: any) => v.name.indexOf(1) > -1)
      let m2 = meshes.find((v: any) => v.name.indexOf(2) > -1)
      let m3 = meshes.find((v: any) => v.name.indexOf(3) > -1)
      if (m2) {
        _mesh = m2
      } else if (m1) {
        _mesh = m1
      } else if (m3) {
        _mesh = m3
      }
      renderSiteCard(data, _mesh)
    }
  }
}

// 绘制场上卡牌
const renderSiteCard = (data: any, mesh: any) => {
  let p1_handGroup = scene.getObjectByName("p1_handGroup")
  let position = new THREE.Vector3(0, 0, 0)
  mesh.getWorldPosition(position)
  mesh.userData.empty = false
  let oldMesh = p1_handGroup.children.find((v: any) => v.name === data.name)
  let newMesh = oldMesh.clone()
  newMesh.userData.areaType = mesh.name // 用来记录卡牌在哪个区域,怪兽区、墓地、手牌、卡组、场地等
  newMesh.scale.set(0.8, 0.8, 0.8)
  handRef.value.removeHandCard(oldMesh)
  scene.add(newMesh)
  newMesh.position.set(position.x, position.y, position.z)
}

在game/hand/p1.vue中添加removeHandCard移除手牌方法,这里重新计算了叠放位置:

// 移除手牌
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)
    }
  })
}

页面效果如下:

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清岚_lxn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值