react+three+fiber实现三维过马路避障游戏代码

代码语言:html

所属分类:游戏

代码描述:react+three+fiber实现三维过马路避障游戏代码,操作上下左右键让立方体过马路,别被车子撞到即可。

代码标签: react three fiber 三维 马路 避障 游戏 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">





    <style>
        @import url("https://fonts.googleapis.com/css?family=Press+Start+2P");
        
        body {
          margin: 0;
          display: flex;
          min-height: 100vh;
        }
        
        #root {
          width: 100%;
        }
        
        .game {
          position: relative;
          width: 100%;
          height: 100%;
          font-family: "Press Start 2P", cursive;
        }
        
        #controls {
          position: absolute;
          bottom: 20px;
          min-width: 100%;
          display: flex;
          align-items: flex-end;
          justify-content: center;
        }
        
        #controls div {
          display: grid;
          grid-template-columns: 50px 50px 50px;
          gap: 10px;
        }
        
        #controls button {
          width: 100%;
          height: 40px;
          background-color: white;
          border: 1px solid lightgray;
          box-shadow: 3px 5px 0px 0px rgba(0, 0, 0, 0.75);
          cursor: pointer;
          outline: none;
        }
        
        #controls button:first-of-type {
          grid-column: 1/-1;
        }
        
        #score {
          position: absolute;
          top: 20px;
          left: 20px;
        
          font-size: 2em;
          color: white;
        }
        
        #result-container {
          position: absolute;
          min-width: 100%;
          min-height: 100%;
          top: 0;
          display: flex;
          align-items: center;
          justify-content: center;
        
          #result {
            display: flex;
            flex-direction: column;
            align-items: center;
            background-color: white;
            padding: 20px;
          }
        
          button {
            background-color: red;
            padding: 20px 50px 20px 50px;
            font-family: inherit;
            font-size: inherit;
            cursor: pointer;
          }
        }
        
        
        #youtube,
        #youtube-card {
          display: none;
          font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
          color: black;
        }
        
        @media (min-height: 425px) {
          /** Youtube logo by https://codepen.io/alvaromontoro */
          #youtube {
            z-index: 50;
            width: 100px;
            display: block;
            height: 70px;
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: red;
            border-radius: 50% / 11%;
            transform: scale(0.8);
            transition: transform 0.5s;
          }
        
          #youtube:hover,
          #youtube:focus {
            transform: scale(0.9);
            color: black;
          }
        
          #youtube::before {
            content: "";
            display: block;
            position: absolute;
            top: 7.5%;
            left: -6%;
            width: 112%;
            height: 85%;
            background: red;
            border-radius: 9% / 50%;
          }
        
          #youtube::after {
            content: "";
            display: block;
            position: absolute;
            top: 20px;
            left: 40px;
            width: 45px;
            height: 30px;
            border: 15px solid transparent;
            box-sizing: border-box;
            border-left: 30px solid white;
          }
        
          #youtube span {
            font-size: 0;
            position: absolute;
            width: 0;
            height: 0;
            overflow: hidden;
          }
        
          #youtube:hover + #youtube-card {
            z-index: 49;
            display: block;
            position: fixed;
            bottom: 12px;
            right: 10px;
            padding: 25px 130px 25px 25px;
            width: 300px;
            background-color: white;
          }
        }
    </style>




</head>

<body translate="no">


    <div id="root"></div>
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/babel.7.18.13.js"></script>

    <script type="text/babel"  data-type="module">
import React, { useRef, useEffect } from "https://esm.sh/react";
import { createRoot } from "https://esm.sh/react-dom/client";
import { Canvas, useFrame, useThree } from "https://esm.sh/@react-three/fiber";
import { Bounds } from "https://esm.sh/@react-three/drei";
import * as THREE from "https://esm.sh/three";
import { create } from "https://esm.sh/zustand";

const minTileIndex = -8;
const maxTileIndex = 8;
const tilesPerRow = maxTileIndex - minTileIndex + 1;
const tileSize = 42;

const playerState = {
  currentRow: 0,
  currentTile: 0,
  movesQueue: [],
  ref: null
};

function queueMove(direction) {
  const isValidMove = endsUpInValidPosition(
    { rowIndex: playerState.currentRow, tileIndex: playerState.currentTile },
    [...playerState.movesQueue, direction]
  );

  if (!isValidMove) return;

  playerState.movesQueue.push(direction);
}

function stepCompleted() {
  const direction = playerState.movesQueue.shift();

  if (direction === "forward") playerState.currentRow += 1;
  if (direction === "backward") playerState.currentRow -= 1;
  if (direction === "left") playerState.currentTile -= 1;
  if (direction === "right") playerState.currentTile += 1;

  // Add new rows if the player is running out of them
  if (playerState.currentRow === useMapStore.getState().rows.length - 10) {
    useMapStore.getState().addRows();
  }

  useGameStore.getState().updateScore(playerState.currentRow);
}

function setPlayerRef(ref) {
  playerState.ref = ref;
}

function resetPlayerStore() {
  playerState.currentRow = 0;
  playerState.currentTile = 0;
  playerState.movesQueue = [];

  if (!playerState.ref) return;
  playerState.ref.position.x = 0;
  playerState.ref.position.y = 0;
  playerState.ref.children[0].rotation.z = 0;
}

const useGameStore = create((set) => ({
  status: "running",
  score: 0,
  updateScore: (rowIndex) => {
    set((state) => ({ score: Math.max(rowIndex, state.score) }));
  },
  endGame: () => {
    set({ status: "over" });
  },
  reset: () => {
    useMapStore.getState().reset();
    resetPlayerStore();
    set({ status: "running", score: 0 });
  }
}));

const useMapStore = create((set) => ({
  rows: generateRows(20),
  addRows: () => {
    const newRows = generateRows(20);
    set((state) => ({ rows: [...state.rows, ...newRows] }));
  },
  reset: () => set({ rows: generateRows(20) })
}));

function Game() {
  return (
    <div className="game">
      <Scene>
        <Player />
        <Map />
      </Scene>
      <Score />
      <Controls />
      <Result />
    </div>
  );
}

const Scene = ({ children }) => {
  return (
    <Canvas
      orthographic={true}
      shadows={true}
      camera={{
        up: [0, 0, 1],
        position: [300, -300, 300]
      }}
    >
      <ambientLight />
      {children}
    </Canvas>
  );
};

function Controls() {
  useEventListeners();

  return (
    <div id="controls">
      <div>
        <button onClick={() => queueMove("forward")}>▲</button>
        <button onClick={() => queueMove("left")}>◀</button>
        <button onClick={() => queueMove("backward")}>▼</button>
        <button onClick={() => queueMove("right")}>▶</button>
      </div>
    </div>
  );
}

function Score() {
  const score = useGameStore((state) => state.score);

  return <div id="score">{score}</div>;
}

function Result() {
  cons.........完整代码请登录后点击上方下载按钮下载查看

网友评论0