ドキュメント ゲームプレイ拡張

キャンバス

ゲーム画面にテキストボックス、パネル、プログレスバー、画像、地面に沿った円、矢印付きのルートを描きます。ランタイムが毎フレーム自前で描画し、ゲームの状態は変えないので、マルチプレイでも安全です。Python、HTTP、共有メモリへの直接書き込みのどれでも使えます。

外部プログラムから、ゲーム画面にテキストボックス、パネル、プログレスバー、画像、地面の円、矢印付きの地面のルートを描けます。描画はランタイムが毎フレーム自前で行います。独自の HUD、補助線、ヒント、チュートリアルの注釈、配信用の情報ボードなどに向いています。

キャンバスと JASS の画面系関数、どちらを使うか

キャンバス(このページ)JASS の画面系関数
描くのは誰かランタイムが自前で描画ゲーム自身(フローティングテキスト、エフェクト、パネル、ポートレート会話……)
マルチプレイ安全:自分のマシンの画面に描くだけで、ゲームオブジェクトを作らず、ゲームの状態も変えないシングルプレイのみ
見た目自由:中国語フォント、角丸、半透明、枠線、任意の色、ローカルの画像ゲーム本来のスタイル
対象への追従ユニット、ワールド座標、画面位置に追従。地面の円は地形の起伏に沿う関数による
コスト実測で 1 フレームあたり 0.2 〜 0.35 ms(要素 9 個)1 回の呼び出しで約 13 ms

2 つの方法は併用できます。ゲーム本来のスタイルの演出は JASS で、独自のパネル、補助線、ヒントはキャンバスで描きます。

Python

c = g.canvas                               # 初回使用時にランタイムが描画フックを入れる(約 0.1 秒)
c.text("title", "こんにちは、キャンバスです", screen=(40, 110), color=(255, 220, 80),
       bg=(0, 0, 0, 170), border="#C49C40", size=22, bold=True)
c.panel("status", "コンパニオン · ヒカリ", ["気分:ご機嫌", "撃破:12"], screen=(16, 330))
c.bar("hp", 0.62, unit=hero, lift=260, width=100, height=12, color=(80, 220, 80), text="62%")   # ユニットに追従
c.text("tag", "ボスが大技を撃つぞ!", unit=boss, lift=320, color=(255, 80, 80), size=22, bold=True)
c.circle("danger", (x, y), 300, color=(255, 60, 60), fill=(255, 60, 60, 60), width=3)          # 地面の危険エリア
c.circle("aura", hero, 450, color=(80, 200, 255, 220))                                         # ユニットに追従する円
c.path("route", [(x1, y1), (x2, y2), (x3, y3)], color=(255, 220, 0), width=5, arrow=True)
c.image("icon", "icon.png", screen=(40, 170), width=64, height=64)
c.remove("danger"); c.hide("tag"); c.clear()   # clear は自分が描いたものだけを消す
c.expire("tag", 5)                         # 5 秒後にひとりでに消える
with c.batch(): ...                        # 多数の変更をまとめ、共有メモリへの書き込みは 1 回だけ
c.stats()                                  # drawnFrames が増えている = 実際に描画されている

各要素は key で識別します。同じ key でもう一度描くと、更新になります。

クリック可能:テキストボックスとパネルに clickable=True を付けると(ホバー時の色は hover= で指定)、クリックされたときにイベントストリームに ui.click が 1 件届き、ev.key がその key になります。その要素の上でのクリックはゲームには届きません。既成のボタン、選択カード、ホットキー、地面のクリックについては UI と入力 を参照してください。

位置(各要素に 1 つ指定):

  • screen=(x, y):画面のピクセル。負の値は右端 / 下端から数えます。center=True で中心揃えになります。
  • frac=(0.5, 0.1):画面に対する比率。
  • world=(x, y):ワールド座標。
  • unit=ユニット:ユニットに追従します。ワールドとユニット上のテキストやプログレスバーは、下辺の中点をその点に合わせ、lift で上に持ち上げます。

ワールドとユニット上の要素は、デフォルトで下部の操作パネルと上部の昼夜の時計を避けます(over_ui=True にすると上に重ねます)。色は (r, g, b)、(r, g, b, a)、"#RRGGBB"、"#RRGGBBAA" のいずれかで書けます。

メソッド描くものよく使う引数
text(key, テキスト, ...)テキストボックス。複数行は \ncolor、bg 背景色(省略すると透明)、border、size、bold、shadow、width(この幅で折り返す)、radius 角丸
panel(key, タイトル, [行...], ...)パネル(暗い半透明の背景、金色の枠)text と同じ
bar(key, 0..1, ...)プログレスバー:HP、クールダウン、詠唱ゲージwidth、height、color、bg、border、text
image(key, パス, ...)ローカルの画像(png / jpg / bmp / gif)width、height(省略すると元のサイズ)
circle(key, ユニットまたは地点, 半径, ...)地面の円。地形に沿うcolor 線の色、fill 塗り(透明度付き)、width 線幅
path(key, [点...], ...)地面の折れ線color、width、arrow 終端の矢印。点は座標でもユニットでもよい

