vue3+threejs新手从零开发卡牌游戏(五):在scene场景中添加手牌区

首先为了方便,我们修改camera相机的位置,将它从正上方往下看,x和z轴值设为0即可

camera.position.set( 0, 6.5, 0 );

此时刷新页面,看到的效果应该是这样的:

然后我想在最下方的一片区域创建手牌区、卡组区、生命值等信息,如下图红框中所示:

接下来我们一步步实现:

第一步:首先在game目录下新建hand文件夹,里面的index.vue是入口文件,这里我设计的是将玩家1(player1)和玩家2(player2)手牌区分别建立,对应就是p1.vue和p2.vue,当然这个demo里的玩家2其实指的是电脑操控,而不是真的联网和别人对战

这里我们先不考虑玩家2的手牌区,那么hand/index.vue代码如下:

<!-- 手牌区 -->
<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()
}
</script>

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

之后在p1.vue中,我们通过new一个Group来代表手牌区,然后我们在手牌区渲染4张卡牌,这里我们写了一个测试卡组列表,里面保存的是各个卡牌对应的card_id,代码如下:

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

<script setup lang="ts">
import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
import { Card } from "@/views/game/Card.ts"
import { CARD_DICT } from "@/utils/dict/card.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']

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

// 测试卡组
const deckList = [
  "YZ-01",
  "YZ-02",
  "YZ-03",
  "YZ-04",
]

const init = () => {
  deckList.forEach((v: any, i: any) => {
    let obj = CARD_DICT.find((b: any) => b.card_id === v)
    if (obj) {
      let card = new Card(obj)
      let mesh = card.init()
      mesh.position.set(i*1, 0.02 * i, 0)
      // mesh.position.set(i*0.4, 0.02 * i, 0)
      handGroup.add( mesh );
    }
  })
}

defineExpose({
  init
})
</script>

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

刷新页面效果如下(之前在game/index.vue中测试用的renderCard方法可以删掉了):

我们看到手牌区已经添加进scene场景中了,现在我们需要把手牌区移动到左下角,这里我自己用的方式是:

1.先确定左下角的屏幕坐标(0, window.innerHeight)

2.将屏幕坐标转换成世界坐标

3.之后在scene中添加一个底层的地面PlaneGeometry,将世界坐标和相机传入光线投射Raycaster中,然后获取射线与地面的相交点,这个相交点的位置就是手牌区Group的位置,p1.vue完整代码如下:

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

<script setup lang="ts">
import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
import { Card } from "@/views/game/Card.ts"
import { CARD_DICT } from "@/utils/dict/card.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();

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

// 测试卡组
const deckList = [
  "YZ-01",
  "YZ-02",
  "YZ-03",
  "YZ-04",
]

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

  deckList.forEach((v: any, i: any) => {
    let obj = CARD_DICT.find((b: any) => b.card_id === v)
    if (obj) {
      let card = new Card(obj)
      let mesh = card.init()
      mesh.position.set(i * 1, 0.02 * i, 0)
      // mesh.position.set(i*0.4, 0.02 * i, 0)
      handGroup.add( mesh );
    }
  })

}

// scene中添加plane几何体
const addPlane = () => {
  const geometry = new THREE.PlaneGeometry( 20, 20);
  const material = new THREE.MeshBasicMaterial( {
    color: new THREE.Color("gray"), 
    side: THREE.FrontSide, 
    alphaHash: true,
    // alphaTest: 0,
    opacity: 0
  } );
  const plane = new THREE.Mesh( geometry, material );
  plane.rotateX(-90 * (Math.PI / 180)) // 弧度
  plane.name = "地面"
  scene.add( plane );
}

// 设置手牌区位置
const setHandPos = () => {
  nextTick(() => {
    let x = 0
    let y = window.innerHeight
    pointer.x = ( x / window.innerWidth ) * 2 - 1;
    pointer.y = - ( y / window.innerHeight ) * 2 + 1;

    raycaster.setFromCamera( pointer, camera );
    let plane = scene.getObjectByName("地面")
    const intersects = raycaster.intersectObject( plane );
    if (intersects.length > 0) {
      let point = intersects[0].point
      // 由于卡牌几何体大小设置的是(1, 0.02, 1.4),所以我们对应进行偏移
      handGroup.position.set(point.x, point.y, point.z)
      // handGroup.position.set(point.x + 0.5, point.y, point.z - 0.7)
    }
    console.log(55,intersects)
  })
}

defineExpose({
  init
})
</script>

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

刷新页面后效果如下:

我们看到手牌区Group已经移动到左下角了,但由于中心点问题,我们需要再进行偏移下:

handGroup.position.set(point.x + 0.5, point.y, point.z - 0.7)

调整后效果如下:

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

清岚_lxn

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

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

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

打赏作者

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

抵扣说明:

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

余额充值