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.
Files changed (123) hide show
  1. {aether_engine-1.8.0 → aether_engine-1.8.2}/MANIFEST.in +3 -0
  2. {aether_engine-1.8.0/aether_engine.egg-info → aether_engine-1.8.2}/PKG-INFO +12 -6
  3. {aether_engine-1.8.0 → aether_engine-1.8.2}/README.md +7 -1
  4. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/__init__.py +1 -1
  5. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/cli.py +41 -15
  6. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/_identity.py +1 -1
  7. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/engine.py +4 -0
  8. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/input.py +9 -0
  9. aether_engine-1.8.2/aether/docs/GUIDE.md +390 -0
  10. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/hud.py +67 -12
  11. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/content.py +106 -17
  12. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/plain_content.py +16 -8
  13. {aether_engine-1.8.0 → aether_engine-1.8.2/aether_engine.egg-info}/PKG-INFO +12 -6
  14. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/SOURCES.txt +2 -0
  15. aether_engine-1.8.2/docs/GUIDE.md +390 -0
  16. {aether_engine-1.8.0 → aether_engine-1.8.2}/setup.py +1 -1
  17. {aether_engine-1.8.0 → aether_engine-1.8.2}/LICENSE +0 -0
  18. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/__main__.py +0 -0
  19. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/accelerators/__init__.py +0 -0
  20. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/accelerators/gameplay.py +0 -0
  21. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/accelerators/voxel.py +0 -0
  22. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ai/behavior_tree.py +0 -0
  23. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ai/navmesh.py +0 -0
  24. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/animation/skeleton.py +0 -0
  25. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/assets/__pycache__/asset_pipeline.cpython-314.pyc +0 -0
  26. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/assets/asset_pipeline.py +0 -0
  27. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/assets/fonts/AetherSans.ttf +0 -0
  28. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/__init__.py +0 -0
  29. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/audio_manager.py +0 -0
  30. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/music.py +0 -0
  31. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/sfx.py +0 -0
  32. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/audio/sound.py +0 -0
  33. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/__init__.py +0 -0
  34. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/achievement_system.py +0 -0
  35. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/application.py +0 -0
  36. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/boot_splash.py +0 -0
  37. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/branding_guard.py +0 -0
  38. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/config.py +0 -0
  39. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/console.py +0 -0
  40. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/event_bus.py +0 -0
  41. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/plugin_system.py +0 -0
  42. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/save_system.py +0 -0
  43. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/time_manager.py +0 -0
  44. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/core/window.py +0 -0
  45. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/editor/__init__.py +0 -0
  46. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/environment/environment_system.py +0 -0
  47. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/__init__.py +0 -0
  48. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/camera.py +0 -0
  49. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/first_person.py +0 -0
  50. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/framebuffer.py +0 -0
  51. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/light.py +0 -0
  52. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/lod_system.py +0 -0
  53. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/material.py +0 -0
  54. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/mesh.py +0 -0
  55. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/mesh_factory.py +0 -0
  56. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/particle_system.py +0 -0
  57. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/post_process.py +0 -0
  58. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/renderer.py +0 -0
  59. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/shader.py +0 -0
  60. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/shader_library.py +0 -0
  61. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/sky_atmosphere.py +0 -0
  62. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/terrain.py +0 -0
  63. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/texture.py +0 -0
  64. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/voxel_mesh.py +0 -0
  65. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics/water.py +0 -0
  66. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/graphics2d/__init__.py +0 -0
  67. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/network/network_manager.py +0 -0
  68. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/__init__.py +0 -0
  69. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/bullet_backend.py +0 -0
  70. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/collider.py +0 -0
  71. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/physics2d.py +0 -0
  72. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/physics3d.py +0 -0
  73. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/physics_world.py +0 -0
  74. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/raycast.py +0 -0
  75. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/physics/rigidbody.py +0 -0
  76. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/platform/steam_api.py +0 -0
  77. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/__init__.py +0 -0
  78. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/asset_database.py +0 -0
  79. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/asset_loader.py +0 -0
  80. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/loaders/model_loader.py +0 -0
  81. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/loaders/obj_loader.py +0 -0
  82. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/loaders/texture_loader.py +0 -0
  83. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/resource_manager.py +0 -0
  84. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/save_load.py +0 -0
  85. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/resources/serialization.py +0 -0
  86. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/__init__.py +0 -0
  87. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/component.py +0 -0
  88. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/entity.py +0 -0
  89. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/scene.py +0 -0
  90. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/scene_graph.py +0 -0
  91. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/scene/scene_manager.py +0 -0
  92. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/tools/__init__.py +0 -0
  93. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/tools/packager.py +0 -0
  94. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/__init__.py +0 -0
  95. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/button.py +0 -0
  96. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/canvas.py +0 -0
  97. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/ui/text.py +0 -0
  98. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/__init__.py +0 -0
  99. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/fonts.py +0 -0
  100. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/logger.py +0 -0
  101. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/math_utils.py +0 -0
  102. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/object_pool.py +0 -0
  103. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/utils/profiler.py +0 -0
  104. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/__init__.py +0 -0
  105. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/bot_detect.py +0 -0
  106. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/config.py +0 -0
  107. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/notifications.py +0 -0
  108. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/plain.py +0 -0
  109. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/registry.py +0 -0
  110. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/secrets.py +0 -0
  111. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/server.py +0 -0
  112. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/templates.py +0 -0
  113. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether/web/urls.py +0 -0
  114. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/dependency_links.txt +0 -0
  115. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/entry_points.txt +0 -0
  116. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/requires.txt +4 -4
  117. {aether_engine-1.8.0 → aether_engine-1.8.2}/aether_engine.egg-info/top_level.txt +0 -0
  118. {aether_engine-1.8.0 → aether_engine-1.8.2}/examples/01_hello_cube.py +0 -0
  119. {aether_engine-1.8.0 → aether_engine-1.8.2}/examples/02_sphere_scene.py +0 -0
  120. {aether_engine-1.8.0 → aether_engine-1.8.2}/examples/03_first_person.py +0 -0
  121. {aether_engine-1.8.0 → aether_engine-1.8.2}/examples/04_physics2d.py +0 -0
  122. {aether_engine-1.8.0 → aether_engine-1.8.2}/pyproject.toml +0 -0
  123. {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.0
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: pygame-ce>=2.5; extra == "full"
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: pyinstaller>=6.0; extra == "full"
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.0 · **Лицензия:** MIT
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.0 · **Лицензия:** MIT
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.0"
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(); hud.draw_crosshair(); hud.draw_bar(...); hud.draw_hotbar(...)
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 — краткий гайд по API
114
- aether website — documentation site (domain in bell notifications)
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/GUIDE.md
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
- guide = Path(__file__).resolve().parents[2] / "docs" / "GUIDE.md"
130
- if guide.exists():
131
- print(guide.read_text(encoding="utf-8"))
132
- else:
133
- print(QUICK_REFERENCE)
134
- return 0
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="Краткая справка по API и командам").set_defaults(func=cmd_help)
165
- sub.add_parser("docs", help="Полная документация").set_defaults(func=cmd_docs)
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 игры")
@@ -20,7 +20,7 @@ _B = {
20
20
  ]),
21
21
  "sig": bytes([
22
22
  230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
23
- 135, 150, 137, 159, 137, 151,
23
+ 135, 150, 137, 159, 137, 149,
24
24
  ]),
25
25
  }
26
26
 
@@ -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