Metadata-Version: 2.4
Name: walimaker
Version: 3.1.0
Summary: A Python game engine designed for educational purposes
Author-email: Walimaker Project <walimaker@example.com>
Maintainer-email: Walimaker Maintainers <walimaker@example.com>
License-Expression: MIT
Project-URL: PyPI, https://pypi.org/project/walimaker/
Keywords: game,pygame,pymunk,education,game-engine
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Education
Classifier: Topic :: Games/Entertainment
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Environment :: MacOS X
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: X11 Applications
Requires-Python: >=3.8.1
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pygame>=2.5.0
Requires-Dist: pymunk>=6.5.0
Requires-Dist: pytmx>=3.32
Provides-Extra: dev
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: flake8>=6.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.4; extra == "docs"
Requires-Dist: mkdocs-material>=9.0; extra == "docs"
Dynamic: license-file

# 🎮 Walimaker - 专为教学设计的Python游戏引擎

[![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Code Style](https://img.shields.io/badge/code%20style-black-black.svg)](https://github.com/psf/black)

<p align="center">
  <img src="./src/Walimaker/static/walimaker-logo.png" alt="Walimaker Logo" width="200"/>
</p>

> **Walimaker** 是一个专为教学场景设计的Python游戏引擎模块。它采用简洁直观的API设计，让学生能够快速上手游戏开发，同时掌握编程核心概念。模块基于Pygame和Pymunk构建，提供完整的2D游戏开发功能。

## ✨ 特性亮点

### 🎯 教学友好
- **简洁API**：基于Python语法糖，类似Processing/turtle的直观接口
- **错误友好**：详尽的错误提示和调试信息，帮助初学者快速定位问题
- **渐进学习**：从简单绘图到复杂游戏逻辑的平滑过渡

### 🛠 功能完备
- **🎨 图形渲染**：精灵动画、位图渲染、TileMap支持
- **⚙️ 物理系统**：刚体动力学、碰撞检测、约束（Pymunk：`connect()` 刚性杆、`Spring()` 弹簧）
- **🎵 音频管理**：背景音乐、音效播放、音量控制
- **📝 UI组件**：文本框、对话框
- **⌨️ 输入系统**：鼠标与键盘事件
- **📊 资源管理**：智能 LRU 缓存，自动内存管理

### 🚀 开发效率
- **调试工具**：调试绘制 `debug()`、自动刷新 `tracer()`、移动限速 `speed()`、性能基准脚本
- **类型友好**：随包提供 `py.typed`，编辑器与 mypy 能直接提示参数类型
- **测试与门禁**：8 个测试文件 + `scripts/check.py`（一条命令跑 black / flake8 / mypy / 全部测试，
  并支持 `--matrix` 自动跑 pymunk 6/7 双版本）
- **模块化设计**：对象模型、精灵动画、物理、渲染、资源各司其职，便于定制

## 🚀 快速开始

### 安装

#### 通过pip安装
```bash
pip install Walimaker
```

### 第一个游戏：移动的角色

```python
from Walimaker import *

# 初始化游戏窗口
setup(800, 600)
title("我的第一个Walimaker游戏")

# 创建角色
character = Character(["idle_1.png", "idle_2.png", "idle_3.png"])
character.scale(2, 2)

# 主游戏循环
while True:
    # 事件处理：key_pressed 接收 pygame 的按键常量
    # （K_UP / K_a / K_SPACE 等常量随 `from Walimaker import *` 一起提供）
    if key_pressed(K_UP):
        character.forward(5)
    if key_pressed(K_LEFT):
        character.left(5)
    if key_pressed(K_RIGHT):
        character.right(5)
    if key_pressed(K_SPACE):
        character.say("跳跃！")

    # 动画播放
    character.play_anim()
    
    # 渲染更新
    update()
```

## 🧭 中文报错提示（教学特性）

程序出错时，**英文原始报错会完整保留**（异常类型、消息、完整 traceback），中文解释追加在它的后面，
方便初学者对照着官方报错一起看：

```
Traceback (most recent call last):
  File "main.py", line 12, in <module>
    if key_pressed(K_SPACE):
NameError: name 'K_SPACE' is not defined

────────────────────────────────────────────────────────────────
【Walimaker 中文提示】按键/事件常量「K_SPACE」没有导入
  原因：pygame 的按键、鼠标、事件常量（K_SPACE、KEYDOWN、QUIT、MOUSEBUTTONDOWN 等）需要先导入才能使用。
  解决：① 确认 walimaker 版本 ≥ 2.1.0（pip install -U walimaker），这些常量会随 from Walimaker import *
        一起提供；② 或者显式写 from pygame.locals import *。
  参考：CHANGELOG 2.1.0「恢复 pygame 事件/按键常量的导出」
────────────────────────────────────────────────────────────────
```

覆盖的常见错误包括：缺少依赖、变量/函数未定义（按键常量有专项提示）、忘记调用 `setup()`、
图片/音乐路径找不到、没有声卡、pymunk 坐标类型错误、pymunk 版本兼容问题、`key_pressed("w")` 传了字符串、
图片类型不支持、`setup()` 参数不对、Tab 与空格混用、语法/缩进错误、除零、全局变量作用域、递归过深等；
如果报错发生在 Walimaker 自身代码里，会提示"可能是框架问题 + 如何反馈"。

**关闭方式**（默认开启）：

```bash
WALIMAKER_ERROR_HELP=0 python main.py     # 环境变量
```

```python
set_error_help(False)                     # 或在代码里关闭
```

**在 try/except 里主动取中文解释**：

```python
try:
    character = Character("hero.png")
except Exception as e:
    print(explain_exception(e))           # 返回中文解释文本
```

## 📁 项目结构

```
Walimaker/
├── 📄 pyproject.toml        # 打包 / 依赖配置
├── 📄 README.md             # 项目说明
├── 📄 LICENSE               # MIT 许可证
├── 📄 OPTIMIZATION_PLAN.md  # 性能 / 结构分析与优化计划
├── 📁 benchmarks/           # 性能基线脚本（不参与打包）
│   └── 🐍 perf_baseline.py
├── 📁 tests/                # 冒烟 / 回归测试（不参与打包）
│   └── 🐍 smoke_test.py
└── 📁 src/
    └── 📁 Walimaker/        # 包源码（import Walimaker）
        ├── 📄 API.py             # 主要API接口（Character、窗口管理等）
        ├── 📄 sprite.py          # 精灵和动画系统
        ├── ⚙️ physics.py         # 物理引擎封装（基于Pymunk）
        ├── 📷 camera.py          # 相机系统（缩放、跟随）
        ├── 🗺️ tiledmap.py        # TileMap地图系统
        ├── 📝 textbox.py         # UI文本组件
        ├── 🎵 music.py           # 音频管理系统
        ├── 📦 resource_manager.py # 资源管理（智能缓存）
        ├── 🔧 config.py          # 全局配置和初始化
        ├── 🎮 test.py            # 示例代码（walimaker-demo 入口）
        └── 📁 static/            # 资源文件（只有体积很小的图片）
            └── 🖼️ *.png          # 图片资源
```

> **关于字体**：从 2.3.0 起包内**不再携带字体文件**（原先的楷体/宋体合计 28.6 MB，
> 使 wheel 接近 16 MB，且属于微软版权字体不允许再分发）。中文显示改为运行时自动解析，
> 详见下文「🅰️ 中文字体」一节。

开发时可用 `pip install -e .`（或把 `src/` 加入 `PYTHONPATH`）后 `import Walimaker`。

### 🧰 本地开发与测试（uv，推荐）

项目已按 [uv](https://docs.astral.sh/uv/) 配好开发环境：**一条命令**建好隔离环境并装齐依赖
（`[dependency-groups] dev`：black / flake8 / mypy / pytest / twine）。

```bash
uv sync                                  # 创建 .venv（Python 版本由 .python-version 指定）并安装依赖
uv run python scripts/check.py           # 静态检查 + 全部测试（等价于 CI）
uv run python scripts/check.py --matrix  # 额外跑 pymunk 6.11.1 / 最新版 双版本矩阵
uv run python tests/smoke_test.py        # 也可以只跑单个测试
uv run python benchmarks/perf_baseline.py
```

- `uv.lock` 随仓库提供（133 个包，按 Python 3.8.1+ 全范围解析），`uv sync` 按锁文件精确安装；
  升级依赖用 `uv lock --upgrade`。
- `--matrix` 用 `uv run --no-project --with pymunk==6.11.1` 现开临时环境，两个版本各跑一遍全部测试，
  **不需要手动准备两个 venv**、也不会污染当前环境（整轮约 15 秒）。
- 不想用 uv 也可以：`pip install -e ".[dev]"` 后 `python scripts/check.py`，效果相同。

**Python 支持矩阵**（`requires-python = ">=3.8.1"`）：

| 版本 | 说明 |
|---|---|
| 3.8.1 ~ 3.8.x | 可用，但会被解析到 **pymunk 6.x**（pymunk 7 要求 ≥ 3.9）；3.8.0 不受支持（没有任何 flake8 版本可用） |
| 3.9 ~ 3.13 | 推荐区间：pymunk 7 + 官方 `pygame` wheel 齐备 |
| 3.14+ | 官方 `pygame` 尚无 wheel，需改用 [`pygame-ce`](https://pypi.org/project/pygame-ce/) |

## 📚 核心模块详解

### 🎨 sprite.py - 精灵与动画系统
- **EasySpriteStrategy** - 静态精灵策略
- **ListSpriteStrategy** - 帧动画策略
- **AnimatorStrategy** - 状态动画策略
- **SpriteSheet** - 精灵表支持
- 支持：旋转、缩放、翻转、颜色修改

### 📦 resource_manager.py - 资源管理
**智能LRU缓存系统**：
```python
from Walimaker.resource_manager import ResourceManager
resource_manager = ResourceManager.get_instance()
# 自动缓存管理
image = resource_manager.load_image("image.png")  # 首次加载
cached_image = resource_manager.load_image("image.png")  # 从缓存读取

# 缓存统计
info = resource_manager.get_cache_info()
print(f"图片缓存: {info['images']['current_size']}/{info['images']['maxsize']}")
```

### 🎮 API.py - 主要接口
提供简洁的面向对象API：
```python
from Walimaker import *
# 创建各种类型的角色
character = Character("image.png")  # 静态角色
anim_character = Character(["frame1.png", "frame2.png"])  # 动画角色
state_character = Character({  # 状态机角色
    "idle": ["idle_1.png", "idle_2.png"],
    "walk": ["walk_1.png", "walk_2.png", "walk_3.png"]
})
```

---
# 🧭 API文档

## 📋 概述
Walimaker采用笛卡尔坐标系（像素单位），提供完整的游戏开发功能。坐标原点位于窗口左上角，角度系统：0°指向右侧，逆时针方向角度增加。

---

## 🖼️ 窗口设置模块

| 函数名 | 功能描述 | 参数说明 | 返回值 | 备注 |
|-------|---------|---------|--------|------|
| `setup(width, height)` | 创建指定尺寸的窗口 | `width`: 宽度<br>`height`: 高度 | - | 窗口在屏幕中心生成 |
| `update()` | 刷新画面显示 | - | - | 将修改内容更新到窗口 |
| `title(text)` | 设置窗口标题 | `text`: 标题文本 | - | - |
| `bgpic(image_path)` | 设置背景（图片 / `Surface` / `TiledMap`），并把相机尺寸设为它的大小 | `image_path`: 图片路径、`Surface` 或 `TiledMap` | 背景对象 | 图片小于窗口时显示黑色背景；传入 `TiledMap` 会真正渲染整张地图 |
| `bgmusic(music_path)` | 播放背景音乐 | `music_path`: 音乐路径 | - | 循环播放模式 |
| `set_volume(level)` | 设置音量 | `level`: 0-1浮点数 | - | 全局音量控制 |
| `tracer(enable)` | 自动刷新控制 | `enable`: 布尔值 | - | 防止主线程卡顿 |
| `save_screen(path)` | 截图保存 | `path`: 保存路径 | - | 保存当前窗口画面 |
| `set_mouse_visible(visible)` | 鼠标显示控制 | `visible`: 布尔值 | - | 显示/隐藏鼠标光标 |

---

## Character 角色类

### 构造方法
| 方式 | 描述 | 参数 | 返回值 |
|------|------|------|--------|
| **单图模式** | 创建静态角色 | `image_path`: 图片路径 | Character对象 |
| **动画序列** | 创建帧动画角色 | `image_list`: 图片路径列表 | Character对象 |
| **状态动画** | 创建多状态角色 | `anim_dict`: {状态名: 图片列表} | Character对象 |

### 核心属性
| 属性 | 类型 | 描述 | 权限 |
|------|------|------|------|
| `x`, `y` | `float` | 角色坐标位置 | 读写 |
| `pos` | `tuple` | 坐标元组 (x, y) | 读写 |
| `rot` | `float` | 旋转角度 (0°=右侧，逆时针) | 读写 |
| `dir` | `vec` | 方向向量 | 读写 |
| `red`, `green`, `blue`, `alpha` | `int` | 颜色通道值 (0-255) | 读写 |
| `color` | `tuple` | 颜色元组 (r, g, b, a) | 读写 |
| `visible` | `bool` | 可见性状态 | 只读 |
| `width`, `height` | `int` | 图片尺寸 | 只读 |
| `frame` | `int` | 当前动画帧索引 | 读写 |
| `state` | `str` | 当前动画状态名 | 读写 |
| `dt` | `float` | 动画帧间隔时间 | 读写 |
| `layer` | `int` | 渲染层级 (0=底层) | 读写 |

### 动作方法
| 方法 | 功能 | 参数 | 返回值 |
|------|------|------|--------|
| `forward(distance)` | 向前移动 | `distance`: 像素距离 | - |
| `backward(distance)` | 向后移动 | `distance`: 像素距离 | - |
| `left(angle)` | 左转 | `angle`: 旋转角度 | - |
| `right(angle)` | 右转 | `angle`: 旋转角度 | - |
| `goto(x, y)` | 瞬移到坐标 | `x, y`: 目标坐标 | - |
| `slide_to(pos, velocity)` | 滑动移动 | `pos`: 目标位置<br>`velocity`: 速度 | - |
| `scale(width, height)` | 缩放尺寸 | `width, height`: 缩放倍数 | - |
| `flipx(enable)` | 水平翻转 | `enable`: 布尔值 | - |
| `flipy(enable)` | 垂直翻转 | `enable`: 布尔值 | - |
| `show()` / `hide()` | 显示/隐藏角色 | - | - |

### 交互方法
| 方法 | 功能 | 参数 | 返回值 |
|------|------|------|--------|
| `play_snd(sound_path, volume)` | 播放音效 | `sound_path`: 音效路径<br>`volume`: 音量(0-1) | - |
| `collide(target)` | 碰撞检测 | `target`: 角色或坐标 | `bool` |
| `separate(target)` | 分离检测 | `target`: 角色或坐标 | `bool` |
| `distance(target)` | 距离计算 | `target`: 角色或坐标 | `float` |
| `say(text)` | 显示对话气泡 | `text`: 对话内容 | - |

### 动画控制
| 方法 | 功能 | 参数 | 返回值 |
|------|------|------|--------|
| `play_anim()` | 开始播放动画 | - | - |
| `stop_anim()` | 停止播放动画 | - | - |
| `set_dt(state, interval)` | 设置帧间隔 | `state`: 状态名<br>`interval`: 间隔时间 | - |
| `set_next_state(from_state, to_state)` | 设置状态切换 | `from_state`: 当前状态<br>`to_state`: 下一状态 | - |
| `set_start_func(state, func)` | 设置开始回调 | `state`: 状态名<br>`func`: 回调函数 | - |
| `set_end_func(state, func)` | 设置结束回调 | `state`: 状态名<br>`func`: 回调函数 | - |

### 鼠标交互
| 方法 | 功能 | 参数 | 返回值 |
|------|------|------|--------|
| `get_mouse_clicked()` | 鼠标点击检测 | - | `bool` |
| `get_mouse_just_clicked()` | 鼠标点击瞬间检测 | - | `bool` |
| `get_mouse_just_released()` | 鼠标释放瞬间检测 | - | `bool` |
| `get_mouse_upon()` | 鼠标悬停检测 | - | `bool` |
| `kill()` | 销毁角色对象 | - | - |

---

## TextBox 文本框类

### 构造函数
`TextBox(font_size, font=None, bg_color=默认值, font_color=默认值)`

`font=None` 表示自动选择字体（包内 static/ → 系统中文字体 → pygame 默认字体）；
也可以直接给字体路径，例如 `TextBox(25, font=r"C:\Windows\Fonts\simhei.ttf")`。

### 属性
| 属性 | 类型 | 描述 | 权限 |
|------|------|------|------|
| `pos` | `tuple` | 文本框位置坐标 | 读写 |
| `color` | `tuple` | 文本颜色 (r, g, b) | 读写 |

### 方法
| 方法 | 功能 | 参数 | 返回值 |
|------|------|------|--------|
| `write(content)` | 设置文本内容 | `content`: 文本字符串 | - |
| `print(content)` | `write()` 的兼容别名（旧代码可用，但会遮蔽内建 `print`） | `content`: 文本字符串 | - |
| `goto(x, y)` | 移动文本框 | `x, y`: 目标坐标 | - |

---

## 事件处理函数

### 鼠标事件
| 函数                          | 功能       | 参数  | 返回值              |
| --------------------------- | -------- | --- | ---------------- |
| `get_mouse_pos()`           | 获取鼠标坐标   | -   | `tuple` (x, y)   |
| `get_mouse_rel()`           | 获取鼠标移动向量 | -   | `tuple` (dx, dy) |
| `get_mouse_clicked()`       | 检测鼠标按下状态 | -   | `bool`           |
| `get_mouse_just_clicked()`  | 检测鼠标按下瞬间 | -   | `bool`           |
| `get_mouse_just_released()` | 检测鼠标释放瞬间 | -   | `bool`           |

### 键盘事件
| 函数 | 功能 | 参数 | 返回值 |
|------|------|------|--------|
| `key_pressed(key)` | 检测按键按下状态 | `key`: 按键常量(可选) | `bool` |
| `key_just_pressed(key)` | 检测按键按下瞬间 | `key`: 按键常量(可选) | `bool` |
| `key_just_released(key)` | 检测按键释放瞬间 | `key`: 按键常量(可选) | `bool` |
| `key_input()` | 获取按键输入字符 | - | `str` |

---

## 🚦 高级用法

### 🌍 多世界（互不干扰的两套场景）

每个 `World` 拥有自己的窗口状态、相机、精灵组、物理空间与更新组。
**所有接口的 `world` 参数都可以省略，省略即默认世界**（`global_var`），所以旧代码完全不用改。

```python
from Walimaker import *
from Walimaker.config import World

# 默认世界（老写法，等价于 world=None）
setup(800, 600)
hero = Character("hero.png", size=(32, 32))

# 第二个世界：自己的相机 / 空间 / 精灵组
w2 = World()
setup(800, 600, world=w2)
ghost = Character("ghost.png", size=(32, 32), world=w2)
text = TextBox(24, world=w2)

update()          # 只推进默认世界
update(w2)        # 只推进 w2；两个世界的物理与画面互不影响

ghost.velocity = vec(50, 0)   # 只影响 w2 里的刚体
hero.kill()                   # 只移除默认世界里的对象
```

- 可用 `world` 的接口：`setup / update / done / save_screen`、`Screen`、`Camera`、
  `Character / Sensor / Wall / Mouse / NewGameObject`、`TextBox / DialogBox`、`Sprite`、
  `Body / TiledMapBodies`、`draw_line / set_gravity / debug / tracer / speed / random_pos / bgpic`、
  鼠标与键盘查询函数。
- 资源缓存（`ResourceManager`）是**进程内共享**的：图片/字体只解码一次，两个世界共用。
- 相机不再全局唯一：`World().CAMERA` 属于各自的世界，`Camera((宽, 高))` 会新建实例。

### 📷 相机手感（抖动 / 跟随死区 / 平滑缩放）

```python
from Walimaker import *

hero = Character("hero.png", size=(32, 32))
camera = global_var.CAMERA

camera.follow(hero)                     # 跟随角色
camera.deadzone = (40, 30)              # 在 80x60 的死区内移动时相机不动（默认 0 = 严格居中）
camera.zoom_to(2.0, speed=1.5)          # 平滑缩放到 2 倍（每秒变化 1.5）
camera.smooth_zoom = True               # 缩放时用 smoothscale（更平滑，但比 scale 慢）
camera.shake(amount=8, duration=0.3)    # 被打中 / 爆炸时抖一下
```

这些都默认关闭（`deadzone=(0,0)`、`smooth_zoom=False`、不抖动），因此不影响既有项目。

### 🔗 把两个物体连起来（约束）

```python
from Walimaker import *

a = Character("a.png", size=(32, 32))
b = Character("b.png", size=(32, 32))
a.goto(-50, 0)
b.goto(50, 0)

connect(a, b)                                  # 刚性杆：保持当前距离（pymunk.PinJoint）
Spring(a, b, rest_length=100, stiffness=200)   # 弹簧：把距离拉回 rest_length（DampedSpring）
```

两者都接受游戏对象或物理层的 `Body`，并自动把关节加到对象所属世界的空间；
距离/劲度/阻尼可以直接读改（`spring.rest_length = 80`）。

### 🗺️ TileMap：铺成背景 / 取出对象层

```python
from Walimaker import *

tiled = TiledMap("map.tmx")

# 1) 把整张地图当背景铺上（相机尺寸自动设成地图大小），返回背景对象
bg = bgpic(tiled)

# 2) 把对象层里的对象变成可碰撞的游戏对象（每个对象一个 POLY 刚体）
#    TMX 图块对象会自动带上贴图；也可以用 images={"对象名": 图片} 指定
objects = tiled.create_objects()                    # 全部
spawns = tiled.create_objects(name="spawn")         # 按名字过滤
for obj in objects:
    print(obj.name, obj.properties, tuple(obj.pos))

# 想自己建对象也行：直接看解析结果（几何 + 属性）
for obj in tiled.get_objects():
    print(obj.name, obj.x, obj.y, obj.points)
```

### 状态机与动画控制
```python
from Walimaker import *

character = Character({
    "idle": ["idle_1.png", "idle_2.png"],
    "walk": ["walk_1.png", "walk_2.png", "walk_3.png"],
    "jump": ["jump_1.png", "jump_2.png"]
})

# 设置动画参数
character.set_dt("walk", 0.1)  # 走路动画每帧0.1秒
character.set_next_state("walk", "idle")  # 走路结束后回到空闲状态

# 状态切换回调
def on_jump_start():
    print("开始跳跃!")
    
def on_jump_end():
    print("跳跃结束!")
    character.state = "idle"

character.set_start_func("jump", on_jump_start)
character.set_end_func("jump", on_jump_end)

# 切换状态
character.state = "walk"  # 开始走路
character.state = "jump"  # 开始跳跃（触发回调）
```

### 资源管理
```python
from Walimaker import *
from Walimaker.config import global_var

# 预加载资源（顶层 API，避免第一次使用时卡顿）
preload({
    "images": ["player.png", "enemy.png", "background.png"],
    "fonts": [(None, 24), (None, 36)],          # None = 自动选择中文字体
    "sounds": ["jump.wav", "collect.wav"]
})

# 查看缓存状态
cache_info = global_var.RESOURCE_MANAGER.get_cache_info()
print(f"已缓存图片: {cache_info['images']['current_size']}张")
print(f"已缓存字体: {cache_info['fonts']['current_size']}种")

# 清理缓存
global_var.RESOURCE_MANAGER.clear_cache("image")  # 仅清理图片缓存
```

> 图片/字体/音效用 LRU 缓存；**碰撞 mask 用弱引用缓存**——同一个 Surface 只构建一次，
> Surface 被回收后缓存条目自动消失，不会把临时 Surface 一直留在内存里。

### 🅰️ 中文字体

从 2.3.0 起包内不再携带字体文件（原先的 `simkai.ttf` + `simsun.ttc` 合计 28.6 MB，
使 wheel 接近 16 MB，且属于微软授权字体不允许再分发）。`TextBox` / `DialogBox`
的字体改为**运行时按顺序解析**：

| 顺序 | 来源 | 说明 |
| --- | --- | --- |
| 1 | 显式传入路径 | `TextBox(25, font=r"C:\Windows\Fonts\simhei.ttf")` |
| 2 | 环境变量 `WALIMAKER_FONT` | 机房统一字体：`set WALIMAKER_FONT=D:\fonts\SourceHanSansSC.otf` |
| 3 | 包内 `Walimaker/static/` | 把任意 ttf/ttc/otf 丢进去即可，无需改代码 |
| 4 | 操作系统中文字体 | Windows 微软雅黑/黑体/宋体；macOS 苹方/冬青黑体；Linux Noto CJK/文泉驿 |
| 5 | pygame 默认字体 | 任何平台都存在，但**不含中文字形**（中文显示成方框） |

- **找不到字体不会报错**：只发出一次中文 + 英文的 `RuntimeWarning`，程序继续运行；
  仅当“显式指定的字体文件确实存在但已损坏”时才抛出错误。
- 排查中文显示成方框：

  ```python
  from Walimaker.fonts import font_info
  print(font_info())
  # {'path': '/System/Library/Fonts/Hiragino Sans GB.ttc', 'source': 'system',
  #  'source_label': '操作系统中文字体', 'has_cjk': True}
  ```

  若 `has_cjk` 为 `False`，说明系统里没有中文字体（常见于精简 Linux / Docker 镜像），
  安装 `fonts-noto-cjk`（Debian/Ubuntu：`apt install fonts-noto-cjk`）或设置
  `WALIMAKER_FONT` 指向一个中文字体文件即可。只跑逻辑、不显示画面
  （`SDL_VIDEODRIVER=dummy`）的场景不受影响。

## 📊 性能优化建议

### 最佳实践
1. **资源复用**：尽量复用Character对象，避免频繁创建销毁
2. **动画优化**：使用精灵表和状态机，减少Draw Call
3. **物理优化**：将静态物体设为STATIC类型，减少物理计算
4. **内存管理**：使用resource_manager的预加载功能
5. **批处理**：相似的对象尽量使用相同的材质和大小

### 常见性能问题
- **问题**: 游戏卡顿，FPS下降
- **检查**: 使用`debug()`模式查看物理调试绘制
- **解决**: 减少同时活动的物理对象数量

- **问题**: 内存占用过高
- **检查**: 查看resource_manager缓存状态
- **解决**: 调整缓存大小或手动清理缓存

## 🧪 测试示例

查看 `Walimaker/test.py` 获取更多完整的游戏示例：
- 平台跳跃游戏
- 弹球物理模拟
- TileMap使用示例
- UI交互演示

## 🤝 贡献指南

欢迎贡献代码！请遵循以下步骤：

1. **Fork项目**
2. **创建功能分支** (`git checkout -b feature/AmazingFeature`)
3. **提交修改** (`git commit -m 'Add some AmazingFeature'`)
4. **推送分支** (`git push origin feature/AmazingFeature`)
5. **开启Pull Request**

### 代码规范
- 遵循 **PEP 8** 编码规范
- 使用 **类型注解** (Python 3.7+)
- **注释**：重要功能和复杂逻辑需要注释
- **测试**：新增功能需包含测试用例

## 📄 许可证

本项目采用 **MIT 许可证** - 查看 [LICENSE](LICENSE) 文件了解详情。

## 📞 支持与反馈

- **文档问题 / Bug 报告**：附上重现步骤、错误日志与运行环境（Python、pygame、pymunk 版本）
- **功能建议**：欢迎直接反馈
- **版本与更新日志**：见 [PyPI 项目页](https://pypi.org/project/walimaker/) 与仓库根目录的 `CHANGELOG.md`

## 🙏 致谢

感谢以下开源项目：
- [Pygame](https://www.pygame.org/) - 游戏开发库
- [Pymunk](https://www.pymunk.org/) - 2D物理引擎
- [Pytmx](https://github.com/pytmx/pytmx) - TMX地图解析库

## 🚀 下一步计划（Roadmap）

> **诚信说明**：早期版本的介绍里曾把下面几项列为「已实现特性」，实际尚未落地。
> 现在统一移到 Roadmap，未做就是未做；欢迎反馈你最想要哪一个。

- [ ] 代码热重载（改代码即时生效，无需重启）
- [ ] 可视化编辑器
- [ ] 插件 / 组件系统
- [ ] 常用 UI 控件（按钮、输入框、列表…）
- [ ] 触控 / 移动端手势
- [ ] WebAssembly 支持（让 Walimaker 在浏览器中运行）
- [ ] 3D 渲染扩展（基于 OpenGL）
- [ ] 网络多人游戏支持
- [ ] 更多教学资源和示例

---

## 🧭 坐标系说明
- **坐标系统**：笛卡尔坐标系，原点位于窗口左上角
- **角度系统**：0°指向右侧，逆时针方向角度增加
- **颜色值范围**：RGB通道 0-255，透明度 0-255
- **音量控制**：0.0（静音）到 1.0（最大音量）
- **物理单位**：像素为单位，重力默认 900 像素/秒²

> **教学提示**：Walimaker的设计目标是让编程初学者能够在30分钟内制作出第一个可玩的游戏。从简单到复杂，循序渐进地学习游戏开发的核心概念。
