
2026/01/05 4:54
**Lua でサーバー側に描画されるマルチプレイヤーゲーム(クライアントコードは不要)**
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Cleoseleneは、サーバー上でグラフィックスをレンダリングし、そのロジックをLuaスクリプティングで公開するマルチプレイヤーファーストのゲームエンジンです。プロジェクトは
にある6つのコールバックで駆動されます:main.lua
– 空間データベース(init())、物理世界(api.new_spatial_db(cell_size))、サウンドなどをセットアップします。api.new_physics_world(db)
– 物理シミュレーションを進め、衝突イベントを処理し、ゲームロジックを実行します。update(dt)
– 画面をクリアし、色を設定してプレイヤー統計を表示し、可視エンティティ(draw(session_id)で空間DBからクエリ)を描画します。db:query_rect
,on_connect(session_id),on_disconnect(session_id)– セッションライフサイクルを処理し、JavaScriptキーコード(37=Left, 38=Up, 39=Right, 40=Down, 32=Space, 90=Z)をゲーム入力状態にマップします。on_input(session_id, key_code, is_down)
エンジンは固定の仮想座標系 800 × 600 を使用し、出力を自動でスケーリングして解像度とアスペクト比を任意のディスプレイで保ちます。空間ハッシュは円形および線分エンティティをサポートし、ユニークIDにより高速な範囲検索・矩形検索・レイ検索が可能です。物理ボディには速度、重力、および衝突コールバックがあります。描画はシンプルなAPI呼び出し(,clear_screen,set_color,fill_rect,draw_line)とサウンド制御関数(draw_text,load_sound,play_sound,stop_sound)で行われます。set_volume
ナビゲーショングラフ()はノード/エッジの追加を許可し、api.new_graph()でA*経路探索を実施します。find_path(start, end)
ドキュメント内のサンプルコードでは、セルサイズ250の空間DBを初期化し、物理世界を作成し、ジャンプ音をロードし、各フレームで物理演算をステップさせ、衝突処理とプレイヤー体力描画を行う例が示されています。
まだプレビュー段階ですが、Cleoseleneはネットワークとレンダリングをサーバーにオフロードし、開発者がLuaでゲームプレイロジックに集中できるようにすることでマルチプレイヤー開発を簡素化することを目指しています。将来のリリースでは物理詳細、衝突処理、およびナビゲーション機能を深化させ、インディスタジオがネットワークタイトルをプロトタイピングできる速度を向上させる予定です。
本文
⚠️ アクティブ開発中
Cleoselene は現在初期プレビュー段階にあります。
本格的なプロジェクトや本番環境でご利用になる前に、必ず help@cleoselene.com までお問い合わせください。
Cleoselene マニュアル
Lua スクリプトを採用したマルチプレイヤー優先型サーバーサイドレンダリングゲームエンジンです。
ゲーム構成
最小限のゲームスクリプト(
main.lua)は、以下のコールバックを実装する必要があります。
-- サーバー起動時に一度だけ呼び出されます function init() -- 物理エンジンやアセットの初期化、状態設定 db = api.new_spatial_db(250) phys = api.new_physics_world(db) api.load_sound("jump", "assets/jump.wav") end -- 毎サーバーフレーム(通常は 30 TPS)で呼び出されます function update(dt) -- 物理シミュレーションを進める phys:step(dt) -- 衝突処理 local events = phys:get_collision_events() for _, pair in ipairs(events) do -- ゲームロジック(ダメージ、スコアなど)をここで扱う end end -- 接続している各クライアントのフレームを生成するために呼び出されます function draw(session_id) api.clear_screen(20, 20, 30) -- プレイヤー固有のビュー(カメラ、HUD)を描画 local p = players[session_id] if p then api.set_color(255, 255, 255) api.draw_text("HP: " .. p.hp, 10, 10) -- db:query_rect を使って可視領域内のエンティティを描画 end end -- ネットワークイベント function on_connect(session_id) print("Player joined: " .. session_id) -- プレイヤーエンティティを生成 end function on_disconnect(session_id) print("Player left: " .. session_id) -- エンティティを削除 end function on_input(session_id, key_code, is_down) -- 入力処理(key_code は JS キーコード) -- 37=左, 38=上, 39=右, 40=下, 32=スペース, 90=Z if players[session_id] then players[session_id].inputs[key_code] = is_down end end
API リファレンス
表示と座標
エンジンは 800 × 600 の固定仮想座標系を採用しています。
api.fill_rect, api.draw_line などの描画コマンドはすべてこの座標系で動作します。出力は自動的にユーザーの画面サイズに合わせてスケールされ、論理解像度とアスペクト比は保持されます。
| メソッド | 説明 |
|---|---|
| 背景色でフレームをクリアします。 |
| 現在の描画色を設定します。 |
| 塗りつぶし長方形を描きます。 |
| 線分を描画します。 |
| 指定位置にテキストを描画します。 |
| URL/パス(スクリプトからの相対)から音声をプリロードします。 |
| ロード済みの音声を再生します。 |
| 再生中の音声を停止します。 |
| 音量(0.0 – 1.0)を設定します。 |
空間データベース(ジオメトリ)
エンジンは高速な空間ハッシュグリッドを提供し、広域クエリを行います。
作成
local db = api.new_spatial_db(cell_size) -- 例: 250
| メソッド | 説明 | 戻り値 |
|---|---|---|
| 円形エンティティを登録します。 | |
| 線分(壁)を登録します。 | |
| エンティティを削除します。 | |
| 手動で位置を更新(テレポート)。 | |
| エンティティの (x, y) を取得します。 | |
クエリ(センサー)
| メソッド | 説明 | 戻り値 |
|---|---|---|
| 半径 r 内のエンティティ ID を取得します。 | |
| AABB 内のエンティティ ID を取得します(カリング用)。 | |
| レイキャストを行います。 | または |
物理エンジン(シミュレーション)
剛体ダイナミクス、積分、衝突解決を担当します。
作成
local phys = api.new_physics_world(db)
| メソッド | 説明 |
|---|---|
| エンティティに物理特性を追加。: など。 |
| 速度を設定します。 |
| を取得します。 |
| グローバル重力ベクトルを設定します。 |
| シミュレーションを進め、衝突解決と DB の更新を行います。 |
| 前回ステップ以降の衝突リストを返します: |
グラフナビゲーション(パスファインディング)
カスタムグラフ上で動作するネイティブ A* 実装です。
| メソッド | 説明 |
|---|---|
| 新しいナビゲーショングラフを生成します。 |
| ノードを追加します。 |
| 2つのノード間にエッジ(接続)を作ります。 |
| 最短経路となるノード ID のリストを返します。 |