Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Juego de fútbol arcade estilo AAA hecho en Three.js, con equipos y jugadores inventados, físicas de balón, IA de 11 vs 11, portero, cámara de transmisión, menús y HUD. Todo en español. Sin assets externos: gráficos, texturas y audio son 100% procedurales. Necesitás Node.js 18 o superior. La primera vez: Abre la dirección que imprime (normalmente http://localhost:5173). Los cambios en el código se recargan al instante, sin perder el estado. Para generar la versión de producción y probarla en local:README 전문 보기 ↗
Juego de fútbol arcade estilo AAA hecho en Three.js, con equipos y jugadores inventados, físicas de balón, IA de 11 vs 11, portero, cámara de transmisión, menús y HUD. Todo en español. Sin assets externos: gráficos, texturas y audio son 100% procedurales.
Necesitás Node.js 18 o superior. La primera vez:
npm install
Y después, para jugar y desarrollar:
npm run dev
Abre la dirección que imprime (normalmente http://localhost:5173). Los cambios en el código se recargan al instante, sin perder el estado.
Para generar la versión de producción y probarla en local:
npm run build && npm run preview
Sin dependencia de CDN. Three.js viene de npm y se empaqueta con el juego, así que funciona sin conexión y no depende de que unpkg esté disponible.
fulbo/
├─ index.html solo el markup (menú, HUD, torneo, pausa)
├─ src/
│ ├─ main.js núcleo del partido y bucle (pendiente de partir)
│ ├─ config/ datos puros, sin lógica
│ │ ├─ teams.js · formations.js · field.js · rules.js
│ ├─ core/
│ │ ├─ state.js S, teams, cards: estado compartido del partido
│ │ └─ rng.js azar determinista con semilla
│ ├─ render/
│ │ ├─ textures.js texturas por canvas
│ │ ├─ effects.js confeti y destellos de las gradas
│ │ └─ scene/ sky · lights · pitch · goals · stadium
│ ├─ league/league.js modo torneo y tabla de posiciones
│ ├─ ui/badge.js escudos de equipo
│ ├─ audio/audio.js WebAudio procedural
│ ├─ app/G.js globales mutables (puente temporal a la Fase 4)
│ └─ styles/base.css
├─ public/legacy/index-v0.html monolito congelado, para comparar A/B
├─ tests/golden/ línea base de determinismo
├─ ARQUITECTURA.md plan de modularización y camino al online
Desde el menú, ⚙ Opciones · Controles:
Durante el partido, el botón ? de abajo a la izquierda muestra u oculta el panel de controles. En el móvil arranca oculto para no tapar el campo.
La interfaz se adapta a pantallas estrechas: marcador compacto, minimapa más
pequeño, menú en una sola columna y panel de controles plegado. Usa 100dvh,
así que la barra del navegador no recorta el campo.
Jugar solo o con gente en la misma máquina no pide cuenta: se crea un perfil anónimo y listo. La cuenta hará falta únicamente para jugar online, y el perfil local se subirá en lugar de perderse. El diseño completo está en ARQUITECTURA.md §7.
En el menú, elige Jugadores: 1-4. Cada persona configura por separado:
Cada persona tiene su anillo de color bajo su futbolista y su propia barra de energía en el HUD. El menú avisa si dos personas eligen el mismo mando o el mismo puesto.
| Acción | Teclado 1 | Teclado 2 |
|---|---|---|
| Mover | W A S D | Flechas |
| Pase | J | , |
| Tiro | K | . |
| Sprint | L / Shift izq. | / / Shift der. |
| Cambiar jugador | Espacio | Enter |
Pulsa 🏆 Jugar torneo para disputar la Superliga Estelar: liga de todos contra todos entre los 8 equipos (7 jornadas, 28 partidos). Juegas los partidos de tu equipo y el resto se simulan según la fuerza de cada plantilla. Entre jornada y jornada verás la tabla de posiciones (PJ, G, E, P, GF, GC, DIF, PTS) y los resultados de la última fecha. Al terminar se corona al campeón.
| Acción | Teclado | Joystick / Mando |
|---|---|---|
| Mover | W A S D o Flechas | Stick izquierdo |
| Pase | J | A |
| Tiro | K | B / X |
| Sprint | L o Shift | RT / RB |
| Cambiar jugador | Espacio | Y |
| Barrida (sin balón) | K | B / X |
| Cabezazo (balón alto) | J o K | A / B |
| Cambiar formación | F | — |
| Pausa | Esc | — |
La cámara es lateral (estilo transmisión): los arcos quedan a izquierda y derecha, y tu equipo ataca hacia la derecha. Los controles son relativos a la cámara:
Davanza hacia el arco rival.
El sprint no es infinito. Correr a tope vacía la barra de energía en unos 12 segundos; al agotarse, el jugador pierde punta de velocidad hasta recuperarse trotando o caminando. Las velocidades son realistas (~7 m/s corriendo, ~9 m/s esprintando): cruzar el campo lleva unos 12 segundos, no uno.
Además, a más velocidad, menos control: al esprintar el balón se separa del pie y cuesta más manejarlo. Para regatear fino, suelta el sprint.
Conecta el mando y pulsa cualquier botón para que el juego lo detecte (indicador abajo a la derecha: Teclado / Joystick).
F:
4-3-3, 4-4-2, 3-5-2 y 5-3-2.El partido lo simula un servidor; los clientes mandan lo que aprietan y dibujan lo que contesta. Nadie puede hacer trampa moviendo su jugador desde la consola: el servidor es la única autoridad sobre el balón, la posesión, las faltas, las tarjetas, el reloj y hasta sobre quién controla a qué jugador.
cd server && npm install # una sola vez
npm run servidor # desde la raíz: ws://localhost:2567
npm run dev # el juego, en otra terminal
En el menú, 🌐 Jugar online te pide primero una cuenta: nombre y contraseña, y listo. Jugar solo, el torneo y varios en la misma máquina no piden nada — el registro aparece justo en el momento en que hace falta, no en la pantalla de carga. La sesión queda guardada, así que la próxima vez entrás directo.
No se pide correo (menos datos tuyos que guardar), y el precio de eso es que
no hay recuperación de contraseña: si la perdés, hay que borrar la cuenta
a mano en el servidor. Los detalles, y cómo delegar todo esto en Supabase para
abrirlo al público, están en server/README.md.
Ya dentro tenés la lista de salas abiertas —se puede entrar sin que nadie
te pase nada— o Crear sala, que te da un código de cuatro letras (ej.
WBPC) para compartir. Cada uno elige equipo y puesto y pulsa Estoy listo;
el anfitrión (quien creó la sala) empieza el partido cuando están todos.
Los puestos que nadie tome los juega la IA de siempre, así que una sala es
jugable con dos personas o con ocho — no hacen falta 22.
Si se te cae internet no pasa nada: tu jugador lo toma la IA en el acto, el partido sigue para los demás, y tenés un minuto para volver al mismo puesto. El juego reintenta solo y te lo dice en pantalla.
Si alguien cierra la pestaña, su jugador no se queda plantado: se libera el asiento y la IA lo retoma en el mismo tick. El partido no se interrumpe.
Para apuntar a un servidor que no sea el de tu máquina:
VITE_SERVIDOR=wss://tu-servidor npm run build.
HNF4: sobre 35 ticks comunes, la huella del estado
que recibió cada una es la misma (9ffac5ea). Ven exactamente el mismo
partido.ana-prueba y beto-prueba) en la sala WBPC, cada
una con su asiento. Sin cuenta, el servidor rechaza entrar a cualquier sala.localStorage,
ni en el log del servidor. Sólo se guardan sal y hash.cd server && npm run probar — 45 comprobaciones entre cuentas, salas,
anfitrión, listo, echar y reconexión.node scripts/sim90.mjs: 90 s de partido en Node en 76 ms, con el mismo
hash que el navegador (f25882e0).node scripts/salas-bench.mjs 40 600: 7,4 µs por tick y sala; cuarenta
salas simultáneas usan el 1,78 % del presupuesto de 16,6 ms.El cliente no espera al servidor. Simula el partido entero por su cuenta, va unos ticks por delante, y cuando llega el estado autoritativo vuelve a ese instante exacto y repite la historia con sus entradas. Apretás y tu jugador arranca en el mismo fotograma, con 40 ms de ping o con 400.
No es "predecí tu jugador e interpolá el resto", que es lo que hunde el netcode de un juego de fútbol: el balón se pega a los pies del que lo lleva, así que si predecís tu cuerpo pero interpolás la pelota, la ves un metro por detrás tuyo. Aquí se resimula el partido completo, balón incluido.
Medido con node scripts/rollback-bench.mjs <ms>:
| ida y vuelta | error medio | mediana | p95 | coste de resimular |
|---|---|---|---|---|
| 60 ms | 0,26 cm | 0,00 cm | 0,00 cm | 0,13 ms (8 ticks) |
| 120 ms | 0,05 cm | 0,00 cm | 0,00 cm | 0,18 ms (12 ticks) |
| 250 ms | 0,11 cm | 0,00 cm | 0,00 cm | 0,26 ms (19 ticks) |
| 400 ms | 0,22 cm | 0,00 cm | 0,00 cm | 0,33 ms (28 ticks) |
El plan daba por bueno un error de 5 cm; la mediana es cero: el cliente acierta el futuro exacto casi siempre. Las veces que falla (una de cada 400, hasta 20 cm) el desvío se cierra en el dibujo en unas décimas, sin salto.
fulbo/
├── index.html ← el juego completo (motor + UI + estilos)
├── README.md ← este archivo
├── vercel.json ← configuración de despliegue
└── .gitignore
¡A jugar! 🏆
El juego es un sitio estático: un único index.html sin proceso de compilación
ni dependencias que instalar. Se publica tal cual.
git remote add origin https://github.com/USUARIO/fulbo.git
git push -u origin main
Opción A — desde la web (más simple): entra en vercel.com → Add New Project
→ importa el repositorio de GitHub → Deploy. No hay que tocar ninguna opción:
Vercel detecta que es estático y sirve index.html desde la raíz. Cada git push
vuelve a desplegar automáticamente.
Opción B — desde la terminal:
npx vercel --prod
En el repositorio: Settings → Pages → Source: Deploy from a branch →
rama main, carpeta / (root).
Nota sobre el mando: la API de Gamepad requiere HTTPS (o
localhost). Tanto Vercel como GitHub Pages sirven por HTTPS, así que los joysticks funcionan sin problema una vez desplegado.
Desde la Fase 1 del refactor, la simulación corre a paso fijo de 60 Hz y todo el azar que afecta a las reglas pasa por un RNG con semilla. El azar cosmético (piel, pelo, césped, público, confeti) sigue siendo libre: esa separación es exactamente la frontera entre núcleo y render.
__dbg)Abre la consola del navegador durante un partido:
__dbg.seed(12345) // fija la semilla
__dbg.hash() // hash del estado autoritativo
__dbg.golden(90, 12345) // 90 s de IA vs IA, hash por segundo
__dbg.tick // número de tick de simulación
__dbg.forceSetPiece('penal', 0) // penal|corner|banda|puerta|falta, equipo
__dbg.card('roja', 1, 6) // tipo, equipo, índice de jugador
__dbg.forceOffside()
__dbg.goal(0) // gol del equipo indicado
__dbg.teleportBall(x, y, z)
__dbg.setClock(85) // adelanta el reloj del partido
El golden master es el oráculo del refactor: __dbg.golden(90, 12345) debe
devolver siempre los mismos 90 hashes. Si una fase del refactor los cambia sin
que lo hayas decidido, es un bug — y sabés exactamente en qué commit.
Con los comandos de forzado, lo que antes exigía veinte minutos de jugar con suerte ahora se comprueba en minuto y medio:
| # | Comprobación | Cómo |
|---|---|---|
| 1 | El menú carga y se eligen equipos | clic en la lista |
| 2 | Dos personas, teclado 1 y 2, equipos distintos | selector Jugadores: 2 |
| 3 | Ambas mueven su jugador de forma independiente | WASD y flechas a la vez |
| 4 | Gol, celebración y goleador | __dbg.goal(0) |
| 5 | Repetición en cámara lenta y vuelta al saque | esperar tras el gol |
| 6 | Córner: los dos equipos cargan el área | __dbg.forceSetPiece('corner',0) |
| 7 | Saque de banda | __dbg.forceSetPiece('banda',0) |
| 8 | Penal: área despejada y ejecutor colocado | __dbg.forceSetPiece('penal',0) |
| 9 | Tarjeta roja: expulsión y equipo con 10 | __dbg.card('roja',1,6) |
| 10 | Fuera de juego señalado | __dbg.forceOffside() |
| 11 | Cambio de formación en caliente | tecla F |
| 12 | Pausa y una jornada de torneo | Esc · botón 🏆 |
public/legacy/index-v0.html es el monolito congelado (accesible en /legacy/index-v0.html) antes del refactor. Ábrelo en
otra pestaña para comparar sensación y aspecto tras cada fase. No se toca nunca.