aether-engine 1.8.0__tar.gz → 1.8.2__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.
- {aether_engine-1.8.0 → aether_engine-1.8.2}/MANIFEST.in +3 -0
- {aether_engine-1.8.0/aether_engine.egg-info → aether_engine-1.8.2}/PKG-INFO +12 -6
- {aether_engine-1.8.0 → aether_engine-1.8.2}/README.md +7 -1
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/__init__.py +1 -1
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/cli.py +41 -15
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/_identity.py +1 -1
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/engine.py +4 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/input.py +9 -0
- aether_engine-1.8.2/aether/docs/GUIDE.md +390 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/hud.py +67 -12
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/content.py +106 -17
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/plain_content.py +16 -8
- {aether_engine-1.8.0 → aether_engine-1.8.2/aether_engine.egg-info}/PKG-INFO +12 -6
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/SOURCES.txt +2 -0
- aether_engine-1.8.2/docs/GUIDE.md +390 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/setup.py +1 -1
- {aether_engine-1.8.0 → aether_engine-1.8.2}/LICENSE +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/__main__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/accelerators/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/accelerators/gameplay.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/accelerators/voxel.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ai/behavior_tree.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ai/navmesh.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/animation/skeleton.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/assets/__pycache__/asset_pipeline.cpython-314.pyc +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/assets/asset_pipeline.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/assets/fonts/AetherSans.ttf +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/audio_manager.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/music.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/sfx.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/sound.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/achievement_system.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/application.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/boot_splash.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/branding_guard.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/config.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/console.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/event_bus.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/plugin_system.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/save_system.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/time_manager.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/window.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/editor/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/environment/environment_system.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/camera.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/first_person.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/framebuffer.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/light.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/lod_system.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/material.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/mesh.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/mesh_factory.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/particle_system.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/post_process.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/renderer.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/shader.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/shader_library.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/sky_atmosphere.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/terrain.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/texture.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/voxel_mesh.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/water.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics2d/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/network/network_manager.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/bullet_backend.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/collider.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/physics2d.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/physics3d.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/physics_world.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/raycast.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/rigidbody.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/platform/steam_api.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/asset_database.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/asset_loader.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/loaders/model_loader.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/loaders/obj_loader.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/loaders/texture_loader.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/resource_manager.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/save_load.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/serialization.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/component.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/entity.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/scene.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/scene_graph.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/scene_manager.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/tools/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/tools/packager.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/button.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/canvas.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/text.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/fonts.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/logger.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/math_utils.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/object_pool.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/profiler.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/__init__.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/bot_detect.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/config.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/notifications.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/plain.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/registry.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/secrets.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/server.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/templates.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/urls.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/dependency_links.txt +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/entry_points.txt +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/requires.txt +4 -4
- {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/top_level.txt +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/examples/01_hello_cube.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/examples/02_sphere_scene.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/examples/03_first_person.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/examples/04_physics2d.py +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/pyproject.toml +0 -0
- {aether_engine-1.8.0 → aether_engine-1.8.2}/setup.cfg +0 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
include README.md
|
|
2
2
|
include LICENSE
|
|
3
3
|
include pyproject.toml
|
|
4
|
+
include docs/GUIDE.md
|
|
4
5
|
|
|
5
6
|
recursive-include aether *.py
|
|
6
7
|
recursive-include aether *.glsl
|
|
7
8
|
recursive-include aether *.vert
|
|
8
9
|
recursive-include aether *.frag
|
|
9
10
|
recursive-include aether *.json
|
|
11
|
+
recursive-include aether *.md
|
|
10
12
|
recursive-include aether/assets *
|
|
13
|
+
recursive-include aether/docs *
|
|
11
14
|
|
|
12
15
|
recursive-include examples *.py
|
|
13
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aether-engine
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.2
|
|
4
4
|
Summary: 3D/2D game engine by entiti937 — OpenGL, physics, FPS, mandatory branding splash
|
|
5
5
|
Home-page: https://pypi.org/project/aether-engine/
|
|
6
6
|
Author: entiti937
|
|
@@ -31,13 +31,13 @@ Requires-Dist: trimesh>=3.21; extra == "models"
|
|
|
31
31
|
Provides-Extra: build
|
|
32
32
|
Requires-Dist: pyinstaller>=6.0; extra == "build"
|
|
33
33
|
Provides-Extra: full
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist: trimesh>=3.21; extra == "full"
|
|
34
|
+
Requires-Dist: freetype-py>=2.4; extra == "full"
|
|
36
35
|
Requires-Dist: imageio>=2.31; extra == "full"
|
|
37
|
-
Requires-Dist:
|
|
36
|
+
Requires-Dist: trimesh>=3.21; extra == "full"
|
|
38
37
|
Requires-Dist: pyyaml>=6.0; extra == "full"
|
|
38
|
+
Requires-Dist: pyinstaller>=6.0; extra == "full"
|
|
39
|
+
Requires-Dist: pygame-ce>=2.5; extra == "full"
|
|
39
40
|
Requires-Dist: pymunk>=6.0; extra == "full"
|
|
40
|
-
Requires-Dist: freetype-py>=2.4; extra == "full"
|
|
41
41
|
Dynamic: author
|
|
42
42
|
Dynamic: classifier
|
|
43
43
|
Dynamic: description
|
|
@@ -52,7 +52,7 @@ Dynamic: summary
|
|
|
52
52
|
# Aether Engine
|
|
53
53
|
|
|
54
54
|
**3D / 2D игровой движок на Python + OpenGL**
|
|
55
|
-
**Разработчик:** entiti937 · **Версия:** 1.8.
|
|
55
|
+
**Разработчик:** entiti937 · **Версия:** 1.8.1 · **Лицензия:** MIT
|
|
56
56
|
**PyPI:** https://pypi.org/project/aether-engine/
|
|
57
57
|
|
|
58
58
|
> Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md) · сайт: `aether website`
|
|
@@ -70,6 +70,12 @@ import aether # правильно
|
|
|
70
70
|
# import aether_engine # НЕПРАВИЛЬНО — такого модуля нет
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
## Что нового в 1.8.1
|
|
74
|
+
|
|
75
|
+
- `Hud.draw_bar(x, y, w, h, value, max_value, color)` — простой и понятный вызов
|
|
76
|
+
- `aether docs` печатает **полный** GUIDE.md (гайд вшит в пакет)
|
|
77
|
+
- Координаты HUD явно: (0,0) = центр экрана
|
|
78
|
+
|
|
73
79
|
## Что нового в 1.8.0
|
|
74
80
|
|
|
75
81
|
- `Scene.remove()` / `despawn()` / `find()`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Aether Engine
|
|
2
2
|
|
|
3
3
|
**3D / 2D игровой движок на Python + OpenGL**
|
|
4
|
-
**Разработчик:** entiti937 · **Версия:** 1.8.
|
|
4
|
+
**Разработчик:** entiti937 · **Версия:** 1.8.1 · **Лицензия:** MIT
|
|
5
5
|
**PyPI:** https://pypi.org/project/aether-engine/
|
|
6
6
|
|
|
7
7
|
> Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md) · сайт: `aether website`
|
|
@@ -19,6 +19,12 @@ import aether # правильно
|
|
|
19
19
|
# import aether_engine # НЕПРАВИЛЬНО — такого модуля нет
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
## Что нового в 1.8.1
|
|
23
|
+
|
|
24
|
+
- `Hud.draw_bar(x, y, w, h, value, max_value, color)` — простой и понятный вызов
|
|
25
|
+
- `aether docs` печатает **полный** GUIDE.md (гайд вшит в пакет)
|
|
26
|
+
- Координаты HUD явно: (0,0) = центр экрана
|
|
27
|
+
|
|
22
28
|
## Что нового в 1.8.0
|
|
23
29
|
|
|
24
30
|
- `Scene.remove()` / `despawn()` / `find()`
|
|
@@ -31,7 +31,7 @@ from aether.audio.sfx import SfxBank
|
|
|
31
31
|
from aether.resources.save_load import save_json, load_json
|
|
32
32
|
from aether.physics.raycast import Raycaster
|
|
33
33
|
|
|
34
|
-
__version__ = "1.8.
|
|
34
|
+
__version__ = "1.8.2"
|
|
35
35
|
|
|
36
36
|
from aether.graphics.voxel_mesh import ChunkedVoxelMesher
|
|
37
37
|
from aether.accelerators import is_native_available, backend_name
|
|
@@ -60,10 +60,15 @@ TRANSFORM
|
|
|
60
60
|
tf.set_position(x,y,z) / set_rotation / set_scale
|
|
61
61
|
tf.translate(dx,dy,dz)
|
|
62
62
|
|
|
63
|
-
HUD
|
|
63
|
+
HUD (координаты: 0,0 = ЦЕНТР экрана, +Y вверх; текста нет — пишите в title)
|
|
64
|
+
from aether import Hud
|
|
64
65
|
engine.draw_hud = True
|
|
65
66
|
hud = Hud(engine.renderer2d)
|
|
66
|
-
hud.clear()
|
|
67
|
+
hud.clear()
|
|
68
|
+
# draw_bar(x, y, width, height, value, max_value, color)
|
|
69
|
+
hud.draw_bar(-500, 300, 200, 20, hp, 100, (0.8, 0.2, 0.2, 1.0))
|
|
70
|
+
hud.draw_crosshair()
|
|
71
|
+
engine.window.set_title(f"HP {{hp}}")
|
|
67
72
|
|
|
68
73
|
FPS / КАМЕРА
|
|
69
74
|
engine.enable_first_person(camera, eye_height=1.7, walk_speed=6)
|
|
@@ -96,10 +101,14 @@ NPC / НАГРАДЫ (RUST, опционально)
|
|
|
96
101
|
engine.physics3d.create_body_for_entity(ent, collider=BoxCollider())
|
|
97
102
|
engine.physics2d.create_box(x, y, w, h, mass=1.0)
|
|
98
103
|
|
|
99
|
-
ВВОД
|
|
104
|
+
ВВОД / МЫШЬ
|
|
100
105
|
import glfw
|
|
101
106
|
engine.input.get_key(glfw.KEY_W)
|
|
102
107
|
engine.input.get_key_down(glfw.KEY_SPACE)
|
|
108
|
+
engine.capture_mouse(True) # захват мыши (НЕ glfw на engine.window!)
|
|
109
|
+
dx, dy = engine.input.mouse_delta() # движение мыши за кадр
|
|
110
|
+
engine.input.lock_cursor(False) # отпустить курсор
|
|
111
|
+
# raw GLFW: engine.window.handle
|
|
103
112
|
|
|
104
113
|
АПДЕЙТ
|
|
105
114
|
engine.on_update(lambda dt: ...)
|
|
@@ -109,13 +118,13 @@ NPC / НАГРАДЫ (RUST, опционально)
|
|
|
109
118
|
aether build my_game.py --name MyGame --console
|
|
110
119
|
|
|
111
120
|
ИНСТРУМЕНТЫ
|
|
112
|
-
aether help —
|
|
113
|
-
aether docs —
|
|
114
|
-
aether website —
|
|
121
|
+
aether help — краткая шпаргалка
|
|
122
|
+
aether docs — ПОЛНЫЙ гайд (весь GUIDE.md в терминал)
|
|
123
|
+
aether website — HTML-сайт документации
|
|
115
124
|
python -m aether website
|
|
116
125
|
python games/aether_craft.py
|
|
117
126
|
|
|
118
|
-
ПОЛНАЯ ДОКУМЕНТАЦИЯ: docs
|
|
127
|
+
ПОЛНАЯ ДОКУМЕНТАЦИЯ: aether docs | сайт: aether website
|
|
119
128
|
PyPI: https://pypi.org/project/aether-engine/
|
|
120
129
|
"""
|
|
121
130
|
|
|
@@ -126,12 +135,29 @@ def cmd_help(_args):
|
|
|
126
135
|
|
|
127
136
|
|
|
128
137
|
def cmd_docs(_args):
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
138
|
+
"""Print the full bundled developer guide."""
|
|
139
|
+
here = Path(__file__).resolve().parent
|
|
140
|
+
candidates = [
|
|
141
|
+
here / "docs" / "GUIDE.md",
|
|
142
|
+
here.parents[1] / "docs" / "GUIDE.md",
|
|
143
|
+
here.parents[0] / "docs" / "GUIDE.md",
|
|
144
|
+
]
|
|
145
|
+
for guide in candidates:
|
|
146
|
+
if guide.exists():
|
|
147
|
+
text = guide.read_text(encoding="utf-8")
|
|
148
|
+
try:
|
|
149
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
150
|
+
except Exception:
|
|
151
|
+
pass
|
|
152
|
+
try:
|
|
153
|
+
print(text)
|
|
154
|
+
except UnicodeEncodeError:
|
|
155
|
+
sys.stdout.buffer.write(text.encode("utf-8", errors="replace"))
|
|
156
|
+
sys.stdout.buffer.write(b"\n")
|
|
157
|
+
return 0
|
|
158
|
+
print(QUICK_REFERENCE)
|
|
159
|
+
print("\n[aether docs] GUIDE.md not found in package — reinstall: pip install -U aether-engine")
|
|
160
|
+
return 1
|
|
135
161
|
|
|
136
162
|
|
|
137
163
|
def cmd_build(args):
|
|
@@ -161,8 +187,8 @@ def main(argv=None):
|
|
|
161
187
|
|
|
162
188
|
sub = parser.add_subparsers(dest="command")
|
|
163
189
|
|
|
164
|
-
sub.add_parser("help", help="Краткая
|
|
165
|
-
sub.add_parser("docs", help="
|
|
190
|
+
sub.add_parser("help", help="Краткая шпаргалка").set_defaults(func=cmd_help)
|
|
191
|
+
sub.add_parser("docs", help="Полный гайд разработчика (весь GUIDE.md)").set_defaults(func=cmd_docs)
|
|
166
192
|
|
|
167
193
|
p_build = sub.add_parser("build", help="Собрать игру в .exe (PyInstaller)")
|
|
168
194
|
p_build.add_argument("script", help="Путь к main.py игры")
|
|
@@ -377,6 +377,10 @@ class Engine:
|
|
|
377
377
|
self.renderer2d.render()
|
|
378
378
|
self.renderer2d.end_frame()
|
|
379
379
|
|
|
380
|
+
def capture_mouse(self, locked: bool = True):
|
|
381
|
+
"""Lock/unlock cursor for custom camera control. Same as input.lock_cursor()."""
|
|
382
|
+
self.input.lock_cursor(locked)
|
|
383
|
+
|
|
380
384
|
def on_update(self, callback):
|
|
381
385
|
"""Регистрирует callback(dt), вызываемый каждый кадр."""
|
|
382
386
|
self._update_callbacks.append(callback)
|
|
@@ -57,6 +57,7 @@ class Input:
|
|
|
57
57
|
self._mouse_buttons_prev = self._mouse_buttons.copy()
|
|
58
58
|
|
|
59
59
|
def lock_cursor(self, locked: bool = True):
|
|
60
|
+
"""Capture mouse for FPS / orbit camera. Use engine.input.lock_cursor(True)."""
|
|
60
61
|
self.cursor_locked = locked
|
|
61
62
|
mode = glfw.CURSOR_DISABLED if locked else glfw.CURSOR_NORMAL
|
|
62
63
|
glfw.set_input_mode(self.window.handle, glfw.CURSOR, mode)
|
|
@@ -66,6 +67,14 @@ class Input:
|
|
|
66
67
|
self.mouse_dx = 0.0
|
|
67
68
|
self.mouse_dy = 0.0
|
|
68
69
|
|
|
70
|
+
def mouse_delta(self) -> tuple:
|
|
71
|
+
"""Mouse movement since last frame (dx, dy). Requires lock_cursor(True) for FPS."""
|
|
72
|
+
return (self.mouse_dx, self.mouse_dy)
|
|
73
|
+
|
|
74
|
+
def get_cursor_pos(self) -> tuple:
|
|
75
|
+
"""Window cursor position in pixels (x, y)."""
|
|
76
|
+
return (self.mouse_x, self.mouse_y)
|
|
77
|
+
|
|
69
78
|
def get_key(self, key) -> bool:
|
|
70
79
|
return self._keys.get(key, False)
|
|
71
80
|
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# Aether Engine — Full Guide (v1.8.2)
|
|
2
|
+
|
|
3
|
+
**PyPI:** `pip install aether-engine` → **import:** `import aether` (NOT `aether_engine`)
|
|
4
|
+
|
|
5
|
+
Commands: `aether help` | `aether docs` (this file) | `aether website`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Engine
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from aether import Engine
|
|
13
|
+
|
|
14
|
+
engine = Engine(width=1280, height=720, title="My Game")
|
|
15
|
+
engine.load_scene(scene)
|
|
16
|
+
engine.on_update(my_update) # def my_update(dt): ...
|
|
17
|
+
engine.run() # blocks until ESC
|
|
18
|
+
|
|
19
|
+
# Properties
|
|
20
|
+
engine.scene # current Scene
|
|
21
|
+
engine.input # keyboard / mouse
|
|
22
|
+
engine.window # Window (title, size, .handle for raw GLFW)
|
|
23
|
+
engine.renderer # 3D renderer
|
|
24
|
+
engine.renderer2d # 2D / HUD sprites
|
|
25
|
+
engine.physics2d # pymunk
|
|
26
|
+
engine.physics3d # lightweight 3D
|
|
27
|
+
engine.time.fps # float
|
|
28
|
+
engine.free_camera # True = built-in WASD fly (set False for custom cam)
|
|
29
|
+
engine.draw_hud # True = render HUD overlay each frame
|
|
30
|
+
engine.mode # "3d" or "2d"
|
|
31
|
+
|
|
32
|
+
# Methods
|
|
33
|
+
engine.set_mode("2d")
|
|
34
|
+
engine.enable_first_person(camera, eye_height=1.7, walk_speed=6)
|
|
35
|
+
engine.capture_mouse(True) # lock cursor — use this, NOT glfw on window directly
|
|
36
|
+
engine.on_update(callback)
|
|
37
|
+
engine.run()
|
|
38
|
+
engine.shutdown()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 2. Input (keyboard / mouse)
|
|
44
|
+
|
|
45
|
+
**Never pass `engine.window` to glfw directly.** Use `engine.window.handle` only if you need raw GLFW.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import glfw
|
|
49
|
+
|
|
50
|
+
inp = engine.input
|
|
51
|
+
|
|
52
|
+
# Keyboard
|
|
53
|
+
inp.get_key(glfw.KEY_W) # held
|
|
54
|
+
inp.get_key_down(glfw.KEY_SPACE) # pressed this frame
|
|
55
|
+
inp.get_key_up(glfw.KEY_E)
|
|
56
|
+
|
|
57
|
+
# Mouse buttons
|
|
58
|
+
inp.get_mouse_button(glfw.MOUSE_BUTTON_LEFT)
|
|
59
|
+
inp.get_mouse_button_down(glfw.MOUSE_BUTTON_RIGHT)
|
|
60
|
+
|
|
61
|
+
# Mouse look (FPS / orbit)
|
|
62
|
+
inp.lock_cursor(True) # or engine.capture_mouse(True)
|
|
63
|
+
dx, dy = inp.mouse_delta() # movement since last frame
|
|
64
|
+
# Also: inp.mouse_dx, inp.mouse_dy
|
|
65
|
+
|
|
66
|
+
# Toggle capture (like Tab in free camera)
|
|
67
|
+
if inp.get_key_down(glfw.KEY_TAB):
|
|
68
|
+
inp.lock_cursor(not inp.cursor_locked)
|
|
69
|
+
|
|
70
|
+
# Cursor position in pixels
|
|
71
|
+
x, y = inp.get_cursor_pos()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 3. Window
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
w = engine.window
|
|
80
|
+
w.set_title("HP 80 | score 5")
|
|
81
|
+
w.width, w.height
|
|
82
|
+
w.handle # raw GLFW window — for glfw.get_cursor_pos(engine.window.handle) etc.
|
|
83
|
+
# Do NOT: glfw.set_input_mode(engine.window, ...) — wrong type
|
|
84
|
+
# Do: engine.input.lock_cursor(True)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 4. Scene
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from aether import Scene
|
|
93
|
+
|
|
94
|
+
scene = Scene("Level1")
|
|
95
|
+
scene.spawn(entity)
|
|
96
|
+
scene.remove(entity) # removes from scene + entity.destroy()
|
|
97
|
+
scene.despawn(entity) # alias
|
|
98
|
+
found = scene.find("Player")
|
|
99
|
+
scene.set_main_camera(camera)
|
|
100
|
+
scene.entities # list
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 5. Entity
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from aether import Entity, Transform, MeshRenderer
|
|
109
|
+
|
|
110
|
+
ent = Entity("Player")
|
|
111
|
+
ent.add_component(Transform(position=(0, 1, 0)))
|
|
112
|
+
ent.add_component(MeshRenderer(mesh=..., material=...))
|
|
113
|
+
ent.get_component("Transform")
|
|
114
|
+
ent.remove_component("MeshRenderer")
|
|
115
|
+
ent.destroy() # prefer scene.remove(ent)
|
|
116
|
+
ent.enabled = True
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 6. Transform
|
|
122
|
+
|
|
123
|
+
`position`, `rotation`, `scale` = **numpy float32[3]**. Rotation in **degrees**.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
tf = Transform(position=(0, 1, 0), rotation=(0, 45, 0), scale=(1, 1, 1))
|
|
127
|
+
tf.set_position(x, y, z)
|
|
128
|
+
tf.position[:] = (1, 2, 3)
|
|
129
|
+
tf.position[1] += 0.1
|
|
130
|
+
tf.set_rotation(rx, ry, rz)
|
|
131
|
+
tf.set_scale(sx, sy, sz)
|
|
132
|
+
tf.translate(dx, dy, dz)
|
|
133
|
+
tf.rotate(0, 90 * dt, 0)
|
|
134
|
+
tf.look_at(target_xyz)
|
|
135
|
+
tf.get_model_matrix()
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 7. Camera
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from aether import Camera
|
|
144
|
+
|
|
145
|
+
cam = Camera(position=(0, 5, 10), yaw=-90, pitch=-20, fov=60)
|
|
146
|
+
cam.position = (x, y, z)
|
|
147
|
+
cam.yaw, cam.pitch, cam.fov, cam.near, cam.far
|
|
148
|
+
cam.front, cam.right, cam.up
|
|
149
|
+
cam.look_at(player_tf.position)
|
|
150
|
+
cam.rotate(mouse_dx, mouse_dy)
|
|
151
|
+
cam.move_forward(dt)
|
|
152
|
+
|
|
153
|
+
# Third-person — disable built-in fly camera first
|
|
154
|
+
engine.free_camera = False
|
|
155
|
+
cam.position = (px, py + 6, pz + 10)
|
|
156
|
+
cam.look_at(target)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 8. MeshFactory
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from aether import MeshFactory
|
|
165
|
+
|
|
166
|
+
MeshFactory.create_cube(size=1.0)
|
|
167
|
+
MeshFactory.create_plane(size=10.0)
|
|
168
|
+
MeshFactory.create_sphere(radius=1.0, sectors=24, stacks=16)
|
|
169
|
+
MeshFactory.create_cylinder(radius=0.5, height=2.0, segments=24, capped=True)
|
|
170
|
+
MeshFactory.create_pyramid(size=1.0, height=1.0)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 9. Material
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from aether import Material
|
|
179
|
+
|
|
180
|
+
mat = Material("wood")
|
|
181
|
+
mat.albedo[:] = (0.8, 0.5, 0.2) # RGB 0..1
|
|
182
|
+
mat.metallic = 0.0
|
|
183
|
+
mat.roughness = 0.6
|
|
184
|
+
mat.ao = 1.0
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 10. HUD
|
|
190
|
+
|
|
191
|
+
Origin **(0,0) = center of screen**, +Y up. **No text rendering** — use `engine.window.set_title(...)`.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from aether import Hud, HudBar
|
|
195
|
+
|
|
196
|
+
engine.draw_hud = True
|
|
197
|
+
hud = Hud(engine.renderer2d)
|
|
198
|
+
|
|
199
|
+
def update(dt):
|
|
200
|
+
hud.clear()
|
|
201
|
+
hud.draw_bar(-500, 300, 200, 20, hp, 100, (0.8, 0.2, 0.2, 1.0))
|
|
202
|
+
hud.draw_crosshair()
|
|
203
|
+
hud.draw_hotbar(7, 0, colors=[(1,0,0),(0,1,0)])
|
|
204
|
+
hud.draw_rect(0, -250, 100, 30, color=(0,0,0,0.5))
|
|
205
|
+
hud.draw_panel(0, 250, 300, 60)
|
|
206
|
+
engine.window.set_title(f"HP {hp}")
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 11. Assets
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from aether import AssetLoader, Sprite
|
|
215
|
+
|
|
216
|
+
mesh = AssetLoader.load_model("hero.glb")
|
|
217
|
+
sprite = Sprite.from_image("icon.png", x=0, y=0)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 12. Physics
|
|
223
|
+
|
|
224
|
+
**2D:** `engine.physics2d` (pymunk) — Y-up, sync `sprite.x, sprite.y = body.x, body.y`
|
|
225
|
+
**3D:** `engine.physics3d` — lightweight; PyBullet planned
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from aether import BoxCollider
|
|
229
|
+
body = engine.physics3d.create_body_for_entity(ent, mass=1.0, collider=BoxCollider(size=(1,1,1)))
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
# EXAMPLE GAMES (copy-paste)
|
|
235
|
+
|
|
236
|
+
## Example A — Hello Cube (3D basics)
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
|
|
240
|
+
|
|
241
|
+
engine = Engine(1280, 720, "Hello Cube")
|
|
242
|
+
scene = Scene("Main")
|
|
243
|
+
camera = Camera(position=(0, 4, 8), yaw=-90, pitch=-25)
|
|
244
|
+
scene.set_main_camera(camera)
|
|
245
|
+
|
|
246
|
+
floor = Entity("Floor")
|
|
247
|
+
floor.add_component(Transform(position=(0, -1, 0)))
|
|
248
|
+
m = Material(); m.albedo[:] = (0.7, 0.7, 0.8)
|
|
249
|
+
floor.add_component(MeshRenderer(mesh=MeshFactory.create_plane(12), material=m))
|
|
250
|
+
scene.spawn(floor)
|
|
251
|
+
|
|
252
|
+
cube = Entity("Cube")
|
|
253
|
+
cube.add_component(Transform(position=(0, 1.5, 0), scale=(2, 2, 2)))
|
|
254
|
+
m2 = Material(); m2.albedo[:] = (0.1, 1.0, 0.2)
|
|
255
|
+
cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=m2))
|
|
256
|
+
scene.spawn(cube)
|
|
257
|
+
|
|
258
|
+
engine.load_scene(scene)
|
|
259
|
+
engine.run()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Example B — Sphere ring (materials)
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
import math
|
|
266
|
+
from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
|
|
267
|
+
|
|
268
|
+
engine = Engine(1280, 720, "Spheres")
|
|
269
|
+
scene = Scene("Spheres")
|
|
270
|
+
scene.set_main_camera(Camera(position=(0, 5, 15), yaw=-90, pitch=-20))
|
|
271
|
+
|
|
272
|
+
floor = Entity("Floor")
|
|
273
|
+
fm = Material(); fm.albedo[:] = (0.55, 0.58, 0.65)
|
|
274
|
+
floor.add_component(Transform(position=(0, -0.5, 0)))
|
|
275
|
+
floor.add_component(MeshRenderer(mesh=MeshFactory.create_plane(14), material=fm))
|
|
276
|
+
scene.spawn(floor)
|
|
277
|
+
|
|
278
|
+
for i in range(12):
|
|
279
|
+
a = i * math.pi * 2 / 12
|
|
280
|
+
mat = Material(); mat.albedo[:] = (0.3 + i*0.05, 0.5, 0.8 - i*0.04)
|
|
281
|
+
s = Entity(f"S{i}")
|
|
282
|
+
s.add_component(Transform(position=(math.cos(a)*4, 0.5, math.sin(a)*4)))
|
|
283
|
+
s.add_component(MeshRenderer(mesh=MeshFactory.create_sphere(0.5), material=mat))
|
|
284
|
+
scene.spawn(s)
|
|
285
|
+
|
|
286
|
+
engine.load_scene(scene)
|
|
287
|
+
engine.run()
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Example C — First person (FPS)
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera
|
|
294
|
+
|
|
295
|
+
engine = Engine(1280, 720, "FPS")
|
|
296
|
+
scene = Scene("FPS")
|
|
297
|
+
cam = Camera(position=(0, 1.7, 8), yaw=-90, fov=75)
|
|
298
|
+
scene.set_main_camera(cam)
|
|
299
|
+
|
|
300
|
+
floor = Entity("Floor")
|
|
301
|
+
floor.add_component(Transform())
|
|
302
|
+
m = Material(); m.albedo[:] = (0.3, 0.34, 0.4)
|
|
303
|
+
floor.add_component(MeshRenderer(mesh=MeshFactory.create_plane(40), material=m))
|
|
304
|
+
scene.spawn(floor)
|
|
305
|
+
|
|
306
|
+
engine.load_scene(scene)
|
|
307
|
+
engine.enable_first_person(cam)
|
|
308
|
+
engine.run()
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Example D — 2D platformer (pymunk)
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
import glfw
|
|
315
|
+
from aether import Engine, Sprite
|
|
316
|
+
|
|
317
|
+
engine = Engine(960, 540, "2D")
|
|
318
|
+
engine.set_mode("2d")
|
|
319
|
+
engine.free_camera = False
|
|
320
|
+
r = engine.renderer2d
|
|
321
|
+
r.camera.zoom = 0.035
|
|
322
|
+
|
|
323
|
+
player_sprite = r.add_sprite(Sprite(x=-200, y=0, width=36, height=48, color=(0.2, 0.85, 1, 1)))
|
|
324
|
+
phys = engine.physics2d
|
|
325
|
+
phys.create_box(0, -120, 700, 40, static=True)
|
|
326
|
+
player = phys.create_box(-200, 0, 36, 48, mass=1.0)
|
|
327
|
+
|
|
328
|
+
def update(dt):
|
|
329
|
+
vx = 0
|
|
330
|
+
if engine.input.get_key(glfw.KEY_A): vx -= 260
|
|
331
|
+
if engine.input.get_key(glfw.KEY_D): vx += 260
|
|
332
|
+
phys.set_velocity(player, vx, player.vy)
|
|
333
|
+
if engine.input.get_key_down(glfw.KEY_SPACE) and abs(player.vy) < 20:
|
|
334
|
+
phys.apply_impulse(player, 0, 420)
|
|
335
|
+
player_sprite.x, player_sprite.y = player.x, player.y
|
|
336
|
+
|
|
337
|
+
engine.on_update(update)
|
|
338
|
+
engine.run()
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Example E — Custom camera + mouse (like your game)
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
import glfw
|
|
345
|
+
import math
|
|
346
|
+
from aether import Engine, Scene, Entity, Transform, MeshRenderer, MeshFactory, Material, Camera, Hud
|
|
347
|
+
|
|
348
|
+
engine = Engine(1280, 720, "Third Person")
|
|
349
|
+
scene = Scene("Main")
|
|
350
|
+
cam = Camera(position=(0, 5, 10), yaw=-90, pitch=-30)
|
|
351
|
+
scene.set_main_camera(cam)
|
|
352
|
+
engine.free_camera = False
|
|
353
|
+
engine.capture_mouse(True) # NOT glfw.set_input_mode(engine.window, ...)
|
|
354
|
+
|
|
355
|
+
player = Entity("Player")
|
|
356
|
+
ptf = Transform(position=(0, 0.5, 0))
|
|
357
|
+
player.add_component(ptf)
|
|
358
|
+
m = Material(); m.albedo[:] = (0.2, 0.6, 1.0)
|
|
359
|
+
player.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=m))
|
|
360
|
+
scene.spawn(player)
|
|
361
|
+
|
|
362
|
+
yaw, pitch, dist = 0.0, 0.6, 10.0
|
|
363
|
+
engine.draw_hud = True
|
|
364
|
+
hud = Hud(engine.renderer2d)
|
|
365
|
+
|
|
366
|
+
def update(dt):
|
|
367
|
+
global yaw, pitch
|
|
368
|
+
if engine.input.get_key(glfw.KEY_TAB):
|
|
369
|
+
engine.capture_mouse(not engine.input.cursor_locked)
|
|
370
|
+
mdx, mdy = engine.input.mouse_delta()
|
|
371
|
+
yaw += mdx * 0.003
|
|
372
|
+
pitch = max(0.1, min(1.5, pitch - mdy * 0.003))
|
|
373
|
+
t = ptf.position
|
|
374
|
+
cam.position = (
|
|
375
|
+
t[0] + dist * math.cos(pitch) * math.sin(yaw),
|
|
376
|
+
t[1] + dist * math.sin(pitch),
|
|
377
|
+
t[2] + dist * math.cos(pitch) * math.cos(yaw),
|
|
378
|
+
)
|
|
379
|
+
cam.look_at(t)
|
|
380
|
+
hud.clear()
|
|
381
|
+
hud.draw_crosshair()
|
|
382
|
+
|
|
383
|
+
engine.on_update(update)
|
|
384
|
+
engine.load_scene(scene)
|
|
385
|
+
engine.run()
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
Aether Engine © entiti937
|