尚未检测到更新。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.