label_batch.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #!/usr/bin/env python3
  2. """
  3. 996传奇客户端 LabelBatchNode / LabelBatchString 接口契约
  4. 来源: 二进制 LabelBatchNode / LabelBatchString 8 + 14 = 22 个 tolua 方法
  5. + cocos2d-x SpriteBatchNode / Label 文档对照
  6. 职责: 批量文本渲染 (伤害数字 / 血条 / 称号等高频文本)
  7. 原理: 所有 LabelBatchString 共享同一张字体纹理图集 (font atlas),
  8. 一次 draw call 渲染几百个文字 → 性能优于 Label × N
  9. 完整文档见 label_batch_doc.md (本目录)
  10. """
  11. from __future__ import annotations
  12. from dataclasses import dataclass, field
  13. from typing import Optional, List
  14. # ============== 二进制接口契约 (22 个 tolua 方法) ==============
  15. # class LabelBatchNode (8 方法)
  16. # create() - 静态创建, 返回节点指针
  17. # getFontAltas() - 获取共享字体图集
  18. # hasOutline() - 是否有描边
  19. # setAliasTexParameters() - 设置纹理抗锯齿 (默认就近)
  20. # refCameraFontScale() - 根据相机缩放调整字号
  21. # addLabelString(string_id) - 关联一个 string
  22. # createLabelString(text) - 创建 string 实例, 返回 id
  23. # removeLabelString(string_id) - 移除
  24. # destroyLabelString(string_id) - 销毁
  25. # class LabelBatchString (14 方法)
  26. # LabelBatchString(text) - 构造
  27. # cleanup() - 释放纹理引用
  28. # get/setColor / Opacity / LocalZ - 视觉属性
  29. # get/setPosition / ContentSize - 几何
  30. # get/setString / Alignment - 内容
  31. # isVisible() - 可见性
  32. # ============== 数据结构 (Python 等价, 纯数据) ==============
  33. @dataclass
  34. class LabelBatchString:
  35. """
  36. 单个文本单元 (对应 LabelBatchString C++ 类)
  37. 真实实现中会被编入 LabelBatchNode 的 VBO 批渲染
  38. """
  39. text: str = ""
  40. x: float = 0.0
  41. y: float = 0.0
  42. color: tuple = (255, 255, 255, 255) # RGBA
  43. opacity: int = 255
  44. local_z: int = 0
  45. visible: bool = True
  46. font_size: int = 14
  47. alignment: int = 0 # 0=left, 1=center, 2=right
  48. _id: int = field(default=0, init=False) # 由 LabelBatchNode 分配
  49. @dataclass
  50. class LabelBatchNode:
  51. """
  52. 批量文本节点 (对应 LabelBatchNode C++ 类)
  53. 一个 Node 实例可容纳多个 LabelBatchString, 共享一张字体图集
  54. """
  55. font_atlas_path: str = "" # 字体纹理图集路径
  56. has_outline: bool = False # 描边开关
  57. use_alias: bool = True # 纹理抗锯齿
  58. ref_camera_scale: bool = True # 是否随相机缩放
  59. strings: List[LabelBatchString] = field(default_factory=list)
  60. _next_id: int = 1
  61. def create(self) -> "LabelBatchNode":
  62. """LabelBatchNode:create() 等价 (静态工厂)"""
  63. return self
  64. def get_font_atlas(self) -> str:
  65. return self.font_atlas_path
  66. def has_outline_q(self) -> bool:
  67. return self.has_outline
  68. def set_alias_tex_parameters(self) -> None:
  69. """设置 GL_TEXTURE_MIN/MAG_FILTER 为 GL_LINEAR (替代默认 NEAREST)"""
  70. self.use_alias = True
  71. def ref_camera_font_scale(self) -> None:
  72. """当相机缩放变化时同步字号 (用于 720p/1080p 切换)"""
  73. self.ref_camera_scale = True
  74. def add_label_string(self, string: LabelBatchString) -> int:
  75. """addLabelString 关联一个 string 实例, 返回其 id"""
  76. string._id = self._next_id
  77. self._next_id += 1
  78. self.strings.append(string)
  79. return string._id
  80. def create_label_string(self, text: str) -> int:
  81. """createLabelString 内部创建并 add, 返回 id"""
  82. s = LabelBatchString(text=text)
  83. return self.add_label_string(s)
  84. def remove_label_string(self, sid: int) -> None:
  85. for s in self.strings:
  86. if s._id == sid:
  87. self.strings.remove(s)
  88. return
  89. def destroy_label_string(self, sid: int) -> None:
  90. self.remove_label_string(sid)
  91. # ============== 单元自测 ==============
  92. if __name__ == "__main__":
  93. print("=== LabelBatchNode 接口自测 ===")
  94. node = LabelBatchNode(font_atlas_path="fonts/arial_14.png")
  95. print(f" font_atlas: {node.get_font_atlas()}")
  96. print(f" has_outline: {node.has_outline_q()}")
  97. s1 = LabelBatchString(text="-100", x=100, y=200, color=(255, 80, 80, 255))
  98. s2 = LabelBatchString(text="+50", x=120, y=200, color=(80, 255, 80, 255))
  99. s3 = LabelBatchString(text="HP: 100/100", x=0, y=0)
  100. id1 = node.add_label_string(s1)
  101. id2 = node.add_label_string(s2)
  102. id3 = node.add_label_string(s3)
  103. print(f" created 3 strings: ids={id1}, {id2}, {id3}")
  104. print(f" total strings: {len(node.strings)}")
  105. # 模拟伤害数字更新
  106. s1.text = "-250"
  107. s1.color = (255, 50, 50, 255)
  108. print(f" updated s1: text={s1.text}, color={s1.color}")
  109. # 删除
  110. node.remove_label_string(id2)
  111. print(f" after remove id2: {len(node.strings)} strings left")
  112. # 真实实现: 一次 draw call 渲染所有 string
  113. # 实际 C++ 端把每个 string 编码成 6 顶点 (quad), 写进共享 VBO
  114. print(f"\n [实际 C++ 渲染]: batch.draw({len(node.strings)} strings)")
  115. print(f" [实际 GPU 调用]: 1 draw call + {len(node.strings) * 6} vertices")