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