You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS简单卡牌对战游戏开发实现问题求助

卡牌对战功能实现方案

代码修改说明

所有改动基于你现有逻辑扩展,核心新增了状态管理控制对战流程、胜负判断逻辑、游戏结束判定,默认你的NFT元数据中包含score字段作为对战评分依据,可根据你的实际数据结构调整对应取值逻辑:

import "./styles.css";
import React, { useState } from "react";
import styled from "styled-components";
import data0 from "./NFTs/Team1/0.json";
import data1 from "./NFTs/Team1/1.json";
import data2 from "./NFTs/Team1/2.json";
import data3 from "./NFTs/Team1/3.json";
import data4 from "./NFTs/Team2/0.json";
import data5 from "./NFTs/Team2/1.json";
import data6 from "./NFTs/Team2/2.json";
import data7 from "./NFTs/Team2/3.json";

// 组件移到App外部避免重复初始化
class Welcome extends React.Component {
  render() {
    return (
      <div className={`NFT ${this.props.winClass ? 'winner-card' : ''}`}>
        <img
          className={this.props.className}
          src={this.props.src}
          alt="NFT"
        />
        <h1 className="nftnum">{this.props.name}</h1>
        {this.props.score && <p className="card-score">得分:{this.props.score}</p>}
      </div>
    );
  }
}

const NavBar = styled.div`
  width: 100%;
  height: 100px;
  background: #fff;
  position: fixed;
  top: 0;
  display: flex;
  justify-content: space-between;
`;
const TeamName = styled.h1`
  font-size: 30px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 50%;
  text-align: center;
`;
const Logo = styled.img`
  position: fixed;
  top: 0;
  left: 50%;
  transform: translate(-50%);
  z-index: 1;
  width: 25%;
`;
const BattleBtn = styled.button`
  position: fixed;
  bottom: 50px;
  left: 50%;
  transform: translateX(-50%);
  padding: 12px 36px;
  font-size: 20px;
  cursor: pointer;
  background: #e74c3c;
  color: #fff;
  border: none;
  border-radius: 8px;
  &:disabled {
    background: #ccc;
    cursor: not-allowed;
  }
`;
const ResultTip = styled.div`
  position: fixed;
  top: 120px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 24px;
  font-weight: bold;
  color: #2c3e50;
`;

export default function App() {
  const matchups = [
    [data0, data1, data2, data3], // 队伍1卡牌池
    [data4, data5, data6, data7] // 队伍2卡牌池
  ];
  // 状态管理
  const [team1Idx, setTeam1Idx] = useState(0); // 队伍1当前出战卡牌索引
  const [team2Idx, setTeam2Idx] = useState(0); // 队伍2当前出战卡牌索引
  const [roundWinner, setRoundWinner] = useState(null); // 单轮胜负:left/right/draw
  const [gameOver, setGameOver] = useState(false); // 游戏是否结束
  const [finalWinner, setFinalWinner] = useState(null); // 最终胜者

  // 对战核心逻辑
  const handleBattle = () => {
    const card1 = matchups[0][team1Idx];
    const card2 = matchups[1][team2Idx];
    // 请根据你的实际JSON结构修改得分取值逻辑
    const score1 = card1.score;
    const score2 = card2.score;

    if (score1 > score2) {
      setRoundWinner('left');
      // 队伍2败,换下一张卡牌
      if (team2Idx + 1 >= matchups[1].length) {
        setGameOver(true);
        setFinalWinner('队伍1');
        return;
      }
      setTeam2Idx(prev => prev + 1);
    } else if (score2 > score1) {
      setRoundWinner('right');
      // 队伍1败,换下一张卡牌
      if (team1Idx + 1 >= matchups[0].length) {
        setGameOver(true);
        setFinalWinner('队伍2');
        return;
      }
      setTeam1Idx(prev => prev + 1);
    } else {
      setRoundWinner('draw');
      // 平局规则可自行调整,当前逻辑为双方都换下一张
      if (team1Idx + 1 >= matchups[0].length || team2Idx + 1 >= matchups[1].length) {
        setGameOver(true);
        setFinalWinner('平局');
        return;
      }
      setTeam1Idx(prev => prev + 1);
      setTeam2Idx(prev => prev + 1);
    }
  };

  return (
    <div className="App">
      <Logo src="logo.png" />
      <NavBar>
        <TeamName>Aids Sack</TeamName>
        <TeamName>Balls Tucked</TeamName>
      </NavBar>
      <ResultTip>
        {gameOver ? `游戏结束,最终胜者:${finalWinner}` : 
         roundWinner === 'left' ? '上轮胜者:左侧卡牌' :
         roundWinner === 'right' ? '上轮胜者:右侧卡牌' :
         roundWinner === 'draw' ? '上轮平局' : '点击下方按钮开始对战'}
      </ResultTip>
      <div className="nftWrapper">
        <Welcome
          className="leftNFT"
          name={matchups[0][team1Idx].name}
          src={matchups[0][team1Idx].properties.files[0].uri}
          score={matchups[0][team1Idx].score}
          winClass={roundWinner === 'left'}
        />
        <Welcome
          className="rightNFT"
          name={matchups[1][team2Idx].name}
          src={matchups[1][team2Idx].properties.files[0].uri}
          score={matchups[1][team2Idx].score}
          winClass={roundWinner === 'right'}
        />
      </div>
      <BattleBtn onClick={handleBattle} disabled={gameOver}>
        {gameOver ? '游戏结束' : '开始对战'}
      </BattleBtn>
    </div>
  );
}

补充说明

  • 你可以在styles.css中添加.winner-card { border: 4px solid #f39c12; box-shadow: 0 0 20px #f39c12; }实现胜者卡牌高亮效果
  • 如果你的NFT元数据中得分字段不是score,请修改handleBattle函数内的score1、score2取值逻辑
  • 平局规则、对战触发方式、动画效果都可以根据你的需求自行扩展调整

内容的提问来源于stack exchange,提问作者Thud Designs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 08:36:02