forked from Shardstone/trail-into-darkness
Made the popup system a lot more generic
This commit is contained in:
@@ -39,7 +39,7 @@ RectTransform:
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 300, y: 500}
|
||||
m_SizeDelta: {x: 300, y: 0}
|
||||
m_Pivot: {x: 0, y: 1}
|
||||
--- !u!225 &1835601435911948781
|
||||
CanvasGroup:
|
||||
@@ -68,11 +68,6 @@ MonoBehaviour:
|
||||
content: {fileID: 176628901263125209}
|
||||
canvasGroup: {fileID: 1835601435911948781}
|
||||
background: {fileID: 8899521584296352500}
|
||||
headerPrefab: {fileID: 6612787789151041457, guid: dfc1bc0bd5b4905409615c3e770a5b77, type: 3}
|
||||
textPrefab: {fileID: 2506259255305457008, guid: bfa97c92d1878cc448ddc7dc456f4b17, type: 3}
|
||||
statPrefab: {fileID: 1843470073663794312, guid: 5882db210c62d8647858933649f64c29, type: 3}
|
||||
imagePrefab: {fileID: 7093821785826926595, guid: 5e715f4b614d02b4fa0b4d3fcfe3c053, type: 3}
|
||||
separatorPrefab: {fileID: 4190588985333916705, guid: 7ccdfa1a2079db044be4b1684303ec7f, type: 3}
|
||||
--- !u!223 &3081303906751693297
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -219,14 +214,14 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.31132078, g: 0.22780241, b: 0.16006587, a: 1}
|
||||
m_Color: {r: 0.1509434, g: 0.116711326, b: 0.088999644, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Sprite: {fileID: 21300000, guid: 52125a3c3df558448a5af5a04dbf8d2d, type: 3}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
|
||||
@@ -4,16 +4,18 @@
|
||||
|
||||
### Prefabs
|
||||
|
||||
Reference prefabs for the popup system. Copy these into your project as a starting point.
|
||||
Reference prefabs for the popup system. Copy these into your project as a starting point. After copying, add entries to your `PopupSettings` asset's Element Prefabs list mapping `PopupElementType` values to these prefabs.
|
||||
|
||||
| Prefab | Description |
|
||||
|---|---|
|
||||
| `PopupReferencePrefab` | Main popup container with Canvas, CanvasGroup, Background, and Content |
|
||||
| `PopupHeader` | Header text element (TMP_Text, bold, larger font) |
|
||||
| `PopupText` | Body text element (TMP_Text, regular) |
|
||||
| `PopupStat` | Stat row element (HorizontalLayoutGroup with label + value TMP_Text) |
|
||||
| `PopupIcon` | Image element for icons or artwork |
|
||||
| `PopupSeparator` | Horizontal divider line (Image, thin) |
|
||||
| Prefab | PopupElementType | Description |
|
||||
|---|---|---|
|
||||
| `PopupReferencePrefab` | N/A | Main popup container with Canvas, CanvasGroup, Background, and Content |
|
||||
| `PopupHeader` | `Header` ("header") | Header text element (TMP_Text, bold, larger font) |
|
||||
| `PopupText` | `Text` ("text") | Body text element (TMP_Text, regular) |
|
||||
| `PopupStat` | `LabelValueText` ("label_value_text") | Label + value row (HorizontalLayoutGroup with two TMP_Text children) |
|
||||
| `PopupIcon` | `Image` ("image") | Image element for icons or artwork |
|
||||
| `PopupSeparator` | `Separator` ("separator") | Horizontal divider line (Image, thin) |
|
||||
|
||||
To add variants, duplicate a prefab, style it differently, and register it with a variant type. Use `PopupElementType.Header.Variant("gold")` in code, and set the type to "header_gold" in the PopupSettings Inspector.
|
||||
|
||||
### Settings
|
||||
|
||||
@@ -25,13 +27,32 @@ Reference prefabs for the popup system. Copy these into your project as a starti
|
||||
|
||||
| Script | Description |
|
||||
|---|---|
|
||||
| `PopupSystemExample` | Basic setup with auto-scanned triggers, GetTrigger by name, and GetTriggers by category |
|
||||
| `DynamicTriggersExample` | Setting up triggers on dynamically instantiated UI using InitializeTriggersInChildren |
|
||||
| `CodeOnlyPopupExample` | Showing popups from code without PopupTrigger (anchored, fixed position, follow mouse) |
|
||||
| `PopupSystemExample` | Basic setup with auto-scanned triggers, `GetTriggerHandler` by name, and `GetTriggerHandlers` by category |
|
||||
| `DynamicTriggersExample` | Setting up triggers on dynamically instantiated UI using `InitializeTriggersInChildren` with (trigger, view) callback |
|
||||
| `CodeOnlyPopupExample` | Showing popups from code (anchored, fixed position, follow mouse), `PopupElementType` variants, and generic `Add()` for custom types |
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
```
|
||||
PopupTrigger (MonoBehaviour) -- reference holder, forwards pointer events
|
||||
|
|
||||
PopupTriggerView (C# class) -- behavior: calls IPopupSystem.Show/Hide
|
||||
|
|
||||
IPopupSystem / PopupSystem -- manages categories, delays, priority, triggers
|
||||
|
|
||||
PopupView (C# class) -- behavior: generic element cache, positioning
|
||||
|
|
||||
PopupReference (MonoBehaviour) -- reference holder: content, canvasGroup, background
|
||||
|
|
||||
PopupSettings (ScriptableObject) -- configuration + element prefab registry (PopupElementType -> prefab)
|
||||
|
|
||||
PopupElementType (struct) -- type-safe element identifier (Header, Text, LabelValueText, Image, Separator + custom)
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
1. Import the samples via the Unity Package Manager (select the package, expand Samples, click Import)
|
||||
2. Copy the prefabs into your project's Prefabs folder
|
||||
3. Create a PopupSettings asset or use the provided one
|
||||
4. Reference the example scripts for integration patterns
|
||||
4. Add Element Prefab entries to PopupSettings using the dropdown (Header, Text, LabelValueText, Image, Separator) mapped to the copied prefabs
|
||||
5. Reference the example scripts for integration patterns
|
||||
|
||||
@@ -7,6 +7,7 @@ using UnityEngine;
|
||||
///
|
||||
/// Use this approach for confirmation dialogs, tutorial tips, or any popup
|
||||
/// that is triggered by game logic rather than hover events.
|
||||
/// Also demonstrates PopupElementType variants and the generic Add() method.
|
||||
/// </summary>
|
||||
public class CodeOnlyPopupExample : MonoBehaviour {
|
||||
[SerializeField] PopupSettings popupSettings;
|
||||
@@ -14,6 +15,9 @@ public class CodeOnlyPopupExample : MonoBehaviour {
|
||||
[SerializeField] Transform canvasRoot;
|
||||
[SerializeField] RectTransform targetElement;
|
||||
|
||||
// Custom element type defined in game code
|
||||
static readonly PopupElementType BadgeElement = new("badge");
|
||||
|
||||
IPopupSystem popupSystem;
|
||||
|
||||
void Start() {
|
||||
@@ -24,12 +28,12 @@ public class CodeOnlyPopupExample : MonoBehaviour {
|
||||
void Update() {
|
||||
popupSystem?.Tick(Time.deltaTime);
|
||||
|
||||
// Show anchored to an element on key press
|
||||
// Show anchored to an element
|
||||
if(Input.GetKeyDown(KeyCode.Alpha1)) {
|
||||
popupSystem.Show(PopupCategory.General, builder => {
|
||||
builder
|
||||
.AddHeader("Anchored Popup")
|
||||
.AddText("This popup is anchored to a UI element.");
|
||||
.AddText("Anchored Popup", PopupElementType.Header)
|
||||
.AddText("This popup is anchored to a UI element.", PopupElementType.Text);
|
||||
}, targetElement, AnchorSide.Right);
|
||||
}
|
||||
|
||||
@@ -37,8 +41,8 @@ public class CodeOnlyPopupExample : MonoBehaviour {
|
||||
if(Input.GetKeyDown(KeyCode.Alpha2)) {
|
||||
popupSystem.ShowAtPosition(PopupCategory.General, builder => {
|
||||
builder
|
||||
.AddHeader("Fixed Position")
|
||||
.AddText("This popup appears at the center of the screen.");
|
||||
.AddText("Fixed Position", PopupElementType.Header)
|
||||
.AddText("This popup appears at the center of the screen.", PopupElementType.Text);
|
||||
}, new Vector2(Screen.width * 0.5f, Screen.height * 0.5f));
|
||||
}
|
||||
|
||||
@@ -46,12 +50,36 @@ public class CodeOnlyPopupExample : MonoBehaviour {
|
||||
if(Input.GetKeyDown(KeyCode.Alpha3)) {
|
||||
popupSystem.Show(PopupCategory.General, builder => {
|
||||
builder
|
||||
.AddHeader("Follow Mouse")
|
||||
.AddText("This popup follows the cursor.");
|
||||
.AddText("Follow Mouse", PopupElementType.Header)
|
||||
.AddText("This popup follows the cursor.", PopupElementType.Text);
|
||||
});
|
||||
}
|
||||
|
||||
// Hide on key press
|
||||
// Demonstrate variant elements
|
||||
// Requires "header_gold" and "separator_thick" entries in PopupSettings.elementPrefabs
|
||||
if(Input.GetKeyDown(KeyCode.Alpha4)) {
|
||||
popupSystem.Show(PopupCategory.General, builder => {
|
||||
builder
|
||||
.AddText("Legendary Item", PopupElementType.Header.Variant("gold"))
|
||||
.AddSeparator(PopupElementType.Separator.Variant("thick"))
|
||||
.AddNameValue("Damage", "150", PopupElementType.LabelValueText)
|
||||
.AddText("A weapon forged in dragon fire.", "FF6600", PopupElementType.Text);
|
||||
}, targetElement, AnchorSide.Below);
|
||||
}
|
||||
|
||||
// Demonstrate generic Add() for fully custom elements
|
||||
// Requires a "badge" entry in PopupSettings.elementPrefabs
|
||||
if(Input.GetKeyDown(KeyCode.Alpha5)) {
|
||||
popupSystem.Show(PopupCategory.General, builder => {
|
||||
builder.AddText("Custom Element", PopupElementType.Header);
|
||||
var badge = builder.Add(BadgeElement);
|
||||
if(badge != null) {
|
||||
// Access any components on the custom prefab
|
||||
// badge.GetComponent<MyBadgeComponent>().SetData(...);
|
||||
}
|
||||
}, targetElement);
|
||||
}
|
||||
|
||||
if(Input.GetKeyDown(KeyCode.Escape)) {
|
||||
popupSystem.HideAll();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ using UnityEngine;
|
||||
/// Example: Using PopupSystem with dynamically instantiated UI elements.
|
||||
///
|
||||
/// When UI elements are created at runtime (e.g. inventory slots, party member portraits),
|
||||
/// use InitializeTriggersInChildren to scan and bind triggers after instantiation.
|
||||
/// use InitializeTriggersInChildren to scan, bind, and configure triggers after instantiation.
|
||||
/// The callback receives both the trigger (MonoBehaviour, for hierarchy queries) and the
|
||||
/// view (behavior handler, for setting content).
|
||||
/// </summary>
|
||||
public class DynamicTriggersExample : MonoBehaviour {
|
||||
[SerializeField] PopupSettings popupSettings;
|
||||
@@ -21,21 +23,17 @@ public class DynamicTriggersExample : MonoBehaviour {
|
||||
popupSystem = new PopupSystem(popupSettings, popupReferencePrefab, canvasRoot);
|
||||
popupSystem.RegisterCategory(PopupCategory.Item, priority: 5);
|
||||
|
||||
// Simulate creating dynamic UI slots
|
||||
for(int i = 0; i < 5; i++) {
|
||||
Instantiate(slotPrefab, slotsContainer);
|
||||
}
|
||||
|
||||
// Scan the container for any PopupTrigger components on the new slots.
|
||||
// Each trigger is automatically bound to the popup system.
|
||||
// The configure callback lets you set content per trigger.
|
||||
popupSystem.InitializeTriggersInChildren(slotsContainer, trigger => {
|
||||
popupSystem.InitializeTriggersInChildren(slotsContainer, (trigger, view) => {
|
||||
var slotName = trigger.gameObject.name;
|
||||
trigger.SetContent(builder => {
|
||||
view.SetContent(builder => {
|
||||
builder
|
||||
.AddHeader(slotName)
|
||||
.AddSeparator()
|
||||
.AddText("This is a dynamically created slot.");
|
||||
.AddText(slotName, PopupElementType.Header)
|
||||
.AddSeparator(PopupElementType.Separator)
|
||||
.AddText("This is a dynamically created slot.", PopupElementType.Text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ using UnityEngine;
|
||||
/// 2. Assign the PopupSettings asset and PopupReference prefab.
|
||||
/// 3. Set canvasRoot to the root Canvas that contains your UI and PopupTrigger components.
|
||||
/// 4. Place PopupTrigger components on any UI elements that should show popups on hover.
|
||||
/// 5. The system auto-scans triggers on creation. Use SetContent() to provide data.
|
||||
/// 5. The system auto-scans triggers on creation. Use GetTriggerHandler() to set content.
|
||||
///
|
||||
/// Element prefabs are configured in PopupSettings via PopupElementType.
|
||||
/// Add entries mapping types (header, text, label_value_text, separator, image) to prefabs.
|
||||
/// </summary>
|
||||
public class PopupSystemExample : MonoBehaviour {
|
||||
[SerializeField] PopupSettings popupSettings;
|
||||
@@ -19,52 +22,46 @@ public class PopupSystemExample : MonoBehaviour {
|
||||
IPopupSystem popupSystem;
|
||||
|
||||
void Start() {
|
||||
// Create the system. Passing canvasRoot auto-scans all PopupTrigger components.
|
||||
popupSystem = new PopupSystem(popupSettings, popupReferencePrefab, canvasRoot);
|
||||
|
||||
// Register categories before showing popups.
|
||||
popupSystem.RegisterCategory(PopupCategory.Character, priority: 10);
|
||||
popupSystem.RegisterCategory(PopupCategory.Item, priority: 5);
|
||||
popupSystem.RegisterCategory(PopupCategory.General, priority: 1);
|
||||
|
||||
// Option A: Set content on an auto-scanned trigger by GameObject name.
|
||||
var characterTrigger = popupSystem.GetTrigger("CharacterPortrait");
|
||||
if(characterTrigger != null) {
|
||||
characterTrigger.SetContent(builder => {
|
||||
builder
|
||||
.AddHeader("Kael")
|
||||
.AddText("Human Warrior", "CCCCCC")
|
||||
.AddSeparator()
|
||||
.AddStat("Health", 55)
|
||||
.AddStat("Mana", 42)
|
||||
.AddStat("Level", 1)
|
||||
.AddSeparator()
|
||||
.AddStat("Might", 8)
|
||||
.AddStat("Reflex", 2)
|
||||
.AddStat("Knowledge", 5)
|
||||
.AddStat("Perception", 1);
|
||||
});
|
||||
}
|
||||
// Set content on an auto-scanned trigger by GameObject name.
|
||||
var characterHandler = popupSystem.GetTriggerHandler("CharacterPortrait");
|
||||
characterHandler?.SetContent(builder => {
|
||||
builder
|
||||
.AddText("Kael", PopupElementType.Header)
|
||||
.AddText("Human Warrior", "CCCCCC", PopupElementType.Text)
|
||||
.AddSeparator(PopupElementType.Separator)
|
||||
.AddNameValue("Health", 55, PopupElementType.LabelValueText)
|
||||
.AddNameValue("Mana", 42, PopupElementType.LabelValueText)
|
||||
.AddNameValue("Level", 1, PopupElementType.LabelValueText)
|
||||
.AddSeparator(PopupElementType.Separator)
|
||||
.AddNameValue("Might", 8, PopupElementType.LabelValueText)
|
||||
.AddNameValue("Reflex", 2, PopupElementType.LabelValueText)
|
||||
.AddNameValue("Knowledge", 5, PopupElementType.LabelValueText)
|
||||
.AddNameValue("Perception", 1, PopupElementType.LabelValueText);
|
||||
});
|
||||
|
||||
// Option B: Set content on all triggers of a category.
|
||||
foreach(var trigger in popupSystem.GetTriggers(PopupCategory.Item)) {
|
||||
trigger.SetContent(builder => {
|
||||
// Set content on all triggers of a category.
|
||||
foreach(var handler in popupSystem.GetTriggerHandlers(PopupCategory.Item)) {
|
||||
handler.SetContent(builder => {
|
||||
builder
|
||||
.AddHeader("Health Potion")
|
||||
.AddSeparator()
|
||||
.AddText("Restores a moderate amount of health.")
|
||||
.AddStat("Heal Amount", 50);
|
||||
.AddText("Health Potion", PopupElementType.Header)
|
||||
.AddSeparator(PopupElementType.Separator)
|
||||
.AddText("Restores a moderate amount of health.", PopupElementType.Text)
|
||||
.AddNameValue("Heal Amount", 50, PopupElementType.LabelValueText);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Update() {
|
||||
// Tick drives delay timers and animations.
|
||||
popupSystem?.Tick(Time.deltaTime);
|
||||
}
|
||||
|
||||
void OnDestroy() {
|
||||
// Clean up all popup views.
|
||||
popupSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,22 @@ MonoBehaviour:
|
||||
followMouseOffset: {x: 15, y: -15}
|
||||
touchHoldDuration: 0.6
|
||||
gamepadFocusTrigger: 1
|
||||
elementPrefabs:
|
||||
- type:
|
||||
id: header
|
||||
prefab: {fileID: 7034836061828108288, guid: dfc1bc0bd5b4905409615c3e770a5b77, type: 3}
|
||||
- type:
|
||||
id: image
|
||||
prefab: {fileID: 5887814251614319338, guid: 5e715f4b614d02b4fa0b4d3fcfe3c053, type: 3}
|
||||
- type:
|
||||
id: label_value_text
|
||||
prefab: {fileID: 6770634903822758885, guid: 7ccdfa1a2079db044be4b1684303ec7f, type: 3}
|
||||
- type:
|
||||
id: separator
|
||||
prefab: {fileID: 6770634903822758885, guid: 7ccdfa1a2079db044be4b1684303ec7f, type: 3}
|
||||
- type:
|
||||
id: text
|
||||
prefab: {fileID: 3157287847714375358, guid: bfa97c92d1878cc448ddc7dc456f4b17, type: 3}
|
||||
categoryPriorities:
|
||||
- category:
|
||||
id: Character
|
||||
|
||||
Reference in New Issue
Block a user