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

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

javascript 复制代码
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代码如下:

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()
}
</script>

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

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

javascript 复制代码
<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完整代码如下:

javascript 复制代码
<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已经移动到左下角了,但由于中心点问题,我们需要再进行偏移下:

javascript 复制代码
handGroup.position.set(point.x + 0.5, point.y, point.z - 0.7)

调整后效果如下:

相关推荐
ekskef_sef20 分钟前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine64144 分钟前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻1 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云1 小时前
npm淘宝镜像
前端·npm·node.js
dz88i81 小时前
修改npm镜像源
前端·npm·node.js
Jiaberrr1 小时前
解锁 GitBook 的奥秘:从入门到精通之旅
前端·gitbook
程序员_三木2 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
顾平安2 小时前
Promise/A+ 规范 - 中文版本
前端
聚名网2 小时前
域名和服务器是什么?域名和服务器是什么关系?
服务器·前端
桃园码工3 小时前
4-Gin HTML 模板渲染 --[Gin 框架入门精讲与实战案例]
前端·html·gin·模板渲染