首先为了方便,我们修改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)
调整后效果如下: