vue3+threejs新手从零开发卡牌游戏(二十四):添加p2战斗逻辑

博客介绍了用代码模拟P2战斗逻辑,对战斗流程进行步骤拆分,包括抽卡、召唤怪兽上场和战斗等环节,还分析了战斗的几种情况。同时提及修改相关代码文件,已初步完成和电脑简单对战的游戏架构,后续可添加卡牌逻辑、优化效果和UI等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用代码模拟p2战斗逻辑,按流程进行步骤拆分:

1.p2抽卡

2.p2召唤怪兽上场

3.p2战斗

其中战斗部分分为几种情况:

情况一:p2场上卡牌由大到小进行排序,按序轮询可以攻击的卡牌,然后攻击p1场上卡牌由大到小攻击力最高的同时攻击力不超过当前p2卡牌的那个,如果p2场上最大的攻击力不能突破p1场上卡牌,那就不攻击并将该卡牌攻击次数置为0,进行下一个循环,当p2场上所以卡牌攻击次数均为0时,认为战斗阶段结束,可以进行下一个回合。

情况二:p1场上卡牌已经全部被破坏,而此时p2场上还有卡牌未行动,那么此时卡牌可以直接攻击p1玩家。

情况三:p2战斗阶段时,p1场上怪兽区没有卡牌时,全员即可轮询直接攻击p1玩家。

修改utils/common.ts:


// 卡牌攻击特效
const cardAttack = (card1: any, card2: any, callback: any) => {
  console.log("cardAttack", card1, card2)
  let isP1 = card1.userData.areaType.indexOf("己方") > -1
  // 获取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: isP1 ? pos2.z + 1.4 * 0.8 : pos2.z - 0.7 * 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: isP1 ? pos2.z + 1.4 * 0.8 : pos2.z - 0.7 * 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 }


// p2玩家手牌召唤上场逻辑
const p2FindHandToSiteCard = (cards: any, mana: any) => {
  let newCards = cards.filter((v: any) => v.userData.Mana <= mana)
  console.log("p2玩家手牌召唤上场逻辑", cards, newCards)
  if (newCards.length > 0) {
    newCards.sort((a: any, b: any) => {
      return a.Mana - b.Mana
    })
    return newCards[0]
  }
  return null
}
export { p2FindHandToSiteCard }

game/index.vue(由于涉及代码较多,所以直接贴上该文件全部代码,主要部分看p2DrawCardEvent相关方法):

<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"/>
  <!-- 阶段操作 -->
  <ul class="stage-box">
    <li v-if="commonStore.$state.currentPlayer==='p1'&&commonStore.$state.flowIndex==1">
      <el-button type="primary" size="small" @click.stop="nextFlow">下一阶段</el-button>
    </li>
    <li v-if="commonStore.$state.currentPlayer==='p1'&&commonStore.$state.flowIndex===2">
      <el-button type="danger" size="small" round @click.stop="nextRound">回合结束</el-button>
    </li>
  </ul>
  
  <div class="info-box">
    <!-- 当前操作玩家 -->
    <div class="">player:{
  { commonStore.$state.currentPlayer }}</div>
    <!-- 回合数 -->
    <div class="round-box">round:{
  { commonStore.$state.round }}</div>
  </div>
</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, cardDirectAttack, p2FindHandToSiteCard } 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"

// 引入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();

watch(() => commonStore.$state.p1HP, () => {
  if (commonStore.$state.p1HP <=0) {
    // alert("你输了")
    // window.location.reload()
  }
})

watch(() => commonStore.$state.p2HP, () => {
  if (commonStore.$state.p2HP <=0) {
    // alert("你赢了")
    // window.location.reload()
  }
})

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.json
    fontLoader.load('fonts/helvetiker_bold.typeface.json', (font: any) => {
      commonStore.loadFont(font)
      resolve(true)
    });
  })
}

// 初始化场景
const initScene = async () => {
  renderer.setSize( window.innerWidth
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清岚_lxn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值