Flatris源码深度剖析:Redux状态管理与Tetromino方块逻辑实现

【免费下载链接】flatris Fast-paced two-player web game 【免费下载链接】flatris 项目地址: https://gitcode.com/gh_mirrors/fl/flatris

Flatris是一款快节奏的双人网页游戏,基于经典俄罗斯方块玩法开发,通过Redux实现高效状态管理,结合精心设计的Tetromino方块逻辑,为玩家带来流畅的游戏体验。本文将深入解析Flatris源码中Redux状态管理架构和Tetromino方块核心逻辑的实现原理。

Redux状态管理架构解析

Flatris采用Redux作为状态管理核心,通过模块化的reducer设计实现游戏状态的高效管理。在web/store.js中,使用combineReducers将多个功能模块的reducer组合成根reducer:

const rootReducer = combineReducers({
  jsReady: jsReadyReducer,
  curUser: curUserReducer,
  games: gamesReducer,
  curGame: curGameReducer,
  backfills: backfillsReducer,
  stats: statsReducer,
});

游戏状态核心reducer

游戏核心逻辑主要由shared/reducers/game.js中的gameReducer处理,该reducer负责处理所有游戏相关的动作,包括玩家加入、方块移动、旋转、消除行等关键操作。其核心工作流程是接收动作类型,根据当前游戏状态计算并返回新的状态。

状态更新与动作处理

在Redux架构中,所有游戏状态的变更都通过动作(Action)触发。例如,方块移动动作会被gameJoinedReducer中的MOVE_LEFTMOVE_RIGHT分支处理:

case 'MOVE_LEFT':
case 'MOVE_RIGHT': {
  const direction = action.type === 'MOVE_LEFT' ? -1 : 1;
  const player = getPlayer(state, userId);
  const { grid, activeTetrominoGrid, activeTetrominoPosition } = player;
  const newPosition = {
    ...activeTetrominoPosition,
    x: activeTetrominoPosition.x + direction,
  };

  if (!isPositionAvailable(grid, activeTetrominoGrid, newPosition)) {
    return state;
  }

  return updatePlayer(state, userId, {
    activeTetrominoPosition: newPosition,
  });
}

这段代码展示了如何处理玩家的左右移动操作,首先计算新位置,然后检查位置是否可用,最后更新玩家状态。

Tetromino方块逻辑实现

Tetromino方块是Flatris游戏的核心元素,其逻辑实现主要集中在shared/utils/tetromino.jsshared/reducers/game.js中。

方块形状与初始位置

游戏中所有方块形状定义在shared/constants/tetromino.jsSHAPES对象中。每个方块类型(I、O、T、J、L、S、Z)都有对应的矩阵表示。getInitialPositionForTetromino函数负责计算方块进入游戏区域时的初始位置:

export function getInitialPositionForTetromino(
  tetromino: Tetromino,
  gridCols: number
): Position2d {
  const grid = SHAPES[tetromino];
  return {
    x: Math.round(gridCols / 2) - Math.round(grid[0].length / 2),
    y: -2,
  };
}

方块生成与序列控制

getNextTetromino函数使用CRC32算法生成伪随机方块序列,确保游戏的公平性和可预测性:

export function getNextTetromino(gameId: GameId, nth: number): Tetromino {
  const tetrominos: Tetromino[] = Object.keys(SHAPES);
  const randNum = crc32(gameId + nth);
  return tetrominos[Math.abs(randNum) % tetrominos.length];
}

方块旋转与碰撞检测

方块旋转是通过shared/utils/grid.js中的rotate函数实现的,而碰撞检测则由isPositionAvailable函数处理。在gameReducerROTATE分支中,实现了方块旋转的完整逻辑:

case 'ROTATE': {
  const player = getPlayer(state, userId);
  const { grid, activeTetrominoGrid, activeTetrominoPosition } = player;
  const newGrid = rotate(activeTetrominoGrid);
  const newPosition = fitTetrominoPositionInWellBounds(
    grid,
    newGrid,
    activeTetrominoPosition
  );

  if (!isPositionAvailable(grid, newGrid, newPosition)) {
    return state;
  }

  return updatePlayer(state, userId, {
    activeTetrominoGrid: newGrid,
    activeTetrominoPosition: newPosition,
  });
}

这段代码首先旋转方块矩阵,然后调整位置以确保旋转后不会超出游戏区域,最后检查新位置是否有效。

游戏核心循环与状态更新

Flatris的游戏循环通过DROP动作实现,该动作定期触发,使方块不断下落。当方块落地时,游戏会检查是否有完整行可以消除,并根据消除行数计算得分:

case 'DROP': {
  // 代码省略...
  // 检查是否可以消除行
  if (!hasLines(newGrid)) {
    return newState;
  }

  const { clearedGrid, rowsCleared } = clearLines(newGrid);
  const blocksCleared = getBlocksFromGridRows(newGrid, rowsCleared);
  newState = updatePlayer(newState, userId, {
    grid: clearedGrid,
    blocksCleared,
    flashYay: altFlashClass(flashYay),
    quake: dropAcceleration ? altQuakeClass(quake, rowsCleared.length) : null,
  });
  newState = rewardClearedBlocks(newState, userId);
  
  // 发送消除的行到对手屏幕
  return sendClearedBlocksToEnemy(newState, userId, grid, rowsCleared);
}

Flatris游戏运行效果

双人对战机制

Flatris的双人对战功能通过将玩家消除的行数作为"攻击"发送给对手来实现。sendClearedBlocksToEnemy函数负责将消除的行转换为对手的障碍物:

function sendClearedBlocksToEnemy(
  game: Game,
  userId: UserId,
  unclearedGrid: WellGrid,
  rowsCleared: Array<number>
): Game {
  const curPlayer = getPlayer(game, userId);
  const enemy = getOtherPlayer(game, curPlayer);
  if (!enemy) {
    return game;
  }

  const blocksPending = overrideBlockIds(
    getBlocksFromGridRows(unclearedGrid, rowsCleared),
    getNextCellId(enemy)
  );

  return updatePlayer(game, enemy.user.id, {
    blocksPending: [...enemy.blocksPending, ...blocksPending],
    flashNay: altFlashClass(flashNay),
  });
}

总结

Flatris通过Redux实现了清晰的状态管理架构,将游戏状态划分为多个模块,使代码结构清晰、易于维护。Tetromino方块逻辑的实现则体现了游戏核心机制的精妙设计,包括方块生成、移动、旋转和碰撞检测等关键功能。双人对战机制的实现则增加了游戏的趣味性和竞争性。

通过深入分析Flatris的源码,我们不仅可以学习到Redux在实际项目中的应用,还能了解游戏开发中的核心算法和设计模式。无论是对前端开发者还是游戏爱好者来说,Flatris都是一个值得研究的优秀项目。

要开始探索Flatris源码,你可以通过以下命令克隆仓库:

git clone https://gitcode.com/gh_mirrors/fl/flatris

然后参考项目中的README.md文件了解更多关于项目结构和运行方法的信息。

【免费下载链接】flatris Fast-paced two-player web game 【免费下载链接】flatris 项目地址: https://gitcode.com/gh_mirrors/fl/flatris

Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