畫板
在遊戲畫面上畫文字框、面板、進度條、圖片、貼著地面的圈和帶箭頭的路線。執行環境每幀自己繪製,不修改遊戲狀態,多人局也安全;Python、HTTP、直接寫共享記憶體都行。
外部程式可以在遊戲畫面上畫文字框、面板、進度條、圖片、地上的圈、地上的路線(帶箭頭),由執行環境每幀自己繪製。拿來做自己的 HUD、輔助線、提示、教學標註、直播資訊板,都很合適。
畫板和 JASS 畫面函式,怎麼選
| 畫板(本頁) | JASS 畫面函式 | |
|---|---|---|
| 誰來畫 | 執行環境自繪 | 遊戲自己(浮動文字、特效、面板、頭像對白……) |
| 多人局 | 安全:只畫在本機畫面上,不建立遊戲物件、不修改遊戲狀態 | 只能單人局 |
| 樣式 | 隨意:中文字型、圓角、半透明、邊框、任意顏色、本機圖片 | 遊戲原生風格 |
| 跟著東西走 | 跟單位、世界座標、螢幕位置;地上的圈貼著地形起伏 | 看具體函式 |
| 開銷 | 實測每幀 0.2 ~ 0.35 ms(9 個元素) | 每次呼叫約 13 ms |
兩條路可以一起用:原生風格的效果用 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", "Boss 要放大招了!", 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(): ... # 一次改很多條,只寫一次共享記憶體
c.stats() # drawnFrames 在增加 = 真的在畫
每個元素用一個 key 識別:同一個 key 再畫一次就是更新。
能點:文字框和面板加上 clickable=True(滑鼠停在上面時的顏色用 hover= 設定),點中時事件流裡會來一條 ui.click,ev.key 就是這個 key,點在它上面的那一下遊戲收不到。現成的按鈕、選項卡片、熱鍵、點地面請見 介面與輸入。
位置(每個元素給一個):
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, 文字, ...) | 文字框,多行用 \n | color、bg 底色(不給就透明)、border、size、bold、shadow、width(依這個寬度換行)、radius 圓角 |
panel(key, 標題, [行...], ...) | 面板(深色半透明底、金邊) | 同 text |
bar(key, 0..1, ...) | 進度條:血量、冷卻、讀條 | 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(任何語言)
遠見後端(只監聽本機):
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 和遠見也可以:先送一次語意命令 canvas_enable(W3P 操作碼 73),執行環境會建好共享記憶體區塊 Local\War3Canvas_<pid>:標頭 64 位元組 + 256 條 × 112 位元組 + 64 KB 文字 / 點池。依 seqlock 寫入(序號變奇數 → 寫條目和池 → 序號變偶數),執行環境每幀讀一次,讀到寫了一半的就沿用上一幀,並回寫已繪製幀數、元素數和異常計數。Python 參考實作是 sdk/python/w3canvas.py,結構定義在協定標頭檔裡,見 W3P 協定。
好幾個程式同時畫
模組、遠見、MCP、閘道可能同時在同一局裡畫東西,畫板只有一塊。規矩是:每個程式只動自己的元素。
- 寫入之前先取得一把具名鎖,讀出現有的元素,保留別人的,換上自己的,再寫回去;
- 每個元素記著是誰畫的(行程 ID + 行程內序號),畫它的程式結束了,下一次有人寫入時順手清掉;它的按鈕也不再攔截點擊;
- 元素編號從共用的計數器分配,不會撞號。
Python SDK 已經這麼做了,clear() 也只清自己的。自己直接寫共享記憶體的話照這個來,否則會把別人的東西沖掉。配置細節見 W3P 協定。
實測與注意事項
- 2026-09-25 實測(1920×1080,2 倍速):9 個元素每幀 0.27 ~ 0.34 ms,約 63 幀 / 秒,0 次異常;寫入 9 條用時 6 ms;英雄走動時,跟著單位的圈、文字、血條都跟得上。內容變了才重畫貼圖,只挪動位置不重畫。
- 畫在遊戲介面之後、滑鼠游標之前:蓋在遊戲自己的血條、單位和介面上面,滑鼠游標則蓋在它上面。它會避開底部操作面板和頂部晝夜球,但不會避讓地圖自己的面板(右上角的排行榜、倒數計時)—— 自己的面板別放在右上角。
- 不在對局裡(主選單、結算畫面)時,放在世界座標和單位上的元素不畫,螢幕位置的照畫。
- 地上的圈是把圓周上的 64 個點各自投影到地面上,地形有高低時形狀會跟著起伏 —— 這是對的:它畫在真實的地面上。
- 第一次開啟要裝掛鉤、預熱字型,約 1 秒,期間文字類元素先不畫,圈和線照畫。
- 繪製時出現一次異常,本次工作階段就不再畫(和頭頂氣泡同一套保護),
stats()裡的faults會變成 1。 - 文字、圖片路徑和點一共 64 KB,最多 256 個元素;圖片路徑必須是遊戲行程讀得到的本機路徑。
AI 玩伴 的狀態面板就是用畫板畫的:血條、正在做什麼、心情、擊殺和補血次數。