diff --git a/.gitignore b/.gitignore
index 2076d190a4..e7da4f54d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,7 +123,6 @@ GlobalConnectionShutdownDelegate*.txt
**/._.DS_Store
# VSCode
-.vscode
.vsconfig
# Custom entries
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000000..ddb6ff85a3
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "visualstudiotoolsforunity.vstuc"
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000000..da60e25ae2
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,10 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Attach to Unity",
+ "type": "vstuc",
+ "request": "attach"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000..f403e6761d
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,61 @@
+{
+ "files.exclude": {
+ "**/.DS_Store": true,
+ "**/.git": true,
+ "**/.vs": true,
+ "**/.gitmodules": true,
+ "**/.vsconfig": true,
+ "**/*.booproj": true,
+ "**/*.pidb": true,
+ "**/*.suo": true,
+ "**/*.user": true,
+ "**/*.userprefs": true,
+ "**/*.unityproj": true,
+ "**/*.dll": true,
+ "**/*.exe": true,
+ "**/*.pdf": true,
+ "**/*.mid": true,
+ "**/*.midi": true,
+ "**/*.wav": true,
+ "**/*.gif": true,
+ "**/*.ico": true,
+ "**/*.jpg": true,
+ "**/*.jpeg": true,
+ "**/*.png": true,
+ "**/*.psd": true,
+ "**/*.tga": true,
+ "**/*.tif": true,
+ "**/*.tiff": true,
+ "**/*.3ds": true,
+ "**/*.3DS": true,
+ "**/*.fbx": true,
+ "**/*.FBX": true,
+ "**/*.lxo": true,
+ "**/*.LXO": true,
+ "**/*.ma": true,
+ "**/*.MA": true,
+ "**/*.obj": true,
+ "**/*.OBJ": true,
+ "**/*.asset": true,
+ "**/*.cubemap": true,
+ "**/*.flare": true,
+ "**/*.mat": true,
+ "**/*.meta": true,
+ "build/": true,
+ "Build/": true,
+ "Library/": true,
+ "library/": true,
+ "obj/": true,
+ "Obj/": true,
+ "Logs/": true,
+ "logs/": true,
+ "ProjectSettings/": true,
+ "UserSettings/": true,
+ "temp/": true,
+ "Temp/": true
+ },
+ "dotnet.defaultSolution": "SEE.sln",
+ "files.insertFinalNewline": true,
+ "files.trimFinalNewlines": true,
+ "files.trimTrailingWhitespace": true
+}
diff --git a/Assets/Plugins/DynamicPanels.meta b/Assets/Plugins/DynamicPanels.meta
index cd5ae2a33b..b392183703 100644
--- a/Assets/Plugins/DynamicPanels.meta
+++ b/Assets/Plugins/DynamicPanels.meta
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8644c2c180be9c4bf84a7ff467eb0faadd90bf7cba4a1d3650026ddd53aa044d
+oid sha256:bfb637ef9995c2b689a2f3ed34ef8fcfedf8e99ec5a26126b8c170c1e220f921
size 192
diff --git a/Assets/Plugins/DynamicPanels/DynamicPanels.Runtime.asmdef b/Assets/Plugins/DynamicPanels/DynamicPanels.Runtime.asmdef
index 27d5f503ea..0532dee555 100644
--- a/Assets/Plugins/DynamicPanels/DynamicPanels.Runtime.asmdef
+++ b/Assets/Plugins/DynamicPanels/DynamicPanels.Runtime.asmdef
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ac53381ff903058517006c8be55250207f8bcbac7cb3f01681af267ddb3e38bc
-size 40
+oid sha256:3a22d2fc514f49b9a35f0624bfbc5b0299db2d954886bd4eed3d35034f3b9fea
+size 102
diff --git a/Assets/Plugins/DynamicPanels/Editor/DynamicPanelsCanvasEditor.cs b/Assets/Plugins/DynamicPanels/Editor/DynamicPanelsCanvasEditor.cs
index a468c9b597..cb64eca90e 100644
--- a/Assets/Plugins/DynamicPanels/Editor/DynamicPanelsCanvasEditor.cs
+++ b/Assets/Plugins/DynamicPanels/Editor/DynamicPanelsCanvasEditor.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:232cb81370ac8607a362d5df9df66dd11c6c9276d1c639c247cccb156b05f781
-size 20630
+oid sha256:8235e8b2f668a857a456bbfd01102a021b698ab79e5aabf1d752d170594c726e
+size 21247
diff --git a/Assets/Plugins/DynamicPanels/README.txt b/Assets/Plugins/DynamicPanels/README.txt
index 5654823a9b..b87ca5b5c8 100644
--- a/Assets/Plugins/DynamicPanels/README.txt
+++ b/Assets/Plugins/DynamicPanels/README.txt
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a0407d803f87f1c732df73ef373e7bf5c02c5cbda861e7fa71eef90a4b7a20c1
-size 4709
+oid sha256:27b645a98b5bdb89897b371c40b46e08c4c81383569175e1858937989c11277f
+size 256
diff --git a/Assets/Plugins/DynamicPanels/Resources/DynamicPanel.prefab b/Assets/Plugins/DynamicPanels/Resources/DynamicPanel.prefab
index 6bc5964f4c..f94bd83c96 100644
--- a/Assets/Plugins/DynamicPanels/Resources/DynamicPanel.prefab
+++ b/Assets/Plugins/DynamicPanels/Resources/DynamicPanel.prefab
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2f98c4d693df3dddf2b756ed16dd56e84feb33b256bac74654498b48f8413679
-size 12975
+oid sha256:517b0cd40e1d471200fec6da96b18c86670cb230b07be929ef4ab2f7682c8c43
+size 12542
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Anchoring/AnchorZoneBase.cs b/Assets/Plugins/DynamicPanels/Scripts/Anchoring/AnchorZoneBase.cs
index 0a7ffd851d..d734765029 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Anchoring/AnchorZoneBase.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Anchoring/AnchorZoneBase.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e22fa66dda515cc37170096b10ca5740805eea2dcad5e29330f269b485dccded
-size 1780
+oid sha256:1e2181452915e76b9c6a97e1ee4ca369b861f91a2198b50fd4f0f47777ba7197
+size 1846
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Anchoring/CanvasAnchorZone.cs b/Assets/Plugins/DynamicPanels/Scripts/Anchoring/CanvasAnchorZone.cs
index f141ee9062..19ff0e86dc 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Anchoring/CanvasAnchorZone.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Anchoring/CanvasAnchorZone.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c14cf841e5fecedb056931b355488fcaccec81b709f7413a9f625beece528299
-size 1145
+oid sha256:5d5c7a9cbf4848fad6ebe0353e820d2fd7b4040ec2d74088ad90b0c2e6f85dbb
+size 1183
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Anchoring/PanelAnchorZone.cs b/Assets/Plugins/DynamicPanels/Scripts/Anchoring/PanelAnchorZone.cs
index f6f7c498e1..cb5ecbcca4 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Anchoring/PanelAnchorZone.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Anchoring/PanelAnchorZone.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f44d1ec80c4c7e1636f0386e734821791205434d8eac983e3a2df198fe4e6f67
-size 2611
+oid sha256:a68918a8863b176ed571a12810edc378a55e0ddb62b027218ec9ab38ab3016cb
+size 2681
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Anchoring/PanelHeaderAnchorZone.cs b/Assets/Plugins/DynamicPanels/Scripts/Anchoring/PanelHeaderAnchorZone.cs
index b4344271b1..f6e5b4098c 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Anchoring/PanelHeaderAnchorZone.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Anchoring/PanelHeaderAnchorZone.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3d793e9e5b46374bde26a8287dd4599db40086dabaeb9de182d75fe6e3abab6f
-size 886
+oid sha256:0c37984ea063711e391e8371a45f88c63913c7d765ce57979edfb54f5ba4e353
+size 913
diff --git a/Assets/Plugins/DynamicPanels/Scripts/DynamicPanelsCanvas.cs b/Assets/Plugins/DynamicPanels/Scripts/DynamicPanelsCanvas.cs
index cbd9a5ccf0..d5da51b591 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/DynamicPanelsCanvas.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/DynamicPanelsCanvas.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b9e789cc39aebd8f1352734ed562af14460865cffb36e8fb5c27c2e96fb192e7
-size 18744
+oid sha256:a75643ccb10c65aefabb1f011d48233033e5d166fa3d4e429f237c15ab88e022
+size 19378
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Grouping/IPanelGroupElement.cs b/Assets/Plugins/DynamicPanels/Scripts/Grouping/IPanelGroupElement.cs
index 37bd5a95f2..5c5913691f 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Grouping/IPanelGroupElement.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Grouping/IPanelGroupElement.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:949158bd1131a964552c8797f1a41c8769c2a884ceee7a277d34c6dbc37575db
-size 542
+oid sha256:27fe14b907513c6b45cb29ba44931f969c0368e497dd8c527ca4112ebc136c3b
+size 562
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Grouping/PanelGroup.cs b/Assets/Plugins/DynamicPanels/Scripts/Grouping/PanelGroup.cs
index a07219d307..8e2c775898 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Grouping/PanelGroup.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Grouping/PanelGroup.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f8af76c622082d23f1169ef4b62f304bf8f49efb4c2ad2103188a888d1d49be3
-size 19478
+oid sha256:caae6b466943acad050da4c4f0889c76b1c3525554e79236fc9cba2809268ff0
+size 20144
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Grouping/UnanchoredPanelGroup.cs b/Assets/Plugins/DynamicPanels/Scripts/Grouping/UnanchoredPanelGroup.cs
index eb9aeefce5..edd27aae4c 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Grouping/UnanchoredPanelGroup.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Grouping/UnanchoredPanelGroup.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3ad6c84e5c8cbcfa3f832211b76a60bfd02b36ae0aa53421cfbb3ad028cf08f7
-size 2494
+oid sha256:3a0f06528fa756d1ade4e22a1493c583812b7c420e9e803c200da1ed917f26a1
+size 2604
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Helpers/NonDrawingGraphic.cs b/Assets/Plugins/DynamicPanels/Scripts/Helpers/NonDrawingGraphic.cs
index ffdd51151d..3eb369b5d3 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Helpers/NonDrawingGraphic.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Helpers/NonDrawingGraphic.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1cbc4f3ef065b152468f11448c543141f212a1d2145185435162c75897b21503
-size 434
+oid sha256:2db76bbee2749f3d453d185f262bc01ce8cb552f049aab2b16e2e99d4829f324
+size 452
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelCursorHandler.cs b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelCursorHandler.cs
index 7710726578..cd3f7de2a6 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelCursorHandler.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelCursorHandler.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d34d49518e88f5ff8a68e25583a4422e00008e81a112131765cb49da7e7f4f30
-size 3770
+oid sha256:f5fd9b974713d278f8087dbff136a765caf904f086488c2080db2463282bf3ed
+size 3924
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelHeader.cs b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelHeader.cs
index f248698569..00ea020882 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelHeader.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelHeader.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:bae1fef0a7a8ffbefb56454cf78c0e2e7a551e02e9863f604919dfc8363fc577
-size 1524
+oid sha256:9a39e823531f6bd12a210480e1a1bd8b6534e2f170bbc82b6c83452ca7238798
+size 1585
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelNotificationCenter.cs b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelNotificationCenter.cs
index caa219394c..409e7410ba 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelNotificationCenter.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelNotificationCenter.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f09530ea5728b3d0ce75efea8a0987d510b15fed70bbdc420b6b49264c7a86b6
-size 4898
+oid sha256:830fd11c327c1f1604234e2dd5c546b0811885956bb22c9fbafeff96acdb72b6
+size 5113
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelResizeHelper.cs b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelResizeHelper.cs
index fc7eee31f1..0373e49a54 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelResizeHelper.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelResizeHelper.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:74724aad5e4178bdcdbf497bf075f57649a75938e7d933cb51066509cc4c14c4
-size 3612
+oid sha256:76ed81b5d0e29ab322d2aacce460f0dbc55081ce0356660329026ae4353d5510
+size 3750
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelSerialization.cs b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelSerialization.cs
index d9b435325d..016b73b4d6 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelSerialization.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelSerialization.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:80068415964b486d44925c2e83497bed2604e2633a6dbe2df0770be9658390ed
-size 9576
+oid sha256:d22df53bfc00967e54e4e9aea2f692ce9cf5aeeb96b52e3f404271a8376f55bf
+size 9950
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelTab.cs b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelTab.cs
index 1a05a47e41..2cafacdeaa 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelTab.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelTab.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:90830b33755db000f7bece7570eccfb22540aff53cf4bba87d7032a236e31b70
-size 5972
+oid sha256:664768824dfb710845a55a8f9b4a26fa7aa92bdc0209bf602ce2664a3689d21f
+size 6230
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelUtils.cs b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelUtils.cs
index 3f0bea69c0..0cef075f8f 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelUtils.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Helpers/PanelUtils.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9bb20230e01a541bf77eebb40cdd27c7d148ae947e89fedeed0d5e05f51a8467
-size 3504
+oid sha256:280173bc8035a85aeaa6455058ba57ccd5053dd4fada0152edc6253e9609b452
+size 3633
diff --git a/Assets/Plugins/DynamicPanels/Scripts/Panel.cs b/Assets/Plugins/DynamicPanels/Scripts/Panel.cs
index 9198f07698..12b01cdc1b 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/Panel.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/Panel.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e2ba33578d167a58ee7f9d02d123fdbfebf46a01e42d79050200e32327d1b9e8
-size 23637
+oid sha256:9f708d47883ec736007c665b8fcbb11e866bbec8cc3528aa43bb8ad8056aab20
+size 24450
diff --git a/Assets/Plugins/DynamicPanels/Scripts/PanelManager.cs b/Assets/Plugins/DynamicPanels/Scripts/PanelManager.cs
index 8b1d6792eb..4a158cdc1b 100644
--- a/Assets/Plugins/DynamicPanels/Scripts/PanelManager.cs
+++ b/Assets/Plugins/DynamicPanels/Scripts/PanelManager.cs
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ae08a95049fd9c801eb488dcbfa6a3d6d7aa1dedeed64801fd91060d98a2c639
-size 16360
+oid sha256:3214195a0e12050543b3c993474f6769eaa3a2986d42839291c407fcd96e7d65
+size 16933
diff --git a/Assets/Plugins/DynamicPanels/Sprites/CloseButton.psd.meta b/Assets/Plugins/DynamicPanels/Sprites/CloseButton.psd.meta
index 0f9b5e908f..caa815c606 100644
--- a/Assets/Plugins/DynamicPanels/Sprites/CloseButton.psd.meta
+++ b/Assets/Plugins/DynamicPanels/Sprites/CloseButton.psd.meta
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d067dd7413834d32f5666a6cd2e1abf6701d6b0c3d3476918b3fe9dde800a368
-size 3689
+oid sha256:3d1f9851961cf4a18ec5edb197c861dbff3ec48020c166d9cc261fb1bc2aa726
+size 3333
diff --git a/Assets/Plugins/DynamicPanels/Sprites/PanelBackground.psd b/Assets/Plugins/DynamicPanels/Sprites/PanelBackground.psd
index b0bff45373..103c1d8b32 100644
--- a/Assets/Plugins/DynamicPanels/Sprites/PanelBackground.psd
+++ b/Assets/Plugins/DynamicPanels/Sprites/PanelBackground.psd
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5f7f47b3bea2c776e9d3832f1d75b9527a83fd49a80673c590f993ae447ce9e2
-size 28594
+oid sha256:f0819ac14b54c236f5d57f070e3731b47221a0739f2293218687502f1c91b612
+size 20830
diff --git a/Assets/Plugins/DynamicPanels/Sprites/PanelBackground.psd.meta b/Assets/Plugins/DynamicPanels/Sprites/PanelBackground.psd.meta
index 8b9161ad2f..38a6a61484 100644
--- a/Assets/Plugins/DynamicPanels/Sprites/PanelBackground.psd.meta
+++ b/Assets/Plugins/DynamicPanels/Sprites/PanelBackground.psd.meta
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c21901cca5f8f65010d1d20cdf4aef47b06b2bde2f7a0af7f68b80a0db689503
-size 3691
+oid sha256:c21c69b8c1467aeba627ec66fbb05e965526a4461b6d2e3c2b50ecb063549c75
+size 3335
diff --git a/Assets/Plugins/DynamicPanels/Sprites/PanelTabBackground.psd b/Assets/Plugins/DynamicPanels/Sprites/PanelTabBackground.psd
index 2dcf1d0a68..be55a59ba1 100644
--- a/Assets/Plugins/DynamicPanels/Sprites/PanelTabBackground.psd
+++ b/Assets/Plugins/DynamicPanels/Sprites/PanelTabBackground.psd
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:674a26e76622100a9ee6655e49029aaceb966692a0064484cb66114dd35feb8f
-size 30419
+oid sha256:7102ee4e5141de8665932ed064d92b6888e4ea7197e9409bd3a3ddb4cf022dd8
+size 30455
diff --git a/Assets/Plugins/DynamicPanels/Sprites/PanelTabBackground.psd.meta b/Assets/Plugins/DynamicPanels/Sprites/PanelTabBackground.psd.meta
index 5a029c241e..1da7ab182c 100644
--- a/Assets/Plugins/DynamicPanels/Sprites/PanelTabBackground.psd.meta
+++ b/Assets/Plugins/DynamicPanels/Sprites/PanelTabBackground.psd.meta
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b2533d8cd3a4c185f921b15db01eb0e47689b3389c23f0579767d1f5a60b5c27
-size 3691
+oid sha256:4f799d41799b7c691fe7979ae9ccdffe3e63bd8cd06e685b09a3da9d9f7db40f
+size 3335
diff --git a/Assets/Plugins/Parser/GenerateLexerCode.bat.meta b/Assets/Plugins/Parser/GenerateLexerCode.bat.meta
index 07d7b89d66..94104109e3 100644
--- a/Assets/Plugins/Parser/GenerateLexerCode.bat.meta
+++ b/Assets/Plugins/Parser/GenerateLexerCode.bat.meta
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6652ab456d19e61b67edcd4ba6e3c12bb8270f9d2d0b914a159a84cd16ebd1ba
-size 326
+oid sha256:df1de65e582508ffc9103391ee947a271024b560e46a047bf2b79bd1f40c782e
+size 152
diff --git a/Assets/Resources/Prefabs/Charts.meta b/Assets/Resources/Prefabs/Charts.meta
index e495525440..db01f48d30 100644
--- a/Assets/Resources/Prefabs/Charts.meta
+++ b/Assets/Resources/Prefabs/Charts.meta
@@ -1,12 +1,8 @@
fileFormatVersion: 2
-<<<<<<< HEAD:Assets/Dependencies.meta
-guid: 31815e5d24ab3b84fb9d47cacc562874
-=======
guid: fb21df1ff65c7ca479f097c3034c820d
->>>>>>> origin/master:Assets/Prefabs/Charts.meta
folderAsset: yes
DefaultImporter:
externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/Prefabs/UI/PopupMenuSubMenuButton.prefab b/Assets/Resources/Prefabs/UI/PopupMenuSubMenuButton.prefab
new file mode 100644
index 0000000000..b4c3703b52
--- /dev/null
+++ b/Assets/Resources/Prefabs/UI/PopupMenuSubMenuButton.prefab
@@ -0,0 +1,659 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &820030453018440495
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4641039880100362998}
+ - component: {fileID: 37939026862503639}
+ - component: {fileID: 6165813934536348160}
+ m_Layer: 5
+ m_Name: Icon
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &4641039880100362998
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 820030453018440495}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 8499577916647195967}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 0, y: 0.5}
+ m_AnchoredPosition: {x: 23.3, y: 0}
+ m_SizeDelta: {x: 30, y: 30}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &37939026862503639
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 820030453018440495}
+ m_CullTransparentMesh: 0
+--- !u!114 &6165813934536348160
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 820030453018440495}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: "\uF05A"
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 4ebb98a3c87fa521a888029274c92b79, type: 2}
+ m_sharedMaterial: {fileID: -8620075009897487826, guid: 4ebb98a3c87fa521a888029274c92b79,
+ type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 30
+ m_fontSizeBase: 30
+ m_fontWeight: 400
+ m_enableAutoSizing: 0
+ m_fontSizeMin: 18
+ m_fontSizeMax: 72
+ m_fontStyle: 0
+ m_HorizontalAlignment: 2
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 0
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 0
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &1272595426619240444
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8499577916647195967}
+ - component: {fileID: 5054268177811721389}
+ - component: {fileID: 6389154563960229086}
+ - component: {fileID: 427450053229378955}
+ - component: {fileID: 8658436434225420978}
+ m_Layer: 5
+ m_Name: PopupMenuSubMenuButton
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8499577916647195967
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1272595426619240444}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 4641039880100362998}
+ - {fileID: 4683048119979389847}
+ - {fileID: 8452244492862562955}
+ - {fileID: 5586743027034129599}
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 55}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5054268177811721389
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1272595426619240444}
+ m_CullTransparentMesh: 0
+--- !u!114 &6389154563960229086
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1272595426619240444}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 0
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.84313726, g: 0.84313726, b: 0.84313726, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_SelectedColor: {r: 0.84313726, g: 0.84313726, b: 0.84313726, a: 1}
+ m_DisabledColor: {r: 1, g: 1, b: 1, a: 0.39215687}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Highlighted
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 427450053229378955}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &427450053229378955
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1272595426619240444}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.33333334, g: 0.37254903, b: 0.4509804, 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: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 12
+--- !u!114 &8658436434225420978
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1272595426619240444}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 1a12ddbc47b17cd478cb447d1113a22b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ buttonText: Test Action
+ clickEvent:
+ m_PersistentCalls:
+ m_Calls: []
+ hoverEvent:
+ m_PersistentCalls:
+ m_Calls: []
+ hoverSound: {fileID: 0}
+ clickSound: {fileID: 0}
+ buttonVar: {fileID: 0}
+ normalText: {fileID: 792250872584375360}
+ soundSource: {fileID: 0}
+ rippleParent: {fileID: 7494196715945445660}
+ useCustomContent: 0
+ enableButtonSounds: 0
+ useHoverSound: 1
+ useClickSound: 1
+ useRipple: 1
+ rippleUpdateMode: 1
+ rippleShape: {fileID: 0}
+ speed: 1
+ maxSize: 4
+ startColor: {r: 1, g: 1, b: 1, a: 1}
+ transitionColor: {r: 1, g: 1, b: 1, a: 0}
+ renderOnTop: 0
+ centered: 0
+--- !u!1 &1970246436482945221
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5586743027034129599}
+ - component: {fileID: 5761857674070296453}
+ - component: {fileID: 7538378613054711224}
+ m_Layer: 5
+ m_Name: RightIcon
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5586743027034129599
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1970246436482945221}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 8499577916647195967}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 1, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: -40, y: 0}
+ m_SizeDelta: {x: 20, y: 20}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5761857674070296453
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1970246436482945221}
+ m_CullTransparentMesh: 0
+--- !u!114 &7538378613054711224
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1970246436482945221}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: "\uF05A"
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 4ebb98a3c87fa521a888029274c92b79, type: 2}
+ m_sharedMaterial: {fileID: -8620075009897487826, guid: 4ebb98a3c87fa521a888029274c92b79,
+ type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 20
+ m_fontSizeBase: 20
+ m_fontWeight: 400
+ m_enableAutoSizing: 0
+ m_fontSizeMin: 18
+ m_fontSizeMax: 72
+ m_fontStyle: 0
+ m_HorizontalAlignment: 2
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 0
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 0
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &4359041347979151087
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4683048119979389847}
+ - component: {fileID: 5059500120611322657}
+ - component: {fileID: 792250872584375360}
+ m_Layer: 5
+ m_Name: Text
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &4683048119979389847
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4359041347979151087}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 8499577916647195967}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: -2.5, y: 0}
+ m_SizeDelta: {x: -95, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5059500120611322657
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4359041347979151087}
+ m_CullTransparentMesh: 0
+--- !u!114 &792250872584375360
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4359041347979151087}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: Test Action
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 84dd14695854bbc43a5faa24fcf93d0d, type: 2}
+ m_sharedMaterial: {fileID: 21261991626553910, guid: 84dd14695854bbc43a5faa24fcf93d0d,
+ type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 18
+ m_fontSizeBase: 22.5
+ m_fontWeight: 400
+ m_enableAutoSizing: 1
+ m_fontSizeMin: 18
+ m_fontSizeMax: 23
+ m_fontStyle: 0
+ m_HorizontalAlignment: 2
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 1
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 0
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 1
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &7494196715945445660
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8452244492862562955}
+ - component: {fileID: 8817740310236070429}
+ - component: {fileID: 1667475523720144969}
+ - component: {fileID: 8929932097463880863}
+ m_Layer: 5
+ m_Name: Ripple
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8452244492862562955
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7494196715945445660}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 8499577916647195967}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &8817740310236070429
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7494196715945445660}
+ m_CullTransparentMesh: 0
+--- !u!114 &1667475523720144969
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7494196715945445660}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 0
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 21300000, guid: 951352f31055aae46b6e9786313c632d, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 12
+--- !u!114 &8929932097463880863
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7494196715945445660}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_ShowMaskGraphic: 0
diff --git a/Assets/Resources/Prefabs/UI/PopupMenuSubMenuButton.prefab.meta b/Assets/Resources/Prefabs/UI/PopupMenuSubMenuButton.prefab.meta
new file mode 100644
index 0000000000..c8e8743605
--- /dev/null
+++ b/Assets/Resources/Prefabs/UI/PopupMenuSubMenuButton.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 91ef025b2e6b81541b7dd4634f41651e
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/Prefabs/UI/PropertyGroupItem.prefab b/Assets/Resources/Prefabs/UI/PropertyGroupItem.prefab
new file mode 100644
index 0000000000..f4b82b8127
--- /dev/null
+++ b/Assets/Resources/Prefabs/UI/PropertyGroupItem.prefab
@@ -0,0 +1,463 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3221561598132462901
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3766619498664164433}
+ - component: {fileID: 6994604829827172676}
+ - component: {fileID: 8043074890402026029}
+ m_Layer: 5
+ m_Name: PropertyGroupItem
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &3766619498664164433
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3221561598132462901}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 7801144434069277683}
+ - {fileID: 766617590976963954}
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 50}
+ m_Pivot: {x: 0, y: 1}
+--- !u!114 &6994604829827172676
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3221561598132462901}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 3
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+ m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Selected
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 6592963879312978613}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &8043074890402026029
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3221561598132462901}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 52dd8aaa3b5d4058ac1b1e9242d35f57, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+--- !u!1 &6543745927510804026
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4151804783001895320}
+ - component: {fileID: 3158015502856716037}
+ - component: {fileID: 8716019332972553389}
+ m_Layer: 5
+ m_Name: Expand Icon
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &4151804783001895320
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6543745927510804026}
+ m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071068}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 766617590976963954}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: -90}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 0, y: 0.5}
+ m_AnchoredPosition: {x: 15, y: 0}
+ m_SizeDelta: {x: 25, y: 35}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3158015502856716037
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6543745927510804026}
+ m_CullTransparentMesh: 1
+--- !u!114 &8716019332972553389
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6543745927510804026}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.6901961, g: 0.6901961, b: 0.6901961, 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: 21300000, guid: 4b835e6972939d148aa7acfed964b9a6, type: 3}
+ m_Type: 0
+ m_PreserveAspect: 1
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 1
+--- !u!1 &8015187158269920060
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6174073020406636757}
+ - component: {fileID: 9133677281363212946}
+ - component: {fileID: 8844280093565481701}
+ m_Layer: 5
+ m_Name: AttributeLine
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &6174073020406636757
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8015187158269920060}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 766617590976963954}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: -100, y: 40}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &9133677281363212946
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8015187158269920060}
+ m_CullTransparentMesh: 1
+--- !u!114 &8844280093565481701
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8015187158269920060}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: IfYouCanSeeThisIDidSomethingWrongThisIsJustAVeryLongStringOfTextToTestTheOverflowCapabilitiesOfThisTextMeshProObjectSoTheHopeIsThatTheTextDoesNotOverflowBeyondTheBoundsOfTheItem
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+ m_sharedMaterial: {fileID: 2100000, guid: 79459efec17a4d00a321bdcc27bbc385, type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 18
+ m_fontSizeBase: 26
+ m_fontWeight: 400
+ m_enableAutoSizing: 1
+ m_fontSizeMin: 18
+ m_fontSizeMax: 30
+ m_fontStyle: 0
+ m_HorizontalAlignment: 1
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 1
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 1
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &8476088519983172622
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 766617590976963954}
+ m_Layer: 5
+ m_Name: Foreground
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &766617590976963954
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8476088519983172622}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 4151804783001895320}
+ - {fileID: 6174073020406636757}
+ m_Father: {fileID: 3766619498664164433}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 50}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &8970180186745827142
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7801144434069277683}
+ - component: {fileID: 5745935338147661148}
+ - component: {fileID: 6592963879312978613}
+ - component: {fileID: 2899379093387412028}
+ m_Layer: 5
+ m_Name: Background
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7801144434069277683
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8970180186745827142}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 3766619498664164433}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 50}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5745935338147661148
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8970180186745827142}
+ m_CullTransparentMesh: 1
+--- !u!114 &6592963879312978613
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8970180186745827142}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_Texture: {fileID: 0}
+ m_UVRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+--- !u!114 &2899379093387412028
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8970180186745827142}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: a9d2f9067277ade4e9b47012ea4d9e17, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _gradientType: 0
+ _blendMode: 2
+ _modifyVertices: 0
+ _offset: 0
+ _zoom: 1
+ _effectGradient:
+ serializedVersion: 2
+ key0: {r: 0, g: 0.15059185, b: 1, a: 1}
+ key1: {r: 0, g: 0.0724779, b: 0.5803922, a: 1}
+ key2: {r: 0, g: 0.0724779, b: 0.5803922, a: 1}
+ key3: {r: 0, g: 0, b: 0, a: 0}
+ key4: {r: 0, g: 0, b: 0, a: 0}
+ key5: {r: 0, g: 0, b: 0, a: 0}
+ key6: {r: 0, g: 0, b: 0, a: 0}
+ key7: {r: 0, g: 0, b: 0, a: 0}
+ ctime0: 0
+ ctime1: 65535
+ ctime2: 65535
+ ctime3: 0
+ ctime4: 0
+ ctime5: 0
+ ctime6: 0
+ ctime7: 0
+ atime0: 0
+ atime1: 65535
+ atime2: 65535
+ atime3: 0
+ atime4: 0
+ atime5: 0
+ atime6: 0
+ atime7: 0
+ m_Mode: 2
+ m_ColorSpace: 0
+ m_NumColorKeys: 2
+ m_NumAlphaKeys: 2
diff --git a/Assets/Resources/Prefabs/UI/PropertyGroupItem.prefab.meta b/Assets/Resources/Prefabs/UI/PropertyGroupItem.prefab.meta
new file mode 100644
index 0000000000..f8fa8ec539
--- /dev/null
+++ b/Assets/Resources/Prefabs/UI/PropertyGroupItem.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 738f21c9185c957488977a5b634dbc8b
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/Prefabs/UI/PropertyRowLine.prefab b/Assets/Resources/Prefabs/UI/PropertyRowLine.prefab
new file mode 100644
index 0000000000..a3f044d94e
--- /dev/null
+++ b/Assets/Resources/Prefabs/UI/PropertyRowLine.prefab
@@ -0,0 +1,473 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &4232413688770282557
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4417611932811040179}
+ - component: {fileID: 7221035067078308661}
+ m_Layer: 5
+ m_Name: PropertyRowLine
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &4417611932811040179
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4232413688770282557}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 804944811992268710}
+ - {fileID: 583018592755812378}
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 50}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7221035067078308661
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4232413688770282557}
+ m_CullTransparentMesh: 1
+--- !u!1 &5351844536383209910
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8247726193395324134}
+ - component: {fileID: 3220663573982420325}
+ - component: {fileID: 3499561155651974852}
+ m_Layer: 5
+ m_Name: AttributeLine
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8247726193395324134
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5351844536383209910}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 583018592755812378}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 7.5, y: 0}
+ m_SizeDelta: {x: -15, y: 50}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3220663573982420325
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5351844536383209910}
+ m_CullTransparentMesh: 1
+--- !u!114 &3499561155651974852
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5351844536383209910}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: Attribute
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+ m_sharedMaterial: {fileID: 2100000, guid: 79459efec17a4d00a321bdcc27bbc385, type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4278190080
+ m_fontColor: {r: 0, g: 0, b: 0, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 19.95
+ m_fontSizeBase: 36
+ m_fontWeight: 400
+ m_enableAutoSizing: 1
+ m_fontSizeMin: 18
+ m_fontSizeMax: 30
+ m_fontStyle: 0
+ m_HorizontalAlignment: 1
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 1
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 1
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 20, y: 0, z: -109.62303, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &5451767514778989835
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 804944811992268710}
+ - component: {fileID: 2395147641264057623}
+ - component: {fileID: 2323185834611997723}
+ - component: {fileID: 8683645501149662081}
+ m_Layer: 5
+ m_Name: Background
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &804944811992268710
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5451767514778989835}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 4417611932811040179}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 50}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &2395147641264057623
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5451767514778989835}
+ m_CullTransparentMesh: 1
+--- !u!114 &2323185834611997723
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5451767514778989835}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_Texture: {fileID: 0}
+ m_UVRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+--- !u!114 &8683645501149662081
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5451767514778989835}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: a9d2f9067277ade4e9b47012ea4d9e17, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ _gradientType: 0
+ _blendMode: 2
+ _modifyVertices: 0
+ _offset: 0
+ _zoom: 1
+ _effectGradient:
+ serializedVersion: 2
+ key0: {r: 0, g: 0.15059185, b: 1, a: 1}
+ key1: {r: 0, g: 0.0724779, b: 0.5803922, a: 1}
+ key2: {r: 0, g: 0.0724779, b: 0.5803922, a: 1}
+ key3: {r: 0, g: 0, b: 0, a: 0}
+ key4: {r: 0, g: 0, b: 0, a: 0}
+ key5: {r: 0, g: 0, b: 0, a: 0}
+ key6: {r: 0, g: 0, b: 0, a: 0}
+ key7: {r: 0, g: 0, b: 0, a: 0}
+ ctime0: 0
+ ctime1: 65535
+ ctime2: 65535
+ ctime3: 0
+ ctime4: 0
+ ctime5: 0
+ ctime6: 0
+ ctime7: 0
+ atime0: 0
+ atime1: 65535
+ atime2: 65535
+ atime3: 0
+ atime4: 0
+ atime5: 0
+ atime6: 0
+ atime7: 0
+ m_Mode: 2
+ m_ColorSpace: 0
+ m_NumColorKeys: 2
+ m_NumAlphaKeys: 2
+--- !u!1 &6677135121746064833
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2256359333229225443}
+ - component: {fileID: 5575769617067488252}
+ - component: {fileID: 5791917304804281608}
+ m_Layer: 5
+ m_Name: ValueLine
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2256359333229225443
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6677135121746064833}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 583018592755812378}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: -7.5, y: 0}
+ m_SizeDelta: {x: -15, y: 50}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5575769617067488252
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6677135121746064833}
+ m_CullTransparentMesh: 1
+--- !u!114 &5791917304804281608
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6677135121746064833}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: Value
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
+ m_sharedMaterial: {fileID: 2100000, guid: 79459efec17a4d00a321bdcc27bbc385, type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4278190080
+ m_fontColor: {r: 0, g: 0, b: 0, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 18
+ m_fontSizeBase: 36
+ m_fontWeight: 400
+ m_enableAutoSizing: 1
+ m_fontSizeMin: 18
+ m_fontSizeMax: 30
+ m_fontStyle: 0
+ m_HorizontalAlignment: 4
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 1
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 1
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 198.69238, y: 0, z: 16.507019, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &7935205825449248997
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 583018592755812378}
+ m_Layer: 5
+ m_Name: Foreground
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &583018592755812378
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7935205825449248997}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 8247726193395324134}
+ - {fileID: 2256359333229225443}
+ m_Father: {fileID: 4417611932811040179}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 50}
+ m_Pivot: {x: 0.5, y: 0.5}
diff --git a/Assets/Resources/Prefabs/UI/PropertyRowLine.prefab.meta b/Assets/Resources/Prefabs/UI/PropertyRowLine.prefab.meta
new file mode 100644
index 0000000000..6968ae97dc
--- /dev/null
+++ b/Assets/Resources/Prefabs/UI/PropertyRowLine.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: c3315def80cb34b41a42daf3a52848b8
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/Prefabs/UI/PropertyWindow.prefab b/Assets/Resources/Prefabs/UI/PropertyWindow.prefab
new file mode 100644
index 0000000000..6381ad29ce
--- /dev/null
+++ b/Assets/Resources/Prefabs/UI/PropertyWindow.prefab
@@ -0,0 +1,2910 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &709606682935592078
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1089177651062366406}
+ - component: {fileID: 5618579176187327547}
+ - component: {fileID: 6969816262348232183}
+ m_Layer: 5
+ m_Name: Items
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &1089177651062366406
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 709606682935592078}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1176873081988812620}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 1}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 1}
+--- !u!114 &5618579176187327547
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 709606682935592078}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Padding:
+ m_Left: 5
+ m_Right: 10
+ m_Top: 5
+ m_Bottom: 5
+ m_ChildAlignment: 0
+ m_Spacing: 5
+ m_ChildForceExpandWidth: 1
+ m_ChildForceExpandHeight: 0
+ m_ChildControlWidth: 1
+ m_ChildControlHeight: 0
+ m_ChildScaleWidth: 0
+ m_ChildScaleHeight: 0
+ m_ReverseArrangement: 0
+--- !u!114 &6969816262348232183
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 709606682935592078}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_HorizontalFit: 0
+ m_VerticalFit: 1
+--- !u!1 &842318586328925508
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4599480176814839446}
+ - component: {fileID: 388032255706994378}
+ - component: {fileID: 4618368004867492041}
+ m_Layer: 5
+ m_Name: Placeholder
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &4599480176814839446
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 842318586328925508}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 6690282667262079164}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 15, y: 0.5}
+ m_SizeDelta: {x: -30, y: -1}
+ m_Pivot: {x: 0, y: 0.5}
+--- !u!222 &388032255706994378
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 842318586328925508}
+ m_CullTransparentMesh: 0
+--- !u!114 &4618368004867492041
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 842318586328925508}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: Search...
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 4bd810f1cbcb0f446a8f5a31453e243f, type: 2}
+ m_sharedMaterial: {fileID: 21539420542967178, guid: 4bd810f1cbcb0f446a8f5a31453e243f,
+ type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 2952790015
+ m_fontColor: {r: 1, g: 1, b: 1, a: 0.6862745}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 28
+ m_fontSizeBase: 28
+ m_fontWeight: 400
+ m_enableAutoSizing: 0
+ m_fontSizeMin: 18
+ m_fontSizeMax: 72
+ m_fontStyle: 0
+ m_HorizontalAlignment: 1
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 1
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 1
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 1
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &1837815958501779517
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7563112671056238965}
+ - component: {fileID: 3089042720783283610}
+ - component: {fileID: 3525724078483443591}
+ m_Layer: 5
+ m_Name: Icon
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7563112671056238965
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1837815958501779517}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1841548754507413200}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: -25, y: -25}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3089042720783283610
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1837815958501779517}
+ m_CullTransparentMesh: 0
+--- !u!114 &3525724078483443591
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1837815958501779517}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: "\uF0B0"
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 4ebb98a3c87fa521a888029274c92b79, type: 2}
+ m_sharedMaterial: {fileID: -8620075009897487826, guid: 4ebb98a3c87fa521a888029274c92b79,
+ type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 36
+ m_fontSizeBase: 36
+ m_fontWeight: 400
+ m_enableAutoSizing: 0
+ m_fontSizeMin: 18
+ m_fontSizeMax: 72
+ m_fontStyle: 0
+ m_HorizontalAlignment: 2
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 1
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 0
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &1874176378163580936
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7136314229054424447}
+ - component: {fileID: 5555767391713249978}
+ - component: {fileID: 7231648946806373039}
+ - component: {fileID: 7734883416778855734}
+ - component: {fileID: 4547095341203468177}
+ - component: {fileID: 6439533209827356462}
+ m_Layer: 5
+ m_Name: Group
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7136314229054424447
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1874176378163580936}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 7073849410189226427}
+ - {fileID: 6855993087584589959}
+ m_Father: {fileID: 8455627934270354126}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 55}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5555767391713249978
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1874176378163580936}
+ m_CullTransparentMesh: 0
+--- !u!114 &7231648946806373039
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1874176378163580936}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 0
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.84313726, g: 0.84313726, b: 0.84313726, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_SelectedColor: {r: 0.84313726, g: 0.84313726, b: 0.84313726, a: 1}
+ m_DisabledColor: {r: 1, g: 1, b: 1, a: 0.39215687}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Highlighted
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 7734883416778855734}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &7734883416778855734
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1874176378163580936}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.33333334, g: 0.37254903, b: 0.4509804, 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: 21300000, guid: 5e16c7aea118d68498053518146c9cf9, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 13
+--- !u!114 &4547095341203468177
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1874176378163580936}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 1a12ddbc47b17cd478cb447d1113a22b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ buttonText: "\uF5FD"
+ clickEvent:
+ m_PersistentCalls:
+ m_Calls: []
+ hoverEvent:
+ m_PersistentCalls:
+ m_Calls: []
+ hoverSound: {fileID: 0}
+ clickSound: {fileID: 0}
+ buttonVar: {fileID: 0}
+ normalText: {fileID: 8146306459036859299}
+ soundSource: {fileID: 0}
+ rippleParent: {fileID: 6395484848576167605}
+ useCustomContent: 0
+ enableButtonSounds: 0
+ useHoverSound: 1
+ useClickSound: 1
+ useRipple: 1
+ rippleUpdateMode: 1
+ rippleShape: {fileID: 0}
+ speed: 1
+ maxSize: 4
+ startColor: {r: 1, g: 1, b: 1, a: 1}
+ transitionColor: {r: 1, g: 1, b: 1, a: 0}
+ renderOnTop: 0
+ centered: 0
+--- !u!114 &6439533209827356462
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1874176378163580936}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreLayout: 0
+ m_MinWidth: 55
+ m_MinHeight: -1
+ m_PreferredWidth: 55
+ m_PreferredHeight: -1
+ m_FlexibleWidth: -1
+ m_FlexibleHeight: -1
+ m_LayoutPriority: 1
+--- !u!1 &1959865772248175663
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7739412744805412235}
+ - component: {fileID: 7909130829181353198}
+ - component: {fileID: 7427878997772969028}
+ - component: {fileID: 8280760184951877564}
+ - component: {fileID: 8663104550250191}
+ - component: {fileID: 2180345664057754735}
+ m_Layer: 5
+ m_Name: Sort
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7739412744805412235
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1959865772248175663}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 6965380495096359521}
+ - {fileID: 3595538001728667684}
+ m_Father: {fileID: 8455627934270354126}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 55}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7909130829181353198
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1959865772248175663}
+ m_CullTransparentMesh: 0
+--- !u!114 &7427878997772969028
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1959865772248175663}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 0
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.84313726, g: 0.84313726, b: 0.84313726, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_SelectedColor: {r: 0.84313726, g: 0.84313726, b: 0.84313726, a: 1}
+ m_DisabledColor: {r: 1, g: 1, b: 1, a: 0.39215687}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Highlighted
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 8280760184951877564}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &8280760184951877564
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1959865772248175663}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.33333334, g: 0.37254903, b: 0.4509804, 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: 21300000, guid: 5e16c7aea118d68498053518146c9cf9, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 13
+--- !u!114 &8663104550250191
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1959865772248175663}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 1a12ddbc47b17cd478cb447d1113a22b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ buttonText: "\uF0DC"
+ clickEvent:
+ m_PersistentCalls:
+ m_Calls: []
+ hoverEvent:
+ m_PersistentCalls:
+ m_Calls: []
+ hoverSound: {fileID: 0}
+ clickSound: {fileID: 0}
+ buttonVar: {fileID: 0}
+ normalText: {fileID: 4794745068905858350}
+ soundSource: {fileID: 0}
+ rippleParent: {fileID: 9041207098413024880}
+ useCustomContent: 0
+ enableButtonSounds: 0
+ useHoverSound: 1
+ useClickSound: 1
+ useRipple: 1
+ rippleUpdateMode: 1
+ rippleShape: {fileID: 0}
+ speed: 1
+ maxSize: 4
+ startColor: {r: 1, g: 1, b: 1, a: 1}
+ transitionColor: {r: 1, g: 1, b: 1, a: 0}
+ renderOnTop: 0
+ centered: 0
+--- !u!114 &2180345664057754735
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1959865772248175663}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreLayout: 0
+ m_MinWidth: 55
+ m_MinHeight: -1
+ m_PreferredWidth: 55
+ m_PreferredHeight: -1
+ m_FlexibleWidth: -1
+ m_FlexibleHeight: -1
+ m_LayoutPriority: 1
+--- !u!1 &2125963005985707447
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7412864419818588360}
+ - component: {fileID: 3850865178301769102}
+ - component: {fileID: 1051915075008402236}
+ m_Layer: 5
+ m_Name: Background
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7412864419818588360
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2125963005985707447}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 612935842169716872}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: -10, y: -10}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3850865178301769102
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2125963005985707447}
+ m_CullTransparentMesh: 0
+--- !u!114 &1051915075008402236
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2125963005985707447}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 0.09803922}
+ 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: 21300000, guid: 5618123237d1d3f49a5a6025287065f7, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 15
+--- !u!1 &2441149418491542768
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1841548754507413200}
+ - component: {fileID: 8114760013786331298}
+ - component: {fileID: 8202626990950085013}
+ - component: {fileID: 8017296535842955226}
+ - component: {fileID: 1208407751294909222}
+ - component: {fileID: 8156587985265908029}
+ m_Layer: 5
+ m_Name: Filter
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &1841548754507413200
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2441149418491542768}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 7563112671056238965}
+ - {fileID: 2393231344255378604}
+ m_Father: {fileID: 8455627934270354126}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 55}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &8114760013786331298
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2441149418491542768}
+ m_CullTransparentMesh: 0
+--- !u!114 &8202626990950085013
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2441149418491542768}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 0
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.84313726, g: 0.84313726, b: 0.84313726, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_SelectedColor: {r: 0.84313726, g: 0.84313726, b: 0.84313726, a: 1}
+ m_DisabledColor: {r: 1, g: 1, b: 1, a: 0.39215687}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Highlighted
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 8017296535842955226}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &8017296535842955226
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2441149418491542768}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.33333334, g: 0.37254903, b: 0.4509804, 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: 21300000, guid: 5e16c7aea118d68498053518146c9cf9, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 13
+--- !u!114 &1208407751294909222
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2441149418491542768}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 1a12ddbc47b17cd478cb447d1113a22b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ buttonText: "\uF0B0"
+ clickEvent:
+ m_PersistentCalls:
+ m_Calls: []
+ hoverEvent:
+ m_PersistentCalls:
+ m_Calls: []
+ hoverSound: {fileID: 0}
+ clickSound: {fileID: 0}
+ buttonVar: {fileID: 0}
+ normalText: {fileID: 3525724078483443591}
+ soundSource: {fileID: 0}
+ rippleParent: {fileID: 6040598831507082404}
+ useCustomContent: 0
+ enableButtonSounds: 0
+ useHoverSound: 1
+ useClickSound: 1
+ useRipple: 1
+ rippleUpdateMode: 1
+ rippleShape: {fileID: 0}
+ speed: 1
+ maxSize: 4
+ startColor: {r: 1, g: 1, b: 1, a: 1}
+ transitionColor: {r: 1, g: 1, b: 1, a: 0}
+ renderOnTop: 0
+ centered: 0
+--- !u!114 &8156587985265908029
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2441149418491542768}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreLayout: 0
+ m_MinWidth: 55
+ m_MinHeight: -1
+ m_PreferredWidth: 55
+ m_PreferredHeight: -1
+ m_FlexibleWidth: -1
+ m_FlexibleHeight: -1
+ m_LayoutPriority: 1
+--- !u!1 &2468651571960220266
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1176873081988812620}
+ - component: {fileID: 2060077036330234244}
+ - component: {fileID: 6932287645544762951}
+ - component: {fileID: 3122064075779552067}
+ m_Layer: 5
+ m_Name: Content
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &1176873081988812620
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2468651571960220266}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1089177651062366406}
+ - {fileID: 2230885584348632300}
+ - {fileID: 612935842169716872}
+ m_Father: {fileID: 6068148083340061293}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: -30}
+ m_SizeDelta: {x: 0, y: -60}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &2060077036330234244
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2468651571960220266}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_ShowMaskGraphic: 0
+--- !u!222 &6932287645544762951
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2468651571960220266}
+ m_CullTransparentMesh: 1
+--- !u!114 &3122064075779552067
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2468651571960220266}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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: 0}
+ m_Type: 0
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 1
+--- !u!1 &2689018009846703236
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8793840307802463971}
+ - component: {fileID: 2382594265750061630}
+ m_Layer: 5
+ m_Name: Text Area
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8793840307802463971
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2689018009846703236}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 8397262446398887593}
+ m_Father: {fileID: 6690282667262079164}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &2382594265750061630
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2689018009846703236}
+ m_CullTransparentMesh: 0
+--- !u!1 &2789169427069279165
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7349605152706409631}
+ - component: {fileID: 4665694166864105030}
+ - component: {fileID: 7726245115218372043}
+ m_Layer: 5
+ m_Name: Filled
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7349605152706409631
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2789169427069279165}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 2871116414277210878}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &4665694166864105030
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2789169427069279165}
+ m_CullTransparentMesh: 0
+--- !u!114 &7726245115218372043
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2789169427069279165}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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: 21300000, guid: 5e16c7aea118d68498053518146c9cf9, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 0
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 10
+--- !u!1 &3452377398954294559
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6965380495096359521}
+ - component: {fileID: 1323069487810200289}
+ - component: {fileID: 4794745068905858350}
+ m_Layer: 5
+ m_Name: Icon
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &6965380495096359521
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3452377398954294559}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 7739412744805412235}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: -25, y: -25}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &1323069487810200289
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3452377398954294559}
+ m_CullTransparentMesh: 0
+--- !u!114 &4794745068905858350
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3452377398954294559}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: "\uF0DC"
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 4ebb98a3c87fa521a888029274c92b79, type: 2}
+ m_sharedMaterial: {fileID: -8620075009897487826, guid: 4ebb98a3c87fa521a888029274c92b79,
+ type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 36
+ m_fontSizeBase: 36
+ m_fontWeight: 400
+ m_enableAutoSizing: 0
+ m_fontSizeMin: 18
+ m_fontSizeMax: 72
+ m_fontStyle: 0
+ m_HorizontalAlignment: 2
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 1
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 0
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &3892552716093057929
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2548444607870852420}
+ m_Layer: 5
+ m_Name: Sliding Area
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2548444607870852420
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3892552716093057929}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 9066124595810444874}
+ m_Father: {fileID: 2230885584348632300}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: -10, y: -10}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &4293368334216072556
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6068148083340061293}
+ - component: {fileID: 1489559290100196060}
+ - component: {fileID: 2394539210909514289}
+ - component: {fileID: 8084997774507780795}
+ - component: {fileID: 4502254089515244849}
+ - component: {fileID: 7147490681011772360}
+ m_Layer: 5
+ m_Name: PropertyWindow
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &6068148083340061293
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4293368334216072556}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1176873081988812620}
+ - {fileID: 8455627934270354126}
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &1489559290100196060
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4293368334216072556}
+ m_CullTransparentMesh: 0
+--- !u!114 &2394539210909514289
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4293368334216072556}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Content: {fileID: 1089177651062366406}
+ m_Horizontal: 1
+ m_Vertical: 1
+ m_MovementType: 2
+ m_Elasticity: 0.1
+ m_Inertia: 1
+ m_DecelerationRate: 0.135
+ m_ScrollSensitivity: 20
+ m_Viewport: {fileID: 1176873081988812620}
+ m_HorizontalScrollbar: {fileID: 7171935558317943900}
+ m_VerticalScrollbar: {fileID: 5270351434699486664}
+ m_HorizontalScrollbarVisibility: 1
+ m_VerticalScrollbarVisibility: 0
+ m_HorizontalScrollbarSpacing: 0
+ m_VerticalScrollbarSpacing: 0
+ m_OnValueChanged:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &8084997774507780795
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4293368334216072556}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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: 0}
+ m_Type: 0
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 1
+--- !u!114 &4502254089515244849
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4293368334216072556}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_ShowMaskGraphic: 0
+--- !u!114 &7147490681011772360
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4293368334216072556}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d0b148fe25e99eb48b9724523833bab1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Delegates:
+ - eventID: 7
+ callback:
+ m_PersistentCalls:
+ m_Calls: []
+ - eventID: 14
+ callback:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!1 &4624945097066885423
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8397262446398887593}
+ - component: {fileID: 7753730364065013434}
+ - component: {fileID: 6807727362598245007}
+ m_Layer: 5
+ m_Name: Text
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8397262446398887593
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4624945097066885423}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 8793840307802463971}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0.5}
+ m_SizeDelta: {x: -30, y: -1}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7753730364065013434
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4624945097066885423}
+ m_CullTransparentMesh: 0
+--- !u!114 &6807727362598245007
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4624945097066885423}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: "\u200B"
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 4bd810f1cbcb0f446a8f5a31453e243f, type: 2}
+ m_sharedMaterial: {fileID: 21539420542967178, guid: 4bd810f1cbcb0f446a8f5a31453e243f,
+ type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 28
+ m_fontSizeBase: 28
+ m_fontWeight: 400
+ m_enableAutoSizing: 0
+ m_fontSizeMin: 18
+ m_fontSizeMax: 72
+ m_fontStyle: 0
+ m_HorizontalAlignment: 1
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 0
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 1
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 1
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &4790813342650741183
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 9066124595810444874}
+ - component: {fileID: 4031127889731311101}
+ - component: {fileID: 4647380939240487652}
+ m_Layer: 5
+ m_Name: Handle
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &9066124595810444874
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4790813342650741183}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 2548444607870852420}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &4031127889731311101
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4790813342650741183}
+ m_CullTransparentMesh: 0
+--- !u!114 &4647380939240487652
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4790813342650741183}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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: 21300000, guid: 5618123237d1d3f49a5a6025287065f7, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 15
+--- !u!1 &5054872679682436084
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6821695238436829111}
+ m_Layer: 5
+ m_Name: Sliding Area
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &6821695238436829111
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5054872679682436084}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 2861595589771483337}
+ m_Father: {fileID: 612935842169716872}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: -10, y: -10}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &6040598831507082404
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2393231344255378604}
+ - component: {fileID: 4371395400278151273}
+ - component: {fileID: 2649856281260980047}
+ - component: {fileID: 6964319325017651106}
+ m_Layer: 5
+ m_Name: Ripple
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2393231344255378604
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6040598831507082404}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1841548754507413200}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &4371395400278151273
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6040598831507082404}
+ m_CullTransparentMesh: 0
+--- !u!114 &2649856281260980047
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6040598831507082404}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 0
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 21300000, guid: 951352f31055aae46b6e9786313c632d, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 12
+--- !u!114 &6964319325017651106
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6040598831507082404}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_ShowMaskGraphic: 0
+--- !u!1 &6349326866027955733
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8522233572888455346}
+ - component: {fileID: 1655775987536774104}
+ - component: {fileID: 3650492158642856089}
+ m_Layer: 5
+ m_Name: Background
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8522233572888455346
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6349326866027955733}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 2230885584348632300}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: -10, y: -10}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &1655775987536774104
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6349326866027955733}
+ m_CullTransparentMesh: 0
+--- !u!114 &3650492158642856089
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6349326866027955733}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 0.09803922}
+ 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: 21300000, guid: 5618123237d1d3f49a5a6025287065f7, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 15
+--- !u!1 &6395484848576167605
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6855993087584589959}
+ - component: {fileID: 3066091430910427611}
+ - component: {fileID: 2923783150305210301}
+ - component: {fileID: 574293250142884438}
+ m_Layer: 5
+ m_Name: Ripple
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &6855993087584589959
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6395484848576167605}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 7136314229054424447}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3066091430910427611
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6395484848576167605}
+ m_CullTransparentMesh: 0
+--- !u!114 &2923783150305210301
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6395484848576167605}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 0
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 21300000, guid: 951352f31055aae46b6e9786313c632d, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 12
+--- !u!114 &574293250142884438
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6395484848576167605}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_ShowMaskGraphic: 0
+--- !u!1 &6567039954909992875
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2230885584348632300}
+ - component: {fileID: 5209801497536507958}
+ - component: {fileID: 5270351434699486664}
+ - component: {fileID: 9020326814598454113}
+ - component: {fileID: 6861991300802042721}
+ m_Layer: 5
+ m_Name: Scrollbar
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2230885584348632300
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6567039954909992875}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 8522233572888455346}
+ - {fileID: 2548444607870852420}
+ m_Father: {fileID: 1176873081988812620}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 1, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 20, y: 0}
+ m_Pivot: {x: 1, y: 0.5}
+--- !u!222 &5209801497536507958
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6567039954909992875}
+ m_CullTransparentMesh: 0
+--- !u!114 &5270351434699486664
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6567039954909992875}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 0
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 0.78431374}
+ m_HighlightedColor: {r: 1, g: 1, b: 1, a: 0.9607843}
+ m_PressedColor: {r: 1, g: 1, b: 1, a: 1}
+ m_SelectedColor: {r: 1, g: 1, b: 1, a: 0.9607843}
+ m_DisabledColor: {r: 1, g: 1, b: 1, a: 0.39215687}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Highlighted
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 4647380939240487652}
+ m_HandleRect: {fileID: 9066124595810444874}
+ m_Direction: 2
+ m_Value: 0
+ m_Size: 1
+ m_NumberOfSteps: 0
+ m_OnValueChanged:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &9020326814598454113
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6567039954909992875}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: e098a0a519700eb4094ec2c8b9d07b30, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ UIManagerAsset: {fileID: 11400000, guid: 2a619a9609984be49b53b928dd94e61b, type: 2}
+ webglMode: 0
+ background: {fileID: 3650492158642856089}
+ bar: {fileID: 4647380939240487652}
+--- !u!114 &6861991300802042721
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6567039954909992875}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d0b148fe25e99eb48b9724523833bab1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Delegates:
+ - eventID: 14
+ callback:
+ m_PersistentCalls:
+ m_Calls: []
+ - eventID: 7
+ callback:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!1 &6866810925557613553
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2861595589771483337}
+ - component: {fileID: 787139698939415621}
+ - component: {fileID: 3149929187128000327}
+ m_Layer: 5
+ m_Name: Handle
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2861595589771483337
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6866810925557613553}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 6821695238436829111}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &787139698939415621
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6866810925557613553}
+ m_CullTransparentMesh: 0
+--- !u!114 &3149929187128000327
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6866810925557613553}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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: 21300000, guid: 5618123237d1d3f49a5a6025287065f7, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 15
+--- !u!1 &6890249196013291024
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 612935842169716872}
+ - component: {fileID: 121790097987027666}
+ - component: {fileID: 7171935558317943900}
+ - component: {fileID: 5568508665376354696}
+ m_Layer: 5
+ m_Name: HorizontalScrollbar
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &612935842169716872
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6890249196013291024}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 7412864419818588360}
+ - {fileID: 6821695238436829111}
+ m_Father: {fileID: 1176873081988812620}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0.000018119812, y: 20}
+ m_Pivot: {x: 0.5, y: 0}
+--- !u!222 &121790097987027666
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6890249196013291024}
+ m_CullTransparentMesh: 0
+--- !u!114 &7171935558317943900
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6890249196013291024}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 0
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 0.78431374}
+ m_HighlightedColor: {r: 1, g: 1, b: 1, a: 0.9607843}
+ m_PressedColor: {r: 1, g: 1, b: 1, a: 1}
+ m_SelectedColor: {r: 1, g: 1, b: 1, a: 0.9607843}
+ m_DisabledColor: {r: 1, g: 1, b: 1, a: 0.39215687}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Highlighted
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 3149929187128000327}
+ m_HandleRect: {fileID: 2861595589771483337}
+ m_Direction: 0
+ m_Value: 0
+ m_Size: 1
+ m_NumberOfSteps: 0
+ m_OnValueChanged:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!114 &5568508665376354696
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6890249196013291024}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: e098a0a519700eb4094ec2c8b9d07b30, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ UIManagerAsset: {fileID: 11400000, guid: 2a619a9609984be49b53b928dd94e61b, type: 2}
+ webglMode: 0
+ background: {fileID: 1051915075008402236}
+ bar: {fileID: 3149929187128000327}
+--- !u!1 &6980893328556673925
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2871116414277210878}
+ - component: {fileID: 6173206746675119111}
+ - component: {fileID: 6424740765246731478}
+ m_Layer: 5
+ m_Name: Background
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2871116414277210878
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6980893328556673925}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 7349605152706409631}
+ m_Father: {fileID: 6690282667262079164}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &6173206746675119111
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6980893328556673925}
+ m_CullTransparentMesh: 0
+--- !u!114 &6424740765246731478
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6980893328556673925}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 0.019607844}
+ 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: 21300000, guid: 5e16c7aea118d68498053518146c9cf9, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 10
+--- !u!1 &7181825098309350580
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8455627934270354126}
+ - component: {fileID: 6605686881163477485}
+ m_Layer: 5
+ m_Name: Search
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8455627934270354126
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7181825098309350580}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 6690282667262079164}
+ - {fileID: 7136314229054424447}
+ - {fileID: 1841548754507413200}
+ - {fileID: 7739412744805412235}
+ m_Father: {fileID: 6068148083340061293}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 1}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: -10}
+ m_SizeDelta: {x: 0, y: 50}
+ m_Pivot: {x: 0.5, y: 1}
+--- !u!114 &6605686881163477485
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7181825098309350580}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 30649d3a9faa99c48a7b1166b86bf2a0, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Padding:
+ m_Left: 5
+ m_Right: 5
+ m_Top: 0
+ m_Bottom: 0
+ m_ChildAlignment: 3
+ m_Spacing: 5
+ m_ChildForceExpandWidth: 1
+ m_ChildForceExpandHeight: 1
+ m_ChildControlWidth: 1
+ m_ChildControlHeight: 0
+ m_ChildScaleWidth: 0
+ m_ChildScaleHeight: 0
+ m_ReverseArrangement: 0
+--- !u!1 &7475176147733851298
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6690282667262079164}
+ - component: {fileID: 3161791815447058949}
+ - component: {fileID: 7237769179235166874}
+ - component: {fileID: 3629503722644211611}
+ - component: {fileID: 790196186597391616}
+ - component: {fileID: 8874951550066978760}
+ - component: {fileID: 9004278678232372259}
+ m_Layer: 5
+ m_Name: SearchField
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &6690282667262079164
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7475176147733851298}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 2871116414277210878}
+ - {fileID: 4599480176814839446}
+ - {fileID: 8793840307802463971}
+ m_Father: {fileID: 8455627934270354126}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 55}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3161791815447058949
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7475176147733851298}
+ m_CullTransparentMesh: 0
+--- !u!114 &7237769179235166874
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7475176147733851298}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 0
+ m_WrapAround: 0
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 0}
+ m_HighlightedColor: {r: 1, g: 1, b: 1, a: 0.039215688}
+ m_PressedColor: {r: 1, g: 1, b: 1, a: 0.05882353}
+ m_SelectedColor: {r: 1, g: 1, b: 1, a: 0.039215688}
+ m_DisabledColor: {r: 1, g: 1, b: 1, a: 0}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_SelectedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_SelectedTrigger: Highlighted
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 7726245115218372043}
+ m_TextViewport: {fileID: 8793840307802463971}
+ m_TextComponent: {fileID: 6807727362598245007}
+ m_Placeholder: {fileID: 0}
+ m_VerticalScrollbar: {fileID: 0}
+ m_VerticalScrollbarEventHandler: {fileID: 0}
+ m_LayoutGroup: {fileID: 0}
+ m_ScrollSensitivity: 1
+ m_ContentType: 0
+ m_InputType: 0
+ m_AsteriskChar: 42
+ m_KeyboardType: 0
+ m_LineType: 0
+ m_HideMobileInput: 0
+ m_HideSoftKeyboard: 0
+ m_CharacterValidation: 0
+ m_RegexValue:
+ m_GlobalPointSize: 14
+ m_CharacterLimit: 0
+ m_OnEndEdit:
+ m_PersistentCalls:
+ m_Calls: []
+ m_OnSubmit:
+ m_PersistentCalls:
+ m_Calls: []
+ m_OnSelect:
+ m_PersistentCalls:
+ m_Calls: []
+ m_OnDeselect:
+ m_PersistentCalls:
+ m_Calls: []
+ m_OnTextSelection:
+ m_PersistentCalls:
+ m_Calls: []
+ m_OnEndTextSelection:
+ m_PersistentCalls:
+ m_Calls: []
+ m_OnValueChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_OnTouchScreenKeyboardStatusChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_CaretColor: {r: 1, g: 1, b: 1, a: 1}
+ m_CustomCaretColor: 1
+ m_SelectionColor: {r: 1, g: 1, b: 1, a: 0.09803922}
+ m_Text:
+ m_CaretBlinkRate: 0.85
+ m_CaretWidth: 2
+ m_ReadOnly: 0
+ m_RichText: 1
+ m_GlobalFontAsset: {fileID: 11400000, guid: 4bd810f1cbcb0f446a8f5a31453e243f, type: 2}
+ m_OnFocusSelectAll: 1
+ m_ResetOnDeActivation: 1
+ m_RestoreOriginalTextOnEscape: 1
+ m_isRichTextEditingAllowed: 1
+ m_LineLimit: 0
+ m_InputValidator: {fileID: 0}
+--- !u!95 &3629503722644211611
+Animator:
+ serializedVersion: 5
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7475176147733851298}
+ m_Enabled: 1
+ m_Avatar: {fileID: 0}
+ m_Controller: {fileID: 9100000, guid: f02ff10900044744b851159f375542e2, type: 2}
+ m_CullingMode: 0
+ m_UpdateMode: 2
+ m_ApplyRootMotion: 0
+ m_LinearVelocityBlending: 0
+ m_StabilizeFeet: 0
+ m_WarningMessage:
+ m_HasTransformHierarchy: 1
+ m_AllowConstantClipSamplingOptimization: 1
+ m_KeepAnimatorStateOnDisable: 0
+ m_WriteDefaultValuesOnDisable: 0
+--- !u!114 &790196186597391616
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7475176147733851298}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: c65c7917835d8a04b94c8b906234b09e, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ inputText: {fileID: 0}
+ inputFieldAnimator: {fileID: 0}
+--- !u!114 &8874951550066978760
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7475176147733851298}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d170bc6b162fcce46a456b2011fd50b4, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ UIManagerAsset: {fileID: 11400000, guid: 2a619a9609984be49b53b928dd94e61b, type: 2}
+ webglMode: 0
+ images:
+ - {fileID: 6980893328556673925}
+ - {fileID: 2789169427069279165}
+ texts:
+ - {fileID: 842318586328925508}
+ - {fileID: 4624945097066885423}
+--- !u!114 &9004278678232372259
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7475176147733851298}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreLayout: 0
+ m_MinWidth: 100
+ m_MinHeight: -1
+ m_PreferredWidth: -1
+ m_PreferredHeight: -1
+ m_FlexibleWidth: 100
+ m_FlexibleHeight: -1
+ m_LayoutPriority: 3
+--- !u!1 &8709522067248902145
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7073849410189226427}
+ - component: {fileID: 4880555759130807316}
+ - component: {fileID: 8146306459036859299}
+ m_Layer: 5
+ m_Name: Icon
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7073849410189226427
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8709522067248902145}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 7136314229054424447}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: -25, y: -25}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &4880555759130807316
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8709522067248902145}
+ m_CullTransparentMesh: 0
+--- !u!114 &8146306459036859299
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8709522067248902145}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, 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_text: "\uF5FD"
+ m_isRightToLeft: 0
+ m_fontAsset: {fileID: 11400000, guid: 4ebb98a3c87fa521a888029274c92b79, type: 2}
+ m_sharedMaterial: {fileID: -8620075009897487826, guid: 4ebb98a3c87fa521a888029274c92b79,
+ type: 2}
+ m_fontSharedMaterials: []
+ m_fontMaterial: {fileID: 0}
+ m_fontMaterials: []
+ m_fontColor32:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontColor: {r: 1, g: 1, b: 1, a: 1}
+ m_enableVertexGradient: 0
+ m_colorMode: 3
+ m_fontColorGradient:
+ topLeft: {r: 1, g: 1, b: 1, a: 1}
+ topRight: {r: 1, g: 1, b: 1, a: 1}
+ bottomLeft: {r: 1, g: 1, b: 1, a: 1}
+ bottomRight: {r: 1, g: 1, b: 1, a: 1}
+ m_fontColorGradientPreset: {fileID: 0}
+ m_spriteAsset: {fileID: 0}
+ m_tintAllSprites: 0
+ m_StyleSheet: {fileID: 0}
+ m_TextStyleHashCode: -1183493901
+ m_overrideHtmlColors: 0
+ m_faceColor:
+ serializedVersion: 2
+ rgba: 4294967295
+ m_fontSize: 36
+ m_fontSizeBase: 36
+ m_fontWeight: 400
+ m_enableAutoSizing: 0
+ m_fontSizeMin: 18
+ m_fontSizeMax: 72
+ m_fontStyle: 0
+ m_HorizontalAlignment: 2
+ m_VerticalAlignment: 512
+ m_textAlignment: 65535
+ m_characterSpacing: 0
+ m_wordSpacing: 0
+ m_lineSpacing: 0
+ m_lineSpacingMax: 0
+ m_paragraphSpacing: 0
+ m_charWidthMaxAdj: 0
+ m_enableWordWrapping: 1
+ m_wordWrappingRatios: 0.4
+ m_overflowMode: 0
+ m_linkedTextComponent: {fileID: 0}
+ parentLinkedComponent: {fileID: 0}
+ m_enableKerning: 1
+ m_enableExtraPadding: 0
+ checkPaddingRequired: 0
+ m_isRichText: 1
+ m_parseCtrlCharacters: 1
+ m_isOrthographic: 1
+ m_isCullingEnabled: 0
+ m_horizontalMapping: 0
+ m_verticalMapping: 0
+ m_uvLineOffset: 0
+ m_geometrySortingOrder: 0
+ m_IsTextObjectScaleStatic: 0
+ m_VertexBufferAutoSizeReduction: 0
+ m_useMaxVisibleDescender: 1
+ m_pageToDisplay: 1
+ m_margin: {x: 0, y: 0, z: 0, w: 0}
+ m_isUsingLegacyAnimationComponent: 0
+ m_isVolumetricText: 0
+ m_hasFontAssetChanged: 0
+ m_baseMaterial: {fileID: 0}
+ m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
+--- !u!1 &9041207098413024880
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3595538001728667684}
+ - component: {fileID: 195908297805271598}
+ - component: {fileID: 977652172377916750}
+ - component: {fileID: 5913044580492746682}
+ m_Layer: 5
+ m_Name: Ripple
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &3595538001728667684
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 9041207098413024880}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 7739412744805412235}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &195908297805271598
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 9041207098413024880}
+ m_CullTransparentMesh: 0
+--- !u!114 &977652172377916750
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 9041207098413024880}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 0
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 21300000, guid: 951352f31055aae46b6e9786313c632d, type: 3}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 12
+--- !u!114 &5913044580492746682
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 9041207098413024880}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_ShowMaskGraphic: 0
diff --git a/Assets/Resources/Prefabs/UI/PropertyWindow.prefab.meta b/Assets/Resources/Prefabs/UI/PropertyWindow.prefab.meta
new file mode 100644
index 0000000000..a0060c198a
--- /dev/null
+++ b/Assets/Resources/Prefabs/UI/PropertyWindow.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 2a322c90736c7d54d8462ef91bbc1c48
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SEE/Controls/Actions/AbstractPlayerAction.cs b/Assets/SEE/Controls/Actions/AbstractPlayerAction.cs
index 8ed5385ab4..6c300346f0 100644
--- a/Assets/SEE/Controls/Actions/AbstractPlayerAction.cs
+++ b/Assets/SEE/Controls/Actions/AbstractPlayerAction.cs
@@ -27,6 +27,11 @@ public abstract class AbstractPlayerAction : IReversibleAction
///
protected IReversibleAction.Progress CurrentState = IReversibleAction.Progress.NoEffect;
+ ///
+ /// Whether the action is executed through the context menu.
+ ///
+ public bool ExecuteViaContextMenu { get; set; } = false;
+
///
/// The undo operation which has to be implemented specifically by subclasses
/// to revert the effect of an executed action.
diff --git a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs
index c354e4960f..55b1ca98d0 100644
--- a/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs
+++ b/Assets/SEE/Controls/Actions/AcceptDivergenceAction.cs
@@ -1,16 +1,18 @@
-using System.Collections.Generic;
+using SEE.Audio;
using SEE.DataModel.DG;
-using SEE.Tools.ReflexionAnalysis;
-using SEE.GO;
-using SEE.Utils.History;
-using UnityEngine;
-using System;
+using SEE.Game;
using SEE.Game.SceneManipulation;
+using SEE.GO;
using SEE.Net.Actions;
-using SEE.Audio;
-using SEE.Game;
+using SEE.Tools.ReflexionAnalysis;
+using SEE.UI.DebugAdapterProtocol.DebugAdapter;
using SEE.UI.Notification;
using SEE.Utils;
+using SEE.Utils.History;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
namespace SEE.Controls.Actions
{
@@ -76,11 +78,16 @@ public Memento(Node source, Node target, string type)
private Memento memento;
///
- /// The edge created by this action. Can be null if no edge has been created yet or whether
- /// an Undo was called. The created edge is stored only to delete it again if Undo is
- /// called. All information to create the edge is kept in .
+ /// The information required to (re-)create the edges that solve the divergence
+ /// via the multi-selection context menu.
///
- private Edge createdEdge;
+ private readonly List mementoList = new();
+
+ ///
+ /// The edges created by this action .
+ /// The list is needed for the multi-selection via context menu.
+ ///
+ private readonly List createdEdgeList = new();
///
/// Registers itself at to listen for hovering events.
@@ -135,12 +142,12 @@ public override bool Update()
// we have both source and target of the edge and use a memento struct
// to remember which edge we have added
memento = new Memento(source, target, Edge.SourceDependency);
-
+ mementoList.Add(memento);
// create the edge
- createdEdge = CreateConvergentEdge(memento);
+ createdEdgeList.Add(CreateConvergentEdge(memento));
// check whether edge creation was successful
- bool divergenceSolved = createdEdge != null;
+ bool divergenceSolved = createdEdgeList[0] != null;
// add audio cue to the appearance of the new architecture edge
AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.NewEdgeSound);
@@ -158,6 +165,12 @@ public override bool Update()
ShowNotification.Warn("Not an edge", $"Selected Element {divergentEdge.name} is not an edge.\n");
}
}
+ if (ExecuteViaContextMenu)
+ {
+ bool divergenceSolved = createdEdgeList.All(e => e != null);
+ CurrentState = divergenceSolved ? IReversibleAction.Progress.Completed : IReversibleAction.Progress.NoEffect;
+ return true;
+ }
return false;
}
@@ -167,30 +180,41 @@ public override bool Update()
public override void Undo()
{
base.Undo();
+ foreach (Edge edge in createdEdgeList)
+ {
+ RemoveDivergence(edge);
+ }
+ createdEdgeList.Clear();
+ }
+ ///
+ /// Removes the divergence (undo).
+ ///
+ /// The edge divergence to remove.
+ /// If the edge not contained in a graph.
+ private void RemoveDivergence(Edge edge)
+ {
// remove the synced edge (its info is saved in memento)
- ReflexionGraph graph = (ReflexionGraph)createdEdge.ItsGraph;
+ ReflexionGraph graph = (ReflexionGraph)edge.ItsGraph;
if (graph != null)
{
// find the corresponding GameObject
- GameObject createdEdgeGO = GraphElementIDMap.Find(createdEdge.ID);
+ GameObject createdEdgeGO = GraphElementIDMap.Find(edge.ID);
// remove the edge's GameObject and graph representation locally and on the network
GameEdgeAdder.Remove(createdEdgeGO);
// propagate the new edge via network
- new DeleteNetAction(createdEdge.ID).Execute();
+ new DeleteNetAction(edge.ID).Execute();
// ensure the edge's GameObject gets destroyed properly
Destroyer.Destroy(createdEdgeGO);
}
else
{
- throw new Exception($"Edge {createdEdge.ID} to be removed is not contained in a graph.");
+ throw new Exception($"Edge {edge.ID} to be removed is not contained in a graph.");
}
- // set any edge references back to null
- createdEdge = null;
}
///
@@ -199,8 +223,10 @@ public override void Undo()
public override void Redo()
{
base.Redo();
- // recreate the edge
- createdEdge = CreateConvergentEdge(memento);
+ foreach (Memento mem in mementoList)
+ {
+ createdEdgeList.Add(CreateConvergentEdge(mem));
+ }
}
///
@@ -219,17 +245,46 @@ private static Edge CreateConvergentEdge(Memento memento)
}
///
- /// Creates a new edge in the architecture to allow the given .
+ /// Used to execute the from the context menu.
+ /// Creates a new edge in the architecture to allow the given
+ /// and ensures that the method performs the execution via context menu.
///
/// the edge representing the divergence
/// the new edge
- public static Edge CreateConvergentEdge(Edge divergence)
+ public void ContextMenuExecution(Edge divergence)
+ {
+ ExecuteViaContextMenu = true;
+ mementoList.Add(CreateMementoAndConvergentEdge(divergence));
+ }
+
+ ///
+ /// Used to execute the from the context menu in multiselection mode.
+ /// Creates new edges in the architecture to allow the given and ensures
+ /// that the method perfoms the execution via context menu.
+ ///
+ /// The edges representing the divergences.
+ public void ContextMenuExecution(IList divergences)
+ {
+ ExecuteViaContextMenu = true;
+ foreach (Edge divergence in divergences)
+ {
+ mementoList.Add(CreateMementoAndConvergentEdge(divergence));
+ }
+ }
+
+ ///
+ /// Creates the memento for restoring the edge and creates the edge.
+ ///
+ /// the edge representing the divergence.
+ /// The created memento.
+ private Memento CreateMementoAndConvergentEdge(Edge divergence)
{
ReflexionGraph graph = (ReflexionGraph)divergence.ItsGraph;
Node source = graph.MapsTo(divergence.Source);
Node target = graph.MapsTo(divergence.Target);
- Memento memento = new(source, target, Edge.SourceDependency);
- return CreateConvergentEdge(memento);
+ memento = new(source, target, Edge.SourceDependency);
+ createdEdgeList.Add(CreateConvergentEdge(memento));
+ return memento;
}
///
@@ -247,18 +302,13 @@ public override ActionStateType GetActionStateType()
/// all IDs of GameObjects manipulated by this action
public override HashSet GetChangedObjects()
{
- if (createdEdge == null)
+ if (createdEdgeList.Count == 0)
{
return new HashSet();
}
else
{
- return new HashSet
- {
- memento.From.ID,
- memento.To.ID,
- createdEdge.ID
- };
+ return mementoList.Zip(createdEdgeList, (m, e) => new[] { m.From.ID, m.To.ID, e.ID }).SelectMany(x => x).ToHashSet();
}
}
}
diff --git a/Assets/SEE/Controls/Actions/ActionStateTypes.cs b/Assets/SEE/Controls/Actions/ActionStateTypes.cs
index 1ec83d88ac..b753e1ee54 100644
--- a/Assets/SEE/Controls/Actions/ActionStateTypes.cs
+++ b/Assets/SEE/Controls/Actions/ActionStateTypes.cs
@@ -1,5 +1,5 @@
-using SEE.Utils;
-using UnityEngine;
+using UnityEngine;
+using SEE.Utils;
using SEE.Controls.Actions.HolisticMetrics;
using SEE.Controls.Actions.Drawable;
@@ -95,6 +95,11 @@ static ActionStateTypes()
Color.green.Darker(), Icons.PenToSquare,
EditNodeAction.CreateReversibleAction);
+ ResizeNode =
+ new("Resize Node", "Change the size of a node",
+ Color.green.Darker(), Icons.Resize,
+ ResizeNodeAction.CreateReversibleAction);
+
ScaleNode =
new("Scale Node", "Scale a node",
Color.green.Darker(), Icons.Scale,
@@ -110,11 +115,6 @@ static ActionStateTypes()
Color.black, Icons.Code,
ShowCodeAction.CreateReversibleAction);
- Draw =
- new("Draw", "Draw freely in world space",
- Color.magenta.Darker(), Icons.Pencil,
- DrawAction.CreateReversibleAction);
-
AcceptDivergence =
new("Accept Divergence", "Accept a diverging edge into the architecture",
Color.grey.Darker(), Icons.CheckedCheckbox,
@@ -308,6 +308,7 @@ static ActionStateTypes()
public static readonly ActionStateType NewEdge;
public static readonly ActionStateType NewNode;
public static readonly ActionStateType EditNode;
+ public static readonly ActionStateType ResizeNode;
public static readonly ActionStateType ScaleNode;
public static readonly ActionStateType Delete;
public static readonly ActionStateType ShowCode;
diff --git a/Assets/SEE/Controls/Actions/AddEdgeAction.cs b/Assets/SEE/Controls/Actions/AddEdgeAction.cs
index a2abf39565..6d40aed0cb 100644
--- a/Assets/SEE/Controls/Actions/AddEdgeAction.cs
+++ b/Assets/SEE/Controls/Actions/AddEdgeAction.cs
@@ -175,6 +175,17 @@ public override bool Update()
return result;
}
+ ///
+ /// Used to execute the from the context menu.
+ /// It ensures that the method performs the execution via context menu.
+ ///
+ /// Is the source node of the edge.
+ public void ContextMenuExecution(GameObject source)
+ {
+ from = source;
+ ShowNotification.Info("Select target", "Next, select a target node for the line.");
+ }
+
///
/// Undoes this AddEdgeAction
///
diff --git a/Assets/SEE/Controls/Actions/AddNodeAction.cs b/Assets/SEE/Controls/Actions/AddNodeAction.cs
index 54b0eb62c7..686696132f 100644
--- a/Assets/SEE/Controls/Actions/AddNodeAction.cs
+++ b/Assets/SEE/Controls/Actions/AddNodeAction.cs
@@ -6,6 +6,9 @@
using SEE.Audio;
using SEE.Game.SceneManipulation;
using SEE.Utils;
+using System;
+using SEE.UI.PropertyDialog;
+using SEE.DataModel.DG;
namespace SEE.Controls.Actions
{
@@ -14,6 +17,43 @@ namespace SEE.Controls.Actions
///
internal class AddNodeAction : AbstractPlayerAction
{
+ ///
+ /// The life cycle of this add node action.
+ ///
+ private enum ProgressState
+ {
+ ///
+ /// Initial state when no parent node is selected.
+ ///
+ NoNodeSelected,
+ ///
+ /// A new node is created and selected, the dialog is opened,
+ /// and we wait for input.
+ ///
+ WaitingForInput,
+ ///
+ /// When the action is finished.
+ ///
+ Finish
+ }
+
+ ///
+ /// The current state of the add node process.
+ ///
+ private ProgressState progress = ProgressState.NoNodeSelected;
+
+ ///
+ /// The chosen parent for the new node.
+ /// Will be used for context menu execution.
+ ///
+ private GameObject parent;
+
+ ///
+ /// The chosen position for the new node.
+ /// Will be used for context menu execution.
+ ///
+ private Vector3 position;
+
///
/// If the user clicks with the mouse hitting a game object representing a graph node,
/// this graph node is a parent to which a new node is created and added as a child.
@@ -24,31 +64,120 @@ public override bool Update()
{
bool result = false;
- // FIXME: Needs adaptation for VR where no mouse is available.
- if (Input.GetMouseButtonDown(0)
- && Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) == HitGraphElement.Node)
+ switch (progress)
{
- // the hit object is the parent in which to create the new node
- GameObject parent = raycastHit.collider.gameObject;
- addedGameNode = GameNodeAdder.AddChild(parent);
- // addedGameNode has the scale and position of parent.
- // The position at which the parent was hit will be the center point of the addedGameNode.
- addedGameNode.transform.position = raycastHit.point;
- // PutOn makes sure addedGameNode fits into parent.
- GameNodeMover.PutOn(child: addedGameNode.transform, parent: parent, true);
- memento = new Memento(child: addedGameNode, parent: parent);
- memento.NodeID = addedGameNode.name;
- new AddNodeNetAction(parentID: memento.Parent.name, newNodeID: memento.NodeID, memento.Position, memento.Scale).Execute();
- result = true;
- CurrentState = IReversibleAction.Progress.Completed;
- AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.NewNodeSound, parent);
+ case ProgressState.NoNodeSelected:
+ if (Input.GetMouseButtonDown(0)
+ && Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) == HitGraphElement.Node)
+ {
+ // the hit object is the parent in which to create the new node
+ GameObject parent = raycastHit.collider.gameObject;
+ AddNode(raycastHit.collider.gameObject, raycastHit.point);
+ }
+ else if (ExecuteViaContextMenu)
+ {
+ AddNode(parent, position);
+ }
+ break;
+ case ProgressState.WaitingForInput:
+ // Waiting until the dialog is closed and all input is present.
+ break;
+ case ProgressState.Finish:
+ result = true;
+ CurrentState = IReversibleAction.Progress.Completed;
+ AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.NewNodeSound, parent);
+ break;
+ default:
+ throw new NotImplementedException($"Unhandled case {nameof(progress)}.");
}
return result;
}
+ ///
+ /// Adds a node on the chosen at the
+ /// chosen .
+ ///
+ /// The parent on which to place the node.
+ /// The position where the node should be placed.
+ private void AddNode(GameObject parent, Vector3 position)
+ {
+ addedGameNode = GameNodeAdder.AddChild(parent);
+ // addedGameNode has the scale and position of parent.
+ // The position at which the parent was hit will be the center point of the addedGameNode.
+ // The node is scaled down and placed on top of its parent.
+ addedGameNode.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
+ addedGameNode.transform.position = GameNodeMover.GetCoordinatesOn(addedGameNode.transform.lossyScale, position, parent);
+ // TODO(#786) The new node is scaled down arbitrarily and might overlap with its siblings.
+ memento = new(child: addedGameNode, parent: parent)
+ {
+ NodeID = addedGameNode.name
+ };
+ new AddNodeNetAction(parentID: memento.Parent.name, newNodeID: memento.NodeID, memento.Position, memento.Scale).Execute();
+ progress = ProgressState.WaitingForInput;
+ OpenDialog(addedGameNode.GetNode());
+ }
+
+ ///
+ /// Opens a dialog where the user can enter the node name and type.
+ /// If the user presses the OK button, the SourceName and Type of
+ /// will have the new values entered
+ /// and and
+ /// will be set to memorize these and is
+ /// moved forward to .
+ /// If the user presses the Cancel button, the node will be created as
+ /// an unnamed node with the unkown type.
+ ///
+ private void OpenDialog(Node node)
+ {
+ NodePropertyDialog dialog = new(node);
+ dialog.OnConfirm.AddListener(OKButtonPressed);
+ dialog.OnCancel.AddListener(CancelButtonPressed);
+ dialog.Open(true);
+ SEEInput.KeyboardShortcutsEnabled = false;
+
+ return;
+
+ void OKButtonPressed()
+ {
+ memento.Name = node.SourceName;
+ memento.Type = node.Type;
+ new EditNodeNetAction(node.ID, node.SourceName, node.Type).Execute();
+ InteractableObject.UnselectAll(true);
+ progress = ProgressState.Finish;
+ SEEInput.KeyboardShortcutsEnabled = true;
+ }
+
+ void CancelButtonPressed()
+ {
+ // Case when last used is used and it has a value other
+ // then 'UNKOWNTYPE', use it.
+ if (node.Type != Graph.UnknownType)
+ {
+ memento.Name = node.SourceName;
+ memento.Type = node.Type;
+ }
+ progress = ProgressState.Finish;
+ SEEInput.KeyboardShortcutsEnabled = true;
+ }
+ }
+
+ ///
+ /// Used to execute the from the context menu.
+ /// Calls and ensures that the method
+ /// performs the execution via context menu.
+ ///
+ /// The parent node.
+ /// The position where the node should be placed.
+ public void ContextMenuExecution(GameObject parent, Vector3 position)
+ {
+ this.parent = parent;
+ this.position = position;
+ ExecuteViaContextMenu = true;
+ }
+
///
/// The node that was added when this action was executed. It is saved so
- /// that it can be removed on Undo().
+ /// that it can be removed on .
///
private GameObject addedGameNode;
@@ -79,6 +208,14 @@ private struct Memento
/// original name of the node in Redo().
///
public string NodeID;
+ ///
+ /// The chosen name for the added node.
+ ///
+ public string Name;
+ ///
+ /// The chosen type for the added node.
+ ///
+ public string Type;
///
/// Constructor setting the information necessary to re-do this action.
@@ -91,11 +228,13 @@ public Memento(GameObject child, GameObject parent)
Position = child.transform.position;
Scale = child.transform.lossyScale;
NodeID = null;
+ Name = string.Empty;
+ Type = string.Empty;
}
}
///
- /// Undoes this AddNodeAction.
+ /// Undoes this action.
///
public override void Undo()
{
@@ -110,15 +249,25 @@ public override void Undo()
}
///
- /// Redoes this AddNodeAction.
+ /// Redoes this action.
///
public override void Redo()
{
base.Redo();
- addedGameNode = GameNodeAdder.AddChild(memento.Parent, worldSpacePosition: memento.Position, worldSpaceScale: memento.Scale, nodeID: memento.NodeID);
+ addedGameNode = GameNodeAdder.AddChild(memento.Parent, worldSpacePosition: memento.Position,
+ worldSpaceScale: memento.Scale, nodeID: memento.NodeID);
if (addedGameNode != null)
{
- new AddNodeNetAction(parentID: memento.Parent.name, newNodeID: memento.NodeID, memento.Position, memento.Scale).Execute();
+ new AddNodeNetAction(parentID: memento.Parent.name,
+ newNodeID: memento.NodeID, memento.Position, memento.Scale).Execute();
+
+ if (!string.IsNullOrEmpty(memento.Type))
+ {
+ Node node = addedGameNode.GetNode();
+ GameNodeEditor.ChangeName(node, memento.Name);
+ GameNodeEditor.ChangeType(node, memento.Type);
+ new EditNodeNetAction(node.ID, node.SourceName, node.Type).Execute();
+ }
}
}
diff --git a/Assets/SEE/Controls/Actions/ContextMenuAction.cs b/Assets/SEE/Controls/Actions/ContextMenuAction.cs
index d223fda756..79f67ce4fa 100644
--- a/Assets/SEE/Controls/Actions/ContextMenuAction.cs
+++ b/Assets/SEE/Controls/Actions/ContextMenuAction.cs
@@ -4,7 +4,6 @@
using Cysharp.Threading.Tasks;
using SEE.DataModel.DG;
using SEE.Game;
-using SEE.Game.SceneManipulation;
using SEE.GO;
using SEE.Tools.ReflexionAnalysis;
using SEE.UI.Notification;
@@ -14,6 +13,10 @@
using SEE.Utils;
using UnityEngine;
using SEE.Game.City;
+using SEE.Utils.History;
+using SEE.GO.Menu;
+using SEE.UI.Menu.Drawable;
+using SEE.UI.Window.PropertyWindow;
namespace SEE.Controls.Actions
{
@@ -28,6 +31,21 @@ public class ContextMenuAction : MonoBehaviour
private PopupMenu popupMenu;
///
+ /// The position where the menu should be opened.
+ ///
+ private Vector3 position;
+
+ ///
+ /// The interactable object during the start must be the same as when
+ /// the right mouse button is released in order for the context menu to open.
+ ///
+ private InteractableObject startObject;
+
+ ///
+ /// Tries to open the context menu with multiselection.
+ ///
+ private bool multiselection = false;
+
/// The position of the mouse when the user started opening the context menu.
///
private Vector3 startMousePosition = Vector3.zero;
@@ -41,36 +59,231 @@ private void Update()
{
if (SEEInput.OpenContextMenuStart())
{
- startMousePosition = Input.mousePosition;
+ if (InteractableObject.SelectedObjects.Count <= 1)
+ {
+ Raycasting.RaycastInteractableObject(out _, out InteractableObject o);
+ startObject = o;
+ startMousePosition = Input.mousePosition;
+ multiselection = false;
+ }
+ else
+ {
+ startObject = null;
+ multiselection = true;
+ }
}
- else if (SEEInput.OpenContextMenuEnd() && (Input.mousePosition - startMousePosition).magnitude < 1)
+ if (SEEInput.OpenContextMenuEnd())
{
- // TODO (#664): Detect if multiple elements are selected and adjust options accordingly.
- HitGraphElement hit = Raycasting.RaycastInteractableObject(out _, out InteractableObject o);
- if (hit == HitGraphElement.None)
+ if (!multiselection)
{
- return;
+ HitGraphElement hit = Raycasting.RaycastInteractableObject(out RaycastHit raycastHit, out InteractableObject o);
+ if (hit == HitGraphElement.None)
+ {
+ return;
+ }
+ if (o == startObject && (Input.mousePosition - startMousePosition).magnitude < 1)
+ {
+ position = Input.mousePosition;
+ IEnumerable entries = GetApplicableOptions(popupMenu, position, raycastHit.point, o.GraphElemRef.Elem, o.gameObject);
+ popupMenu.ShowWith(entries, position);
+ }
+ }
+ else
+ {
+ HitGraphElement hit = Raycasting.RaycastInteractableObject(out RaycastHit raycastHit, out InteractableObject o);
+ if (hit == HitGraphElement.None)
+ {
+ return;
+ }
+ if (InteractableObject.SelectedObjects.Contains(o))
+ {
+ position = Input.mousePosition;
+ IEnumerable entries = GetApplicableOptionsForMultiselection(popupMenu, InteractableObject.SelectedObjects);
+ popupMenu.ShowWith(entries, position);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Updates the context menu.
+ ///
+ /// The popup menu in which the options should be displayed.
+ /// The position to be displayed the popup menu.
+ /// The new entries for the context menu.
+ private static void UpdateEntries(PopupMenu popupMenu, Vector3 position, IEnumerable entries)
+ {
+ popupMenu.ShowWith(entries, position);
+ }
+
+ #region Multiple-Selection
+ ///
+ /// Returns the options available for multiple selection.
+ ///
+ /// The popup menu in which the options should be displayed.
+ /// The selected objects.
+ /// Options available for the selected objects.
+ private IEnumerable GetApplicableOptionsForMultiselection(PopupMenu popupMenu, HashSet selectedObjects)
+ {
+ List entries = new()
+ {
+ new PopupMenuHeading($"{selectedObjects.Count} elements selected!", int.MaxValue),
+
+ new PopupMenuActionDoubleIcon("Inspect", () =>
+ {
+ List submenuEntries = new()
+ {
+ new PopupMenuAction("Inspect", () =>
+ {
+ UpdateEntries(popupMenu, position, GetApplicableOptionsForMultiselection(popupMenu, selectedObjects));
+ }, Icons.ArrowLeft, CloseAfterClick: false),
+ new PopupMenuAction("Properties", ShowProperties, Icons.Info),
+ new PopupMenuAction("Show Metrics", ShowMetrics, Icons.Info),
+ new PopupMenuAction("Show in City", Highlight, Icons.LightBulb)
+ };
+
+ if (selectedObjects.Any(o => o.GraphElemRef.Elem.Filename != null))
+ {
+ submenuEntries.Add(new PopupMenuAction("Show Code", ShowCode, Icons.Code));
+ if (selectedObjects.Any(o => o.gameObject.ContainingCity() != null))
+ {
+ submenuEntries.Add(new PopupMenuAction("Show Code Diff", ShowDiffCode, Icons.Code));
+ }
+ }
+ UpdateEntries(popupMenu, position, submenuEntries);
+ },
+ Icons.Info, Icons.ArrowRight, CloseAfterClick: false, Priority: 5),
+ new PopupMenuAction("Delete", Delete, Icons.Trash)
+ };
+
+ if (selectedObjects.Any(iO => iO.GraphElemRef.Elem is Edge edge && edge.IsInImplementation() && ReflexionGraph.IsDivergent(edge)))
+ {
+ entries.Add(new PopupMenuAction("Accept Divergence", AcceptDivergence, Icons.Checkmark, Priority: 1));
+ }
+ return entries;
+
+ void Delete()
+ {
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.Delete);
+ DeleteAction action = (DeleteAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(selectedObjects.Select(iO => iO.gameObject));
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
+ }
+
+ void AcceptDivergence()
+ {
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.AcceptDivergence);
+ AcceptDivergenceAction action = (AcceptDivergenceAction)GlobalActionHistory.CurrentAction();
+ List divergences = selectedObjects
+ .Select(x => x.GraphElemRef.Elem)
+ .OfType()
+ .Where(e => e.IsInImplementation() && ReflexionGraph.IsDivergent(e))
+ .ToList();
+ action.ContextMenuExecution(divergences);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
+ }
+
+ void ShowProperties()
+ {
+ foreach (InteractableObject iO in selectedObjects)
+ {
+ if (iO.gameObject != null)
+ {
+ ActivateWindow(CreatePropertyWindow(iO.gameObject.MustGetComponent()));
+ }
+ }
+ }
+
+ void ShowMetrics()
+ {
+ foreach (InteractableObject iO in selectedObjects)
+ {
+ if (iO.gameObject != null)
+ {
+ ActivateWindow(CreateMetricWindow(iO.gameObject.MustGetComponent()));
+ }
}
+ }
- IEnumerable actions = GetApplicableOptions(o.GraphElemRef.Elem, o.gameObject);
- popupMenu.ShowWith(actions, Input.mousePosition);
+ void ShowCode()
+ {
+ foreach (InteractableObject iO in selectedObjects)
+ {
+ if (iO.gameObject != null)
+ {
+ ActivateWindow(ShowCodeAction.ShowCode(iO.gameObject.MustGetComponent()));
+ }
+ }
+ }
+
+ void ShowDiffCode()
+ {
+ foreach (InteractableObject iO in selectedObjects)
+ {
+ if (iO.gameObject != null && iO.gameObject.ContainingCity())
+ {
+ ActivateWindow(ShowCodeAction.ShowVCSDiff(iO.gameObject.MustGetComponent(),
+ iO.gameObject.ContainingCity()));
+ }
+ }
+ }
+
+ void Highlight()
+ {
+ foreach (InteractableObject iO in selectedObjects)
+ {
+ if (iO.gameObject != null)
+ {
+ iO.gameObject.Operator().Highlight(duration: 10);
+ }
+ }
}
}
+
+ #endregion
+
+ #region Single-Selection
///
/// Returns the options available for the given graph element.
///
+ /// The popup menu in which the options should be displayed.
+ /// The context menu position.
/// The graph element to get the options for
/// The game object that the graph element is attached to
+ /// Actions to be append at the end of the entries.
/// Options available for the given graph element
/// Thrown if the graph element is neither a node nor an edge
- public static IEnumerable GetApplicableOptions(GraphElement graphElement, GameObject gameObject = null)
+ public static IEnumerable GetOptionsForTreeView(PopupMenu popupMenu, Vector3 position,
+ GraphElement graphElement, GameObject gameObject = null, IEnumerable appendActions = null)
{
- IEnumerable options = GetCommonOptions(graphElement, gameObject);
+ return GetApplicableOptions(popupMenu, position, position, graphElement, gameObject, appendActions)
+ .OfType();
+ }
+
+ ///
+ /// Returns the options available for the given graph element.
+ ///
+ /// The popup menu in which the options should be displayed.
+ /// The context menu position.
+ /// The position of the raycast hit.
+ /// The graph element to get the options for
+ /// The game object that the graph element is attached to
+ /// Actions to be append at the end of the entries.
+ /// Options available for the given graph element
+ /// Thrown if the graph element is neither a node nor an edge
+ private static IEnumerable GetApplicableOptions(PopupMenu popupMenu, Vector3 position,
+ Vector3 raycastHitPosition, GraphElement graphElement, GameObject gameObject = null,
+ IEnumerable appendActions = null)
+ {
+ IEnumerable options
+ = GetCommonOptions(popupMenu, position, raycastHitPosition, graphElement, gameObject, appendActions);
return options.Concat(graphElement switch
{
- Node node => GetNodeOptions(node, gameObject),
- Edge edge => GetEdgeOptions(edge, gameObject),
+ Node node => GetNodeOptions(popupMenu, position, raycastHitPosition, node, gameObject, appendActions),
+ Edge edge => GetEdgeOptions(popupMenu, position, raycastHitPosition, edge, gameObject, appendActions),
_ => throw new ArgumentOutOfRangeException()
});
}
@@ -78,51 +291,103 @@ public static IEnumerable GetApplicableOptions(GraphElement gra
///
/// Returns the common options available for all graph elements.
///
+ /// The popup menu in which the options should be displayed.
+ /// The position to be displayed the popup menu.
+ /// The position of the raycast hit.
/// The graph element to get the options for
/// The game object that the graph element is attached to
+ /// Actions to be appended at the end of the entries.
/// Common options available for all graph elements
- private static IEnumerable GetCommonOptions(GraphElement graphElement, GameObject gameObject = null)
+ private static IEnumerable GetCommonOptions(PopupMenu popupMenu, Vector3 position,
+ Vector3 raycastHitPosition, GraphElement graphElement, GameObject gameObject = null,
+ IEnumerable appendActions = null)
{
- IList actions = new List
+ string name = graphElement.ID;
+ string target, source = target = null;
+ if (graphElement is Node node
+ && !string.IsNullOrEmpty(node.SourceName))
+ {
+ name = node.SourceName;
+ }
+ if (graphElement is Edge edge)
+ {
+ name = edge.Type;
+ source = edge.Source.SourceName ?? edge.Source.ID;
+ target = edge.Target.SourceName ?? edge.Target.ID;
+ }
+ IList entries = new List
{
- // TODO (#665): Ask for confirmation or allow undo.
- new("Delete", DeleteElement, Icons.Trash),
- // TODO (#666): Better properties view
- new("Properties", ShowProperties, Icons.Info),
- new("Show Metrics", ShowMetrics, Icons.Info),
+ new PopupMenuHeading(name, Priority: int.MaxValue)
};
-
- if (gameObject != null)
+ if (source != null && target != null)
{
- actions.Add(new("Show in City", Highlight, Icons.LightBulb));
+ entries.Add(new PopupMenuHeading("Source: " + source, Priority: int.MaxValue));
+ entries.Add(new PopupMenuHeading("Target: " + target, Priority: int.MaxValue));
}
+ entries.Add(new PopupMenuAction("Delete", DeleteElement, Icons.Trash, Priority: 0));
- if (graphElement.Filename != null)
+ entries.Add(new PopupMenuActionDoubleIcon("Inspect", () =>
{
- actions.Add(new("Show Code", ShowCode, Icons.Code));
- if (gameObject.ContainingCity() != null)
+ List subMenuEntries = new()
+ {
+ new PopupMenuAction("Inspect", () =>
+ {
+ ProvideParentMenuActions(popupMenu, position, raycastHitPosition, graphElement, gameObject, appendActions);
+ },
+ Icons.ArrowLeft, CloseAfterClick: false),
+ new PopupMenuAction("Properties", ShowProperties, Icons.Info),
+ new PopupMenuAction("Show Metrics", ShowMetrics, Icons.Info),
+ };
+ if (gameObject != null)
{
- actions.Add(new("Show Code Diff", ShowDiffCode, Icons.Code));
+ subMenuEntries.Add(new PopupMenuAction("Show in City", Highlight, Icons.LightBulb));
}
- }
- return actions;
+ if (graphElement.Filename != null)
+ {
+ subMenuEntries.Add(new PopupMenuAction("Show Code", ShowCode, Icons.Code));
+ if (gameObject.ContainingCity() != null)
+ {
+ subMenuEntries.Add(new PopupMenuAction("Show Code Diff", ShowDiffCode, Icons.Code));
+ }
+ }
+ subMenuEntries.AddRange(graphElement switch
+ {
+ Node node => GetNodeShowOptions(node, gameObject, appendActions != null),
+ Edge edge => GetEdgeShowOptions(edge, gameObject),
+ _ => throw new ArgumentOutOfRangeException()
+ });
+ UpdateEntries(popupMenu, position, subMenuEntries);
+
+ }, Icons.Info, Icons.ArrowRight, CloseAfterClick: false, Priority: 1));
+
+ return entries;
void DeleteElement()
{
+ if (graphElement is Node node && node.IsRoot())
+ {
+ ShowNotification.Warn("Forbidden!", "You can't delete a root node.");
+ return;
+ }
if (gameObject != null)
{
- GameElementDeleter.Delete(gameObject);
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.Delete);
+ DeleteAction action = (DeleteAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(gameObject);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
}
else
{
- graphElement.ItsGraph.RemoveElement(graphElement);
+ ConfirmDialogMenu confirm = new($"Do you really want to delete the element {graphElement.ID}?\r\nThis action cannot be undone.");
+ confirm.ExecuteAfterConfirmAsync(() => graphElement.ItsGraph.RemoveElement(graphElement)).Forget();
}
}
void ShowProperties()
{
- ShowNotification.Info("Node Properties", graphElement.ToString(), log: false);
+ ActivateWindow(CreatePropertyWindow(gameObject.MustGetComponent()));
}
void ShowMetrics()
@@ -155,104 +420,75 @@ void Highlight()
}
///
- /// Activates the tree window for the given graph element and returns it.
+ /// Provides the actions of the main menu and takes into account any appended actions.
///
- /// The graph element to activate the tree window for
- /// The transform of the game object that the graph element is attached to
- /// The title of the tree window to be used. Should only be set if this is not supposed
- /// to be the main tree window.
- /// The activated tree window
- private static TreeWindow ActivateTreeWindow(GraphElement graphElement, Transform transform, string title = null)
+ /// The popup menu in which the options should be displayed.
+ /// The position to be displayed the popup menu.
+ /// The position of the raycast hit.
+ /// The graph element to get the options for
+ /// The game object that the graph element is attached to
+ /// Actions to be appended at the end of the entries.
+ private static void ProvideParentMenuActions(PopupMenu popupMenu, Vector3 position,
+ Vector3 raycastHitPosition, GraphElement graphElement, GameObject gameObject = null,
+ IEnumerable appendActions = null)
{
- WindowSpace manager = WindowSpaceManager.ManagerInstance[WindowSpaceManager.LocalPlayer];
- TreeWindow openWindow = manager.Windows.OfType().FirstOrDefault(x => x.Graph == graphElement.ItsGraph && (title == null || x.Title == title));
- if (openWindow == null)
- {
- // Window is not open yet, so we create it.
- GameObject city = SceneQueries.GetCodeCity(transform).gameObject;
- openWindow = city.AddComponent();
- openWindow.Graph = graphElement.ItsGraph;
- if (title != null)
+ if (appendActions != null)
{
- openWindow.Title = title;
+ List actions = new(GetApplicableOptions(popupMenu, position, raycastHitPosition,
+ graphElement, gameObject, appendActions)
+ .OfType()
+ .Where(x => !x.Name.Contains("TreeWindow")));
+ actions.AddRange(appendActions);
+ UpdateEntries(popupMenu, position, actions);
+ }
+ else
+ {
+ UpdateEntries(popupMenu, position, GetApplicableOptions(popupMenu, position, raycastHitPosition,
+ graphElement, gameObject));
}
- manager.AddWindow(openWindow);
- }
- manager.ActiveWindow = openWindow;
- return openWindow;
- }
-
- ///
- /// Returns a showing the attributes of .
- ///
- /// The graph element to activate the metric window for
- /// The object showing the attributes of the specified graph element.
- private static MetricWindow CreateMetricWindow(GraphElementRef graphElementRef)
- {
- // Create new window for active selection, or use existing one
- if (!graphElementRef.TryGetComponent(out MetricWindow metricMenu))
- {
- metricMenu = graphElementRef.gameObject.AddComponent();
- metricMenu.Title = "Metrics for " + graphElementRef.Elem.ToShortString();
- metricMenu.GraphElement = graphElementRef.Elem;
- }
- return metricMenu;
- }
-
- ///
- /// Activates the given window, that is, adds it to the window space and makes it the active window.
- ///
- /// The window to activate
- private static void ActivateWindow(BaseWindow window)
- {
- WindowSpace manager = WindowSpaceManager.ManagerInstance[WindowSpaceManager.LocalPlayer];
- if (!manager.Windows.Contains(window))
- {
- manager.AddWindow(window);
- }
- manager.ActiveWindow = window;
}
///
- /// Returns the options available for the given node.
+ /// Returns the show options available for the given node.
///
- /// The node to get the options for
+ /// The node to get the show options for
/// The game object that the node is attached to
- /// Options available for the given node
- private static IEnumerable GetNodeOptions(Node node, GameObject gameObject)
+ /// Whether the popup menu was opened via tree view.
+ /// Show options available for the given node
+ private static IEnumerable GetNodeShowOptions(Node node, GameObject gameObject, bool openViaTreeView)
{
- IList actions = new List
+ List actions = new();
+ if (!openViaTreeView)
{
- new("Reveal in TreeView", RevealInTreeView, Icons.TreeView),
- };
-
+ actions.Add(new PopupMenuAction("Reveal in TreeView", RevealInTreeView, Icons.TreeView));
+ }
if (node.OutgoingsOfType(LSP.Reference).Any())
{
- actions.Add(new("Show References", () => ShowTargets(LSP.Reference, false).Forget(), Icons.IncomingEdge));
+ actions.Add(new PopupMenuAction("Show References", () => ShowTargets(LSP.Reference, false).Forget(), Icons.IncomingEdge));
}
if (node.OutgoingsOfType(LSP.Declaration).Any())
{
- actions.Add(new("Show Declaration", () => ShowTargets(LSP.Declaration).Forget(), Icons.OutgoingEdge));
+ actions.Add(new PopupMenuAction("Show Declaration", () => ShowTargets(LSP.Declaration).Forget(), Icons.OutgoingEdge));
}
if (node.OutgoingsOfType(LSP.Definition).Any())
{
- actions.Add(new("Show Definition", () => ShowTargets(LSP.Definition).Forget(), Icons.OutgoingEdge));
+ actions.Add(new PopupMenuAction("Show Definition", () => ShowTargets(LSP.Definition).Forget(), Icons.OutgoingEdge));
}
if (node.OutgoingsOfType(LSP.Extend).Any())
{
- actions.Add(new("Show Supertype", () => ShowTargets(LSP.Extend).Forget(), Icons.OutgoingEdge));
+ actions.Add(new PopupMenuAction("Show Supertype", () => ShowTargets(LSP.Extend).Forget(), Icons.OutgoingEdge));
}
if (node.OutgoingsOfType(LSP.Call).Any())
{
- actions.Add(new("Show Outgoing Calls", () => ShowTargets(LSP.Call).Forget(), Icons.OutgoingEdge));
+ actions.Add(new PopupMenuAction("Show Outgoing Calls", () => ShowTargets(LSP.Call).Forget(), Icons.OutgoingEdge));
}
if (node.OutgoingsOfType(LSP.OfType).Any())
{
- actions.Add(new("Show Type", () => ShowTargets(LSP.OfType).Forget(), 'T'));
+ actions.Add(new PopupMenuAction("Show Type", () => ShowTargets(LSP.OfType).Forget(), 'T'));
}
-
return actions;
+
void RevealInTreeView()
{
ActivateTreeWindow(node, gameObject.transform).RevealElementAsync(node).Forget();
@@ -285,30 +521,133 @@ async UniTaskVoid ShowTargets(string kind, bool outgoings = true)
}
///
- /// Returns the options available for the given edge.
+ /// Returns the options available for the given node.
///
- /// The edge to get the options for
- /// The game object that the edge is attached to
- /// Options available for the given edge
- private static IEnumerable GetEdgeOptions(Edge edge, GameObject gameObject)
+ /// The popup menu in which the options should be displayed.
+ /// The position to be displayed the popup menu.
+ /// The position of the raycast hit.
+ /// The node to get the options for
+ /// The game object that the node is attached to
+ /// Actions to be appended at the end of the entries.
+ /// Options available for the given node
+ private static IEnumerable GetNodeOptions(PopupMenu popupMenu, Vector3 position, Vector3 raycastHitPosition,
+ Node node, GameObject gameObject, IEnumerable appendActions)
{
- IList actions = new List
+ IList actions = new List();
+
+ if (appendActions == null)
{
- new("Show at Source (TreeView)", RevealAtSource, Icons.TreeView),
- new("Show at Target (TreeView)", RevealAtTarget, Icons.TreeView),
- };
+ actions.Add(new PopupMenuAction("Edit Node", EditNode, Icons.PenToSquare, Priority: 1));
+ actions.Add(new PopupMenuAction("Move", MoveNode, Icons.Move, Priority: 5));
+ actions.Add(new PopupMenuAction("New Edge", NewEdge, Icons.Edge, Priority: 2));
+ actions.Add(new PopupMenuAction("New Node", NewNode, '+', Priority: 3));
- if (edge.Type == "Clone")
+ if (gameObject != null)
+ {
+ VisualNodeAttributes gameNodeAttributes = gameObject.ContainingCity().NodeTypes[node.Type];
+ if (gameNodeAttributes.AllowManualNodeManipulation)
+ {
+ actions.Add(new PopupMenuAction("Rotate", RotateNode, Icons.Rotate, Priority: 4));
+ actions.Add(new PopupMenuAction("Resize Node", ResizeNode, Icons.Resize));
+ actions.Add(new PopupMenuAction("Scale Node", ScaleNode, Icons.Scale));
+ }
+ }
+ }
+
+ return node.IsRoot() ? new List() { } :
+ new List() { CreateSubMenu(popupMenu, position, raycastHitPosition,
+ "Node Options", Icons.Node, actions, node, gameObject, 2, appendActions) };
+
+ void MoveNode()
{
- actions.Add(new("Show Unified Diff", ShowUnifiedDiff, Icons.Compare));
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.Move);
+ UpdatePlayerMenu();
+ MoveAction action = (MoveAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(gameObject, raycastHitPosition);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
}
- if (edge.IsInImplementation() && ReflexionGraph.IsDivergent(edge))
+ void RotateNode()
{
- actions.Add(new("Accept Divergence", AcceptDivergence, Icons.Checkmark));
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.Rotate);
+ UpdatePlayerMenu();
+ RotateAction action = (RotateAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(gameObject);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
}
- return actions;
+ void NewNode()
+ {
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.NewNode);
+ AddNodeAction action = (AddNodeAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(gameObject, raycastHitPosition);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
+ }
+
+ void NewEdge()
+ {
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.NewEdge);
+ UpdatePlayerMenu();
+ AddEdgeAction action = (AddEdgeAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(gameObject);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
+ }
+
+ void EditNode()
+ {
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.EditNode);
+ UpdatePlayerMenu();
+ EditNodeAction action = (EditNodeAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(node);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
+ }
+
+ void ResizeNode()
+ {
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.ResizeNode);
+ UpdatePlayerMenu();
+ ResizeNodeAction action = (ResizeNodeAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(gameObject);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
+ }
+
+ void ScaleNode()
+ {
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.ScaleNode);
+ UpdatePlayerMenu();
+ ScaleNodeAction action = (ScaleNodeAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(gameObject);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
+ }
+ }
+
+ ///
+ /// Returns the show options available for the given edge.
+ ///
+ /// The edge to get the show options for
+ /// The game object that the edge is attached to
+ /// Show options available for the given edge
+ private static IEnumerable GetEdgeShowOptions(Edge edge, GameObject gameObject)
+ {
+ List entries = new() {
+ new PopupMenuAction("Show at Source (TreeView)", RevealAtSource, Icons.TreeView),
+ new PopupMenuAction("Show at Target (TreeView)", RevealAtTarget, Icons.TreeView)
+ };
+
+ if (edge.Type == "Clone")
+ {
+ entries.Add(new PopupMenuAction("Show Unified Diff", ShowUnifiedDiff, Icons.Compare));
+ }
+
+ return entries;
+
void RevealAtSource()
{
@@ -325,10 +664,185 @@ void ShowUnifiedDiff()
ActivateWindow(ShowCodeAction.ShowUnifiedDiff(gameObject.MustGetComponent()));
}
+ }
+
+ ///
+ /// Returns the options available for the given edge.
+ ///
+ /// The popup menu in which the options should be displayed.
+ /// The position to be displayed the popup menu.
+ /// The position of the raycast hit.
+ /// The edge to get the options for
+ /// The game object that the edge is attached to
+ /// Options to be append at the end of the entries.
+ /// Options available for the given edge
+ private static IEnumerable GetEdgeOptions
+ (PopupMenu popupMenu,
+ Vector3 position,
+ Vector3 raycastHitPosition,
+ Edge edge,
+ GameObject gameObject,
+ IEnumerable appendActions = null)
+ {
+ IList actions = new List();
+
+ if (edge.IsInImplementation() && ReflexionGraph.IsDivergent(edge))
+ {
+ actions.Add(new PopupMenuAction("Accept Divergence", AcceptDivergence, Icons.Checkmark));
+ }
+
+ List entries = new();
+ if (actions.Count > 0)
+ {
+ entries.Add(CreateSubMenu(popupMenu, position, raycastHitPosition,
+ "Edge Options", Icons.Node, actions, edge, gameObject, 2, appendActions));
+ }
+ return entries;
+
void AcceptDivergence()
{
- AcceptDivergenceAction.CreateConvergentEdge(edge);
+ ActionStateType previousAction = GlobalActionHistory.Current();
+ GlobalActionHistory.Execute(ActionStateTypes.AcceptDivergence);
+ AcceptDivergenceAction action = (AcceptDivergenceAction)GlobalActionHistory.CurrentAction();
+ action.ContextMenuExecution(edge);
+ ExcecutePreviousActionAsync(action, previousAction).Forget();
}
}
+ #endregion
+
+ ///
+ /// Activates the tree window for the given graph element and returns it.
+ ///
+ /// The graph element to activate the tree window for
+ /// The transform of the game object that the graph element is attached to
+ /// The title of the tree window to be used. Should only be set if this is not supposed
+ /// to be the main tree window.
+ /// The activated tree window
+ private static TreeWindow ActivateTreeWindow(GraphElement graphElement, Transform transform, string title = null)
+ {
+ WindowSpace manager = WindowSpaceManager.ManagerInstance[WindowSpaceManager.LocalPlayer];
+ TreeWindow openWindow = manager.Windows.OfType()
+ .FirstOrDefault(x => x.Graph == graphElement.ItsGraph && (title == null || x.Title == title));
+
+ if (openWindow == null)
+ {
+ // Window is not open yet, so we create it.
+ GameObject city = SceneQueries.GetCodeCity(transform).gameObject;
+ openWindow = city.AddComponent();
+ openWindow.Graph = graphElement.ItsGraph;
+ if (title != null)
+ {
+ openWindow.Title = title;
+ }
+ manager.AddWindow(openWindow);
+ }
+ manager.ActiveWindow = openWindow;
+ return openWindow;
+ }
+
+ ///
+ /// Returns a showing the attributes of .
+ ///
+ /// The graph element to activate the metric window for
+ /// The object showing the attributes of the specified graph element.
+ private static MetricWindow CreateMetricWindow(GraphElementRef graphElementRef)
+ {
+ // Create new window for active selection, or use existing one
+ if (!graphElementRef.TryGetComponent(out MetricWindow metricMenu))
+ {
+ metricMenu = graphElementRef.gameObject.AddComponent();
+ metricMenu.Title = "Metrics for " + graphElementRef.Elem.ToShortString();
+ metricMenu.GraphElement = graphElementRef.Elem;
+ }
+ return metricMenu;
+ }
+
+ ///
+ /// Returns a showing the attributes of .
+ ///
+ /// The graph element to activate the property window for
+ /// The object showing the attributes of the specified graph element.
+ private static PropertyWindow CreatePropertyWindow(GraphElementRef graphElementRef)
+ {
+ // Create new window for active selection, or use existing one
+ if (!graphElementRef.TryGetComponent(out PropertyWindow propertyMenu))
+ {
+ propertyMenu = graphElementRef.gameObject.AddComponent();
+ propertyMenu.Title = "Properties for " + graphElementRef.Elem.ToShortString();
+ propertyMenu.GraphElement = graphElementRef.Elem;
+ }
+ return propertyMenu;
+ }
+
+ ///
+ /// Activates the given window, that is, adds it to the window space and makes it the active window.
+ ///
+ /// The window to activate
+ private static void ActivateWindow(BaseWindow window)
+ {
+ WindowSpace manager = WindowSpaceManager.ManagerInstance[WindowSpaceManager.LocalPlayer];
+ if (!manager.Windows.Contains(window))
+ {
+ manager.AddWindow(window);
+ }
+ manager.ActiveWindow = window;
+ }
+
+ ///
+ /// Creates a sub menu for the context menu.
+ ///
+ /// The popup menu in which the options should be displayed.
+ /// The position to be displayed in the popup menu.
+ /// The position of the raycast hit.
+ /// The name for the sub menu.
+ /// The icon for the sub menu.
+ /// A list of the actions which should be displayed in the sub menu.
+ /// The graph element to get the options for
+ /// The game object that the graph element is attached to
+ /// The priority for this sub menu.
+ /// Actions to be append at the end of the entries.
+ /// The created sub menu.
+ private static PopupMenuActionDoubleIcon CreateSubMenu(PopupMenu popupMenu, Vector3 position,
+ Vector3 raycastHitPosition, string name, char icon, IEnumerable actions,
+ GraphElement graphElement, GameObject gameObject = null, int priority = 0,
+ IEnumerable appendActions = null)
+ {
+ return new(name, () =>
+ {
+ List entries = new()
+ {
+ new PopupMenuAction(name, () =>
+ {
+ ProvideParentMenuActions(popupMenu, position, raycastHitPosition, graphElement, gameObject, appendActions);
+ },
+ Icons.ArrowLeft, CloseAfterClick: false, Priority: int.MaxValue)
+ };
+ entries.AddRange(actions);
+ UpdateEntries(popupMenu, position, entries);
+ }, icon, Icons.ArrowRight, CloseAfterClick: false, priority);
+ }
+
+ ///
+ /// Ensures that the previous action is executed again after the current action has
+ /// been fully completed ().
+ /// Additionally, the is updated.
+ ///
+ /// The current action which was executed via context menu.
+ /// The previously executed action to be re-executed.
+ private static async UniTask ExcecutePreviousActionAsync(IReversibleAction action, ActionStateType previousAction)
+ {
+ await UniTask.WaitUntil(() => action.CurrentProgress() == IReversibleAction.Progress.Completed);
+ GlobalActionHistory.Execute(previousAction);
+ UpdatePlayerMenu();
+ }
+
+ ///
+ /// Updates the current active entry in the .
+ ///
+ private static void UpdatePlayerMenu()
+ {
+ LocalPlayer.TryGetPlayerMenu(out PlayerMenu menu);
+ menu.UpdateActiveEntry();
+ }
}
}
diff --git a/Assets/SEE/Controls/Actions/DeleteAction.cs b/Assets/SEE/Controls/Actions/DeleteAction.cs
index 9c5a120a07..d4a49b7fd3 100644
--- a/Assets/SEE/Controls/Actions/DeleteAction.cs
+++ b/Assets/SEE/Controls/Actions/DeleteAction.cs
@@ -83,10 +83,10 @@ public override void Stop()
}
///
- /// The graph element (a game object representing a node or edge) that was
- /// hit by the user for deletion. Set in .
+ /// The graph elements (game objects, each representing a node or edge) that were
+ /// chosen by the user for deletion.
///
- private GameObject hitGraphElement;
+ private List hitGraphElements = new();
///
/// Contains all implicitly deleted nodes and edges as a consequence of the deletion
@@ -98,9 +98,6 @@ public override void Stop()
/// are deleted and contained in this set. This set will always include the
/// explicitly selected node to be deleted.
///
- /// The will always be included in this set
- /// unless it is null.
- ///
/// Note that we will not actually destroy the deleted objects for the time
/// being to be able to revert the deletion. Instead the objects will simply be set
/// to inactive so that they are no longer visible and findable. They will
@@ -120,14 +117,12 @@ public override bool Update()
&& Raycasting.RaycastGraphElement(out RaycastHit raycastHit, out GraphElementRef _) != HitGraphElement.None)
{
// the hit object is the one to be deleted
- hitGraphElement = raycastHit.collider.gameObject;
- Assert.IsTrue(hitGraphElement.HasNodeRef() || hitGraphElement.HasEdgeRef());
- InteractableObject.UnselectAll(true);
- (_, deletedGameObjects) = GameElementDeleter.Delete(hitGraphElement);
- new DeleteNetAction(hitGraphElement.name).Execute();
- CurrentState = IReversibleAction.Progress.Completed;
- AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound);
- return true; // the selected objects are deleted and this action is done now
+ hitGraphElements.Add(raycastHit.collider.gameObject);
+ return Delete(); // the selected objects are deleted and this action is done now
+ }
+ else if (ExecuteViaContextMenu)
+ {
+ return Delete();
}
else
{
@@ -135,6 +130,52 @@ public override bool Update()
}
}
+ ///
+ /// Executes the deletion.
+ ///
+ /// true if the deletion can be executed.
+ private bool Delete()
+ {
+ deletedGameObjects = new HashSet();
+ InteractableObject.UnselectAll(true);
+ foreach (GameObject go in hitGraphElements)
+ {
+ if (!go.HasNodeRef() && !go.HasEdgeRef()
+ || go.HasNodeRef() && go.IsRoot())
+ {
+ continue;
+ }
+ (_, ISet deleted) = GameElementDeleter.Delete(go);
+ deletedGameObjects.UnionWith(deleted);
+ }
+ CurrentState = IReversibleAction.Progress.Completed;
+ AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound);
+ return true;
+ }
+
+ ///
+ /// Used to execute the from the context menu.
+ /// It sets the object to be deleted and ensures that the method
+ /// performs the execution via context menu.
+ ///
+ /// The object to be deleted.
+ public void ContextMenuExecution(GameObject toDelete)
+ {
+ ContextMenuExecution(new List { toDelete });
+ }
+
+ ///
+ /// Used to execute the from the multiselection context menu.
+ /// It sets the objects to be deleted and ensures that the method
+ /// performs the execution via context menu.
+ ///
+ /// The objects to be deleted.
+ public void ContextMenuExecution(IEnumerable toDelete)
+ {
+ ExecuteViaContextMenu = true;
+ hitGraphElements = toDelete.ToList();
+ }
+
///
/// Undoes this DeleteAction.
///
@@ -151,8 +192,17 @@ public override void Undo()
public override void Redo()
{
base.Redo();
- GameElementDeleter.Delete(hitGraphElement);
- new DeleteNetAction(hitGraphElement.name).Execute();
+ foreach (GameObject go in hitGraphElements)
+ {
+ if (go.IsRoot())
+ {
+ continue;
+ }
+ new DeleteNetAction(go.name).Execute();
+#pragma warning disable VSTHRD110
+ GameElementDeleter.Delete(go);
+#pragma warning restore VSTHRD110
+ }
}
///
diff --git a/Assets/SEE/Controls/Actions/DrawAction.cs b/Assets/SEE/Controls/Actions/DrawAction.cs
deleted file mode 100644
index fcd0dfce83..0000000000
--- a/Assets/SEE/Controls/Actions/DrawAction.cs
+++ /dev/null
@@ -1,197 +0,0 @@
-using SEE.Game;
-using SEE.GO;
-using SEE.Utils;
-using System;
-using System.Collections.Generic;
-using UnityEngine;
-using SEE.Audio;
-using SEE.Utils.History;
-
-namespace SEE.Controls.Actions
-{
- ///
- /// Allows to create drawings by the mouse cursor.
- /// It serves as an example for a continuous action that modifies the
- /// scene while active.
- ///
- internal class DrawAction : AbstractPlayerAction
- {
- ///
- /// A new instance of .
- /// See .
- ///
- /// new instance of
- public static IReversibleAction CreateReversibleAction()
- {
- return new DrawAction();
- }
-
- ///
- /// A new instance of .
- /// See .
- ///
- /// new instance of
- public override IReversibleAction NewInstance()
- {
- return CreateReversibleAction();
- }
-
- ///
- /// The material for all lines drawn by this action. Will be generated randomly
- /// (see the static constructor).
- ///
- private static readonly Material material;
-
- ///
- /// The object holding the line renderer.
- ///
- private GameObject line;
-
- ///
- /// The renderer used to draw the line.
- ///
- private LineRenderer renderer;
-
- ///
- /// The positions of the line in world space.
- ///
- private Vector3[] positions;
-
- ///
- /// Static constructor initializing .
- ///
- static DrawAction()
- {
- // A random color.
- Color color = UnityEngine.Random.ColorHSV();
- // A range of exactly that single random color.
- ColorRange colorRange = new ColorRange(color, color, 1);
- // The materials factory for exactly that single random color.
- Materials materials = new Materials(Materials.ShaderType.PortalFree, colorRange);
- // The material for exactly that single random color.
- material = materials.Get(0, 0);
- }
-
- ///
- /// Initializes , , and .
- /// See .
- ///
- public override void Awake()
- {
- base.Awake();
- positions = new Vector3[0];
- }
-
- ///
- /// Creates and adds to it.
- /// The matrial for the line will be .
- /// Sets the attributes of the line. Does not actually draw anything.
- ///
- private void SetUpRenderer()
- {
- line = new GameObject("line");
- renderer = line.AddComponent();
- renderer.sharedMaterial = material; // all lines share the same material
- renderer.startWidth = 0.01f;
- renderer.endWidth = renderer.startWidth;
- renderer.useWorldSpace = true;
- renderer.positionCount = positions.Length;
- }
-
- ///
- /// Continues the line at the point of the mouse position and draws it.
- /// See .
- ///
- public override bool Update()
- {
- if (!Raycasting.IsMouseOverGUI())
- {
- if (Input.GetMouseButtonDown(0) || Input.GetMouseButton(0))
- {
- // We create the line on demand so that there is no left-over
- // when the drawing action has never actually started to draw anything.
- if (line == null)
- {
- SetUpRenderer();
- }
- // FIXME: This would needed to be adjusted to VR and AR.
- // The position at which to continue the line.
- Vector3 newPosition = Input.mousePosition;
- newPosition.z = 1.0f;
- newPosition = Camera.main.ScreenToWorldPoint(newPosition);
-
- // Add newPosition to the line renderer.
- Vector3[] newPositions = new Vector3[positions.Length + 1];
- Array.Copy(sourceArray: positions, destinationArray: newPositions, length: positions.Length);
- newPositions[newPositions.Length - 1] = newPosition;
- positions = newPositions;
-
- DrawLine();
- // The line has been continued so this action has had a visible effect.
- CurrentState = IReversibleAction.Progress.Completed;
- }
- if (Input.GetMouseButtonDown(0))
- {
- AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.ScribbleSound);
- }
- // The action is considered complete if the mouse button is no longer pressed.
- return Input.GetMouseButtonUp(0);
- }
- return false;
- }
-
- ///
- /// Draws the line given the .
- ///
- private void DrawLine()
- {
- renderer.positionCount = positions.Length;
- renderer.SetPositions(positions);
- }
-
- ///
- /// Destroys the drawn line.
- /// See .
- ///
- public override void Undo()
- {
- base.Undo(); // required to set properly.
- Destroyer.Destroy(line);
- line = null;
- renderer = null;
- }
-
- ///
- /// Redraws the drawn line (setting up and adds
- /// before that).
- /// See .
- ///
- public override void Redo()
- {
- base.Redo(); // required to set properly.
- SetUpRenderer();
- DrawLine();
- }
-
- ///
- /// Returns the of this action.
- ///
- ///
- public override ActionStateType GetActionStateType()
- {
- return ActionStateTypes.Draw;
- }
-
- ///
- /// The set of IDs of all gameObjects changed by this action.
- ///
- /// Because this action does not actually change any game object,
- /// an empty set is always returned.
- ///
- /// an empty set
- public override HashSet GetChangedObjects()
- {
- return new HashSet();
- }
- }
-}
diff --git a/Assets/SEE/Controls/Actions/EditNodeAction.cs b/Assets/SEE/Controls/Actions/EditNodeAction.cs
index 089f755b70..7ebea455e1 100644
--- a/Assets/SEE/Controls/Actions/EditNodeAction.cs
+++ b/Assets/SEE/Controls/Actions/EditNodeAction.cs
@@ -7,6 +7,8 @@
using SEE.Utils;
using UnityEngine;
using SEE.Utils.History;
+using SEE.Game.SceneManipulation;
+using SEE.UI.Notification;
namespace SEE.Controls.Actions
{
@@ -99,9 +101,16 @@ public override bool Update()
GameObject editedNode = raycastHit.collider.gameObject;
if (editedNode.TryGetNode(out Node node))
{
- progress = ProgressState.WaitingForInput;
- memento = new Memento(node);
- OpenDialog();
+ if (!node.IsRoot())
+ {
+ progress = ProgressState.WaitingForInput;
+ memento = new Memento(node);
+ OpenDialog();
+ }
+ else
+ {
+ ShowNotification.Warn("Root node is readonly", "You cannot edit the root node.");
+ }
}
else
{
@@ -126,11 +135,24 @@ public override bool Update()
break;
default:
- throw new NotImplementedException("Unhandled case.");
+ throw new NotImplementedException($"Unhandled case {nameof(progress)}.");
}
return result;
}
+ ///
+ /// Used to execute the from the context menu.
+ /// Opens the edit dialog for the
+ /// and ensures that the method performs the execution via context menu.
+ ///
+ /// The node to be edit.
+ public void ContextMenuExecution(Node node)
+ {
+ progress = ProgressState.WaitingForInput;
+ memento = new Memento(node);
+ OpenDialog();
+ }
+
///
/// Sends an EditNodeNetAction to all clients with the given 's
/// ID, SourceName and Type.
@@ -147,8 +169,8 @@ private static void NotifyClients(Node node)
public override void Undo()
{
base.Undo();
- memento.Node.SourceName = memento.OriginalName;
- memento.Node.Type = memento.OriginalType;
+ GameNodeEditor.ChangeName(memento.Node, memento.OriginalName);
+ GameNodeEditor.ChangeType(memento.Node, memento.OriginalType);
NotifyClients(memento.Node);
}
@@ -158,8 +180,8 @@ public override void Undo()
public override void Redo()
{
base.Redo();
- memento.Node.SourceName = memento.NewName;
- memento.Node.Type = memento.NewType;
+ GameNodeEditor.ChangeName(memento.Node, memento.NewName);
+ GameNodeEditor.ChangeType(memento.Node, memento.NewType);
NotifyClients(memento.Node);
}
diff --git a/Assets/SEE/Controls/Actions/GlobalActionHistory.cs b/Assets/SEE/Controls/Actions/GlobalActionHistory.cs
index 40540af29d..3b6bf6fe77 100644
--- a/Assets/SEE/Controls/Actions/GlobalActionHistory.cs
+++ b/Assets/SEE/Controls/Actions/GlobalActionHistory.cs
@@ -1,5 +1,4 @@
-using SEE.Utils;
-using SEE.Utils.History;
+using SEE.Utils.History;
using static SEE.Utils.History.ActionHistory;
namespace SEE.Controls.Actions
@@ -13,7 +12,7 @@ public static class GlobalActionHistory
///
/// The history of actions.
///
- private static readonly ActionHistory history = new ActionHistory();
+ private static readonly ActionHistory history = new();
///
/// Executes the currently active action (if there is any).
@@ -95,6 +94,15 @@ public static ActionStateType Current()
return history.CurrentAction?.GetActionStateType();
}
+ ///
+ /// Gets the currently executed action.
+ ///
+ /// The currently executed action.
+ public static IReversibleAction CurrentAction()
+ {
+ return history.CurrentAction;
+ }
+
///
/// True if the action history is empty.
///
diff --git a/Assets/SEE/Controls/Actions/MoveAction.cs b/Assets/SEE/Controls/Actions/MoveAction.cs
index 919b1cf2db..882b0de9be 100644
--- a/Assets/SEE/Controls/Actions/MoveAction.cs
+++ b/Assets/SEE/Controls/Actions/MoveAction.cs
@@ -1,17 +1,21 @@
using System;
using System.Collections.Generic;
using SEE.Audio;
+using SEE.Controls.Interactables;
+using SEE.DataModel.DG;
using SEE.Game;
using SEE.Game.City;
+using SEE.Game.Drawable.ActionHelpers;
using SEE.Game.SceneManipulation;
-using SEE.UI.Notification;
using SEE.GO;
using SEE.Net.Actions;
using SEE.Tools.ReflexionAnalysis;
+using SEE.UI.Notification;
using SEE.Utils;
-using UnityEngine;
-using Node = SEE.DataModel.DG.Node;
using SEE.Utils.History;
+using UnityEngine;
+using UnityEngine.EventSystems;
+
namespace SEE.Controls.Actions
{
@@ -20,6 +24,23 @@ namespace SEE.Controls.Actions
///
internal class MoveAction : AbstractPlayerAction
{
+ ///
+ /// The currently grabbed object if any.
+ ///
+ private GrabbedObject grabbedObject;
+
+ ///
+ /// The object to move which was selected via context menu.
+ ///
+ private GameObject contextMenuObjectToMove;
+
+ ///
+ /// The offset of the cursor to the pivot of .
+ ///
+ private Vector3 cursorOffset = Vector3.zero;
+
+ #region ReversibleAction
+
///
/// Returns a new instance of .
///
@@ -53,6 +74,141 @@ public override ActionStateType GetActionStateType()
return ActionStateTypes.Move;
}
+ ///
+ /// Reacts to the user interactions. An object can be grabbed and moved
+ /// around. If it is put onto another node, it will be re-parented onto this
+ /// node. If we are operating in a , re-parenting
+ /// may be a mapping of an implementation node onto an architecture node
+ /// or a hierarchical re-parenting. If we are operating in a different kind
+ /// of city, re-parenting is always hierarchically interpreted. A hierarchical
+ /// re-parenting means that the moved node becomes a child of the target node
+ /// both in the game-node hierarchy as well as in the underlying graph.
+ /// .
+ ///
+ /// true if completed
+ public override bool Update()
+ {
+ if (EventSystem.current.IsPointerOverGameObject())
+ {
+ // User interacts with UI element
+ return false;
+ }
+ bool mouseHeldDown = Queries.LeftMouseInteraction();
+ if (!grabbedObject.IsGrabbed) // grab object
+ {
+ if (Queries.LeftMouseDown() && !ExecuteViaContextMenu)
+ {
+ // User starts dragging the currently hovered object.
+ InteractableObject hoveredObject = InteractableObject.HoveredObjectWithWorldFlag;
+ if (hoveredObject == null)
+ {
+ return false;
+ }
+
+ if (Raycasting.RaycastGraphElement(out RaycastHit grabbedObjectHit, out GraphElementRef _)
+ == HitGraphElement.Node)
+ {
+ cursorOffset = grabbedObjectHit.point - hoveredObject.transform.position;
+ }
+
+ // An object to be grabbed must be representing a node that is not the root.
+ if (hoveredObject.gameObject.TryGetNode(out Node node) && !node.IsRoot())
+ {
+ grabbedObject.Grab(hoveredObject.gameObject);
+ AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.PickupSound, hoveredObject.gameObject);
+ CurrentState = IReversibleAction.Progress.InProgress;
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+ else if (ExecuteViaContextMenu && !mouseHeldDown)
+ {
+ // User starts dragging object selected via context menu.
+ // Override the initial cursorOffset based on new mouse position to reduce jump
+ if (contextMenuObjectToMove.TryGetNodeRef(out NodeRef nodeRef)
+ && Raycasting.RaycastLowestNode(out RaycastHit? targetObjectHit, out Node _, nodeRef))
+ {
+ // Calculate position on object and close to the cursor
+ Vector3 objectSize = contextMenuObjectToMove.WorldSpaceSize();
+ Vector3 objectPosition = contextMenuObjectToMove.transform.position;
+ Vector3 anchorPosition = targetObjectHit.Value.point;
+ anchorPosition.x = Mathf.Clamp(anchorPosition.x, objectPosition.x - 0.5f * objectSize.x, objectPosition.x + 0.5f * objectSize.x);
+ anchorPosition.z = Mathf.Clamp(anchorPosition.z, objectPosition.z - 0.5f * objectSize.z, objectPosition.z + 0.5f * objectSize.z);
+ cursorOffset = anchorPosition - objectPosition;
+ }
+
+ grabbedObject.Grab(contextMenuObjectToMove);
+ CurrentState = IReversibleAction.Progress.InProgress;
+ }
+ }
+ else if (mouseHeldDown ^ ExecuteViaContextMenu) // drag grabbed object
+ {
+ Raycasting.RaycastLowestNode(out RaycastHit? targetObjectHit, out Node _, grabbedObject.Node);
+ if (targetObjectHit.HasValue)
+ {
+ GameObject newTarget = targetObjectHit.Value.transform.gameObject;
+ grabbedObject.MoveToTarget(newTarget, targetObjectHit.Value.point - cursorOffset);
+ // The grabbed node is not yet at its final destination. The user is still moving
+ // it. We will run a what-if reflexion analysis to give immediate feedback on the
+ // consequences if the user were putting the grabbed node onto the node the user
+ // is currently aiming at.
+ grabbedObject.Reparent(newTarget, true);
+ }
+ }
+ else // end dragging
+ {
+ if (grabbedObject.GrabbedGameObject != null)
+ {
+ AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound, grabbedObject.GrabbedGameObject);
+ }
+
+ bool wasMoved = grabbedObject.UnGrab();
+ // Action is finished.
+ // Prevent instant re-grab if the action was started via context menu and is not completed
+ ExecuteViaContextMenu = ExecuteViaContextMenu && wasMoved;
+ CurrentState = wasMoved ? IReversibleAction.Progress.Completed : IReversibleAction.Progress.NoEffect;
+ return wasMoved;
+ }
+ return false;
+ }
+
+ ///
+ /// .
+ ///
+ public override void Undo()
+ {
+ base.Undo();
+ grabbedObject.Undo();
+ }
+
+ ///
+ /// .
+ ///
+ public override void Redo()
+ {
+ base.Redo();
+ grabbedObject.Redo();
+ }
+
+ #endregion ReversibleAction
+
+ ///
+ /// Used to execute the from the context menu.
+ /// It ensures that the method performs the execution via context menu for
+ /// the selected game object .
+ ///
+ /// The object to be moved.
+ /// The hit position of the object
+ public void ContextMenuExecution(GameObject objToMove, Vector3 raycastHitPosition)
+ {
+ ExecuteViaContextMenu = true;
+ cursorOffset = raycastHitPosition - objToMove.transform.position;
+ contextMenuObjectToMove = objToMove;
+ }
+
///
/// Data structure to manage the game object that was grabbed.
/// Provides also the necessary capability for Undo/Redo.
@@ -85,7 +241,6 @@ public void Grab(GameObject gameObject)
{
GrabbedGameObject = gameObject;
originalParent = gameObject.transform.parent;
- originalLocalScale = gameObject.transform.localScale;
originalWorldPosition = gameObject.transform.position;
IsGrabbed = true;
if (gameObject.TryGetComponent(out InteractableObject interactableObject))
@@ -111,50 +266,77 @@ public void Grab(GameObject gameObject)
}
///
- /// The currently grabbed object is considered to be ungrabbed.
+ /// Ends the move action by making the new position final.
+ /// The grabbed object will be reset to its original position if placement at the current position is not possible.
///
+ /// true if the object has been placed, or false if placement has been reset
/// thrown if no object is currently
/// grabbed
- public void UnGrab()
+ public bool UnGrab()
{
if (!IsGrabbed || GrabbedGameObject == null)
{
throw new InvalidOperationException("No object is being grabbed.");
}
+
+ bool wasMoved = originalWorldPosition != currentPositionOfGrabbedObject;
+ if (!CanBePlaced())
+ {
+ // Node does not fit, resetting…
+ UnReparent();
+ wasMoved = false;
+ }
else
{
- UnmarkAsTarget();
- if (GrabbedGameObject.TryGetComponent(out InteractableObject interactableObject))
- {
- interactableObject.SetGrab(grab: false, isInitiator: true) ;
- }
- ShowLabel.Off(GrabbedGameObject);
- IsGrabbed = false;
- // Note: We do not set grabbedObject to null because we may need its
- // value later for Undo/Redo.
+ UnHighlightTarget();
+ }
+
+ if (GrabbedGameObject.TryGetComponent(out InteractableObject interactableObject))
+ {
+ interactableObject.SetGrab(grab: false, isInitiator: true);
}
+ ShowLabel.Off(GrabbedGameObject);
+ IsGrabbed = false;
+ // Note: We do not set grabbedObject to null because we may need its
+ // value later for Undo/Redo.
+
+ return wasMoved;
+ }
+
+ ///
+ /// Checks if can be placed.
+ ///
+ /// It can be placed if:
+ ///
+ /// - fits in the 2D area of , and
+ /// - does not overlap with its new siblings in
+ ///
+ ///
+ /// true if can be placed
+ public readonly bool CanBePlaced()
+ {
+ Vector3 size = GrabbedGameObject.WorldSpaceSize();
+ Vector3 parentSize = NewParent.WorldSpaceSize();
+ return size.x <= parentSize.x
+ && size.z <= parentSize.z
+ && !GrabbedGameObject.OverlapsWithSiblings();
}
///
/// Memorizes the new parent of after it was moved.
/// Can be the original parent. Relevant for .
///
- private GameObject newParent;
+ internal GameObject NewParent { private set; get; }
///
/// The original parent of before it was grabbed.
///
private Transform originalParent;
- ///
- /// The original local scale of before it was grabbed.
- ///
- private Vector3 originalLocalScale;
-
///
/// Whether an object has been grabbed.
///
- /// true if an object has been grabbed
+ /// true if an object has been grabbed
/// The default value for bool is false, which is exactly
/// what we need
internal bool IsGrabbed { private set; get; }
@@ -162,7 +344,7 @@ public void UnGrab()
///
/// The name of the grabbed object if any was grabbed; otherwise the empty string.
///
- internal string Name => GrabbedGameObject != null ? GrabbedGameObject.name : string.Empty;
+ internal readonly string Name => GrabbedGameObject != null ? GrabbedGameObject.name : string.Empty;
///
/// The position of the grabbed object in world space.
@@ -187,7 +369,7 @@ internal readonly Vector3 Position
/// The node reference associated with the grabbed object. May be null if no
/// node is associated with the grabbed object.
///
- public NodeRef Node => GrabbedGameObject.TryGetNodeRef(out NodeRef result) ? result : null;
+ public readonly NodeRef Node => GrabbedGameObject.TryGetNodeRef(out NodeRef result) ? result : null;
///
/// The original position of when it was grabbed.
@@ -207,10 +389,11 @@ internal readonly Vector3 Position
///
private readonly void MoveToOrigin()
{
- if (GrabbedGameObject)
+ if (GrabbedGameObject == null)
{
- MoveTo(GrabbedGameObject, originalWorldPosition);
+ return;
}
+ MoveTo(GrabbedGameObject, originalWorldPosition);
}
///
@@ -218,61 +401,80 @@ private readonly void MoveToOrigin()
/// to its origin via .
/// This method will be called for Redo.
///
- private readonly void MoveToLastUserRequestedPosition()
+ private readonly void MoveToNewPosition()
{
- if (GrabbedGameObject)
+ if (GrabbedGameObject == null)
{
- MoveTo(GrabbedGameObject, currentPositionOfGrabbedObject);
+ return;
}
+ MoveTo(GrabbedGameObject, currentPositionOfGrabbedObject, 1);
}
///
- /// Moves the grabbed object to in world space
- /// immediately, that is, without any animation.
+ /// Moves the grabbed game object onto at the approximate position
+ /// of in world space coordinates.
+ /// The placement is immediate and without any animation.
+ ///
+ /// The is adapted so that the grabbed node will appear on top of the
+ /// .
+ ///
+ /// The will be updated to reflect the actual target
+ /// world-space position after the move operation.
+ ///
+ /// The will NOT be reparented to the .
+ ///
///
- /// the position where the grabbed object
- /// should be moved in world space
- internal void MoveTo(Vector3 targetPosition)
+ /// the game object to place the grabbed node on
+ /// the world-space position where the grabbed node should be moved
+ internal void MoveToTarget(GameObject targetGameObject, Vector3 targetPosition)
{
- if (GrabbedGameObject)
+ if (GrabbedGameObject == null)
{
- currentPositionOfGrabbedObject = targetPosition;
- MoveTo(GrabbedGameObject, targetPosition, 0);
+ return;
}
+ currentPositionOfGrabbedObject = GameNodeMover.GetCoordinatesOn(GrabbedGameObject.WorldSpaceSize(), targetPosition, targetGameObject);
+ MoveTo(GrabbedGameObject, currentPositionOfGrabbedObject, 0);
}
#region HitColor
///
- /// The game node we have marked as a target of the grabbed and moved node (where it
- /// should be put on / mapped onto). May be null if none has been marked. It is saved
- /// here because we need to do the unmarking later.
+ /// The game node we have highlighted as a target of the move action.
+ /// May be null if none has been marked.
+ /// It is stored here because we need to remove the highlight later.
///
- private GameObject markedGameObject;
+ private GameObject highlightedTarget;
///
- /// Highlights as a target of the grabbed and moved node.
+ /// Highlights the current target, , visually and removes previous target highlight.
///
- /// the target of the grabbed and moved node
- private void MarkAsTarget(Transform hitObject)
+ private void HighlightTarget()
{
- markedGameObject = hitObject.gameObject;
+ if (highlightedTarget == NewParent)
+ {
+ return;
+ }
+
+ UnHighlightTarget();
+ highlightedTarget = NewParent;
+
// [Highlight Plus note] Important! If you change the hierarchy of your object
// (change its parent or attach it to another object), you need to call effect.Refresh()
// to make Highlight Plus update its internal data.
- Highlighter.SetHighlight(markedGameObject, true);
- new HighlightNetAction(markedGameObject.name, true).Execute();
+ Highlighter.SetHighlight(highlightedTarget, true);
+ new HighlightNetAction(highlightedTarget.name, true).Execute();
}
///
- /// Turns off the highlighting of if not null.
+ /// Removes the highlight of if not null.
///
- private void UnmarkAsTarget()
+ private void UnHighlightTarget()
{
- if (markedGameObject)
+ if (highlightedTarget != null)
{
- Highlighter.SetHighlight(markedGameObject, false);
- new HighlightNetAction(markedGameObject.name, false).Execute();
+ Highlighter.SetHighlight(highlightedTarget, false);
+ new HighlightNetAction(highlightedTarget.name, false).Execute();
+ highlightedTarget = null;
}
}
@@ -283,18 +485,20 @@ private void UnmarkAsTarget()
///
/// The new state of after this call is its inital
/// state at the point in time when it was grabbed, i.e.
- /// (1) has its
- /// (2) has its
- /// (3) has its
- /// (4) all side effects of re-parenting have been undone (e.g.,
+ ///
+ /// - has its
+ /// - has its
+ /// - all side effects of re-parenting have been undone (e.g.,
/// the parent of the graph node associated with
/// is the graph node associated with ;
/// there may be additional side effects of re-parenting, however,
- /// triggered by the reflexion analysis; all of these are reverted).
- ///
+ /// triggered by the reflexion analysis; all of these are reverted).
+ ///
+ ///
+ ///
/// Important note: Some of these changes do not come into effect immediately.
/// They may be delayed by an animation.
- ///
+ ///
internal void Undo()
{
UnReparent();
@@ -303,91 +507,99 @@ internal void Undo()
///
/// Reverts .
///
+ ///
/// The new state of after this call is its
/// state at the point in time just before was called, i.e.
- ///
- /// (1) is put on the roof of possibly scaled down to fit
- /// at the world-space position
- /// (2) has as its game-object parent
- /// (4) all side effects of re-parenting have been are in place (e.g.,
+ ///
+ /// - is put on the roof of at the world-space position
+ ///
+ /// - has as its game-object parent
+ /// - all side effects of re-parenting are applied (e.g.,
/// the parent of the graph node associated with
/// is the graph node associated with
/// if both game nodes represent implementation entities; there may be other
/// side effects of re-parenting, however, triggered by the reflexion analysis;
- /// all of these are in place).
- ///
+ /// all of these are in place).
+ ///
+ ///
+ ///
+ ///
/// Important note: Some of these changes do not come into effect immediately.
/// They may be delayed by an animation.
- ///
+ ///
internal void Redo()
{
- MoveToLastUserRequestedPosition();
- Reparent(newParent);
+ MoveToNewPosition();
+ if (NewParent != originalParent)
+ {
+ Reparent(NewParent, false, true);
+ }
}
///
- /// Moves onto the roof of
- /// visually and marks it as target (the previously marked object is unmarked).
+ /// Reparents the grabbed object to .
+ ///
+ /// If the placement , the target node will be highlighted
+ /// as well as the grabbed node. The color of the grabbed node's outline indicates if a
+ /// placement is possible at the current position based available space on .
+ ///
/// Also re-parents onto semantically.
/// If , the exact semantics of the re-parenting
/// is determined by ;
/// otherwise by .
- ///
+ ///
/// The will be an immediate child of in the
/// game-object hierarchy afterwards.
///
/// the target node of the re-parenting, i.e., the new parent
- internal void Reparent(GameObject target)
+ /// if true, the new target is considered temporary
+ /// if true, the method is executed as part of undo or redo
+ internal void Reparent(GameObject target, bool isProvisional, bool isUnOrRedo = false)
{
- // target must not be a descendant of grabbedObject
- if (!IsDescendant(target, GrabbedGameObject))
+ // Note: If target is a descendant of the grabbed node something must be wrong with the raycast!
+ bool targetChanged = NewParent != target;
+ NewParent = isUnOrRedo ? NewParent : target; // continue working with target!
+
+ if (isProvisional)
{
- PutOnAndFit(GrabbedGameObject, target, originalParent.gameObject, originalLocalScale);
- UnmarkAsTarget();
- MarkAsTarget(target.transform);
-
- newParent = target;
- // The mapping is only possible if we are in a reflexion city
- // and the mapping target is not the root of the graph.
- if (withinReflexionCity && !target.IsRoot())
- {
- ReflexionMapperSetParent(GrabbedGameObject, target);
- }
- else
+ HighlightTarget();
+ if (GrabbedGameObject.TryGetComponent(out Outline outline))
{
- GameNodeMoverSetParent(GrabbedGameObject, target);
+ outline.OutlineColor = CanBePlaced() ? Color.green : Color.red;
}
}
- // True if node is a descendant of root in the underlying graph.
- static bool IsDescendant(GameObject node, GameObject root)
+ if (!targetChanged && !isUnOrRedo)
{
- return node.GetNode().IsDescendantOf(root.GetNode());
+ return;
+ }
+
+ // The mapping is only possible if we are in a reflexion city
+ // and the mapping target is not the root of the graph.
+ if (withinReflexionCity && !target.IsRoot())
+ {
+ ReflexionMapperSetParent(GrabbedGameObject, target);
+ }
+ else
+ {
+ GameNodeMoverSetParent(GrabbedGameObject, target);
}
}
///
/// Reverts the parenting of the , i.e., unmarks the
- /// current target and restores original position and scale of .
+ /// current target and restores original position of .
/// If , its explicit architecture mapping will be
/// removed; otherwise its will be restored.
///
+ ///
/// This method is the reverse function of .
+ ///
///
internal void UnReparent()
{
- UnmarkAsTarget();
- if (GrabbedGameObject.transform.parent.gameObject != originalParent.gameObject)
- {
- if (withinReflexionCity)
- {
- ReflexionMapperSetParent(GrabbedGameObject, originalParent.gameObject);
- }
- else
- {
- GameNodeMoverSetParent(GrabbedGameObject, originalParent.gameObject);
- }
- }
+ UnHighlightTarget();
+ Reparent(originalParent.gameObject, false, true);
RestoreOriginalAppearance();
}
@@ -406,8 +618,7 @@ private static void NewVersion(GameObject grabbedObject)
///
/// Moves the grabbed object to in world space.
///
- /// the position where the grabbed object
- /// should be moved in world space
+ /// the world-space position the grabbed object should be moved to
/// the factor of the animation for moving the grabbed object
/// This is only a movement, not a change to any hierarchy.
private static void MoveTo(GameObject grabbedObject, Vector3 targetPosition, float factor = 1)
@@ -416,22 +627,6 @@ private static void MoveTo(GameObject grabbedObject, Vector3 targetPosition, flo
new MoveNetAction(grabbedObject.name, targetPosition, factor).Execute();
}
- ///
- /// Runs and propagates it to all clients.
- ///
- /// The will be an immediate child of in the
- /// game-object hierarchy afterwards.
- ///
- /// the child to be put on
- /// new parent of
- /// original parent of
- /// original local scale of
- private static void PutOnAndFit(GameObject child, GameObject newParent, GameObject originalParent, Vector3 originalLocalScale)
- {
- GameNodeMover.PutOnAndFit(child.transform, newParent, originalParent, originalLocalScale);
- new PutOnAndFitNetAction(child.name, newParent.name, originalParent.name, originalLocalScale).Execute();
- }
-
///
/// Runs and propagates it to all clients.
///
@@ -475,147 +670,15 @@ private static void GameNodeMoverSetParent(GameObject child, GameObject parent)
///
/// Resets the marking of the target node and moves
- /// back to its and restores
- /// its .
+ /// back to its .
///
/// No changes are made to the game-node hierarchy or graph-node hierarchy.
///
private void RestoreOriginalAppearance()
{
- UnmarkAsTarget();
+ UnHighlightTarget();
MoveToOrigin();
- GrabbedGameObject.NodeOperator().ScaleTo(originalLocalScale);
- new ScaleNodeNetAction(GrabbedGameObject.name, originalLocalScale).Execute();
}
}
-
- ///
- /// The currently grabbed object if any.
- ///
- private GrabbedObject grabbedObject;
-
- ///
- /// The distance from the the position of when it was grabbed to
- /// the origin of the user's pointing device (e.g., main camera on a desktop, controller
- /// in VR) in world space. This distance will be maintained while the user has grabbed
- /// an object.
- ///
- private float distanceToUser;
-
- ///
- /// Reacts to the user interactions. An object can be grabbed and moved
- /// around. If it is put onto another node, it will be re-parented onto this
- /// node. If we are operating in a , re-parenting
- /// may be a mapping of an implementation node onto an architecture node
- /// or a hierarchical re-parenting. If we are operating in a different kind
- /// of city, re-parenting is always hierarchically interpreted. A hierarchical
- /// re-parenting means that the moved node becomes a child of the target node
- /// both in the game-node hierarchy as well as in the underlying graph.
- /// .
- ///
- /// true if completed
- public override bool Update()
- {
- if (UserIsGrabbing()) // start to grab the object or continue to move the grabbed object
- {
- if (!grabbedObject.IsGrabbed)
- {
- // User is starting dragging the currently hovered object.
- InteractableObject hoveredObject = InteractableObject.HoveredObjectWithWorldFlag;
- // An object to be grabbed must be representing a node that is not the root.
- if (hoveredObject && hoveredObject.gameObject.TryGetNode(out Node node) && !node.IsRoot())
- {
- grabbedObject.Grab(hoveredObject.gameObject);
- AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.PickupSound, hoveredObject.gameObject);
- // Remember the current distance from the pointing device to the grabbed object.
- distanceToUser = Vector3.Distance(Raycasting.UserPointsTo().origin, grabbedObject.Position);
- CurrentState = IReversibleAction.Progress.InProgress;
- }
- }
- else // continue moving the grabbed object
- {
- // The grabbed object will be moved on the surface of a sphere with
- // radius distanceToUser in the direction the user is pointing to.
- Ray ray = Raycasting.UserPointsTo();
- grabbedObject.MoveTo(ray.origin + distanceToUser * ray.direction);
- }
-
- // The grabbed node is not yet at its final destination. The user is still moving
- // it. We will run a what-if reflexion analysis to give immediate feedback on the
- // consequences if the user were putting the grabbed node onto the node the user
- // is currently aiming at.
- UpdateHierarchy();
- }
- else if (grabbedObject.IsGrabbed) // dragging has ended
- {
- // Finalize the action with the grabbed object.
- if (grabbedObject.GrabbedGameObject != null)
- {
- AudioManagerImpl.EnqueueSoundEffect(IAudioManager.SoundEffect.DropSound, grabbedObject.GrabbedGameObject);
- }
- grabbedObject.UnGrab();
- // Action is finished.
- CurrentState = IReversibleAction.Progress.Completed;
- return true;
- }
- return false;
- }
-
- ///
- /// Returns true if the user is currently grabbing.
- ///
- /// true if user is grabbing
- private static bool UserIsGrabbing()
- {
- // Index of the left mouse button.
- const int leftMouseButton = 0;
- // FIXME: We need a VR interaction, too.
- return Input.GetMouseButton(leftMouseButton);
- }
-
- ///
- /// If no node is grabbed, nothing happens. Otherwise:
- /// (1) If the user is currently pointing on a node, the grabbed object
- /// will be re-parented onto this node (.
- ///
- /// (2) If the user currently not pointing to any node, the grabbed object
- /// will be un-parented (.
- ///
- private void UpdateHierarchy()
- {
- if (grabbedObject.IsGrabbed)
- {
- if (Raycasting.RaycastLowestNode(out RaycastHit? raycastHit, out Node _, grabbedObject.Node))
- {
- // Note: the root node can never be grabbed. See above.
- // We need to undo the reparenting of the grabbed node if it is
- // currently reparented onto another node.
- grabbedObject.UnReparent();
- if (raycastHit.HasValue)
- {
- // The user is currently aiming at a node. The grabbed node is reparented onto this aimed node.
- grabbedObject.Reparent(raycastHit.Value.transform.gameObject);
- }
- }
- }
- }
-
- ///
- /// .
- ///
- public override void Undo()
- {
- base.Undo();
- grabbedObject.Undo();
- }
-
- ///
- /// .
- ///
- public override void Redo()
- {
- base.Redo();
- grabbedObject.Redo();
- }
}
}
diff --git a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs
index e0754dc2e3..bca279c947 100644
--- a/Assets/SEE/Controls/Actions/NodeManipulationAction.cs
+++ b/Assets/SEE/Controls/Actions/NodeManipulationAction.cs
@@ -1,8 +1,10 @@
using RTG;
using SEE.Audio;
using SEE.Game;
+using SEE.Game.City;
using SEE.Game.Operator;
using SEE.GO;
+using SEE.UI.Notification;
using SEE.Utils;
using SEE.Utils.History;
using System.Collections.Generic;
@@ -193,6 +195,17 @@ public override bool Update()
/// game node to start the manipulation with
protected void StartAction(GameObject gameNode)
{
+ if (!gameNode.TryGetNodeRef(out NodeRef nodeRef))
+ {
+ return;
+ }
+ VisualNodeAttributes gameNodeAttributes = gameNode.ContainingCity().NodeTypes[nodeRef.Value.Type];
+ if (!gameNodeAttributes.AllowManualNodeManipulation)
+ {
+ ShowNotification.Info("Manipulation Forbidden", "The node cannot be manipulated!", 5, false);
+ return;
+ }
+
GameNodeSelected = gameNode;
GameNodeMemento = CreateMemento(GameNodeSelected);
UsedGizmo.Enable(GameNodeSelected);
@@ -220,6 +233,18 @@ protected virtual void FinalizeAction()
/// true if has had a change
protected abstract bool HasChanges();
+ ///
+ /// Used to execute the from the context menu.
+ /// It ensures that the method performs the execution via context menu for
+ /// the selected game object .
+ ///
+ /// The object to be modify.
+ public void ContextMenuExecution(GameObject obj)
+ {
+ ExecuteViaContextMenu = true;
+ StartAction(obj);
+ }
+
#endregion Update
#region Memento
diff --git a/Assets/SEE/Controls/Actions/ResizeNodeAction.cs b/Assets/SEE/Controls/Actions/ResizeNodeAction.cs
new file mode 100644
index 0000000000..c0c80f3a9e
--- /dev/null
+++ b/Assets/SEE/Controls/Actions/ResizeNodeAction.cs
@@ -0,0 +1,824 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using SEE.DataModel.DG;
+using SEE.Game.City;
+using SEE.GO;
+using SEE.Net.Actions;
+using SEE.Utils;
+using SEE.Utils.History;
+
+namespace SEE.Controls.Actions
+{
+ ///
+ /// Action to resize a node.
+ ///
+ internal class ResizeNodeAction : AbstractPlayerAction
+ {
+ ///
+ /// The memento holding the information for and .
+ ///
+ private Memento memento;
+
+ ///
+ /// Reference to the resize gizmo that is attached to the game object during resize.
+ ///
+ private ResizeGizmo gizmo;
+
+ ///
+ /// Whether the action is finished and can be completed.
+ ///
+ private bool finished = false;
+
+ #region ReversibleAction
+
+ ///
+ /// Returns a new instance of .
+ ///
+ /// new instance
+ internal static IReversibleAction CreateReversibleAction() => new ResizeNodeAction();
+
+ ///
+ /// Returns a new instance of .
+ ///
+ /// new instance
+ public override IReversibleAction NewInstance() => new ResizeNodeAction();
+
+ ///
+ /// Returns the of this action.
+ ///
+ ///
+ public override ActionStateType GetActionStateType()
+ {
+ return ActionStateTypes.ResizeNode;
+ }
+
+ ///
+ /// Returns all IDs of gameObjects manipulated by this action.
+ ///
+ /// all IDs of gameObjects manipulated by this action
+ public override HashSet GetChangedObjects()
+ {
+ return memento.GameObject == null || CurrentState == IReversibleAction.Progress.NoEffect
+ ? new HashSet()
+ : new HashSet() { memento.GameObject.name };
+ }
+
+ /// .
+ ///
+ /// true if completed
+ public override bool Update()
+ {
+ if (finished)
+ {
+ CurrentState = IReversibleAction.Progress.Completed;
+ }
+ return CurrentState == IReversibleAction.Progress.Completed;
+ }
+
+ ///
+ /// Starts this .
+ ///
+ public override void Start()
+ {
+ base.Start();
+
+ InteractableObject.MultiSelectionAllowed = false;
+ InteractableObject.LocalAnySelectIn += OnSelectionChanged;
+ InteractableObject.LocalAnySelectOut += OnSelectionChanged;
+ }
+
+ ///
+ /// Stops this without completing it.
+ ///
+ public override void Stop()
+ {
+ base.Stop();
+
+ InteractableObject.LocalAnySelectIn -= OnSelectionChanged;
+ InteractableObject.LocalAnySelectOut -= OnSelectionChanged;
+ InteractableObject.MultiSelectionAllowed = true;
+
+ if (gizmo != null)
+ {
+ gizmo.OnSizeChanged -= OnResizeStep;
+ Destroyer.Destroy(gizmo);
+ }
+
+ if (CurrentState == IReversibleAction.Progress.NoEffect)
+ {
+ memento = new Memento();
+ }
+
+ }
+
+ ///
+ /// Undoes this ResizeNodeAction.
+ ///
+ public override void Undo()
+ {
+ base.Undo();
+
+ if (memento.GameObject == null)
+ {
+ return;
+ }
+
+ memento.GameObject.NodeOperator().ResizeTo(memento.OriginalLocalScale, memento.OriginalPosition);
+ new ResizeNodeNetAction(memento.GameObject.name, memento.OriginalLocalScale, memento.OriginalPosition).Execute();
+ }
+
+ ///
+ /// Redoes this ResizeNodeAction.
+ ///
+ public override void Redo()
+ {
+ base.Redo();
+
+ if (memento.GameObject == null)
+ {
+ return;
+ }
+
+ memento.GameObject.NodeOperator().ResizeTo(memento.NewLocalScale, memento.NewPosition);
+ new ResizeNodeNetAction(memento.GameObject.name, memento.NewLocalScale, memento.NewPosition).Execute();
+ }
+
+ #endregion ReversibleAction
+
+ ///
+ /// Used to execute the from the context menu.
+ ///
+ /// The object to be resize
+ ///
+ /// This method does not check if the object's type has
+ /// flag set.
+ ///
+ public void ContextMenuExecution(GameObject go)
+ {
+ ExecuteViaContextMenu = true;
+ InteractableObject.UnselectAll(true);
+ InteractableObject interactable = go.GetComponent();
+ interactable.SetSelect(true, true);
+
+ InteractableObject.MultiSelectionAllowed = false;
+ InteractableObject.LocalAnySelectIn += OnSelectionChanged;
+ InteractableObject.LocalAnySelectOut += OnSelectionChanged;
+ }
+
+ ///
+ /// Event handler that is called every time the node selection in changes.
+ ///
+ private void OnSelectionChanged(InteractableObject interactableObject)
+ {
+ // Interactable object is deselected when a handle is clicked, so we cannot stop here…
+ if (!interactableObject.IsSelected || InteractableObject.SelectedObjects.Count != 1)
+ {
+ return;
+ }
+
+ // New selection
+ if (memento.GameObject != null && interactableObject.gameObject != memento.GameObject)
+ {
+ Stop();
+
+ if (CurrentState == IReversibleAction.Progress.InProgress)
+ {
+ finished = true;
+ }
+ return;
+ }
+
+ // Incompatible type
+ GameObject selectedGameObject = interactableObject.gameObject;
+ if (!selectedGameObject.TryGetNodeRef(out NodeRef selectedNodeRef)
+ || !selectedGameObject.ContainingCity().NodeTypes[selectedNodeRef.Value.Type].AllowManualNodeManipulation)
+ {
+ return;
+ }
+
+ // Start resizing
+ memento = new Memento(selectedGameObject);
+ gizmo = memento.GameObject.AddOrGetComponent();
+ gizmo.OnSizeChanged += OnResizeStep;
+ }
+
+ ///
+ /// Event handler that is called by each time a resize step is finished.
+ ///
+ /// One resize step is finished each time a handle is released. This, however, does not finish the
+ /// .
+ ///
+ ///
+ /// The new local scale after the resize step
+ /// The new world-space position after the resize step
+ private void OnResizeStep(Vector3 newLocalScale, Vector3 newPosition)
+ {
+ CurrentState = IReversibleAction.Progress.InProgress;
+
+ memento.NewLocalScale = newLocalScale;
+ memento.NewPosition = newPosition;
+
+ // Apply new position and scale to update edges and propagate changes to other players
+ memento.GameObject.NodeOperator().ResizeTo(newLocalScale, newPosition, 0, reparentChildren: false);
+ new ResizeNodeNetAction(memento.GameObject.name, newLocalScale, newPosition).Execute();
+ }
+
+ ///
+ /// The metadata of a resize action that affects the scale and position of a node.
+ ///
+ private struct Memento
+ {
+ ///
+ /// The GameObject of the game object.
+ ///
+ public readonly GameObject GameObject;
+
+ ///
+ /// The original world-space position of the game object.
+ ///
+ public readonly Vector3 OriginalPosition;
+
+ ///
+ /// The original scale of the game object.
+ ///
+ public readonly Vector3 OriginalLocalScale;
+
+ ///
+ /// The new world-space position of the game object.
+ ///
+ public Vector3 NewPosition;
+
+ ///
+ /// The new scale of the game object.
+ ///
+ public Vector3 NewLocalScale;
+
+ ///
+ /// Constructs a .
+ ///
+ public Memento(GameObject go)
+ {
+ GameObject = go;
+ OriginalPosition = go.transform.position;
+ OriginalLocalScale = go.transform.localScale;
+ NewPosition = OriginalPosition;
+ NewLocalScale = OriginalLocalScale;
+ }
+ }
+
+ ///
+ /// Added as a component to a game object, this will add handles for manual resize.
+ ///
+ /// The event is emitted each time the user finishes a resize
+ /// step. To conclude the resize process, the component should be destroyed.
+ ///
+ ///
+ private class ResizeGizmo : MonoBehaviour
+ {
+ ///
+ /// The data of the resize step that is in action.
+ ///
+ private ResizeStepData currentResizeStep;
+
+ ///
+ /// The node reference.
+ ///
+ private NodeRef nodeRef;
+
+ ///
+ /// Stores the directional vectors that belong to the individual handles.
+ ///
+ private Dictionary handles;
+
+ ///
+ /// Used to remember if a click was detected in an earlier frame.
+ ///
+ private bool clicked = false;
+
+ #region Configurations
+
+ ///
+ /// The minimal size of a node in world space.
+ ///
+ private const float minSize = 0.04f;
+
+ ///
+ /// The minimal world-space distance between nodes while resizing.
+ ///
+ private const float padding = 0.004f;
+
+ ///
+ /// A small offset is used as a difference between the detection and the set value
+ /// to prevent instant re-detection.
+ ///
+ private const float detectionOffset = 0.0001f;
+
+ ///
+ /// The size of the handles.
+ ///
+ private static readonly Vector3 handleScale = new(0.02f, 0.02f, 0.02f);
+
+ ///
+ /// The color of the handles.
+ ///
+ private static Color handleColor = Color.cyan;
+
+ #endregion Configurations
+
+ #region Change Event
+
+ ///
+ /// The event is emitted each time a resize step has finished.
+ ///
+ public event Action OnSizeChanged;
+
+ #endregion Change Event
+
+ ///
+ /// Initializes the .
+ ///
+ ///
+ /// Thrown when the object script is attached to is not a node, i.e., has no NodeRef
+ /// component.
+ ///
+ private void Start()
+ {
+ if (!gameObject.TryGetNodeRef(out NodeRef nodeRef))
+ {
+ throw new InvalidOperationException("Can only be attached to GameObjects with NodeRef!");
+ }
+
+ this.nodeRef = nodeRef;
+ InitHandles();
+ }
+
+ ///
+ /// Destroys the handles.
+ ///
+ private void OnDestroy()
+ {
+ if (handles == null)
+ {
+ return;
+ }
+ foreach (GameObject handle in handles.Keys)
+ {
+ Destroyer.Destroy(handle);
+ }
+ }
+
+ ///
+ /// Manages the resize steps.
+ ///
+ /// A resize step starts when a handle is clicked and it ends when the handle is released.
+ ///
+ /// Calls when a new step is detected.
+ ///
+ /// During the resize step progress, each frame is called.
+ ///
+ ///
+ private void Update()
+ {
+ bool newClick = false;
+ if (Input.GetMouseButton(0))
+ {
+ newClick = !clicked;
+ clicked = true;
+ }
+ else if (clicked)
+ {
+ clicked = false;
+ currentResizeStep = new();
+ OnSizeChanged?.Invoke(transform.localScale, transform.position);
+ }
+
+ if (newClick)
+ {
+ StartResizing();
+ }
+
+ if (currentResizeStep.IsSet)
+ {
+ UpdateSize();
+ }
+ }
+
+ ///
+ /// Initializes the resize handles and stores them in
+ /// together with the direction vector.
+ ///
+ private void InitHandles()
+ {
+ handles = new Dictionary();
+
+ Vector3[] directions = new[] { Vector3.right, Vector3.left, Vector3.forward, Vector3.back,
+ Vector3.right + Vector3.forward, Vector3.right + Vector3.back,
+ Vector3.left + Vector3.forward, Vector3.left + Vector3.back };
+ Vector3 position = transform.position;
+ Vector3 size = gameObject.WorldSpaceSize();
+ foreach (Vector3 direction in directions)
+ {
+ handles[CreateHandle(direction, position, size)] = direction;
+ }
+ }
+
+ ///
+ /// Creates a resize handle game object at the appropriate position.
+ ///
+ /// The direction for which the handle is created.
+ /// The cached parent world-space position.
+ /// The cached parent world-space scale.
+ private GameObject CreateHandle(Vector3 direction, Vector3 parentWorldPosition, Vector3 parentWorldScale)
+ {
+ GameObject handle = GameObject.CreatePrimitive(PrimitiveType.Cube);
+ handle.GetComponent().material.color = handleColor;
+ handle.transform.localScale = handleScale;
+ handle.transform.localPosition = parentWorldPosition + 0.5f * Vector3.Scale(parentWorldScale, direction);
+ handle.name = $"handle__{direction.x}_{direction.y}_{direction.z}";
+ handle.transform.SetParent(transform);
+ return handle;
+ }
+
+ ///
+ /// Starts a resize step if a handle is selected.
+ ///
+ /// If the user points to a handle, the attribute is set
+ /// to the respective direction vector and the current transform values.
+ ///
+ ///
+ void StartResizing()
+ {
+ if (!Raycasting.RaycastAnything(out RaycastHit hit))
+ {
+ return;
+ }
+ Vector3? resizeDirection = handles.TryGetValue(hit.collider.gameObject, out Vector3 value) ? value : null;
+ if (resizeDirection == null)
+ {
+ return;
+ }
+
+ currentResizeStep = new(hit.point, resizeDirection.Value, transform);
+ }
+
+ ///
+ /// Does the actual resizing (scaling and positioning).
+ ///
+ void UpdateSize()
+ {
+ Raycasting.RaycastLowestNode(out RaycastHit? targetObjectHit, out Node hitNode, nodeRef);
+ if (!targetObjectHit.HasValue)
+ {
+ return;
+ }
+
+ // Collect siblings
+ Transform parent = transform.parent;
+ // We use an initial size of the list so that the memory does not need to get
+ // reallocated each time an item is added.
+ List siblings = new(parent.childCount);
+ foreach (Transform sibling in parent)
+ {
+ if (sibling != transform && sibling.gameObject.IsNodeAndActiveSelf())
+ {
+ siblings.Add(sibling);
+ }
+ }
+
+ // Collect children
+ List children = new(transform.childCount);
+ foreach (Transform child in transform)
+ {
+ if (child.gameObject.IsNodeAndActiveSelf())
+ {
+ children.Add(child);
+ }
+ }
+
+ // Calculate new scale and position
+ Vector3 hitPoint = targetObjectHit.Value.point;
+ Vector3 cursorMovement = Vector3.Scale(currentResizeStep.InitialHitPoint - hitPoint, currentResizeStep.Direction);
+ Vector3 newLocalSize = currentResizeStep.InitialLocalSize - Vector3.Scale(currentResizeStep.LocalScaleFactor, cursorMovement);
+ Vector3 newLocalPosition = currentResizeStep.InitialLocalPosition
+ - 0.5f * Vector3.Scale(currentResizeStep.LocalScaleFactor, Vector3.Scale(cursorMovement, currentResizeStep.Direction));
+
+ // Contain in parent
+ Bounds2D bounds = new(
+ newLocalPosition.x - newLocalSize.x / 2,
+ newLocalPosition.x + newLocalSize.x / 2,
+ newLocalPosition.z - newLocalSize.z / 2,
+ newLocalPosition.z + newLocalSize.z / 2
+ );
+ // Parent scale in its own context is always 1
+ Bounds2D otherBounds = new(
+ -0.5f + currentResizeStep.LocalPadding.x,
+ 0.5f - currentResizeStep.LocalPadding.x,
+ -0.5f + currentResizeStep.LocalPadding.z,
+ 0.5f - currentResizeStep.LocalPadding.z
+ );
+ if (currentResizeStep.Left && bounds.Left < otherBounds.Left)
+ {
+ bounds.Left = otherBounds.Left;
+ }
+ if (currentResizeStep.Right && bounds.Right > otherBounds.Right)
+ {
+ bounds.Right = otherBounds.Right;
+ }
+ if (currentResizeStep.Back && bounds.Back < otherBounds.Back)
+ {
+ bounds.Back = otherBounds.Back;
+ }
+ if (currentResizeStep.Forward && bounds.Front > otherBounds.Front)
+ {
+ bounds.Front = otherBounds.Front;
+ }
+
+ // Correct sibling overlap
+ foreach (Transform sibling in siblings)
+ {
+ Vector3 siblingSize = sibling.gameObject.LocalSize();
+ Vector3 siblingPos = sibling.localPosition;
+ otherBounds.Left = siblingPos.x - siblingSize.x / 2 - currentResizeStep.LocalPadding.x + detectionOffset;
+ otherBounds.Right = siblingPos.x + siblingSize.x / 2 + currentResizeStep.LocalPadding.x - detectionOffset;
+ otherBounds.Back = siblingPos.z - siblingSize.z / 2 - currentResizeStep.LocalPadding.z + detectionOffset;
+ otherBounds.Front = siblingPos.z + siblingSize.z / 2 + currentResizeStep.LocalPadding.z - detectionOffset;
+
+ if (bounds.Back > otherBounds.Front || bounds.Front < otherBounds.Back
+ || bounds.Left > otherBounds.Right || bounds.Right < otherBounds.Left)
+ {
+ // No overlap detected
+ continue;
+ }
+
+ // Calculate overlap
+ float[] overlap = { float.MaxValue, float.MaxValue };
+ if (currentResizeStep.Right)
+ {
+ overlap[0] = bounds.Right - otherBounds.Left;
+ }
+ if (currentResizeStep.Left)
+ {
+ overlap[0] = otherBounds.Right - bounds.Left;
+ }
+ if (currentResizeStep.Forward)
+ {
+ overlap[1] = bounds.Front - otherBounds.Back;
+ }
+ if (currentResizeStep.Back)
+ {
+ overlap[1] = otherBounds.Front - bounds.Back;
+ }
+
+ // Pick correction direction
+ if (overlap[0] < overlap[1] && newLocalSize.x - overlap[0] > currentResizeStep.MinLocalSize.x)
+ {
+ if (currentResizeStep.Right)
+ {
+ bounds.Right = otherBounds.Left - detectionOffset;
+ }
+ else
+ {
+ bounds.Left = otherBounds.Right + detectionOffset;
+ }
+ }
+ else if (newLocalSize.z - overlap[1] > currentResizeStep.MinLocalSize.z)
+ {
+ if (currentResizeStep.Forward)
+ {
+ bounds.Front = otherBounds.Back - detectionOffset;
+ }
+ else
+ {
+ bounds.Back = otherBounds.Front + detectionOffset;
+ }
+ }
+ }
+
+ // Contain all children
+ foreach (Transform child in children)
+ {
+ // Child position and scale on common parent
+ Vector3 childPos = Vector3.Scale(child.localPosition, transform.localScale) + transform.localPosition;
+ Vector3 childSize = Vector3.Scale(child.gameObject.LocalSize(), transform.localScale);
+ otherBounds.Left = childPos.x - childSize.x / 2 - currentResizeStep.LocalPadding.x + detectionOffset;
+ otherBounds.Right = childPos.x + childSize.x / 2 + currentResizeStep.LocalPadding.x - detectionOffset;
+ otherBounds.Back = childPos.z - childSize.z / 2 - currentResizeStep.LocalPadding.z + detectionOffset;
+ otherBounds.Front = childPos.z + childSize.z / 2 + currentResizeStep.LocalPadding.z - detectionOffset;
+
+ if (currentResizeStep.Right && bounds.Right < otherBounds.Right)
+ {
+ bounds.Right = otherBounds.Right + detectionOffset;
+ }
+
+ if (currentResizeStep.Left && bounds.Left > otherBounds.Left)
+ {
+ bounds.Left = otherBounds.Left - detectionOffset;
+ }
+
+
+ if (currentResizeStep.Forward && bounds.Front < otherBounds.Front)
+ {
+ bounds.Front = otherBounds.Front + detectionOffset;
+ }
+
+ if (currentResizeStep.Back && bounds.Back > otherBounds.Back)
+ {
+ bounds.Back = otherBounds.Back - detectionOffset;
+ }
+ }
+
+ newLocalSize.x = bounds.Right - bounds.Left;
+ newLocalSize.z = bounds.Front - bounds.Back;
+ newLocalPosition.x = bounds.Left + newLocalSize.x / 2;
+ newLocalPosition.z = bounds.Back + newLocalSize.z / 2;
+
+ // Ensure minimal size
+ if (newLocalSize.x < currentResizeStep.MinLocalSize.x || newLocalSize.z < currentResizeStep.MinLocalSize.z)
+ {
+ return;
+ }
+
+ // Prevent child nodes from getting scaled
+ Transform tempParent = transform.parent;
+ foreach (Transform childNode in children)
+ {
+ childNode.SetParent(tempParent);
+ }
+
+ // Apply new scale and position
+ transform.localScale = Vector3.Scale(newLocalSize, currentResizeStep.ScaleSizeFactor);
+ transform.localPosition = newLocalPosition;
+
+ // Reparent children
+ foreach (Transform child in children)
+ {
+ child.SetParent(transform);
+ }
+
+ // Restore handle scale
+ foreach (GameObject handle in handles.Keys)
+ {
+ handle.transform.SetParent(null);
+ handle.transform.localScale = handleScale;
+ handle.transform.SetParent(transform);
+ }
+ }
+
+ ///
+ /// Data structure for the individual resize steps.
+ ///
+ private readonly struct ResizeStepData
+ {
+ ///
+ /// Whether the struct has been explicitly initialized with values.
+ ///
+ public readonly bool IsSet;
+
+ ///
+ /// The initial raycast hit from which the resize step is started.
+ ///
+ public readonly Vector3 InitialHitPoint;
+
+ ///
+ /// The resize direction.
+ ///
+ public readonly Vector3 Direction;
+
+ ///
+ /// The position right before the resize step is started.
+ ///
+ public readonly Vector3 InitialLocalPosition;
+
+ ///
+ /// The local size right before the resize step is started.
+ ///
+ public readonly Vector3 InitialLocalSize;
+
+ ///
+ /// The factor to convert from local size to local scale.
+ ///
+ public readonly Vector3 ScaleSizeFactor;
+
+ ///
+ /// The factor to convert world-space to local coordinates in the parent's
+ /// coordinate system for the resize step.
+ ///
+ public readonly Vector3 LocalScaleFactor;
+
+ ///
+ /// The scaled by .
+ ///
+ public readonly Vector3 MinLocalSize;
+
+ ///
+ /// The scaled by .
+ /// This is effectively the local-space padding in parent, e.g., between
+ /// the resized object and its siblings.
+ ///
+ public readonly Vector3 LocalPadding;
+
+ ///
+ /// Does point to the left?
+ ///
+ public readonly bool Left;
+
+ ///
+ /// Does point to the right?
+ ///
+ public readonly bool Right;
+
+ ///
+ /// Does point forward?
+ ///
+ public readonly bool Forward;
+
+ ///
+ /// Does point backward?
+ ///
+ public readonly bool Back;
+
+ ///
+ /// Initializes the struct.
+ ///
+ public ResizeStepData (Vector3 initialHitPoint, Vector3 direction, Transform transform)
+ {
+ InitialHitPoint = initialHitPoint;
+ Direction = direction;
+ InitialLocalPosition = transform.localPosition;
+ InitialLocalSize = transform.gameObject.LocalSize();
+ Vector3 localScale = transform.localScale;
+ ScaleSizeFactor = new(
+ InitialLocalSize.x / localScale.x,
+ InitialLocalSize.y / localScale.y,
+ InitialLocalSize.z / localScale.z
+ );
+ Vector3 lossyScale = transform.lossyScale;
+ LocalScaleFactor = new(
+ localScale.x / lossyScale.x,
+ localScale.y / lossyScale.y,
+ localScale.z / lossyScale.z
+ );
+ IsSet = true;
+ MinLocalSize = minSize * LocalScaleFactor;
+ LocalPadding = padding * LocalScaleFactor;
+ Left = Direction.x < 0;
+ Right = Direction.x > 0;
+ Back = Direction.z < 0;
+ Forward = Direction.z > 0;
+ }
+ }
+
+ ///
+ /// Data structure for 2-dimensional bounds.
+ ///
+ private struct Bounds2D
+ {
+ ///
+ /// The left side of the area.
+ ///
+ public float Left;
+ ///
+ /// The right side of the area.
+ ///
+ public float Right;
+ ///
+ /// The back side of the area.
+ ///
+ public float Back;
+ ///
+ /// The front side of the area.
+ ///
+ public float Front;
+
+ ///
+ /// Initializes the struct.
+ ///
+ public Bounds2D (float left, float right, float back, float front)
+ {
+ Left = left;
+ Right = right;
+ Back = back;
+ Front = front;
+ }
+
+ ///
+ /// Implicit conversion to string.
+ ///
+ public static implicit operator string(Bounds2D bounds)
+ {
+ return bounds.ToString();
+ }
+
+ ///
+ /// Returns a printable string with the struct's values.
+ ///
+ public override readonly string ToString()
+ {
+ return $"{nameof(Bounds2D)}(Left: {Left}, Right: {Right}, Bottom: {Back}, Top: {Front})";
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/SEE/Net/Actions/ZoomNetAction.cs.meta b/Assets/SEE/Controls/Actions/ResizeNodeAction.cs.meta
similarity index 83%
rename from Assets/SEE/Net/Actions/ZoomNetAction.cs.meta
rename to Assets/SEE/Controls/Actions/ResizeNodeAction.cs.meta
index 49eeeb07c9..9ae5a32229 100644
--- a/Assets/SEE/Net/Actions/ZoomNetAction.cs.meta
+++ b/Assets/SEE/Controls/Actions/ResizeNodeAction.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: c9abcec227a331f45a2bc0addc501745
+guid: 99781f53228c4b2339f6a8816e1a353a
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Assets/SEE/Controls/Actions/ShowLabel.cs b/Assets/SEE/Controls/Actions/ShowLabel.cs
index f4e86969d9..b25db52ccd 100644
--- a/Assets/SEE/Controls/Actions/ShowLabel.cs
+++ b/Assets/SEE/Controls/Actions/ShowLabel.cs
@@ -160,7 +160,7 @@ public void On()
if (nodeOperator.Node != null)
{
LabelAttributes settings = GetLabelSettings(nodeOperator.Node, nodeOperator.City);
- if (settings.Show && pointer.Value.On)
+ if (settings.Show && pointer.Value.On && nodeOperator.LabelIsNotEmpty())
{
nodeOperator.FadeLabel(settings.LabelAlpha, pointer.Value.LastHit, settings.AnimationFactor);
}
diff --git a/Assets/SEE/Controls/Actions/ZoomAction.cs b/Assets/SEE/Controls/Actions/ZoomAction.cs
index 71c53d39f3..eac43e0e1a 100644
--- a/Assets/SEE/Controls/Actions/ZoomAction.cs
+++ b/Assets/SEE/Controls/Actions/ZoomAction.cs
@@ -29,6 +29,10 @@ protected class ZoomCommand
///
internal readonly Vector2 ZoomCenter;
///
+ /// Should the position be reset to original position?
+ ///
+ internal readonly bool ResetPosition;
+ ///
/// The amount of time in seconds that it should take to reach .
///
private readonly float duration;
@@ -37,12 +41,13 @@ protected class ZoomCommand
///
private readonly float startTime;
- internal ZoomCommand(Vector2 zoomCenter, float targetZoomSteps, float duration)
+ internal ZoomCommand(Vector2 zoomCenter, float targetZoomSteps, float duration, bool resetPosition = false)
{
TargetZoomSteps = targetZoomSteps;
ZoomCenter = zoomCenter;
this.duration = duration;
startTime = Time.realtimeSinceStartup;
+ ResetPosition = resetPosition;
}
///
@@ -82,16 +87,20 @@ protected struct ZoomState
///
/// The maximal number of zooming steps.
///
- internal const uint ZoomMaxSteps = 32;
+ internal const uint ZoomMaxSteps = 10;
///
/// Handles the speed in which we zoom into the city.
///
internal const float ZoomFactor = 0.5f;
///
- /// Original scale of city for reset.
+ /// Original local scale of city for reset.
///
- internal Vector3 OriginalScale;
+ internal Vector3 OriginalLocalScale;
+ ///
+ /// Original world-space position of city for reset.
+ ///
+ internal Vector3 OriginalPosition;
///
/// The list of active zoom commands, that is, those that are still to be executed.
@@ -109,6 +118,11 @@ protected struct ZoomState
///
/// Pushes a zoom command for execution. Zoom commands are automatically removed
/// once they are finished.
+ ///
+ /// The position will automatically be reset if +
+ /// equals 0, i.e., the zoom factor reaches 1.
+ /// Otherwise, is used for calculating the new position.
+ ///
///
/// The position to be zoomed towards.
/// The desired amount of zoom steps.
@@ -119,7 +133,7 @@ internal void PushZoomCommand(Vector2 zoomCenter, float zoomSteps, float duratio
if (zoomSteps != 0.0f)
{
float newZoomStepsInProgress = CurrentTargetZoomSteps + zoomSteps;
- ZoomCommands.Add(new ZoomCommand(zoomCenter, zoomSteps, duration));
+ ZoomCommands.Add(new ZoomCommand(zoomCenter, zoomSteps, duration, Mathf.Approximately(CurrentTargetZoomSteps + zoomSteps, 0)));
CurrentTargetZoomSteps = newZoomStepsInProgress;
}
}
@@ -128,20 +142,20 @@ internal void PushZoomCommand(Vector2 zoomCenter, float zoomSteps, float duratio
///
/// The zoom states for every root transform of a city. Once the first zoom is initiated,
/// the zoom state will be inserted here.
- ///
+ ///
/// The key of this mapping is the root game node (tagged by )
/// of the game-object hierarchy. It is the game object that will be zoomed;
/// all its descendants are scaled along with it.
- ///
+ ///
/// Because we may have multiple code cities in the scene, there is not only one such
/// root node.
///
- private readonly Dictionary rootTransformToZoomStates = new Dictionary();
+ private readonly Dictionary rootTransformToZoomStates = new();
///
/// The node operator for every root transform of a city.
///
- private readonly Dictionary rootTransformToOperator = new Dictionary();
+ private readonly Dictionary rootTransformToOperator = new();
///
/// Factor to apply to the animation.
@@ -173,12 +187,14 @@ private void FixedUpdate()
// If there is any zoom command, execute it.
float zoomSteps = zoomState.CurrentTargetZoomSteps;
int positionCount = 0;
- Vector2 positionSum = Vector3.zero;
+ Vector2 positionSum = Vector2.zero;
+ bool resetPosition = false;
for (int i = 0; i < zoomState.ZoomCommands.Count; i++)
{
positionCount++;
positionSum += zoomState.ZoomCommands[i].ZoomCenter;
+ resetPosition = zoomState.ZoomCommands[i].ResetPosition;
if (zoomState.ZoomCommands[i].IsFinished())
{
zoomState.ZoomCommands.RemoveAt(i--);
@@ -188,25 +204,20 @@ private void FixedUpdate()
zoomSteps -= zoomState.ZoomCommands[i].TargetZoomSteps - zoomState.ZoomCommands[i].CurrentDeltaScale();
}
}
- Vector3 averagePosition = new Vector3(positionSum.x / positionCount, rootTransform.position.y, positionSum.y / positionCount);
zoomState.CurrentZoomFactor = ConvertZoomStepsToZoomFactor(zoomSteps);
- Vector3 cityCenterToHitPoint = averagePosition - rootTransform.position;
- Vector3 cityCenterToHitPointUnscaled = cityCenterToHitPoint.DividePairwise(rootTransform.localScale);
-
- nodeOperator.ScaleTo(zoomState.CurrentZoomFactor * zoomState.OriginalScale, AnimationFactor);
- nodeOperator.MoveTo(rootTransform.position + cityCenterToHitPoint - Vector3.Scale(cityCenterToHitPointUnscaled, nodeOperator.TargetScale), AnimationFactor);
-
- new ZoomNetAction(rootTransform.name, nodeOperator.TargetPosition, nodeOperator.TargetScale).Execute();
- }
- else
- {
- float lastZoomFactor = zoomState.CurrentZoomFactor;
- zoomState.CurrentZoomFactor = ConvertZoomStepsToZoomFactor(zoomState.CurrentTargetZoomSteps);
- if (!Mathf.Approximately(lastZoomFactor, zoomState.CurrentZoomFactor))
+ Vector3 targetScale = zoomState.CurrentZoomFactor * zoomState.OriginalLocalScale;
+ Vector3 targetPosition = zoomState.OriginalPosition;
+ if (!resetPosition)
{
- nodeOperator.ScaleTo(zoomState.CurrentZoomFactor * zoomState.OriginalScale, AnimationFactor);
+ Vector3 averagePosition = new(positionSum.x / positionCount, rootTransform.position.y, positionSum.y / positionCount);
+ Vector3 cityCenterToHitPoint = averagePosition - rootTransform.position;
+ Vector3 cityCenterToHitPointUnscaled = cityCenterToHitPoint.DividePairwise(rootTransform.localScale);
+ targetPosition = rootTransform.position + cityCenterToHitPoint - Vector3.Scale(cityCenterToHitPointUnscaled, nodeOperator.TargetScale);
}
+
+ nodeOperator.ResizeTo(targetScale, targetPosition, AnimationFactor, true, false);
+ new ResizeNodeNetAction(rootTransform.name, targetScale, targetPosition, true, false).Execute();
}
}
}
@@ -224,7 +235,8 @@ protected ZoomState GetZoomStateCopy(Transform transform)
{
result = new ZoomState
{
- OriginalScale = transform.localScale,
+ OriginalLocalScale = transform.localScale,
+ OriginalPosition = transform.position,
ZoomCommands = new List((int)ZoomState.ZoomMaxSteps),
CurrentTargetZoomSteps = 0,
CurrentZoomFactor = 1.0f
diff --git a/Assets/SEE/Controls/Actions/ZoomActionDesktop.cs b/Assets/SEE/Controls/Actions/ZoomActionDesktop.cs
index d087d05160..a5e229d26f 100644
--- a/Assets/SEE/Controls/Actions/ZoomActionDesktop.cs
+++ b/Assets/SEE/Controls/Actions/ZoomActionDesktop.cs
@@ -98,7 +98,6 @@ private void Update()
// as requested per mouse wheel.
if (zoomTowards)
{
- zoomSteps = Mathf.Clamp(zoomSteps, -(int)zoomState.CurrentTargetZoomSteps, (int)ZoomState.ZoomMaxSteps - (int)zoomState.CurrentTargetZoomSteps);
zoomState.PushZoomCommand(hitPointOnPlane.XZ(), zoomSteps, ZoomState.DefaultZoomDuration);
}
diff --git a/Assets/SEE/Controls/Interactables/InteractableObject.cs b/Assets/SEE/Controls/Interactables/InteractableObject.cs
index f2239ae8aa..85d008dd2c 100644
--- a/Assets/SEE/Controls/Interactables/InteractableObject.cs
+++ b/Assets/SEE/Controls/Interactables/InteractableObject.cs
@@ -11,6 +11,7 @@
#endif
using SEE.Net.Actions;
using SEE.Audio;
+using SEE.Game.Operator;
namespace SEE.Controls
{
@@ -77,6 +78,11 @@ public sealed class InteractableObject : MonoBehaviour
///
public static readonly HashSet GrabbedObjects = new();
+ ///
+ /// If multiple objects should be selectable at the same time.
+ ///
+ public static bool MultiSelectionAllowed = true;
+
///
/// The selected objects per graph.
///
@@ -354,6 +360,11 @@ public void SetSelect(bool select, bool isInitiator)
{
Assert.IsTrue(IsSelected != select);
+ if (select && !MultiSelectionAllowed && SelectedObjects.Count > 0)
+ {
+ return;
+ }
+
IsSelected = select;
if (select)
@@ -371,7 +382,12 @@ public void SetSelect(bool select, bool isInitiator)
graphToSelectedIOs[graph].Add(this);
// Start blinking indefinitely.
- gameObject.Operator().Blink(-1);
+ GraphElementOperator op = gameObject.Operator();
+ op.Blink(-1);
+ if (op is EdgeOperator eop)
+ {
+ eop.AnimateDataFlow(true);
+ }
// Invoke events
SelectIn?.Invoke(this, isInitiator);
@@ -393,7 +409,12 @@ public void SetSelect(bool select, bool isInitiator)
graphToSelectedIOs[GraphElemRef.Elem.ItsGraph].Remove(this);
// Stop blinking.
- gameObject.Operator().Blink(0);
+ GraphElementOperator op = gameObject.Operator();
+ op.Blink(0);
+ if (op is EdgeOperator eop)
+ {
+ eop.AnimateDataFlow(false);
+ }
// Invoke events
SelectOut?.Invoke(this, isInitiator);
diff --git a/Assets/SEE/Game/City/VisualNodeAttributes.cs b/Assets/SEE/Game/City/VisualNodeAttributes.cs
index cc5d7e7247..99b759664b 100644
--- a/Assets/SEE/Game/City/VisualNodeAttributes.cs
+++ b/Assets/SEE/Game/City/VisualNodeAttributes.cs
@@ -159,6 +159,11 @@ public string DepthMetric
///
[Tooltip("Whether the source name will be added to a node.")]
public bool ShowNames = false;
+ ///
+ /// Whether users may manipulate (e.g., resize) instances of this type of node.
+ ///
+ [Tooltip("Whether users may manipulate (e.g., resize) instances of this type of node.")]
+ public bool AllowManualNodeManipulation = false;
///
/// Saves the settings in the configuration file.
@@ -178,6 +183,7 @@ public override void Save(ConfigWriter writer, string label)
AntennaSettings.Save(writer, antennaSettingsLabel);
writer.Save(OutlineWidth, outlineWidthLabel);
writer.Save(ShowNames, showNamesLabel);
+ writer.Save(AllowManualNodeManipulation, allowManualNodeManipulationLabel);
writer.EndGroup();
}
@@ -213,6 +219,7 @@ internal virtual void Restore(Dictionary values)
AntennaSettings.Restore(values, antennaSettingsLabel);
ConfigIO.Restore(values, outlineWidthLabel, ref OutlineWidth);
ConfigIO.Restore(values, showNamesLabel, ref ShowNames);
+ ConfigIO.Restore(values, allowManualNodeManipulationLabel, ref AllowManualNodeManipulation);
}
///
@@ -255,5 +262,10 @@ internal virtual void Restore(Dictionary values)
/// Label in the configuration file for .
///
private const string showNamesLabel = "ShowNames";
+
+ ///
+ /// Label in the configuration file for .
+ ///
+ private const string allowManualNodeManipulationLabel = "AllowManualNodeManipulation";
}
}
diff --git a/Assets/SEE/Game/CityRendering/NodeRenderer.cs b/Assets/SEE/Game/CityRendering/NodeRenderer.cs
index 3a0bbba05d..cc5b5132b3 100644
--- a/Assets/SEE/Game/CityRendering/NodeRenderer.cs
+++ b/Assets/SEE/Game/CityRendering/NodeRenderer.cs
@@ -511,7 +511,7 @@ private float GetMetricValue(Node node, string metricName)
/// for inner node kinds and nodelayout.
///
///
- protected void AddDecorations(GameObject gameNode)
+ public void AddDecorations(GameObject gameNode)
{
AddDecorations(new List { gameNode });
}
diff --git a/Assets/SEE/Game/Drawable/ActionHelpers/Queries.cs b/Assets/SEE/Game/Drawable/ActionHelpers/Queries.cs
index 824d723f8f..c1ddd8551b 100644
--- a/Assets/SEE/Game/Drawable/ActionHelpers/Queries.cs
+++ b/Assets/SEE/Game/Drawable/ActionHelpers/Queries.cs
@@ -13,12 +13,15 @@ public static class Queries
/// true if the user uses the left mouse button.
public static bool LeftMouseInteraction()
{
- return Input.GetMouseButton(0) || Input.GetMouseButtonDown(0);
+ return Input.GetMouseButton(0);
}
///
/// Registers the users left mouse down input.
///
+ ///
+ /// This is only true in the exact frame the mouse button is pressed down.
+ ///
/// True if the user uses left mouse down.
public static bool LeftMouseDown()
{
@@ -31,7 +34,7 @@ public static bool LeftMouseDown()
/// true if the user uses the left mouse button.
public static bool RightMouseInteraction()
{
- return Input.GetMouseButton(1) || Input.GetMouseButtonDown(1);
+ return Input.GetMouseButton(1);
}
///
diff --git a/Assets/SEE/Game/Operator/EdgeDirectionVisualizer.cs b/Assets/SEE/Game/Operator/EdgeDirectionVisualizer.cs
new file mode 100644
index 0000000000..7fcf3695ea
--- /dev/null
+++ b/Assets/SEE/Game/Operator/EdgeDirectionVisualizer.cs
@@ -0,0 +1,154 @@
+using System.Linq;
+using MoreLinq;
+using SEE.GO;
+using SEE.Utils;
+using UnityEngine;
+
+namespace SEE.Game.Operator
+{
+ ///
+ /// Provides the to the .
+ ///
+ public partial class EdgeOperator : GraphElementOperator<(Color start, Color end)>
+ {
+ ///
+ /// Implements a data flow visualization to indicate the direction of an edge.
+ ///
+ private class EdgeDirectionVisualizer : MonoBehaviour
+ {
+ ///
+ /// Maximal count of particles.
+ ///
+ private const int maxParticleCount = 12;
+ ///
+ /// Minimal distance between particles for the actual particle count.
+ ///
+ private const float minParticleDistance = 0.16f;
+ ///
+ /// Scale of the particle meshes.
+ ///
+ private static readonly Vector3 particleScale = new(0.012f, 0.012f, 0.012f);
+ ///
+ /// Color of the particle material.
+ ///
+ private static readonly Color particleColor = new(0.06f, 0.81f, 1.0f, 1.0f);
+ ///
+ /// Particle speed.
+ ///
+ private const float particleSpeed = 50f;
+
+ ///
+ /// The spline the edge is based on.
+ ///
+ private SEESpline seeSpline;
+ ///
+ /// The coordinates of the edge's vertices.
+ ///
+ private Vector3[] vertices;
+
+ ///
+ /// The actual particle count as calculated based on
+ /// and capped by .
+ ///
+ private int particleCount;
+ ///
+ /// The particle game objects.
+ ///
+ private GameObject[] particles;
+ ///
+ /// The current position of the particles.
+ ///
+ private float[] particlePositions;
+
+ ///
+ /// Destroys the particles when the component is destroyed.
+ ///
+ private void OnDestroy()
+ {
+ foreach (GameObject particle in particles)
+ {
+ seeSpline.OnRendererChanged -= OnSplineChanged;
+ Destroyer.Destroy(particle);
+ }
+ }
+
+ ///
+ /// Initializes the particles and fields.
+ ///
+ public void Start()
+ {
+ seeSpline = GetComponent();
+ seeSpline.OnRendererChanged += OnSplineChanged;
+ vertices = seeSpline.GenerateVertices();
+
+ particleCount = (int)Mathf.Max(Mathf.Min(GetApproxEdgeLength() / minParticleDistance, maxParticleCount), 1);
+
+ particles = new GameObject[particleCount];
+ particlePositions = new float[particleCount];
+
+ float separation = vertices.Length / (float)particleCount;
+ for (int i = 0; i < particleCount; i++)
+ {
+ particles[i] = GameObject.CreatePrimitive(PrimitiveType.Sphere);
+ particles[i].GetComponent().material.color = particleColor;
+ particlePositions[i] = separation * i;
+ particles[i].transform.localScale = particleScale;
+ particles[i].transform.SetParent(transform);
+ particles[i].transform.localPosition = GetPositionOnEdge(particlePositions[i]);
+ }
+ }
+
+ ///
+ /// Updates the position and color of the vertices.
+ ///
+ private void Update()
+ {
+ for (int i = 0; i < particleCount; i++)
+ {
+ particlePositions[i] += particleSpeed * Time.deltaTime;
+ if (particlePositions[i] >= vertices.Length)
+ {
+ particlePositions[i] = 0;
+ }
+ particles[i].transform.localPosition = GetPositionOnEdge(particlePositions[i]);
+ }
+ }
+
+ ///
+ /// This callback is triggered whenever the spline has changed.
+ /// It will then re-calculate .
+ ///
+ private void OnSplineChanged()
+ {
+ vertices = seeSpline.GenerateVertices();
+ }
+
+ ///
+ /// Calculates the coordinate of the position on the edge by interpolating between two
+ /// neighboring vertices.
+ ///
+ /// The vertices of the edge are derived from the integer places of ,
+ /// while the decimal places represent the progress between the two vertices.
+ ///
+ ///
+ /// The position on the edge between zero and
+ /// .Length - 1.
+ /// The coordinate of the position on the edge.
+ private Vector3 GetPositionOnEdge(float position)
+ {
+ if (position >= vertices.Length - 1)
+ {
+ return vertices[^1]; // last element
+ }
+
+ return Vector3.Lerp(vertices[(int)position], vertices[(int)position + 1], position - (int)position);
+ }
+
+ ///
+ /// Calculates the approximate length of the edge that is represented by .
+ ///
+ /// Approximate edge length.
+ private float GetApproxEdgeLength() => vertices.Pairwise(Vector3.Distance).Sum();
+ }
+ }
+}
diff --git a/Assets/SEE/Controls/Actions/DrawAction.cs.meta b/Assets/SEE/Game/Operator/EdgeDirectionVisualizer.cs.meta
similarity index 83%
rename from Assets/SEE/Controls/Actions/DrawAction.cs.meta
rename to Assets/SEE/Game/Operator/EdgeDirectionVisualizer.cs.meta
index 78ba3c5c9a..12a81a5756 100644
--- a/Assets/SEE/Controls/Actions/DrawAction.cs.meta
+++ b/Assets/SEE/Game/Operator/EdgeDirectionVisualizer.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 4a2f8a38aa579c94e8d7016b511a41a8
+guid: b7e1e625e7090fca797f6b19f170fd2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Assets/SEE/Game/Operator/EdgeOperator.cs b/Assets/SEE/Game/Operator/EdgeOperator.cs
index b281b81cee..1da8a02a23 100644
--- a/Assets/SEE/Game/Operator/EdgeOperator.cs
+++ b/Assets/SEE/Game/Operator/EdgeOperator.cs
@@ -143,6 +143,23 @@ public IOperationCallback ShowOrHide(bool show, EdgeAnimationKind animat
};
}
+ ///
+ /// Enables or disables the data flow animation to indicate edge direction.
+ ///
+ /// Enable or disable animation.
+ public void AnimateDataFlow(bool enable = true)
+ {
+ if (enable)
+ {
+ gameObject.AddOrGetComponent();
+ }
+ else
+ {
+ EdgeDirectionVisualizer edfv = gameObject.GetComponent();
+ Destroyer.Destroy(edfv);
+ }
+ }
+
#endregion
protected override IEnumerable AsEnumerable((Color start, Color end) color)
diff --git a/Assets/SEE/Game/Operator/LabelOperator.cs b/Assets/SEE/Game/Operator/LabelOperator.cs
index fbcc871baf..70483bfb46 100644
--- a/Assets/SEE/Game/Operator/LabelOperator.cs
+++ b/Assets/SEE/Game/Operator/LabelOperator.cs
@@ -1,6 +1,4 @@
-using System.Linq;
using DG.Tweening;
-using SEE.Controls.Actions;
using SEE.GO;
using SEE.Utils;
using TMPro;
@@ -48,6 +46,15 @@ public void UpdateLabelLayout(Vector3 labelBase)
labelEndLinePosition.AnimateTo(DesiredLabelEndLinePosition, duration);
}
+ ///
+ /// Returns true if the label is not empty.
+ ///
+ /// True if the label is not empty.
+ public bool LabelIsNotEmpty()
+ {
+ return !string.IsNullOrWhiteSpace(labelText?.text);
+ }
+
///
/// Creates and prepares the game object along with its components.
/// If already exists, nothing will happen.
diff --git a/Assets/SEE/Game/Operator/NodeOperator.cs b/Assets/SEE/Game/Operator/NodeOperator.cs
index 710e8f6fbc..5360a34a63 100644
--- a/Assets/SEE/Game/Operator/NodeOperator.cs
+++ b/Assets/SEE/Game/Operator/NodeOperator.cs
@@ -191,6 +191,76 @@ public IOperationCallback MoveTo(Vector3 newPosition, float factor = 1,
}, a => a);
}
+ ///
+ /// Moves and scales the node at the same time.
+ ///
+ /// If is true> (default), children are not scaled and moved along.
+ /// For this purpose they are reparented to their grandparent during the animation and back to the original
+ /// parent after the animation has completed.
+ ///
+ ///
+ /// the desired new local scale
+ /// the desired new target position in world space
+ /// Factor to apply to the
+ /// that controls the animation duration.
+ /// If set to 0, will execute directly, that is, the value is set before control is returned to the caller.
+ ///
+ /// if true, the children are not moved and scaled along with their parent
+ /// if true, the connecting edges will be moved along with the node
+ /// An operation callback for the requested animation
+ public IOperationCallback ResizeTo
+ (Vector3 newLocalScale,
+ Vector3 newPosition,
+ float factor = 1,
+ bool updateEdges = true,
+ bool reparentChildren = true)
+ {
+ float duration = ToDuration(factor);
+ updateLayoutDuration = duration;
+ this.updateEdges = updateEdges;
+
+ List children = null;
+ Transform originalParent = transform;
+ Transform tempParent = transform.parent;
+ if (reparentChildren)
+ {
+ children = new(transform.childCount);
+ foreach (Transform child in transform)
+ {
+ if (child.gameObject.IsNodeAndActiveSelf())
+ {
+ children.Add(child);
+ }
+ }
+ reparent(tempParent);
+ }
+
+ IOperationCallback animation = new AndCombinedOperationCallback
+ (new[]
+ {
+ positionX.AnimateTo(newPosition.x, duration),
+ positionY.AnimateTo(newPosition.y, duration),
+ positionZ.AnimateTo(newPosition.z, duration),
+ scale.AnimateTo(newLocalScale, duration)
+ },
+ a => a);
+ animation.OnComplete(() => reparent(originalParent));
+ animation.OnKill(() => reparent(originalParent));
+ return animation;
+
+ void reparent(Transform newParent)
+ {
+ if (!reparentChildren)
+ {
+ return;
+ }
+ foreach (Transform child in children)
+ {
+ child.SetParent(newParent);
+ }
+ }
+ }
+
///
/// Rotates the node to the given quaternion .
///
diff --git a/Assets/SEE/Game/ReflexionMapper.cs b/Assets/SEE/Game/ReflexionMapper.cs
index 37edccccaf..99b5378738 100644
--- a/Assets/SEE/Game/ReflexionMapper.cs
+++ b/Assets/SEE/Game/ReflexionMapper.cs
@@ -3,7 +3,6 @@
using SEE.GO;
using SEE.Tools.ReflexionAnalysis;
using System;
-using System.Collections.Generic;
using System.Linq;
using UnityEngine;
@@ -20,17 +19,28 @@ internal static class ReflexionMapper
/// Maps onto distinguishing
/// the following four cases regarding to which domains
/// and belong to:
- /// (1) implementation -> architecture: interpreted as an architecture mapping,
- /// i.e., is mapped onto
- /// in the architecture.
- /// (2) implementation -> implementation: interpreted as a restructuring in the implementation
- /// (3) architecture -> architecture: interpreted as a restructuring in the architecture
- /// (4) architecture -> implementation: makes no sense; will be ignored
- ///
- /// In cases (2)-(3), becomes a child graph node of
+ ///
+ /// - 1.
+ /// implementation -> architecture: interpreted as an architecture mapping,
+ /// i.e., is mapped onto
+ /// in the architecture.
+ ///
+ /// - 2.
+ /// implementation -> implementation: interpreted as a restructuring in the implementation
+ ///
+ /// - 3.
+ /// architecture -> architecture: interpreted as a restructuring in the architecture
+ ///
+ /// - 4.
+ /// architecture -> implementation: makes no sense; will be ignored
+ ///
+ ///
+ /// In cases 2–3, becomes a child graph node of
/// in the underlying graph.
- /// In cases (1)-(3), becomes a child game object of
+ ///
+ /// In cases 1–3, becomes a child game object of
/// . In all theses cases, the reflexion data is updated.
+ ///
///
/// the node to be mapped
/// the target which is mapped onto
@@ -50,65 +60,67 @@ internal static void SetParent(GameObject mappingSource, GameObject mappingTarge
}
// The mapping is only possible if mapping target is actually a node.
- if (mappingTarget.TryGetNode(out Node target))
+ if (!mappingTarget.TryGetNode(out Node target))
{
- // The source of the mapping
- Node source = mappingSource.GetNode();
+ return;
+ }
- if (source.ItsGraph != target.ItsGraph)
- {
- throw new Exception("For a mapping, both nodes must be in the same graph.");
- }
+ // The source of the mapping
+ Node source = mappingSource.GetNode();
- // implementation -> architecture
- if (source.IsInImplementation() && target.IsInArchitecture())
- {
- // If there is a previous mapping that already mapped the node
- // on the current target, nothing needs to be done.
- // If there is a previous mapping that mapped the node onto
- // another target, the previous mapping must be reverted and the
- // node must be mapped onto the new target.
+ if (source.ItsGraph != target.ItsGraph)
+ {
+ throw new Exception("For a mapping, both nodes must be in the same graph.");
+ }
- reflexionCity.ReflexionGraph.AddToMapping(source, target, overrideMapping: true);
- mappingSource.transform.SetParent(mappingTarget.transform);
- }
- // implementation -> implementation
- else if (source.IsInImplementation() && target.IsInImplementation())
+ // implementation -> architecture
+ if (source.IsInImplementation() && target.IsInArchitecture())
+ {
+ // If there is a previous mapping that already mapped the node
+ // on the current target, nothing needs to be done.
+ // If there is a previous mapping that mapped the node onto
+ // another target, the previous mapping must be reverted and the
+ // node must be mapped onto the new target.
+
+ reflexionCity.ReflexionGraph.AddToMapping(source, target, overrideMapping: true);
+ mappingSource.transform.SetParent(mappingTarget.transform);
+ }
+ // implementation -> implementation
+ else if (source.IsInImplementation() && target.IsInImplementation())
+ {
+ if (reflexionCity.ReflexionGraph.IsExplicitlyMapped(source))
{
- if (reflexionCity.ReflexionGraph.IsExplicitlyMapped(source))
- {
- reflexionCity.ReflexionGraph.RemoveFromMapping(source);
- }
- // TODO (falko17): This branch and the next branch can be merged as soon
- // as the general Unparent and AddChild methods are implemented.
- // This changes the node hierarchy in the implementation only.
- if (source.Parent != null)
- {
- // If `AddChildInImplementation` fails, the source will be left without a parent, hence the if.
- // TODO: Implement a proper transaction model for the reflexion analysis.
- reflexionCity.ReflexionGraph.UnparentInImplementation(source);
- }
- reflexionCity.ReflexionGraph.AddChildInImplementation(source, target);
- mappingSource.transform.SetParent(mappingTarget.transform);
+ reflexionCity.ReflexionGraph.RemoveFromMapping(source);
}
- // architecture -> architecture
- else if (source.IsInArchitecture() && target.IsInArchitecture())
+ // TODO (#785): This branch and the next branch can be merged as soon
+ // as the general Unparent and AddChild methods are implemented.
+ // This changes the node hierarchy in the implementation only.
+ if (source.Parent != null)
{
- // TODO (falko17): This branch and the previous branch can be merged as soon
- // as the general Unparent and AddChild methods are implemented.
- // This changes the node hierarchy in the architecture only.
- if (source.Parent != null)
- {
- reflexionCity.ReflexionGraph.UnparentInArchitecture(source);
- }
- reflexionCity.ReflexionGraph.AddChildInArchitecture(source, target);
- mappingSource.transform.SetParent(mappingTarget.transform);
+ // If `AddChildInImplementation` fails, the source will be left without a parent, hence the if.
+ // TODO (#785): Implement a proper transaction model for the reflexion analysis.
+ reflexionCity.ReflexionGraph.UnparentInImplementation(source);
}
- // architecture -> implementation: forbidden
- else
+ reflexionCity.ReflexionGraph.AddChildInImplementation(source, target);
+ mappingSource.transform.SetParent(mappingTarget.transform);
+ }
+ // architecture -> architecture
+ else if (source.IsInArchitecture() && target.IsInArchitecture())
+ {
+ // TODO (#785): This branch and the previous branch can be merged as soon
+ // as the general Unparent and AddChild methods are implemented.
+ // This changes the node hierarchy in the architecture only.
+ if (source.Parent != null)
{
- // Nothing to be done.
+ reflexionCity.ReflexionGraph.UnparentInArchitecture(source);
}
+ reflexionCity.ReflexionGraph.AddChildInArchitecture(source, target);
+ mappingSource.transform.SetParent(mappingTarget.transform);
+ }
+ // architecture -> implementation: forbidden
+ else
+ {
+ // Nothing to be done.
}
}
diff --git a/Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs b/Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs
index 5f7aa824c7..e351b893a1 100644
--- a/Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs
+++ b/Assets/SEE/Game/SceneManipulation/GameElementDeleter.cs
@@ -123,9 +123,9 @@ private static (GraphElementsMemento, ISet) DeleteEdge(GameObject ga
/// contained in this tree are the transitive descendants of .
/// The edges in this tree are those whose source or target node
/// is contained in the tree.
- ///
+ ///
/// Note: The result consists of both nodes and edges.
- ///
+ ///
/// Precondition: is a game node.
///
/// the root of the tree to be deleted
diff --git a/Assets/SEE/Game/SceneManipulation/GameNodeEditor.cs b/Assets/SEE/Game/SceneManipulation/GameNodeEditor.cs
new file mode 100644
index 0000000000..d52d1b8d8a
--- /dev/null
+++ b/Assets/SEE/Game/SceneManipulation/GameNodeEditor.cs
@@ -0,0 +1,97 @@
+using SEE.DataModel.DG;
+using SEE.Game.City;
+using SEE.Game.CityRendering;
+using SEE.GO;
+using SEE.Utils;
+using TMPro;
+using UnityEngine;
+
+namespace SEE.Game.SceneManipulation
+{
+ ///
+ /// Provides methods to edit a node, that is, to change its name or type.
+ ///
+ public class GameNodeEditor
+ {
+ ///
+ /// Changes the name of a node.
+ ///
+ /// The node which name should be changed.
+ /// The new name.
+ public static void ChangeName(Node node, string newName)
+ {
+ node.SourceName = newName.Trim();
+ if (node.GameObject() != null)
+ {
+ ShouldAddTextObject(node);
+ Transform transform = node.GameObject().transform;
+ foreach (Transform t in transform)
+ {
+ if (t.name.Contains("Text") || t.name.Contains("Label"))
+ {
+ t.GetComponent().text = newName.Trim();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Changes the type of a node.
+ ///
+ /// The node which type should be changed.
+ /// The new type.
+ public static void ChangeType(Node node, string type)
+ {
+ node.Type = type;
+ GameObject nodeObject = node.GameObject(true);
+ AbstractSEECity city = nodeObject.ContainingCity();
+
+ ShouldAddTextObject(node);
+ GetText(nodeObject)?.SetActive(city.NodeTypes[node.Type].ShowNames);
+
+ if (city.Renderer is GraphRenderer renderer)
+ {
+ renderer.AdjustStyle(nodeObject);
+ if (GetText(nodeObject) != null)
+ {
+ GetText(nodeObject).GetComponent().color = nodeObject.GetColor().Invert();
+ }
+ }
+ }
+
+ ///
+ /// Returns the text object of . This is the first child of
+ /// that has a component and whose
+ /// name contains 'Text'.
+ ///
+ /// the game object whose text object is requested
+ /// The text object or null if there is no such object.
+ private static GameObject GetText(GameObject go)
+ {
+ foreach (Transform transform in go.transform)
+ {
+ if (transform.name.Contains("Text") && transform.GetComponent() != null)
+ {
+ return transform.gameObject;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Checks if the decorations of the node needs to be added
+ /// and adds them if necessary.
+ ///
+ /// The node to be checked.
+ private static void ShouldAddTextObject(Node node)
+ {
+ if (node.GameObject().ContainingCity().NodeTypes[node.Type].ShowNames
+ && node.GameObject().ContainingCity().Renderer is GraphRenderer renderer
+ && GetText(node.GameObject()) == null
+ && !string.IsNullOrWhiteSpace(node.SourceName))
+ {
+ renderer.AddDecorations(node.GameObject());
+ }
+ }
+ }
+}
diff --git a/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs.meta b/Assets/SEE/Game/SceneManipulation/GameNodeEditor.cs.meta
similarity index 83%
rename from Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs.meta
rename to Assets/SEE/Game/SceneManipulation/GameNodeEditor.cs.meta
index 4fb6792e51..e6faf36256 100644
--- a/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs.meta
+++ b/Assets/SEE/Game/SceneManipulation/GameNodeEditor.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 535d025d894bb894cbfdde3140259c45
+guid: d3fc378885545bc4db11e44a170b6ac4
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Assets/SEE/Game/SceneManipulation/GameNodeMover.cs b/Assets/SEE/Game/SceneManipulation/GameNodeMover.cs
index 362c2ca9ee..fbec5415d4 100644
--- a/Assets/SEE/Game/SceneManipulation/GameNodeMover.cs
+++ b/Assets/SEE/Game/SceneManipulation/GameNodeMover.cs
@@ -1,7 +1,5 @@
-using SEE.Game.Operator;
-using SEE.GO;
+using SEE.GO;
using UnityEngine;
-using UnityEngine.Assertions;
namespace SEE.Game.SceneManipulation
{
@@ -10,11 +8,6 @@ namespace SEE.Game.SceneManipulation
///
public static class GameNodeMover
{
- ///
- /// Factor by which nodes should be scaled relative to their parents in .
- ///
- public const float ScalingFactor = 0.2f;
-
///
/// Sets the for both in the
/// game-object hierarchy and in the underlying graph. If
@@ -49,173 +42,55 @@ public static void SetParent(GameObject child, GameObject newParent)
}
///
- /// Puts on top of the roof of ,
- /// and scales it down,
- /// assuming is true. This method makes sure that
- /// will be contained within the area of the roof of .
- ///
- /// The will be an immediate child of in the
- /// game-object hierarchy afterwards.
- ///
- /// Precondition: is not null.
+ /// Returns the new world coordinates based on so that the child
+ /// node with a size of would appear on top of
+ /// if moved there.
///
- /// child to be put on
- /// parent the is put on
- /// Whether should be scaled down to fit into
- ///
- /// Additional amount of empty space that should be between
- /// and in absolute world-space terms
+ ///
+ /// Keep in mind that might be hanging over if it is too big.
+ ///
+ /// the world-space scale of a node
+ /// the world position of a node
+ /// the target node's GameObject
+ /// additional amount of empty space on the Y-axis that should be between
+ /// and in absolute world-space units
+ /// the new world position after the correction
///
- public static void PutOn(Transform child, GameObject parent,
- bool scaleDown = false, float topPadding = 0.0001f)
+ public static Vector3 GetCoordinatesOn(Vector3 childWorldScale, Vector3 childPosition, GameObject target, float topPadding = 0.0001f)
{
- // This assignment must take place before we set the parent of child to null
- // because a newly created node operator attempts to derive its code city.
- NodeOperator nodeOperator = child.gameObject.NodeOperator();
-
- // Release child from its current parent so that local position and scale
- // and world-space position and scale are the same, respectively.
- // The child will receive its new parent at the very end of this method.
- child.SetParent(null);
-
- if (scaleDown)
- {
- // ScaleTo with animation factor = 0 has immediate effect.
- nodeOperator.ScaleTo(ShrinkedWorldSpaceScale(child, parent), 0);
- }
-
- // Where to move the child.
- Vector3 targetWorldPosition = child.position;
- Vector3 childWorldExtent = child.lossyScale / 2;
- targetWorldPosition.y = parent.GetRoof() + childWorldExtent.y + topPadding;
+ Vector3 childWorldExtent = childWorldScale / 2;
+ childPosition.y = target.GetRoof() + childWorldExtent.y + topPadding;
// Make sure mappingTarget stays within the roof of parent.
{
- Vector3 parentWorldExtent = parent.transform.lossyScale / 2;
+ Vector3 parentWorldExtent = target.WorldSpaceSize() / 2;
// Fit child into x range of parent.
- if (targetWorldPosition.x + childWorldExtent.x > parent.transform.position.x + parentWorldExtent.x)
+ if (childPosition.x + childWorldExtent.x > target.transform.position.x + parentWorldExtent.x)
{
// Right corner of child must not be farther than right corner of parent.
- targetWorldPosition.x = parent.transform.position.x + parentWorldExtent.x - childWorldExtent.x;
+ childPosition.x = target.transform.position.x + parentWorldExtent.x - childWorldExtent.x;
}
- else if (targetWorldPosition.x - childWorldExtent.x < parent.transform.position.x - parentWorldExtent.x)
+ else if (childPosition.x - childWorldExtent.x < target.transform.position.x - parentWorldExtent.x)
{
// Left corner of child must be right from right corner of parent.
- targetWorldPosition.x = parent.transform.position.x - parentWorldExtent.x + childWorldExtent.x;
+ childPosition.x = target.transform.position.x - parentWorldExtent.x + childWorldExtent.x;
}
// Fit child into z range of parent.
- if (targetWorldPosition.z + childWorldExtent.z > parent.transform.position.z + parentWorldExtent.z)
+ if (childPosition.z + childWorldExtent.z > target.transform.position.z + parentWorldExtent.z)
{
// Front edge of child must not be farther than back edge of parent.
- targetWorldPosition.z = parent.transform.position.z + parentWorldExtent.z - childWorldExtent.z;
+ childPosition.z = target.transform.position.z + parentWorldExtent.z - childWorldExtent.z;
}
- else if (targetWorldPosition.z - childWorldExtent.z < parent.transform.position.z - parentWorldExtent.z)
+ else if (childPosition.z - childWorldExtent.z < target.transform.position.z - parentWorldExtent.z)
{
// Front edge of child must not be before front edge of parent.
- targetWorldPosition.z = parent.transform.position.z - parentWorldExtent.z + childWorldExtent.z;
- }
- }
-
- nodeOperator.MoveTo(targetWorldPosition, 0);
- child.SetParent(parent.transform);
-
- // Returns the target world space scale of child relative to parent when child is to be put onto parent.
- static Vector3 ShrinkedWorldSpaceScale(Transform child, GameObject parent)
- {
- // TODO: We need a strategy to scale down a node to the maximal size that is still
- // fitting into the area where the nodes has been placed.
- // We want to shrink only the ground area, but maintain the height.
- return new Vector3(ScalingFactor * parent.transform.lossyScale.x, child.lossyScale.y, ScalingFactor * parent.transform.lossyScale.z);
- }
- }
-
- ///
- /// FIXME: This is intended to become an improved version of PutOn().
- /// It is still work in progress.
- ///
- private static void PutOn2(Transform child, GameObject parent, bool scaleDown = false, float topPadding = 0.0001f)
- {
- Assert.IsNotNull(parent);
- // child's parent is set to null so that we do not need to make a distinction
- // between localScale and lossyScale. The child will receive its requested
- // parent at the end of the method.
- child.SetParent(null);
-
- Vector3 childExtent = child.lossyScale / 2;
- Vector3 parentExtent = parent.transform.lossyScale / 2;
- float parentLeftX = parent.transform.position.x - parentExtent.x;
- float parentRightX = parent.transform.position.x + parentExtent.x;
- float parentFrontZ = parent.transform.position.z - parentExtent.z;
- float parentBackZ = parent.transform.position.z + parentExtent.z;
-
- // First, we will position child onto the roof of parent (and so that
- // child's center is contained in the roof of parent if necessary).
-
- // The target position of child in word space.
- Vector3 targetPosition;
-
- // Is the center of child enclosed in the roof rectangle of parent?
- if (!(parentLeftX <= child.transform.position.x && child.transform.position.x <= parentRightX
- && parentFrontZ <= child.transform.position.z && child.transform.position.z <= parentBackZ))
- {
- // TODO: We need to develop a better strategy.
- // child will be put at the center of parent.
- targetPosition = parent.transform.position;
- }
- else
- {
- targetPosition = child.position;
- }
- targetPosition.y = parent.GetRoof() + childExtent.y + topPadding;
- NodeOperator nodeOperator = child.gameObject.NodeOperator();
- nodeOperator.MoveTo(targetPosition, 0);
-
- // From now on, we assume that child's center is contained in the
- // roof rectangle of parent. It will stay at its current location
- // and only be shrinked so that it fits into parent.
-
- // Now, we will shrink child so that it is totally enclosed in parent.
- if (scaleDown)
- {
- {
- // Shrink if back edge of child is farther back than back edge of parent.
- float factor = (parentBackZ - child.position.z) / childExtent.z;
- if (factor < 1)
- {
- childExtent *= factor;
- }
- }
- {
- // Shrink if front edge of child is in front of front edge of parent.
- float factor = (child.position.z - parentFrontZ) / childExtent.z;
- if (factor < 1)
- {
- childExtent *= factor;
- }
+ childPosition.z = target.transform.position.z - parentWorldExtent.z + childWorldExtent.z;
}
- {
- // Shrink if left edge of child is farther left than left edge of parent.
- float factor = (child.position.x - parentLeftX) / childExtent.x;
- if (factor < 1)
- {
- childExtent *= factor;
- }
- }
- {
- // Shrink if right edge of child is farther right than right edge of parent.
- float factor = (parentRightX - child.position.x) / childExtent.x;
- if (factor < 1)
- {
- childExtent *= factor;
- }
- }
- // ScaleTo with animation factor = 0 has immediate effect.
- nodeOperator.ScaleTo(2 * childExtent, 0);
}
- child.SetParent(parent.transform);
+ return childPosition;
}
///
@@ -233,38 +108,6 @@ public static void NewMovementVersion(GameObject movedObject)
}
}
- ///
- /// Puts on visually. If
- /// is different from , the will be scaled
- /// down so that it fits into . If instead
- /// and are the same, will be scaled back
- /// to its .
- ///
- /// The will be an immediate child of in the
- /// game-object hierarchy afterwards.
- ///
- /// Calling this method is equivalent to
- /// with a previous scaling of to if
- /// equals .
- /// game object to be put onto
- /// where to put
- /// the of
- /// original local scale of relative to
- /// ; used to restore this scale if
- /// and are the same
- public static void PutOnAndFit(Transform child, GameObject newParent,
- GameObject originalParent, Vector3 originalLocalScale)
- {
- bool scaleDown = newParent != originalParent;
- if (!scaleDown)
- {
- // The gameObject may have already been scaled down, hence,
- // we need to restore its original scale.
- child.gameObject.NodeOperator().ScaleTo(originalLocalScale, 0);
- }
- PutOn(child, newParent, scaleDown: scaleDown);
- }
-
///
/// Moves (assumed to represent a node) to
/// through some animation. All existing animations are cancelled.
diff --git a/Assets/SEE/GameObjects/GameObjectExtensions.cs b/Assets/SEE/GameObjects/GameObjectExtensions.cs
index 1d732ac882..c0020ed0c9 100644
--- a/Assets/SEE/GameObjects/GameObjectExtensions.cs
+++ b/Assets/SEE/GameObjects/GameObjectExtensions.cs
@@ -345,7 +345,7 @@ public static void SetAbsoluteScale(this GameObject gameObject, Vector3 worldSca
/// world-space y position of the roof of this
public static float GetRoof(this GameObject gameObject)
{
- return gameObject.transform.position.y + gameObject.WorldSpaceScale().y / 2.0f;
+ return gameObject.transform.position.y + gameObject.WorldSpaceSize().y / 2.0f;
}
///
@@ -356,7 +356,7 @@ public static float GetRoof(this GameObject gameObject)
public static Vector3 GetRoofCenter(this GameObject gameObject)
{
Vector3 result = gameObject.transform.position;
- result.y += gameObject.WorldSpaceScale().y / 2.0f;
+ result.y += gameObject.WorldSpaceSize().y / 2.0f;
return result;
}
@@ -368,7 +368,7 @@ public static Vector3 GetRoofCenter(this GameObject gameObject)
public static Vector3 GetGroundCenter(this GameObject gameObject)
{
Vector3 result = gameObject.transform.position;
- result.y -= gameObject.WorldSpaceScale().y / 2.0f;
+ result.y -= gameObject.WorldSpaceSize().y / 2.0f;
return result;
}
@@ -435,13 +435,25 @@ public static Vector3 GetTop(this GameObject gameObject, Func f
///
/// Returns the size of the given in world space.
+ ///
+ /// This is a shorthand to get the bounds.size of the component, if present.
+ /// This value reflects the actual world-space bounds of the cuboid that contains the rendered object.
+ ///
+ /// This value should often be used instead of the transform.lossyScale because the scale only reflects
+ /// the size for objects with a standardized size like cube primitives.
+ ///
+ /// Local-space counterpart:
+ ///
///
- /// object whose size is requested
+ ///
+ /// If the game object has no renderer, its lossyScale is returned.
+ ///
+ /// object whose scale is requested
/// size of given
- public static Vector3 WorldSpaceScale(this GameObject gameObject)
+ public static Vector3 WorldSpaceSize(this GameObject gameObject)
{
// For some objects, such as capsules, lossyScale gives wrong results.
- // The more reliable option to determine the size is using the
+ // The more reliable option to determine the scale is using the
// object's renderer if it has one.
if (gameObject.TryGetComponent(out Renderer renderer))
{
@@ -454,6 +466,37 @@ public static Vector3 WorldSpaceScale(this GameObject gameObject)
}
}
+ ///
+ /// Returns the size of the given in local space,
+ /// i.e., in relation to its parent.
+ ///
+ /// This value should often be used instead of the transform.localScale because the scale only reflects
+ /// the size for objects with a standardized size like cube primitives.
+ ///
+ /// World-space counterpart:
+ ///
+ ///
+ ///
+ /// If the game object has no renderer, its localScale is returned.
+ ///
+ /// object whose scale is requested
+ /// size of given
+ public static Vector3 LocalSize(this GameObject gameObject)
+ {
+ // For some objects, such as capsules, localScale gives wrong results.
+ // The more reliable option to determine the scale is using the
+ // object's renderer if it has one.
+ if (gameObject.TryGetComponent(out Renderer renderer))
+ {
+ return Vector3.Scale(renderer.localBounds.size, gameObject.transform.localScale);
+ }
+ else
+ {
+ // No renderer, so we use localScale as a fallback.
+ return gameObject.transform.localScale;
+ }
+ }
+
///
/// Returns true if this is within the spatial area of ,
/// that is, if the bounding box of plus the extra padding
@@ -588,8 +631,29 @@ public static bool IsNode(this GameObject gameObject)
}
///
- /// Returns true if has a
- /// component attached to it.
+ /// Returns true if 's
+ /// is true and it is tagged by .
+ ///
+ /// the game object to check
+ /// true if is an active node
+ public static bool IsNodeAndActiveSelf(this GameObject gameObject)
+ {
+ return gameObject.activeSelf && gameObject.CompareTag(Tags.Node);
+ }
+
+ ///
+ /// Returns true if 's
+ /// is true and it is tagged by .
+ ///
+ /// the game object to check
+ /// true if is an active node
+ public static bool IsNodeAndActiveInHierarchy(this GameObject gameObject)
+ {
+ return gameObject.CompareTag(Tags.Node) && gameObject.activeInHierarchy;
+ }
+
+ ///
+ /// Retrieves the node reference component, if possible.
///
/// the game object whose NodeRef is checked
/// the attached NodeRef; defined only if this method
@@ -956,5 +1020,48 @@ public static GraphElementOperator Operator(this GameObject gameObject)
}
}
}
+
+ ///
+ /// Checks if overlaps with any other direct child node of its parent.
+ ///
+ /// Overlap is checked based on the Collider components. Objects with no Collider
+ /// component are ignored.
+ ///
+ ///
+ ///
+ /// The must be a node, i.e., coantain a NodeRef component.
+ ///
+ /// The game object whose operator to retrieve.
+ /// false if does not have a Collider component,
+ /// or does not overlap with its siblings
+ ///
+ /// Thrown when the object the method is called on is not a node, i.e., has no NodeRef
+ /// component.
+ ///
+ public static bool OverlapsWithSiblings(this GameObject gameObject)
+ {
+ if (!gameObject.HasNodeRef())
+ {
+ throw new InvalidOperationException("GameObject must be a node!");
+ }
+ if (!gameObject.TryGetComponent(out Collider collider))
+ {
+ return false;
+ }
+ foreach (Transform sibling in gameObject.transform.parent)
+ {
+ if (sibling.gameObject == gameObject || !sibling.gameObject.HasNodeRef() || !sibling.gameObject.TryGetComponent(out Collider siblingCollider))
+ {
+ continue;
+ }
+
+ if (collider.bounds.Intersects(siblingCollider.bounds))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/Assets/SEE/GameObjects/SEESpline.cs b/Assets/SEE/GameObjects/SEESpline.cs
index 55095d671b..b4c5952ee6 100644
--- a/Assets/SEE/GameObjects/SEESpline.cs
+++ b/Assets/SEE/GameObjects/SEESpline.cs
@@ -22,8 +22,8 @@ namespace SEE.GO
/// ), the internal state is marked dirty and the
/// rendering is updated in the next frame (via ).
/// There are two rendering methods:
- ///
- /// 1. : The spline is rendered as polyline.
+ /// -
+ /// : The spline is rendered as polyline.
/// This method is comparatively fast, but lacks more advanced features
/// such as collision detection. It serves as a placeholder until the
/// runtime environment found the time to create a
@@ -31,15 +31,15 @@ namespace SEE.GO
/// etc.). This class doesn't create
/// instances on its own, but rather updates
/// them if they are present.
- ///
- /// 2. : The spline is rendered as tubular mesh. This
+ ///
-
+ /// : The spline is rendered as tubular mesh. This
/// method is a lot slower than , but in
/// contrast creates "real" 3D objects with collision detection. Because
/// the creation of a larger amount of meshes is quite slow, it is up to
/// an external client to replace any with a
/// . For this purpose there is the method
/// .
- ///
+ ///
/// The geometric characteristics of the generated mesh, e.g., the radius
/// of the tube, can be set via properties. By setting a property, the
/// rendering of the spline is updated in the next frame. If an update
@@ -84,6 +84,11 @@ public class SEESpline : SerializedMonoBehaviour
[SerializeField]
private float subsplineEndT = 1.0f;
+ ///
+ /// The event is emitted each time the renderer is updated (see ).
+ ///
+ public event Action OnRendererChanged;
+
///
/// Property of .
///
@@ -303,6 +308,7 @@ private void Update()
UpdateLineRenderer();
UpdateMesh();
needsUpdate = needsColorUpdate = false;
+ OnRendererChanged?.Invoke();
}
else if (needsColorUpdate)
{
@@ -348,8 +354,7 @@ private void UpdateLineRenderer()
{
if (gameObject.TryGetComponent(out LineRenderer lr))
{
- BSpline subSpline = CreateSubSpline();
- Vector3[] polyLine = TinySplineInterop.ListToVectors(subSpline.Sample());
+ Vector3[] polyLine = GenerateVertices();
lr.positionCount = polyLine.Length;
lr.SetPositions(polyLine);
@@ -359,6 +364,16 @@ private void UpdateLineRenderer()
needsUpdate = false;
}
+ ///
+ /// Generates the vertices that represent this spline.
+ ///
+ /// The vertices that make up this spline.
+ public Vector3[] GenerateVertices()
+ {
+ BSpline subSpline = CreateSubSpline();
+ return TinySplineInterop.ListToVectors(subSpline.Sample());
+ }
+
///
/// Updates the start and end color of the line renderer attached
/// to the gameObject using the values of
diff --git a/Assets/SEE/GraphProviders/AllGitBranchesSingleGraphProvider.cs b/Assets/SEE/GraphProviders/AllGitBranchesSingleGraphProvider.cs
index 5c4bb57ba4..7b4a5e036e 100644
--- a/Assets/SEE/GraphProviders/AllGitBranchesSingleGraphProvider.cs
+++ b/Assets/SEE/GraphProviders/AllGitBranchesSingleGraphProvider.cs
@@ -52,7 +52,7 @@ public class AllGitBranchesSingleGraphProvider : SingleGraphProvider
Tooltip("Path globbings and whether they are inclusive (true) or exclusive (false)."),
RuntimeTab(GraphProviderFoldoutGroup),
HideReferenceObjectPicker]
- public Dictionary PathGlobbing = new Dictionary()
+ public Dictionary PathGlobbing = new()
{
{ "**/*", true }
};
@@ -79,7 +79,7 @@ public class AllGitBranchesSingleGraphProvider : SingleGraphProvider
///
/// The interval in seconds in which git fetch should be called.
///
- [Tooltip("The interval in seconds in which the repositor should be polled. Used only if Auto Fetch is true."),
+ [Tooltip("The interval in seconds in which the repository should be polled. Used only if Auto Fetch is true."),
EnableIf(nameof(AutoFetch)), Range(5, 200)]
public int PollingInterval = 5;
diff --git a/Assets/SEE/Net/Actions/DeleteNetAction.cs b/Assets/SEE/Net/Actions/DeleteNetAction.cs
index e27945320f..c332e89131 100644
--- a/Assets/SEE/Net/Actions/DeleteNetAction.cs
+++ b/Assets/SEE/Net/Actions/DeleteNetAction.cs
@@ -42,7 +42,9 @@ public override void ExecuteOnServer()
public override void ExecuteOnClient()
{
GameObject gameObject = GraphElementIDMap.Find(GameObjectID, mustFindElement: true);
+#pragma warning disable VSTHRD110
GameElementDeleter.Delete(gameObject);
+#pragma warning restore VSTHRD110
}
}
}
diff --git a/Assets/SEE/Net/Actions/EditNodeNetAction.cs b/Assets/SEE/Net/Actions/EditNodeNetAction.cs
index b399ad49a1..38f2eef930 100644
--- a/Assets/SEE/Net/Actions/EditNodeNetAction.cs
+++ b/Assets/SEE/Net/Actions/EditNodeNetAction.cs
@@ -1,5 +1,6 @@
using SEE.DataModel.DG;
using SEE.Game;
+using SEE.Game.SceneManipulation;
using SEE.GO;
using UnityEngine;
@@ -54,8 +55,8 @@ public override void ExecuteOnServer()
public override void ExecuteOnClient()
{
Node node = Find(NodeID).GetNode();
- node.SourceName = SourceName;
- node.Type = NodeType;
+ GameNodeEditor.ChangeName(node, SourceName);
+ GameNodeEditor.ChangeType(node, NodeType);
}
}
}
diff --git a/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs b/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs
deleted file mode 100644
index c33070d426..0000000000
--- a/Assets/SEE/Net/Actions/PutOnAndFitNetAction.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using SEE.Game;
-using SEE.Game.SceneManipulation;
-using UnityEngine;
-
-namespace SEE.Net.Actions
-{
- ///
- /// Propagates through the network.
- ///
- internal class PutOnAndFitNetAction : AbstractNetAction
- {
- ///
- /// The unique name of the child gameObject that needs to be put onto a new parent.
- /// Must be known to .
- ///
- public string ChildID;
-
- ///
- /// The unique name of the gameObject that becomes the new parent of the child.
- /// Must be known to .
- ///
- public string NewParentID;
-
- ///
- /// The unique name of the gameObject that was the original parent of the child.
- /// Must be known to .
- ///
- public string OriginalParentID;
-
- ///
- /// The original scale of the child relative to its original parent (local scale).
- ///
- public Vector3 OriginalLocalScale;
-
- ///
- /// Constructor.
- ///
- /// the unique game-object name of the game object of the child to
- /// be put and fit onto the ;
- /// must be known to
- /// the unique game-object name of the game object becoming the
- /// new parent of ;
- /// must be known to
- /// the unique name of the gameObject that was the original
- /// parent of the child;
- /// must be known to .
- /// the original local scale of
- /// relative to
- public PutOnAndFitNetAction(string childID, string newParentID, string originalParentID, Vector3 originalLocalScale)
- {
- ChildID = childID;
- NewParentID = newParentID;
- OriginalParentID = originalParentID;
- OriginalLocalScale = originalLocalScale;
- }
-
- ///
- /// Movement in all clients except the requesting client.
- ///
- public override void ExecuteOnClient()
- {
- GameObject child = Find(ChildID);
- GameObject newParent = Find(NewParentID);
- GameObject originalParent = Find(OriginalParentID);
- GameNodeMover.PutOnAndFit(child.transform, newParent, originalParent, OriginalLocalScale);
- }
-
- ///
- /// Does not do anything.
- ///
- public override void ExecuteOnServer()
- {
- // Intentionally left blank.
- }
- }
-}
diff --git a/Assets/SEE/Net/Actions/ResizeNodeNetAction.cs b/Assets/SEE/Net/Actions/ResizeNodeNetAction.cs
new file mode 100644
index 0000000000..375bffa331
--- /dev/null
+++ b/Assets/SEE/Net/Actions/ResizeNodeNetAction.cs
@@ -0,0 +1,72 @@
+using SEE.GO;
+using UnityEngine;
+
+namespace SEE.Net.Actions
+{
+ ///
+ /// Propagates changed size and position of a single game node through the network.
+ ///
+ /// Children are not scaled or moved along with the resized node.
+ ///
+ ///
+ public class ResizeNodeNetAction : AbstractNetAction
+ {
+ ///
+ /// The id of the gameObject that has to be resized.
+ ///
+ public string GameObjectID;
+
+ ///
+ /// The new local scale to transfer over the network.
+ ///
+ public Vector3 LocalScale;
+
+ ///
+ /// The new world-space position to transfer over the network.
+ ///
+ public Vector3 Position;
+
+ ///
+ /// Should the edges be updated along with the targe node?
+ ///
+ public bool UpdateEdges;
+
+ ///
+ /// Should the child nodes keep their size?
+ ///
+ public bool ReparentChildren;
+
+ ///
+ /// Constructs a .
+ ///
+ /// The unique name of the that should be resized
+ /// The new local scale of the
+ /// The new world-space position of the
+ /// if true, the children are not moved and scaled along with their parent
+ /// if true, the connecting edges will be moved along with the node
+ public ResizeNodeNetAction(string gameObjectID, Vector3 localScale, Vector3 position, bool updateEdges = true, bool reparentChildren = true)
+ {
+ GameObjectID = gameObjectID;
+ LocalScale = localScale;
+ Position = position;
+ UpdateEdges = updateEdges;
+ ReparentChildren = reparentChildren;
+ }
+
+ ///
+ /// Finds the GameObject on the client and sets its scale.
+ ///
+ public override void ExecuteOnClient()
+ {
+ GameObject go = Find(GameObjectID);
+ if (go != null)
+ {
+ go.NodeOperator().ResizeTo(LocalScale, Position, factor: 1, UpdateEdges, ReparentChildren);
+ }
+ else
+ {
+ throw new System.Exception($"There is no game object with the ID {GameObjectID}.");
+ }
+ }
+ }
+}
diff --git a/Assets/SEE/Net/Actions/ResizeNodeNetAction.cs.meta b/Assets/SEE/Net/Actions/ResizeNodeNetAction.cs.meta
new file mode 100644
index 0000000000..2cabc4ff00
--- /dev/null
+++ b/Assets/SEE/Net/Actions/ResizeNodeNetAction.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 202a53012045be647b8ce6642276bee3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SEE/Net/Actions/ZoomNetAction.cs b/Assets/SEE/Net/Actions/ZoomNetAction.cs
deleted file mode 100644
index c3180dbc86..0000000000
--- a/Assets/SEE/Net/Actions/ZoomNetAction.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using SEE.Controls.Actions;
-using SEE.Game;
-using SEE.Game.Operator;
-using SEE.GO;
-using SEE.Utils;
-using UnityEngine;
-
-namespace SEE.Net.Actions
-{
- ///
- /// Propagates the zooming of a game node through the network.
- ///
- internal class ZoomNetAction : AbstractNetAction
- {
- ///
- /// The unique name of the gameObject of a node that needs to be moved.
- ///
- public string GameObjectID;
-
- ///
- /// Where the game object should be placed in world space.
- ///
- public Vector3 Position;
-
- ///
- /// The local scale of the game object after the zooming.
- ///
- public Vector3 LocalScale;
-
- ///
- /// Constructor.
- ///
- /// the unique game-object name of the game object to be moved
- /// the new position of the game object
- /// the new local scale of the game object
- public ZoomNetAction(string gameObjectID, Vector3 position, Vector3 localScale)
- {
- GameObjectID = gameObjectID;
- Position = position;
- LocalScale = localScale;
- }
-
- ///
- /// Zooming in all clients except the requesting client.
- ///
- public override void ExecuteOnClient()
- {
- GameObject gameObject = GraphElementIDMap.Find(GameObjectID);
- if (gameObject != null)
- {
- NodeOperator nodeOperator = gameObject.NodeOperator();
- nodeOperator.MoveTo(Position, ZoomAction.AnimationFactor);
- nodeOperator.ScaleTo(LocalScale, ZoomAction.AnimationFactor);
- }
- else
- {
- throw new System.Exception($"There is no game object with the ID {GameObjectID}.");
- }
- }
-
- ///
- /// Does not do anything.
- ///
- public override void ExecuteOnServer()
- {
- // Intentionally left blank.
- }
- }
-}
diff --git a/Assets/SEE/SEE.asmdef b/Assets/SEE/SEE.asmdef
index 9eb9289ac1..63d900fb59 100644
--- a/Assets/SEE/SEE.asmdef
+++ b/Assets/SEE/SEE.asmdef
@@ -69,7 +69,8 @@
"System.Collections.Immutable.dll",
"MoreLinq.dll",
"Microsoft.Extensions.FileSystemGlobbing.dll",
- "MediatR.dll"
+ "MediatR.dll",
+ "MediatR.Contracts.dll"
],
"autoReferenced": false,
"defineConstraints": [],
diff --git a/Assets/SEE/UI/Drawable/DrawableActionBar.cs b/Assets/SEE/UI/Drawable/DrawableActionBar.cs
index e87ccde24c..986a5faada 100644
--- a/Assets/SEE/UI/Drawable/DrawableActionBar.cs
+++ b/Assets/SEE/UI/Drawable/DrawableActionBar.cs
@@ -65,7 +65,8 @@ void Start()
///
void Update()
{
- if (GlobalActionHistory.Current().Parent == ActionStateTypes.Drawable)
+ if (GlobalActionHistory.Current() != null
+ && GlobalActionHistory.Current().Parent == ActionStateTypes.Drawable)
{
barInstance.SetActive(true);
togglerInstance.SetActive(false);
diff --git a/Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs b/Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs
index 267398be73..c792236477 100644
--- a/Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs
+++ b/Assets/SEE/UI/HelpSystem/HelpSystemEntry.cs
@@ -47,12 +47,16 @@ internal class HelpSystemEntry : PlatformDependentComponent
public bool IsPlaying { get; set; }
///
- /// Path to the HelpSystemEntry prefab.
+ /// Path to the HelpSystemEntry prefab. It contains the video player,
+ /// the text area for the help text, and the buttons for controlling the video
+ /// as well as the button to close the dialog and the button to go back to the
+ /// parent in the help menu.
///
private const string helpSystemEntryPrefab = "Prefabs/UI/HelpSystemEntry";
///
- /// Path to the HelpSystemEntrySpace prefab.
+ /// Path to the HelpSystemEntrySpace prefab. It is a panel in which
+ /// the HelpSystemEntry is placed.
///
private const string helpSystemEntrySpacePrefab = "Prefabs/UI/HelpSystemEntrySpace";
@@ -96,13 +100,17 @@ internal class HelpSystemEntry : PlatformDependentComponent
private ButtonManagerBasicIcon backwardButton;
///
- /// The helpSystemEntry-GameObject.
+ /// The helpSystemEntry GameObject.
+ /// It will be instantiated from the prefab .
+ /// It will be a child of .
///
private GameObject helpSystemEntry;
///
/// The helpSystemEntrySpace-GameObject which contains the helpSystemEntry.
/// It is nessecary because of the dynamic panel for scaling the entry.
+ /// It will be instantiated from the prefab .
+ /// It will be a child of .
///
private GameObject helpSystemSpace;
@@ -141,13 +149,25 @@ protected override void StartDesktop()
{
/// Note: called below accesses
/// that is why we need to instantiate it first.
- helpSystemSpace = PrefabInstantiator.InstantiatePrefab(helpSystemEntrySpacePrefab, Canvas.transform, false);
+ SetUpHelpSystemEntryAndSpace();
helpSystemSpace.SetActive(false);
- helpSystemEntry = PrefabInstantiator.InstantiatePrefab(helpSystemEntryPrefab, helpSystemSpace.transform, false);
helpSystemEntry.SetActive(false);
instructionsDisplay = GetTextField();
}
+ ///
+ /// Instantiates the help system entry and the space if necessary, that is,
+ /// if is null.
+ ///
+ private void SetUpHelpSystemEntryAndSpace()
+ {
+ if (helpSystemSpace == null)
+ {
+ helpSystemSpace = PrefabInstantiator.InstantiatePrefab(helpSystemEntrySpacePrefab, Canvas.transform, false);
+ helpSystemEntry = PrefabInstantiator.InstantiatePrefab(helpSystemEntryPrefab, helpSystemSpace.transform, false);
+ }
+ }
+
///
/// Returns the game object holding a TextMeshPro component in which the instructions
/// are printed. It is the descendant of with the
@@ -249,6 +269,9 @@ public VideoPlayer GetVideoPlayer()
///
public void ShowEntry()
{
+ /// If the dialog was closed, and
+ /// are null and need to be instantiated again.
+ SetUpHelpSystemEntryAndSpace();
helpSystemSpace.SetActive(true);
helpSystemEntry.SetActive(true);
helpSystemSpace.transform.localScale = new Vector3(1.7f, 1.7f);
diff --git a/Assets/SEE/UI/Menu/Drawable/ConfirmDialogMenu.cs b/Assets/SEE/UI/Menu/Drawable/ConfirmDialogMenu.cs
index ce40e51fea..351ac13719 100644
--- a/Assets/SEE/UI/Menu/Drawable/ConfirmDialogMenu.cs
+++ b/Assets/SEE/UI/Menu/Drawable/ConfirmDialogMenu.cs
@@ -1,6 +1,9 @@
-using Michsky.UI.ModernUIPack;
+using Cysharp.Threading.Tasks;
+using Michsky.UI.ModernUIPack;
using SEE.Game.Drawable;
+using SEE.Utils;
using TMPro;
+using UnityEngine.Events;
namespace SEE.UI.Menu.Drawable
{
@@ -18,37 +21,48 @@ public class ConfirmDialogMenu : Menu
/// Enables the dialog menu with given
///
/// The text that should be displayed.
- public void Enable(string text)
+ public ConfirmDialogMenu(string text)
{
- if (gameObject == null)
+ Instantiate(confirmMenuPrefab);
+
+ /// Initialize the buttons.
+ ButtonManagerBasic cancelDragger = GameFinder.FindChild(gameObject, "CancelDragger")
+ .GetComponent();
+ cancelDragger.clickEvent.AddListener(() =>
+ {
+ WasCanceled = true;
+ Destroyer.Destroy(gameObject);
+ });
+ ButtonManagerBasic confirm = GameFinder.FindChild(gameObject, "Confirm")
+ .GetComponent();
+ confirm.clickEvent.AddListener(() =>
+ {
+ WasConfirmed = true;
+ Destroyer.Destroy(gameObject);
+ });
+ ButtonManagerBasic cancel = GameFinder.FindChild(gameObject, "Cancel")
+ .GetComponent();
+ cancel.clickEvent.AddListener(() =>
{
- Instantiate(confirmMenuPrefab);
+ WasCanceled = true;
+ Destroyer.Destroy(gameObject);
+ });
+ TextMeshProUGUI description = GameFinder.FindChild(gameObject, "Description")
+ .GetComponent();
+ description.text = text;
+ }
- /// Initialize the buttons.
- ButtonManagerBasic cancelDragger = GameFinder.FindChild(gameObject, "CancelDragger")
- .GetComponent();
- cancelDragger.clickEvent.AddListener(() =>
- {
- WasCanceled = true;
- Destroy();
- });
- ButtonManagerBasic confirm = GameFinder.FindChild(gameObject, "Confirm")
- .GetComponent();
- confirm.clickEvent.AddListener(() =>
- {
- WasConfirmed = true;
- Destroy();
- });
- ButtonManagerBasic cancel = GameFinder.FindChild(gameObject, "Cancel")
- .GetComponent();
- cancel.clickEvent.AddListener(() =>
- {
- WasCanceled = true;
- Destroy();
- });
- TextMeshProUGUI description = GameFinder.FindChild(gameObject, "Description")
- .GetComponent();
- description.text = text;
+ ///
+ /// Executes the action after confirming the dialog.
+ ///
+ /// The action to be executed.
+ /// nothing, it waits until the dialog was confirmed or canceled.
+ public async UniTask ExecuteAfterConfirmAsync(UnityAction action)
+ {
+ await UniTask.WaitWhile(IsOpen);
+ if (WasConfirmed && !WasCanceled)
+ {
+ action.Invoke();
}
}
diff --git a/Assets/SEE/UI/PopupMenu/PopupMenu.cs b/Assets/SEE/UI/PopupMenu/PopupMenu.cs
index 708316d63b..91d70658b1 100644
--- a/Assets/SEE/UI/PopupMenu/PopupMenu.cs
+++ b/Assets/SEE/UI/PopupMenu/PopupMenu.cs
@@ -1,9 +1,11 @@
-using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using DG.Tweening;
using Michsky.UI.ModernUIPack;
using SEE.GO;
using SEE.Utils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -70,13 +72,32 @@ public class PopupMenu : PlatformDependentComponent
///
/// The height of the menu.
///
- private float MenuHeight => menu.sizeDelta.y;
+ private float MenuHeight => GetHeight();
+
+ ///
+ /// The width of the menu.
+ ///
+ private float MenuWidth => menu.sizeDelta.x;
///
/// Duration of the animation that is used to show or hide the menu.
///
private const float animationDuration = 0.5f;
+ ///
+ /// The popup entries.
+ ///
+ private readonly List entries = new();
+
+ ///
+ /// Gets the height of the menu.
+ ///
+ /// The current height.
+ private float GetHeight()
+ {
+ return entries.Sum(x => ((RectTransform) x.transform).rect.height);
+ }
+
protected override void StartDesktop()
{
// Instantiate the menu.
@@ -122,6 +143,9 @@ public void AddEntry(PopupMenuEntry entry)
switch (entry)
{
+ case PopupMenuActionDoubleIcon doubleIconAction:
+ AddDoubleIconAction(doubleIconAction);
+ break;
case PopupMenuAction action:
AddAction(action);
break;
@@ -131,25 +155,31 @@ public void AddEntry(PopupMenuEntry entry)
default:
throw new System.ArgumentException($"Unknown entry type: {entry.GetType()}");
}
-
- // TODO (#668): Respect priority
}
///
/// Adds a new to the menu.
///
/// The action to be added.
- private void AddAction(PopupMenuAction action)
+ /// The item to be added to the PopupMenu.
+ /// If the default item is to be added, null should be used.
+ /// Another item can be, for example, the SubMenuButton.
+ ///
+ private void AddAction(PopupMenuAction action, GameObject actionItem = null)
{
- GameObject actionItem = PrefabInstantiator.InstantiatePrefab("Prefabs/UI/PopupMenuButton", actionList, false);
+ actionItem ??= PrefabInstantiator.InstantiatePrefab("Prefabs/UI/PopupMenuButton", actionList, false);
ButtonManagerBasic button = actionItem.MustGetComponent();
button.buttonText = action.Name;
+ PriorityHolder priorityHolder = actionItem.AddComponent();
+ priorityHolder.Priority = action.Priority;
+
button.clickEvent.AddListener(OnClick);
if (action.IconGlyph != default)
{
actionItem.transform.Find("Icon").gameObject.MustGetComponent().text = action.IconGlyph.ToString();
}
+ entries.Add(actionItem);
return;
void OnClick()
@@ -162,6 +192,22 @@ void OnClick()
}
}
+ ///
+ /// Adds a new to the menu.
+ /// It can be used for popup sub-menus.
+ ///
+ /// The sub-menu to be added.
+ private void AddDoubleIconAction(PopupMenuActionDoubleIcon doubleIconAction)
+ {
+ GameObject actionItem = PrefabInstantiator.InstantiatePrefab("Prefabs/UI/PopupMenuSubMenuButton", actionList, false);
+ if (doubleIconAction.RightIconGlyph != default)
+ {
+ actionItem.transform.Find("RightIcon").gameObject.MustGetComponent()
+ .text = doubleIconAction.RightIconGlyph.ToString();
+ }
+ AddAction(doubleIconAction, actionItem);
+ }
+
///
/// Adds a new to the menu.
///
@@ -171,6 +217,9 @@ private void AddHeading(PopupMenuHeading heading)
GameObject headingItem = PrefabInstantiator.InstantiatePrefab("Prefabs/UI/PopupMenuHeading", actionList, false);
TextMeshProUGUI text = headingItem.MustGetComponent();
text.text = heading.Text;
+ PriorityHolder priorityHolder = headingItem.AddComponent();
+ priorityHolder.Priority = heading.Priority;
+ entries.Add(headingItem);
}
///
@@ -195,11 +244,11 @@ public void ClearEntries()
entriesBeforeStart.Clear();
return;
}
-
- foreach (Transform child in actionList)
+ foreach (GameObject entry in entries)
{
- Destroyer.Destroy(child.gameObject);
+ Destroyer.Destroy(entry);
}
+ entries.Clear();
}
///
@@ -209,15 +258,19 @@ public void ClearEntries()
public void MoveTo(Vector2 position)
{
AdjustMenuHeight();
+ bool moved = false;
if (position.y < MenuHeight * ScaleFactor)
{
// If the menu is too close to the bottom of the screen, expand it upwards instead.
- position.y += MenuHeight * ScaleFactor;
- // The mouse should hover over the first menu item already rather than being just outside of it,
- // so we move the menu down and to the left a bit.
- position += new Vector2(-5, -5);
+ position.y = MenuHeight * ScaleFactor;
+ moved = true;
+ }
+ if (Screen.width < position.x + MenuWidth * ScaleFactor)
+ {
+ position.x = Screen.width - MenuWidth * ScaleFactor;
+ moved = true;
}
- else
+ if (!moved)
{
// The mouse should hover over the first menu item already rather than being just outside of it,
// so we move the menu up and to the left a bit.
@@ -233,10 +286,32 @@ public void MoveTo(Vector2 position)
private void AdjustMenuHeight()
{
// Menu should not take up more than 40% of the screen.
- float height = Mathf.Clamp(actionList.sizeDelta.y, 100f, Screen.height / (2.5f*ScaleFactor));
+ float height = Mathf.Clamp(MenuHeight, 100f, Screen.height / (2.5f * ScaleFactor));
scrollView.sizeDelta = new Vector2(scrollView.sizeDelta.x, height);
}
+ ///
+ /// Sorts the entries by their priority.
+ /// Keep in mind: A higher priority is displayed first.
+ ///
+ private void SortEntries()
+ {
+ entries.Sort((a, b) =>
+ {
+ PriorityHolder aHolder = a.GetComponent();
+ PriorityHolder bHolder = b.GetComponent();
+ if (aHolder == null || bHolder == null)
+ {
+ return 0;
+ }
+ return bHolder.Priority.CompareTo(aHolder.Priority);
+ });
+ for (int i = 0; i < entries.Count; i++)
+ {
+ entries[i].transform.SetSiblingIndex(i);
+ }
+ }
+
///
/// Activates the menu and fades it in.
/// This asynchronous method will return once the menu is fully shown.
@@ -253,6 +328,7 @@ public async UniTask ShowMenuAsync()
await UniTask.WaitForEndOfFrame();
contentSizeFitter.enabled = true;
AdjustMenuHeight();
+ SortEntries();
await UniTask.WhenAll(menu.DOScale(1, animationDuration).AsyncWaitForCompletion().AsUniTask(),
menuCanvasGroup.DOFade(1, animationDuration / 2).AsyncWaitForCompletion().AsUniTask());
}
diff --git a/Assets/SEE/UI/PopupMenu/PopupMenuEntry.cs b/Assets/SEE/UI/PopupMenu/PopupMenuEntry.cs
index a40808e7d2..1977ea9047 100644
--- a/Assets/SEE/UI/PopupMenu/PopupMenuEntry.cs
+++ b/Assets/SEE/UI/PopupMenu/PopupMenuEntry.cs
@@ -1,5 +1,4 @@
using System;
-using UnityEngine.Events;
namespace SEE.UI.PopupMenu
{
@@ -21,7 +20,7 @@ public abstract record PopupMenuEntry(int Priority);
/// The priority of the entry. Entries with a higher priority
/// are displayed first.
public record PopupMenuAction(string Name, Action Action, char IconGlyph, bool CloseAfterClick = true,
- int Priority = default) : PopupMenuEntry(Priority);
+ int Priority = default) : PopupMenuEntry(Priority);
///
/// A heading that can be added to a .
@@ -30,4 +29,19 @@ public record PopupMenuAction(string Name, Action Action, char IconGlyph, bool C
/// The priority of the entry. Entries with a higher priority
/// are displayed first.
public record PopupMenuHeading(string Text, int Priority = default) : PopupMenuEntry(Priority);
+
+ ///
+ /// A sub menu action that can be added to a .
+ ///
+ /// The name of the action.
+ /// The action to be executed when the user clicks on the action.
+ /// The unicode glyph of the FontAwesome v6 icon
+ /// that should be displayed left of the .
+ /// The unicode glyph of the FontAwesome v6 icon
+ /// that should be displayed right of the .
+ /// Whether the menu should be closed after the action is executed.
+ /// The priority of the entry. Entries with a higher priority
+ /// are displayed first.
+ public record PopupMenuActionDoubleIcon(string Name, Action Action, char LeftIconGlyph, char RightIconGlyph,
+ bool CloseAfterClick = true, int Priority = default) : PopupMenuAction(Name, Action, LeftIconGlyph, CloseAfterClick, Priority);
}
diff --git a/Assets/SEE/UI/PopupMenu/PriorityHolder.cs b/Assets/SEE/UI/PopupMenu/PriorityHolder.cs
new file mode 100644
index 0000000000..60ad2db2f5
--- /dev/null
+++ b/Assets/SEE/UI/PopupMenu/PriorityHolder.cs
@@ -0,0 +1,15 @@
+using UnityEngine;
+
+namespace SEE.UI.PopupMenu
+{
+ ///
+ /// Component which holds the priority of a .
+ ///
+ public class PriorityHolder : MonoBehaviour
+ {
+ ///
+ /// The priority of a .
+ ///
+ public int Priority { get; set; } = 0;
+ }
+}
diff --git a/Assets/SEE/UI/PopupMenu/PriorityHolder.cs.meta b/Assets/SEE/UI/PopupMenu/PriorityHolder.cs.meta
new file mode 100644
index 0000000000..1ad6503a2a
--- /dev/null
+++ b/Assets/SEE/UI/PopupMenu/PriorityHolder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c12d1a37675d8ee4dbcac6733d44a20d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/SEE/UI/PropertyDialog/NodePropertyDialog.cs b/Assets/SEE/UI/PropertyDialog/NodePropertyDialog.cs
index ae1133f7c0..e626bffc20 100644
--- a/Assets/SEE/UI/PropertyDialog/NodePropertyDialog.cs
+++ b/Assets/SEE/UI/PropertyDialog/NodePropertyDialog.cs
@@ -1,6 +1,11 @@
using SEE.Controls;
+using SEE.Controls.Actions; /// Reference in comment.
using SEE.DataModel.DG;
+using SEE.Game.SceneManipulation;
+using SEE.GO;
+using SEE.Net.Actions;
using SEE.Utils;
+using System.Linq;
using UnityEngine;
using UnityEngine.Events;
@@ -49,12 +54,18 @@ public NodePropertyDialog(Node node)
///
/// The dialog property for the type of the node to be entered in the dialog.
///
- private StringProperty nodeType;
+ private SelectionProperty nodeType;
+
+ ///
+ /// The last chosen node type for the mode.
+ ///
+ private static string lastUsed = string.Empty;
///
/// Creates and opens the dialog.
///
- public void Open()
+ /// Whether the last used index should be used and updated.
+ public void Open(bool useLastUsed = false)
{
dialog = new GameObject("Node attributes");
@@ -65,9 +76,22 @@ public void Open()
nodeName.Description = "Name of the node";
// Type of the node
- nodeType = dialog.AddComponent();
+ nodeType = dialog.AddComponent();
nodeType.Name = "Node type";
- nodeType.Value = node.Type;
+ nodeType.AddOptions(node.GameObject().ContainingCity().NodeTypes.Types.OrderBy(t => t));
+ if (!useLastUsed || string.IsNullOrEmpty(lastUsed))
+ {
+ nodeType.Value = node.Type;
+ }
+ else if (useLastUsed)
+ {
+ if (node.Type != lastUsed)
+ {
+ GameNodeEditor.ChangeType(node, lastUsed);
+ new EditNodeNetAction(node.ID, node.SourceName, lastUsed).Execute();
+ }
+ nodeType.Value = lastUsed;
+ }
nodeType.Description = "Type of the node";
// Group for node name and type
@@ -89,6 +113,25 @@ public void Open()
SEEInput.KeyboardShortcutsEnabled = false;
// Go online
propertyDialog.DialogShouldBeShown = true;
+
+ ///
+ /// Sets the attributes of to the trimmed values entered in the dialog,
+ /// notifies all listeners on , and closes the dialog.
+ ///
+ void OKButtonPressed()
+ {
+ GameNodeEditor.ChangeName(node, nodeName.Value);
+ GameNodeEditor.ChangeType(node, nodeType.Value);
+
+ /// Updates the attribute.
+ if (useLastUsed)
+ {
+ lastUsed = nodeType.Value;
+ }
+ OnConfirm.Invoke();
+ SEEInput.KeyboardShortcutsEnabled = true;
+ Close();
+ }
}
///
@@ -101,19 +144,6 @@ private void CancelButtonPressed()
Close();
}
- ///
- /// Sets the attributes of to the trimmed values entered in the dialog,
- /// notifies all listeners on , and closes the dialog.
- ///
- private void OKButtonPressed()
- {
- node.SourceName = nodeName.Value.Trim();
- node.Type = nodeType.Value.Trim();
- OnConfirm.Invoke();
- SEEInput.KeyboardShortcutsEnabled = true;
- Close();
- }
-
///
/// Destroys . will be null afterwards.
///
diff --git a/Assets/SEE/UI/Window/BaseWindow.cs b/Assets/SEE/UI/Window/BaseWindow.cs
index c7308ca5fb..9c9f4d6c6b 100644
--- a/Assets/SEE/UI/Window/BaseWindow.cs
+++ b/Assets/SEE/UI/Window/BaseWindow.cs
@@ -73,11 +73,13 @@ protected override void StartDesktop()
// Set title
Window.transform.Find("Dragger/Title").gameObject.GetComponent().text = Title;
- // TODO: Disable IDE Button if unused
+ /// Disables the window dragger IDE buttons.
+ /// Note: If a sub class needs the IDE buttons, call
+ DisableWindowDraggerButtons();
}
///
- /// Disables all window dragger buttons.
+ /// Disables the window dragger buttons.
/// dd
public void DisableWindowDraggerButtons()
{
@@ -88,6 +90,18 @@ public void DisableWindowDraggerButtons()
}
}
+ ///
+ /// Activates the window dragger buttons.
+ ///
+ public void ActivateWindowDraggerButtons()
+ {
+ Button[] buttons = Window.transform.Find("Dragger").GetComponentsInChildren