pycoode 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycoode-1.0.0/PKG-INFO +221 -0
- pycoode-1.0.0/README.md +202 -0
- pycoode-1.0.0/pycoode/__init__.py +37 -0
- pycoode-1.0.0/pycoode/audio.py +69 -0
- pycoode-1.0.0/pycoode/core.py +455 -0
- pycoode-1.0.0/pycoode/demo.py +59 -0
- pycoode-1.0.0/pycoode/gfx.py +95 -0
- pycoode-1.0.0/pycoode/py.typed +0 -0
- pycoode-1.0.0/pycoode/scene.py +65 -0
- pycoode-1.0.0/pycoode/sprites.py +161 -0
- pycoode-1.0.0/pycoode/text.py +103 -0
- pycoode-1.0.0/pycoode/utils.py +137 -0
- pycoode-1.0.0/pycoode.egg-info/PKG-INFO +221 -0
- pycoode-1.0.0/pycoode.egg-info/SOURCES.txt +18 -0
- pycoode-1.0.0/pycoode.egg-info/dependency_links.txt +1 -0
- pycoode-1.0.0/pycoode.egg-info/requires.txt +1 -0
- pycoode-1.0.0/pycoode.egg-info/top_level.txt +1 -0
- pycoode-1.0.0/pyproject.toml +34 -0
- pycoode-1.0.0/setup.cfg +4 -0
- pycoode-1.0.0/tests/test_headless.py +359 -0
pycoode-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pycoode
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: 让做游戏像写一行代码一样简单 —— 基于 pygame 的极简 2D 游戏开发包
|
|
5
|
+
Author: pycoode
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/pycoode/pycoode
|
|
8
|
+
Project-URL: Documentation, https://github.com/pycoode/pycoode#readme
|
|
9
|
+
Keywords: game,pygame,2d,easy,beginner
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Games/Entertainment
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: pygame>=2.0
|
|
19
|
+
|
|
20
|
+
# pycoode
|
|
21
|
+
|
|
22
|
+
**让做游戏像写一行代码一样简单。**
|
|
23
|
+
|
|
24
|
+
`pycoode` 是基于 pygame 的极简 2D 游戏开发包。它替你扛下了所有麻烦事:
|
|
25
|
+
窗口创建、中文字体、高 DPI、分辨率适配、游戏循环、输入、精灵、音效、场景管理。
|
|
26
|
+
你只需要写游戏逻辑。
|
|
27
|
+
|
|
28
|
+
## 特性
|
|
29
|
+
|
|
30
|
+
- **一行起跑** —— `pc.run()` 直接弹窗口;`pc.run(draw=...)` 就能画出东西
|
|
31
|
+
- **零适配问题** —— 游戏按设计分辨率(如 800x600)写坐标,窗口任意拉伸自动信箱缩放、不变形不错位;Windows 高 DPI(150% 缩放)自动适配
|
|
32
|
+
- **中文开箱即用** —— 自动匹配系统中文字体,`g.text("你好")` 不用准备字体文件
|
|
33
|
+
- **输入超简单** —— `g.key_held("space")`、`g.mouse_pressed(1)`,支持方向键/WASD/触屏
|
|
34
|
+
- **精灵系统** —— 移动、旋转、缩放、翻转、矩形/圆形碰撞、逐帧动画,`Sprite.solid("red", 50, 50)` 没有美术素材也能开工
|
|
35
|
+
- **音效一行播放** —— `pc.play("hit.wav")`、`pc.music("bgm.mp3")`,无声环境自动降级不崩溃
|
|
36
|
+
- **场景管理** —— 菜单 / 关卡 / 结算各自一个类,`self.switch(...)` 秒切换
|
|
37
|
+
- **跨平台** —— Windows / macOS / Linux,Python 3.8+
|
|
38
|
+
|
|
39
|
+
## 安装
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install pygame pycoode
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 30 秒上手
|
|
46
|
+
|
|
47
|
+
**第 1 行代码:**
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import pycoode as pc
|
|
51
|
+
|
|
52
|
+
pc.run(draw=lambda g: g.text("Hello, pycoode!", 400, 300, size=48,
|
|
53
|
+
color="gold", center=True))
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**一个能玩的小游戏(键盘控制 + 碰撞 + 计分):**
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import pycoode as pc
|
|
60
|
+
|
|
61
|
+
player = pc.Sprite.solid("cyan", 48, 48, 400, 520)
|
|
62
|
+
coins, score = [], 0
|
|
63
|
+
|
|
64
|
+
def update(g, dt):
|
|
65
|
+
global score
|
|
66
|
+
dx = int(g.key_held("right")) - int(g.key_held("left"))
|
|
67
|
+
player.x = pc.clamp(player.x + dx * 420 * dt, 24, 776)
|
|
68
|
+
if pc.chance(dt * 3):
|
|
69
|
+
coins.append(pc.Sprite.solid("gold", 22, 22, pc.rand(30, 770), -20))
|
|
70
|
+
for c in coins:
|
|
71
|
+
c.y += 260 * dt
|
|
72
|
+
if player.collides_with(c):
|
|
73
|
+
c.kill(); score += 1
|
|
74
|
+
coins[:] = [c for c in coins if c.alive and c.y < 640]
|
|
75
|
+
|
|
76
|
+
def draw(g):
|
|
77
|
+
g.clear("dark")
|
|
78
|
+
for c in coins: c.draw(g)
|
|
79
|
+
player.draw(g)
|
|
80
|
+
g.text(f"得分 {score}", 16, 14, size=28, color="white")
|
|
81
|
+
|
|
82
|
+
pc.run(update=update, draw=draw, title="接金币")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**内置演示**(安装后直接玩):
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
import pycoode
|
|
89
|
+
pycoode.demo()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## API 速查
|
|
93
|
+
|
|
94
|
+
### 入口
|
|
95
|
+
|
|
96
|
+
| 写法 | 作用 |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `pc.run()` | 弹出 800x600 窗口,ESC 退出 |
|
|
99
|
+
| `pc.run(update=f, draw=g)` | 带逻辑和绘制 |
|
|
100
|
+
| `pc.run(scene=Menu())` | 用场景管理 |
|
|
101
|
+
| `pc.run(width=960, height=640, title="...", fps=60, background="dark")` | 常用参数 |
|
|
102
|
+
| `pc.run(size="hd")` | 预设窗口大小:`small/md/large/square/hd/fullhd`,也可 `size=(1280,720)` |
|
|
103
|
+
| `pc.run(fit_screen=True)` | 窗口自动收缩到你的屏幕内(小笔记本不怕超出屏幕) |
|
|
104
|
+
| `pc.run(fullscreen=True, show_fps=True)` | 全屏 / 显示 FPS |
|
|
105
|
+
| `pc.run(mouse_visible=False)` | 隐藏鼠标指针 |
|
|
106
|
+
| `pc.run(icon="icon.png")` | 窗口图标 |
|
|
107
|
+
|
|
108
|
+
### 窗口控制(运行时)
|
|
109
|
+
|
|
110
|
+
| 写法 | 作用 |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `g.toggle_fullscreen()` | 全屏/窗口切换(快捷键 **F11** 已内置) |
|
|
113
|
+
| `g.set_window_size(1024, 768)` | 随时改窗口大小,游戏画面自动缩放适配,逻辑不用动 |
|
|
114
|
+
| `g.screen_size` | 当前真实窗口像素大小,如 `(1280, 720)` |
|
|
115
|
+
| `g.set_mouse_visible(False)` | 随时隐藏/显示鼠标 |
|
|
116
|
+
| `g.width, g.height` | 游戏设计分辨率(画布大小,永远不变) |
|
|
117
|
+
|
|
118
|
+
### 输入(都接受 "space"/"a"/"up" 等名字)
|
|
119
|
+
|
|
120
|
+
| 写法 | 作用 |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `g.key_held("right")` | 按住中 |
|
|
123
|
+
| `g.key_pressed("space")` | 本帧刚按下 |
|
|
124
|
+
| `g.key_released("esc")` | 本帧刚松开 |
|
|
125
|
+
| `g.mouse_x, g.mouse_y` / `g.mouse_pos` | 鼠标坐标(游戏内坐标) |
|
|
126
|
+
| `g.mouse_held(1)` / `g.mouse_pressed(1)` | 左键按住 / 刚按下 |
|
|
127
|
+
| `g.on_click = lambda x, y, button: ...` | 点击回调 |
|
|
128
|
+
| `g.touches` | 触屏触摸点列表 |
|
|
129
|
+
|
|
130
|
+
### 绘制
|
|
131
|
+
|
|
132
|
+
| 写法 | 作用 |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `g.clear("dark")` | 清屏 |
|
|
135
|
+
| `g.rect(x, y, w, h, "red", width=0, radius=8)` | 矩形(width=0 实心) |
|
|
136
|
+
| `g.border(x, y, w, h, "white", width=2)` | 描边矩形 |
|
|
137
|
+
| `g.circle(x, y, r, "orange")` | 圆 |
|
|
138
|
+
| `g.ellipse(x, y, w, h, "cyan")` | 椭圆 |
|
|
139
|
+
| `g.line(x1, y1, x2, y2, "gold", width=2)` | 线段 |
|
|
140
|
+
| `g.polygon([(x,y), ...], "grass")` | 多边形 |
|
|
141
|
+
| `g.arc(x, y, r, 0, 3.14)` | 圆弧 |
|
|
142
|
+
| `g.text("你好", x, y, size=24, color="white", center=True)` | 文字,返回矩形可做命中检测 |
|
|
143
|
+
| `g.image(img, x, y, angle=0, scale=1.0)` | 图片 |
|
|
144
|
+
|
|
145
|
+
颜色支持 `"red"`、`"#ff0000"`、`(255,0,0)`,另附赠 `dark/gold/orange/cyan/skyblue/grass/brick` 等好看的名字。
|
|
146
|
+
|
|
147
|
+
### 精灵
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
s = pc.sprite("hero.png", 400, 300) # 从图片加载
|
|
151
|
+
s = pc.Sprite.solid("red", 50, 50, 400, 300) # 纯色块
|
|
152
|
+
s = pc.Sprite("walk.png", frames=4, fps=10) # 逐帧动画
|
|
153
|
+
|
|
154
|
+
s.vx, s.vy = 200, -100 # 速度(像素/秒)
|
|
155
|
+
s.move(dt) # 按速度移动
|
|
156
|
+
s.animate(dt) # 播放动画
|
|
157
|
+
s.bounce_in(800, 600) # 撞墙反弹
|
|
158
|
+
s.collides_with(other) # 矩形碰撞
|
|
159
|
+
s.collides_circle(other) # 圆形碰撞
|
|
160
|
+
s.draw(g) # 画出来
|
|
161
|
+
s.kill() # 标记删除(配合 s.alive 过滤)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 音频
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
pc.play("hit.wav", volume=0.8) # 音效
|
|
168
|
+
pc.music("bgm.mp3") # 循环 BGM
|
|
169
|
+
pc.stop_music() / pc.pause_music() / pc.resume_music()
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 场景
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
class Menu(pc.Scene):
|
|
176
|
+
def draw(self, g):
|
|
177
|
+
g.text("按空格开始", 400, 300, size=36, center=True)
|
|
178
|
+
def update(self, g, dt):
|
|
179
|
+
if g.key_pressed("space"):
|
|
180
|
+
self.switch(Playing())
|
|
181
|
+
|
|
182
|
+
pc.run(scene=Menu)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`self.switch(s)` 换场景、`self.push(s)` 压栈(暂停界面)、`self.pop()` 返回。
|
|
186
|
+
|
|
187
|
+
### 小工具
|
|
188
|
+
|
|
189
|
+
`pc.clamp` `pc.lerp` `pc.dist` `pc.rand` `pc.randint` `pc.choice` `pc.chance` `pc.Vector`
|
|
190
|
+
|
|
191
|
+
## 示例
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
python examples/01_hello.py # 一行代码
|
|
195
|
+
python examples/02_bounce.py # 键盘 + 反弹
|
|
196
|
+
python examples/03_catch.py # 完整小游戏
|
|
197
|
+
python examples/04_scene.py # 场景管理
|
|
198
|
+
python examples/05_paint.py # 鼠标画板
|
|
199
|
+
python examples/06_anim.py # 逐帧动画
|
|
200
|
+
python examples/07_window.py # 窗口大小 / 全屏 / 鼠标控制
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## 它是怎么做到"零适配"的
|
|
204
|
+
|
|
205
|
+
1. **虚拟画布**:所有绘制先画在 800x600 的离屏画布上,再整体等比缩放贴到真实窗口(信箱式留边),所以窗口多大、玩家屏幕多稀奇,你的坐标永远对。
|
|
206
|
+
2. **高 DPI**:Windows 下自动声明 `per monitor` DPI 感知,笔记本 150% 缩放不再模糊错位。
|
|
207
|
+
3. **中文字体**:内置 Windows/macOS/Linux 三平台字体候选链,自动挑选第一个可用的。
|
|
208
|
+
4. **无声降级**:没有音频设备时音效函数静默跳过,游戏照常跑。
|
|
209
|
+
|
|
210
|
+
## 开发与测试
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pip install -e . # 可编辑安装
|
|
214
|
+
python tests/test_headless.py # 无头测试(SDL dummy 驱动,无需显示器)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## 路线图
|
|
218
|
+
|
|
219
|
+
- [ ] 瓦片地图 / 摄像机跟随
|
|
220
|
+
- [ ] 简单物理(重力、平台碰撞)
|
|
221
|
+
- [ ] 打包成 exe 的辅助命令
|
pycoode-1.0.0/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# pycoode
|
|
2
|
+
|
|
3
|
+
**让做游戏像写一行代码一样简单。**
|
|
4
|
+
|
|
5
|
+
`pycoode` 是基于 pygame 的极简 2D 游戏开发包。它替你扛下了所有麻烦事:
|
|
6
|
+
窗口创建、中文字体、高 DPI、分辨率适配、游戏循环、输入、精灵、音效、场景管理。
|
|
7
|
+
你只需要写游戏逻辑。
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
- **一行起跑** —— `pc.run()` 直接弹窗口;`pc.run(draw=...)` 就能画出东西
|
|
12
|
+
- **零适配问题** —— 游戏按设计分辨率(如 800x600)写坐标,窗口任意拉伸自动信箱缩放、不变形不错位;Windows 高 DPI(150% 缩放)自动适配
|
|
13
|
+
- **中文开箱即用** —— 自动匹配系统中文字体,`g.text("你好")` 不用准备字体文件
|
|
14
|
+
- **输入超简单** —— `g.key_held("space")`、`g.mouse_pressed(1)`,支持方向键/WASD/触屏
|
|
15
|
+
- **精灵系统** —— 移动、旋转、缩放、翻转、矩形/圆形碰撞、逐帧动画,`Sprite.solid("red", 50, 50)` 没有美术素材也能开工
|
|
16
|
+
- **音效一行播放** —— `pc.play("hit.wav")`、`pc.music("bgm.mp3")`,无声环境自动降级不崩溃
|
|
17
|
+
- **场景管理** —— 菜单 / 关卡 / 结算各自一个类,`self.switch(...)` 秒切换
|
|
18
|
+
- **跨平台** —— Windows / macOS / Linux,Python 3.8+
|
|
19
|
+
|
|
20
|
+
## 安装
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install pygame pycoode
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 30 秒上手
|
|
27
|
+
|
|
28
|
+
**第 1 行代码:**
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import pycoode as pc
|
|
32
|
+
|
|
33
|
+
pc.run(draw=lambda g: g.text("Hello, pycoode!", 400, 300, size=48,
|
|
34
|
+
color="gold", center=True))
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**一个能玩的小游戏(键盘控制 + 碰撞 + 计分):**
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import pycoode as pc
|
|
41
|
+
|
|
42
|
+
player = pc.Sprite.solid("cyan", 48, 48, 400, 520)
|
|
43
|
+
coins, score = [], 0
|
|
44
|
+
|
|
45
|
+
def update(g, dt):
|
|
46
|
+
global score
|
|
47
|
+
dx = int(g.key_held("right")) - int(g.key_held("left"))
|
|
48
|
+
player.x = pc.clamp(player.x + dx * 420 * dt, 24, 776)
|
|
49
|
+
if pc.chance(dt * 3):
|
|
50
|
+
coins.append(pc.Sprite.solid("gold", 22, 22, pc.rand(30, 770), -20))
|
|
51
|
+
for c in coins:
|
|
52
|
+
c.y += 260 * dt
|
|
53
|
+
if player.collides_with(c):
|
|
54
|
+
c.kill(); score += 1
|
|
55
|
+
coins[:] = [c for c in coins if c.alive and c.y < 640]
|
|
56
|
+
|
|
57
|
+
def draw(g):
|
|
58
|
+
g.clear("dark")
|
|
59
|
+
for c in coins: c.draw(g)
|
|
60
|
+
player.draw(g)
|
|
61
|
+
g.text(f"得分 {score}", 16, 14, size=28, color="white")
|
|
62
|
+
|
|
63
|
+
pc.run(update=update, draw=draw, title="接金币")
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**内置演示**(安装后直接玩):
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
import pycoode
|
|
70
|
+
pycoode.demo()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## API 速查
|
|
74
|
+
|
|
75
|
+
### 入口
|
|
76
|
+
|
|
77
|
+
| 写法 | 作用 |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `pc.run()` | 弹出 800x600 窗口,ESC 退出 |
|
|
80
|
+
| `pc.run(update=f, draw=g)` | 带逻辑和绘制 |
|
|
81
|
+
| `pc.run(scene=Menu())` | 用场景管理 |
|
|
82
|
+
| `pc.run(width=960, height=640, title="...", fps=60, background="dark")` | 常用参数 |
|
|
83
|
+
| `pc.run(size="hd")` | 预设窗口大小:`small/md/large/square/hd/fullhd`,也可 `size=(1280,720)` |
|
|
84
|
+
| `pc.run(fit_screen=True)` | 窗口自动收缩到你的屏幕内(小笔记本不怕超出屏幕) |
|
|
85
|
+
| `pc.run(fullscreen=True, show_fps=True)` | 全屏 / 显示 FPS |
|
|
86
|
+
| `pc.run(mouse_visible=False)` | 隐藏鼠标指针 |
|
|
87
|
+
| `pc.run(icon="icon.png")` | 窗口图标 |
|
|
88
|
+
|
|
89
|
+
### 窗口控制(运行时)
|
|
90
|
+
|
|
91
|
+
| 写法 | 作用 |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `g.toggle_fullscreen()` | 全屏/窗口切换(快捷键 **F11** 已内置) |
|
|
94
|
+
| `g.set_window_size(1024, 768)` | 随时改窗口大小,游戏画面自动缩放适配,逻辑不用动 |
|
|
95
|
+
| `g.screen_size` | 当前真实窗口像素大小,如 `(1280, 720)` |
|
|
96
|
+
| `g.set_mouse_visible(False)` | 随时隐藏/显示鼠标 |
|
|
97
|
+
| `g.width, g.height` | 游戏设计分辨率(画布大小,永远不变) |
|
|
98
|
+
|
|
99
|
+
### 输入(都接受 "space"/"a"/"up" 等名字)
|
|
100
|
+
|
|
101
|
+
| 写法 | 作用 |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `g.key_held("right")` | 按住中 |
|
|
104
|
+
| `g.key_pressed("space")` | 本帧刚按下 |
|
|
105
|
+
| `g.key_released("esc")` | 本帧刚松开 |
|
|
106
|
+
| `g.mouse_x, g.mouse_y` / `g.mouse_pos` | 鼠标坐标(游戏内坐标) |
|
|
107
|
+
| `g.mouse_held(1)` / `g.mouse_pressed(1)` | 左键按住 / 刚按下 |
|
|
108
|
+
| `g.on_click = lambda x, y, button: ...` | 点击回调 |
|
|
109
|
+
| `g.touches` | 触屏触摸点列表 |
|
|
110
|
+
|
|
111
|
+
### 绘制
|
|
112
|
+
|
|
113
|
+
| 写法 | 作用 |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `g.clear("dark")` | 清屏 |
|
|
116
|
+
| `g.rect(x, y, w, h, "red", width=0, radius=8)` | 矩形(width=0 实心) |
|
|
117
|
+
| `g.border(x, y, w, h, "white", width=2)` | 描边矩形 |
|
|
118
|
+
| `g.circle(x, y, r, "orange")` | 圆 |
|
|
119
|
+
| `g.ellipse(x, y, w, h, "cyan")` | 椭圆 |
|
|
120
|
+
| `g.line(x1, y1, x2, y2, "gold", width=2)` | 线段 |
|
|
121
|
+
| `g.polygon([(x,y), ...], "grass")` | 多边形 |
|
|
122
|
+
| `g.arc(x, y, r, 0, 3.14)` | 圆弧 |
|
|
123
|
+
| `g.text("你好", x, y, size=24, color="white", center=True)` | 文字,返回矩形可做命中检测 |
|
|
124
|
+
| `g.image(img, x, y, angle=0, scale=1.0)` | 图片 |
|
|
125
|
+
|
|
126
|
+
颜色支持 `"red"`、`"#ff0000"`、`(255,0,0)`,另附赠 `dark/gold/orange/cyan/skyblue/grass/brick` 等好看的名字。
|
|
127
|
+
|
|
128
|
+
### 精灵
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
s = pc.sprite("hero.png", 400, 300) # 从图片加载
|
|
132
|
+
s = pc.Sprite.solid("red", 50, 50, 400, 300) # 纯色块
|
|
133
|
+
s = pc.Sprite("walk.png", frames=4, fps=10) # 逐帧动画
|
|
134
|
+
|
|
135
|
+
s.vx, s.vy = 200, -100 # 速度(像素/秒)
|
|
136
|
+
s.move(dt) # 按速度移动
|
|
137
|
+
s.animate(dt) # 播放动画
|
|
138
|
+
s.bounce_in(800, 600) # 撞墙反弹
|
|
139
|
+
s.collides_with(other) # 矩形碰撞
|
|
140
|
+
s.collides_circle(other) # 圆形碰撞
|
|
141
|
+
s.draw(g) # 画出来
|
|
142
|
+
s.kill() # 标记删除(配合 s.alive 过滤)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 音频
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
pc.play("hit.wav", volume=0.8) # 音效
|
|
149
|
+
pc.music("bgm.mp3") # 循环 BGM
|
|
150
|
+
pc.stop_music() / pc.pause_music() / pc.resume_music()
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 场景
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
class Menu(pc.Scene):
|
|
157
|
+
def draw(self, g):
|
|
158
|
+
g.text("按空格开始", 400, 300, size=36, center=True)
|
|
159
|
+
def update(self, g, dt):
|
|
160
|
+
if g.key_pressed("space"):
|
|
161
|
+
self.switch(Playing())
|
|
162
|
+
|
|
163
|
+
pc.run(scene=Menu)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`self.switch(s)` 换场景、`self.push(s)` 压栈(暂停界面)、`self.pop()` 返回。
|
|
167
|
+
|
|
168
|
+
### 小工具
|
|
169
|
+
|
|
170
|
+
`pc.clamp` `pc.lerp` `pc.dist` `pc.rand` `pc.randint` `pc.choice` `pc.chance` `pc.Vector`
|
|
171
|
+
|
|
172
|
+
## 示例
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
python examples/01_hello.py # 一行代码
|
|
176
|
+
python examples/02_bounce.py # 键盘 + 反弹
|
|
177
|
+
python examples/03_catch.py # 完整小游戏
|
|
178
|
+
python examples/04_scene.py # 场景管理
|
|
179
|
+
python examples/05_paint.py # 鼠标画板
|
|
180
|
+
python examples/06_anim.py # 逐帧动画
|
|
181
|
+
python examples/07_window.py # 窗口大小 / 全屏 / 鼠标控制
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 它是怎么做到"零适配"的
|
|
185
|
+
|
|
186
|
+
1. **虚拟画布**:所有绘制先画在 800x600 的离屏画布上,再整体等比缩放贴到真实窗口(信箱式留边),所以窗口多大、玩家屏幕多稀奇,你的坐标永远对。
|
|
187
|
+
2. **高 DPI**:Windows 下自动声明 `per monitor` DPI 感知,笔记本 150% 缩放不再模糊错位。
|
|
188
|
+
3. **中文字体**:内置 Windows/macOS/Linux 三平台字体候选链,自动挑选第一个可用的。
|
|
189
|
+
4. **无声降级**:没有音频设备时音效函数静默跳过,游戏照常跑。
|
|
190
|
+
|
|
191
|
+
## 开发与测试
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
pip install -e . # 可编辑安装
|
|
195
|
+
python tests/test_headless.py # 无头测试(SDL dummy 驱动,无需显示器)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## 路线图
|
|
199
|
+
|
|
200
|
+
- [ ] 瓦片地图 / 摄像机跟随
|
|
201
|
+
- [ ] 简单物理(重力、平台碰撞)
|
|
202
|
+
- [ ] 打包成 exe 的辅助命令
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""pycoode —— 让做游戏像写一行代码一样简单
|
|
2
|
+
|
|
3
|
+
基于 pygame 封装: 自动处理窗口创建、中文字体、高 DPI、
|
|
4
|
+
分辨率适配、游戏循环、输入、精灵、音效和场景。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .core import Game, run, keycode
|
|
8
|
+
from .sprites import Sprite, load_image
|
|
9
|
+
from .scene import Scene
|
|
10
|
+
from .audio import (
|
|
11
|
+
play, music, stop_music, pause_music, resume_music,
|
|
12
|
+
set_music_volume, is_music_playing,
|
|
13
|
+
)
|
|
14
|
+
from .utils import (
|
|
15
|
+
Vector, to_color, clamp, lerp, dist, angle,
|
|
16
|
+
rand, randint, choice, chance,
|
|
17
|
+
)
|
|
18
|
+
from .demo import demo
|
|
19
|
+
|
|
20
|
+
__version__ = "1.0.0"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def sprite(path, x=0, y=0, **kw):
|
|
24
|
+
"""加载精灵: pc.sprite("hero.png", 400, 300)"""
|
|
25
|
+
return Sprite.load(path, x, y, **kw)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Game", "run", "demo", "keycode",
|
|
30
|
+
"Sprite", "sprite", "load_image",
|
|
31
|
+
"Scene",
|
|
32
|
+
"play", "music", "stop_music", "pause_music", "resume_music",
|
|
33
|
+
"set_music_volume", "is_music_playing",
|
|
34
|
+
"Vector", "to_color", "clamp", "lerp", "dist", "angle",
|
|
35
|
+
"rand", "randint", "choice", "chance",
|
|
36
|
+
"__version__",
|
|
37
|
+
]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""pycoode.audio —— 音效与背景音乐,一行播放
|
|
2
|
+
|
|
3
|
+
没有音频设备时所有函数静默降级,不会让游戏崩掉。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import pygame
|
|
7
|
+
|
|
8
|
+
_sound_cache = {}
|
|
9
|
+
_mixer_ok = True
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _ensure_mixer():
|
|
13
|
+
global _mixer_ok
|
|
14
|
+
if not _mixer_ok:
|
|
15
|
+
return False
|
|
16
|
+
try:
|
|
17
|
+
if not pygame.mixer.get_init():
|
|
18
|
+
pygame.mixer.init()
|
|
19
|
+
return True
|
|
20
|
+
except Exception:
|
|
21
|
+
_mixer_ok = False
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def play(path, volume=1.0):
|
|
26
|
+
"""播放音效(可重复调用,自动缓存)"""
|
|
27
|
+
if not _ensure_mixer():
|
|
28
|
+
return None
|
|
29
|
+
key = str(path)
|
|
30
|
+
if key not in _sound_cache:
|
|
31
|
+
_sound_cache[key] = pygame.mixer.Sound(key)
|
|
32
|
+
s = _sound_cache[key]
|
|
33
|
+
s.set_volume(max(0.0, min(1.0, volume)))
|
|
34
|
+
s.play()
|
|
35
|
+
return s
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def music(path, loop=True, volume=0.6, fade_ms=0):
|
|
39
|
+
"""播放背景音乐(默认循环)"""
|
|
40
|
+
if not _ensure_mixer():
|
|
41
|
+
return
|
|
42
|
+
pygame.mixer.music.load(str(path))
|
|
43
|
+
pygame.mixer.music.set_volume(max(0.0, min(1.0, volume)))
|
|
44
|
+
pygame.mixer.music.play(-1 if loop else 0, fade_ms=fade_ms)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def stop_music(fade_ms=0):
|
|
48
|
+
if not _ensure_mixer():
|
|
49
|
+
return
|
|
50
|
+
pygame.mixer.music.fadeout(fade_ms) if fade_ms else pygame.mixer.music.stop()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def pause_music():
|
|
54
|
+
if _ensure_mixer():
|
|
55
|
+
pygame.mixer.music.pause()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def resume_music():
|
|
59
|
+
if _ensure_mixer():
|
|
60
|
+
pygame.mixer.music.unpause()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def set_music_volume(v):
|
|
64
|
+
if _ensure_mixer():
|
|
65
|
+
pygame.mixer.music.set_volume(max(0.0, min(1.0, v)))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def is_music_playing():
|
|
69
|
+
return _ensure_mixer() and pygame.mixer.music.get_busy()
|