尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A tour to our Church
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A little side-scrolling pixel tour of our church. A visitor opens the link, walks in from the main road, and meets pixel versions of the people who actually go here — who idle, wave, and have something to say.
Built with Next.js 16 + Phaser 3 + Tailwind 4, deployed free on Vercel.
npm install
npm run dev # → http://localhost:3000
Controls: ← → walk · SPACE jump · E or ENTER talk · M map · YOU to change your look · ♪ button for sound On a phone, use the on-screen buttons.
It opens with a short film with its own music: the camera glides through each zone, then the title and PRESS SPACE TO START (TAP TO START on a phone). The SKIP button, or Esc, jumps to the title; a stray tap or key never does. Most browsers hold the music until the visitor's first tap or key press, so a TAP FOR SOUND button shows until then. Anyone whose device asks for reduced motion gets the title on its own.
Pressing start opens Your visitor, where the player makes the visitor their own — see below — and then START THE WALK.
Almost everything about the tour is data, not code:
| File | What it controls |
|---|---|
data/zones.ts | The map. Zones, their background layers, ground height, signposts, and which zone connects to which. |
data/characters.ts | Every person. Their name, role, where they stand, what they say, whether they wave. |
And for the opening, data/intro.ts: which zones the film shows, where the
camera glides from and to in each, the caption under each shot, the title, and
the film's own music track.
Add a person, add a zone, rewrite the pastor's dialogue — all of it happens in
those two files. game/scenes/ZoneScene.ts builds every zone from that data,
so there is no new code to write when the tour grows.
The names in
data/characters.tsright now are placeholders. Replace them with your real church family.
After the title, and from the YOU button any time after, the player makes the visitor their own:
game/visitor/paint.ts), so its
shading carries over to any colour, on every frame — standing, walking and
jumping. Only what the look has is offered: a dress has no trousers row.data/characters.ts takes it with {, name} — see the top
of that file — and still reads naturally when no name was given.raw/characters/, run
npm run character-sheets, and it appears on the screen.The swatches are in data/visitor.ts, free to edit. The choice is
remembered in the browser, so a returning player just presses START THE WALK.
npm run character-sheets also writes each look's parts mask — which
pixel is hair, skin, shirt, trousers, shoes or eyes — and a magnified copy of
the art over its mask to .preview/visitor-<look>-parts.png. Look at it after
adding a drawing: the colours only land right if the mask does.
The screen's preview doesn't enlarge the 48-pixel game sprite, which would be
blocks of colour. It shows a sharp copy of each look, cut from the same
drawing at five times game size (player-…-hd.png, with its own mask), so the
face, the collar and the buttons are all there, recoloured the same way. The
game itself plays the small sprite.
The fonts — Press Start 2P and Geist — are in app/fonts/ with their
licences, rather than fetched from Google Fonts at build time, so a dropped
connection can't swap them for Arial.
Zones 01, 02 and 03 are drawn from photographs of the real place, and zone 04 is finished art brought in whole. All seven people are code-drawn placeholders. The game is fully playable at every stage — that is deliberate, so drawing the art never blocks the code and the code never blocks the art.
Three routes, and you can mix them freely:
raw/. What to shoot, and why a pan beats a single frame. The sanctuary
and the people are the two sets still missing.npm run art will never overwrite what you draw.npm run pixelify. Fast, and good for scenery;
it cannot give you your own people.npm run art — regenerate backgrounds procedurally from
scripts/generate-art.mjs. Instant and on-palette.npm run art -- --check validates any art on disk — hand-drawn included — for
size, palette, transparency and tiling.
You replace assets one at a time. A half-finished art folder still runs.
app/page.tsx → renders <GameShell />
components/
GameShell.tsx the bridge: holds UI state, wires React ↔ Phaser
PhaserGame.tsx owns the Phaser instance (client-only, StrictMode-safe)
TouchControls.tsx on-screen D-pad / JUMP / TALK
DialogueBox.tsx speech panel with typewriter text
MapOverlay.tsx the "‹ MAP" zone picker
ZoneBanner.tsx "#01 Main Road" title card
IntroOverlay.tsx the opening's letterbox, captions and PRESS SPACE TO START
MusicPanel.tsx the "♫ MUSIC" song picker
VisitorPanel.tsx "Your visitor": colours, glasses, name, body and hair
SoundBoard.tsx every sound on one page (/sounds, dev only)
game/
config.ts Phaser config (pixelArt, FIT scaling, gravity)
bus.ts tiny typed event bus — the ONLY React ↔ Phaser link
audio/
engine.ts the one sound engine: mixing, mute, zone crossfades
sfx.ts every one-off sound, as a recipe
ambience.ts each zone's background sound, and the people's loops
kit.ts the noise, tones and envelopes they're made from
music.ts background songs: play, pause, next, volume, your own
visitor/
look.ts the player's chosen look, remembered; names in lines
paint.ts recolours the visitor's drawing, adds the glasses
texture.ts turns that into the game's "player" texture
placeholders.ts generates stand-in art so the game runs without PNGs
types.ts shapes for zones and characters
scenes/
PreloadScene.ts loads real art, fills gaps with placeholders
ZoneScene.ts the one data-driven gameplay scene
data/ ★ zones.ts, characters.ts, intro.ts, music.ts and visitor.ts — edit these
public/assets/ ★ your PNGs go here
Phaser draws the world. React draws the interface. They never import each
other — they only pass messages through game/bus.ts. That's what keeps the
dialogue text crisp (real DOM text, not upscaled canvas pixels) and keeps hot
reload from breaking the UI.
Three details in the Phaser setup are load-bearing and shouldn't be changed casually; they're commented where they live:
pixelArt: true — without it the browser blurs every pixel of your art.Scale.FIT at a fixed 640×360 — one build that fits a phone and a projector.ssr: false + a StrictMode-safe cleanup — Phaser reads window on import,
and React 19 runs effects twice in dev.There are no sound files. Every sound is synthesized in the browser as it
plays, by game/audio/: a footstep is a burst of filtered noise, a chime is
two soft tones on one pentatonic scale, the traffic is noise filtered down to a
rumble. Nothing loops audibly, and there is nothing to license.
data/zones.ts: ambience (highway, street,
yard, sanctuary) and floor (pavement or tile, for footsteps).data/characters.ts: sound.call plays with their
bubble (Kiwi's bark), and sound.loop plays from where they stand, fading
with distance (the worship pad around Kuya Aldhie).data/music.ts:
ones licensed for publishing go in that file, with their MP3s in
public/assets/audio/music/; ones for your own computer only go in
data/music.local.json, which git ignores along with their MP3s. A copy
without that file, like this public repository, has no local songs.music in data/intro.ts. It is not in
the ♫ MUSIC list, and fades out as the tour starts. While it plays, the same
rule holds: the worship pad stays silent.Browsers keep a page silent until you first press a key or tap, so the sound starts with your first step.
To listen to or retune a sound, run npm run dev and open
localhost:3000/sounds. It has a button for every
effect, each zone's ambience with a slider to stand anywhere along it, and
Measure every sound, which renders each one silently and checks its level
against a target. Levels live in the recipes (gain: in sfx.ts and
ambience.ts); measure again after changing one.
Push to GitHub, then import the repo at vercel.com. No configuration needed — it's a stock Next.js app. Every push redeploys.
Not built yet, and intentionally so. data/characters.ts already uses the
field shape a Supabase row would have, so moving dialogue into a database is a
one-file change when you want it.
The plan when you're ready: a mailbox object in the sanctuary, one guestbook
table with an approved boolean for moderation, and a Row Level Security
policy allowing anonymous insert but only reading approved rows.