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,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Arcade tennis in the browser, in the spirit of the 16-bit classics: a court seen
from above, two small players, one button, and a ball that has to land in.
1 player against the CPU, 2 players on one keyboard, or online with a
four-character room code.
No dependencies, no build step. HTML, CSS and JavaScript exactly as the browser
receives them.
It is the second game built this way. The first is
websoccer, and the two share their
engine — see Shared with websoccer.
Then open http://localhost:5173/. There is no npm install; there are no
packages to install.檢視完整 README ↗Arcade tennis in the browser, in the spirit of the 16-bit classics: a court seen from above, two small players, one button, and a ball that has to land in. 1 player against the CPU, 2 players on one keyboard, or online with a four-character room code.
No dependencies, no build step. HTML, CSS and JavaScript exactly as the browser receives them.
It is the second game built this way. The first is websoccer, and the two share their engine — see Shared with websoccer.
git clone https://github.com/markclausing/webtennis.git
cd webtennis
npm start
Then open http://localhost:5173/. There is no npm install; there are no
packages to install.
| Player 1 (blue) | Player 2 (red) | |
|---|---|---|
| Move | W A S D | Arrow keys |
| Serve / hit | Space | Enter |
| Pause | Esc |
The court is not the real shape. A singles court is nearly three times as long as it is wide, and at that shape there is time to stroll to every ball - the width is the only thing you have to cover in a hurry and there was not enough of it. This one is about two to one: shorter, wider, and it makes you choose which ball to go for. The ball speeds came down with the length, or the same shot would simply arrive sooner in a smaller court, which is a different game again - at the old speeds one point in seven was an ace.
The rules themselves are the real ones, because they are what makes tennis tennis: the ball has to land in, it may bounce once, a serve has to find the diagonal box, and the score runs fifteen, thirty, forty, deuce, advantage. A set is won by two clear games, and the score goes up in the middle of the screen after every point, in the words the umpire uses.
Two things decide points beyond getting to the ball.
You can volley. Anything up to head height can be played out of the air, so standing at the net is a real position rather than a place to watch balls go past. The CPU answers it by lobbing you, which is what a lob is for.
Pace forces errors. The speed of the ball arriving eats into the wind-up you had, so a shot struck flat out is awkward to handle however early your opponent started - it cannot be outrun, because it is charged at the moment of contact. Measured against the CPU, feeding it the same ball at different speeds: at 262 px/s on arrival it comes back 52% of the way down the court, at 336 it comes back short and sitting up, and at 414 and above it does not come back at all.
The net opens the angles. How sharp a shot you can play depends on how far up the court you are standing: from the baseline a full sideways swing moves the ball 26% of the way to the sideline, from the service line 45%, and from the net 53% - and aftertouch adds to all of them. The price is paid automatically. Stand that close and the next ball is on you before you have finished admiring this one.
The CPU does not come to the net, so for now that half of it is yours alone.
Three difficulty settings, and what separates them is measured rather than guessed. The strongest lever is a delay before the CPU sets off after the ball — reading the shot early is most of what makes a good player look quick. The second is how far its aim strays, and that aim is allowed to stray past the lines. That last part sounds like a detail and is not: an earlier version clamped every shot into the court, which meant the CPU could not miss, made no errors at any level, and easy and hard finished level with each other. Games taken off HARD over four matches: EASY 6, NORMAL 17, HARD 22.
Points end the way they do in tennis: at HARD against itself, 93% of them are winners and 7% aces; against EASY, a quarter of the points are errors.
Eleven files are identical in both games — the input mask, the touch controls, the lockstep netcode, the relay, the Cloudflare Worker, the high score table, the three-letter name entry, the formant speech synthesiser and the maths. They are shared by being the same file in both repositories rather than by a package, because neither game has a build step and neither is going to grow one for this.
Copying is only worth anything if somebody notices when the copies part ways:
node tools/sync-shared.js # are they still the same?
node tools/sync-shared.js --pull # take websoccer's copy
node tools/sync-shared.js --push # send this one back
It runs as part of npm test, so a change on one side shows up as a failing test
on the other rather than as a mystery six months later. It expects websoccer to
be checked out beside this repository, and says so and passes if it is not.
Nothing that knows what sport it is gets shared. The simulation, the court, the
sounds and the words are each game's own — a shared file full of if (tennis)
would be worse than two files.
The score is called out loud by the same formant synthesiser websoccer uses for its commentator: a buzz through three sharp filters, which is roughly what the speech chips of the era did. The voice is shared; the words are not. Tennis is the easy case for a talking scoreboard, because the calls have not changed in a century — love, fifteen, thirty, forty, deuce, advantage, game.
npm test # the lot
npm run test:sim # whole matches headless: determinism, scoring, the rules
npm run test:shared # the shared files, against websoccer next door
The scoring is tested by driving points straight through the scorer rather than by playing, because deuce, advantage and advantage-lost take a long time to reach by playing and are exactly where a scoreboard goes subtly wrong.
Online play and the shared high score board run on a Cloudflare Worker of this game's own — the same code websoccer uses, deployed separately on purpose. The board lives in a single Durable Object under one key, so one Worker serving both games would mean one table with football and tennis results mixed into it. Two Workers, two boards. They post into the same Discord channel, which is only a webhook address and does not care which game is talking.
cd worker && npx wrangler login && npx wrangler deploy
npx wrangler secret put DISCORD_WEBHOOK # optional: announce new entries
npx wrangler secret put ADMIN_KEY # optional: lets you clean the board
Then put the address it prints in src/config.js as DEFAULT_RELAY, with
wss://.
MIT.
An original tribute to the top-down sports games of the nineties: no code, artwork or other parts of any existing game, and no affiliation with their makers or rights holders.