|
VR-Engage
2.2
|
Purpose: This example demonstrates extending VR-Engage's built-in player station states with custom UI elements and message handling. It illustrates the complete pattern for replacing default states while integrating Qt Quick/QML for custom user interfaces.
Observable Behavior: When running this example, you will see a collapsible radio messages window appear in the upper-right corner of the screen when entering the engaged state. The window displays real-time text messages exchanged between entities, showing sender names, receiver names, timestamps, and message content. The window automatically appears when entering the engaged state and hides when exiting. Users can manually toggle window visibility using the expand/collapse button and clear all messages using the clear button.
Prerequisites:
Related Examples:
This example demonstrates:
DtEngagedState functionality with custom behavior. See Player Station Framework for state machine details. This example uses an identical state name to replace rather than extend the default engaged state.QAbstractListModel for dynamic QML data binding. The implementation uses custom role enumeration to map C++ data to QML property names and demonstrates proper model change notifications for automatic view updates.DtQtQuickRenderer for non-blocking QML file loading and shows context property registration enabling bidirectional C++ to QML communication.onEnter() with mandatory cleanup in onExit(), and UI window management through state stacking operations.The plugin registers a replacement state using the same identifier as the built-in state:
The DtExtendedEngagedState constructor internally sets its name to "ENGAGED_STATE", which matches the built-in state's identifier. When the state manager encounters a state registration with an existing name, it automatically replaces the previous state with the new implementation. This approach allows seamless extension of built-in states without modifying core state transition logic.
The extended state manages both UI windows and message subscriptions across its complete lifecycle:
Message handlers must be explicitly removed in onExit() because they accumulate if not cleaned up, leading to memory leaks and duplicate message processing on subsequent state entries. The chat window object is reused rather than recreated across state transitions to maintain accumulated messages and improve performance. Calling the base class methods (DtEngagedState::onEnter(), etc.) preserves all standard engaged state functionality including entity control and simulation updates. The onStacked() and onExposed() methods manage window visibility when modal dialogs or other states temporarily overlay the engaged state.
The chat model exposes C++ data to QML through the standard Qt model-view architecture:
The beginInsertRows() and endInsertRows() calls bracket the data modification and trigger automatic ListView updates in QML. The roleNames() method maps C++ role enumerations to string property names that QML delegate items can reference directly (e.g., sender, receiver, msg). This pattern enables real-time data binding where QML views automatically reflect changes to the underlying C++ model without explicit refresh calls.
The chat window demonstrates complete Qt Quick integration within VR-Engage's rendering system:
Context properties expose C++ objects directly to QML through globally accessible names, enabling the QML UI to display model data and invoke C++ methods marked with Q_INVOKABLE (such as clearChat()). The loadQMLFile() call is asynchronous to prevent blocking VR-Engage's main rendering thread during file I/O and QML parsing. The lambda callback executes after loading completes, storing the root QML item reference and performing any initialization that requires the fully-loaded QML scene.
The state processes incoming VR-Forces text messages and displays them with human-readable entity names:
Raw entity IDs consisting of site, application, and entity numbers are not meaningful to human operators, so the DtEntityResolver service translates these numeric identifiers to human-readable marking text or call signs configured in the simulation. A null receiver ID indicates a broadcast message addressed to all entities, displayed as "All" in the UI. Returning HANDLED from the message handler prevents other registered handlers from processing the same message, which is appropriate when the message has been fully consumed.
Build the example (see Environment Setup & Build Guide):
Install the plugin to the VR-Engage installation:
This copies the plugin to <VR-Engage-Install-Dir>\plugins64\vrEngage\release\exampleState.dll.
Verify installation:
The QML UI file is located at <VR-Engage-Install-Dir>\data\UI\examples\exampleState.qml and should be available in the standard VR-Engage installation.
Option 1: Use any existing scenario (no configuration required):
Option 2: Verify with VR-Forces integration:
[timestamp] (SenderName) ---> (ReceiverName): Message content. The window should automatically scroll to show the newest messages, and entity names should display as marking text rather than numeric IDs.Verification: Check the VR-Engage log (the most recent *.log file in the MAK log directory, typically C:/MAK/logs) for initialization messages such as [ExtendedEngagedState] Registered extended engaged state and [ExampleChatWindow] Chat window created successfully. Verify that the window appears only in the engaged state and hides during state transitions. Confirm that message formatting includes proper timestamps and entity name resolution.
Plugin not loading: Verify the DLL is in the correct plugins directory at <VR-Engage-Install-Dir>\plugins64\vrEngage\release\. Check Qt dependencies using Dependency Walker to ensure Qt5Core.dll, Qt5Qml.dll, and Qt5Quick.dll are accessible. Review the VR-Engage log (most recent *.log file in the MAK log directory) for DLL load errors or missing symbols.
QML file loading errors:
myTargetFileexampleState.qml exists at data\UI\examples\exampleState.qmlWindow appears but no messages display:
Entity names show as numbers:
Window doesn't hide on state exit:
onExit() implementation| Class | Base Class | Purpose | Header |
|---|---|---|---|
DtExtendedEngagedState | DtEngagedState | Custom engaged state with chat UI | extendedEngagedState.h |
DtExampleChatWindow | QObject | Qt Quick window manager | exampleQtQuick.h |
DtChatQmlModel | QAbstractListModel | Chat data model for QML | exampleQtQuick.h |
DtChatEntry | (none) | Chat message data container | exampleQtQuick.h |
DtPlayerStationStateManager::registerState() - Register custom state with state machineDtVreMessageManager::addHandler() / removeHandler() - Message handler lifecycleDtQtQuickRenderer::loadQMLFile() - Asynchronous QML loadingQQmlContext::setContextProperty() - Expose C++ objects to QMLQAbstractListModel::beginInsertRows() / endInsertRows() - Model change notificationsDtEntityResolver::findDisplayName() - Entity ID to marking text resolution| Role | Enum Value | QML Property | Description |
|---|---|---|---|
SenderRole | Qt::UserRole + 1 | sender | Message sender entity name |
ReceiverRole | Qt::UserRole + 2 | receiver | Message receiver entity name |
TimeRole | Qt::UserRole + 3 | time | Message timestamp (simulation time) |
MessageRole | Qt::UserRole + 4 | msg | Message text content |
exampleState.dll (Windows)plugins64/vrEngage/Related Documentation: