pygamestudio 1.0.0.dev5__tar.gz → 1.0.0.dev7__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.
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/PKG-INFO +4 -4
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/README.md +2 -2
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/README_zh-CN.md +2 -2
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/pyproject.toml +2 -2
- pygamestudio-1.0.0.dev7/src/pygamestudio/api/__init__.py +7 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/api/core/audio.py +217 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/api/core/game.py +24 -2
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/api/core/scene.py +130 -4
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/i18n/languages/en.json +120 -4
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/i18n/languages/zh_CN.json +121 -5
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/i18n/translator.py +16 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/audios/game_start.mp3 +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/bucket.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/color_picker.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/eraser.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/fit_window_size.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/flip_horizontal.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/flip_vertical.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/frame_sequence.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/new.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/next.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/particle.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/pause.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/pen.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/polygon.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/previous.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/redo.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/refresh.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/rotate_ccw.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/rotate_cw.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/run.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/save.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/save_as.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/square.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/stop.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/undo.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/zoom_in.png +0 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/zoom_out.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/qss/dark.qss +157 -3
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/qss/light.qss +157 -4
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/resources.qrc +25 -2
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/resources_rc.py +2765 -999
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/templates/editor_template.pygs +5 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/templates/main_template.py +4 -3
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/templates/object_script_template.py +38 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/utils/constant.py +1 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/utils/path.py +7 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/common/utils/system.py +87 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/game/core/collision.py +169 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/core/command.py +43 -6
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/core/manager.py +376 -55
- pygamestudio-1.0.0.dev7/src/pygamestudio/game/object/__init__.py +11 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/game/object/base.py +708 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/object/button.py +54 -3
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/object/canvas.py +7 -3
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/object/ellipse.py +6 -2
- pygamestudio-1.0.0.dev7/src/pygamestudio/game/object/frame_sequence.py +261 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/object/image.py +12 -2
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/object/line.py +61 -2
- pygamestudio-1.0.0.dev7/src/pygamestudio/game/object/particle.py +265 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/game/object/polygon.py +126 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/game/object/rect.py +97 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/object/text.py +67 -11
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/game/object/type.py +4 -1
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/asset/menu.py +27 -6
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/asset/tree.py +144 -28
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/asset/window.py +8 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/audio_player/__init__.py +12 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/audio_player/engine.py +257 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/audio_player/widgets.py +129 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/audio_player/window.py +465 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/base/window.py +90 -43
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/code_editor/__init__.py +2 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/code_editor/completion.py +188 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/code_editor/editor.py +602 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/code_editor/highlighter.py +172 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/code_editor/menu.py +63 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/code_editor/window.py +250 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/console/browser.py +321 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/console/widget.py +2 -3
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/console/window.py +10 -3
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/config.py +5 -5
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/dialog.py +2 -1
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/list.py +29 -11
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/hierarchy/menu.py +13 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/hierarchy/tree.py +6 -6
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/image_editor/__init__.py +11 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/image_editor/canvas.py +538 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/image_editor/window.py +781 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/color.py +18 -11
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/component/checkbox.py +33 -1
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/component/combo.py +43 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/component/lineedit.py +499 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/component/spinbox.py +85 -1
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/component/widget.py +189 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/container.py +255 -16
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/layout/button.py +12 -1
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/layout/canvas.py +12 -1
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/layout/collision.py +90 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/layout/ellipse.py +12 -1
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/layout/frame_sequence.py +141 -0
- pygamestudio-1.0.0.dev5/src/pygamestudio/gui/inspector/layout/rect.py → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/layout/image.py +25 -2
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/layout/line.py +34 -1
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/layout/particle.py +185 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/layout/polygon.py +110 -0
- pygamestudio-1.0.0.dev5/src/pygamestudio/gui/inspector/layout/image.py → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector/layout/rect.py +8 -9
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/layout/text.py +20 -9
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/scene/gizmo.py +74 -11
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/scene/grid.py +11 -0
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/scene/guide.py +111 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/scene/screen.py +121 -20
- pygamestudio-1.0.0.dev7/src/pygamestudio/gui/scene/widget.py +75 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/settings/project.py +0 -2
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/window.py +83 -5
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/main.py +16 -0
- pygamestudio-1.0.0.dev5/src/pygamestudio/api/__init__.py +0 -4
- pygamestudio-1.0.0.dev5/src/pygamestudio/common/res/templates/editor_template.pygs +0 -4
- pygamestudio-1.0.0.dev5/src/pygamestudio/common/utils/constant.py +0 -1
- pygamestudio-1.0.0.dev5/src/pygamestudio/game/object/base.py +0 -119
- pygamestudio-1.0.0.dev5/src/pygamestudio/game/object/rect.py +0 -52
- pygamestudio-1.0.0.dev5/src/pygamestudio/gui/console/browser.py +0 -191
- pygamestudio-1.0.0.dev5/src/pygamestudio/gui/inspector/component/lineedit.py +0 -188
- pygamestudio-1.0.0.dev5/src/pygamestudio/gui/scene/widget.py +0 -28
- pygamestudio-1.0.0.dev5/src/pygamestudio/gui/settings/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/.github/FUNDING.yml +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/.gitignore +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/LICENSE +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/api/config/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/api/config/project.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/api/core/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/api/event/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/api/event/constant.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/api/sound → pygamestudio-1.0.0.dev7/src/pygamestudio/common}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/common → pygamestudio-1.0.0.dev7/src/pygamestudio/common/i18n}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/fonts/SIMHEI.ttf +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/add.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/add_item.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/browse.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/button.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/canvas.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/check.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/clear.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/close.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/create_project.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/down_arrow.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/ellipse.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/error.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/expand_or_collapse_all.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/eye_closed.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/eye_off.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/eye_open.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/heart.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/image.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/import_project.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/info.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/left_arrow.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/left_arrow_gray.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/line.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/logo.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/maximize.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/maximize_disabled.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/minimize.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/normalize.png +0 -0
- /pygamestudio-1.0.0.dev5/src/pygamestudio/common/res/images/run.png → /pygamestudio-1.0.0.dev7/src/pygamestudio/common/res/images/play.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/project_icon.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/rect.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/refresh_asset.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/right_arrow.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/right_arrow_gray.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/search.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/sort.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/text.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/tree_arrow_down.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/tree_arrow_right.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/up_arrow.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/images/warning.png +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/res/templates/project_template.pygs +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/utils/config.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/common/utils/theme.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/common/i18n → pygamestudio-1.0.0.dev7/src/pygamestudio/game}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/game → pygamestudio-1.0.0.dev7/src/pygamestudio/game/core}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/game/core → pygamestudio-1.0.0.dev7/src/pygamestudio/gui}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/gui → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/about}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/about/window.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/asset/delegate.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/asset/model.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/asset/search.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/asset/type.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/asset/widget.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/gui/about → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/build}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/build/desktop.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/build/window.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/gui/build → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/console}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/console/logger.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/console/menu.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/console/search.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/console/type.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/gui/console → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/dashboard}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/delegate.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/menu.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/model.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/search.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/type.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/widget.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/dashboard/window.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/gui/dashboard → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/hierarchy}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/hierarchy/delegate.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/hierarchy/search.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/hierarchy/widget.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/hierarchy/window.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/gui/hierarchy → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/inspector}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/component/button.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/component/combobox.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/component/label.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/component/picker.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/component/textedit.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/widget.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/inspector/window.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/main.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/gui/inspector → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/scene}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/scene/window.py +0 -0
- {pygamestudio-1.0.0.dev5/src/pygamestudio/gui/scene → pygamestudio-1.0.0.dev7/src/pygamestudio/gui/settings}/__init__.py +0 -0
- {pygamestudio-1.0.0.dev5 → pygamestudio-1.0.0.dev7}/src/pygamestudio/gui/settings/editor.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygamestudio
|
|
3
|
-
Version: 1.0.0.
|
|
4
|
-
Summary: Let's create
|
|
3
|
+
Version: 1.0.0.dev7
|
|
4
|
+
Summary: Let's create python games visually!
|
|
5
5
|
Project-URL: Documentation, https://pygamestudio.com/tutorial/installation
|
|
6
6
|
Project-URL: Homepage, https://pygamestudio.com
|
|
7
7
|
Project-URL: Github, https://github.com/pygamestudio/pygamestudio
|
|
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
English | [简体中文](https://github.com/pygamestudio/pygamestudio/blob/main/README_zh-CN.md)
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
|
-
**Pygame Studio** is a visual editor built
|
|
45
|
+
**Pygame Studio** is a visual editor built with **PyGame-ce** and **PySide6**, designed to help users create Python games visually, making it easier for beginners, students, and hobby developers to get started with game development.
|
|
46
46
|
|
|
47
47
|
<br>
|
|
48
48
|
|
|
@@ -114,7 +114,7 @@ Once created, the game project will be displayed in the Dashboard.
|
|
|
114
114
|
|
|
115
115
|
**3.** Double-click the created project to open the editing window and start your creation.
|
|
116
116
|
|
|
117
|
-
<img src="https://i-blog.csdnimg.cn/direct/
|
|
117
|
+
<img src="https://i-blog.csdnimg.cn/direct/f1dfa703ce8843b6bd5b62bb3c8b6cc3.png" width="800"/>
|
|
118
118
|
|
|
119
119
|
<br>
|
|
120
120
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
English | [简体中文](https://github.com/pygamestudio/pygamestudio/blob/main/README_zh-CN.md)
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
|
-
**Pygame Studio** is a visual editor built
|
|
16
|
+
**Pygame Studio** is a visual editor built with **PyGame-ce** and **PySide6**, designed to help users create Python games visually, making it easier for beginners, students, and hobby developers to get started with game development.
|
|
17
17
|
|
|
18
18
|
<br>
|
|
19
19
|
|
|
@@ -85,7 +85,7 @@ Once created, the game project will be displayed in the Dashboard.
|
|
|
85
85
|
|
|
86
86
|
**3.** Double-click the created project to open the editing window and start your creation.
|
|
87
87
|
|
|
88
|
-
<img src="https://i-blog.csdnimg.cn/direct/
|
|
88
|
+
<img src="https://i-blog.csdnimg.cn/direct/f1dfa703ce8843b6bd5b62bb3c8b6cc3.png" width="800"/>
|
|
89
89
|
|
|
90
90
|
<br>
|
|
91
91
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
[English](https://github.com/pygamestudio/pygamestudio) | 简体中文
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
|
-
**Pygame Studio**
|
|
16
|
+
**Pygame Studio** 是基于 **PyGame-ce** 和 **PySide6** 打造的可视化编辑器,支持以可视化方式制作 Python 游戏,让编程新手、在校学生以及业余开发者都能更轻松地入门游戏开发。
|
|
17
17
|
|
|
18
18
|
<br>
|
|
19
19
|
|
|
@@ -85,7 +85,7 @@ pygs
|
|
|
85
85
|
|
|
86
86
|
**3.** 双击已创建的项目,即可打开编辑窗口,开始你的游戏创作。
|
|
87
87
|
|
|
88
|
-
<img src="https://i-blog.csdnimg.cn/direct/
|
|
88
|
+
<img src="https://i-blog.csdnimg.cn/direct/f1dfa703ce8843b6bd5b62bb3c8b6cc3.png" width="800"/>
|
|
89
89
|
|
|
90
90
|
<br>
|
|
91
91
|
|
|
@@ -4,9 +4,9 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pygamestudio"
|
|
7
|
-
version = "1.0.0.
|
|
7
|
+
version = "1.0.0.dev7"
|
|
8
8
|
authors = [{ name="Louis Ren", email="louis@pygamestudio.com" }]
|
|
9
|
-
description = "Let's create
|
|
9
|
+
description = "Let's create python games visually!"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
license-files = ["LICEN[CS]E*"]
|
|
12
12
|
requires-python = ">=3.9"
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Audio manager API for generated games.
|
|
3
|
+
|
|
4
|
+
Provides a simple interface to play sound effects and background music:
|
|
5
|
+
|
|
6
|
+
import pygamestudio as studio
|
|
7
|
+
|
|
8
|
+
studio.play_sound('./audio/jump.wav')
|
|
9
|
+
studio.play_music('./audio/bgm.ogg', loops=-1)
|
|
10
|
+
studio.set_music_volume(0.5)
|
|
11
|
+
studio.stop_sound('./audio/jump.wav')
|
|
12
|
+
|
|
13
|
+
Sound files are resolved relative to the project path (the project's `audio/`
|
|
14
|
+
folder). The audio mixer is initialized lazily on first use; if no audio device
|
|
15
|
+
is available, all calls become no-ops with a warning instead of crashing.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import pygame
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from pygamestudio.common.i18n.translator import Translator as T
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AudioManager:
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self._is_available = False
|
|
27
|
+
self._sounds = {} # absolute path -> pygame.mixer.Sound
|
|
28
|
+
self._looping_channels = {} # absolute path -> Channel (looping sfx)
|
|
29
|
+
self._music_volume = 1.0
|
|
30
|
+
|
|
31
|
+
# ---------- internals ----------
|
|
32
|
+
|
|
33
|
+
def _init_mixer(self) -> bool:
|
|
34
|
+
"""Initialize pygame.mixer lazily. Returns False when audio is unavailable."""
|
|
35
|
+
if self._is_available:
|
|
36
|
+
return True
|
|
37
|
+
try:
|
|
38
|
+
if not pygame.mixer.get_init():
|
|
39
|
+
pygame.mixer.init()
|
|
40
|
+
self._is_available = True
|
|
41
|
+
except Exception as e:
|
|
42
|
+
print(T.tr('api.fail_to_init_mixer', 'Failed to initialize audio mixer: {}').format(e))
|
|
43
|
+
self._is_available = False
|
|
44
|
+
return self._is_available
|
|
45
|
+
|
|
46
|
+
def _resolve_path(self, sound_path) -> Path:
|
|
47
|
+
path = Path(sound_path)
|
|
48
|
+
if path.is_absolute():
|
|
49
|
+
return path
|
|
50
|
+
return Path(os.environ.get('PROJECT_PATH', '')) / path
|
|
51
|
+
|
|
52
|
+
def _load_sound(self, sound_path):
|
|
53
|
+
if not self._init_mixer():
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
absolute_path = self._resolve_path(sound_path)
|
|
57
|
+
if not absolute_path.exists():
|
|
58
|
+
print(T.tr('api.no_sound_path', 'The sound path {} does not exist.').format(absolute_path))
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
key = absolute_path.as_posix()
|
|
62
|
+
if key not in self._sounds:
|
|
63
|
+
try:
|
|
64
|
+
self._sounds[key] = pygame.mixer.Sound(str(absolute_path))
|
|
65
|
+
except Exception as e:
|
|
66
|
+
print(T.tr('api.fail_to_load_sound', 'Failed to load sound {}: {}').format(absolute_path, e))
|
|
67
|
+
return None
|
|
68
|
+
return self._sounds[key]
|
|
69
|
+
|
|
70
|
+
# ---------- sound effects ----------
|
|
71
|
+
|
|
72
|
+
def load_sound(self, sound_path):
|
|
73
|
+
"""Load (and cache) a sound effect. Returns a pygame Sound or None."""
|
|
74
|
+
return self._load_sound(sound_path)
|
|
75
|
+
|
|
76
|
+
def play_sound(self, sound_path, volume=1.0, loops=0, fade_ms=0):
|
|
77
|
+
"""
|
|
78
|
+
Play a sound effect. Returns the pygame Channel, or None on failure.
|
|
79
|
+
|
|
80
|
+
:param sound_path: path relative to the project (e.g. './audio/jump.wav')
|
|
81
|
+
:param volume: playback volume in [0.0, 1.0]
|
|
82
|
+
:param loops: 0 = play once, -1 = loop forever, N = play N+1 times
|
|
83
|
+
:param fade_ms: fade-in duration in milliseconds
|
|
84
|
+
"""
|
|
85
|
+
sound = self._load_sound(sound_path)
|
|
86
|
+
if sound is None:
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
channel = sound.play(loops=loops, fade_ms=fade_ms)
|
|
91
|
+
if channel:
|
|
92
|
+
channel.set_volume(max(0.0, min(1.0, float(volume))))
|
|
93
|
+
if loops != 0:
|
|
94
|
+
self._looping_channels[self._resolve_path(sound_path).as_posix()] = channel
|
|
95
|
+
return channel
|
|
96
|
+
except Exception as e:
|
|
97
|
+
print(T.tr('api.fail_to_play_sound', 'Failed to play sound {}: {}').format(self._resolve_path(sound_path), e))
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
def stop_sound(self, sound_path):
|
|
101
|
+
"""Stop a looping sound effect started with play_sound(loops=...)."""
|
|
102
|
+
key = self._resolve_path(sound_path).as_posix()
|
|
103
|
+
channel = self._looping_channels.pop(key, None)
|
|
104
|
+
if channel:
|
|
105
|
+
channel.stop()
|
|
106
|
+
|
|
107
|
+
def stop_all_sounds(self):
|
|
108
|
+
"""Stop every currently playing sound effect."""
|
|
109
|
+
if self._is_available:
|
|
110
|
+
pygame.mixer.stop()
|
|
111
|
+
self._looping_channels.clear()
|
|
112
|
+
|
|
113
|
+
# ---------- background music ----------
|
|
114
|
+
|
|
115
|
+
def play_music(self, music_path, loops=-1, fade_ms=0):
|
|
116
|
+
"""
|
|
117
|
+
Play a background music track (one stream at a time).
|
|
118
|
+
|
|
119
|
+
:param music_path: path relative to the project (e.g. './audio/bgm.ogg')
|
|
120
|
+
:param loops: -1 = loop forever (default), 0 = play once
|
|
121
|
+
:param fade_ms: fade-in duration in milliseconds
|
|
122
|
+
"""
|
|
123
|
+
if not self._init_mixer():
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
absolute_path = self._resolve_path(music_path)
|
|
127
|
+
if not absolute_path.exists():
|
|
128
|
+
print(T.tr('api.no_sound_path', 'The sound path {} does not exist.').format(absolute_path))
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
pygame.mixer.music.load(str(absolute_path))
|
|
133
|
+
pygame.mixer.music.set_volume(self._music_volume)
|
|
134
|
+
pygame.mixer.music.play(loops=loops, fade_ms=fade_ms)
|
|
135
|
+
return True
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print(T.tr('api.fail_to_play_music', 'Failed to play music {}: {}').format(absolute_path, e))
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
def stop_music(self, fade_ms=0):
|
|
141
|
+
"""Stop the background music (with an optional fade-out in ms)."""
|
|
142
|
+
if not self._is_available:
|
|
143
|
+
return
|
|
144
|
+
if fade_ms > 0:
|
|
145
|
+
pygame.mixer.music.fadeout(fade_ms)
|
|
146
|
+
else:
|
|
147
|
+
pygame.mixer.music.stop()
|
|
148
|
+
|
|
149
|
+
def pause_music(self):
|
|
150
|
+
if self._is_available:
|
|
151
|
+
pygame.mixer.music.pause()
|
|
152
|
+
|
|
153
|
+
def resume_music(self):
|
|
154
|
+
if self._is_available:
|
|
155
|
+
pygame.mixer.music.unpause()
|
|
156
|
+
|
|
157
|
+
def is_music_playing(self) -> bool:
|
|
158
|
+
if not self._is_available:
|
|
159
|
+
return False
|
|
160
|
+
return pygame.mixer.music.get_busy()
|
|
161
|
+
|
|
162
|
+
def set_music_volume(self, volume):
|
|
163
|
+
"""Set background music volume in [0.0, 1.0]."""
|
|
164
|
+
self._music_volume = max(0.0, min(1.0, float(volume)))
|
|
165
|
+
if self._is_available:
|
|
166
|
+
pygame.mixer.music.set_volume(self._music_volume)
|
|
167
|
+
|
|
168
|
+
def get_music_volume(self) -> float:
|
|
169
|
+
return self._music_volume
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# Module-level singleton, mirroring scene_loader.
|
|
173
|
+
audio_manager = AudioManager()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def load_sound(sound_path):
|
|
177
|
+
return audio_manager.load_sound(sound_path)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def play_sound(sound_path, volume=1.0, loops=0, fade_ms=0):
|
|
181
|
+
return audio_manager.play_sound(sound_path, volume, loops, fade_ms)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def stop_sound(sound_path):
|
|
185
|
+
return audio_manager.stop_sound(sound_path)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def stop_all_sounds():
|
|
189
|
+
return audio_manager.stop_all_sounds()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def play_music(music_path, loops=-1, fade_ms=0):
|
|
193
|
+
return audio_manager.play_music(music_path, loops, fade_ms)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def stop_music(fade_ms=0):
|
|
197
|
+
return audio_manager.stop_music(fade_ms)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def pause_music():
|
|
201
|
+
return audio_manager.pause_music()
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def resume_music():
|
|
205
|
+
return audio_manager.resume_music()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def is_music_playing() -> bool:
|
|
209
|
+
return audio_manager.is_music_playing()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def set_music_volume(volume):
|
|
213
|
+
return audio_manager.set_music_volume(volume)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def get_music_volume() -> float:
|
|
217
|
+
return audio_manager.get_music_volume()
|
|
@@ -3,10 +3,18 @@ import sys
|
|
|
3
3
|
import pygame
|
|
4
4
|
import inspect
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
from pygamestudio.api.core.scene import scene_loader
|
|
6
7
|
from pygamestudio.api.config.project import get_project_config
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class Game:
|
|
11
|
+
"""Runtime game class used by every built/exported project.
|
|
12
|
+
|
|
13
|
+
Subclass it in the project's main.py and override the on_* hooks. run()
|
|
14
|
+
starts the main loop: it auto-detects the project root from the caller's
|
|
15
|
+
file, initializes pygame, dispatches every pygame event to the matching
|
|
16
|
+
on_* hook, then calls on_update(delta_time) and flips the display.
|
|
17
|
+
"""
|
|
10
18
|
_instance = None
|
|
11
19
|
|
|
12
20
|
def __init__(self):
|
|
@@ -17,6 +25,11 @@ class Game:
|
|
|
17
25
|
self._running = False
|
|
18
26
|
|
|
19
27
|
def _init_game(self):
|
|
28
|
+
"""Set up pygame and resolve the project root from the calling file.
|
|
29
|
+
|
|
30
|
+
The project path is derived from the stack frame of whoever created
|
|
31
|
+
the Game (the project's main.py), so no path config is required.
|
|
32
|
+
"""
|
|
20
33
|
caller_frame = inspect.stack()[-1]
|
|
21
34
|
caller_file_path = caller_frame.filename
|
|
22
35
|
self._project_path = Path(caller_file_path).parent.resolve().as_posix()
|
|
@@ -29,6 +42,7 @@ class Game:
|
|
|
29
42
|
self._clock = pygame.time.Clock()
|
|
30
43
|
|
|
31
44
|
def run(self):
|
|
45
|
+
"""Start the game loop. Blocks until the window is closed."""
|
|
32
46
|
Game._instance = self
|
|
33
47
|
self._init_game()
|
|
34
48
|
self._running = True
|
|
@@ -36,6 +50,7 @@ class Game:
|
|
|
36
50
|
self.on_start()
|
|
37
51
|
|
|
38
52
|
while self._running:
|
|
53
|
+
# --- 1. Dispatch all pending pygame events to the on_* hooks. ---
|
|
39
54
|
for event in pygame.event.get():
|
|
40
55
|
# ---------- Quit Event ----------
|
|
41
56
|
if event.type == pygame.QUIT:
|
|
@@ -55,7 +70,7 @@ class Game:
|
|
|
55
70
|
elif event.type == pygame.MOUSEBUTTONUP:
|
|
56
71
|
self.on_mouse_button_up(event.pos, event.button, getattr(event, 'touch', 0), getattr(event, "clicks", 1), getattr(event, 'window', None))
|
|
57
72
|
elif event.type == pygame.MOUSEWHEEL:
|
|
58
|
-
self.on_mouse_wheel(event.
|
|
73
|
+
self.on_mouse_wheel(event.flipped, event.x, event.y, getattr(event, 'touch', 0), event.precise_x, event.precise_y, getattr(event, 'window', None))
|
|
59
74
|
|
|
60
75
|
# ---------- Joystick Event ----------
|
|
61
76
|
elif event.type == pygame.JOYAXISMOTION:
|
|
@@ -181,9 +196,16 @@ class Game:
|
|
|
181
196
|
|
|
182
197
|
|
|
183
198
|
delta_time = self._clock.tick(self._fps) / 1000
|
|
199
|
+
# Make the current frame's delta time available to the scene's
|
|
200
|
+
# scripts (their on_update(delta_time) hooks).
|
|
201
|
+
scene_loader._set_delta_time(delta_time)
|
|
202
|
+
# --- 2. Fixed/step logic: user update + draw, then present. ---
|
|
184
203
|
self.on_update(delta_time)
|
|
185
204
|
pygame.display.flip()
|
|
186
205
|
|
|
206
|
+
# Let every attached behavior script run its on_destroy() cleanup hook
|
|
207
|
+
# before the game exits (mirrors on_start fired on scene load).
|
|
208
|
+
scene_loader._destroy_scripts()
|
|
187
209
|
self.on_quit()
|
|
188
210
|
pygame.quit()
|
|
189
211
|
sys.exit()
|
|
@@ -215,7 +237,7 @@ class Game:
|
|
|
215
237
|
def on_mouse_button_up(self, pos, btn, touch=0, clicks=1, window=None):
|
|
216
238
|
pass
|
|
217
239
|
|
|
218
|
-
def on_mouse_wheel(self,
|
|
240
|
+
def on_mouse_wheel(self, flipped, x, y, touch=0, precise_x=0.0, precise_y=0.0, window=None):
|
|
219
241
|
pass
|
|
220
242
|
|
|
221
243
|
# ---------- Joystick Event Hooks ----------
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import sys
|
|
2
3
|
import re
|
|
3
4
|
import json
|
|
5
|
+
import inspect
|
|
6
|
+
import importlib.util
|
|
4
7
|
import pygame
|
|
5
8
|
from pathlib import Path
|
|
6
9
|
from pygamestudio.game.object.type import *
|
|
@@ -9,16 +12,29 @@ from pygamestudio.game.object.canvas import *
|
|
|
9
12
|
from pygamestudio.game.object.text import *
|
|
10
13
|
from pygamestudio.game.object.ellipse import *
|
|
11
14
|
from pygamestudio.game.object.line import *
|
|
15
|
+
from pygamestudio.game.object.polygon import *
|
|
12
16
|
from pygamestudio.game.object.image import *
|
|
13
17
|
from pygamestudio.game.object.button import *
|
|
18
|
+
from pygamestudio.game.object.particle import *
|
|
19
|
+
from pygamestudio.game.object.frame_sequence import *
|
|
14
20
|
from pygamestudio.api.config.project import get_project_config
|
|
15
21
|
from pygamestudio.common.i18n.translator import Translator as T
|
|
16
22
|
|
|
17
23
|
|
|
18
24
|
class SceneLoader:
|
|
25
|
+
"""Runtime mirror of the editor's scene tree.
|
|
26
|
+
|
|
27
|
+
Reads the .scene JSON produced by the editor and rebuilds the same object
|
|
28
|
+
tree with is_for_api=True objects, rendering them each frame and driving
|
|
29
|
+
the attached behavior scripts (on_start / on_update / on_destroy).
|
|
30
|
+
"""
|
|
31
|
+
|
|
19
32
|
def __init__(self):
|
|
20
33
|
self._current_scene_path = ''
|
|
21
34
|
self._all_object_tree_struct = {}
|
|
35
|
+
# Seconds elapsed since the last frame, updated by Game.run() each
|
|
36
|
+
# frame and passed to the scripts' on_update(delta_time) hooks.
|
|
37
|
+
self._delta_time = 0
|
|
22
38
|
|
|
23
39
|
def load_scene(self, screen_surface:pygame.Surface, scene_path:str=''):
|
|
24
40
|
if not scene_path and self._current_scene_path or scene_path and scene_path==self._current_scene_path:
|
|
@@ -35,6 +51,9 @@ class SceneLoader:
|
|
|
35
51
|
if not Path(scene_path).exists():
|
|
36
52
|
raise RuntimeError(T.tr('api.no_scene_path', 'The scene path {} does not exist.').format(scene_path))
|
|
37
53
|
|
|
54
|
+
if self._all_object_tree_struct:
|
|
55
|
+
# The current scene is about to be replaced; let its scripts clean up.
|
|
56
|
+
self._destroy_scripts()
|
|
38
57
|
self._all_object_tree_struct = {}
|
|
39
58
|
self._current_scene_path = scene_path
|
|
40
59
|
with open(scene_path, 'r', encoding='utf-8') as f:
|
|
@@ -55,6 +74,8 @@ class SceneLoader:
|
|
|
55
74
|
_l(key, child_object_tree_struct)
|
|
56
75
|
|
|
57
76
|
_l('', scene_data)
|
|
77
|
+
# The whole object tree now exists; fire the scripts' on_start hooks.
|
|
78
|
+
self._start_scripts()
|
|
58
79
|
self._update_scene(screen_surface)
|
|
59
80
|
|
|
60
81
|
def _add(self, parent_uuid, object_type, object_data={}):
|
|
@@ -78,13 +99,73 @@ class SceneLoader:
|
|
|
78
99
|
obj = ObjectText(self, object_data, is_for_api=True)
|
|
79
100
|
elif object_type == OBJECT_ELLIPSE:
|
|
80
101
|
obj = ObjectEllipse(self, object_data, is_for_api=True)
|
|
102
|
+
elif object_type == OBJECT_POLYGON:
|
|
103
|
+
obj = ObjectPolygon(self, object_data, is_for_api=True)
|
|
81
104
|
elif object_type == OBJECT_LINE:
|
|
82
105
|
obj = ObjectLine(self, object_data, is_for_api=True)
|
|
83
106
|
elif object_type == OBJECT_IMAGE:
|
|
84
107
|
obj = ObjectImage(self, object_data, is_for_api=True)
|
|
85
108
|
elif object_type == OBJECT_BUTTON:
|
|
86
109
|
obj = ObjectButton(self, object_data, is_for_api=True)
|
|
110
|
+
elif object_type == OBJECT_PARTICLE:
|
|
111
|
+
obj = ObjectParticle(self, object_data, is_for_api=True)
|
|
112
|
+
elif object_type == OBJECT_FRAME_SEQUENCE:
|
|
113
|
+
obj = ObjectFrameSequence(self, object_data, is_for_api=True)
|
|
114
|
+
else:
|
|
115
|
+
raise RuntimeError(T.tr('api.unknown_object_type', 'Unknown object type: {}').format(object_type))
|
|
116
|
+
|
|
117
|
+
# Attach the behavior script (if any) so the script's lifecycle hooks
|
|
118
|
+
# can be driven later (see ObjectBase._start/_update_surface/_destroy).
|
|
119
|
+
obj.script_instance = self._load_script(obj)
|
|
87
120
|
return obj
|
|
121
|
+
|
|
122
|
+
def _load_script(self, obj):
|
|
123
|
+
"""
|
|
124
|
+
Dynamically load and instantiate the object's behavior script.
|
|
125
|
+
|
|
126
|
+
The script module (obj.script_path, relative to the project) is loaded
|
|
127
|
+
with importlib, the first user-defined class is instantiated with
|
|
128
|
+
owner=obj, and the instance is stored on obj.script_instance. Returns
|
|
129
|
+
None when no script is attached or it fails to load (the failure is
|
|
130
|
+
reported to stderr but does not stop the game).
|
|
131
|
+
"""
|
|
132
|
+
if not obj.script_path:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
script_absolute_path = Path(os.environ.get('PROJECT_PATH', '')) / obj.script_path
|
|
136
|
+
if not script_absolute_path.exists():
|
|
137
|
+
print(T.tr('api.no_script_path', 'The script path {} does not exist.').format(script_absolute_path), file=sys.stderr)
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
# A unique module name per object keeps every scene (re)load fresh,
|
|
142
|
+
# so the latest script content is always picked up.
|
|
143
|
+
module_name = f'pygamestudio_runtime_script_{obj.uuid}'
|
|
144
|
+
spec = importlib.util.spec_from_file_location(module_name, script_absolute_path)
|
|
145
|
+
module = importlib.util.module_from_spec(spec)
|
|
146
|
+
spec.loader.exec_module(module)
|
|
147
|
+
except Exception:
|
|
148
|
+
print(T.tr('api.fail_to_load_script', 'Failed to load script {}: {}').format(script_absolute_path, e), file=sys.stderr)
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
script_class = self._get_script_class(module)
|
|
152
|
+
if script_class is None:
|
|
153
|
+
print(T.tr('api.no_script_class', 'No script class found in {}.').format(script_absolute_path), file=sys.stderr)
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
return script_class(obj)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
print(T.tr('api.fail_to_init_script', 'Failed to initialize script {}: {}').format(script_absolute_path, e), file=sys.stderr)
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
def _get_script_class(module):
|
|
164
|
+
"""Return the first class defined in the given script module (ignores imports)."""
|
|
165
|
+
for name, value in module.__dict__.items():
|
|
166
|
+
if inspect.isclass(value) and value.__module__ == module.__name__:
|
|
167
|
+
return value
|
|
168
|
+
return None
|
|
88
169
|
|
|
89
170
|
def _add_object_tree_struct(self, parent_uuid, object_tree_struct_to_add):
|
|
90
171
|
if not self._all_object_tree_struct:
|
|
@@ -114,16 +195,61 @@ class SceneLoader:
|
|
|
114
195
|
def _update(object_tree_struct, parent_surface):
|
|
115
196
|
value = list(object_tree_struct.values())[0]
|
|
116
197
|
obj = value['object']
|
|
117
|
-
obj.
|
|
198
|
+
obj._update_surface()
|
|
199
|
+
self._update_script(obj)
|
|
118
200
|
|
|
119
|
-
if obj.
|
|
201
|
+
if obj.visible:
|
|
120
202
|
for child_object_tree_struct in value['children']:
|
|
121
|
-
_update(child_object_tree_struct, obj.
|
|
203
|
+
_update(child_object_tree_struct, obj._get_surface())
|
|
122
204
|
|
|
123
|
-
obj.
|
|
205
|
+
obj._draw(parent_surface)
|
|
124
206
|
|
|
125
207
|
_update(self._all_object_tree_struct, screen_surface)
|
|
126
208
|
|
|
209
|
+
def _set_delta_time(self, delta_time):
|
|
210
|
+
"""Set the per-frame delta time (seconds) passed to scripts' on_update."""
|
|
211
|
+
self._delta_time = delta_time
|
|
212
|
+
|
|
213
|
+
def _start_scripts(self):
|
|
214
|
+
"""Fire on_start() on every attached script (after the scene is loaded)."""
|
|
215
|
+
for obj in self._iter_objects():
|
|
216
|
+
if not obj.script_instance:
|
|
217
|
+
continue
|
|
218
|
+
try:
|
|
219
|
+
obj.script_instance.on_start()
|
|
220
|
+
except Exception as e:
|
|
221
|
+
print(T.tr('api.script_start_error', 'Script on_start error for {}: {}').format(obj.name, e), file=sys.stderr)
|
|
222
|
+
|
|
223
|
+
def _update_script(self, obj):
|
|
224
|
+
"""Fire on_update(delta_time) on an object's attached script (per frame)."""
|
|
225
|
+
if not obj.script_instance:
|
|
226
|
+
return
|
|
227
|
+
try:
|
|
228
|
+
obj.script_instance.on_update(self._delta_time)
|
|
229
|
+
except Exception as e:
|
|
230
|
+
print(T.tr('api.script_update_error', 'Script on_update error for {}: {}').format(obj.name, e), file=sys.stderr)
|
|
231
|
+
|
|
232
|
+
def _destroy_scripts(self):
|
|
233
|
+
"""Fire on_destroy() on every attached script (before the scene is replaced)."""
|
|
234
|
+
for obj in self._iter_objects():
|
|
235
|
+
if not obj.script_instance:
|
|
236
|
+
continue
|
|
237
|
+
try:
|
|
238
|
+
obj.script_instance.on_destroy()
|
|
239
|
+
except Exception as e:
|
|
240
|
+
print(T.tr('api.script_destroy_error', 'Script on_destroy error for {}: {}').format(obj.name, e), file=sys.stderr)
|
|
241
|
+
|
|
242
|
+
def _iter_objects(self):
|
|
243
|
+
"""Yield every object in the current scene tree (depth-first)."""
|
|
244
|
+
def _gen(object_tree_struct):
|
|
245
|
+
value = list(object_tree_struct.values())[0]
|
|
246
|
+
yield value['object']
|
|
247
|
+
for child_object_tree_struct in value['children']:
|
|
248
|
+
yield from _gen(child_object_tree_struct)
|
|
249
|
+
|
|
250
|
+
if self._all_object_tree_struct:
|
|
251
|
+
yield from _gen(self._all_object_tree_struct)
|
|
252
|
+
|
|
127
253
|
def _get_object_tree_struct_by_path(self, object_path):
|
|
128
254
|
def _get(name, target_item_index, current_item_index, recursion_time, part_number, object_tree_struct):
|
|
129
255
|
key = list(object_tree_struct.keys())[0]
|