Three-dimensional-Games 0.3.0__py3-none-any.whl

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 (34) hide show
  1. three_dimensional_games/__init__.py +256 -0
  2. three_dimensional_games/animation.py +321 -0
  3. three_dimensional_games/application.py +350 -0
  4. three_dimensional_games/audio.py +208 -0
  5. three_dimensional_games/builtin_textures.py +606 -0
  6. three_dimensional_games/camera.py +16 -0
  7. three_dimensional_games/color.py +160 -0
  8. three_dimensional_games/entity.py +494 -0
  9. three_dimensional_games/gamepad.py +330 -0
  10. three_dimensional_games/input.py +145 -0
  11. three_dimensional_games/mesh.py +3 -0
  12. three_dimensional_games/models.py +246 -0
  13. three_dimensional_games/network.py +722 -0
  14. three_dimensional_games/particles.py +191 -0
  15. three_dimensional_games/physics.py +430 -0
  16. three_dimensional_games/prefabs/__init__.py +11 -0
  17. three_dimensional_games/prefabs/first_person.py +153 -0
  18. three_dimensional_games/prefabs/second_person.py +154 -0
  19. three_dimensional_games/prefabs/sky.py +51 -0
  20. three_dimensional_games/prefabs/third_person.py +194 -0
  21. three_dimensional_games/raycaster.py +571 -0
  22. three_dimensional_games/saveload.py +219 -0
  23. three_dimensional_games/scene.py +141 -0
  24. three_dimensional_games/texture.py +6 -0
  25. three_dimensional_games/trigger.py +159 -0
  26. three_dimensional_games/ui.py +305 -0
  27. three_dimensional_games/util.py +215 -0
  28. three_dimensional_games/vector.py +383 -0
  29. three_dimensional_games/voice.py +490 -0
  30. three_dimensional_games-0.3.0.dist-info/METADATA +184 -0
  31. three_dimensional_games-0.3.0.dist-info/RECORD +34 -0
  32. three_dimensional_games-0.3.0.dist-info/WHEEL +5 -0
  33. three_dimensional_games-0.3.0.dist-info/licenses/LICENSE +21 -0
  34. three_dimensional_games-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,256 @@
1
+ """
2
+ Three-dimensional-Games 3D Game Engine
3
+ ======================================
4
+ 比 Ursina 更简洁:开头一行 init() 拿到 game,结尾一行 run() 启动;
5
+ 同时又像 Panda3D 一样严谨实用,内置物理、联机、三种视角控制器、
6
+ 大量贴图、补间动画、粒子、音效、触发区等模块。
7
+
8
+ 标准写法(开头初始化 + 结尾启动):
9
+ from three_dimensional_games import *
10
+
11
+ game = init(title="我的 3D 游戏")
12
+
13
+ ground = Entity(model='plane', texture='grass', scale=50, collider='box')
14
+ player = FirstPerson()
15
+ player.y = 2
16
+
17
+ run()
18
+
19
+ 也兼容“省略 init / run”的极简写法(引擎会自己兜底):
20
+ from three_dimensional_games import *
21
+ Entity(model='cube', collider='box', mass=1)
22
+ FirstPerson()
23
+ # 不用写 run(),脚本结束时也会自动运行
24
+ """
25
+
26
+ __version__ = "0.3.0"
27
+ __author__ = "Your Name"
28
+
29
+ import atexit
30
+
31
+ # ----------------------------------------------------------------------------
32
+ # 引擎实例:init() 显式创建,或按需自动创建
33
+ # ----------------------------------------------------------------------------
34
+ _app = None
35
+ _explicit_run = False
36
+
37
+
38
+ def init(title: str = "Three-dimensional Game", **kwargs):
39
+ """
40
+ 初始化游戏引擎,返回 game 对象(引擎实例)。写在脚本开头。
41
+
42
+ 常用参数:
43
+ init(title='我的游戏', window_size=(1280, 720), fullscreen=False,
44
+ vsync=True, show_fps=True, background_color=(0.1, 0.1, 0.1, 1))
45
+ """
46
+ global _app
47
+ from .application import Application
48
+ if Application._instance is not None:
49
+ _app = Application._instance
50
+ else:
51
+ _app = Application(title=title, **kwargs)
52
+ return _app
53
+
54
+
55
+ def run():
56
+ """启动游戏主循环。写在脚本最后一行。"""
57
+ global _app, _explicit_run
58
+ _app = _ensure_app()
59
+ _explicit_run = True
60
+ _app.run()
61
+
62
+
63
+ def _ensure_app():
64
+ """确保引擎已创建(第一次创建 Entity / UI 时自动调用)"""
65
+ global _app
66
+ if _app is None:
67
+ from .application import Application
68
+ if Application._instance is not None:
69
+ _app = Application._instance
70
+ else:
71
+ _app = Application()
72
+ return _app
73
+
74
+
75
+ def get_app():
76
+ """获取当前引擎实例;如果没有会自动创建一个"""
77
+ return _ensure_app()
78
+
79
+
80
+ # ----------------------------------------------------------------------------
81
+ # 核心 API(导入即可用)
82
+ # ----------------------------------------------------------------------------
83
+ from .application import Application
84
+ from .entity import Entity
85
+ from .vector import Vec2, Vec3, Vec4, to_panda_vec3, to_panda_vec2, to_panda_vec4
86
+ from .color import Color, color
87
+ from .scene import scene, camera
88
+ from .input import input_handler, held_keys, mouse
89
+ from .physics import BoxCollider, SphereCollider, MeshCollider, RigidBody, raycast, RaycastHit, physics_world
90
+ from .models import load_model
91
+ from .builtin_textures import TextureManager, textures as texture_manager
92
+ from .ui import Text, Button, Slider, InputField, WindowPanel
93
+
94
+ # 补间动画
95
+ from .animation import (
96
+ Tween, Sequence, animate,
97
+ animate_position, animate_scale, animate_rotation, animate_color,
98
+ stop_all_tweens,
99
+ )
100
+
101
+ # 粒子
102
+ from .particles import Particles, ParticleSystem
103
+
104
+ # 音效
105
+ from .audio import Audio, Sound, Music, tone, beep, set_master_volume
106
+
107
+ # 触发区
108
+ from .trigger import Trigger, on_enter, on_exit, clear_triggers
109
+
110
+ # 存档 / 读档
111
+ from .saveload import (
112
+ save, load, save_slot, load_slot, slot_path,
113
+ has_save, has_slot, delete_save, list_saves,
114
+ )
115
+
116
+ # 三种视角控制器 + 天空盒:名字更短,直接 FirstPerson() / SecondPerson() / ThirdPerson()
117
+ from .prefabs import (
118
+ FirstPersonController,
119
+ SecondPersonController,
120
+ ThirdPersonController,
121
+ Sky,
122
+ )
123
+ FirstPerson = FirstPersonController
124
+ SecondPerson = SecondPersonController
125
+ ThirdPerson = ThirdPersonController
126
+
127
+ # ----------------------------------------------------------------------------
128
+ # 联机模块:host() 一行开房
129
+ # host(public=True) -> 公网联机
130
+ # host(public=False) -> 局域网联机
131
+ # ----------------------------------------------------------------------------
132
+ from .network import NetworkManager, NetworkPlayer
133
+
134
+ network = NetworkManager()
135
+
136
+
137
+ def host(public: bool = False, port: int = 5555, max_players: int = 16,
138
+ game_name: str = "Three-dimensional Game"):
139
+ """开房间。public=True 公网联机;public=False 局域网联机"""
140
+ _ensure_app()
141
+ return network.host(public=public, port=port, max_players=max_players, game_name=game_name)
142
+
143
+
144
+ def connect(ip: str = "127.0.0.1", port: int = 5555, player_name: str = "Player"):
145
+ """加入别人的房间"""
146
+ _ensure_app()
147
+ return network.join(host_ip=ip, port=port, player_name=player_name)
148
+
149
+
150
+ # ----------------------------------------------------------------------------
151
+ # 高级射线 / 虚拟碰撞:准星射线、穿透、boxcast/spherecast/capsulecast、调试线、Layer
152
+ # ----------------------------------------------------------------------------
153
+ from .raycaster import (
154
+ Raycaster, raycaster, Layer,
155
+ camera_ray, screen_ray, mouse_ray, raycast_all,
156
+ boxcast, spherecast, capsulecast,
157
+ overlap_box, overlap_sphere, linecast,
158
+ LineRenderer, draw_line, draw_ray,
159
+ hit_distance_to, nearest_hit, hits_with_tag,
160
+ )
161
+
162
+ # 手柄
163
+ from .gamepad import (
164
+ Gamepad, gamepad,
165
+ on_pad_press, on_pad_release, on_pad_axis, rumble, set_gamepad_deadzone,
166
+ )
167
+
168
+ # 语音:TTS 播报、麦克风录音、ASR 识别、联机语音
169
+ from .voice import (
170
+ speak, say,
171
+ record, stop_recording, recognize, listen,
172
+ VoiceChat, voice_chat,
173
+ is_mic_available, is_tts_available,
174
+ set_voice_volume, get_voice_volume,
175
+ VoiceRecorder,
176
+ )
177
+
178
+ # ----------------------------------------------------------------------------
179
+ # 常用工具函数
180
+ # ----------------------------------------------------------------------------
181
+ from .util import (
182
+ destroy,
183
+ invoke,
184
+ invoke_repeating,
185
+ random_range,
186
+ random_int,
187
+ random_color,
188
+ lerp,
189
+ lerp_color,
190
+ clamp,
191
+ distance,
192
+ sin,
193
+ cos,
194
+ tan,
195
+ time as get_time,
196
+ )
197
+
198
+ __all__ = [
199
+ "init", "run", "get_app", "Application",
200
+ "Entity",
201
+ "Vec2", "Vec3", "Vec4", "Color", "color",
202
+ "scene", "camera", "input_handler", "held_keys", "mouse",
203
+ "BoxCollider", "SphereCollider", "MeshCollider", "RigidBody", "raycast", "RaycastHit", "physics_world",
204
+ "load_model", "TextureManager", "texture_manager",
205
+ "Text", "Button", "Slider", "InputField", "WindowPanel",
206
+ "Tween", "Sequence", "animate",
207
+ "animate_position", "animate_scale", "animate_rotation", "animate_color", "stop_all_tweens",
208
+ "Particles", "ParticleSystem",
209
+ "Audio", "Sound", "Music", "tone", "beep", "set_master_volume",
210
+ "Trigger", "on_enter", "on_exit", "clear_triggers",
211
+ "save", "load", "save_slot", "load_slot", "slot_path",
212
+ "has_save", "has_slot", "delete_save", "list_saves",
213
+ "FirstPerson", "SecondPerson", "ThirdPerson", "Sky",
214
+ "FirstPersonController", "SecondPersonController", "ThirdPersonController",
215
+ "network", "NetworkManager", "NetworkPlayer", "host", "connect",
216
+ # 高级射线
217
+ "Raycaster", "raycaster", "Layer",
218
+ "camera_ray", "screen_ray", "mouse_ray", "raycast_all",
219
+ "boxcast", "spherecast", "capsulecast",
220
+ "overlap_box", "overlap_sphere", "linecast",
221
+ "LineRenderer", "draw_line", "draw_ray",
222
+ "hit_distance_to", "nearest_hit", "hits_with_tag",
223
+ # 手柄
224
+ "Gamepad", "gamepad",
225
+ "on_pad_press", "on_pad_release", "on_pad_axis", "rumble", "set_gamepad_deadzone",
226
+ # 语音
227
+ "speak", "say",
228
+ "record", "stop_recording", "recognize", "listen",
229
+ "VoiceChat", "voice_chat",
230
+ "is_mic_available", "is_tts_available",
231
+ "set_voice_volume", "get_voice_volume", "VoiceRecorder",
232
+ # 工具
233
+ "destroy", "invoke", "invoke_repeating",
234
+ "random_range", "random_int", "random_color",
235
+ "lerp", "lerp_color", "clamp", "distance",
236
+ "sin", "cos", "tan", "get_time",
237
+ ]
238
+
239
+ __version__ = "0.3.0"
240
+
241
+
242
+ # ----------------------------------------------------------------------------
243
+ # 兜底:如果脚本没写 run(),程序结束时自动运行
244
+ # (写了 run() 就完全走显式流程,不会重复运行)
245
+ # ----------------------------------------------------------------------------
246
+ def _auto_run():
247
+ if _explicit_run:
248
+ return
249
+ if _app is not None and not getattr(_app, "running", False):
250
+ try:
251
+ _app.run()
252
+ except Exception as exc: # pragma: no cover
253
+ print(f"[Three-dimensional-Games] 自动运行结束:{exc}")
254
+
255
+
256
+ atexit.register(_auto_run)
@@ -0,0 +1,321 @@
1
+ """
2
+ 动画 / 补间系统(Tween / Animation)
3
+ =====================================
4
+ 比 Ursina 的 animate_position / animate_scale 更通用,任意属性都能补间:
5
+
6
+ animate(entity, 'position', (5, 2, 0), duration=1.0, curve='ease_in_out')
7
+ animate_color(entity, color.red, duration=0.5)
8
+ animate_scale(entity, 3, duration=0.3)
9
+
10
+ 也支持把多个动画串起来顺序播放:
11
+
12
+ Sequence(
13
+ Tween(entity, 'position', (5, 0, 0), 1.0),
14
+ Tween(entity, 'position', (5, 5, 0), 1.0),
15
+ ).start()
16
+
17
+ 支持的插值曲线:
18
+ linear / ease_in / ease_out / ease_in_out
19
+ ease_in_cubic / ease_out_cubic / ease_in_out_cubic
20
+ smoothstep / bounce / elastic
21
+ """
22
+
23
+ from __future__ import annotations
24
+ import math
25
+ from typing import Any, Callable, Optional, Union
26
+
27
+ from .vector import Vec2, Vec3
28
+ from .color import Color
29
+
30
+ __all__ = [
31
+ "Tween", "Sequence", "animate", "animate_position",
32
+ "animate_scale", "animate_rotation", "animate_color",
33
+ "stop_all_tweens",
34
+ ]
35
+
36
+ # 记录所有正在运行的补间,方便统一停止
37
+ _active_tweens = []
38
+
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # 曲线
42
+ # ---------------------------------------------------------------------------
43
+ def evaluate_curve(curve: str, t: float) -> float:
44
+ """把 0~1 的进度 t 按曲线映射成 0~1 的插值参数"""
45
+ t = max(0.0, min(1.0, t))
46
+ if curve in (None, "linear"):
47
+ return t
48
+ if curve == "ease_in":
49
+ return t * t
50
+ if curve == "ease_out":
51
+ return 1 - (1 - t) * (1 - t)
52
+ if curve in ("ease_in_out", "smoothstep"):
53
+ return t * t * (3 - 2 * t)
54
+ if curve == "ease_in_cubic":
55
+ return t ** 3
56
+ if curve == "ease_out_cubic":
57
+ return 1 - (1 - t) ** 3
58
+ if curve == "ease_in_out_cubic":
59
+ return 4 * t ** 3 if t < 0.5 else 1 - (-2 * t + 2) ** 3 / 2
60
+ if curve == "bounce":
61
+ n1, d1 = 7.5625, 2.75
62
+ if t < 1 / d1:
63
+ return n1 * t * t
64
+ if t < 2 / d1:
65
+ t -= 1.5 / d1
66
+ return n1 * t * t + 0.75
67
+ if t < 2.5 / d1:
68
+ t -= 2.25 / d1
69
+ return n1 * t * t + 0.9375
70
+ t -= 2.625 / d1
71
+ return n1 * t * t + 0.984375
72
+ if curve == "elastic":
73
+ if t == 0 or t == 1:
74
+ return t
75
+ c4 = (2 * math.pi) / 3
76
+ return -(2 ** (10 * t - 10)) * math.sin((t * 10 - 10.75) * c4)
77
+ return t
78
+
79
+
80
+ # ---------------------------------------------------------------------------
81
+ # 值处理:把 float / Vec2 / Vec3 / Color / tuple 拆成能插值的形式
82
+ # ---------------------------------------------------------------------------
83
+ def _split(value):
84
+ if isinstance(value, Color):
85
+ return "color", (value.r, value.g, value.b, value.a)
86
+ if isinstance(value, Vec3):
87
+ return "vec3", (value.x, value.y, value.z)
88
+ if isinstance(value, Vec2):
89
+ return "vec2", (value.x, value.y)
90
+ if isinstance(value, (tuple, list)):
91
+ return "tuple", tuple(float(v) for v in value)
92
+ return "number", (float(value),)
93
+
94
+
95
+ def _rebuild(kind, comps):
96
+ if kind == "color":
97
+ return Color(*comps)
98
+ if kind == "vec3":
99
+ return Vec3(*comps)
100
+ if kind == "vec2":
101
+ return Vec2(*comps)
102
+ if kind == "tuple":
103
+ return tuple(comps)
104
+ return comps[0]
105
+
106
+
107
+ def _clone(value):
108
+ if isinstance(value, Color):
109
+ return Color(value.r, value.g, value.b, value.a)
110
+ if isinstance(value, Vec3):
111
+ return Vec3(value.x, value.y, value.z)
112
+ if isinstance(value, Vec2):
113
+ return Vec2(value.x, value.y)
114
+ if isinstance(value, (tuple, list)):
115
+ return tuple(value)
116
+ return value
117
+
118
+
119
+ def _interpolate(start, end, t):
120
+ kind_a, comps_a = _split(start)
121
+ kind_b, comps_b = _split(end)
122
+ if kind_a != kind_b:
123
+ # 类型不一致时不做插值,直接切换
124
+ return _clone(end) if t >= 1 else _clone(start)
125
+ comps = [comps_a[i] + (comps_b[i] - comps_a[i]) * t for i in range(len(comps_a))]
126
+ return _rebuild(kind_a, comps)
127
+
128
+
129
+ def _dt() -> float:
130
+ from .util import time as _time
131
+ return _time.dt
132
+
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # 单个补间
136
+ # ---------------------------------------------------------------------------
137
+ class Tween:
138
+ """
139
+ 对某个对象的某个属性做一段补间动画。
140
+
141
+ Tween(entity, 'position', (5, 2, 0), duration=1.0, curve='ease_in_out')
142
+
143
+ :param target: 要动的是谁(一般是一个 Entity)
144
+ :param attr: 要动哪个属性,如 'position' / 'scale' / 'rotation' / 'color'
145
+ :param end_value: 目标值
146
+ :param duration: 持续时间(秒)
147
+ :param curve: 缓动曲线
148
+ :param delay: 延迟多久开始
149
+ :param loop: 是否循环播放
150
+ :param ping_pong: 循环时是否来回往复(A→B→A)
151
+ :param on_complete: 播放结束后的回调
152
+ """
153
+
154
+ def __init__(self, target, attr: str, end_value, duration: float = 1.0,
155
+ curve: str = "linear", delay: float = 0.0, loop: bool = False,
156
+ ping_pong: bool = False, on_complete: Optional[Callable] = None):
157
+ self.target = target
158
+ self.attr = attr
159
+ self.end_value = end_value
160
+ self.duration = max(float(duration), 1e-6)
161
+ self.curve = curve
162
+ self.delay = float(delay)
163
+ self.loop = loop
164
+ self.ping_pong = ping_pong
165
+ self.on_complete = on_complete
166
+
167
+ self.elapsed = 0.0
168
+ self.is_running = False
169
+ self._forward = True
170
+ self._start_value = None
171
+ self._task = None
172
+
173
+ # -- 内部控制 ---------------------------------------------------------
174
+ def _current(self):
175
+ return getattr(self.target, self.attr)
176
+
177
+ def _step(self, task):
178
+ dt = _dt()
179
+ self.elapsed += dt
180
+
181
+ if self.elapsed < self.delay:
182
+ return task.cont
183
+
184
+ t = (self.elapsed - self.delay) / self.duration
185
+ if t >= 1.0:
186
+ t = 1.0
187
+
188
+ eased = evaluate_curve(self.curve, t)
189
+ a, b = (self._start_value, self.end_value) if self._forward \
190
+ else (self.end_value, self._start_value)
191
+ setattr(self.target, self.attr, _interpolate(a, b, eased))
192
+
193
+ if t >= 1.0:
194
+ if self.on_complete:
195
+ self.on_complete()
196
+ if self.loop:
197
+ self.elapsed = self.delay
198
+ if self.ping_pong:
199
+ self._forward = not self._forward
200
+ return task.cont
201
+ self.is_running = False
202
+ if self in _active_tweens:
203
+ _active_tweens.remove(self)
204
+ self._task = None
205
+ return task.done
206
+ return task.cont
207
+
208
+ # -- 对外 API ---------------------------------------------------------
209
+ def start(self):
210
+ """开始播放"""
211
+ from .application import Application
212
+ app = Application._instance
213
+ if app is None:
214
+ # 引擎还没起来(比如在导入阶段调),先记下起点,稍后由 animate() 兜底
215
+ self._start_value = _clone(self._current())
216
+ return self
217
+ self._start_value = _clone(self._current())
218
+ self.elapsed = 0.0
219
+ self.is_running = True
220
+ self._forward = True
221
+ if self not in _active_tweens:
222
+ _active_tweens.append(self)
223
+ self._task = app.taskMgr.add(self._step, f"tdg_tween_{id(self)}")
224
+ return self
225
+
226
+ def stop(self):
227
+ """立即停止"""
228
+ from .application import Application
229
+ app = Application._instance
230
+ if app is not None and self._task is not None:
231
+ try:
232
+ app.taskMgr.remove(self._task)
233
+ except Exception:
234
+ pass
235
+ self.is_running = False
236
+ self._task = None
237
+ if self in _active_tweens:
238
+ _active_tweens.remove(self)
239
+
240
+ @property
241
+ def finished(self) -> bool:
242
+ return not self.is_running
243
+
244
+
245
+ class Sequence:
246
+ """
247
+ 把多个 Tween 串起来,一个播完接着播下一个。
248
+
249
+ Sequence(
250
+ Tween(entity, 'position', (5, 0, 0), 1.0),
251
+ Tween(entity, 'scale', 2, 0.5),
252
+ ).start()
253
+ """
254
+
255
+ def __init__(self, *tweens, loop: bool = False):
256
+ self.tweens = list(tweens)
257
+ self.loop = loop
258
+ self._index = 0
259
+ self.is_running = False
260
+
261
+ def start(self):
262
+ self._index = 0
263
+ self.is_running = True
264
+ self._play_next()
265
+ return self
266
+
267
+ def _play_next(self):
268
+ if self._index >= len(self.tweens):
269
+ if self.loop:
270
+ self._index = 0
271
+ else:
272
+ self.is_running = False
273
+ return
274
+ tween = self.tweens[self._index]
275
+ original = tween.on_complete
276
+
277
+ def _on_done():
278
+ if original:
279
+ original()
280
+ self._index += 1
281
+ self._play_next()
282
+
283
+ tween.on_complete = _on_done
284
+ tween.loop = False
285
+ tween.start()
286
+
287
+ def stop(self):
288
+ for t in self.tweens:
289
+ t.stop()
290
+ self.is_running = False
291
+
292
+
293
+ # ---------------------------------------------------------------------------
294
+ # 便捷函数
295
+ # ---------------------------------------------------------------------------
296
+ def animate(target, attr: str, end_value, **kwargs) -> Tween:
297
+ """通用补间:把 target 的 attr 属性补间到 end_value"""
298
+ return Tween(target, attr, end_value, **kwargs).start()
299
+
300
+
301
+ def animate_position(target, end_value, duration: float = 1.0, **kwargs) -> Tween:
302
+ return Tween(target, "position", end_value, duration=duration, **kwargs).start()
303
+
304
+
305
+ def animate_scale(target, end_value, duration: float = 1.0, **kwargs) -> Tween:
306
+ return Tween(target, "scale", end_value, duration=duration, **kwargs).start()
307
+
308
+
309
+ def animate_rotation(target, end_value, duration: float = 1.0, **kwargs) -> Tween:
310
+ return Tween(target, "rotation", end_value, duration=duration, **kwargs).start()
311
+
312
+
313
+ def animate_color(target, end_value, duration: float = 1.0, **kwargs) -> Tween:
314
+ return Tween(target, "color", end_value, duration=duration, **kwargs).start()
315
+
316
+
317
+ def stop_all_tweens():
318
+ """停止所有正在运行的补间动画"""
319
+ for t in list(_active_tweens):
320
+ t.stop()
321
+ _active_tweens.clear()