# 畫板

> 在遊戲畫面上畫文字框、面板、進度條、圖片、貼著地面的圈和帶箭頭的路線。執行環境每幀自己繪製，不修改遊戲狀態，多人局也安全；Python、HTTP、直接寫共享記憶體都行。

來源: https://war3ai.com/zh-tw/docs/canvas/

外部程式可以在遊戲畫面上畫**文字框、面板、進度條、圖片、地上的圈、地上的路線（帶箭頭）**，由執行環境每幀自己繪製。拿來做自己的 HUD、輔助線、提示、教學標註、直播資訊板，都很合適。

## 畫板和 JASS 畫面函式，怎麼選

| | 畫板（本頁） | [JASS 畫面函式](https://war3ai.com/zh-tw/docs/jass/) |
|---|---|---|
| 誰來畫 | 執行環境自繪 | 遊戲自己（浮動文字、特效、面板、頭像對白……） |
| 多人局 | **安全**：只畫在本機畫面上，不建立遊戲物件、不修改遊戲狀態 | 只能單人局 |
| 樣式 | 隨意：中文字型、圓角、半透明、邊框、任意顏色、本機圖片 | 遊戲原生風格 |
| 跟著東西走 | 跟單位、世界座標、螢幕位置；地上的圈貼著地形起伏 | 看具體函式 |
| 開銷 | 實測每幀 0.2 ~ 0.35 ms（9 個元素） | 每次呼叫約 13 ms |

兩條路可以一起用：原生風格的效果用 JASS，自訂的面板、輔助線和提示用畫板。

## Python

```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，點在它上面的那一下遊戲收不到。現成的按鈕、選項卡片、熱鍵、點地面請見 [介面與輸入](https://war3ai.com/zh-tw/docs/ui-input/)。

**位置**（每個元素給一個）：

- `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（任何語言）

遠見後端（只監聽本機）：

```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 協定](https://war3ai.com/zh-tw/docs/protocol/)。

## 好幾個程式同時畫

模組、遠見、MCP、閘道可能同時在同一局裡畫東西，畫板只有一塊。規矩是：**每個程式只動自己的元素**。

- 寫入之前先取得一把具名鎖，讀出現有的元素，保留別人的，換上自己的，再寫回去；
- 每個元素記著是誰畫的（行程 ID + 行程內序號），畫它的程式結束了，下一次有人寫入時順手清掉；它的按鈕也不再攔截點擊；
- 元素編號從共用的計數器分配，不會撞號。

Python SDK 已經這麼做了，`clear()` 也只清自己的。自己直接寫共享記憶體的話照這個來，否則會把別人的東西沖掉。配置細節見 [W3P 協定](https://war3ai.com/zh-tw/docs/protocol/)。

## 實測與注意事項

- 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 玩伴](https://war3ai.com/zh-tw/docs/companion/) 的狀態面板就是用畫板畫的：血條、正在做什麼、心情、擊殺和補血次數。
