vue3+threejs新手从零开发卡牌游戏(二十):添加卡牌被破坏进入墓地逻辑

在game目录下新建graveyard文件夹存放墓地相关代码:

game/graveyard/p1.vue,这里主要设置了墓地group的位置:

<template><div></div>
</template><script setup lang="ts">
import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
import { useCommonStore } from "@/stores/common.ts"
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
import { Card } from "@/views/game/Card.ts"
import { CARD_DICT } from "@/utils/dict/card.ts"
import { transPos, renderDeckText } from "@/utils/common.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();const commonStore = useCommonStore()// 墓地group
const deckGroup = new THREE.Group()
deckGroup.name = "p1_graveyardGroup"
scene.add(deckGroup)const init = () => {setDeckPos()
}// 设置墓地位置
const setDeckPos = () => {nextTick(() => {let sitePlane = scene.getObjectByName("己方战域Plane")let siteDeckMesh = sitePlane.getObjectByName("己方墓地")let pos = new THREE.Vector3(0, 0, 0)siteDeckMesh.getWorldPosition(pos)deckGroup.position.set(pos.x, pos.y, pos.z)deckGroup.userData["position"] = posdeckGroup.scale.set(0.8, 0.8, 0.8) // 缩放})
}defineExpose({init,
})
</script><style lang="scss" scoped>
</style>

然后在utils/common.ts文件中添加修改墓地和渲染墓地文字的方法(参考卡组相关方法):


// 修改墓地
const editGraveyardCard = (group:any, mesh: any, type: any) => {return new Promise((resolve, reject) => {let text = group.children.find((v: any) => v.name === "墓地数量")let shadowText = group.children.find((v: any) => v.name === "墓地数量阴影")// console.log(22, group.children, commonStore.$state.p1Deck)if (type === "remove") { // 删除墓地中的卡牌let child = group.children.find((v: any) => v.name === mesh.name)if (child) {group.remove(child)}}group.remove(text)group.remove(shadowText)group.children.forEach((v: any, i: any) => {v.position.set(0, 0.005 * i, 0)})resolve(true)})
}
export { editGraveyardCard }// 渲染墓地文字
const renderGraveyardText = (group: any, text: any, font: any, position: any) => {return new Promise((resolve, reject) => {const geometry = new TextGeometry( `${text}`, {font,size: 0.4,height: 0,curveSegments: 4,bevelEnabled: true,bevelThickness: 0,bevelSize: 0,bevelSegments: 0});geometry.center()const material = new THREE.MeshBasicMaterial({ color: new THREE.Color("white"),alphaHash: true})const mesh = new THREE.Mesh( geometry, material ) ;mesh.position.set(position.x, position.y + 0.01, position.z)// mesh.position.set(0, 0.005 * commonStore.$state.p1Deck.length + 0.01, 0)mesh.rotateX(-90 * (Math.PI / 180)) // 弧度mesh.name = "墓地数量"// 阴影let shadowGeometry = geometry.clone()shadowGeometry.translate(0.02, 0.02, 0);let shadowMaterial = new THREE.MeshBasicMaterial({color: new THREE.Color("black"),alphaHash: true});let shadowMesh = new THREE.Mesh(shadowGeometry, shadowMaterial);shadowMesh.position.set(position.x, position.y, position.z)// shadowMesh.position.set(0, 0.005 * commonStore.$state.p1Deck.length, 0)shadowMesh.rotateX(-90 * (Math.PI / 180)) // 弧度shadowMesh.name = "墓地数量阴影"group.add(mesh)group.add(shadowMesh)resolve(true)})
}
export { renderGraveyardText }

然后我们修改game/index.vue中的fight战斗方法,卡牌被破坏后不直接将其从scene中移除,而且改为进入墓地:

// 战斗
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}// 移除卡牌const removeCard = async (card: any) => {if (card.children && card.children.length > 0) {card.children.forEach((v: any) => {card.remove(v)})}console.log(321, card)let areaType = card.userData.areaTypelet isP1 = areaType.indexOf("己方") > -1let graveyardGroup = nulllet graveyardGroupPos = new THREE.Vector3(0, 0, 0)let cards = []card.material.forEach((v: any) => {v.transparent = falsev.opacity = 1v.alphaTest = 0.1;})card.rotateX(180 * (Math.PI / 180)) // 弧度if (isP1) {card.userData.areaType = "己方墓地"graveyardGroup = scene.getObjectByName("p1_graveyardGroup")graveyardGroup.getWorldPosition(graveyardGroupPos)card.position.set(graveyardGroupPos.x, graveyardGroupPos.y, graveyardGroupPos.z)cards = scene.children.filter((v: any) => v.userData?.areaType === "己方墓地")} else {card.userData.areaType = "对方墓地"graveyardGroup = scene.getObjectByName("p2_graveyardGroup")graveyardGroup.getWorldPosition(graveyardGroupPos)card.position.set(graveyardGroupPos.x, graveyardGroupPos.y, graveyardGroupPos.z)cards = scene.children.filter((v: any) => v.userData?.areaType === "对方墓地")}// 修改墓地let position = new THREE.Vector3(0, 0.005 * cards.length, 0)await editGraveyardCard(graveyardGroup, card, "remove")await renderGraveyardText(graveyardGroup, `${cards.length}`, commonStore.$state._font, position)if (isP1) {let sitePlane = scene.getObjectByName("己方战域Plane")let mesh = sitePlane.children.find((v: any) => v.name === areaType)if (mesh) {mesh.userData.empty = true}} else {let sitePlane = scene.getObjectByName("对方战域Plane")let mesh = sitePlane.children.find((v: any) => v.name === areaType)if (mesh) {mesh.userData.empty = true}}}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}
}

页面效果如下:

附:
game/index.vue完整代码:

<template><div ref="sceneRef" class="scene"></div><!-- 玩家区 --><Player ref="playerRef"/><!-- 手牌 --><Hand ref="handRef"/><!-- 卡组 --><Deck ref="deckRef"/><!-- 战域 --><Site ref="siteRef"/><!-- 墓地 --><Graveyard ref="graveyardRef"/><!-- 抽卡逻辑 --><DrawCard ref="drawCardRef" :handRef="handRef"/><!-- 对话框 --><Dialog ref="dialogRef" @handToSite="handToSite" @onCancel="onCancel"/>
</template><script setup lang="ts">
import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // 轨道控制器
import { DragControls } from 'three/addons/controls/DragControls.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { GammaCorrectionShader } from 'three/examples/jsm/shaders/GammaCorrectionShader.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
import { FontLoader } from 'three/addons/loaders/FontLoader.js';
import { useCommonStore } from "@/stores/common.ts"
import { transPos, editDeckCard, renderDeckText, editGraveyardCard, renderGraveyardText, renderSiteCardText, cardAttack, cardDestroy } from "@/utils/common.ts"
import { Card } from "./Card.ts"
import { CARD_DICT } from "@/utils/dict/card.ts"
import { p1TestDeck, p2TestDeck} from "@/utils/deck/test.ts"
import Hand from "./hand/index.vue"
import Deck from "./deck/index.vue"
import Site from "./site/index.vue"
import Player from "./player/index.vue"
import Graveyard from "./graveyard/index.vue"
import DrawCard from "@/components/DrawCard.vue"
import Dialog from "@/components/Dialog.vue"
import { resolve } from 'dns';// 引入threejs变量
const {proxy} = getCurrentInstance()
const THREE = proxy['THREE']
const scene = proxy['scene']
const camera = proxy['camera']
const renderer = proxy['renderer']
const composer = proxy['composer']
const TWEEN = proxy['TWEEN']// 后期处理
const renderPass = new RenderPass( scene, camera );
composer.addPass( renderPass );// 
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();const commonStore = useCommonStore()// 场景ref
const sceneRef = ref()
const playerRef = ref()
const siteRef = ref()
const handRef = ref()
const deckRef = ref()
const graveyardRef = ref()
const drawCardRef = ref()
const dialogRef = ref()
const selectedCard = ref() // 选中的场上card
const selectedTargetCard = ref() // 选中的目标场上card// 坐标轴辅助
const axesHelper = new THREE.AxesHelper(5);
// 创建轨道控制器
// const orbitControls = new OrbitControls( camera, renderer.domElement );
// 字体加载器
const fontLoader = new FontLoader();onMounted(async () => {await initResource()initScene()initGame()// 鼠标按下window.addEventListener('touchstart', onMousedown)window.addEventListener('touchmove', onMousemove)window.addEventListener('touchend', onMouseup)// window.addEventListener('click', onMousedown)// 监听浏览器窗口变化进行场景自适应window.addEventListener('resize', onWindowResize, false);
})// 资源加载
const initResource = () => {// 字体加载return new Promise((resolve, reject) => {// Microsoft YaHei_Regular// fonts/helvetiker_regular.typeface.jsonfontLoader.load('fonts/helvetiker_bold.typeface.json', (font: any) => {commonStore.loadFont(font)resolve(true)});})
}// 初始化场景
const initScene = async () => {renderer.setSize( window.innerWidth, window.innerHeight );sceneRef.value.appendChild( renderer.domElement );scene.add(axesHelper)// addSceneBg()// camera.position.set( 5, 5, 5 );camera.position.set( 0, 6.5, 0 );camera.lookAt(0, 0, 0)// const glitchPass = new GlitchPass();// composer.addPass( glitchPass );// const outputPass = new OutputPass();// composer.addPass( outputPass );addPlane()animate();
}// scene添加动态背景
const addSceneBg = () => {const vertices = [];for ( let i = 0; i < 5000; i ++ ) {const x = THREE.MathUtils.randFloatSpread( 1000 );const y = THREE.MathUtils.randFloatSpread( 1000 );const z = THREE.MathUtils.randFloatSpread( 1000 );vertices.push( x, y, z );}const particlesGeometry = new THREE.BufferGeometry();particlesGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );// 设置点材质const pointsMaterial = new THREE.PointsMaterial();// pointsMaterial.size = 0.9;pointsMaterial.color.set(new THREE.Color("#fff"));// 相机深度而衰减pointsMaterial.sizeAttenuation = true;const points = new THREE.Points(particlesGeometry, pointsMaterial);scene.add(points);// const texture = new THREE.TextureLoader().load( "textures/bg.jpg" );// // const geometry = new THREE.SphereGeometry( 1, 32, 16 );// const geometry = new THREE.CircleGeometry( 6, 32 );// const material = new THREE.MeshBasicMaterial({//   map: texture// });// const sphere = new THREE.Mesh( geometry, material );// sphere.name = "场景背景"// sphere.position.set(0, 0, 0)// sphere.rotateX(-90 * (Math.PI / 180)) // 弧度// scene.add( sphere );// texture.wrapS = THREE.RepeatWrapping;// texture.wrapT = THREE.RepeatWrapping;// scene.background = texture
}// 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 );
}// 用requestAnimationFrame进行渲染循环
// let bgMesh = scene.getObjectByName("场景背景")
// console.log(123, bgMesh)// 移动线纹理
let arrowTexture = new THREE.TextureLoader().load('textures/arrow.png');
arrowTexture.wrapT = THREE.RepeatWrapping;
arrowTexture.repeat.set(1, 4);
arrowTexture.center = new THREE.Vector2(0.5, 0.5)
arrowTexture.rotation = (-90 * (Math.PI / 180))
arrowTexture.needsUpdate = true;const animate = () => {requestAnimationFrame( animate );if (arrowTexture) {arrowTexture.offset.y -= 0.02; //更新箭头纹理偏移量}TWEEN.update()// let bgMesh = scene.getObjectByName("场景背景")// if (bgMesh) {//   bgMesh.rotation.z += 0.0002// }// renderer.render( scene, camera );composer.render()
}// 场景跟随浏览器窗口大小自适应
const onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);
}// 初始化游戏
const initGame = async () => {// 初始化玩家initPlayer()// 初始化场地initSite()// 初始化墓地initGraveyard()// 初始化卡组await initDeck()// 初始化手牌await initHand()// 绑定手牌事件onHandEvent()// await p2DrawCardEvent(1)let p2_handGroup = scene.getObjectByName("p2_handGroup")let cards = p2_handGroup.children.filter((v: any) => v.userData.type === "怪兽")p2HandToSite(cards[0])
}// 初始化玩家
const initPlayer = () => {playerRef.value.init()
}// 初始化场地
const initSite = () => {siteRef.value.init()
}// 初始化墓地
const initGraveyard = () => {graveyardRef.value.init()
}// 初始化卡组
const initDeck = () => {return new Promise((resolve, reject) => {let p1Deck: any = []let p2Deck: any = []// 洗牌p1TestDeck.sort(() => {return Math.random() - 0.5})p2TestDeck.sort(() => {return Math.random() - 0.5})p1TestDeck.forEach((v: any, i: any) => {let obj = CARD_DICT.find((b: any) => b.card_id === v)if (obj) {p1Deck.push({card_id: v,name: `${obj.name}_${i}`})}})p2TestDeck.forEach((v: any, i: any) => {let obj = CARD_DICT.find((b: any) => b.card_id === v)if (obj) {p2Deck.push({card_id: v,name: `${obj.name}_${i}`})}})// console.log("p1Deck", newDeck)commonStore.updateP1Deck(p1Deck)commonStore.updateP2Deck(p2Deck)nextTick(() => {handRef.value.init()deckRef.value.init()resolve(true)})})
}// 初始化手牌
const initHand = () => {let p1 = new Promise((resolve, reject) => {let cardNumber = 4let _number = 0let p1Deck = JSON.parse(JSON.stringify(commonStore.$state.p1Deck))let deckGroup = scene.getObjectByName("p1_deckGroup")let position = new THREE.Vector3(0, 0.005 * p1Deck.length, 0)let _interval = setInterval(async() => {// console.log(123, p1Deck)if (_number < cardNumber) {let obj = p1Deck[p1Deck.length - 1]p1Deck.splice(p1Deck.length-1, 1)commonStore.updateP1Deck(p1Deck)// 修改卡组await editDeckCard(deckGroup, obj, "remove")await renderDeckText(deckGroup, `${commonStore.$state.p1Deck.length}`, commonStore.$state._font, position)// 手牌区添加手牌handRef.value.addP1HandCard(obj, deckGroup)} else {clearInterval(_interval)resolve(true)}_number++}, 200)})let p2 = new Promise((resolve, reject) => {let cardNumber = 4let _number = 0let p2Deck = JSON.parse(JSON.stringify(commonStore.$state.p2Deck))let deckGroup = scene.getObjectByName("p2_deckGroup")let position = new THREE.Vector3(0, 0.005 * p2Deck.length, 0)let _interval = setInterval(async() => {// console.log(123, p1Deck)if (_number < cardNumber) {let obj = p2Deck[p2Deck.length - 1]p2Deck.splice(p2Deck.length-1, 1)commonStore.updateP2Deck(p2Deck)// 修改卡组await editDeckCard(deckGroup, obj, "remove")await renderDeckText(deckGroup, `${commonStore.$state.p2Deck.length}`, commonStore.$state._font, position)// 手牌区添加手牌handRef.value.addP2HandCard(obj, deckGroup)} else {clearInterval(_interval)resolve(true)}_number++}, 200)})return new Promise((resolve, reject) => {Promise.all([p1, p2]).then((res: any) => {resolve(true)})})
}// p2抽牌事件
const p2DrawCardEvent = (cardNumber: any) => {return new Promise((resolve, reject) => {let _number = 0let p2Deck = JSON.parse(JSON.stringify(commonStore.$state.p2Deck))let deckGroup = scene.getObjectByName("p2_deckGroup")let position = new THREE.Vector3(0, 0.005 * p2Deck.length, 0)let _interval = setInterval(async() => {// console.log(123, p1Deck)if (_number < cardNumber) {let obj = p2Deck[p2Deck.length - 1]p2Deck.splice(p2Deck.length-1, 1)commonStore.updateP2Deck(p2Deck)// 修改卡组await editDeckCard(deckGroup, obj, "remove")await renderDeckText(deckGroup, `${commonStore.$state.p2Deck.length}`, commonStore.$state._font, position)// 手牌区添加手牌handRef.value.addP2HandCard(obj, deckGroup)} else {clearInterval(_interval)resolve(true)}_number++}, 200)})
}// p2卡牌上场事件
const p2HandToSite = (card: any) => {let sitePlane = scene.getObjectByName("对方战域Plane")let userData = card.userDataif (userData.type === "怪兽") {let meshes = sitePlane.children.filter((v: any) => v.name.indexOf("对方怪兽区") > -1 && v.userData.empty === true)if (meshes.length > 0) {let _mesh = nulllet 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}let p2_handGroup = scene.getObjectByName("p2_handGroup")// card.rotateX(180 * (Math.PI / 180)) // 弧度renderSiteCard(p2_handGroup, card, _mesh)}}
}// 鼠标按下事件
const onMousedown = (ev: any) => {// console.log(222, ev.target)// 判断是否点击到canvas上if(!(ev.target instanceof HTMLCanvasElement)){return;}// 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)let clientX = ev.clientX || ev.changedTouches[0].pageXlet clientY = ev.clientY || ev.changedTouches[0].pageYlet point = transPos(clientX, clientY)// 通过摄像机和鼠标位置更新射线raycaster.setFromCamera( point, camera );// 点击卡组事件// onP1DeckEvent()// 鼠标选中场上卡牌事件onSelectSiteCard()
}// 鼠标移动事件
const onMousemove = (ev: any) => {// 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)pointer.x = ev.clientX || ev.changedTouches[0].pageXpointer.y = ev.clientY || ev.changedTouches[0].pageY// 画线if (selectedCard.value) {drawLine()}
}// 鼠标抬起事件
const onMouseup = (ev: any) => {let lineMesh = scene.getObjectByName("移动线")if (lineMesh) {scene.remove(lineMesh)}fight()
}// 鼠标选中场上卡牌事件
const onSelectSiteCard = () => {let p1Cards = scene.children.filter((v: any) => v.userData?.areaType?.indexOf("己方怪兽区") > -1)if (p1Cards.length <= 0) {return}let arr = raycaster.intersectObjects(p1Cards, true)if (arr.length > 0) {selectedCard.value = arr[0].object// drawLine()}}// 画线
const drawLine = () => {let mesh = scene.getObjectByName("移动线")if (mesh) {scene.remove(mesh)}let startPos = new THREE.Vector3(0, 0, 0)selectedCard.value.getWorldPosition(startPos)let point = transPos(pointer.x, pointer.y)// 通过摄像机和鼠标位置更新射线raycaster.setFromCamera( point, camera );let plane = scene.getObjectByName("地面")let arr = raycaster.intersectObject(plane)if (arr.length > 0) {// let pos = new THREE.Vector3(0, 0, 0)// arr[0].object.getWorldPosition(pos)let point = arr[0].pointlet curve = new THREE.CatmullRomCurve3([new THREE.Vector3(startPos.x, startPos.y + 0.2, startPos.z),new THREE.Vector3(point.x, point.y + 0.2, point.z)]);let tubeGeometry = new THREE.TubeGeometry(curve, 80, 0.08);const material = new THREE.MeshBasicMaterial({map: arrowTexture,side: THREE.BackSide, //显示背面transparent: true,// color: new THREE.Color("#ff6347")});const _mesh = new THREE.Mesh( tubeGeometry, material );_mesh.name = "移动线"scene.add(_mesh);}// 获取目标对象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}
}// 战斗
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}// 移除卡牌const removeCard = async (card: any) => {if (card.children && card.children.length > 0) {card.children.forEach((v: any) => {card.remove(v)})}console.log(321, card)let areaType = card.userData.areaTypelet isP1 = areaType.indexOf("己方") > -1let graveyardGroup = nulllet graveyardGroupPos = new THREE.Vector3(0, 0, 0)let cards = []card.material.forEach((v: any) => {v.transparent = falsev.opacity = 1v.alphaTest = 0.1;})card.rotateX(180 * (Math.PI / 180)) // 弧度if (isP1) {card.userData.areaType = "己方墓地"graveyardGroup = scene.getObjectByName("p1_graveyardGroup")graveyardGroup.getWorldPosition(graveyardGroupPos)card.position.set(graveyardGroupPos.x, graveyardGroupPos.y, graveyardGroupPos.z)cards = scene.children.filter((v: any) => v.userData?.areaType === "己方墓地")} else {card.userData.areaType = "对方墓地"graveyardGroup = scene.getObjectByName("p2_graveyardGroup")graveyardGroup.getWorldPosition(graveyardGroupPos)card.position.set(graveyardGroupPos.x, graveyardGroupPos.y, graveyardGroupPos.z)cards = scene.children.filter((v: any) => v.userData?.areaType === "对方墓地")}// 修改墓地let position = new THREE.Vector3(0, 0.005 * cards.length, 0)await editGraveyardCard(graveyardGroup, card, "remove")await renderGraveyardText(graveyardGroup, `${cards.length}`, commonStore.$state._font, position)if (isP1) {let sitePlane = scene.getObjectByName("己方战域Plane")let mesh = sitePlane.children.find((v: any) => v.name === areaType)if (mesh) {mesh.userData.empty = true}} else {let sitePlane = scene.getObjectByName("对方战域Plane")let mesh = sitePlane.children.find((v: any) => v.name === areaType)if (mesh) {mesh.userData.empty = true}}}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}
}// 点击卡组事件
const onP1DeckEvent = () => {if (commonStore.$state.p1Deck.length <= 0) {return}let p1_deckGroup = scene.getObjectByName("p1_deckGroup")let arr = raycaster.intersectObject(p1_deckGroup, true)if (arr.length <= 0) {return}let pos1 = p1_deckGroup.userData.positionlet pos2 = new THREE.Vector3(0, 2, 0)// console.log(444, pos1, pos2)if (p1_deckGroup.position.x !== pos2.x) {drawCardRef.value.drawCardAnimate1(p1_deckGroup, pos1, pos2)}
}// 手牌事件
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.04let 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)}});
}// 手牌移入己方战域
const handToSite = (data: any) => {let sitePlane = scene.getObjectByName("己方战域Plane")// console.log(data)let userData = data.userDataif (userData.type === "怪兽") {let meshes = sitePlane.children.filter((v: any) => v.name.indexOf("己方怪兽区") > -1 && v.userData.empty === true)if (meshes.length > 0) {let _mesh = nulllet 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}let p1_handGroup = scene.getObjectByName("p1_handGroup")renderSiteCard(p1_handGroup, data, _mesh)}}
}// 绘制场上卡牌
const renderSiteCard = async (handGroup: any, data: any, mesh: any) => {let position = new THREE.Vector3(0, 0, 0)mesh.getWorldPosition(position)mesh.userData.empty = falselet oldMesh = 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)if (handGroup.name === "p1_handGroup") {handRef.value.removeP1HandCard(oldMesh)} else if (handGroup.name === "p2_handGroup") {handRef.value.removeP2HandCard(oldMesh)newMesh.rotateX(180 * (Math.PI / 180)) // 弧度newMesh.rotateY(180 * (Math.PI / 180)) // 弧度}scene.add(newMesh)newMesh.position.set(position.x, position.y, position.z)await renderSiteCardText(handGroup, newMesh, commonStore.$state._font)// 创建伽马校正通道// const gammaPass= new ShaderPass(GammaCorrectionShader)// composer.addPass( gammaPass );// const outlinePass = new OutlinePass(new THREE.Vector2( window.innerWidth, window.innerHeight ), scene, camera); // 模糊// outlinePass.edgeStrength = 1.0; // 调整边缘强度// outlinePass.edgeGlow = 1.0; // 边缘发光强度// outlinePass.usePatternTexture = false; // 是否使用纹理// outlinePass.visibleEdgeColor.set(0xffffff); // 设置边缘颜色// outlinePass.hiddenEdgeColor.set(0x000000); // 设置隐藏边缘的颜色// outlinePass.selectedObjects = [newMesh.children[0]]// composer.addPass( outlinePass );// console.log(123, newMesh)
}// 取消
const onCancel = (data: any) => {handRef.value.backPosition(data)
}
</script><style lang="scss" scoped>
.scene {position: fixed;top: 0;left: 0;width: 100%;height: 100vh;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/785784.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python入门(八)

引入 引入函数 为了减少代码的冗余&#xff0c;减轻我们的工作量&#xff0c;我们常常将代码分块编写&#xff0c;在Python中更是如此&#xff0c;那么我们怎么在一个新的程序文件中调用我们已经编写好程序文件的函数&#xff0c;我们使用import。我们先写一个first.py为例语…

WinForm_初识_事件_消息提示

文章目录 WinForm开发环境的使用软件部署的架构B/S 架构应用程序C/S 架构应用程序 创建 Windows 应用程序窗口介绍查看设计窗体 Form1.cs 后台代码窗体 Form1.cs窗体的常用属性 事件驱动机制事件的应用事件的测试测试事件的级联响应常用控件的事件事件响应的公共方法 消息提示的…

CCIE-02-PPPoE

目录 实验条件网络拓朴实验目标 开始配置R1验证效果 实验条件 网络拓朴 实验目标 R2为PPPoE Server&#xff0c;已预配了相关信息&#xff1b;R1作为PPPoE Client&#xff0c;进行PPPoE拨号 用户名为R1&#xff0c;密码为cisco &#xff0c; 采用CHAP的认证方式&#xff0c;I…

算法学习——LeetCode力扣补充篇3(143. 重排链表、141. 环形链表、205. 同构字符串、1002. 查找共用字符、925. 长按键入)

算法学习——LeetCode力扣补充篇3 143. 重排链表 143. 重排链表 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln 请将其重新排列后变为&#xff1a; L0 → Ln → …

Python学习笔记 - 如何在google Colab中显示图像?

这里是使用的opencv进行图片的读取&#xff0c;自然也是想使用opencv的imshow方法来显示图像&#xff0c;但是在google Colab中不可以使用&#xff0c;所以寻找了一下变通的显示方法。 方法1&#xff1a;使用matplotlib 使用plt需要交换一下r、b通道&#xff0c;否则显示不正常…

第十九章 UML

统一建模语言(Unified Modeling Language&#xff0c; UML )是一种为面向对象系统的产品进行说明、可视化和编制文档的一种标准语言&#xff0c;是非专利的第三代建模和规约语言。 UML 是面向对象设计的建模工具&#xff0c;独立于任何具体程序设计语言。 一、简介 UML 作为一…

正大国际:做qi货靠运气多点还是靠自己学习到认知度?

一个人能赚到自己认知范围以外的钱靠的是运气&#xff0c;能赚到自己认知范围内的钱靠的是能力。期货市场试错成本较高&#xff0c;交易自己熟悉和擅长的领域会大大降低失败概率。期货市场机会很多&#xff0c;陷阱也很多&#xff0c;如何坚持做认知范围内的决策是一个重要的交…

docker部署开源软件的国内镜像站点

下载镜像 docker pull registry.cn-beijing.aliyuncs.com/wuxingge123/le_monitor:latestdocker-compose部署 vim docker-compose.yml version: 3 services:le_monitor:container_name: le_monitorimage: registry.cn-beijing.aliyuncs.com/wuxingge123/le_monitor:latestpo…

算法——距离计算

距离计算常用的算法包括欧氏距离、曼哈顿距离、切比雪夫距离、闵可夫斯基距离、余弦相似度等。这些算法在数据挖掘、机器学习和模式识别等领域中被广泛应用。 1.欧氏距离 欧式距离也称欧几里得距离&#xff0c;是最常见的距离度量&#xff0c;衡量的是多维空间中两个点之间的…

心里健康(健康与生存)

你还认为 健康 是有个强壮的身体吗&#xff1f; 这样 肯定是错的 我们来说说 什么是健康与现代健康观 以及影响健康的因素 有哪些&#xff1f; 以及 健康对个人与社会的意义 首先 我们来看看 健康演变过程 公元 1000 年 Health 首次出现 它代表了 强壮 健全 完整等含义 健康 …

十四.PyEcharts基础学习

目录 1-PyEcharts介绍 优点&#xff1a; 安装: 官方文档&#xff1a; 2-PyEcharts快速入门 2.1 第一个图表绘制 2.2 链式调用 2.3 opeions配置项 2.4 渲染图片文件 2.5 使用主题 3-PyEcharts配置项 3.1 初始化配置项InitOpts InitOpts 3.2 全局配置项set_global_o…

京东云明修“价格战”,暗渡“政企云”

文&#xff5c;白 鸽 编&#xff5c;王一粟 云计算行业越来越“卷”&#xff0c;一边卷大模型&#xff0c;一边卷价格。 2024 刚一开年&#xff0c;阿里云就宣布百余款产品大降价&#xff0c;最高降幅达55%。在阿里云宣布降价后&#xff0c;京东云紧随其后宣布&#xff0…

80C51实现四种流水灯流水灯 | 自用学习笔记

单个流水灯 #include <reg51.h> #include <intrins.h> // 包含移位库函数的头文件void delay(void) // 定义延时函数 {unsigned char i, j;for(i 0; i < 200; i) // 循环延时&#xff0c;延时约50msfor(j 0; j < 125; j); }void main(void){ unsigned ch…

AcWing刷题-游戏

游戏 DP l lambda: [int(x) for x in input().split()]n l()[0] w [0] while len(w) < n:w l()s [0] * (n 1) for i in range(1, n 1): s[i] s[i - 1] w[i]f [[0] * (n 1) for _ in range(n 1)]for i in range(1, n 1): f[i][i] w[i]for length in range(2, …

[BT]BUUCTF刷题第13天(4.1)

第13天 Upload-Labs-Linux (Basic) Pass-01 根据题目提示&#xff0c;该题为绕过js验证。 一句话木马&#xff1a; <?php eval(system($_POST["cmd"]));?> // 符号 表示后面的语句即使执行错误&#xff0c;也不报错。 // eval() 把括号内的字符串全部…

rtsp协议详解-最详细的流媒体传输协议

流媒体传输协议-rtsp协议详解 rtsp RTSP出现以前&#xff0c;最热的大概就是HTTP协议。想象一下&#xff0c;当你需要欣赏网络中的某一段视频&#xff0c;通过HTTP协议访问其URL、开始下载、下载完成后播放。对于早期的视频采集设备、网络带宽或是负责渲染的显示器而言&#…

Redis中的复制功能(一)

复制 概述 在Redis中&#xff0c;用户可以通过执行SLAVEOF命令或者设置slaveof选项&#xff0c;让一个服务器去复制(replicate)另一个服务器&#xff0c;我们称呼被复制的服务器为主服务器(master)&#xff0c;而对主服务器进行复制的服务器则被称为从服务器(slave),如图所示…

关于list的那些事

1.什么是List 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection。 Collection也是一个接口&#xff0c;该接口中规范了后序容器中常用的一些方法具体如下所示 Iterable也是一个接口&#xff0c;表示实现该接口的类是可以逐个元素遍历的&#xff0c;具体如…

深度学习| DiceLoss解决图像数据不平衡问题

图像数据不平衡问题 图像数据不平衡&#xff1a;在进行图像分割时&#xff0c;二分类问题中&#xff0c;背景过大&#xff0c;前景过小&#xff1b;多分类问题中&#xff0c;某一类别的物体体积过小。在很多图像数据的时候都会遇到这个情况&#xff0c;尤其是在医学图像处理的…

SSM框架学习——Spring的控制反转IoC与依赖注入DI

控制反转IoC与依赖注入DI 概念 提到Spring首先想到的肯定是Spring的IoC容器了。在了解Spring的用法之前我们必须了解什么是控制反转IoC和依赖注入DI。 **控制反转&#xff08;Inversion of Control&#xff09;**是面向对象编程中的一种设计原则&#xff0c;它建议将不需要的…