Migrate settings to a more manageable structure and implement Service Locator pattern for runtime Addressables retrieval
This commit is contained in:
@@ -15,7 +15,7 @@ MonoBehaviour:
|
||||
m_DefaultGroup: 6f3207429a65b3e4b83935ac19791077
|
||||
m_currentHash:
|
||||
serializedVersion: 2
|
||||
Hash: c0cf00979528ae95d3583c572e4eb343
|
||||
Hash: 00000000000000000000000000000000
|
||||
m_OptimizeCatalogSize: 0
|
||||
m_BuildRemoteCatalog: 0
|
||||
m_CatalogRequestsTimeout: 0
|
||||
@@ -61,6 +61,7 @@ MonoBehaviour:
|
||||
m_GroupAssets:
|
||||
- {fileID: 11400000, guid: efe7e1728e73e9546ac5dfee2eff524f, type: 2}
|
||||
- {fileID: 11400000, guid: 6e4927e7e19eef34b93dc2baa9e9e8e2, type: 2}
|
||||
- {fileID: 11400000, guid: e25c7672a65b5974bb354fcfb2a8400c, type: 2}
|
||||
- {fileID: 11400000, guid: 7fcc03e584505ed4381983b6ebb1179d, type: 2}
|
||||
m_BuildSettings:
|
||||
m_LogResourceManagerExceptions: 1
|
||||
|
||||
36
Assets/AddressableAssetsData/AssetGroups/Settings.asset
Normal file
36
Assets/AddressableAssetsData/AssetGroups/Settings.asset
Normal file
@@ -0,0 +1,36 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bbb281ee3bf0b054c82ac2347e9e782c, type: 3}
|
||||
m_Name: Settings
|
||||
m_EditorClassIdentifier:
|
||||
m_GroupName: Settings
|
||||
m_GUID: c62e6f02418e19949bca4cccdd5fa02e
|
||||
m_SerializeEntries:
|
||||
- m_GUID: 35bfcff00faa72c4eb272a9e8288f965
|
||||
m_Address: Settings/PlayerFollowerSettings
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 8f5195fb013895049a19488fd4d8f2a1
|
||||
m_Address: Settings/InteractionSettings
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: a9569848f604a6540827d4d4bb0a35c2
|
||||
m_Address: Settings/MinigameSettings
|
||||
m_ReadOnly: 0
|
||||
m_SerializedLabels: []
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 0
|
||||
m_Settings: {fileID: 11400000, guid: 11da9bb90d9dd5848b4f7629415a6937, type: 2}
|
||||
m_SchemaSet:
|
||||
m_Schemas: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e25c7672a65b5974bb354fcfb2a8400c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -18,7 +18,7 @@ namespace Editor
|
||||
private enum ItemType { None, Pickup, ItemSlot }
|
||||
private ItemType _itemType = ItemType.None;
|
||||
|
||||
[MenuItem("Tools/Item Prefab Editor")]
|
||||
[MenuItem("AppleHills/Item Prefab Editor")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<ItemPrefabEditorWindow>("Item Prefab Editor");
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Editor
|
||||
|
||||
private bool _createNext = false;
|
||||
|
||||
[MenuItem("Tools/Prefab Creator")]
|
||||
[MenuItem("AppleHills/Item Prefab Creator")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<PrefabCreatorWindow>("Prefab Creator");
|
||||
|
||||
@@ -9,7 +9,7 @@ public class PuzzleChainEditorWindow : EditorWindow
|
||||
private Vector2 scrollPos;
|
||||
private const int INDENT_SIZE = 24;
|
||||
|
||||
[MenuItem("Tools/Puzzle Chain Editor")]
|
||||
[MenuItem("AppleHills/Puzzle Chain Editor")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<PuzzleChainEditorWindow>("Puzzle Chain Editor");
|
||||
|
||||
@@ -15,7 +15,7 @@ public class SceneObjectLocatorWindow : EditorWindow
|
||||
private List<PickupInfo> pickupInfos = new List<PickupInfo>();
|
||||
private Vector2 scrollPos;
|
||||
|
||||
[MenuItem("Tools/Scene Object Locator")]
|
||||
[MenuItem("AppleHills/Scene Object Locator")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<SceneObjectLocatorWindow>("Scene Object Locator");
|
||||
|
||||
190
Assets/Editor/SettingsEditorWindow.cs
Normal file
190
Assets/Editor/SettingsEditorWindow.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
|
||||
namespace AppleHills.Core.Settings.Editor
|
||||
{
|
||||
public class SettingsEditorWindow : EditorWindow
|
||||
{
|
||||
private Vector2 scrollPosition;
|
||||
private List<BaseSettings> allSettings = new List<BaseSettings>();
|
||||
private string[] tabNames = new string[] { "Player & Follower", "Interaction & Items", "Minigames" };
|
||||
private int selectedTab = 0;
|
||||
private Dictionary<string, SerializedObject> serializedSettingsObjects = new Dictionary<string, SerializedObject>();
|
||||
private GUIStyle headerStyle;
|
||||
|
||||
[MenuItem("AppleHills/Settings Editor")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<SettingsEditorWindow>("Game Settings");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
LoadAllSettings();
|
||||
}
|
||||
|
||||
private void LoadAllSettings()
|
||||
{
|
||||
allSettings.Clear();
|
||||
serializedSettingsObjects.Clear();
|
||||
|
||||
// Find all settings assets
|
||||
string[] guids = AssetDatabase.FindAssets("t:BaseSettings");
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
BaseSettings settings = AssetDatabase.LoadAssetAtPath<BaseSettings>(path);
|
||||
if (settings != null)
|
||||
{
|
||||
allSettings.Add(settings);
|
||||
serializedSettingsObjects[settings.GetType().Name] = new SerializedObject(settings);
|
||||
}
|
||||
}
|
||||
|
||||
// If any settings are missing, create them
|
||||
CreateSettingsIfMissing<PlayerFollowerSettings>("PlayerFollowerSettings");
|
||||
CreateSettingsIfMissing<InteractionSettings>("InteractionSettings");
|
||||
CreateSettingsIfMissing<MinigameSettings>("MinigameSettings");
|
||||
}
|
||||
|
||||
private void CreateSettingsIfMissing<T>(string fileName) where T : BaseSettings
|
||||
{
|
||||
if (!allSettings.Any(s => s is T))
|
||||
{
|
||||
// Check if the asset already exists
|
||||
string[] guids = AssetDatabase.FindAssets($"t:{typeof(T).Name}");
|
||||
if (guids.Length == 0)
|
||||
{
|
||||
// Create the settings folder if it doesn't exist
|
||||
if (!AssetDatabase.IsValidFolder("Assets/Settings"))
|
||||
{
|
||||
AssetDatabase.CreateFolder("Assets", "Settings");
|
||||
}
|
||||
|
||||
// Create new settings asset
|
||||
T settings = CreateInstance<T>();
|
||||
string path = $"Assets/Settings/{fileName}.asset";
|
||||
AssetDatabase.CreateAsset(settings, path);
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
allSettings.Add(settings);
|
||||
serializedSettingsObjects[typeof(T).Name] = new SerializedObject(settings);
|
||||
Debug.Log($"Created missing settings asset: {path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load existing asset
|
||||
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
T settings = AssetDatabase.LoadAssetAtPath<T>(path);
|
||||
allSettings.Add(settings);
|
||||
serializedSettingsObjects[typeof(T).Name] = new SerializedObject(settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (headerStyle == null)
|
||||
{
|
||||
headerStyle = new GUIStyle(EditorStyles.boldLabel);
|
||||
headerStyle.fontSize = 14;
|
||||
headerStyle.margin = new RectOffset(0, 0, 10, 10);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField("Apple Hills Game Settings", headerStyle);
|
||||
EditorGUILayout.HelpBox("Use this window to modify game settings. Changes are saved automatically.", MessageType.Info);
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
selectedTab = GUILayout.Toolbar(selectedTab, tabNames);
|
||||
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
switch (selectedTab)
|
||||
{
|
||||
case 0: // Player & Follower
|
||||
DrawSettingsEditor<PlayerFollowerSettings>();
|
||||
break;
|
||||
case 1: // Interaction & Items
|
||||
DrawSettingsEditor<InteractionSettings>();
|
||||
break;
|
||||
case 2: // Minigames
|
||||
DrawSettingsEditor<MinigameSettings>();
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Refresh", GUILayout.Width(100)))
|
||||
{
|
||||
LoadAllSettings();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Save All", GUILayout.Width(100)))
|
||||
{
|
||||
foreach (var serializedObj in serializedSettingsObjects.Values)
|
||||
{
|
||||
serializedObj.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(serializedObj.targetObject);
|
||||
}
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log("All settings saved!");
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawSettingsEditor<T>() where T : BaseSettings
|
||||
{
|
||||
BaseSettings settings = allSettings.Find(s => s is T);
|
||||
if (settings == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox($"No {typeof(T).Name} found. Click Refresh to create one.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
SerializedObject serializedObj = serializedSettingsObjects[typeof(T).Name];
|
||||
serializedObj.Update();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Draw all properties
|
||||
SerializedProperty property = serializedObj.GetIterator();
|
||||
bool enterChildren = true;
|
||||
while (property.NextVisible(enterChildren))
|
||||
{
|
||||
enterChildren = false;
|
||||
|
||||
// Skip the script field
|
||||
if (property.name == "m_Script") continue;
|
||||
|
||||
EditorGUILayout.PropertyField(property, true);
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
if (serializedObj.ApplyModifiedProperties())
|
||||
{
|
||||
EditorUtility.SetDirty(settings);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to highlight important fields
|
||||
private void DrawHighlightedProperty(SerializedProperty property, string tooltip = null)
|
||||
{
|
||||
GUI.backgroundColor = new Color(1f, 1f, 0.8f);
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
EditorGUILayout.PropertyField(property, new GUIContent(property.displayName, tooltip));
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Editor/SettingsEditorWindow.cs.meta
Normal file
3
Assets/Editor/SettingsEditorWindow.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfb9e77c746e41a2903603a39df3d424
|
||||
timeCreated: 1758619952
|
||||
307
Assets/Editor/SettingsMigrationWindow.cs
Normal file
307
Assets/Editor/SettingsMigrationWindow.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using System.IO;
|
||||
using AppleHills.Core.Settings;
|
||||
|
||||
namespace AppleHills.Editor
|
||||
{
|
||||
public class SettingsMigrationWindow : EditorWindow
|
||||
{
|
||||
private GameSettings legacySettings;
|
||||
private bool settingsFolderExists;
|
||||
private bool addressablesInstalled;
|
||||
private AddressableAssetSettings addressableSettings;
|
||||
private AddressableAssetGroup settingsGroup;
|
||||
private GUIStyle headerStyle;
|
||||
private GUIStyle successStyle;
|
||||
private Vector2 scrollPosition;
|
||||
private bool migrationCompleted = false;
|
||||
|
||||
[MenuItem("AppleHills/Migrate Legacy Settings")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<SettingsMigrationWindow>("Settings Migration");
|
||||
window.minSize = new Vector2(450, 400);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Check if Settings folder exists
|
||||
settingsFolderExists = AssetDatabase.IsValidFolder("Assets/Settings");
|
||||
|
||||
// Check if Addressables package is installed
|
||||
addressablesInstalled = AddressableAssetSettingsDefaultObject.SettingsExists;
|
||||
|
||||
if (addressablesInstalled)
|
||||
{
|
||||
addressableSettings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (headerStyle == null)
|
||||
{
|
||||
headerStyle = new GUIStyle(EditorStyles.boldLabel);
|
||||
headerStyle.fontSize = 14;
|
||||
headerStyle.margin = new RectOffset(0, 0, 10, 10);
|
||||
|
||||
successStyle = new GUIStyle(EditorStyles.label);
|
||||
successStyle.normal.textColor = Color.green;
|
||||
successStyle.fontSize = 12;
|
||||
}
|
||||
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
EditorGUILayout.LabelField("Migrate Legacy Settings", headerStyle);
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
"This tool will migrate your legacy GameSettings to the new modular settings system. " +
|
||||
"It will create new settings assets in the Assets/Settings folder, mark them as Addressables, " +
|
||||
"and copy values from your legacy settings.",
|
||||
MessageType.Info);
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Prerequisites section
|
||||
EditorGUILayout.LabelField("Prerequisites", EditorStyles.boldLabel);
|
||||
|
||||
// Check Addressables
|
||||
GUI.enabled = false;
|
||||
EditorGUILayout.Toggle("Addressables Package Installed", addressablesInstalled);
|
||||
GUI.enabled = true;
|
||||
|
||||
if (!addressablesInstalled)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
"The Addressables package is not installed. Please install it via Window > Package Manager.",
|
||||
MessageType.Error);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Legacy settings field
|
||||
legacySettings = EditorGUILayout.ObjectField("Legacy GameSettings", legacySettings, typeof(GameSettings), false) as GameSettings;
|
||||
|
||||
if (legacySettings == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
"Please assign your legacy GameSettings asset to migrate from.",
|
||||
MessageType.Warning);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(15);
|
||||
|
||||
// Migration button
|
||||
GUI.enabled = legacySettings != null && addressablesInstalled;
|
||||
|
||||
if (GUILayout.Button("Migrate Settings", GUILayout.Height(30)))
|
||||
{
|
||||
MigrateSettings();
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
|
||||
// Success message
|
||||
if (migrationCompleted)
|
||||
{
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField("Migration completed successfully!", successStyle);
|
||||
EditorGUILayout.HelpBox(
|
||||
"The legacy settings have been migrated to the new system. " +
|
||||
"You can now access these settings through the AppleHills > Settings Editor menu.",
|
||||
MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void MigrateSettings()
|
||||
{
|
||||
// Create Settings folder if it doesn't exist
|
||||
if (!settingsFolderExists)
|
||||
{
|
||||
AssetDatabase.CreateFolder("Assets", "Settings");
|
||||
settingsFolderExists = true;
|
||||
}
|
||||
|
||||
// Setup Addressables group for settings
|
||||
SetupAddressablesGroup();
|
||||
|
||||
// Create and populate the new settings assets
|
||||
CreatePlayerFollowerSettings();
|
||||
CreateInteractionSettings();
|
||||
CreateMinigameSettings();
|
||||
|
||||
// Save all assets
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
migrationCompleted = true;
|
||||
}
|
||||
|
||||
private void SetupAddressablesGroup()
|
||||
{
|
||||
// Find or create a settings group
|
||||
settingsGroup = addressableSettings.FindGroup("Settings");
|
||||
|
||||
if (settingsGroup == null)
|
||||
{
|
||||
settingsGroup = addressableSettings.CreateGroup("Settings", false, false, true, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAssetToAddressables(string assetPath, string address)
|
||||
{
|
||||
// Create entry in addressables
|
||||
var guid = AssetDatabase.AssetPathToGUID(assetPath);
|
||||
var entry = addressableSettings.CreateOrMoveEntry(guid, settingsGroup);
|
||||
|
||||
// Set the address
|
||||
entry.address = address;
|
||||
|
||||
Debug.Log($"Added {assetPath} to Addressables with address {address}");
|
||||
}
|
||||
|
||||
private void CreatePlayerFollowerSettings()
|
||||
{
|
||||
// Create the settings asset
|
||||
var settings = ScriptableObject.CreateInstance<PlayerFollowerSettings>();
|
||||
|
||||
// Copy values from legacy settings
|
||||
if (legacySettings != null)
|
||||
{
|
||||
// Player settings
|
||||
var playerSettings = typeof(GameSettings).GetField("moveSpeed");
|
||||
if (playerSettings != null) settings.GetType().GetField("moveSpeed", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.moveSpeed);
|
||||
|
||||
var stopDistanceField = typeof(GameSettings).GetField("stopDistance");
|
||||
if (stopDistanceField != null) settings.GetType().GetField("stopDistance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.stopDistance);
|
||||
|
||||
var useRigidbodyField = typeof(GameSettings).GetField("useRigidbody");
|
||||
if (useRigidbodyField != null) settings.GetType().GetField("useRigidbody", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.useRigidbody);
|
||||
|
||||
var defaultHoldMovementModeField = typeof(GameSettings).GetField("defaultHoldMovementMode");
|
||||
if (defaultHoldMovementModeField != null) settings.GetType().GetField("defaultHoldMovementMode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.defaultHoldMovementMode);
|
||||
|
||||
// Follower settings
|
||||
var followDistanceField = typeof(GameSettings).GetField("followDistance");
|
||||
if (followDistanceField != null) settings.GetType().GetField("followDistance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.followDistance);
|
||||
|
||||
var manualMoveSmoothField = typeof(GameSettings).GetField("manualMoveSmooth");
|
||||
if (manualMoveSmoothField != null) settings.GetType().GetField("manualMoveSmooth", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.manualMoveSmooth);
|
||||
|
||||
var thresholdFarField = typeof(GameSettings).GetField("thresholdFar");
|
||||
if (thresholdFarField != null) settings.GetType().GetField("thresholdFar", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.thresholdFar);
|
||||
|
||||
var thresholdNearField = typeof(GameSettings).GetField("thresholdNear");
|
||||
if (thresholdNearField != null) settings.GetType().GetField("thresholdNear", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.thresholdNear);
|
||||
|
||||
var stopThresholdField = typeof(GameSettings).GetField("stopThreshold");
|
||||
if (stopThresholdField != null) settings.GetType().GetField("stopThreshold", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.stopThreshold);
|
||||
|
||||
// Backend settings
|
||||
var followUpdateIntervalField = typeof(GameSettings).GetField("followUpdateInterval");
|
||||
if (followUpdateIntervalField != null) settings.GetType().GetField("followUpdateInterval", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.followUpdateInterval);
|
||||
|
||||
var followerSpeedMultiplierField = typeof(GameSettings).GetField("followerSpeedMultiplier");
|
||||
if (followerSpeedMultiplierField != null) settings.GetType().GetField("followerSpeedMultiplier", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.followerSpeedMultiplier);
|
||||
|
||||
var heldIconDisplayHeightField = typeof(GameSettings).GetField("heldIconDisplayHeight");
|
||||
if (heldIconDisplayHeightField != null) settings.GetType().GetField("heldIconDisplayHeight", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.heldIconDisplayHeight);
|
||||
}
|
||||
|
||||
// Save the asset
|
||||
string assetPath = "Assets/Settings/PlayerFollowerSettings.asset";
|
||||
AssetDatabase.CreateAsset(settings, assetPath);
|
||||
|
||||
// Add to addressables
|
||||
AddAssetToAddressables(assetPath, "Settings/PlayerFollowerSettings");
|
||||
|
||||
Debug.Log("Created PlayerFollowerSettings asset");
|
||||
}
|
||||
|
||||
private void CreateInteractionSettings()
|
||||
{
|
||||
// Create the settings asset
|
||||
var settings = ScriptableObject.CreateInstance<InteractionSettings>();
|
||||
|
||||
// Copy values from legacy settings
|
||||
if (legacySettings != null)
|
||||
{
|
||||
// Interaction settings
|
||||
var playerStopDistanceField = typeof(GameSettings).GetField("playerStopDistance");
|
||||
if (playerStopDistanceField != null) settings.GetType().GetField("playerStopDistance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.playerStopDistance);
|
||||
|
||||
var playerStopDistanceDirectInteractionField = typeof(GameSettings).GetField("playerStopDistanceDirectInteraction");
|
||||
if (playerStopDistanceDirectInteractionField != null) settings.GetType().GetField("playerStopDistanceDirectInteraction", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.playerStopDistanceDirectInteraction);
|
||||
|
||||
var followerPickupDelayField = typeof(GameSettings).GetField("followerPickupDelay");
|
||||
if (followerPickupDelayField != null) settings.GetType().GetField("followerPickupDelay", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.followerPickupDelay);
|
||||
|
||||
var interactableLayerMaskField = typeof(GameSettings).GetField("interactableLayerMask");
|
||||
if (interactableLayerMaskField != null) settings.GetType().GetField("interactableLayerMask", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.interactableLayerMask);
|
||||
|
||||
// Prefabs
|
||||
var basePickupPrefabField = typeof(GameSettings).GetField("basePickupPrefab");
|
||||
if (basePickupPrefabField != null) settings.GetType().GetField("basePickupPrefab", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.basePickupPrefab);
|
||||
|
||||
var levelSwitchMenuPrefabField = typeof(GameSettings).GetField("levelSwitchMenuPrefab");
|
||||
if (levelSwitchMenuPrefabField != null) settings.GetType().GetField("levelSwitchMenuPrefab", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.levelSwitchMenuPrefab);
|
||||
|
||||
// Item configuration
|
||||
var combinationRulesField = typeof(GameSettings).GetField("combinationRules");
|
||||
if (combinationRulesField != null) settings.GetType().GetField("combinationRules", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.combinationRules);
|
||||
|
||||
var slotItemConfigsField = typeof(GameSettings).GetField("slotItemConfigs");
|
||||
if (slotItemConfigsField != null) settings.GetType().GetField("slotItemConfigs", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.slotItemConfigs);
|
||||
}
|
||||
|
||||
// Save the asset
|
||||
string assetPath = "Assets/Settings/InteractionSettings.asset";
|
||||
AssetDatabase.CreateAsset(settings, assetPath);
|
||||
|
||||
// Add to addressables
|
||||
AddAssetToAddressables(assetPath, "Settings/InteractionSettings");
|
||||
|
||||
Debug.Log("Created InteractionSettings asset");
|
||||
}
|
||||
|
||||
private void CreateMinigameSettings()
|
||||
{
|
||||
// Create the settings asset
|
||||
var settings = ScriptableObject.CreateInstance<MinigameSettings>();
|
||||
|
||||
// Copy values from legacy settings
|
||||
if (legacySettings != null)
|
||||
{
|
||||
// Endless descender settings
|
||||
var endlessDescenderLerpSpeedField = typeof(GameSettings).GetField("endlessDescenderLerpSpeed");
|
||||
if (endlessDescenderLerpSpeedField != null) settings.GetType().GetField("endlessDescenderLerpSpeed", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderLerpSpeed);
|
||||
|
||||
var endlessDescenderMaxOffsetField = typeof(GameSettings).GetField("endlessDescenderMaxOffset");
|
||||
if (endlessDescenderMaxOffsetField != null) settings.GetType().GetField("endlessDescenderMaxOffset", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderMaxOffset);
|
||||
|
||||
var endlessDescenderClampXMinField = typeof(GameSettings).GetField("endlessDescenderClampXMin");
|
||||
if (endlessDescenderClampXMinField != null) settings.GetType().GetField("endlessDescenderClampXMin", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderClampXMin);
|
||||
|
||||
var endlessDescenderClampXMaxField = typeof(GameSettings).GetField("endlessDescenderClampXMax");
|
||||
if (endlessDescenderClampXMaxField != null) settings.GetType().GetField("endlessDescenderClampXMax", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderClampXMax);
|
||||
|
||||
var endlessDescenderSpeedExponentField = typeof(GameSettings).GetField("endlessDescenderSpeedExponent");
|
||||
if (endlessDescenderSpeedExponentField != null) settings.GetType().GetField("endlessDescenderSpeedExponent", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderSpeedExponent);
|
||||
}
|
||||
|
||||
// Save the asset
|
||||
string assetPath = "Assets/Settings/MinigameSettings.asset";
|
||||
AssetDatabase.CreateAsset(settings, assetPath);
|
||||
|
||||
// Add to addressables
|
||||
AddAssetToAddressables(assetPath, "Settings/MinigameSettings");
|
||||
|
||||
Debug.Log("Created MinigameSettings asset");
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Editor/SettingsMigrationWindow.cs.meta
Normal file
3
Assets/Editor/SettingsMigrationWindow.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b16caebbe9934df3a34f0b75879e65f2
|
||||
timeCreated: 1758630926
|
||||
@@ -1,4 +1,6 @@
|
||||
using UnityEngine;
|
||||
using AppleHills.Core.Settings;
|
||||
using System.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton manager for global game state and settings. Provides accessors for various gameplay parameters.
|
||||
@@ -29,53 +31,115 @@ public class GameManager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
[Header("Game Settings")]
|
||||
public GameSettings gameSettings;
|
||||
[Header("Legacy Game Settings (Deprecated)")]
|
||||
[Tooltip("This is only used for migration to the new settings system")]
|
||||
public GameSettings legacyGameSettings;
|
||||
|
||||
[Header("Settings Status")]
|
||||
[SerializeField] private bool _settingsLoaded = false;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_instance = this;
|
||||
if (gameSettings == null)
|
||||
{
|
||||
gameSettings = Resources.Load<GameSettings>("DefaultSettings");
|
||||
if (gameSettings == null)
|
||||
{
|
||||
Debug.LogError("GameSettings asset not found in Resources!");
|
||||
}
|
||||
}
|
||||
|
||||
// Create settings provider if it doesn't exist
|
||||
SettingsProvider.Instance.gameObject.name = "Settings Provider";
|
||||
|
||||
// Load all settings
|
||||
StartCoroutine(InitializeSettings());
|
||||
|
||||
// DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
private IEnumerator InitializeSettings()
|
||||
{
|
||||
// Initialize the settings provider
|
||||
var initComplete = false;
|
||||
SettingsProvider.Instance.PreloadAllSettings(() => initComplete = true);
|
||||
|
||||
// Wait for settings to be loaded
|
||||
while (!initComplete)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Register settings with service locator
|
||||
ServiceLocator.Register<IPlayerFollowerSettings>(
|
||||
SettingsProvider.Instance.GetSettings<PlayerFollowerSettings>());
|
||||
|
||||
ServiceLocator.Register<IInteractionSettings>(
|
||||
SettingsProvider.Instance.GetSettings<InteractionSettings>());
|
||||
|
||||
ServiceLocator.Register<IMinigameSettings>(
|
||||
SettingsProvider.Instance.GetSettings<MinigameSettings>());
|
||||
|
||||
// Log success
|
||||
Debug.Log("All settings loaded and registered with ServiceLocator");
|
||||
_settingsLoaded = true;
|
||||
|
||||
// Migrate settings if needed
|
||||
if (legacyGameSettings != null)
|
||||
{
|
||||
MigrateFromLegacySettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void MigrateFromLegacySettings()
|
||||
{
|
||||
// This method can be used to copy settings from the old GameSettings to the new system
|
||||
// Implement if needed for your production environment
|
||||
Debug.Log("Legacy settings migration available but not implemented.");
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
_isQuitting = true;
|
||||
ServiceLocator.Clear();
|
||||
}
|
||||
|
||||
// Accessors for game settings
|
||||
public float PlayerStopDistance => gameSettings != null ? gameSettings.playerStopDistance : 1.0f;
|
||||
public float FollowerPickupDelay => gameSettings != null ? gameSettings.followerPickupDelay : 0.2f;
|
||||
public float FollowDistance => gameSettings != null ? gameSettings.followDistance : 1.5f;
|
||||
public float ManualMoveSmooth => gameSettings != null ? gameSettings.manualMoveSmooth : 8f;
|
||||
public float ThresholdFar => gameSettings != null ? gameSettings.thresholdFar : 2.5f;
|
||||
public float ThresholdNear => gameSettings != null ? gameSettings.thresholdNear : 0.5f;
|
||||
public float StopThreshold => gameSettings != null ? gameSettings.stopThreshold : 0.5f;
|
||||
public float MoveSpeed => gameSettings != null ? gameSettings.moveSpeed : 5f;
|
||||
public float StopDistance => gameSettings != null ? gameSettings.stopDistance : 0.1f;
|
||||
public bool UseRigidbody => gameSettings != null ? gameSettings.useRigidbody : true;
|
||||
public float FollowUpdateInterval => gameSettings != null ? gameSettings.followUpdateInterval : 0.1f;
|
||||
public float FollowerSpeedMultiplier => gameSettings != null ? gameSettings.followerSpeedMultiplier : 1.2f;
|
||||
public float HeldIconDisplayHeight => gameSettings != null ? gameSettings.heldIconDisplayHeight : 2.0f;
|
||||
public GameObject BasePickupPrefab => gameSettings != null ? gameSettings.basePickupPrefab : null;
|
||||
public LayerMask InteractableLayerMask => gameSettings != null ? gameSettings.interactableLayerMask : -1;
|
||||
public float PlayerStopDistanceDirectInteraction => gameSettings != null ? gameSettings.playerStopDistanceDirectInteraction : 2.0f;
|
||||
// Helper method to get settings
|
||||
private T GetSettings<T>() where T : class
|
||||
{
|
||||
return ServiceLocator.Get<T>();
|
||||
}
|
||||
|
||||
// PLAYER & FOLLOWER SETTINGS
|
||||
|
||||
// Player settings
|
||||
public float MoveSpeed => GetSettings<IPlayerFollowerSettings>()?.MoveSpeed ?? 5f;
|
||||
public float StopDistance => GetSettings<IPlayerFollowerSettings>()?.StopDistance ?? 0.1f;
|
||||
public bool UseRigidbody => GetSettings<IPlayerFollowerSettings>()?.UseRigidbody ?? true;
|
||||
public GameSettings.HoldMovementMode DefaultHoldMovementMode =>
|
||||
GetSettings<IPlayerFollowerSettings>()?.DefaultHoldMovementMode ?? GameSettings.HoldMovementMode.Pathfinding;
|
||||
|
||||
// Follower settings
|
||||
public float FollowDistance => GetSettings<IPlayerFollowerSettings>()?.FollowDistance ?? 1.5f;
|
||||
public float ManualMoveSmooth => GetSettings<IPlayerFollowerSettings>()?.ManualMoveSmooth ?? 8f;
|
||||
public float ThresholdFar => GetSettings<IPlayerFollowerSettings>()?.ThresholdFar ?? 2.5f;
|
||||
public float ThresholdNear => GetSettings<IPlayerFollowerSettings>()?.ThresholdNear ?? 0.5f;
|
||||
public float StopThreshold => GetSettings<IPlayerFollowerSettings>()?.StopThreshold ?? 0.1f;
|
||||
public float FollowUpdateInterval => GetSettings<IPlayerFollowerSettings>()?.FollowUpdateInterval ?? 0.1f;
|
||||
public float FollowerSpeedMultiplier => GetSettings<IPlayerFollowerSettings>()?.FollowerSpeedMultiplier ?? 1.2f;
|
||||
public float HeldIconDisplayHeight => GetSettings<IPlayerFollowerSettings>()?.HeldIconDisplayHeight ?? 2.0f;
|
||||
|
||||
// INTERACTION SETTINGS
|
||||
|
||||
public float PlayerStopDistance => GetSettings<IInteractionSettings>()?.PlayerStopDistance ?? 6.0f;
|
||||
public float PlayerStopDistanceDirectInteraction => GetSettings<IInteractionSettings>()?.PlayerStopDistanceDirectInteraction ?? 2.0f;
|
||||
public float FollowerPickupDelay => GetSettings<IInteractionSettings>()?.FollowerPickupDelay ?? 0.2f;
|
||||
public LayerMask InteractableLayerMask => GetSettings<IInteractionSettings>()?.InteractableLayerMask ?? -1;
|
||||
public GameObject BasePickupPrefab => GetSettings<IInteractionSettings>()?.BasePickupPrefab;
|
||||
public GameObject LevelSwitchMenuPrefab => GetSettings<IInteractionSettings>()?.LevelSwitchMenuPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the combination rule for two items, if any.
|
||||
/// </summary>
|
||||
public GameSettings.CombinationRule GetCombinationRule(PickupItemData item1, PickupItemData item2)
|
||||
{
|
||||
if (gameSettings == null || gameSettings.combinationRules == null) return null;
|
||||
foreach (var rule in gameSettings.combinationRules)
|
||||
var settings = GetSettings<IInteractionSettings>();
|
||||
if (settings == null || settings.CombinationRules == null) return null;
|
||||
|
||||
foreach (var rule in settings.CombinationRules)
|
||||
{
|
||||
if ((PickupItemData.AreEquivalent(rule.itemA, item1) && PickupItemData.AreEquivalent(rule.itemB, item2)) ||
|
||||
(PickupItemData.AreEquivalent(rule.itemA, item2) && PickupItemData.AreEquivalent(rule.itemB, item1)))
|
||||
@@ -91,20 +155,23 @@ public class GameManager : MonoBehaviour
|
||||
/// </summary>
|
||||
public GameSettings.SlotItemConfig GetSlotItemConfig(PickupItemData slotItem)
|
||||
{
|
||||
if (gameSettings == null || gameSettings.slotItemConfigs == null || slotItem == null) return null;
|
||||
foreach (var config in gameSettings.slotItemConfigs)
|
||||
var settings = GetSettings<IInteractionSettings>();
|
||||
if (settings == null || settings.SlotItemConfigs == null || slotItem == null) return null;
|
||||
|
||||
foreach (var config in settings.SlotItemConfigs)
|
||||
{
|
||||
if (PickupItemData.AreEquivalent(slotItem, config.slotItem))
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Add more accessors as needed
|
||||
public float EndlessDescenderLerpSpeed => gameSettings != null ? gameSettings.endlessDescenderLerpSpeed : 12f;
|
||||
public float EndlessDescenderMaxOffset => gameSettings != null ? gameSettings.endlessDescenderMaxOffset : 3f;
|
||||
public float EndlessDescenderClampXMin => gameSettings != null ? gameSettings.endlessDescenderClampXMin : -5f;
|
||||
public float EndlessDescenderClampXMax => gameSettings != null ? gameSettings.endlessDescenderClampXMax : 5f;
|
||||
public float EndlessDescenderSpeedExponent => gameSettings != null ? gameSettings.endlessDescenderSpeedExponent : 2.5f;
|
||||
public GameSettings.HoldMovementMode DefaultHoldMovementMode => gameSettings != null ? gameSettings.defaultHoldMovementMode : GameSettings.HoldMovementMode.Pathfinding;
|
||||
public GameObject LevelSwitchMenuPrefab => gameSettings != null ? gameSettings.levelSwitchMenuPrefab : null;
|
||||
|
||||
// MINIGAME SETTINGS
|
||||
|
||||
// Endless Descender settings
|
||||
public float EndlessDescenderLerpSpeed => GetSettings<IMinigameSettings>()?.EndlessDescenderLerpSpeed ?? 12f;
|
||||
public float EndlessDescenderMaxOffset => GetSettings<IMinigameSettings>()?.EndlessDescenderMaxOffset ?? 3f;
|
||||
public float EndlessDescenderClampXMin => GetSettings<IMinigameSettings>()?.EndlessDescenderClampXMin ?? -3.5f;
|
||||
public float EndlessDescenderClampXMax => GetSettings<IMinigameSettings>()?.EndlessDescenderClampXMax ?? 3.5f;
|
||||
public float EndlessDescenderSpeedExponent => GetSettings<IMinigameSettings>()?.EndlessDescenderSpeedExponent ?? 2.5f;
|
||||
}
|
||||
|
||||
3
Assets/Scripts/Core/Settings.meta
Normal file
3
Assets/Scripts/Core/Settings.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e380783135324fcd925048783e01d691
|
||||
timeCreated: 1758619858
|
||||
16
Assets/Scripts/Core/Settings/BaseSettings.cs
Normal file
16
Assets/Scripts/Core/Settings/BaseSettings.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppleHills.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all settings ScriptableObjects.
|
||||
/// Provides common functionality for all settings types.
|
||||
/// </summary>
|
||||
public abstract class BaseSettings : ScriptableObject
|
||||
{
|
||||
public virtual void OnValidate()
|
||||
{
|
||||
// Override in derived classes to add validation
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Core/Settings/BaseSettings.cs.meta
Normal file
3
Assets/Scripts/Core/Settings/BaseSettings.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd33ef6036eb49358acbbd50dfd9bb13
|
||||
timeCreated: 1758619858
|
||||
48
Assets/Scripts/Core/Settings/InteractionSettings.cs
Normal file
48
Assets/Scripts/Core/Settings/InteractionSettings.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppleHills.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings related to interactions and items
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "InteractionSettings", menuName = "AppleHills/Settings/Interaction & Items", order = 2)]
|
||||
public class InteractionSettings : BaseSettings, IInteractionSettings
|
||||
{
|
||||
[Header("Interactions")]
|
||||
[SerializeField] private float playerStopDistance = 6.0f;
|
||||
[SerializeField] private float playerStopDistanceDirectInteraction = 2.0f;
|
||||
[SerializeField] private float followerPickupDelay = 0.2f;
|
||||
|
||||
[Header("InputManager Settings")]
|
||||
[Tooltip("Layer(s) to use for interactable objects.")]
|
||||
[SerializeField] private LayerMask interactableLayerMask = -1; // Default to Everything
|
||||
|
||||
[Header("Default Prefabs")]
|
||||
[SerializeField] private GameObject basePickupPrefab;
|
||||
[SerializeField] private GameObject levelSwitchMenuPrefab;
|
||||
|
||||
[Header("Item Configuration")]
|
||||
[SerializeField] private List<GameSettings.CombinationRule> combinationRules = new List<GameSettings.CombinationRule>();
|
||||
[SerializeField] private List<GameSettings.SlotItemConfig> slotItemConfigs = new List<GameSettings.SlotItemConfig>();
|
||||
|
||||
// IInteractionSettings implementation
|
||||
public float PlayerStopDistance => playerStopDistance;
|
||||
public float PlayerStopDistanceDirectInteraction => playerStopDistanceDirectInteraction;
|
||||
public float FollowerPickupDelay => followerPickupDelay;
|
||||
public LayerMask InteractableLayerMask => interactableLayerMask;
|
||||
public GameObject BasePickupPrefab => basePickupPrefab;
|
||||
public GameObject LevelSwitchMenuPrefab => levelSwitchMenuPrefab;
|
||||
public List<GameSettings.CombinationRule> CombinationRules => combinationRules;
|
||||
public List<GameSettings.SlotItemConfig> SlotItemConfigs => slotItemConfigs;
|
||||
|
||||
public override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
// Validate values
|
||||
playerStopDistance = Mathf.Max(0.1f, playerStopDistance);
|
||||
playerStopDistanceDirectInteraction = Mathf.Max(0.1f, playerStopDistanceDirectInteraction);
|
||||
followerPickupDelay = Mathf.Max(0f, followerPickupDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Core/Settings/InteractionSettings.cs.meta
Normal file
3
Assets/Scripts/Core/Settings/InteractionSettings.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac22b092dc6f4db5b3dad35172b6e4c4
|
||||
timeCreated: 1758619914
|
||||
49
Assets/Scripts/Core/Settings/MinigameSettings.cs
Normal file
49
Assets/Scripts/Core/Settings/MinigameSettings.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppleHills.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings related to minigames
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "MinigameSettings", menuName = "AppleHills/Settings/Minigames", order = 3)]
|
||||
public class MinigameSettings : BaseSettings, IMinigameSettings
|
||||
{
|
||||
[Header("Endless Descender Settings")]
|
||||
[Tooltip("How quickly the character follows the finger horizontally (higher = more responsive)")]
|
||||
[SerializeField] private float endlessDescenderLerpSpeed = 12f;
|
||||
|
||||
[Tooltip("Maximum horizontal offset allowed between character and finger position")]
|
||||
[SerializeField] private float endlessDescenderMaxOffset = 3f;
|
||||
|
||||
[Tooltip("Minimum allowed X position for endless descender movement")]
|
||||
[SerializeField] private float endlessDescenderClampXMin = -3.5f;
|
||||
|
||||
[Tooltip("Maximum allowed X position for endless descender movement")]
|
||||
[SerializeField] private float endlessDescenderClampXMax = 3.5f;
|
||||
|
||||
[Tooltip("Exponent for speed drop-off curve (higher = sharper drop near target)")]
|
||||
[SerializeField] private float endlessDescenderSpeedExponent = 2.5f;
|
||||
|
||||
// IMinigameSettings implementation
|
||||
public float EndlessDescenderLerpSpeed => endlessDescenderLerpSpeed;
|
||||
public float EndlessDescenderMaxOffset => endlessDescenderMaxOffset;
|
||||
public float EndlessDescenderClampXMin => endlessDescenderClampXMin;
|
||||
public float EndlessDescenderClampXMax => endlessDescenderClampXMax;
|
||||
public float EndlessDescenderSpeedExponent => endlessDescenderSpeedExponent;
|
||||
|
||||
public override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
// Validate values
|
||||
endlessDescenderLerpSpeed = Mathf.Max(0.1f, endlessDescenderLerpSpeed);
|
||||
endlessDescenderMaxOffset = Mathf.Max(0.1f, endlessDescenderMaxOffset);
|
||||
endlessDescenderSpeedExponent = Mathf.Max(0.1f, endlessDescenderSpeedExponent);
|
||||
|
||||
// Ensure min is less than max
|
||||
if (endlessDescenderClampXMin >= endlessDescenderClampXMax)
|
||||
{
|
||||
endlessDescenderClampXMin = endlessDescenderClampXMax - 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Core/Settings/MinigameSettings.cs.meta
Normal file
3
Assets/Scripts/Core/Settings/MinigameSettings.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ce4dba7a1c54e73b1b3d7131a1c0570
|
||||
timeCreated: 1758619927
|
||||
53
Assets/Scripts/Core/Settings/PlayerFollowerSettings.cs
Normal file
53
Assets/Scripts/Core/Settings/PlayerFollowerSettings.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppleHills.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings related to player and follower behavior
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "PlayerFollowerSettings", menuName = "AppleHills/Settings/Player & Follower", order = 1)]
|
||||
public class PlayerFollowerSettings : BaseSettings, IPlayerFollowerSettings
|
||||
{
|
||||
[Header("Player Settings")]
|
||||
[SerializeField] private float moveSpeed = 5f;
|
||||
[SerializeField] private float stopDistance = 0.1f;
|
||||
[SerializeField] private bool useRigidbody = true;
|
||||
[SerializeField] private GameSettings.HoldMovementMode defaultHoldMovementMode = GameSettings.HoldMovementMode.Pathfinding;
|
||||
|
||||
[Header("Follower Settings")]
|
||||
[SerializeField] private float followDistance = 1.5f;
|
||||
[SerializeField] private float manualMoveSmooth = 8f;
|
||||
[SerializeField] private float thresholdFar = 2.5f;
|
||||
[SerializeField] private float thresholdNear = 0.5f;
|
||||
[SerializeField] private float stopThreshold = 0.1f;
|
||||
|
||||
[Header("Backend Settings")]
|
||||
[Tooltip("Technical parameters, not for design tuning")]
|
||||
[SerializeField] private float followUpdateInterval = 0.1f;
|
||||
[SerializeField] private float followerSpeedMultiplier = 1.2f;
|
||||
[SerializeField] private float heldIconDisplayHeight = 2.0f;
|
||||
|
||||
// IPlayerFollowerSettings implementation
|
||||
public float MoveSpeed => moveSpeed;
|
||||
public float StopDistance => stopDistance;
|
||||
public bool UseRigidbody => useRigidbody;
|
||||
public GameSettings.HoldMovementMode DefaultHoldMovementMode => defaultHoldMovementMode;
|
||||
public float FollowDistance => followDistance;
|
||||
public float ManualMoveSmooth => manualMoveSmooth;
|
||||
public float ThresholdFar => thresholdFar;
|
||||
public float ThresholdNear => thresholdNear;
|
||||
public float StopThreshold => stopThreshold;
|
||||
public float FollowUpdateInterval => followUpdateInterval;
|
||||
public float FollowerSpeedMultiplier => followerSpeedMultiplier;
|
||||
public float HeldIconDisplayHeight => heldIconDisplayHeight;
|
||||
|
||||
public override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
// Validate values
|
||||
moveSpeed = Mathf.Max(0.1f, moveSpeed);
|
||||
followDistance = Mathf.Max(0.1f, followDistance);
|
||||
followerSpeedMultiplier = Mathf.Max(0.1f, followerSpeedMultiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32cd6d14d9304d5ba0fd590da1346654
|
||||
timeCreated: 1758619904
|
||||
51
Assets/Scripts/Core/Settings/ServiceLocator.cs
Normal file
51
Assets/Scripts/Core/Settings/ServiceLocator.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppleHills.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Service Locator implementation for managing settings services.
|
||||
/// Provides a central registry for all settings services.
|
||||
/// </summary>
|
||||
public static class ServiceLocator
|
||||
{
|
||||
private static readonly Dictionary<Type, object> _services = new Dictionary<Type, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Register a service with the service locator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The interface type for the service</typeparam>
|
||||
/// <param name="service">The service implementation</param>
|
||||
public static void Register<T>(T service) where T : class
|
||||
{
|
||||
_services[typeof(T)] = service;
|
||||
Debug.Log($"Service registered: {typeof(T).Name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a service from the service locator.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The interface type for the service</typeparam>
|
||||
/// <returns>The service implementation, or null if not found</returns>
|
||||
public static T Get<T>() where T : class
|
||||
{
|
||||
if (_services.TryGetValue(typeof(T), out object service))
|
||||
{
|
||||
return service as T;
|
||||
}
|
||||
|
||||
Debug.LogWarning($"Service of type {typeof(T).Name} not found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all registered services.
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
_services.Clear();
|
||||
Debug.Log("All services cleared");
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Core/Settings/ServiceLocator.cs.meta
Normal file
3
Assets/Scripts/Core/Settings/ServiceLocator.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16cc39d2f99b4e7fa65c4a8b39f3e87c
|
||||
timeCreated: 1758619866
|
||||
54
Assets/Scripts/Core/Settings/SettingsInterfaces.cs
Normal file
54
Assets/Scripts/Core/Settings/SettingsInterfaces.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace AppleHills.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for player and follower settings
|
||||
/// </summary>
|
||||
public interface IPlayerFollowerSettings
|
||||
{
|
||||
// Player settings
|
||||
float MoveSpeed { get; }
|
||||
float StopDistance { get; }
|
||||
bool UseRigidbody { get; }
|
||||
GameSettings.HoldMovementMode DefaultHoldMovementMode { get; }
|
||||
|
||||
// Follower settings
|
||||
float FollowDistance { get; }
|
||||
float ManualMoveSmooth { get; }
|
||||
float ThresholdFar { get; }
|
||||
float ThresholdNear { get; }
|
||||
float StopThreshold { get; }
|
||||
float FollowUpdateInterval { get; }
|
||||
float FollowerSpeedMultiplier { get; }
|
||||
float HeldIconDisplayHeight { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for interaction and item settings
|
||||
/// </summary>
|
||||
public interface IInteractionSettings
|
||||
{
|
||||
float PlayerStopDistance { get; }
|
||||
float PlayerStopDistanceDirectInteraction { get; }
|
||||
float FollowerPickupDelay { get; }
|
||||
LayerMask InteractableLayerMask { get; }
|
||||
GameObject BasePickupPrefab { get; }
|
||||
GameObject LevelSwitchMenuPrefab { get; }
|
||||
System.Collections.Generic.List<GameSettings.CombinationRule> CombinationRules { get; }
|
||||
System.Collections.Generic.List<GameSettings.SlotItemConfig> SlotItemConfigs { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for minigame settings
|
||||
/// </summary>
|
||||
public interface IMinigameSettings
|
||||
{
|
||||
// Endless Descender settings
|
||||
float EndlessDescenderLerpSpeed { get; }
|
||||
float EndlessDescenderMaxOffset { get; }
|
||||
float EndlessDescenderClampXMin { get; }
|
||||
float EndlessDescenderClampXMax { get; }
|
||||
float EndlessDescenderSpeedExponent { get; }
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Core/Settings/SettingsInterfaces.cs.meta
Normal file
3
Assets/Scripts/Core/Settings/SettingsInterfaces.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54611ae012ab4455a53bd60961d9e7ea
|
||||
timeCreated: 1758619892
|
||||
106
Assets/Scripts/Core/Settings/SettingsProvider.cs
Normal file
106
Assets/Scripts/Core/Settings/SettingsProvider.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace AppleHills.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Responsible for loading and caching settings from Addressables.
|
||||
/// </summary>
|
||||
public class SettingsProvider : MonoBehaviour
|
||||
{
|
||||
private static SettingsProvider _instance;
|
||||
private Dictionary<string, BaseSettings> _settingsCache = new Dictionary<string, BaseSettings>();
|
||||
|
||||
// Singleton instance
|
||||
public static SettingsProvider Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
GameObject go = new GameObject("Settings Provider");
|
||||
_instance = go.AddComponent<SettingsProvider>();
|
||||
DontDestroyOnLoad(go);
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
else if (_instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load settings asynchronously using Addressables
|
||||
/// </summary>
|
||||
public void LoadSettings<T>(Action<T> onLoaded) where T : BaseSettings
|
||||
{
|
||||
string key = typeof(T).Name;
|
||||
|
||||
// Return from cache if already loaded
|
||||
if (_settingsCache.TryGetValue(key, out BaseSettings cachedSettings))
|
||||
{
|
||||
onLoaded?.Invoke(cachedSettings as T);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load using Addressables
|
||||
Addressables.LoadAssetAsync<T>($"Settings/{key}.asset").Completed += handle =>
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
_settingsCache[key] = handle.Result;
|
||||
onLoaded?.Invoke(handle.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Failed to load settings: {key}");
|
||||
onLoaded?.Invoke(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get cached settings
|
||||
/// </summary>
|
||||
public T GetSettings<T>() where T : BaseSettings
|
||||
{
|
||||
string key = typeof(T).Name;
|
||||
if (_settingsCache.TryGetValue(key, out BaseSettings settings))
|
||||
{
|
||||
return settings as T;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preload all settings - call this at game startup
|
||||
/// </summary>
|
||||
public void PreloadAllSettings(Action onComplete)
|
||||
{
|
||||
// Load all necessary settings types
|
||||
int pendingLoads = 3; // Number of settings types
|
||||
Action decrementCounter = () => {
|
||||
pendingLoads--;
|
||||
if (pendingLoads <= 0)
|
||||
onComplete?.Invoke();
|
||||
};
|
||||
|
||||
LoadSettings<PlayerFollowerSettings>(settings => decrementCounter());
|
||||
LoadSettings<InteractionSettings>(settings => decrementCounter());
|
||||
LoadSettings<MinigameSettings>(settings => decrementCounter());
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Core/Settings/SettingsProvider.cs.meta
Normal file
3
Assets/Scripts/Core/Settings/SettingsProvider.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d212b25192045d198f2bf42ef74f278
|
||||
timeCreated: 1758619879
|
||||
50
Assets/Settings/InteractionSettings.asset
Normal file
50
Assets/Settings/InteractionSettings.asset
Normal file
@@ -0,0 +1,50 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ac22b092dc6f4db5b3dad35172b6e4c4, type: 3}
|
||||
m_Name: InteractionSettings
|
||||
m_EditorClassIdentifier:
|
||||
playerStopDistance: 10
|
||||
playerStopDistanceDirectInteraction: 2
|
||||
followerPickupDelay: 0.2
|
||||
interactableLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1024
|
||||
basePickupPrefab: {fileID: 7447346505753002421, guid: bf4b9d7045397f946b2125b1ad4a3fbd, type: 3}
|
||||
levelSwitchMenuPrefab: {fileID: 4062459998181038721, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
|
||||
combinationRules:
|
||||
- itemA: {fileID: 11400000, guid: 33e7ca06b22108d4e802486e08bcdfd1, type: 2}
|
||||
itemB: {fileID: 11400000, guid: 8b2616beb14825a46b9b1ed85ad3cb25, type: 2}
|
||||
resultPrefab: {fileID: 1610562450228973293, guid: 58654125374567147839eb382fcde422, type: 3}
|
||||
- itemA: {fileID: 11400000, guid: 983414276ae3f004c854e9c450f27f88, type: 2}
|
||||
itemB: {fileID: 11400000, guid: 0c6986639ca176a419c92f5a327d95ce, type: 2}
|
||||
resultPrefab: {fileID: 5562196803416682102, guid: bb505cdbd2463b64790deffc6244c55c, type: 3}
|
||||
slotItemConfigs:
|
||||
- slotItem: {fileID: 11400000, guid: e0fad48a84a6b6346ac17c84bc512500, type: 2}
|
||||
allowedItems:
|
||||
- {fileID: 11400000, guid: ecae2d83a5ab2a047a2733ebff607380, type: 2}
|
||||
forbiddenItems: []
|
||||
- slotItem: {fileID: 11400000, guid: f97b9e24d6dceb145b56426c1152ebeb, type: 2}
|
||||
allowedItems:
|
||||
- {fileID: 11400000, guid: ff4bbba87722023468a0f6395d1f96c7, type: 2}
|
||||
forbiddenItems: []
|
||||
- slotItem: {fileID: 11400000, guid: d28f5774afad9d14f823601707150700, type: 2}
|
||||
allowedItems:
|
||||
- {fileID: 11400000, guid: 6934dcb56c610c44da228f7f24ca13c9, type: 2}
|
||||
forbiddenItems: []
|
||||
- slotItem: {fileID: 11400000, guid: aaf36cd26cf74334e9c7db6c1b03b3fb, type: 2}
|
||||
allowedItems:
|
||||
- {fileID: 11400000, guid: c50330645a2b9d549aae3639bdfcea19, type: 2}
|
||||
forbiddenItems: []
|
||||
- slotItem: {fileID: 11400000, guid: c68dea945fecbf44094359769db04f31, type: 2}
|
||||
allowedItems:
|
||||
- {fileID: 11400000, guid: ab57c8237aac144439a18d69f56d36c6, type: 2}
|
||||
forbiddenItems: []
|
||||
8
Assets/Settings/InteractionSettings.asset.meta
Normal file
8
Assets/Settings/InteractionSettings.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f5195fb013895049a19488fd4d8f2a1
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
Assets/Settings/MinigameSettings.asset
Normal file
19
Assets/Settings/MinigameSettings.asset
Normal file
@@ -0,0 +1,19 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0ce4dba7a1c54e73b1b3d7131a1c0570, type: 3}
|
||||
m_Name: MinigameSettings
|
||||
m_EditorClassIdentifier:
|
||||
endlessDescenderLerpSpeed: 2.03
|
||||
endlessDescenderMaxOffset: 10
|
||||
endlessDescenderClampXMin: -3.5
|
||||
endlessDescenderClampXMax: 3.5
|
||||
endlessDescenderSpeedExponent: 0.97
|
||||
8
Assets/Settings/MinigameSettings.asset.meta
Normal file
8
Assets/Settings/MinigameSettings.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9569848f604a6540827d4d4bb0a35c2
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
26
Assets/Settings/PlayerFollowerSettings.asset
Normal file
26
Assets/Settings/PlayerFollowerSettings.asset
Normal file
@@ -0,0 +1,26 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 32cd6d14d9304d5ba0fd590da1346654, type: 3}
|
||||
m_Name: PlayerFollowerSettings
|
||||
m_EditorClassIdentifier:
|
||||
moveSpeed: 25
|
||||
stopDistance: 2
|
||||
useRigidbody: 1
|
||||
defaultHoldMovementMode: 1
|
||||
followDistance: 5
|
||||
manualMoveSmooth: 2
|
||||
thresholdFar: 10
|
||||
thresholdNear: 7
|
||||
stopThreshold: 0.5
|
||||
followUpdateInterval: 0.1
|
||||
followerSpeedMultiplier: 1.2
|
||||
heldIconDisplayHeight: 2
|
||||
8
Assets/Settings/PlayerFollowerSettings.asset.meta
Normal file
8
Assets/Settings/PlayerFollowerSettings.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35bfcff00faa72c4eb272a9e8288f965
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user