pyscrh 0.1.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.
- pyscrh-0.1.0/LICENSE +21 -0
- pyscrh-0.1.0/PKG-INFO +135 -0
- pyscrh-0.1.0/README.md +93 -0
- pyscrh-0.1.0/pyproject.toml +30 -0
- pyscrh-0.1.0/pyscrh/__init__.py +322 -0
- pyscrh-0.1.0/pyscrh.egg-info/PKG-INFO +135 -0
- pyscrh-0.1.0/pyscrh.egg-info/SOURCES.txt +9 -0
- pyscrh-0.1.0/pyscrh.egg-info/dependency_links.txt +1 -0
- pyscrh-0.1.0/pyscrh.egg-info/requires.txt +1 -0
- pyscrh-0.1.0/pyscrh.egg-info/top_level.txt +1 -0
- pyscrh-0.1.0/setup.cfg +4 -0
pyscrh-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 魔丸编程
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pyscrh-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyscrh
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A tiny Scratch-style sprite library built on pygame, for people moving from Scratch to Python.
|
|
5
|
+
Author-email: 魔丸编程 <17516332069@163.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 魔丸编程
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/CutePandaXJK/pyscrh
|
|
29
|
+
Project-URL: Repository, https://github.com/CutePandaXJK/pyscrh
|
|
30
|
+
Keywords: scratch,pygame,game,sprite,education,beginner
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Operating System :: OS Independent
|
|
34
|
+
Classifier: Topic :: Games/Entertainment
|
|
35
|
+
Classifier: Topic :: Education
|
|
36
|
+
Classifier: Intended Audience :: Education
|
|
37
|
+
Requires-Python: >=3.8
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
License-File: LICENSE
|
|
40
|
+
Requires-Dist: pygame>=2.0
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# pyscrh
|
|
44
|
+
|
|
45
|
+
A tiny, **Scratch-style** sprite library built on [pygame](https://www.pygame.org/),
|
|
46
|
+
made for people moving from Scratch to Python.
|
|
47
|
+
|
|
48
|
+
Every Scratch block you know has a short Python equivalent — no game loop,
|
|
49
|
+
no event handling, no surface juggling to remember.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install pyscrh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
(Requires Python 3.8+ and pygame, which is installed automatically.)
|
|
58
|
+
|
|
59
|
+
## Quick start
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import pyscrh
|
|
63
|
+
|
|
64
|
+
apple = Sprite("apple.png") # load an image (PNG with transparency)
|
|
65
|
+
apple.turnway = "lr" # flip left-right when bouncing
|
|
66
|
+
|
|
67
|
+
def forever():
|
|
68
|
+
apple.move(5) # walk 5 steps in the current direction
|
|
69
|
+
apple.edgeb() # bounce off the screen edge
|
|
70
|
+
# stop_all() # call this anywhere to quit the game
|
|
71
|
+
|
|
72
|
+
clickGF() # start the game
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Just `import pyscrh` and you're done — `Sprite`, `clickGF`, `stop_all`,
|
|
76
|
+
`WIDTH` and `HEIGHT` are available directly, just like `import pgzrun`.
|
|
77
|
+
|
|
78
|
+
That's it. The apple walks right, bounces off the right edge, walks left,
|
|
79
|
+
bounces off the left edge, and repeats.
|
|
80
|
+
|
|
81
|
+
## Scratch → Python cheat sheet
|
|
82
|
+
|
|
83
|
+
| Scratch block | pyscrh |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| move 10 steps | `sprite.move(10)` |
|
|
86
|
+
| turn 15° clockwise | `sprite.turnright(15)` |
|
|
87
|
+
| turn 15° counter-clockwise | `sprite.turnleft(15)` |
|
|
88
|
+
| go to x: y: | `sprite.gotoxy(x, y)` |
|
|
89
|
+
| go to random position | `sprite.goto("r")` |
|
|
90
|
+
| glide 1 sec to x: y: | `sprite.glidexy(1, x, y)` |
|
|
91
|
+
| point in direction 90 | `sprite.point(90)` |
|
|
92
|
+
| point towards mouse | `sprite.pointtow("m")` |
|
|
93
|
+
| if on edge, bounce | `sprite.edgeb()` |
|
|
94
|
+
|
|
95
|
+
## How it works
|
|
96
|
+
|
|
97
|
+
You only write one required function:
|
|
98
|
+
|
|
99
|
+
- `forever()` — called once every frame, like Scratch's *forever* block.
|
|
100
|
+
Put `sprite.move(...)`, `edgeb()`, etc. in here. No `dt` parameter
|
|
101
|
+
needed; the library handles timing.
|
|
102
|
+
|
|
103
|
+
An optional `draw()` is also called every frame, *after* all sprites are
|
|
104
|
+
drawn, if you want extra drawing (text, backgrounds, etc.).
|
|
105
|
+
|
|
106
|
+
Call `clickGF()` to start, and `stop_all()` to close the window and quit.
|
|
107
|
+
pyscrh handles the window, clock, event loop and rendering for you.
|
|
108
|
+
|
|
109
|
+
## API reference
|
|
110
|
+
|
|
111
|
+
### `Sprite(image_path, name="")`
|
|
112
|
+
|
|
113
|
+
Create a sprite from an image file.
|
|
114
|
+
|
|
115
|
+
| Method | Description |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| `move(steps)` | Walk `steps` pixels in the current direction. |
|
|
118
|
+
| `turnright(deg)` / `turnleft(deg)` | Rotate by `deg` degrees. |
|
|
119
|
+
| `gotoxy(x, y)` | Jump to `(x, y)`. |
|
|
120
|
+
| `goto("r")` / `goto("m")` | Jump to a random spot / the mouse. |
|
|
121
|
+
| `glidexy(sec, x, y)` | Glide to `(x, y)` over `sec` seconds. |
|
|
122
|
+
| `glideto(sec, "r")` / `glideto(sec, "m")` | Glide to a spot / the mouse. |
|
|
123
|
+
| `point(deg)` | Set direction to `deg` degrees. |
|
|
124
|
+
| `pointtow("r")` / `pointtow("m")` | Face a random direction / the mouse. |
|
|
125
|
+
| `edgeb()` | Bounce off the screen edge. |
|
|
126
|
+
|
|
127
|
+
| Attribute | Description |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `x`, `y` | Center position (read or write). |
|
|
130
|
+
| `angle` | Direction in degrees (0 = right, 90 = down). |
|
|
131
|
+
| `turnway` | `"r"` rotate-bounce, `"lr"` flip-bounce, `"n"` no bounce. |
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
pyscrh-0.1.0/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# pyscrh
|
|
2
|
+
|
|
3
|
+
A tiny, **Scratch-style** sprite library built on [pygame](https://www.pygame.org/),
|
|
4
|
+
made for people moving from Scratch to Python.
|
|
5
|
+
|
|
6
|
+
Every Scratch block you know has a short Python equivalent — no game loop,
|
|
7
|
+
no event handling, no surface juggling to remember.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install pyscrh
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
(Requires Python 3.8+ and pygame, which is installed automatically.)
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import pyscrh
|
|
21
|
+
|
|
22
|
+
apple = Sprite("apple.png") # load an image (PNG with transparency)
|
|
23
|
+
apple.turnway = "lr" # flip left-right when bouncing
|
|
24
|
+
|
|
25
|
+
def forever():
|
|
26
|
+
apple.move(5) # walk 5 steps in the current direction
|
|
27
|
+
apple.edgeb() # bounce off the screen edge
|
|
28
|
+
# stop_all() # call this anywhere to quit the game
|
|
29
|
+
|
|
30
|
+
clickGF() # start the game
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Just `import pyscrh` and you're done — `Sprite`, `clickGF`, `stop_all`,
|
|
34
|
+
`WIDTH` and `HEIGHT` are available directly, just like `import pgzrun`.
|
|
35
|
+
|
|
36
|
+
That's it. The apple walks right, bounces off the right edge, walks left,
|
|
37
|
+
bounces off the left edge, and repeats.
|
|
38
|
+
|
|
39
|
+
## Scratch → Python cheat sheet
|
|
40
|
+
|
|
41
|
+
| Scratch block | pyscrh |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| move 10 steps | `sprite.move(10)` |
|
|
44
|
+
| turn 15° clockwise | `sprite.turnright(15)` |
|
|
45
|
+
| turn 15° counter-clockwise | `sprite.turnleft(15)` |
|
|
46
|
+
| go to x: y: | `sprite.gotoxy(x, y)` |
|
|
47
|
+
| go to random position | `sprite.goto("r")` |
|
|
48
|
+
| glide 1 sec to x: y: | `sprite.glidexy(1, x, y)` |
|
|
49
|
+
| point in direction 90 | `sprite.point(90)` |
|
|
50
|
+
| point towards mouse | `sprite.pointtow("m")` |
|
|
51
|
+
| if on edge, bounce | `sprite.edgeb()` |
|
|
52
|
+
|
|
53
|
+
## How it works
|
|
54
|
+
|
|
55
|
+
You only write one required function:
|
|
56
|
+
|
|
57
|
+
- `forever()` — called once every frame, like Scratch's *forever* block.
|
|
58
|
+
Put `sprite.move(...)`, `edgeb()`, etc. in here. No `dt` parameter
|
|
59
|
+
needed; the library handles timing.
|
|
60
|
+
|
|
61
|
+
An optional `draw()` is also called every frame, *after* all sprites are
|
|
62
|
+
drawn, if you want extra drawing (text, backgrounds, etc.).
|
|
63
|
+
|
|
64
|
+
Call `clickGF()` to start, and `stop_all()` to close the window and quit.
|
|
65
|
+
pyscrh handles the window, clock, event loop and rendering for you.
|
|
66
|
+
|
|
67
|
+
## API reference
|
|
68
|
+
|
|
69
|
+
### `Sprite(image_path, name="")`
|
|
70
|
+
|
|
71
|
+
Create a sprite from an image file.
|
|
72
|
+
|
|
73
|
+
| Method | Description |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `move(steps)` | Walk `steps` pixels in the current direction. |
|
|
76
|
+
| `turnright(deg)` / `turnleft(deg)` | Rotate by `deg` degrees. |
|
|
77
|
+
| `gotoxy(x, y)` | Jump to `(x, y)`. |
|
|
78
|
+
| `goto("r")` / `goto("m")` | Jump to a random spot / the mouse. |
|
|
79
|
+
| `glidexy(sec, x, y)` | Glide to `(x, y)` over `sec` seconds. |
|
|
80
|
+
| `glideto(sec, "r")` / `glideto(sec, "m")` | Glide to a spot / the mouse. |
|
|
81
|
+
| `point(deg)` | Set direction to `deg` degrees. |
|
|
82
|
+
| `pointtow("r")` / `pointtow("m")` | Face a random direction / the mouse. |
|
|
83
|
+
| `edgeb()` | Bounce off the screen edge. |
|
|
84
|
+
|
|
85
|
+
| Attribute | Description |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `x`, `y` | Center position (read or write). |
|
|
88
|
+
| `angle` | Direction in degrees (0 = right, 90 = down). |
|
|
89
|
+
| `turnway` | `"r"` rotate-bounce, `"lr"` flip-bounce, `"n"` no bounce. |
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyscrh"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A tiny Scratch-style sprite library built on pygame, for people moving from Scratch to Python."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "魔丸编程", email = "17516332069@163.com" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["scratch", "pygame", "game", "sprite", "education", "beginner"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Topic :: Games/Entertainment",
|
|
21
|
+
"Topic :: Education",
|
|
22
|
+
"Intended Audience :: Education",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"pygame>=2.0"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/CutePandaXJK/pyscrh"
|
|
30
|
+
Repository = "https://github.com/CutePandaXJK/pyscrh"
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pyscrh
|
|
3
|
+
======
|
|
4
|
+
|
|
5
|
+
A tiny, **Scratch-style** sprite library built on top of pygame, made for
|
|
6
|
+
people moving from Scratch to Python.
|
|
7
|
+
|
|
8
|
+
Every Scratch block you know has a short Python equivalent::
|
|
9
|
+
|
|
10
|
+
from pyscrh import Sprite, clickGF
|
|
11
|
+
|
|
12
|
+
apple = Sprite("apple.png")
|
|
13
|
+
apple.turnway = "lr"
|
|
14
|
+
|
|
15
|
+
def forever():
|
|
16
|
+
apple.move(5) # move 5 steps in the current direction
|
|
17
|
+
apple.edgeb() # bounce off the edge
|
|
18
|
+
|
|
19
|
+
clickGF() # start the game
|
|
20
|
+
|
|
21
|
+
The public API is intentionally small: create a :class:`Sprite`, put what
|
|
22
|
+
should repeat forever in a ``forever()`` function, and call :func:`clickGF`
|
|
23
|
+
to start. You never need to touch ``dt`` or the game loop. Call
|
|
24
|
+
:func:`stop_all` at any point to close the window and quit.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import pygame
|
|
28
|
+
import math
|
|
29
|
+
import random
|
|
30
|
+
import inspect
|
|
31
|
+
import sys
|
|
32
|
+
|
|
33
|
+
__version__ = "0.1.0"
|
|
34
|
+
|
|
35
|
+
# --- pygame / window setup (runs on first import) -----------------------
|
|
36
|
+
pygame.init()
|
|
37
|
+
|
|
38
|
+
WIDTH = 960
|
|
39
|
+
HEIGHT = 720
|
|
40
|
+
_screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
|
41
|
+
pygame.display.set_caption("pyscrh")
|
|
42
|
+
_clock = pygame.time.Clock()
|
|
43
|
+
|
|
44
|
+
# All live sprites. Internal; users never touch this.
|
|
45
|
+
_sprites = []
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Sprite:
|
|
49
|
+
"""A single game character, loaded from an image file.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
image_path : str
|
|
54
|
+
Full path to the sprite image (PNG with transparency works best).
|
|
55
|
+
name : str, optional
|
|
56
|
+
A friendly name. Not required, but handy when you have many sprites.
|
|
57
|
+
|
|
58
|
+
Attributes
|
|
59
|
+
----------
|
|
60
|
+
x, y : float
|
|
61
|
+
Center position of the sprite (read or write directly).
|
|
62
|
+
angle : float
|
|
63
|
+
Direction in degrees: ``0`` points right, ``90`` points down.
|
|
64
|
+
turnway : str
|
|
65
|
+
How the sprite behaves at the screen edge:
|
|
66
|
+
``"r"`` rotates to bounce, ``"lr"`` flips left-right,
|
|
67
|
+
``"n"`` does not bounce at all.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, image_path, name=""):
|
|
71
|
+
self.original = pygame.image.load(image_path).convert_alpha()
|
|
72
|
+
self.width = self.original.get_width()
|
|
73
|
+
self.height = self.original.get_height()
|
|
74
|
+
|
|
75
|
+
# Center position
|
|
76
|
+
self.x = self.width / 2
|
|
77
|
+
self.y = self.height / 2
|
|
78
|
+
|
|
79
|
+
# Movement state
|
|
80
|
+
self.angle = 0
|
|
81
|
+
self.direction_x = 1 # flipped by edgeb on left/right hits
|
|
82
|
+
self.direction_y = 1 # flipped by edgeb on top/bottom hits
|
|
83
|
+
self.flip_x = False # visual horizontal flip
|
|
84
|
+
self.flip_y = False # visual vertical flip
|
|
85
|
+
self.turnway = "r"
|
|
86
|
+
self.name = name
|
|
87
|
+
|
|
88
|
+
# Gliding state (internal, driven by glidexy / glideto)
|
|
89
|
+
self.glide_target_x = 0
|
|
90
|
+
self.glide_target_y = 0
|
|
91
|
+
self.glide_delta_x = 0
|
|
92
|
+
self.glide_delta_y = 0
|
|
93
|
+
self.glide_distance = 0.0
|
|
94
|
+
self.glide_speed = 0.0
|
|
95
|
+
self.gliding = False
|
|
96
|
+
|
|
97
|
+
_sprites.append(self)
|
|
98
|
+
|
|
99
|
+
# ------------------------------------------------------------------
|
|
100
|
+
# Movement
|
|
101
|
+
# ------------------------------------------------------------------
|
|
102
|
+
def move(self, steps):
|
|
103
|
+
"""Walk ``steps`` pixels in the current direction.
|
|
104
|
+
|
|
105
|
+
Call this once per frame inside your ``update(dt)`` and the sprite
|
|
106
|
+
keeps walking. Equivalent to Scratch's *move 10 steps*.
|
|
107
|
+
"""
|
|
108
|
+
rad = math.radians(self.angle)
|
|
109
|
+
self.x += self.direction_x * steps * math.cos(rad)
|
|
110
|
+
self.y += self.direction_y * steps * math.sin(rad)
|
|
111
|
+
|
|
112
|
+
def turnright(self, degrees):
|
|
113
|
+
"""Turn right by ``degrees``."""
|
|
114
|
+
self.angle += degrees
|
|
115
|
+
|
|
116
|
+
def turnleft(self, degrees):
|
|
117
|
+
"""Turn left by ``degrees``."""
|
|
118
|
+
self.angle -= degrees
|
|
119
|
+
|
|
120
|
+
def gotoxy(self, x, y):
|
|
121
|
+
"""Jump instantly to position ``(x, y)``."""
|
|
122
|
+
self.x = x
|
|
123
|
+
self.y = y
|
|
124
|
+
|
|
125
|
+
def goto(self, target):
|
|
126
|
+
"""Jump instantly to a target.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
target : str
|
|
131
|
+
``"r"`` for a random spot, ``"m"`` for the mouse position.
|
|
132
|
+
"""
|
|
133
|
+
if target == "r":
|
|
134
|
+
self.gotoxy(random.randint(0, WIDTH), random.randint(0, HEIGHT))
|
|
135
|
+
elif target == "m":
|
|
136
|
+
self.gotoxy(*pygame.mouse.get_pos())
|
|
137
|
+
|
|
138
|
+
# ------------------------------------------------------------------
|
|
139
|
+
# Gliding
|
|
140
|
+
# ------------------------------------------------------------------
|
|
141
|
+
def glidexy(self, seconds, x, y):
|
|
142
|
+
"""Glide smoothly to ``(x, y)`` over ``seconds`` seconds."""
|
|
143
|
+
if seconds <= 0:
|
|
144
|
+
return
|
|
145
|
+
distance = math.hypot(x - self.x, y - self.y)
|
|
146
|
+
if distance < 1e-9:
|
|
147
|
+
self.gliding = False
|
|
148
|
+
return
|
|
149
|
+
self.glide_target_x = x
|
|
150
|
+
self.glide_target_y = y
|
|
151
|
+
self.glide_delta_x = x - self.x
|
|
152
|
+
self.glide_delta_y = y - self.y
|
|
153
|
+
self.glide_distance = distance
|
|
154
|
+
self.glide_speed = distance / seconds
|
|
155
|
+
self.gliding = True
|
|
156
|
+
|
|
157
|
+
def glideto(self, seconds, target):
|
|
158
|
+
"""Glide smoothly over ``seconds`` seconds.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
seconds : float
|
|
163
|
+
How long the glide takes.
|
|
164
|
+
target : str
|
|
165
|
+
``"r"`` for a random spot, ``"m"`` for the mouse position.
|
|
166
|
+
"""
|
|
167
|
+
if target == "r":
|
|
168
|
+
self.glidexy(seconds,
|
|
169
|
+
random.randint(0, WIDTH),
|
|
170
|
+
random.randint(0, HEIGHT))
|
|
171
|
+
elif target == "m":
|
|
172
|
+
self.glidexy(seconds, *pygame.mouse.get_pos())
|
|
173
|
+
|
|
174
|
+
# ------------------------------------------------------------------
|
|
175
|
+
# Direction
|
|
176
|
+
# ------------------------------------------------------------------
|
|
177
|
+
def point(self, degrees):
|
|
178
|
+
"""Set the direction to ``degrees`` (0 = right, 90 = down)."""
|
|
179
|
+
self.angle = degrees
|
|
180
|
+
|
|
181
|
+
def pointtow(self, target):
|
|
182
|
+
"""Point towards something.
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
target : str
|
|
187
|
+
``"r"`` for a random direction, ``"m"`` to face the mouse.
|
|
188
|
+
"""
|
|
189
|
+
if target == "r":
|
|
190
|
+
self.angle = random.randint(0, 360)
|
|
191
|
+
elif target == "m":
|
|
192
|
+
mouse_x, mouse_y = pygame.mouse.get_pos()
|
|
193
|
+
self.angle = math.degrees(math.atan2(
|
|
194
|
+
mouse_y - self.y, mouse_x - self.x))
|
|
195
|
+
|
|
196
|
+
# ------------------------------------------------------------------
|
|
197
|
+
# Edge bouncing
|
|
198
|
+
# ------------------------------------------------------------------
|
|
199
|
+
def edgeb(self):
|
|
200
|
+
"""Bounce off the screen edge.
|
|
201
|
+
|
|
202
|
+
Equivalent to Scratch's *if on edge, bounce*. The horizontal /
|
|
203
|
+
vertical direction flips independently, and the sprite is visually
|
|
204
|
+
flipped according to :attr:`turnway`.
|
|
205
|
+
"""
|
|
206
|
+
left = self.x - self.width / 2
|
|
207
|
+
right = self.x + self.width / 2
|
|
208
|
+
top = self.y - self.height / 2
|
|
209
|
+
bottom = self.y + self.height / 2
|
|
210
|
+
|
|
211
|
+
if self.turnway == "n":
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
if right > WIDTH:
|
|
215
|
+
self.x = WIDTH - self.width / 2
|
|
216
|
+
self.direction_x = -self.direction_x
|
|
217
|
+
self.flip_x = not self.flip_x
|
|
218
|
+
elif left < 0:
|
|
219
|
+
self.x = self.width / 2
|
|
220
|
+
self.direction_x = -self.direction_x
|
|
221
|
+
self.flip_x = not self.flip_x
|
|
222
|
+
|
|
223
|
+
if bottom > HEIGHT:
|
|
224
|
+
self.y = HEIGHT - self.height / 2
|
|
225
|
+
self.direction_y = -self.direction_y
|
|
226
|
+
if self.turnway == "r":
|
|
227
|
+
self.flip_y = not self.flip_y
|
|
228
|
+
elif top < 0:
|
|
229
|
+
self.y = self.height / 2
|
|
230
|
+
self.direction_y = -self.direction_y
|
|
231
|
+
if self.turnway == "r":
|
|
232
|
+
self.flip_y = not self.flip_y
|
|
233
|
+
|
|
234
|
+
# ------------------------------------------------------------------
|
|
235
|
+
# Internal per-frame hooks (called by clickGF, not by you)
|
|
236
|
+
# ------------------------------------------------------------------
|
|
237
|
+
def _update(self, dt):
|
|
238
|
+
"""Advance an in-progress glide. Regular movement is done by move()."""
|
|
239
|
+
if not self.gliding:
|
|
240
|
+
return
|
|
241
|
+
remaining = math.hypot(self.glide_target_x - self.x,
|
|
242
|
+
self.glide_target_y - self.y)
|
|
243
|
+
step = self.glide_speed * dt
|
|
244
|
+
if remaining <= step:
|
|
245
|
+
self.x = self.glide_target_x
|
|
246
|
+
self.y = self.glide_target_y
|
|
247
|
+
self.gliding = False
|
|
248
|
+
else:
|
|
249
|
+
self.x += self.glide_delta_x / self.glide_distance * step
|
|
250
|
+
self.y += self.glide_delta_y / self.glide_distance * step
|
|
251
|
+
|
|
252
|
+
def _draw(self, surface):
|
|
253
|
+
"""Render this sprite onto ``surface``."""
|
|
254
|
+
image = self.original
|
|
255
|
+
if self.turnway == "r":
|
|
256
|
+
image = pygame.transform.rotate(image, self.angle)
|
|
257
|
+
if self.flip_x:
|
|
258
|
+
image = pygame.transform.flip(image, True, False)
|
|
259
|
+
if self.flip_y:
|
|
260
|
+
image = pygame.transform.flip(image, False, True)
|
|
261
|
+
rect = image.get_rect(center=(self.x, self.y))
|
|
262
|
+
surface.blit(image, rect)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def clickGF():
|
|
266
|
+
"""Start the game loop.
|
|
267
|
+
|
|
268
|
+
It automatically looks up ``forever()`` in the module that called it and
|
|
269
|
+
calls it once every frame, like Scratch's *forever* block. You don't
|
|
270
|
+
need to know about ``dt`` — the library handles timing internally.
|
|
271
|
+
|
|
272
|
+
An optional ``draw()`` is also called every frame, *after* all sprites
|
|
273
|
+
are drawn, if you want extra drawing. Close the window to quit, or call
|
|
274
|
+
:func:`stop_all` to quit from code.
|
|
275
|
+
"""
|
|
276
|
+
frame = inspect.currentframe()
|
|
277
|
+
caller_globals = frame.f_back.f_globals
|
|
278
|
+
del frame
|
|
279
|
+
user_forever = caller_globals.get("forever")
|
|
280
|
+
user_draw = caller_globals.get("draw")
|
|
281
|
+
|
|
282
|
+
running = True
|
|
283
|
+
while running:
|
|
284
|
+
dt = _clock.tick(60) / 1000 # seconds since last frame (internal use)
|
|
285
|
+
|
|
286
|
+
for event in pygame.event.get():
|
|
287
|
+
if event.type == pygame.QUIT:
|
|
288
|
+
running = False
|
|
289
|
+
|
|
290
|
+
if user_forever is not None:
|
|
291
|
+
user_forever()
|
|
292
|
+
for sprite in _sprites:
|
|
293
|
+
sprite._update(dt)
|
|
294
|
+
|
|
295
|
+
_screen.fill((0, 0, 0))
|
|
296
|
+
for sprite in _sprites:
|
|
297
|
+
sprite._draw(_screen)
|
|
298
|
+
if user_draw is not None:
|
|
299
|
+
user_draw()
|
|
300
|
+
pygame.display.flip()
|
|
301
|
+
|
|
302
|
+
pygame.quit()
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def stop_all():
|
|
306
|
+
"""Close the window and stop the program.
|
|
307
|
+
|
|
308
|
+
Equivalent to Scratch's *stop all* block. Call it from anywhere, e.g.
|
|
309
|
+
inside ``forever()`` when your game logic says the program should end.
|
|
310
|
+
"""
|
|
311
|
+
pygame.quit()
|
|
312
|
+
sys.exit()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
# 魔法:让用户只写 "import pyscrh" 就能直接用 Sprite / clickGF / stop_all,
|
|
316
|
+
# 不用再写 "from pyscrh import *"(和 pgzrun 的体验一致)。
|
|
317
|
+
import __main__
|
|
318
|
+
__main__.Sprite = Sprite
|
|
319
|
+
__main__.clickGF = clickGF
|
|
320
|
+
__main__.stop_all = stop_all
|
|
321
|
+
__main__.WIDTH = WIDTH
|
|
322
|
+
__main__.HEIGHT = HEIGHT
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyscrh
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A tiny Scratch-style sprite library built on pygame, for people moving from Scratch to Python.
|
|
5
|
+
Author-email: 魔丸编程 <17516332069@163.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 魔丸编程
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/CutePandaXJK/pyscrh
|
|
29
|
+
Project-URL: Repository, https://github.com/CutePandaXJK/pyscrh
|
|
30
|
+
Keywords: scratch,pygame,game,sprite,education,beginner
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Operating System :: OS Independent
|
|
34
|
+
Classifier: Topic :: Games/Entertainment
|
|
35
|
+
Classifier: Topic :: Education
|
|
36
|
+
Classifier: Intended Audience :: Education
|
|
37
|
+
Requires-Python: >=3.8
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
License-File: LICENSE
|
|
40
|
+
Requires-Dist: pygame>=2.0
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# pyscrh
|
|
44
|
+
|
|
45
|
+
A tiny, **Scratch-style** sprite library built on [pygame](https://www.pygame.org/),
|
|
46
|
+
made for people moving from Scratch to Python.
|
|
47
|
+
|
|
48
|
+
Every Scratch block you know has a short Python equivalent — no game loop,
|
|
49
|
+
no event handling, no surface juggling to remember.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install pyscrh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
(Requires Python 3.8+ and pygame, which is installed automatically.)
|
|
58
|
+
|
|
59
|
+
## Quick start
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import pyscrh
|
|
63
|
+
|
|
64
|
+
apple = Sprite("apple.png") # load an image (PNG with transparency)
|
|
65
|
+
apple.turnway = "lr" # flip left-right when bouncing
|
|
66
|
+
|
|
67
|
+
def forever():
|
|
68
|
+
apple.move(5) # walk 5 steps in the current direction
|
|
69
|
+
apple.edgeb() # bounce off the screen edge
|
|
70
|
+
# stop_all() # call this anywhere to quit the game
|
|
71
|
+
|
|
72
|
+
clickGF() # start the game
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Just `import pyscrh` and you're done — `Sprite`, `clickGF`, `stop_all`,
|
|
76
|
+
`WIDTH` and `HEIGHT` are available directly, just like `import pgzrun`.
|
|
77
|
+
|
|
78
|
+
That's it. The apple walks right, bounces off the right edge, walks left,
|
|
79
|
+
bounces off the left edge, and repeats.
|
|
80
|
+
|
|
81
|
+
## Scratch → Python cheat sheet
|
|
82
|
+
|
|
83
|
+
| Scratch block | pyscrh |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| move 10 steps | `sprite.move(10)` |
|
|
86
|
+
| turn 15° clockwise | `sprite.turnright(15)` |
|
|
87
|
+
| turn 15° counter-clockwise | `sprite.turnleft(15)` |
|
|
88
|
+
| go to x: y: | `sprite.gotoxy(x, y)` |
|
|
89
|
+
| go to random position | `sprite.goto("r")` |
|
|
90
|
+
| glide 1 sec to x: y: | `sprite.glidexy(1, x, y)` |
|
|
91
|
+
| point in direction 90 | `sprite.point(90)` |
|
|
92
|
+
| point towards mouse | `sprite.pointtow("m")` |
|
|
93
|
+
| if on edge, bounce | `sprite.edgeb()` |
|
|
94
|
+
|
|
95
|
+
## How it works
|
|
96
|
+
|
|
97
|
+
You only write one required function:
|
|
98
|
+
|
|
99
|
+
- `forever()` — called once every frame, like Scratch's *forever* block.
|
|
100
|
+
Put `sprite.move(...)`, `edgeb()`, etc. in here. No `dt` parameter
|
|
101
|
+
needed; the library handles timing.
|
|
102
|
+
|
|
103
|
+
An optional `draw()` is also called every frame, *after* all sprites are
|
|
104
|
+
drawn, if you want extra drawing (text, backgrounds, etc.).
|
|
105
|
+
|
|
106
|
+
Call `clickGF()` to start, and `stop_all()` to close the window and quit.
|
|
107
|
+
pyscrh handles the window, clock, event loop and rendering for you.
|
|
108
|
+
|
|
109
|
+
## API reference
|
|
110
|
+
|
|
111
|
+
### `Sprite(image_path, name="")`
|
|
112
|
+
|
|
113
|
+
Create a sprite from an image file.
|
|
114
|
+
|
|
115
|
+
| Method | Description |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| `move(steps)` | Walk `steps` pixels in the current direction. |
|
|
118
|
+
| `turnright(deg)` / `turnleft(deg)` | Rotate by `deg` degrees. |
|
|
119
|
+
| `gotoxy(x, y)` | Jump to `(x, y)`. |
|
|
120
|
+
| `goto("r")` / `goto("m")` | Jump to a random spot / the mouse. |
|
|
121
|
+
| `glidexy(sec, x, y)` | Glide to `(x, y)` over `sec` seconds. |
|
|
122
|
+
| `glideto(sec, "r")` / `glideto(sec, "m")` | Glide to a spot / the mouse. |
|
|
123
|
+
| `point(deg)` | Set direction to `deg` degrees. |
|
|
124
|
+
| `pointtow("r")` / `pointtow("m")` | Face a random direction / the mouse. |
|
|
125
|
+
| `edgeb()` | Bounce off the screen edge. |
|
|
126
|
+
|
|
127
|
+
| Attribute | Description |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `x`, `y` | Center position (read or write). |
|
|
130
|
+
| `angle` | Direction in degrees (0 = right, 90 = down). |
|
|
131
|
+
| `turnway` | `"r"` rotate-bounce, `"lr"` flip-bounce, `"n"` no bounce. |
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pygame>=2.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyscrh
|
pyscrh-0.1.0/setup.cfg
ADDED