BeamNG.Earth
A 1:1 scale streamed Earth for BeamNG.drive: real elevation and satellite imagery, floating origin, LOD streaming from a companion server
Arcade-style 1v1 3D mech versus action for the browser. Boost, step-cancel, lock-on, melee combos, awakening. Plays on phones. three.js + Vite.
GamesJavaScript · PythonGitHub @yutianqiao7-devOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
アーケードのバーサス系 3D 対戦アクション(2機のロボットが射撃と格闘で撃ち合うタイプ)。ブラウザで動き、スマホのタッチ操作に対応。 ▶ 遊ぶ: https://yutianqiao7-dev.github.io/assault-frame-vs/ (スマホのブラウザでそのまま動きます。ダウンロード不要) モードは CPU戦 と 通信対戦(スマホ2台) の2つ。機体は12機、タイトル画面で自機を選べる。 1. 両方で「通信対戦」を選び、それぞれ自分の機体を選ぶ 2. 片方が「部屋を作る」→ 4桁の数字が出る 3. もう片方が「部屋に入る」→ そのコードを入力して接続 繋がった時点で自動的に開始する。同じ Wi-Fi なら遅延は数ms程度。 http://localhost:5174 を開く。server.host = true なので、同じ Wi-Fi のスマホから http:// :5174 でも遊べる。 通信対戦中は相手の時間を止められないので、メニューを開いてもゲームは止まらない(表示も PAUSE ではなく MENU になる)。 移動は常に敵基準。スティック上=敵に接近、下=離脱、左右=敵を軸に横移動。Read the full README ↗
アーケードのバーサス系 3D 対戦アクション(2機のロボットが射撃と格闘で撃ち合うタイプ)。ブラウザで動き、スマホのタッチ操作に対応。
▶ 遊ぶ: https://yutianqiao7-dev.github.io/assault-frame-vs/ (スマホのブラウザでそのまま動きます。ダウンロード不要)
モードは CPU戦 と 通信対戦(スマホ2台) の2つ。機体は12機、タイトル画面で自機を選べる。
繋がった時点で自動的に開始する。同じ Wi-Fi なら遅延は数ms程度。
npm install
npm run dev
http://localhost:5174 を開く。server.host = true なので、同じ Wi-Fi のスマホから http://<PCのIP>:5174 でも遊べる。
| 操作 | キーボード | スマホ |
|---|---|---|
| 移動 | WASD / 方向キー | 左バーチャルスティック |
| ブースト(地上滑走・上昇・空中移動) | L または Space(押しっぱなし) | ジャンプ |
| ステップ(回避・行動キャンセル) | Shift | ステップ |
| 射撃 | J(押しっぱなしで連射) | 射撃 |
| 格闘 | K(当たった直後にもう一度で連撃) | 格闘 |
| ┗ 横格闘 | ←→ を入れながら K | スティック横+格闘 |
| ┗ 前格闘 | ↑ を入れながら K | スティック前+格闘 |
| サブ射撃 | U | サブ |
| 特殊射撃 | I | 特射 |
| 特殊格闘 | O | 特格 |
| 覚醒 | Q | 右上の覚醒ボタン |
| 一時停止 / メニュー | Esc | 左上の ❚❚ ボタン |
通信対戦中は相手の時間を止められないので、メニューを開いてもゲームは止まらない(表示も PAUSE ではなく MENU になる)。
移動は常に敵基準。スティック上=敵に接近、下=離脱、左右=敵を軸に横移動。
meleeCost 14)+突進と振りの間は毎秒消費が続く。N格1段で約 30、3段フルコンボで約 61。ブーストが足りない/オーバーヒート中は格闘を出せないので、コンボを完走すると着地を晒すことになる。src/collision.js):AABB との押し出し・屋上への着地・線分判定による射線と弾の遮蔽。ビルは必ず地面から生えた軸沿いの箱なので、横の押し出しを先に解決すれば「足元にある屋上」を見るだけで床が決まる。src/net.js / src/netsync.js)。COMBO_SCALE)aimError)が変わる。タイトル画面で自機と相手を選べる。コストが高いほど強いが、落ちたときに減る戦力ゲージも大きい(戦力ゲージは両陣営 6000)。
| 機体 | コスト | 耐久 | 役割 | 射撃 | サブ | 特射 | 特格 |
|---|---|---|---|---|---|---|---|
| ラーヴァ | 1500 | 480 | 軽量 | バルカン(4点バースト) | マイクロミサイル(3連) | スプレッド(3発) | ダッシュスラスト |
| ヴェスパ | 1500 | 470 | 軽量格闘 | ニードル | 投擲ナイフ(2発) | ワイヤーフック | ピアススラスト |
| ファング | 1500 | 440 | 格闘 | 投擲ナイフ | 散弾ブレード(4発) | カッターブーメラン | ダブルファング |
| バルワーク | 1500 | 610 | 弾幕 | ガトリング(6点バースト) | グレネード | 散弾砲(5発) | ラムアタック |
| ブレイヴ | 2000 | 620 | バランス | パルスライフル | ヘヴィバズーカ | ウェイトアンカー | プラズマランス |
| アイギス | 2000 | 640 | 射撃 | ツインビーム(2発同時) | ミサイルポッド(4連) | 拡散ビーム(5発) | シールドバッシュ |
| ガルム | 2000 | 600 | 手数 | アサルトマシンガン(3点バースト) | ショートバズーカ | グレネード | タックル |
| オルカ | 2000 | 610 | 変則 | 貫通ビーム | ブーメラン | 拡散ビーム(4発) | ソードスロー |
| ヘイゼル | 2000 | 600 | 設置 | アサルトライフル(3点バースト) | 散布地雷 | 散布地雷(3個) | チェーンソー |
| ノクス | 2500 | 620 | 格闘 | ハンドガン | 投擲ブレード(3発拡散) | スラッシュハーケン | 瞬間突撃 |
| グラーヴ | 2500 | 690 | 重格闘 | ハンドキャノン | ブレードスロー | 衝撃波(4発) | 両断 |
| テンペスト | 2500 | 720 | 砲撃 | ロングキャノン | ミサイル斉射(6連) | 榴弾(4発拡散) | アンカー |
| タイタン | 2500 | 800 | 重装 | ガトリング(5点バースト) | 重ミサイル(4連) | 迫撃砲 | 圧殺 |
コスト帯は 1500×4 / 2000×5 / 2500×5 / 3000×4 の計18機。戦力ゲージは両陣営 6000 なので、1500 なら4回、3000 なら2回落ちると負ける。
性能の目安(CPU に持たせて、前進してくる的へ 20 秒で与えたダメージの中央値。7回試行):
| コスト | 数値 | 最低/最高 |
|---|---|---|
| 1500 | ファング 550 / バルワーク 522 / ヴェスパ 506 / ラーヴァ 493 | 0.90 |
| 2000 | アイギス 734 / ブレイヴ 711 / オルカ 643 / ガルム 582 / ヘイゼル 581 | 0.79 |
| 2500 | テンペスト 822 / グラーヴ 709 / ランス 683 / タイタン 679 / ノクス 584 | 0.71 |
| 3000 | ヴァルカ 818 / セラフ 763 / ガルーダ 665 / ソヴリン 635 | 0.78 |
この数値は CPU が撃った結果なので、格闘機(ファング・ソヴリン)と設置機(ヘイゼル)は低めに出る — CPU は格闘に踏み込まないし、的は地雷を避けないため。人が操作したときの強さとは別物として見ること。
| kind | 挙動 |
|---|---|
beam | 直進ビーム。誘導あり |
bullet | 実弾。burst 発を burstGap 秒おきに連射。弾数 1 = 引き金 1 回(連射ぶんは追加消費しない) |
shell | 砲弾。arc で山なり、splash で爆風。落下ぶんを撃ち上げて狙う弾道計算つき |
spread | 拡散。count 発を spreadAngle の範囲にばらまく |
missile | 誘導弾を count 発、launchGap 秒おきに斉射。近距離ではばらつきを絞って命中させる |
laser | 照射ビーム。duration 秒のあいだ判定が出続け、tickGap ごとにダメージ。撃っている間は動けずキャンセルもできない |
funnel | オールレンジ攻撃。count 基のビットが敵の周囲を回りながら撃つ |
boomerang | 投げて戻ってくる。turnAt 秒で折り返し、往路と復路の両方に判定が出る |
melee_special | 突撃してから斬る。rushSpeed で寄り、rushRange の内側でだけ出せる(覚醒技版は距離制限なし) |
mine | 設置地雷。地面や壁に貼り付き、armTime 後に trigger の距離へ敵が入ると splash で爆発。life 秒で不発のまま消える。相手の足元ではなく dropAt の割合だけ手前に落ちる(足元に落ちると即起爆してただの遅いグレネードになる)。count 個まくと落点も横位置もばらける |
pierce(フラグ) | 命中しても消えない貫通弾。同じ相手には1回だけ当たる |
連射のクールタイムは自動で下限が入る: tryFire() が cooldown を「連射を撃ち終わるのに必要な時間」まで引き上げる。ここが短いと前の連射が続いているうちに次が始まって弾が重なり、設定値の 2〜2.5 倍の手数になる(実際に機関銃系5機すべてがそうなっていて、素の DPS が 246/261/251/233/174 とビーム機の 2 倍以上あった)。
設定ミスを黙って通さない: validateMechs()(src/config.js)が kind ごとに必要なキーを検査する。開発サーバ起動時に自動で走り、不備があればコンソールに出る。laser に duration を書き忘れると life が NaN になり、NaN <= 0 が false なのでビームが永久に消えない — 実際にこれを踏んだので、同じ取り違え(life/tick と duration/tickGap)を名指しで検出するようにしてある。
どの機体も N格闘 / 横格闘 / 前格闘 の3系統を持つ。スティック(またはキー)の入れ方で出し分ける。
段数・ダメージ・ダウン値は機体ごとに違う(例: ノクスのN格は4段、テンペストの前格は1段115ダメージ)。
モデルはプリミティブ(面取りした箱・円柱・球)から src/mechmodel.js で組んでいる。差し替え前提。
UnrealBloomPass)でビーム・サーベル・スラスターを発光させている。しきい値 0.86 で発光体だけ拾い、機体や地面はにじませない。スマホではブルームの内部解像度を半分にして塗りつぶし負荷を 1/4 にしている。src/glow.js)。加算合成 + toneMapped:false。単色の不透明メッシュのままだと、ブルームを掛けてもただの明るい棒にしか見えない。Trail)。刃の根本から引くと巨大な扇になって画面を覆うので、外側 38% だけを引いている。shape 指定で組み立てる(後述)。装甲の内側に一段細い暗色フレームを入れて板の重なりを見せている。| ID | 名前 | 遮蔽 | 性格 |
|---|---|---|---|
city | ナイトシティ | 15 | 夜の高層ビル街。背の高い遮蔽が多い。手続き生成(アセット不要) |
colony | コロニー内部 | 30 | 閉じた円筒。頭上に街が反り上がり採光窓が光る。遮蔽が最も密 |
ruins | 廃墟 | 26 | 崩れた市街。低い瓦礫が多く高さがばらばら。屋上の取り合いが濃い |
canyon | 渓谷 | 17 | 切り立った岩山。遮蔽は少なく大きい。回り込みと高度差の勝負 |
city 以外は Blender 製の GLB。タイトル画面で選ぶ。通信対戦では部屋を作った側のステージが使われる(ホストが start に載せて送り、ゲストは読み込んでから開始する)。
どのステージでも守っている約束が 3 つある。これを守る限り、ステージを足しても物理・当たり判定・ネットコードには一切触らなくていい。
ARENA_R の外側だけで見せる。コロニーの湾曲も渓谷の崖もそこにある。<id>_boxes.json に吐き、collision.js がそのまま読む。岩のように輪郭がでこぼこな物は、当たり判定を見た目より一回り内側に置く(Placer.add の shrink)。見えている面より手前で弾かれると嘘に見えるため。ステージごとに引っかかった点:
tools/ の 2 本のスクリプトが public/models/ の GLB を書き出す。生成はコードなので、いつでも作り直せる。
blender --background --python tools/blender_mech_parts.py # 機体パーツ
blender --background --python tools/blender_colony.py # コロニー
blender --background --python tools/blender_ruins.py # 廃墟
blender --background --python tools/blender_canyon.py # 渓谷
機体で置き換わったのは見た目だけ。可動ピボット(pelvis / torso / armL / legR …)は mechmodel.js のまま残してあるので、アニメーションも当たり判定も一行も書き換えていない。GLB が読めなければ従来のプリミティブ生成にそのまま落ちる。
hd_*(頭7種)/ sh_*(肩8種)/ bk_*(背中7種) / gun_*(銃6種)/ 胴・腰・腕・脚。shape 指定でこれを組み合わせるので、12機ぶんのモデルではなく1ファイル(Draco 圧縮で 250KB)。m_main / m_accent / m_trim / m_joint / m_frame / m_eye …)を機体ごとのパレットに差し替えている。scale.x = -1。three.js は行列式が負のとき法線と面の向きを自動で反転させる。src/
config.js 機体データと全チューニング定数(ここを触ると挙動が変わる)
main.js レンダラ・ゲームループ・勝敗・タイトル/リザルト
mech.js 機体のステートマシン(移動/攻撃/被弾/ダウン/覚醒/ポーズ)
mechmodel.js 機体のプロシージャルモデル
combat.js 弾と命中判定、エフェクト
ai.js CPU の思考
camera.js ロックオンカメラ
input.js キーボード + バーチャルスティック
hud.js HUD の DOM 更新
stages.js ステージ(市街=手続き生成 / その他=GLB)とライティング
mechlocal.js src/mechs.local.js があれば機体データに被せる(開発時のみ)
glow.js 発光体(ビーム/グロー/軌跡)の共通パーツ
collision.js ビル(AABB)との当たり判定・射線判定
net.js WebRTC の接続(部屋コードの発行と参加)
netsync.js 状態のパック/アンパック
style.css HUD とタッチ UI
tools/
mechs.local.example.js 個人用の機体上書きの雛形(src/mechs.local.js にコピーして使う)
stagekit.py ステージ生成の共通部品(形状・配置・書き出し・検算)
blender_mech_parts.py 機体パーツの生成 → public/models/mechparts.glb
blender_colony.py コロニー → colony.glb + colony_boxes.json
blender_ruins.py 廃墟 → ruins.glb + ruins_boxes.json
blender_canyon.py 渓谷 → canyon.glb + canyon_boxes.json
public/
models/ GLB とコリジョン JSON
draco/ Draco デコーダ(GLB の展開に使う。CDN は踏まない)
チューニングはほぼ全部 src/config.js に集約してある。
src/mechs.local.js を置くと、名前・説明・配色・形・武装・数値を好きに差し替えられる。既存機体への部分上書きも、base を指定した新機体の追加もできる。雛形は tools/mechs.local.example.js。
cp tools/mechs.local.example.js src/mechs.local.js
npm run dev
この仕組みは開発サーバでしか動かない。 本番ビルドには一切入らないので、公開している GitHub Pages 側は元のまま。src/mechs.local.js は .gitignore に入れてあるのでコミットもされない。
スマホで遊ぶときは、PC で npm run dev を立ち上げて同じ Wi-Fi から http://<PCのIPアドレス>:5174 を開く(vite.config.js の server.host が true)。
main がソース、gh-pages がビルド済みサイト。更新するときは:
npm run build
そのあと dist を gh-pages ブランチに force push する(vite.config.js の base が /assault-frame-vs/ を付けるので、そのままのパスで動く)。
window.__dbg にゲーム内部を出している。
__dbg.step(dt) … 1 フレーム進めて描画(rAF が止まっていても手動で進められる)__dbg.sim(n) … 描画せず n フレーム進める(対戦のシミュレーションに使う)__dbg.game / __dbg.world / __dbg.chase …POST /__shot/<name> がある。base64 の JPEG を投げると .shots/<name>.jpg に保存する(プレビューが見えない環境でのレンダリング確認用)。preserveDrawingBuffer と window.__dbg は import.meta.env.DEV で囲ってあるので、本番ビルドには入らない。ホスト権威にしている。両者が独立に同じ計算をする方式(ロックステップ)は、
今のシミュレーションが Math.random() を各所で使っていて決定的ではないため成立しない。
ゲストは自機だけをローカルで先読みする(クライアント予測)。 これが無いと、自分の機体が「入力 → ホスト → スナップショット到着」を待ってから動き出すので、往復ぶんまるごと操作が遅れて「重い」と感じる。ホストと同じ mech.update() をそのまま回し、スナップショット到着時に位置だけ 0.18 の重みで引き戻す(0.4 で引くと自分の操作と綱引きになってガタつく)。3 ユニット以上離れたら見た目より正しさを優先して即座に合わせる。
予測はあくまで見た目なので、その間の発射とダメージは捨てる(world.predicting)。ホストから届く本物と二重になるため。よろけ・ダウン・撃墜中は操作が効かないので予測せず、素直にホストの値に従う。
チャンネルを2本張っている。
| 用途 | 性質 | |
|---|---|---|
| 制御 | hello / start / fin / end / menu / ping | 順序保証あり・確実に届く |
afvs-rt | スナップショットと入力 | 順序保証なし・落ちてよい |
状態同期を順序保証ありのチャンネルで流すと、1パケット落ちただけで後続が全部待たされ(head-of-line blocking)、取り戻したときにまとめて届いてカクつく。古いスナップショットは使い道が無いので、落ちてくれたほうがよい。afvs-rt がまだ開いていなければ制御チャンネルに流すので、張れない環境でも動く。
既知の割り切り:ロールバックは入れていない。予測が外れた瞬間(相手の攻撃で急によろけた等)は 1 スナップショットぶん遅れて反映される。
main.js の finishSeq。演出そのものは実時間で進み、シミュレーションだけが timeScale で遅くなる(tick(dt * timeScale))。
| 時刻 | 内容 |
|---|---|
| 0.00〜0.14秒 | timeScale 0.06。ほぼ静止させて当たった重さを出す。白フラッシュとバナー |
| 0.14〜1.45秒 | 0.24 のスローモーション。カメラが倒された機体に寄って回り込み、爆散と破片、0.2秒おきに追い討ちの小爆発 |
| 1.45〜2.35秒 | 等速へ戻しながらカメラが引く |
| 2.75秒 | リザルトをフェードイン |
DEAD_SHOW)。即座に消すと、寄ったカメラが何もない地面を映すことになる。スロー中なので体感では1.5秒ほど見えている。RingGeometry(0.975, 1.0))。太い輪を巨大化させると、寄りのカメラではただの白い帯になって画面を覆う。fin を送り、両者が同時に自前の演出を回す。結果はゲスト側で反転する。遅れて届く end は演出中なら無視する。機体名・武装名・タイトルはすべてオリジナル。3D モデル・コード・UI もすべてこのリポジトリ内で自作(外部アセットの取り込みなし)。GLB も tools/ のスクリプトから Blender で生成している。依存は three.js と Vite、および同梱の Draco デコーダのみ。
ジャンルとしてアーケードのバーサス系 3D 対戦アクションを参考にしているが、特定作品の名称・意匠・アセットは使用していない。
| ランス | 2500 | 620 | 狙撃 | 貫通ロングライフル | 照射ビーム | 狙撃グレネード | ブレイクランス |
| ヴァルカ | 3000 | 700 | 高性能 | ハイビームライフル | ファンネル(3基) | 照射ビーム(1.5秒) | 高速突撃 |
| セラフ | 3000 | 720 | 万能 | 貫通ビーム | ファンネル(6基) | ツインビーム | 急速接近 |
| ガルーダ | 3000 | 680 | 空戦 | ツインビーム | 降下ミサイル(4連) | 貫通ダイブビーム | フォールダウン |
| ソヴリン | 3000 | 740 | 格闘 | ビームライフル | ブーメラン | 照射ビーム | ロイヤルスラッシュ |