OhMyGame
OhMyGame은 AI로 게임을 만들 수 있는 오픈소스 스튜디오로, 디자인·구축·플레이테스트·출판을 하나의 앱에서 모델 키와 함께 수행합니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗ブラウザ向け3Dドライビングゲーム「Prefix D」(個人開発)。 自分で運転して街を走るほか、タイムアタック・NPC対戦・オンラインで友だちと走れる。
起動するとタイトル画面から始まる。どれかのキーを押す(スマホはタップ)とトップメニューになり、プレイモード(自由に走る・タイムアタック・NPCと対戦・ライバルとバトル・オンライン)を選んで、コース・対戦相手・ルール・自分の車・時間帯と天気を1画面ずつ選び、最後に「スタート」。左に一覧、右に選んでいるものの詳しい説明が出る(コースは上から見た形・高低差のグラフ・全長・最大勾配・道幅・おすすめの車、車は回る車の見た目と性能、ライバルは写真・ひとこと紹介・強さ・マシン)。走っている間は Esc(スマホは ☰)でメニュー画面を開き、「タイトルに戻る」で最初のタイトル画面へ戻れる。スマホは ☰ をもう一度押すと走りに戻る。レース中はメニュー画面の「コース・車などを変える」とゴールの画面の「コース・車など」で、同じモードのまま、そのモードの選ぶ画面(ライバルとバトルなら対戦相手・ルール・コース・車・時間帯と天気、NPCと対戦なら NPC の強さも)をもう一度たどって選び直せる。どの画面もいまの内容が選ばれた状態で始まるので、変えたいところだけ変えればよい(タイトルに戻らなくてよい)。自由に走っている間も、メニュー画面の「コース・車などを変える」でコース・車・時間帯と天気を選び直せる。タイトル画面の後ろには、峠・首都高のどれか(毎回変わる。練習用のコースと、作るのに時間のかかる超ロング・八方ヶ原・いろは坂は除く)が映る。?map=3 のようにマップを指定して開いたときは、タイトルを飛ばしてすぐ走れる。
| キー | ゲームパッド | 操作 |
|---|---|---|
W / ↑ | RT(または A) | アクセル |
S / ↓ / Space | LT | ブレーキ(停止中に押し続けると後退) |
A D / ← → | 左スティック | ハンドル |
キーボードのハンドルは、軽く叩くと少しだけ切れ、押し続けるほど深く切れる(0.25秒で目いっぱい)。細かい修正がしやすい。さらに最初は「速さに合わせる」で、押し続けても、その速さ(とブレーキ)でタイヤが曲がれる限界の角度の 1.2 倍までしか切らない(keySteerLimit。低速のヘアピンでは目いっぱい切れる)。キーは押す・離すしかないので、そのままだと高速で少し押しただけで限界を大きく超えてアンダーステアになるため。軽く叩けば限界の内側、押し続ければほんの少しアンダー。☰ メニューの設定「キーのハンドル」で 速さに合わせる/なめらか(限界を気にせず深く切れる)/すぐ全開(押した瞬間から目いっぱい)を選ぶ(src/core/input.ts)。コントローラー・スマホは自分で切る量を決められるので、そのまま。
速度の上に、ハンドルのメーターを出す(設定の「ハンドルの表示」で消せる)。いまの切れ角(「左 12%」。目いっぱいが 100%)と、その速さで曲がれる限界の帯(緑)を出し、はっきりアンダーステアになっている間はバーと文字がオレンジになる。高速では限界がとても小さい(標準の車で 60 km/h なら目いっぱいの 12%、90 km/h なら 6%)ので、小さな角度ほど目盛りを広げている(平方根の目盛り。src/ui/steerMeter.ts)。
T(またはトップメニューの「タイムアタック」)で、スタートラインの手前に並ぶ。アクセルを踏むとカウントダウンが始まり、タイムを計る(自分のタイミングでスタートできる)。レース用のマップにいればそのコース、それ以外なら首都高へ移動する。走っている間は N でコースを替えられる。
| コース | 内容 |
|---|---|
| 首都高(都心環状線) | 都心環状線のようなオリジナルの首都高(一周 約4.5km)。実際の道の形のデータは使わず、名所の並び(内回り)をなぞった。高架の2車線で両側は壁。長い直線はなく、京橋から日本橋川の上へ出て江戸橋 JCT のきつい左、神田橋〜竹橋の S 字、北の丸の下の千代田トンネル、三宅坂〜霞が関の中速コーナー、谷町 JCT のきつい左、一ノ橋〜芝公園、浜崎橋 JCT のきつい左と汐留の S 字を通って京橋に戻る。1周 |
| 峠(ヒルクライム) | ふもとから頂上まで 150m 上る一本道(約2km)。半径 150m の高速コーナー、S 字、3つのヘアピンが混じる。坂で加速が鈍る |
| 峠(ダウンヒル) | 同じ峠を頂上から下る。スピードが乗るので早めのブレーキが大事 |
| 峠(山越え) | 100m あまり上って峠を越え、反対側へ下る(約2.1km)。頂上は見通しのよい高速の左右 |
| 峠(Uターン) | 谷沿いに上り、頂上のヘアピンで折り返して 38m 横を並んで下る(約2.2km)。行きの高速コーナーは帰りの低速コーナーになる。ゴールはスタートのそば |
| いろは坂(ヒルクライム) | 第二いろは坂(上り専用・約9.4km・高低差 440m・20 カーブ)。馬返から、谷沿いの緩い上りの高速区間を抜け、斜面に取り付いて 12 個のヘアピンを積み重ねて上り(ヘアピンの間も、ヘアピンのすぐ前後の短い直線のほかは左右にうねり続けるスネークヘアピン。木が道路のすぐ脇まで茂り、山側は道路のすぐ脇から岩の崖が立っていて見通しが悪い)、明智平の先のトンネルを抜けて中禅寺湖のそばの頂上へ。2 車線の一方通行で道幅は広い。上りも下りも、実際の道の形のデータではなく、全長・カーブの数・高低差と区間の並びを実際に合わせている |
| いろは坂(ダウンヒル) | 第一いろは坂(下り専用・約6.5km・高低差 440m・28 カーブ)。頂上の剣ヶ峰付近の中速の左右から、上側のつづら折り(ヘアピン 9 個)、斜面のトラバース、下側のつづら折り(ヘアピン 8 個)を下って馬返へ。ヘアピンの間も、ヘアピンのすぐ前後の短い直線のほかは左右にうねり続けるスネークヘアピン。木が道路のすぐ脇まで茂り、山側(すぐ上を一つ前の区間が通る側)は道路のすぐ脇から山を切り開いた岩の崖が立っていて見通しが悪い。崖にはぶつかる(落ちない)。馬返の手前の最後は、谷を橋で渡る。ヘアピンの内側を突っ切って、段差から下の道路へ飛び降りる近道(下記)がある |
| 激坂(ヒルクライム) | 約1.4km で 230m 上る急坂(平均 17%)。きついヘアピンが 3 つ。パワーのない車は伸びなくなる |
| 激坂(ダウンヒル) | 同じ激坂を下る。ブレーキが遅れるとコーナーで止まりきれない |
| 峠(ロング) | 上りと下りを何度もくり返す約2.9kmの長いコース。高速コーナーとヘアピンが交互に来る |
| 峠(超ロング) | 上りと下りをくり返す約6.2kmのいちばん長いコース |
| ロングストレート(ヒルクライム / ダウンヒル) | 峠の途中に、まっすぐな 800m のロングストレートと、ごく僅かに左右へくねる 650m のロングストレートがある。前後と間はふつうの峠のコーナー(約2.9km、高低差 170m) |
| 橋とトンネルの峠(ヒルクライム / ダウンヒル) | 約4.8km の峠。コーナーの続く区間の途中で 3 回谷を橋で渡り、短いトンネル(90m)と長いトンネル(360m)を抜ける(高低差 230m) |
| 八方ヶ原風の峠(往復) | 4つのセクションに分かれた峠(道路 3.5km)を往復する(約6.8km)。第1は道幅が狭く木が茂って見通しの悪い古い道、第2は拡幅工事を終えた新設の広い道(真新しい舗装と白線、両側に白いガードレール、削った山の斜面は吹き付けたばかりのコンクリート)だが、路面が荒れていて車が跳ねる。ハイポイントコーナーの先の第3はきついダウンヒルで、高さ 55m の谷に架かる大きな橋を渡ると、第4はきついヒルクライム(道幅 10m で、追い抜きやすい)。330° 回り込むループ橋、カーブの途中の 240m のトンネルを抜け、上り切った先の平らな道を少し走って頂上の広場で U ターンし、同じ道を戻ってきてスタートラインがゴール |
| 秋名(ダウンヒル) | 秋名(榛名山)のダウンヒル(約4.3km・高低差 330m)。実際の道の形のデータではなく、原作・アニメ・アーケードで知られる区間の並びを再現: 緩い下りから中速の左右、長い下りのストレートの先の最初のヘアピン、連続コーナー、スケートセンター前のいちばん長い直線、左右交互の5連続ヘアピン、S字、最後の直線。ヘアピンなどのカーブの内側の路肩には側溝があり、内側のタイヤを落とす(車体の中心を道路の端から 0.2〜0.8m 内側まで寄せる)と溝の壁に引っかかって、横に滑らずに曲がれる力が 4割増える(溝落とし。車体が内側に傾いてゴトゴト揺れる)。コースは src/map/maps.ts の AKINA_DOWN_MAP、溝は RoadPiece.gutter |
| 妙義(ヒルクライム) |
走っている間は、これまでの周回タイム(自己ベストの同じ周との差)と、右上に地図(自分は赤、ゴーストは水色)を出す。地図は自由に走る・オンラインの部屋でも出る(街・教習所は道路のつながりを描く。部屋の他の人の位置も出る)。地図の出し方は設定の「マップ」で選ぶ: 周辺・回転(最初はこれ。アーケードのように自分の周り 460m 四方だけを、進む向きが上になるよう回して出す。自分は少し下寄りで前が広く見える)/周辺・北が上(回さずに自分を真ん中に)/全体(コース全体)。
峠の高低差は見た目と坂の重力だけで、衝突の計算は平らなまま(src/map/terrain.ts)。
いろは坂(ダウンヒル)では、ヘアピンの内側(行きと帰りの道路にはさまれた所)がコンクリートの面になっている(いろは坂の「インベタのさらにインベタ」)。コーナーの手前で内側へ飛び込めば、道路よりずっと小さく回れる。面は上の道路の高さから下っていき、下の道路の縁で段差(最大 2.5m)になって終わるので、段差から飛び降りると真下が下の道路。面の上は落ちず、コースアウトにもならない(周回も進む)。浮いている間はハンドルもアクセルも効かないので、向きを決めてから飛ぶ。面の外側は崖(src/map/roadShortcuts.ts。180° の円弧の前後が直線のヘアピンを探して帯を作る)。
峠では、道路の外は少しの路肩の先が崖(岩肌の色の斜面)になっている。路肩より外へ出ると「コースアウト!」と出て落ち、1 秒ほどで落ちた地点の少し手前に止まった状態で戻される(その間もタイムは進む)。カーブの外側にはガードレールがある。NPC も同じ。
峠の橋は深い谷(底に川)に架かり、トンネルは山の中を抜ける。橋の上とトンネルの中は両側が壁で、落ちることはない。地図の記号は =(橋)・~(川)・#(トンネル)。
練習用の小さな街と教習所コースを除くすべてのコースは、タイルではなく直線と円弧をつないだ中心線で道路を描く(src/map/road.ts)。タイルのコースのように「90° 曲がって直線」をくり返すのではなく、半径(大きいほど速く回れる)と曲がる角度を区間ごとに決めるので、半径 200m の高速コーナーから半径 16m のヘアピンまで、なめらかな円のカーブになる。周回のコースは最後の直線・円弧・直線を計算して始まりにつなぎ(closeLoop)、ダウンヒルは上りの道路を逆向きにたどる(reverseRoad)。峠(Uターン)の帰り道は、行きの道を横へずらした道を逆向きにたどる(alongside。内側になるカーブは半径が小さくなる)。区間ごとに高さ・道幅・橋・トンネル・荒れた路面を指定でき、道路が自分の上を越える立体交差も作れる。衝突の計算は平らなままなので、立体交差では上の道路と下の道路の壁を別の層に分け、車はいま走っている道の層の壁と車にだけぶつかる。どちらの道にいるかは、直前にいたコース上の位置から近くだけを探して追う(src/race/roadCourse.ts)。往復のコースでも、行きと帰りを向きで見分ける。
荒れた路面は、舗装全体がゆるく波打っている(継ぎ目だけの段差ではない)。波の高さは ±3.5cm、長さ 14m と 30m の波を重ねてあり、180km/h くらいまでは揺すられるだけだが、それより速いと波の頂上で少し浮く(浮いている間はタイヤが効かない)。
コースのタイル(中心線で描く道路では 10m ごとの区間)を順にたどった場合だけ周回に数える。コースを外れて先へ近道すると「近道はできません」と出て、1.2 秒後にコースを外れた所へ自動で戻される(どこまで戻ればよいか迷わないように)。逆向きに走ると「逆走しています」と出る。
自己ベスト(3 周の合計と最速ラップ)は、コースと車の組み合わせごとにブラウザ内(localStorage)に保存する。コースはコースの id と「形の版」(コースの定義から作る短い英数字。src/map/courseRevision.ts)で区別し、コースの形を直すと前の形で走った自己ベスト・ゴーストは使われなくなる(ランキングも形の版ごとに別になる)。名前や標識を変えただけなら版は変わらない。道路の作り方のコードを変えてコースの形が変わったときは COURSE_BUILDER_VERSION を上げる。設定の「プレイデータを消す」で、自己ベスト・ゴースト・勝ったライバルの記録を消せる(確認の画面で「消す」を選んだときだけ消す。名前などの設定とランキングの記録は残る)。
ブラウザに残すデータには版(car-game:save-version)をつけ、起動のたびに古い版のデータを今の版の形に直す(src/game/saveVersion.ts)。データの形を変えるときは版を上げて、直す手順を足す。あとから車を手に入れる仕組みを作るなら、持っている車を一覧で保存し、一覧にない車は使えないことにすれば、今までのデータには一覧がないので最初の車だけになる。ただしブラウザのデータは本人が書き換えられるので、確実に防ぐにはサーバーで持つ。
自己ベストの走りは、半透明の水色の車(ゴースト)として一緒に走る。
R でスタートからやり直し、もう一度 T でやめる。
L(スマホは ☰ メニューの「ランキング」)で、コースごとのランキング(上位 20 件)を、順位・名前・車・タイム・日付(年月日)の列で出す(表の上に列の名前)。左のコースの一覧(縦向きのスマホでは横に並ぶ札)でコースを、上の車の札で車を選ぶ。最初は「すべての車」の記録をまとめて出し、車を選ぶとその車だけのランキングになる。オンライン用のサーバーを使う。
タイムアタック・NPC 対戦・ライバルとのバトルでゴールすると、結果の「リプレイ」(V)で走りを見返せる。ランキングの「リプレイ」では、他の人の走りも見られる。
src/race/ghost.ts の GhostRecorder、src/race/replay.ts)。全員ゴールして 2 秒経つまで(NPC が遅ければ自分のゴールから 30 秒まで)記録する。タイムアタックでは、一緒に走った自己ベスト・ランキングの人のゴーストも半透明で出す。C)は運転中と同じ視点(三人称 近/遠・運転席)のあとに、リプレイだけの TV中継(車が来る所の脇に置いたカメラが首を振って追い、通り過ぎたら次のカメラへ切り替える)・横から・前から・上空 の順に替わる(src/render/replayCamera.ts)。Space 再生・一時停止、← → 5 秒戻る・進む(Shift で 1 秒)、, . 0.1 秒ずつ、↑ ↓ 再生の速さ(×0.25〜×4)、V(Tab)追いかける車を替える、C カメラ、Esc 終わる。スマホは画面のボタンで操作する。inputs:<記録の id> に保存)。操作を記録する前の記録は、位置と速さだけを見せる。コースと車は、表示名ではなく変わらない id(src/map/maps.ts の id、src/sim/vehicle.ts の VEHICLE_PRESETS の id)でランキングを分けている。表示名は自由に変えてよい(例: 「普通車」を具体的な車名にしても、id が standard のままならランキングはそのまま)。車の性能を大きく変えるなら、新しい id にすると新しいランキングになる(古いランキングはどこからも開かれなくなるだけで、消さなくても困らない)。
Y(スマホは ☰ メニューの「NPCと対戦」)で、タイムアタックと同じコースを NPC 3台と走る。NPC が前に並び、自分は最後尾からスタートする。NPC の並び順(どの色の車がどこに並ぶか)は毎回ランダム。
K(スマホはメニュー)で 初級/中級/上級/超上級 を選ぶ(?npc=legend のように URL でも指定できる。id は easy / normal / hard / legend)。強いほど理想のライン(レーシングライン)を正確になぞり、カーブとブレーキで車の限界近くまで使う。超上級は車の性能をほぼ使い切る。ひとりで走ったタイムは、超上級に比べて上級が約2〜4%、中級が約5〜8%、初級が約11〜17% 遅い。src/sim/car.ts の CONTACT_FRICTION)。壁に押し付けて曲がったり、NPC にぶつけてインをこじ開けたりすると、そのぶん遅くなる。| キー | ゲームパッド | 操作 |
|---|---|---|
C | Y | カメラ切替(三人称 近/遠・運転席。運転席は屋根の前の縁が視界の上をふさがないよう、車の形に合わせて目の高さ・前後を変える) |
Esc | メニュー画面(やり直す・コース・車を変える・レースをやめる・対戦のエントリーを始める・エントリー画面・部屋に入る・部屋から抜ける・タイトルに戻る・ランキング(タイムアタック中だけ)・設定。左下の「走りに戻る」で閉じる。スマホは ☰ をもう一度押して閉じる)。メニューの中では1つ前の画面へ戻る。↑ ↓ で選び Enter で決定 | |
N | BACK | マップ切替(順番に) |
T | - | タイムアタック |
Y | - | NPC対戦(K で強さ) |
B | - | ライバルとバトル |
P | - | 車の切替(下の表) |
F | ヘッドライト ON/OFF(朝・夜・雨・霧では最初からつく) | |
G | クラクション(押している間だけ鳴る。部屋の人にも聞こえる) | |
E | セリフ(エモート)。設定で決めたセリフを言う | |
O | オンラインの部屋に入る(部屋コードか招待リンクで参加) | |
L | ランキング(上位の人のゴーストと対戦・リプレイ) | |
V | ゴールした後の結果から、リプレイを見る | |
R | START | スタート地点に戻る |
M | サウンド ON/OFF | |
H | 操作説明の表示切替 |
| 車 | 最高速 | 0-100km/h | 特徴 |
|---|---|---|---|
| 普通車 | 180km/h | 11.0秒 | 扱いやすいふつうの車 |
| 軽自動車 | 140km/h | 14.0秒 | 遅いけれど小回りがきく |
| カプチーノ | 170km/h | 10.0秒 | FR・軽のオープンカー。とても軽く、小さく鋭く曲がる |
| AE85 | 160km/h | 11.5秒 | FR・AE86の兄弟車。軽いが非力で、坂と直線は苦手 |
| AE86 | 210km/h | 8.0秒 | FR・軽い。直線は遅いがコーナーとブレーキが強い |
| シビック (EG6) | 215km/h | 7.0秒 | FF・軽い。パワーは控えめだが曲がりやすい |
| シビック (EK9) | 225km/h | 6.5秒 | FF・高回転のVTEC。軽くて立ち上がりが鋭い |
| シルビア (S13) | 220km/h | 6.8秒 | FR・ターボ。パワーはそこそこで、お尻を流しやすい |
| シルビア (S14) | 235km/h | 6.3秒 | FR・ターボ。S13よりパワーがあり、少し大きく安定 |
| RX-7 (FC3S) | 255km/h | 5.6秒 | FR・ロータリーターボ。FDより少し重く非力だが、素直でバランスがよい。リトラクタブルヘッドライト |
| RX-7 (FD3S) | 260km/h | 5.2秒 | FR・軽くて前後のバランスがよく、速くてよく曲がる |
| ランエボIII | 240km/h | 5.5秒 | 4WD・大きなウイング。IVより少し非力 |
| ランエボIV | 245km/h | 5.0秒 | 4WD・加速とグリップが強い |
| GT-R (R32) | 265km/h | 5.0秒 | 4WD・最高速が高いが、重くてハンドルの反応が遅め |
| GT-R (R34) | 285km/h | 4.8秒 | 4WD・最高速がいちばん高い。R32より速く、高速コーナーも安定 |
src/sim/vehicle.ts の cornerGripShare)。NPC も同じで、手前でブレーキを済ませてからコーナーに入る。ハンドルは速さに関係なく最大舵角まで切れるが、曲がる力が出るのはタイヤのグリップの限界の角度まで(高速ほど小さい)。それより多く切ると前のタイヤが滑ってアンダーステアになり、曲がる力が少し落ちて(最大 25%)、タイヤが路面をこすって減速し、タイヤが鳴る。限界のちょうど手前で切り続けるのがいちばん速い(src/sim/vehicle.ts の stepVehicle)。キーボードで押しっぱなしにすると高速では切りすぎになるので、軽く叩いて舵角を調整する。車の動きを大きく変えたときは、タイムアタックの記録・ランキングを前の動きで走った分と分ける(src/map/courseRevision.ts の HANDLING_VERSION)。速くなるとグリップは少しだけ増える(ふつうは 8%)。重い GT-R(R32・R34)は低速の横Gが控えめな代わりに高速で20%ほど増え、軽い FD3S・エボは低速から強い。そのためタイトな峠や都心環状線のような首都高では FD3S・エボ、高速コーナーの多いコースでは GT-R が速い。src/sim/vehicle.ts の VEHICLE_PRESETS)が違う。見た目は src/render/carMesh.ts の CAR_STYLES。F(スマホはペダルの上の「ライト」)でつける・消す。朝・夜・雨・霧を選ぶと最初からつく。つけると前のランプが光り、テールランプも明るくなる。部屋の人の車にも反映され、近くの2台までは、その車のライトが前の道を照らす様子も自分の画面に出る。G(スマホは「ホーン」)を押している間だけ鳴る。部屋の人にも、遠いほど小さく聞こえる(160 m より遠いと聞こえない。鳴らしている間に近づく・離れると大きさも変わる)。src/render/mirrors.ts)。E(スマホは「セリフ」)で言う。言うセリフは設定の「セリフ」で自分で決める(20 文字まで。ブラウザ内に保存。空にすると最初の「俺のRについて来れるか!?」に戻る)。自分の車の上に吹き出しが出て、左に「名前 セリフ」が少しの間残る。部屋にいれば全員に出る(続けて言えるのは 1.5 秒おき)。src/game/emotes.ts。B(☰ メニューの「ライバルとバトル」)で、名前付きのライバルと 1 対 1 で走る。メニューでは 対戦相手 → ルール → コース(最初はライバルの得意なコース)→ 自分の車 → 時間帯・天気(最初は今の時間帯・天気。夜・雨が得意なライバルは夜・雨)の順に選ぶ。
ルールは2つ。
src/race/suddenDeath.ts)横に並ぶルールでは、ゴールの画面にコースを区間(700m くらいずつ、3〜8 区間)に分けたライバルとのタイム差を出す。上から見たコースの地図で、離された区間は赤、速かった区間は緑。いちばん離された区間も書くので、どこを練習すればよいかが分かる。周回コースは、同じ区間の周ごとの差を足す(src/race/sections.ts)。
| ライバル | 車 | 得意なコース | 強さ |
|---|---|---|---|
| 武内樹(秋名スピードスターズ) | AE85 | 峠(ダウンヒル) | 初級 |
| 池谷浩一郎(秋名スピードスターズ) | シルビア (S13) | 峠(ヒルクライム) | 初級 |
| 中村賢太(赤城レッドサンズ) | シルビア (S14) | 峠(山越え)・夜・雨 | 中級 |
| 庄司慎吾(妙義ナイトキッズ) | シビック (EG6) | 峠(Uターン) | 中級 |
| 中里毅(妙義ナイトキッズ) | GT-R (R32) | 妙義(ヒルクライム) | 上級 |
| 岩城清次(チームエンペラー) | ランエボIV | いろは坂(ヒルクライム) | 上級 |
| 坂本(埼玉北西エリア連合) | カプチーノ | 激坂(ダウンヒル)・夜・雨 | 上級 |
| 高橋啓介(赤城レッドサンズ) | RX-7 (FD3S) | 橋とトンネルの峠(ヒルクライム) | 上級 |
| 須藤京一(チームエンペラー) | ランエボIII(黒) | いろは坂(ダウンヒル) | 上級 |
| 高橋涼介(赤城レッドサンズ) | RX-7 (FC3S・白) | 橋とトンネルの峠(ダウンヒル)・夜・普通(晴れ) | 超上級 |
| 星野好造(パープルシャドウ) | GT-R (R34・カーキ) | 首都高(都心環状線) | 超上級 |
| 舘智幸(プロレーサー) | シビック (EK9) | 八方ヶ原風の峠(往復) | 超上級 |
| 北条凛(死神) | GT-R (R32) | 峠(超ロング) | 超上級 |
| 藤原拓海(藤原とうふ店) | AE86 | 秋名(ダウンヒル) | 超上級 |
src/race/npcDriver.ts の NPC_LEVELS)src/race/rivals.ts の rough、動きは src/race/npcDriver.tssrc/race/rivals.ts の isRivalUnlocked)src/race/rivals.ts で追加・変更できる。public/rivals/ に置き、src/race/rivals.ts のそのライバルに photo: 'rivals/takumi.jpg' のように書くと出る(縦長 3:4 くらいがよい)。書いていないライバルは名前の頭文字の絵になる。☰ メニュー(パソコンは Esc)でプレイモードとコースなどを選んだ後の「時間帯・天気」の画面で、朝・昼・夜と普通・雨・霧を選べる(ブラウザ内に保存。?time=night&weather=rain のように URL でも指定できる)。
src/render/scene.ts、雨の性能は src/game/environment.ts。タッチ操作の画面が出る(パソコンでも画面に触れるか ?touch を付けると出る)。
URL に ?map=1 を付けると教習所コースから始まる(?map=0 は小さな街)。?car=fd3s(standard/kei/ae86 など、車の id)で開始時の車を選べる。?room=ABCD は招待リンクで、開くと参加画面が出る。?debug を付けると開発者ツールから window.__game で状態を触れる。
Node.js 22 以降。
npm install
npm run dev # 開発サーバー(http://localhost:5173)
npm test # 単体テスト(Vitest)
npm run build # 型チェック + 本番ビルド(dist/)
クライアント権威方式: 各プレイヤーが自分の車を計算して 20 回/秒で位置を送り、サーバー(Cloudflare Workers + Durable Objects)は部屋ごとに中継する。他の人の車は 0.1 秒前の位置を補間して表示し、自分の物理ワールドには動かない車体として置く(ぶつかると自分の車だけが押し返される)。
プレイモードの「オンライン」は1つだけで、部屋に入って一緒に自由に走るのも、部屋のみんなとの対戦も、この中で行う(走っている間の Esc・☰ の画面は、オンラインではゲームが止まらないので「メニュー」と呼ぶ)。
部屋にはトップメニューの「オンライン」か、メニュー画面の「部屋に入る」(部屋に入っていないときだけ出る)から入る。部屋コードは前に入った部屋のコードが入った状態で開くので、抜けた後もすぐ入り直せる。部屋コードと招待リンクは、それぞれ横の「コピー」で写せる。入っても選ぶ画面には進まず、部屋のいまのコースをそのまま走る(部屋でエントリー中なら、そのエントリー画面が開く)。コースを変えるときはメニュー画面の「コース・車などを変える」で、部屋の全員がそのコースに移動する(オンライン対戦のエントリー中・対戦中は変えられない)。抜けるときはメニュー画面の「部屋から抜ける」(または部屋に入る画面の「退出する」)。
部屋にいる間は、右上の地図の下に部屋コードと人数(対戦中・エントリー中はその表示)、部屋の人の色の点と「名前 ほかN人」を出す。最大8人でも大きさが変わらない。
部屋にいる間、メニュー画面の「対戦のエントリーを始める」からコースと時間帯・天気を決めると(決めた人が主催)、部屋の全員にエントリー画面が開き、全員が同じコース・条件に移る。各自が車を選んで「準備OK」か「今回は見送る」にし、主催が「スタート」を押すと準備OK の人で一斉にカウントダウンが始まる。全員が選び終えるのを待つのが基本だが、選んでいる人がいても、準備OK の人がいれば「準備OKのN人でスタート」で始められる(確認が出る。選んでいなかった人は今回は見ているだけ)。
何人かがほぼ同時にエントリーを始めても、主催は先にサーバーに届いた1人だけ。後の人の分は捨てて、その人には先の人の対戦をもう一度知らせ、そのエントリー画面を開く(選ぶ画面にいる間に誰かが始めたときも、その画面は閉じてエントリー画面が開く)。
エントリー中に部屋に来た人・一度見送った人も、車を選んで準備OK にすれば出られる。エントリー画面を閉じても、メニュー画面の「エントリー画面」(スマホは右上の「エントリー」)で開き直せる。
並ぶ順は準備OK にした順(2列に互い違い)。カウントダウンの間は車どうしがぶつからない。
走っている間は順位を出す(上位3人と、その外にいれば自分。最大8人でもスマホの画面を埋めないように)。順位は、ゴールした人はタイム順、まだの人はコースを進んだ距離の順(各自が自分の進んだ距離を位置と一緒に送る)。地図には他の人の位置も出る。
自分がまだ走っている間に他の人がゴールすると、画面の真ん中に「〇〇 がゴール」と順位・タイムが少しの間出る。
ゴールすると順位表を出し、まだ走っている人がゴールするたびに更新する。全員がゴールすると対戦が終わり、次の対戦を始められる。
対戦の間は、やり直しの代わりにメニュー画面の「コースに戻る」(PC は R)でいる所のコースの真ん中に戻る。コースは変えられない。途中で抜けることもできる(主催がエントリー中に抜けると対戦ごとやめる)。
サーバーが管理するのは対戦の進行(エントリー・スタートの合図・ゴールの順とタイム)だけ(server/src/room.ts)。タイムは各自の計測をそのまま使う。20 分たっても終わらない対戦は打ち切る。
無料枠を守るための制限:
https://shuheikomatsuki.github.io)と手元の localhost からだけ。ほかのサイトからの接続は断る。server/wrangler.jsonc の vars で変えられる("*" で制限なし)。happogahara-1a2b3c4)と車の組み合わせごとに1つの Durable Object(server/src/leaderboard.ts)に、上位 50 件とそのゴースト・操作の記録を保存する。読み書きは GET/POST /boards/<コース>/<車>、ゴーストと操作の記録は GET /boards/<コース>/<車>/ghosts/<記録の id>({ ghost, inputs })。接続できるページと国の制限は部屋と同じ。手元で試すとき(npm run server:dev は国の制限を外して起動する):
npm run server:dev # http://127.0.0.1:8787 でサーバーを起動
npm run dev # 別のターミナルで
# http://localhost:5173/?server=http://127.0.0.1:8787 を2つのウィンドウで開き、O キーで同じ部屋コードに参加
公開の設定(初回のみ):
workers.dev のサブドメインが作られていることを確認する。CLOUDFLARE_API_TOKEN と CLOUDFLARE_ACCOUNT_ID を登録する。deploy-server ジョブがサーバーを公開し、ログに https://car-game-server.<サブドメイン>.workers.dev が出る。SERVER_URL としてその URL を登録し、もう一度 CI を実行すると、ゲーム側にサーバーの URL が組み込まれる。| ディレクトリ | 役割 |
|---|---|
server/ | マルチプレイの中継サーバー(Cloudflare Workers + Durable Objects) |
src/net/ | 通信内容の定義、WebSocket 接続、受信した車の補間 |
src/core/ | 固定タイムステップのゲームループ、キーボード・ゲームパッド入力 |
src/game/ | マップ1つ分(物理ワールド・車・描画物)の生成と破棄、他のプレイヤーの車、ランキング(ranking.ts)、クラクション(horn.ts) |
src/main.ts | 起動処理と、それぞれの部品をつなぐところ(レースの開始・終了、オンラインの部屋、毎フレームの更新) |
src/audio/ | Web Audio API で合成する効果音(エンジン・スキール・衝突) |
src/sim/ | 車両モデル(vehicle.ts、描画非依存)、Rapier 物理ワールド、車 |
src/race/ | タイムアタックと NPC: コースの抽出と中心線、周回・近道・逆走の判定、ゴースト・リプレイ、自己ベストの保存、レーシングラインと NPC の運転 |
src/map/ | マップ定義(maps.ts)。タイルマップの解析(tilemap.ts)と、中心線で描く道路(road.ts)。障害物配置 |
src/render/ | Three.js のシーン、マップ・車・標識のメッシュ、カメラ |
src/ui/ | HUD(速度・ギア・操作説明・通知)、メニュー(menu.ts 画面の流れ、menuFlows.ts プレイモードごとの画面とポーズ画面の項目、settingsItems.ts 設定の項目、menuDetails.ts 右側の説明、courseInfo.ts コースの形と高低差、carStats.ts 車の性能の棒)、リプレイの画面(replayPanel.ts) |
src/sim/vehicle.ts の DEFAULT_VEHICLE_PARAMS。src/map/maps.ts の文字グリッドを書き換えるだけで変更・追加できる(凡例は src/map/tilemap.ts)。標識やカーブのガードレールもここで指定する。MapDefinition.road に直線(straight)と円弧(arc)の区間を並べて書く(src/map/road.ts の RoadPiece)。周回の道路は始まりに戻るよう長さを合わせる(合わないとエラーになる)。高さの差の足りない交差もエラーになる。main ブランチへ push すると GitHub Actions がビルドし、GitHub Pages(https://shuheikomatsuki.github.io/prefix-d/ )に公開する。テストは PR で通しておき、main ではビルドしてすぐ公開する(main のテストは公開と並行して走る)。
ビルドは相対パスなので、リポジトリ名を変えても設定はいらない(URL の最後がリポジトリ名になる)。
初回のみ、リポジトリの Settings → Pages → Source を GitHub Actions に設定する。
妙義(妙義山)のヒルクライム(約4.7km・高低差 290m)。妙義ナイトキッズのホームコース。道の形は妙義のヒルクライムのコース図をなぞって作った: ふもとの中速の左右、道幅が狭く路面の荒れた崖沿いの中低速の連続コーナー、細かく折り返す連続コーナーの先の電球の形にぐるりと回り込むヘアピン、中速の S 字の連続、岩の崖が迫る見通しの悪いヘアピンの 3 連続、ゴール手前の左右。コースは src/map/maps.ts の MYOGI_UP_MAP |