接上一节实现画线后,现在可以根据鼠标移动位置判断是否选中了对方区域怪兽卡牌:
修改game/index.vue代码,在画线方法中添加获取目标对象方法:
const selectedCard = ref() // 选中的场上card
const selectedTargetCard = ref() // 选中的目标场上card
// 画线
const drawLine = () => {
...
// 获取目标对象
getTargetMesh()
}
// 获取目标对象
const getTargetMesh = () => {
let p2Cards = scene.children.filter((v: any) => v.userData?.areaType?.indexOf("对方怪兽区") > -1)
if (p2Cards.length <= 0) {
selectedTargetCard.value = null
return
}
let arr = raycaster.intersectObjects(p2Cards, true)
if (arr.length > 0) {
selectedTargetCard.value = arr[0].object
} else {
selectedTargetCard.value = null
}
}
然后我们设计卡牌战斗逻辑:
1.卡牌移动到对方卡牌的位置进行碰撞,然后退回到原位置
2.两张卡牌比较大小,攻击力高的存活,攻击力低的被破坏
我们在utils/common.ts添加卡牌攻击和卡牌被破坏的动效(这里就是简单修改了卡牌的透明度):
// 卡牌攻击特效
const cardAttack = (card1: any, card2: any, callback: any) => {
// 获取card1世界坐标
let pos1 = new THREE.Vector3(0, 0, 0)
card1.getWorldPosition(pos1)
// 获取card2世界坐标
let pos2 = new THREE.Vector3(0, 0, 0)
card2.getWorldPosition(pos2)
// 动画1:移动到对方卡面前
const twA = new TWEEN.Tween({
x: pos1.x,
y: pos1.y,
z: pos1.z,
card1,
})
twA.to({
x: pos2.x,
y: pos2.y + 0.1,
z: pos2.z + 1.4 * 0.8,
}, 300)
twA.easing(TWEEN.Easing.Quadratic.Out)
twA.onUpdate((obj: any) => {
obj.card1.position.set(obj.x, obj.y, obj.z)
})
twA.onComplete(function() {
//动画结束:关闭允许透明,恢复到模型原来状态
TWEEN.remove(twA)
callback && callback()
})
// 动画2:退回到原位置
const twB = new TWEEN.Tween({
x: pos2.x,
y: pos2.y + 0.1,
z: pos2.z + 1.4 * 0.8,
card1,
})
twB.to({
x: pos1.x,
y: pos1.y,
z: pos1.z,
}, 400)
twB.easing(TWEEN.Easing.Quadratic.In)
twB.onUpdate((obj: any) => {
obj.card1.position.set(obj.x, obj.y, obj.z)
})
twB.onComplete(function() {
//动画结束:关闭允许透明,恢复到模型原来状态
// TWEEN.remove(twA)
// callback && callback()
})
twA.chain(twB)
twA.start();
}
export { cardAttack }
// 卡牌被破坏动效
const cardDestroy = (card: any, callback: any) => {
const tw = new TWEEN.Tween({
opacity: 0.5,
card
})
tw.to({opacity: 0.0}, 500)
tw.easing(TWEEN.Easing.Quadratic.InOut)
tw.onUpdate((obj: any) => {
if (obj.card?.material && obj.card.material.length > 0) {
obj.card.material.forEach((v: any) => {
v.transparent = true
v.opacity = obj.opacity
v.alphaTest = 0.1;
})
}
})
tw.onComplete(function() {
//动画结束:关闭允许透明,恢复到模型原来状态
callback && callback()
})
tw.start();
}
export { cardDestroy }
然后我们在game/index.vue代码中引用(里面有一个判断mesh名称是否是攻击力,这个攻击力其实指的是卡牌的攻击力文字,因为鼠标移动也可能选到卡牌的文字上,所以需要找到它的parent即可以找到对应的卡牌):
// 鼠标抬起事件
const onMouseup = (ev: any) => {
...
fight()
}
// 战斗
const fight = () => {
if (selectedCard.value && selectedTargetCard.value) {
let _selectedCard: any = selectedCard.value
let _selectedTargetCard: any = selectedTargetCard.value
if (selectedCard.value.name === "攻击力") {
_selectedCard = _selectedCard.value.parent
}
if (selectedTargetCard.value.name === "攻击力") {
_selectedTargetCard = _selectedTargetCard.value.parent
}
// 移除卡牌
function removeCard(card: any) {
if (card.children && card.children.length > 0) {
card.children.forEach((v: any) => {
card.remove(v)
})
}
scene.remove(card)
}
cardAttack(_selectedCard, _selectedTargetCard, () => {
console.log(888, Number(_selectedCard.userData.ATK), Number(_selectedTargetCard.userData.ATK))
if (Number(_selectedCard.userData.ATK) > Number(_selectedTargetCard.userData.ATK)) {
cardDestroy(_selectedTargetCard, () => {
removeCard(_selectedTargetCard)
})
} else if (Number(_selectedCard.userData.ATK) === Number(_selectedTargetCard.userData.ATK)) {
cardDestroy(_selectedCard, () => {
removeCard(_selectedCard)
})
cardDestroy(_selectedTargetCard, () => {
removeCard(_selectedTargetCard)
})
} else {
cardDestroy(_selectedCard, () => {
removeCard(_selectedCard)
})
}
})
selectedCard.value = null
selectedTargetCard.value = null
}
}
页面效果如下: