杭州定制网站制作,美丽寮步网站建设,dw做汽车网站,网站建设公司电话销售话术手牌上场后#xff0c;点击己方怪兽区卡牌会跟随鼠标移动画出线条#xff0c;之后可以通过判断鼠标移动到对方场地的某卡牌进行战斗操作#xff0c;代码主要改动在game/index.vue文件。
1.添加鼠标移动监听事件#xff08;移动端#xff09;#xff1a;
window.addEven…手牌上场后点击己方怪兽区卡牌会跟随鼠标移动画出线条之后可以通过判断鼠标移动到对方场地的某卡牌进行战斗操作代码主要改动在game/index.vue文件。
1.添加鼠标移动监听事件移动端
window.addEventListener(touchstart, onMousedown)
window.addEventListener(touchmove, onMousemove)
window.addEventListener(touchend, onMouseup)
2.鼠标按下获取选中手牌
// 鼠标按下事件
const onMousedown (ev: any) {...// 鼠标选中场上卡牌事件onSelectSiteCard(ev)
}// 鼠标选中场上卡牌事件
const onSelectSiteCard (point: any) {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].objectconsole.log(444, arr[0].object)drawLine()}}
3.添加画线逻辑主要通过CatmullRomCurve3和TubeGeometry管道缓冲几何体配合画线网上随便找了个箭头的png图做素材
// 鼠标移动事件
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)}
}// 画线
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);}
}
页面效果如下后续方便进行战斗逻辑处理 附 game/index.vue代码
templatediv refsceneRef classscene/div!-- 玩家区 --Player refplayerRef/!-- 手牌 --Hand refhandRef/!-- 卡组 --Deck refdeckRef/!-- 战域 --Site refsiteRef/!-- 抽卡逻辑 --DrawCard refdrawCardRef :handRefhandRef/!-- 对话框 --Dialog refdialogRef handToSitehandToSite onCancelonCancel/
/templatescript setup langts
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, renderSiteCardText } 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 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 drawCardRef ref()
const dialogRef ref()
const selectedCard 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()// 初始化卡组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 怪兽)console.log(123, cards)p2HandToSite(cards[0])
}// 初始化玩家
const initPlayer () {playerRef.value.init()
}// 初始化场地
const initSite () {siteRef.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(ev)
}// 鼠标移动事件
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)}
}// 鼠标选中场上卡牌事件
const onSelectSiteCard (point: any) {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].objectconsole.log(444, 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);}
}// 点击卡组事件
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)
}
/scriptstyle langscss scoped
.scene {position: fixed;top: 0;left: 0;width: 100%;height: 100vh;
}
/style