上一节中实现了手牌移到场上的基本逻辑,这节优化下细节:
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)
}
})
}
页面效果如下: