接上一节实现画线后,现在可以根据鼠标移动位置判断是否选中了对方区域怪兽卡牌:
修改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 = nullreturn}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 = truev.opacity = obj.opacityv.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.valuelet _selectedTargetCard: any = selectedTargetCard.valueif (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 = nullselectedTargetCard.value = null}
}
页面效果如下: