#!/usr/bin/env python3 """ 996传奇客户端 LabelBatchNode / LabelBatchString 接口契约 来源: 二进制 LabelBatchNode / LabelBatchString 8 + 14 = 22 个 tolua 方法 + cocos2d-x SpriteBatchNode / Label 文档对照 职责: 批量文本渲染 (伤害数字 / 血条 / 称号等高频文本) 原理: 所有 LabelBatchString 共享同一张字体纹理图集 (font atlas), 一次 draw call 渲染几百个文字 → 性能优于 Label × N 完整文档见 label_batch_doc.md (本目录) """ from __future__ import annotations from dataclasses import dataclass, field from typing import Optional, List # ============== 二进制接口契约 (22 个 tolua 方法) ============== # class LabelBatchNode (8 方法) # create() - 静态创建, 返回节点指针 # getFontAltas() - 获取共享字体图集 # hasOutline() - 是否有描边 # setAliasTexParameters() - 设置纹理抗锯齿 (默认就近) # refCameraFontScale() - 根据相机缩放调整字号 # addLabelString(string_id) - 关联一个 string # createLabelString(text) - 创建 string 实例, 返回 id # removeLabelString(string_id) - 移除 # destroyLabelString(string_id) - 销毁 # class LabelBatchString (14 方法) # LabelBatchString(text) - 构造 # cleanup() - 释放纹理引用 # get/setColor / Opacity / LocalZ - 视觉属性 # get/setPosition / ContentSize - 几何 # get/setString / Alignment - 内容 # isVisible() - 可见性 # ============== 数据结构 (Python 等价, 纯数据) ============== @dataclass class LabelBatchString: """ 单个文本单元 (对应 LabelBatchString C++ 类) 真实实现中会被编入 LabelBatchNode 的 VBO 批渲染 """ text: str = "" x: float = 0.0 y: float = 0.0 color: tuple = (255, 255, 255, 255) # RGBA opacity: int = 255 local_z: int = 0 visible: bool = True font_size: int = 14 alignment: int = 0 # 0=left, 1=center, 2=right _id: int = field(default=0, init=False) # 由 LabelBatchNode 分配 @dataclass class LabelBatchNode: """ 批量文本节点 (对应 LabelBatchNode C++ 类) 一个 Node 实例可容纳多个 LabelBatchString, 共享一张字体图集 """ font_atlas_path: str = "" # 字体纹理图集路径 has_outline: bool = False # 描边开关 use_alias: bool = True # 纹理抗锯齿 ref_camera_scale: bool = True # 是否随相机缩放 strings: List[LabelBatchString] = field(default_factory=list) _next_id: int = 1 def create(self) -> "LabelBatchNode": """LabelBatchNode:create() 等价 (静态工厂)""" return self def get_font_atlas(self) -> str: return self.font_atlas_path def has_outline_q(self) -> bool: return self.has_outline def set_alias_tex_parameters(self) -> None: """设置 GL_TEXTURE_MIN/MAG_FILTER 为 GL_LINEAR (替代默认 NEAREST)""" self.use_alias = True def ref_camera_font_scale(self) -> None: """当相机缩放变化时同步字号 (用于 720p/1080p 切换)""" self.ref_camera_scale = True def add_label_string(self, string: LabelBatchString) -> int: """addLabelString 关联一个 string 实例, 返回其 id""" string._id = self._next_id self._next_id += 1 self.strings.append(string) return string._id def create_label_string(self, text: str) -> int: """createLabelString 内部创建并 add, 返回 id""" s = LabelBatchString(text=text) return self.add_label_string(s) def remove_label_string(self, sid: int) -> None: for s in self.strings: if s._id == sid: self.strings.remove(s) return def destroy_label_string(self, sid: int) -> None: self.remove_label_string(sid) # ============== 单元自测 ============== if __name__ == "__main__": print("=== LabelBatchNode 接口自测 ===") node = LabelBatchNode(font_atlas_path="fonts/arial_14.png") print(f" font_atlas: {node.get_font_atlas()}") print(f" has_outline: {node.has_outline_q()}") s1 = LabelBatchString(text="-100", x=100, y=200, color=(255, 80, 80, 255)) s2 = LabelBatchString(text="+50", x=120, y=200, color=(80, 255, 80, 255)) s3 = LabelBatchString(text="HP: 100/100", x=0, y=0) id1 = node.add_label_string(s1) id2 = node.add_label_string(s2) id3 = node.add_label_string(s3) print(f" created 3 strings: ids={id1}, {id2}, {id3}") print(f" total strings: {len(node.strings)}") # 模拟伤害数字更新 s1.text = "-250" s1.color = (255, 50, 50, 255) print(f" updated s1: text={s1.text}, color={s1.color}") # 删除 node.remove_label_string(id2) print(f" after remove id2: {len(node.strings)} strings left") # 真实实现: 一次 draw call 渲染所有 string # 实际 C++ 端把每个 string 编码成 6 顶点 (quad), 写进共享 VBO print(f"\n [实际 C++ 渲染]: batch.draw({len(node.strings)} strings)") print(f" [实际 GPU 调用]: 1 draw call + {len(node.strings) * 6} vertices")