Compare commits

...

2 Commits

Author SHA1 Message Date
Michal Pikulski
f6ebadf21e Update Pause menu and MinigameSelection menu to icon-based with no text 2025-10-30 10:47:34 +01:00
Michal Pikulski
eeca4973ae Add component search+replace 2025-10-30 10:29:57 +01:00
10 changed files with 1781 additions and 2486 deletions

View File

@@ -0,0 +1,550 @@
using UnityEditor;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using UnityEditor.IMGUI.Controls;
namespace Editor.Tools
{
public class ComponentSearchReplaceWindow : EditorWindow
{
private Type selectedSearchType;
private Type selectedReplaceType;
private List<ComponentInfo> foundComponents = new List<ComponentInfo>();
private Vector2 scrollPos;
private int selectedComponentIndex = -1;
private string searchTypeName = "Select a Component...";
private string replaceTypeName = "Select a Component...";
private List<Type> allMonoBehaviourTypes = new List<Type>();
[MenuItem("Tools/Component Search & Replace")]
public static void ShowWindow()
{
var window = GetWindow<ComponentSearchReplaceWindow>("Component Search & Replace");
window.minSize = new Vector2(700, 500);
window.maxSize = new Vector2(1400, 1000);
window.position = new Rect(120, 120, 800, 600);
}
private void OnEnable()
{
CacheMonoBehaviourTypes();
EditorSceneManager.sceneOpened += (scene, mode) => ClearSearch();
SceneManager.activeSceneChanged += (oldScene, newScene) => ClearSearch();
}
private void OnDisable()
{
EditorSceneManager.sceneOpened -= (scene, mode) => ClearSearch();
SceneManager.activeSceneChanged -= (oldScene, newScene) => ClearSearch();
}
private void CacheMonoBehaviourTypes()
{
allMonoBehaviourTypes.Clear();
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var types = assembly.GetTypes()
.Where(t => (t.IsSubclassOf(typeof(MonoBehaviour)) || t.IsSubclassOf(typeof(Component)))
&& !t.IsAbstract
&& t != typeof(Transform)) // Exclude Transform as it can't be removed
.OrderBy(t => t.Name);
allMonoBehaviourTypes.AddRange(types);
}
catch (Exception)
{
// Some assemblies may throw exceptions when getting types
continue;
}
}
}
private void ClearSearch()
{
foundComponents.Clear();
selectedComponentIndex = -1;
Repaint();
}
private void OnGUI()
{
GUILayout.Space(10);
// Search Section
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Search for Component", EditorStyles.boldLabel);
GUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Component Type:", GUILayout.Width(120));
if (GUILayout.Button(searchTypeName, EditorStyles.popup, GUILayout.ExpandWidth(true)))
{
var dropdown = new ComponentTypeDropdown(
new AdvancedDropdownState(),
(type) =>
{
selectedSearchType = type;
searchTypeName = type != null ? type.Name : "Select a Component...";
ClearSearch();
Repaint();
});
dropdown.Show(GUILayoutUtility.GetLastRect());
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(5);
EditorGUI.BeginDisabledGroup(selectedSearchType == null);
if (GUILayout.Button("Search Scene", GUILayout.Height(30)))
{
SearchForComponents();
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();
GUILayout.Space(10);
// Results Section
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField($"Search Results ({foundComponents.Count} found)", EditorStyles.boldLabel);
GUILayout.Space(5);
scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.ExpandHeight(true));
if (foundComponents.Count == 0)
{
EditorGUILayout.HelpBox("No components found. Select a component type and click 'Search Scene'.", MessageType.Info);
}
else
{
for (int i = 0; i < foundComponents.Count; i++)
{
var info = foundComponents[i];
if (info.component == null) continue;
bool isSelected = selectedComponentIndex == i;
Color originalColor = GUI.backgroundColor;
if (isSelected)
{
GUI.backgroundColor = new Color(0.3f, 0.6f, 1f, 0.5f);
}
EditorGUILayout.BeginVertical("box");
GUI.backgroundColor = originalColor;
EditorGUILayout.BeginHorizontal();
// Selection toggle
bool newSelected = GUILayout.Toggle(isSelected, "", GUILayout.Width(20));
if (newSelected != isSelected)
{
selectedComponentIndex = newSelected ? i : -1;
}
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField($"GameObject: {info.gameObject.name}", EditorStyles.boldLabel);
EditorGUILayout.LabelField($"Path: {info.hierarchyPath}", EditorStyles.miniLabel);
EditorGUILayout.LabelField($"Component: {info.component.GetType().Name}", EditorStyles.miniLabel);
EditorGUILayout.EndVertical();
if (GUILayout.Button("Ping", GUILayout.Width(80)))
{
Selection.activeObject = info.gameObject;
EditorGUIUtility.PingObject(info.gameObject);
SceneView.FrameLastActiveSceneView();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
GUILayout.Space(2);
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
GUILayout.Space(10);
// Replace Section
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Replace Component", EditorStyles.boldLabel);
GUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Replace With:", GUILayout.Width(120));
if (GUILayout.Button(replaceTypeName, EditorStyles.popup, GUILayout.ExpandWidth(true)))
{
var dropdown = new ComponentTypeDropdown(
new AdvancedDropdownState(),
(type) =>
{
selectedReplaceType = type;
replaceTypeName = type != null ? type.Name : "Select a Component...";
Repaint();
});
dropdown.Show(GUILayoutUtility.GetLastRect());
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(5);
bool canReplace = selectedComponentIndex >= 0 &&
selectedComponentIndex < foundComponents.Count &&
selectedReplaceType != null;
EditorGUI.BeginDisabledGroup(!canReplace);
if (!canReplace && selectedComponentIndex >= 0 && selectedReplaceType == null)
{
EditorGUILayout.HelpBox("Select a component type to replace with.", MessageType.Warning);
}
else if (!canReplace && selectedReplaceType != null)
{
EditorGUILayout.HelpBox("Select an object from the search results above.", MessageType.Warning);
}
if (GUILayout.Button("Replace Selected Component", GUILayout.Height(30)))
{
ReplaceComponent();
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();
GUILayout.Space(5);
}
private void SearchForComponents()
{
if (selectedSearchType == null)
{
Debug.LogWarning("No component type selected for search.");
return;
}
foundComponents.Clear();
selectedComponentIndex = -1;
var allObjects = FindObjectsByType<GameObject>(FindObjectsInactive.Include, FindObjectsSortMode.None);
foreach (var go in allObjects)
{
var component = go.GetComponent(selectedSearchType);
if (component != null)
{
foundComponents.Add(new ComponentInfo
{
gameObject = go,
component = component,
hierarchyPath = GetHierarchyPath(go)
});
}
}
foundComponents = foundComponents.OrderBy(c => c.hierarchyPath).ToList();
Debug.Log($"Found {foundComponents.Count} objects with component type '{selectedSearchType.Name}'");
Repaint();
}
private void ReplaceComponent()
{
if (selectedComponentIndex < 0 || selectedComponentIndex >= foundComponents.Count)
{
Debug.LogError("No component selected to replace.");
return;
}
if (selectedReplaceType == null)
{
Debug.LogError("No replacement component type selected.");
return;
}
var info = foundComponents[selectedComponentIndex];
var go = info.gameObject;
var oldComponent = info.component;
if (go == null || oldComponent == null)
{
Debug.LogError("Selected component or GameObject is null.");
foundComponents.RemoveAt(selectedComponentIndex);
selectedComponentIndex = -1;
Repaint();
return;
}
// Confirm the replacement
bool confirmed = EditorUtility.DisplayDialog(
"Replace Component",
$"Are you sure you want to replace '{selectedSearchType.Name}' with '{selectedReplaceType.Name}' on GameObject '{go.name}'?\n\n" +
"This action can be undone with Ctrl+Z.",
"Replace",
"Cancel"
);
if (!confirmed)
{
return;
}
// Record undo
Undo.RegisterCompleteObjectUndo(go, "Replace Component");
// Copy serialized data if possible (optional enhancement)
// This attempts to preserve common fields between components
var serializedOldComponent = new SerializedObject(oldComponent);
// Remove old component
Undo.DestroyObjectImmediate(oldComponent);
// Add new component
var newComponent = Undo.AddComponent(go, selectedReplaceType);
// Try to copy matching serialized properties
var serializedNewComponent = new SerializedObject(newComponent);
CopyMatchingSerializedProperties(serializedOldComponent, serializedNewComponent);
serializedNewComponent.ApplyModifiedProperties();
// Mark scene as dirty
EditorSceneManager.MarkSceneDirty(go.scene);
Debug.Log($"Replaced '{selectedSearchType.Name}' with '{selectedReplaceType.Name}' on '{go.name}'");
// Update the found components list
foundComponents[selectedComponentIndex] = new ComponentInfo
{
gameObject = go,
component = newComponent,
hierarchyPath = info.hierarchyPath
};
// Update search type if needed
if (selectedSearchType != selectedReplaceType)
{
foundComponents.RemoveAt(selectedComponentIndex);
selectedComponentIndex = -1;
}
Repaint();
}
private void CopyMatchingSerializedProperties(SerializedObject source, SerializedObject destination)
{
SerializedProperty iterator = source.GetIterator();
bool enterChildren = true;
while (iterator.NextVisible(enterChildren))
{
enterChildren = false;
// Skip script reference
if (iterator.propertyPath == "m_Script")
continue;
SerializedProperty destProp = destination.FindProperty(iterator.propertyPath);
if (destProp != null && destProp.propertyType == iterator.propertyType)
{
try
{
destination.CopyFromSerializedProperty(iterator);
}
catch
{
// Some properties may not be copyable
}
}
}
}
private string GetHierarchyPath(GameObject go)
{
string path = go.name;
Transform t = go.transform.parent;
while (t != null)
{
path = t.name + "/" + path;
t = t.parent;
}
return path;
}
private class ComponentInfo
{
public GameObject gameObject;
public Component component;
public string hierarchyPath;
}
}
// Advanced Dropdown for Component Type Selection (including Unity built-in components)
public class ComponentTypeDropdown : AdvancedDropdown
{
private Action<Type> onTypeSelected;
private static List<Type> cachedTypes;
public ComponentTypeDropdown(AdvancedDropdownState state, Action<Type> onTypeSelected)
: base(state)
{
this.onTypeSelected = onTypeSelected;
minimumSize = new Vector2(300, 400);
if (cachedTypes == null)
{
CacheTypes();
}
}
private static void CacheTypes()
{
cachedTypes = new List<Type>();
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var types = assembly.GetTypes()
.Where(t => (t.IsSubclassOf(typeof(MonoBehaviour)) || t.IsSubclassOf(typeof(Component)))
&& !t.IsAbstract
&& t != typeof(Transform)) // Exclude Transform as it can't be removed
.OrderBy(t => t.Name);
cachedTypes.AddRange(types);
}
catch (Exception)
{
// Some assemblies may throw exceptions when getting types
}
}
}
protected override AdvancedDropdownItem BuildRoot()
{
var root = new AdvancedDropdownItem("Component Types");
// Add "None" option
var noneItem = new TypeDropdownItem("None", null);
root.AddChild(noneItem);
root.AddSeparator();
// Separate Unity built-in components from custom ones
var unityComponents = cachedTypes.Where(t =>
t.Namespace != null && t.Namespace.StartsWith("UnityEngine")).ToList();
var customComponents = cachedTypes.Except(unityComponents).ToList();
// Add Unity Components section
if (unityComponents.Any())
{
var unitySection = new AdvancedDropdownItem("Unity Components");
// Group Unity components by category (second part of namespace)
var unityGrouped = unityComponents.GroupBy(t =>
{
if (t.Namespace == "UnityEngine")
return "Core";
var parts = t.Namespace.Split('.');
return parts.Length > 1 ? parts[1] : "Other";
}).OrderBy(g => g.Key);
foreach (var group in unityGrouped)
{
if (group.Count() <= 5)
{
// Add directly if few items
foreach (var type in group.OrderBy(t => t.Name))
{
var typeItem = new TypeDropdownItem(type.Name, type);
unitySection.AddChild(typeItem);
}
}
else
{
// Create subcategory
var categoryItem = new AdvancedDropdownItem(group.Key);
foreach (var type in group.OrderBy(t => t.Name))
{
var typeItem = new TypeDropdownItem(type.Name, type);
categoryItem.AddChild(typeItem);
}
unitySection.AddChild(categoryItem);
}
}
root.AddChild(unitySection);
}
// Add Custom Components section
if (customComponents.Any())
{
root.AddSeparator();
// Group by namespace
var groupedTypes = customComponents.GroupBy(t =>
string.IsNullOrEmpty(t.Namespace) ? "(No Namespace)" : t.Namespace);
foreach (var group in groupedTypes.OrderBy(g => g.Key))
{
// For small namespaces or no namespace, add directly to root
if (group.Count() <= 3 || group.Key == "(No Namespace)")
{
foreach (var type in group)
{
var typeItem = new TypeDropdownItem(
group.Key == "(No Namespace)" ? type.Name : $"{group.Key}.{type.Name}",
type);
root.AddChild(typeItem);
}
}
else
{
// Create namespace folder
var namespaceItem = new AdvancedDropdownItem(group.Key);
foreach (var type in group)
{
var typeItem = new TypeDropdownItem(type.Name, type);
namespaceItem.AddChild(typeItem);
}
root.AddChild(namespaceItem);
}
}
}
return root;
}
protected override void ItemSelected(AdvancedDropdownItem item)
{
var typeItem = item as TypeDropdownItem;
if (typeItem != null)
{
onTypeSelected?.Invoke(typeItem.Type);
}
}
private class TypeDropdownItem : AdvancedDropdownItem
{
public Type Type { get; private set; }
public TypeDropdownItem(string name, Type type) : base(name)
{
Type = type;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 604b18a354fa44a49723ab9f9173762e
timeCreated: 1761816008

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -0,0 +1,195 @@
fileFormatVersion: 2
guid: c3d03e932743da340b8b2a2c290013f0
TextureImporter:
internalIDToNameTable:
- first:
213: -6037909929086895425
second: applehills_kort_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: applehills_kort_0
rect:
serializedVersion: 2
x: 8
y: 21
width: 498
height: 491
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: fbab8f2e8e8053ca0800000000000000
internalID: -6037909929086895425
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
applehills_kort_0: -6037909929086895425
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,195 @@
fileFormatVersion: 2
guid: 1bb06ae831042e24c915cd682fa2c6ab
TextureImporter:
internalIDToNameTable:
- first:
213: 3559306594824124945
second: power_off_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: power_off_0
rect:
serializedVersion: 2
x: 0
y: 0
width: 384
height: 384
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 11a723f6d72356130800000000000000
internalID: 3559306594824124945
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
power_off_0: 3559306594824124945
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -136,6 +136,81 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &671326379795878693
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4498104668003212261}
- component: {fileID: 1384216111310773312}
- component: {fileID: 8822046367999630453}
m_Layer: 5
m_Name: Image
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4498104668003212261
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 671326379795878693}
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: 538506048417711235}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.2, y: 0.2}
m_AnchorMax: {x: 0.8, y: 0.8}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1384216111310773312
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 671326379795878693}
m_CullTransparentMesh: 1
--- !u!114 &8822046367999630453
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 671326379795878693}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
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: -5941552768301239376, guid: 300528d856c15824989bade57a7e04e4, type: 3}
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 &1519948061307491961
GameObject:
m_ObjectHideFlags: 0
@@ -969,13 +1044,14 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Children:
- {fileID: 4498104668003212261}
m_Father: {fileID: 5494371867874695937}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: -100, y: 100}
m_SizeDelta: {x: 100, y: 100}
m_AnchoredPosition: {x: -56, y: 64}
m_SizeDelta: {x: 250, y: 250}
m_Pivot: {x: 1, y: 0}
--- !u!222 &1082932495279705792
CanvasRenderer:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -6,7 +7,7 @@ using UnityEngine.UI;
namespace Levels
{
/// <summary>
/// UI overlay for confirming a level switch. Displays level info and handles confirm/cancel actions, and supports leaderboard view.
/// UI overlay for confirming a level switch. Displays level info and handles confirm/cancel actions, and supports scrolling between level info and leaderboard.
/// </summary>
public class MinigameSwitchMenu : MonoBehaviour
{
@@ -15,14 +16,15 @@ namespace Levels
public TMP_Text levelNameText;
public Button confirmButton;
public Button cancelButton;
public Button leaderboardButton;
public GameObject levelInfoContainer;
public GameObject leaderboardContainer;
public Button leaderboardDismissButton;
public Button toLeaderboardButton;
public Button toLevelSelectButton;
public ScrollRect scrollView;
public float scrollDuration = 1f;
private Action _onLevelConfirm;
private Action _onCancel;
private LevelSwitchData _switchData;
private Coroutine _activeScrollCoroutine;
/// <summary>
/// Initialize the menu with data and callbacks.
@@ -36,18 +38,26 @@ namespace Levels
if (levelNameText) levelNameText.text = switchData?.targetLevelSceneName ?? "";
if (confirmButton) confirmButton.onClick.AddListener(OnConfirmClicked);
if (cancelButton) cancelButton.onClick.AddListener(OnCancelClicked);
if (leaderboardButton) leaderboardButton.onClick.AddListener(OnLeaderboardClicked);
if (leaderboardDismissButton) leaderboardDismissButton.onClick.AddListener(OnLeaderboardDismissClicked);
if (levelInfoContainer) levelInfoContainer.SetActive(true);
if (leaderboardContainer) leaderboardContainer.SetActive(false);
if (toLeaderboardButton) toLeaderboardButton.onClick.AddListener(OnToLeaderboardClicked);
if (toLevelSelectButton) toLevelSelectButton.onClick.AddListener(OnToLevelSelectClicked);
// Show only the toLeaderboardButton at start
if (toLeaderboardButton) toLeaderboardButton.gameObject.SetActive(true);
if (toLevelSelectButton) toLevelSelectButton.gameObject.SetActive(false);
// Initialize scroll view to start at left (level info view)
if (scrollView) scrollView.horizontalNormalizedPosition = 0f;
}
private void OnDestroy()
{
if (confirmButton) confirmButton.onClick.RemoveListener(OnConfirmClicked);
if (cancelButton) cancelButton.onClick.RemoveListener(OnCancelClicked);
if (leaderboardButton) leaderboardButton.onClick.RemoveListener(OnLeaderboardClicked);
if (leaderboardDismissButton) leaderboardDismissButton.onClick.RemoveListener(OnLeaderboardDismissClicked);
if (toLeaderboardButton) toLeaderboardButton.onClick.RemoveListener(OnToLeaderboardClicked);
if (toLevelSelectButton) toLevelSelectButton.onClick.RemoveListener(OnToLevelSelectClicked);
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
_activeScrollCoroutine = null;
}
}
private void OnConfirmClicked()
@@ -62,16 +72,60 @@ namespace Levels
Destroy(gameObject);
}
private void OnLeaderboardClicked()
private void OnToLeaderboardClicked()
{
if (levelInfoContainer) levelInfoContainer.SetActive(false);
if (leaderboardContainer) leaderboardContainer.SetActive(true);
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
}
_activeScrollCoroutine = StartCoroutine(ScrollToLeaderboardCoroutine());
}
private void OnLeaderboardDismissClicked()
private void OnToLevelSelectClicked()
{
if (levelInfoContainer) levelInfoContainer.SetActive(true);
if (leaderboardContainer) leaderboardContainer.SetActive(false);
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
}
_activeScrollCoroutine = StartCoroutine(ScrollToLevelSelectCoroutine());
}
private IEnumerator ScrollToLeaderboardCoroutine()
{
if (toLeaderboardButton) toLeaderboardButton.gameObject.SetActive(false);
float elapsed = 0f;
float startPos = scrollView.horizontalNormalizedPosition;
float targetPos = 0.95f;
while (elapsed < scrollDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / scrollDuration);
float smoothT = Mathf.SmoothStep(0f, 1f, t);
scrollView.horizontalNormalizedPosition = Mathf.Lerp(startPos, targetPos, smoothT);
yield return null;
}
scrollView.horizontalNormalizedPosition = targetPos;
if (toLevelSelectButton) toLevelSelectButton.gameObject.SetActive(true);
_activeScrollCoroutine = null;
}
private IEnumerator ScrollToLevelSelectCoroutine()
{
if (toLevelSelectButton) toLevelSelectButton.gameObject.SetActive(false);
float elapsed = 0f;
float startPos = scrollView.horizontalNormalizedPosition;
float targetPos = 0f;
while (elapsed < scrollDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / scrollDuration);
float smoothT = Mathf.SmoothStep(0f, 1f, t);
scrollView.horizontalNormalizedPosition = Mathf.Lerp(startPos, targetPos, smoothT);
yield return null;
}
scrollView.horizontalNormalizedPosition = targetPos;
if (toLeaderboardButton) toLeaderboardButton.gameObject.SetActive(true);
_activeScrollCoroutine = null;
}
}
}