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