文档 玩法扩展

画板

往游戏画面上画文字框、面板、进度条、图片、贴着地面的圈和带箭头的路线。运行时每帧自己画,不改游戏状态,多人局也安全;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, 文字, ...)文字框,多行用 \ncolor、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、网关可能同时往同一局里画东西,画板只有一块。规矩是:每个程序只动自己的元素。

  • 写之前先拿一把命名锁,读出现有的元素,留下别人的,换上自己的,再写回;
  • 每个元素记着是谁画的(进程号 + 进程内序号),画它的程序退出了,下一次有人写时顺手清掉;它的按钮也不再拦点击;
  • 元素编号从共用的计数器分配,不会撞号。

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 玩伴 的状态面板就是用画板画的:血条、正在干什么、心情、击杀和加血次数。