diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..cc67606
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "python.linting.pylintEnabled": true,
+ "python.linting.enabled": true
+}
\ No newline at end of file
diff --git a/Editor/Scripts/create_wind_forces_tutorial.py b/Editor/Scripts/create_wind_forces_tutorial.py
deleted file mode 100644
index 004d0a5..0000000
--- a/Editor/Scripts/create_wind_forces_tutorial.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""
-Copyright (c) Contributors to the Open 3D Engine Project.
-For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
-SPDX-License-Identifier: Apache-2.0 OR MIT
-"""
-
-from tutorial import Tutorial, TutorialStep
-
-class WindForcesTutorial(Tutorial):
- def __init__(self):
- super(WindForcesTutorial, self).__init__()
-
- self.title = "Create Wind Forces"
-
- self.add_step(TutorialStep("Create Wind Forces", """
Greetings!
We can use a PhysX Force Region component to
- create both global and local wind forces. Wind forces affect entities with components that are affected by wind,
- such as cloth. Wind forces don't affect PhysX Rigid Body components.
- For this tutorial, make sure that you have the Nvidia Cloth Gem enabled in your project.
-
Click next to continue.
"""))
- self.add_step(TutorialStep("Create a wind provider entity", """First, create an entity
- for the wind provider. Next, add a Tag component for the entity. The Tag will specify whether the
- wind is a global or a local force. Edit the tag value to specify the wind type by choosing PhysX Configuration
- from the Tools menu and toggling the Wind Configuration.
- Wind Configuration includes the Global wind tag and the Local wind tag. """, "EntityOutlinerWidgetUI"))
- self.add_step(TutorialStep("Update the Shape", """Set the PhysX Collider component's Shape
- property to Box.
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Scale the Entity", """Adjust the Box Dimensions
- to your specifications. If you're creating a localized wind force, make the dimenesions large enough that they can
- contain the entity that receives the wind force.
""", "InspectorMainWindow"))
- self.add_step(TutorialStep("Position the Entity", """ Use the Move tool
- to position the entity in the level. For instance, you might consider positioning the entity so that
- the bottom of the box is level with the ground.
""", "InspectorMainWindow"))
- self.add_step(TutorialStep("Add a PhysX Region component to the entity", """
- Select the Add button located next to Forces. Then, in the Direction property
- of your new force, set the Y component to -1.0, and the Z component to 0.0, to create
- a direction for the wind. The PhysX collider box displays cones indicating the direction of the wind force.
- Set the Magnitude to 10.0.
""", "InspectorMainWindow"))
- self.add_step(TutorialStep("Add a Cloth Prefab", """We'll test the wind provider by adding
- a NVIDIA Cloth Mesh. In Asset Browser, navigate to Gems\NvCloth\Assets\prefabs\Cloth, then locate
- cloth_locked_edge.prefab, and drag that asset into the viewport.
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Position the Prefab", """Use the Move tool to place the cloth prefab.
- Once the prefab's in position, you can hide the wind provider entity. In Entity Outliner, in the column to the
- right of the wind provider entity, toggle the Show/Hide Entity setting.
Note: if you're using the
- Local wind tag, you must place the cloth asset inside the PhysX Collider volume of the wind provider entity.
-
""", "EntityOutlinerWidgetUI"))
- self.add_step(TutorialStep("Edit the Prefab", """The cloth prefab has a local wind property
- enabled which generates a local wind force that overrides our wind provider entity. We will deactiate the local
- wind force of the prefab so that we can view the results of the wind provider we created.
- In Entity Outliner, double-click the cloth_locked_edge prefab to edit it in Focus Mode, and select the cloth_locked_edge
- entity. Then, in the Cloth component of the Entity Inspector, expand the Wind propoerty group and deselect
- Enable local wind velocity.
""", "EntityOutlinerWidgetUI"))
- self.add_step(TutorialStep("Run the simulation", """We can now run the
- simualtion and view the results. With the cloth_locked_edge prefab open for editing in Focus mode, select the
- Simulate in editor option located at the top of the Cloth component. As the simulation begins, the cloth object might flip and
- stretch wildly, but it will quickly settle into a breezy wind simulation. The simulation plays while in editor mode, so you can
- adjust various properties within the Cloth component and view the results in real time.
Consider adjusting the:
- Air drag coefficient Air lift coefficientAir density
""","InspectorMainWindow"))
- def on_tutorial_start(self):
- print("Starting Wind Forces tutorial.")
-
- def on_tutorial_end(self):
- print("Wind Forces tutorial complete!")
-
diff --git a/Editor/Scripts/customize_mesh_asset_processing.py b/Editor/Scripts/customize_mesh_asset_processing.py
deleted file mode 100644
index b64bcd2..0000000
--- a/Editor/Scripts/customize_mesh_asset_processing.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""
-Copyright (c) Contributors to the Open 3D Engine Project.
-For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
-SPDX-License-Identifier: Apache-2.0 OR MIT
-"""
-
-from tutorial import Tutorial, TutorialStep
-
-class CustomizeMeshAssetProcessingTutorial(Tutorial):
- def __init__(self):
- super(CustomizeMeshAssetProcessingTutorial, self).__init__()
-
- self.title = "Customize Mesh Asset Processing"
-
- self.add_step(TutorialStep("Customize Mesh Asset Processing",
- """Greetings!
Meshes refer to the external appearances
- of objects.
In this tutorial, we'll edit the mesh of a sphere by enlarging it. We'll also introduce
- some other ways you can edit the mesh.
Click next to continue.
"""))
- self.add_step(TutorialStep("Preparing the scene", """The Asset Processor runs
- in the background automatically detecting source assets and scheduling process jobs for them.
-
In Asset Browser, search for the sphere.fbx source asset and
- right click to expand it. Select 'Edit Settings'.
-
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Edit the Scene Settings", """By default,
- all the meshes in a source asset are processed as a single group, each of which produces a set of product assets. Let's create additional mesh groups for our sphere source asset by
- choosing Add another mesh.
- The Name mesh property contains the name of the source asset.
- The Select meshes property reads All meshes selected.
- You can choose the file select button to select which meshes to include in the mesh group.
- For this tutorial, you can use the default mesh group with all meshes selected.
-
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Change the Coordinate System", """Let’s add a modifier to customize
- how the asset is processed. Choose the Add Modifier button to view the mesh modifier list
- and select Coordinate system change.
- The Coordinate system change mesh modifier is used to scale or transform the asset for scenarios
- where the asset might be too small, too large, or incorrectly oriented in O3DE.
- By default, the modifier provides a single option to rotate the mesh 180 degrees.
- Activate the Use advanced settings toggle to expose the advanced modifier settings.
- Let’s customize the scale of the asset. Set the Scale property to 5.0 to scale the asset to five times its size.
-
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("View Changes", """To use the prefab in our
- level we need to create an instance, or instantiate the prefab in the level.
Drag the
- 20-sided-dice.prefab into the
- viewport to instatiate it in the level.
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Position the prefab", """
- Choose the Update button at the bottom-right of Scene Settings.
- This creates or updates the .assetinfo sidecar file and triggers Asset Processor to reprocess the asset.
- Drag the .azmodel product asset from Asset Browser into the viewport.
-
""", "renderOverlay"))
-
- def on_tutorial_start(self):
- print("Starting Mesh Asset Process tutorial.")
-
- def on_tutorial_end(self):
- print("Mesh Asset Process tutorial complete!")
-
diff --git a/Editor/Scripts/decompose_input_meshes.py b/Editor/Scripts/decompose_input_meshes.py
deleted file mode 100644
index c9149ec..0000000
--- a/Editor/Scripts/decompose_input_meshes.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-Copyright (c) Contributors to the Open 3D Engine Project.
-For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
-SPDX-License-Identifier: Apache-2.0 OR MIT
-"""
-
-from tutorial import Tutorial, TutorialStep
-
-class DecomposeInputMeshes(Tutorial):
- def __init__(self):
- super(DecomposeInputMeshes, self).__init__()
-
- self.title = "Decompose Input Meshes"
-
- self.add_step(TutorialStep("Decompose Input Meshes",
- """Greetings!
Assets composed of multiple and/or complex meshes,
- such as some kinematic or dynamic entities, might require similarly complex PhysX collider assets. In these cases,
- you can decompose the input mesh into convex parts. You can automatically generate primitive or convex colliders,
- fit them to each part, and process them as collider .pxmesh product assets.
- Mesh decomposition is part of the process of
- generating and fitting primitive or convex collider assets, and it doesn’t alter the input mesh.
- .
Click next to continue.
"""))
- self.add_step(TutorialStep("Select the Asset", """Locate your source asset in the
- Asset Browser. You can use your own or use one of the provided .fbx files, such as sphere.fbx.
- Right click the .fbx source asset and select 'Edit Settings.'
-
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Decompose Meshes", """In Scene Settings, underneath
- PhysX Mesh Group, choose to Export As either Primitive or Convex. Then enable
- Decompose Meshes.
-
For primitive colliders, the input mesh is decomposed into primitive parts. The best fitting primitive shapes
- are automatically selected and transformed to encompass each part and the primitives are processed as a
- product asset. For convex colliders, the input mesh is decomposed into convex parts.
- A convex hull is generated for each part and the convex hulls are processed as a collider product asset.
- In general, collider assets generated with decomposed meshes provide a more accurate representation of
- the render mesh than a single primitive or convex collider can. """, "EntityOutlinerWidgetUI"))
-
- def on_tutorial_start(self):
- print("Starting tutorial to decompose input meshes.")
-
- def on_tutorial_end(self):
- print("'Decompose input meshes' tutorial complete!")
diff --git a/Editor/Scripts/demo_tutorial.py b/Editor/Scripts/demo_tutorial.py
index 9726b15..0fdd519 100644
--- a/Editor/Scripts/demo_tutorial.py
+++ b/Editor/Scripts/demo_tutorial.py
@@ -21,6 +21,7 @@ def on_step_start(self):
def on_step_end(self):
print("Ended the select Entity step")
+
class DemoTutorial(Tutorial):
def __init__(self):
diff --git a/Editor/Scripts/editor_tutorial.py b/Editor/Scripts/editor_tutorial.py
new file mode 100644
index 0000000..030f801
--- /dev/null
+++ b/Editor/Scripts/editor_tutorial.py
@@ -0,0 +1,134 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
+"""
+import azlmbr.bus as bus # type: ignore
+import azlmbr.editor as editor # type: ignore
+import azlmbr.entity # type: ignore
+from azlmbr.entity import EntityId # type: ignore
+from PySide2.QtWidgets import QMenuBar
+from tutorial import Tutorial, TutorialStep
+
+
+# Example of a custom step that overrides the start/end hooks
+class SelectEntityStep(TutorialStep):
+ def __init__(self):
+ super(SelectEntityStep, self).__init__("Select an Entity",
+ "Next, select any Entity in the Entity Outliner", "EntityOutlinerWidgetUI")
+
+ def on_step_start(self):
+ print("Starting X from AutomatedTesting")
+ print("Starting the select Entity step")
+ DemoTutorial.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId())
+
+ def on_step_end(self):
+ print("Ended the select Entity step")
+
+class DemoTutorial(Tutorial):
+ def __init__(self):
+ super(DemoTutorial, self).__init__()
+
+ self.title = "Demo Tutorial"
+
+ self.add_step(TutorialStep("First things first", "Welcome! This first step shouldn't highlight any widget."))
+ self.add_step(SelectEntityStep())
+ self.add_step(TutorialStep("Add a component", "Use the Add Component button to add a component to the Entity", "m_addComponentButton"))
+ self.add_step(TutorialStep("Cool menu bar", "This step is just to showcase highlighting an item without a direct name but by using a type pattern instead", {"type": QMenuBar}))
+
+ def on_tutorial_start(self):
+ print("Where we're going, we don't need roads")
+ rootentity = DemoTutorial.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId())
+
+ def on_tutorial_end(self):
+ print("Follow the yellow brick road")
+
+# Interactive tutorial version of this Editor tour:
+# https://www.o3de.org/docs/welcome-guide/tours/editor-tour/
+class IntroTutorial(Tutorial):
+ def __init__(self):
+ super(IntroTutorial, self).__init__()
+
+ self.title = "Intro to the Editor"
+
+ self.add_step(TutorialStep("Introduction", """The default layout of O3DE Editor contains the most commonly used tools in a configuration, similar to other content creation applications. The core workflow of O3DE is to create and place entities in a level, so the default layout contains a menu bar, toolbars, panes, and tool tabs focused on entity creation and placement.
+
+You can customize the layout through drag and drop, and save to a custom layout through the Layouts option in the View menu of the main menu bar. Drag the separator bars between panes to resize the panes. Drag the title bar of a pane to tear off the pane. The pane can be dropped anywhere in the layout or dropped outside of O3DE Editor as its own window. To restore the default layout, in the main menu bar choose View > Layouts > Default Layout.
+"""))
+
+ self.add_step(TutorialStep("Menu Bar", """Near the top of O3DE Editor are the Menu Bar and the Tool Bar.
+
+The Menu Bar contains several familiar menus:
+
+ - File - File menu items include actions for opening and saving levels, managing editor and project settings, and creating and opening projects.
+ - Edit - Edit menu items include actions for working with selections such as duplicate, delete, hide and show selection, and working with selection transforms.
+ - Game - Game menu items include actions for running the project, enabling in editor simulation, enabling and refreshing audio, and debugging.
+ - Tools - Tools menu items provide access to all of O3DE’s tools and editors.
+ - View - View menu items include actions to configure both the Perspective viewport and O3DE Editor layout.
+ - AWS - AWS menu items include tools and links to documentation for working with AWS in your O3DE projects.
+ - Help - Help menu items provide links to O3DE community and documentation resources.
+
+""", {"type": QMenuBar}))
+
+ self.add_step(TutorialStep("Toolbar", """
+The Tool Bar provides easy access to various editor tools and features. On the left are buttons to open various O3DE tools and editors, on the right are controls to run your project or activate simulation in editor. The Tool Bar is docked at the top of the editor by default, but you can also dock it vertically on the edges of the editor. To customize the toolbar, right-click anywhere on the toolbar and select Customize from the context menu. You can choose which toolbars to include, and add commands to the toolbar.
+""", "EditMode"))
+
+ self.add_step(TutorialStep("Entity Outliner", """
+On the left side of O3DE Editor, Entity Outliner displays a list of entities and prefabs in the current level. Right-click in Entity Outliner to open the context menu to create entities and instantiate prefabs. When an entity or prefab is selected in Entity Outliner, the context menu also has options to duplicate or delete entities, find selected entities and prefabs, organize the list, and open the properties for the selected entity or prefab.
+""", "EntityOutlinerWidgetUI"))
+
+ self.add_step(TutorialStep("Asset Browser", """
+Below Entity Outliner is Asset Browser, which you can use to browse your project’s on-disk assets. Assets such as meshes, animations, and textures are created in third-party applications. Assets such as materials, scripts, and prefabs are created in O3DE Editor, or in editor tools such as Script Canvas. The assets that you create are stored in your project directory. You can also browse default assets that are included with O3DE, as well as assets that are included with Gems that have been added to your project.
+
+The left pane of the Asset Browser displays a directory structure that you can browse for available assets. When an asset is selected, the preview pane on the right displays a thumbnail preview and information about the asset, if available.
+
+With an asset selected in Asset Browser, the right-click context menu has options to open Scene Settings where you can set Asset Processor options for the asset, as well as open the asset in an associated application such as a modeling program, or open the file location in the system file browser.
+""", "AzAssetBrowserWindowClass"))
+
+ self.add_step(TutorialStep("Entity Inspector", """
+On the right side of O3DE Editor, Entity Inspector displays the components of the currently selected entity. At the top of Entity Inspector is a field for the entity Name and an Add Component button. The Add Component button opens a list of available components, sorted by type, that can be added to the entity. Each component has its own set of properties that are displayed in Entity Inspector. All entities contain a transform component that sets the position, rotation, and scale of the entity in the level.
+""", "InspectorMainWindow"))
+
+ self.add_step(TutorialStep("Console", """
+At the bottom of the default O3DE Editor layout is the Editor Console, which shows command and process output from O3DE Editor and your project. When you load a level, for example, the console displays messages about assets and configuration files as they load, and might display warnings and errors if issues are encountered.
+
+You can enter console commands such as setting console variables in the entry field at the bottom of the console. Choose the {x} button in the lower left of Editor Console to open the Console Variables Editor, which provides a simple interface for setting console variables.
+""", "Console"))
+
+ self.add_step(TutorialStep("Viewport", """
+In the center of the default O3DE Editor layout is Perspective. This 3D viewport is a real-time view of your level. In Perspective, you create and place entities, and view and play your project.
+
+Right-click in the title bar of Perspective to open the perspective menu. From the perspective menu, you can toggle visibility for various helpers such as the construction plane, icons, bounds, and guides. You can also select an aspect ratio, view through various cameras placed in the level, create new cameras from the current view, and split Perspective into multiple views.
+
+On the right side of the Perspective title bar, are several icons to select cameras, set camera movement speed, set information display, enable view icons, set aspect ratio, and set grid snapping options.
+
+Right-click in the viewport of Perspective to open the context menu to create entities and prefabs. Much of the context menu functionality in Perspective is shared with the context menu functionality of Entity Outliner.
+
+In the upper left and upper right corners of Perspective are icon trays for manipulating entities. On the left, you can use the icons to select a transform operation. From top to bottom the icons represent translate, rotate, and scale operations. On the right, you can use the icons to select a space for the transform operation. From top to bottom the icons represent world, parent, and local spaces.
+""", "renderOverlay"))
+
+ self.add_step(TutorialStep("Navigating the O3DE Perspective viewport", """
+O3DE has familiar viewport interaction models based on first-person PC games and popular modeling applications, with a few minor tweaks and additions. Movement is handled by keyboard input, and view is handled by pointer device input.
+
+ - W - Move forward.
+ - S - Move backward.
+ - A - Move left.
+ - D - Move right.
+ - Q - Move down.
+ - E - Move up.
+ - Z - Focus on selected.
+ - Right mouse + drag - Rotate view, known as mouselook in most games.
+ - Mouse wheel scroll - Zoom view.
+ - Middle mouse + drag - Pan view.
+ - Left mouse - Select entity.
+ - Left mouse + drag - Area select entities.
+
+The camera controls above are game-centric. If you prefer to use camera controls closer to those you would find in a DCC application such as Maya, use these hotkeys.
+
+ - ALT + left mouse + drag - Orbit view.
+ - ALT + right mouse + drag - Dolly view.
+ - ALT + right mouse + drag - Track view.
+
+""", "renderOverlay"))
diff --git a/Editor/Scripts/finding_ui_objects.py b/Editor/Scripts/finding_ui_objects.py
new file mode 100644
index 0000000..875a478
--- /dev/null
+++ b/Editor/Scripts/finding_ui_objects.py
@@ -0,0 +1,80 @@
+"""
+Copyright (c) Contributors to the Open 3D Engine Project.
+For complete copyright and license terms please see the LICENSE at the root of this distribution.
+SPDX-License-Identifier: Apache-2.0 OR MIT
+"""
+
+import sys
+import azlmbr
+
+from PySide2 import QtWidgets
+import editor_python_test_tools.pyside_utils as pyside_utils
+
+from tutorial import Tutorial, TutorialStep
+
+class FindingUIObjectsTutorial(Tutorial):
+ def __init__(self):
+ super(FindingUIObjectsTutorial, self).__init__()
+
+ self.title = "Highlighting UI Objects"
+
+ self.add_step(TutorialStep("Highlighting UI Objects",
+ """Greetings!
This tutorial demonstrates methods to find UI
+ objects to highlight in tutorial steps. You can use the Object Tree tool to find named UI
+ objects or a named parent of a UI object. Then, use one of methods in this tutorial to specify the
+ widget to highlight in a TutorialStep
+ .
The available methods are:
- Highlight by name - Specify the name of the
+ UI object to highlight.
- Highlight by parent - Specify a named parent and a pattern for
+ the UI object. The UI object is the first child of the parent that matches the specified pattern.
+ - Highlight from hierarchy with index - Specify an index to select a specific child UI object
+ when the parent has multiple unnamed children of the same type.
"""))
+ self.add_step(TutorialStep("Highlight by name", """This step highlights the
+ Entity Outliner, finding it by its name,
+ "EntityOutlinerWidgetUI".
""", "EntityOutlinerWidgetUI"))
+ self.add_step(TutorialStep("Highlight by pattern", """This step highlights
+ the Console Variables tool button in the lower-left corner of the Editor. The Console Variables
+ tool button has a name, but is found through a dictionary that contains text (the widget name) and its
+ type, {"text": "button", "type":
+ QtWidgets.QToolButton}.
""", {"text": "button",
+ "type": QtWidgets.QToolButton}))
+ self.add_step(TutorialStep("Highlight by pattern", """This step
+ highlights the Play tool button in the upper-right corner of the Editor. The Play tool button is
+ unnamed, but it has a text attribute for a tooltip. We can use a dictionary to find this UI
+ element by providing the widget type and the tooltip text, {"type": QtWidgets.QToolButton, "text": "Play Game"}.
""",
+ {"type": QtWidgets.QToolButton, "text": "Play Game"}))
+ self.add_step(TutorialStep("Highlight from named parent", """This step highlights
+ the Transform Toolbar in the upper-left corner of the viewport. The Transform Toolbar widget is
+ unnamed and has no tooltip text. We can find it by its type through its named parent. It's the first
+ child of type QtWidgets.QToolBar of a
+ widget named "ViewportUiOverlay".
+
""", QtWidgets.QToolBar, "ViewportUiOverlay"))
+ self.add_step(TutorialStep("Highlight from named parent with index", """In
+ the upper-right corner of the viewport is the Transform Space Toolbar. It is an unnamed widget
+ of type QtWidgets.QToolBar. It's also
+ the second child of the "ViewportUiOverlay"
+ widget we referenced in the previous step. To highlight the Transform Space Toolbar, we use
+ the same method as the previous step, but provide an optional index value of
+ 1 to select the second child.
""",
+ QtWidgets.QToolBar, "ViewportUiOverlay", 1))
+ item_parent = pyside_utils.find_child_by_pattern(None, "ViewportUiOverlay")
+ item = pyside_utils.find_child_by_hierarchy(item_parent, QtWidgets.QToolBar, child_index=1)
+ self.add_step(TutorialStep("Highlight from named parent with index - advanced", """
+ What if you want to highlight a UI element, but its nearest named parent is a couple of levels and
+ indices away?
+ The Transform Space Toolbar has three buttons: World, Parent, and Local space. Suppose we want to
+ highlight the Local space button. It's the fourth
+ QtWidgets.QToolButton in the second
+ QtWidgets.QToolBar beneath a parent named
+ ViewportUiOverlay.
+ We can get the local space button by finding it's direct unnamed parent prior to defining the step, and
+ supplying the direct parent and an index to the tutorial step. See this step in this tutorial's Python
+ script for an example.
""",
+ QtWidgets.QToolButton, item, 3))
+
+
+ def on_tutorial_start(self):
+ print("Starting Widget By Hierarchy tutorial.")
+
+ def on_tutorial_end(self):
+ print("Widget By Hierarchy tutorial complete!")
\ No newline at end of file
diff --git a/Editor/Scripts/interactivetutorials_dialog.py b/Editor/Scripts/interactivetutorials_dialog.py
index e5bcf2d..aa32013 100644
--- a/Editor/Scripts/interactivetutorials_dialog.py
+++ b/Editor/Scripts/interactivetutorials_dialog.py
@@ -1,7 +1,6 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# -------------------------------------------------------------------------
@@ -9,8 +8,8 @@
Generated from O3DE PythonToolGem Template"""
from PySide2 import QtCore
-from PySide2.QtCore import QMargins, QStringListModel, Qt
-from PySide2.QtGui import QColor, QPainter, QPen
+from PySide2.QtCore import QMargins, QStringListModel, Qt, QVariantAnimation, QRect
+from PySide2.QtGui import QColor, QPainter, QPen, QBrush
from PySide2.QtWidgets import (QDialog, QDialogButtonBox, QLabel, QListView,
QMessageBox, QPushButton, QStackedWidget, QTextEdit, QVBoxLayout, QWidget
)
@@ -23,19 +22,17 @@
from demo_tutorial import DemoTutorial, IntroTutorial
from rigid_body_tutorial import RigidBodyTutorial
-from process_physx_collider_assets import ColliderAssetsTutorial
-from decompose_input_meshes import DecomposeInputMeshes
-from customize_mesh_asset_processing import CustomizeMeshAssetProcessingTutorial
-
+from finding_ui_objects import FindingUIObjectsTutorial
from tutorial import Tutorial
+
class HighlightWidget(QWidget):
def __init__(self, parent=None):
super(HighlightWidget, self).__init__(parent)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool | Qt.WindowTransparentForInput | Qt.WindowDoesNotAcceptFocus | Qt.WindowStaysOnTopHint)
- self.setAttribute(Qt.WA_TranslucentBackground);
- self.setAttribute(Qt.WA_NoSystemBackground);
+ self.setAttribute(Qt.WA_TranslucentBackground)
+ self.setAttribute(Qt.WA_NoSystemBackground)
self.setAttribute(Qt.WA_TransparentForMouseEvents)
self.border_width = 5
@@ -46,6 +43,36 @@ def paintEvent(self, event):
pen.setWidth(self.border_width)
painter.setPen(pen)
painter.drawRect(event.rect())
+ #print(self.width)
+ #print(self.height)
+
+ #option 1: highlight the content of the widget
+ if self.width < 50 and self.height < 50:
+ painter.setOpacity(0.3)
+ painter.fillRect(event.rect(), QBrush(QColor ("blue")))
+
+ #option 2: highlight the area around the widget not including the widget
+ if self.width < 50 and self.height < 50:
+ painter.setOpacity(0.3)
+ topLeftX = event.rect().topLeft().x
+ #topRightX = event.rect().topRight().x
+ bottomLeftX = event.rect().bottomLeft().x
+ bottomRightX = event.rect().bottomRight().x
+ topLeftY = event.rect().topLeft().y
+ #topRightY = event.rect().topRight().y
+ bottomLeftY = event.rect().bottomLeft().y
+ #bottomRightY = event.rect().bottomRight().y
+ #draw 4 rectangles around "self"
+ painter.fillRect(bottomLeftX - self.width, bottomLeftY, self.width, self.height, QBrush(QColor ("blue")))
+ painter.fillRect(bottomRightX, bottomLeftY, self.width, self.height, QBrush(QColor ("blue")))
+ painter.fillRect(topLeftX - self.width, topLeftY, self.width * 3, self.height, QBrush(QColor ("blue")))
+ painter.fillRect(topLeftX - self.width, bottomLeftY - self.height, self.width * 3, self.height, QBrush(QColor ("blue")))
+
+ #option 3: add a pulse effect
+ if self.width < 50 and self.height < 50:
+ update_widget(self, event)
+ #edit the outline scale property as this will make the widget shrink and grow back gently
+
def update_widget(self, item):
if item:
@@ -61,6 +88,14 @@ def update_widget(self, item):
else:
self.hide()
+ blinking = False
+ if blinking:
+ painter = QPainter(self)
+ pen = QPen(QColor(255, 0, 152)) #Rhodamine Red
+ pen.setWidth(self.border_width + 10)
+ painter.setPen(pen)
+ painter.drawRect(item)
+
class InteractiveTutorialsDialog(QDialog):
def __init__(self, parent=None):
super(InteractiveTutorialsDialog, self).__init__(parent)
@@ -97,16 +132,8 @@ def __init__(self, parent=None):
"tutorial": RigidBodyTutorial
},
{
- "name": "Process PhysX Collider Assets",
- "tutorial": ColliderAssetsTutorial
- },
- {
- "name": "Decompose Input Meshes",
- "tutorial": DecomposeInputMeshes
- },
- {
- "name": "Customize Mesh Asset Processing",
- "tutorial": CustomizeMeshAssetProcessingTutorial
+ "name": "Highlighting UI Objects",
+ "tutorial": FindingUIObjectsTutorial
}
]
tutorial_names = [tutorial['name'] for tutorial in self.tutorials]
@@ -139,9 +166,6 @@ def __init__(self, parent=None):
self.content_area.setWordWrap(True)
self.tutorial_layout.addWidget(self.content_area, 1)
- self.step_label = QLabel(self)
- self.tutorial_layout.addWidget(self.step_label)
-
self.button_box = QDialogButtonBox(self)
self.next_button = QPushButton("Next", self)
self.next_button.setDefault(True)
@@ -165,8 +189,6 @@ def load_tutorial(self, index):
tutorial_factory = self.tutorials[index]["tutorial"]
self.current_tutorial = tutorial_factory()
-
- self.current_tutorial_num_steps = len(self.current_tutorial.get_steps())
# Invoke the tutorial start method
self.current_tutorial.on_tutorial_start()
@@ -175,11 +197,9 @@ def load_tutorial(self, index):
self.setWindowTitle("InteractiveTutorials - " + self.current_tutorial.get_title())
# Reset initial state and load first step
- self.current_step_index = 0
self.current_step = None
first_step = self.current_tutorial.get_first_step()
self.load_step(first_step)
-
def end_tutorial(self):
if not self.current_step:
@@ -208,7 +228,7 @@ def update_step_view(self):
self.title_label.setText(self.current_step.get_title())
self.content_area.setText(self.current_step.get_content())
- self.step_label.setText(f"Step {self.current_step_index} of {self.current_tutorial_num_steps}")
+
# If there are no steps remaining in the tutorial, then
# update the Next button text to "End"
next_button_text = "Next"
@@ -218,14 +238,27 @@ def update_step_view(self):
# If a highlight pattern was set for this step, then find that widget/item
# and highlight it
+ highlight_item = None
highlight_pattern = self.current_step.get_highlight_pattern()
- if highlight_pattern:
- item = pyside_utils.find_child_by_pattern(None, highlight_pattern)
- self.highlight_widget.update_widget(item)
- if not item:
- print(f"Couldn't find widget or item matching pattern: { highlight_pattern }")
+ if not highlight_pattern:
+ self.highlight_widget.update_widget(None)
+ return
+
+ highlight_parent = self.current_step.get_highlight_parent()
+ if not highlight_parent:
+ highlight_item = pyside_utils.find_child_by_pattern(None, highlight_pattern)
else:
+ if isinstance(highlight_parent, str):
+ highlight_parent = pyside_utils.find_child_by_pattern(None, highlight_parent)
+ highlight_item = pyside_utils.find_child_by_hierarchy(highlight_parent, highlight_pattern,
+ child_index=self.current_step.get_highlight_index())
+
+ if not highlight_item:
self.highlight_widget.update_widget(None)
+ print(f"Couldn't find widget or item matching pattern: { highlight_pattern }")
+ return
+
+ self.highlight_widget.update_widget(highlight_item)
def load_step(self, step):
# If there was a step already loaded, call its ending method
@@ -233,7 +266,6 @@ def load_step(self, step):
self.current_step.on_step_end()
self.current_step = step
- self.current_step_index += 1
# Invoke the method for the beginning of this step
self.current_step.on_step_start()
@@ -252,13 +284,10 @@ def load_next_step(self):
def load_previous_step(self):
if self.current_step:
- self.current_step_index -= 1
prev_step = self.current_step.prev_step
if prev_step:
- self.current_step_index -= 1
self.load_step(prev_step)
-
def on_start_button_clicked(self):
tutorial_index = self.tutorial_list.currentIndex().row()
@@ -270,4 +299,4 @@ def on_start_button_clicked(self):
# which allows for quick iteration without having to close/re-launch the Editor
test_dialog = InteractiveTutorialsDialog()
test_dialog.show()
- test_dialog.adjustSize()
+ test_dialog.adjustSize()
\ No newline at end of file
diff --git a/Editor/Scripts/process_physx_collider_assets.py b/Editor/Scripts/process_physx_collider_assets.py
deleted file mode 100644
index ecdfc6c..0000000
--- a/Editor/Scripts/process_physx_collider_assets.py
+++ /dev/null
@@ -1,65 +0,0 @@
-"""
-Copyright (c) Contributors to the Open 3D Engine Project.
-For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
-SPDX-License-Identifier: Apache-2.0 OR MIT
-"""
-
-from tutorial import Tutorial, TutorialStep
-
-class ColliderAssetsTutorial(Tutorial):
- def __init__(self):
- super(ColliderAssetsTutorial, self).__init__()
-
- self.title = "PhysX Collider Assets"
-
- self.add_step(TutorialStep("Process PhysX Collider Assets",
- """Greetings!
Collider assets are assets
- generated based on input meshes. There are various types of colliders, such as triangle, primitive, and convex.
- Colliders are categorized based on the shapes they are comprised of: triangle, sphere,
- box, capsule, and/or convex.
In this tutorial, we'll edit an
- existing source asset to create a Physx collider asset.
Click next to continue.
"""))
- self.add_step(TutorialStep("Select the Asset", """Locate your source asset in the
- Asset Browser. You can use your own or use one of the provided .fbx files, such as sphere.fbx.
- Right click the .fbx source asset and select 'Edit Settings.'
-
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Add a PhysX Mesh", """ Select the
- PhysX tab and select Add another physxmesh to create a PhysX mesh group.
- Each PhysX group produces as .pxmesh product asset.
-
""",
- "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Add a PhysX Mesh", """ To select which
- meshes to include in the PhysX mesh group, use the file select button.
If you select multiple meshes,
- you might want to also enable the Merge Meshes and Weld Vertices
- features to optimize the asset's appearance.
-
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("Customize the PhysX Mesh Collider Type", """ Customize the
- PhysX mesh collider type by setting the Export As property to the type you choose. You might prefer to
- selecct the 'Convex' type so that you can create any type of entity (static, kinematic, dynamic), but
- Triangle and Primitive types each also have their respective advantages and limitations.
You can visit the
- Docs online if you want to learn more.
Then select Update (in the bottom right corner)
- to update the .assetinfo file and trigger the Asset Processor.
""", "AzAssetBrowserWindowClass"))
- self.add_step(TutorialStep("View the Entity", """ .
Drag the .azmodel
- product asset into the viewport from the Asset Browser.
As you do this, O3DE automatically creates
- an entity with a Mesh component referencing the mesh product asset.
- """, "renderOverlay"))
- self.add_step(TutorialStep("View the Entity with the Collider component", """Entity Inspector, select Add Component and then PhysX Collider.
- The component we've just added automatically detects the .pxmesh asset.
- The is set to PhysicsAsset and the references the .pxmesh product asset.
- """, "InspectorMainWindow"))
- self.add_step(TutorialStep("Optimize your Entity", """Our entity is currently static, because
- we only have a PhysX Collider component. A static entity is solid (it can be collided with) but does not move
- in response to collisions.
If you want a static entity, you can enable the Static property within the Transform
- component to maximize your entity's runtime performance. If you want a dynamic entity, add a PhysX Rigid Body
- component. (Make sure that the Static property in the Transform component is disabled.)
- Select the entity in the viewport. Then, in Entity Inspector, select Add Compoonent and then
- PhysX Rigid Body to enable the effect of gravity on your entity. You can observe the changes by selecting the
- simulation button. If you want a kinematic entity, add a PhysX Rigid Body Component. Then, within the component,
- enable the Kinematic property. (Make sure that the Static property in the Transform component is disabled.)
""", "InspectorMainWindow"))
-
- def on_tutorial_start(self):
- print("Starting PhysX Collider Assets tutorial.")
-
- def on_tutorial_end(self):
- print("PhysX Collider Assets tutorial complete!")
diff --git a/Editor/Scripts/rigid_body_tutorial.py b/Editor/Scripts/rigid_body_tutorial.py
index 544e45e..824c576 100644
--- a/Editor/Scripts/rigid_body_tutorial.py
+++ b/Editor/Scripts/rigid_body_tutorial.py
@@ -1,7 +1,6 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
-
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
@@ -31,43 +30,44 @@ def __init__(self):
functionality. The Sun entity, for example, has a Directional Light component the
simulates a very bright distant light with parallel rays. It also has a Transform component that
places it in the level in realtion to it's parent, the Atom Default Environment entity.
-