HTTP(任意の言語)

Farsight のバックエンド(ローカルでのみ待ち受け):

POST /api/instances/20/canvas
{"set": [
   {"key": "banner", "kind": "text", "text": "HTTP から描いたキャンバス", "frac": [0.5, 0.12], "center": true,
    "color": "#FFDC50", "bg": [0, 0, 0, 180]},
   {"key": "hp", "kind": "bar", "value": 0.8, "unit": 596125988, "lift": 260, "text": "80%"},
   {"key": "zone", "kind": "circle", "center": 596125988, "radius": 600, "color": [255, 200, 0], "width": 4},
   {"key": "route", "kind": "path", "points": [[-4587, -9092], [-5387, -8792]], "color": "#50C8FF"}
 ],
 "remove": ["old"], "clear": false}

GET  /api/instances/20/canvas        いま描いている要素 + 描画したフレーム数

kind は Python のメソッド名と同じで、引数名も同じです。ユニットはスナップショットのアドレス addr で指定します。

共有メモリに直接書き込む

Python や Farsight を経由しなくても使えます。まずセマンティックコマンド canvas_enable(W3P オペコード 73)を 1 回送ると、ランタイムが共有メモリブロック Local\War3Canvas_<pid> を作成します。構成は、先頭 64 バイト + 256 エントリ × 112 バイト + 64 KB のテキスト / 点プールです。書き込みは seqlock で行います(シーケンス番号を奇数にする → エントリとプールを書く → シーケンス番号を偶数にする)。ランタイムは毎フレーム 1 回読み、書きかけのデータを読んだ場合は前のフレームの内容を使い続けます。また、描画済みフレーム数、要素数、異常カウントを書き戻します。Python のリファレンス実装は sdk/python/w3canvas.py で、構造体の定義はプロトコルのヘッダーファイルにあります。W3P プロトコル を参照してください。

複数のプログラムが同時に描く

MOD、Farsight、MCP、ゲートウェイが同じ試合に同時に描くことがありますが、キャンバスは 1 つしかありません。ルールは、各プログラムは自分の要素だけを扱うことです。

  • 書き込む前に名前付きロックを取り、既存の要素を読み出して、他のプログラムの分は残し、自分の分を差し替えてから書き戻します。
  • 各要素には描いたのが誰かが記録されます(プロセス ID + プロセス内の連番)。描いたプログラムが終了すると、次に誰かが書き込むときについでに消されます。そのボタンもクリックを横取りしなくなります。
  • 要素の番号は共有カウンターから割り当てるので、重複しません。

Python SDK はすでにこのとおりに動作し、clear() も自分の分だけを消します。共有メモリに直接書き込む場合もこのルールに従ってください。そうしないと、他のプログラムの要素を消してしまいます。レイアウトの詳細は W3P プロトコル を参照してください。

実測と注意点

  • 2026-09-25 の実測(1920×1080、2 倍速):要素 9 個で 1 フレームあたり 0.27 〜 0.34 ms、約 63 フレーム / 秒、異常 0 回。9 件の書き込みに 6 ms。ヒーローが移動しても、ユニットに追従する円、テキスト、HP バーはきちんとついてきました。内容が変わったときだけテクスチャを描き直し、位置が動くだけなら描き直しません。
  • 描画はゲームの UI の後、マウスカーソルの前に行われます。そのため、ゲーム自身の HP バー、ユニット、UI の上に重なり、マウスカーソルはさらにその上に描かれます。下部の操作パネルと上部の昼夜の時計は避けますが、マップ独自のパネル(右上のリーダーボード、カウントダウン)は避けません —— 自分のパネルは右上に置かないでください。
  • 試合中でないとき(メインメニュー、リザルト画面)は、ワールド座標やユニット上に置いた要素は描かれません。画面位置に置いた要素は通常どおり描かれます。
  • 地面の円は、円周上の 64 点をそれぞれ地面に投影したものです。地形に高低差があると形も起伏します —— これは正しい動作で、実際の地面の上に描いているからです。
  • 初回はフックの導入とフォントのウォームアップに約 1 秒かかり、その間テキスト系の要素はまだ描かれません。円と線は描かれます。
  • 描画中に異常が 1 回でも起きると、そのセッションでは以後描画しません(頭上の吹き出しと同じ保護機構)。stats() の faults が 1 になります。
  • テキスト、画像パス、点の合計は 64 KB まで、要素は最大 256 個です。画像パスは、ゲームプロセスから読めるローカルのパスである必要があります。

AI コンパニオン のステータスパネルもキャンバスで描いています:HP バー、いま何をしているか、気分、撃破数と回復回数。