diff --git a/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset new file mode 100644 index 00000000..ad30b569 --- /dev/null +++ b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset @@ -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: dea69d41f90c6ea4fa55c27c1d60c145, type: 3} + m_Name: AddressableAssetGroupSortSettings + m_EditorClassIdentifier: + sortOrder: + - eb8a37153d819c44194f7ce97570a3d3 + - 2b3d7cefec0915e42be04aebf0400a56 + - 6f3207429a65b3e4b83935ac19791077 + - c62e6f02418e19949bca4cccdd5fa02e diff --git a/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset.meta b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset.meta new file mode 100644 index 00000000..f0331bb7 --- /dev/null +++ b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 32c1a9c8651793e41848a173d66d98fd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Settings.asset b/Assets/AddressableAssetsData/AssetGroups/Settings.asset index 43153491..c2281311 100644 --- a/Assets/AddressableAssetsData/AssetGroups/Settings.asset +++ b/Assets/AddressableAssetsData/AssetGroups/Settings.asset @@ -15,6 +15,11 @@ MonoBehaviour: m_GroupName: Settings m_GUID: c62e6f02418e19949bca4cccdd5fa02e m_SerializeEntries: + - m_GUID: 328ce914b893df646be3ad3c62755453 + m_Address: Settings/Developer/DivingDeveloperSettings + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 - m_GUID: 35bfcff00faa72c4eb272a9e8288f965 m_Address: Settings/PlayerFollowerSettings m_ReadOnly: 0 diff --git a/Assets/Editor/DeveloperSettingsEditorWindow.cs b/Assets/Editor/DeveloperSettingsEditorWindow.cs new file mode 100644 index 00000000..85935172 --- /dev/null +++ b/Assets/Editor/DeveloperSettingsEditorWindow.cs @@ -0,0 +1,207 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +namespace AppleHills.Core.Settings.Editor +{ + public class DeveloperSettingsEditorWindow : EditorWindow + { + private Vector2 scrollPosition; + private List allDeveloperSettings = new List(); + private string[] tabNames = new string[] { "Diving", "Other Systems" }; // Add more tabs as needed + private int selectedTab = 0; + private Dictionary serializedSettingsObjects = new Dictionary(); + private GUIStyle headerStyle; + + [MenuItem("AppleHills/Developer Settings Editor")] + public static void ShowWindow() + { + GetWindow("Developer Settings"); + } + + private void OnEnable() + { + LoadAllSettings(); + } + + private void LoadAllSettings() + { + allDeveloperSettings.Clear(); + serializedSettingsObjects.Clear(); + + // Find all developer settings assets + string[] guids = AssetDatabase.FindAssets("t:BaseDeveloperSettings"); + foreach (string guid in guids) + { + string path = AssetDatabase.GUIDToAssetPath(guid); + BaseDeveloperSettings settings = AssetDatabase.LoadAssetAtPath(path); + if (settings != null) + { + allDeveloperSettings.Add(settings); + serializedSettingsObjects[settings.GetType().Name] = new SerializedObject(settings); + } + } + + // If any settings are missing, create them + CreateSettingsIfMissing("DivingDeveloperSettings"); + + // Add more developer settings types here as needed + // CreateSettingsIfMissing("OtherDeveloperSettings"); + } + + private void CreateSettingsIfMissing(string fileName) where T : BaseDeveloperSettings + { + if (!allDeveloperSettings.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 + string settingsFolder = "Assets/Settings/Developer"; + if (!AssetDatabase.IsValidFolder("Assets/Settings")) + { + AssetDatabase.CreateFolder("Assets", "Settings"); + } + if (!AssetDatabase.IsValidFolder(settingsFolder)) + { + AssetDatabase.CreateFolder("Assets/Settings", "Developer"); + } + + // Create new settings asset + T settings = CreateInstance(); + string path = $"{settingsFolder}/{fileName}.asset"; + AssetDatabase.CreateAsset(settings, path); + AssetDatabase.SaveAssets(); + + allDeveloperSettings.Add(settings); + serializedSettingsObjects[typeof(T).Name] = new SerializedObject(settings); + Debug.Log($"Created missing developer settings asset: {path}"); + } + else + { + // Load existing asset + string path = AssetDatabase.GUIDToAssetPath(guids[0]); + T settings = AssetDatabase.LoadAssetAtPath(path); + allDeveloperSettings.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 Developer Settings", headerStyle); + EditorGUILayout.HelpBox("This editor is for technical settings intended for developers. For gameplay settings, use the Game Settings editor.", MessageType.Info); + + EditorGUILayout.Space(10); + selectedTab = GUILayout.Toolbar(selectedTab, tabNames); + + scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); + + switch (selectedTab) + { + case 0: // Diving + DrawSettingsEditor(); + break; + case 1: // Other Systems + EditorGUILayout.HelpBox("Other developer settings will appear here as they are added.", MessageType.Info); + break; + // Add additional cases as more developer settings types are added + } + + 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 developer settings saved!"); + } + + EditorGUILayout.EndHorizontal(); + } + + private void DrawSettingsEditor() where T : BaseDeveloperSettings + { + BaseDeveloperSettings settings = allDeveloperSettings.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; + + // Group headers + if (property.isArray && property.propertyType == SerializedPropertyType.Generic) + { + EditorGUILayout.LabelField(property.displayName, EditorStyles.boldLabel); + } + + EditorGUILayout.PropertyField(property, true); + } + + // Apply changes + if (serializedObj.ApplyModifiedProperties()) + { + EditorUtility.SetDirty(settings); + + // Trigger OnValidate on the asset + if (settings != null) + { + settings.OnValidate(); + } + } + } + + // Helper method to highlight important fields + private void DrawHighlightedProperty(SerializedProperty property, string tooltip = null) + { + GUI.backgroundColor = new Color(0.8f, 0.9f, 1f); // Light blue for developer settings + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + GUI.backgroundColor = Color.white; + + EditorGUILayout.PropertyField(property, new GUIContent(property.displayName, tooltip)); + + EditorGUILayout.EndVertical(); + } + } +} diff --git a/Assets/Editor/DeveloperSettingsEditorWindow.cs.meta b/Assets/Editor/DeveloperSettingsEditorWindow.cs.meta new file mode 100644 index 00000000..c99b4494 --- /dev/null +++ b/Assets/Editor/DeveloperSettingsEditorWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8ab6d9fee0924431b8e22edb500b35df +timeCreated: 1758710778 \ No newline at end of file diff --git a/Assets/Editor/LayerPropertyDrawer.cs b/Assets/Editor/LayerPropertyDrawer.cs new file mode 100644 index 00000000..38c24e00 --- /dev/null +++ b/Assets/Editor/LayerPropertyDrawer.cs @@ -0,0 +1,54 @@ +using UnityEngine; +using UnityEditor; + +namespace AppleHills.Core.Settings.Editor +{ + /// + /// Custom property drawer for layer fields to display a dropdown with layer names + /// + [CustomPropertyDrawer(typeof(LayerAttribute))] + public class LayerPropertyDrawer : PropertyDrawer + { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); + + // Draw a nice layer selection dropdown like the one in Unity inspector + if (property.propertyType == SerializedPropertyType.Integer) + { + property.intValue = EditorGUI.LayerField(position, label, property.intValue); + } + else + { + EditorGUI.LabelField(position, label.text, "Use [Layer] with int fields only"); + } + + EditorGUI.EndProperty(); + } + } + + /// + /// Custom property drawer for LayerMask fields to display a mask dropdown with layer names + /// + [CustomPropertyDrawer(typeof(LayerMaskAttribute))] + public class LayerMaskPropertyDrawer : PropertyDrawer + { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); + + // Draw a nice layer mask selection like the one in Unity inspector + if (property.propertyType == SerializedPropertyType.LayerMask) + { + property.intValue = EditorGUI.MaskField(position, label, + property.intValue, UnityEditorInternal.InternalEditorUtility.layers); + } + else + { + EditorGUI.LabelField(position, label.text, "Use [LayerMask] with LayerMask fields only"); + } + + EditorGUI.EndProperty(); + } + } +} diff --git a/Assets/Editor/LayerPropertyDrawer.cs.meta b/Assets/Editor/LayerPropertyDrawer.cs.meta new file mode 100644 index 00000000..8cdac000 --- /dev/null +++ b/Assets/Editor/LayerPropertyDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6c6b274ce7b14e91bf5a294a23ba0609 +timeCreated: 1758711663 \ No newline at end of file diff --git a/Assets/Scripts/Core/Settings/DeveloperSettingsProvider.cs b/Assets/Scripts/Core/Settings/DeveloperSettingsProvider.cs index 2612ed08..d4608552 100644 --- a/Assets/Scripts/Core/Settings/DeveloperSettingsProvider.cs +++ b/Assets/Scripts/Core/Settings/DeveloperSettingsProvider.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using UnityEngine; +using UnityEngine.AddressableAssets; +using System; namespace AppleHills.Core.Settings { @@ -38,8 +40,8 @@ namespace AppleHills.Core.Settings // Dictionary to cache loaded settings private Dictionary _settingsCache = new Dictionary(); - // Default developer settings stored in the Resources folder - [SerializeField] private string _resourcesPath = "Settings/Developer"; + // Path prefix for addressable developer settings + [SerializeField] private string _addressablePath = "Settings/Developer"; private void Awake() { @@ -71,16 +73,35 @@ namespace AppleHills.Core.Settings return cachedSettings as T; } - // Load from Resources if not cached - T settings = Resources.Load($"{_resourcesPath}/{type.Name}"); + // Load from Addressables if not cached + string key = $"{_addressablePath}/{type.Name}"; - if (settings != null) + try { - _settingsCache[type] = settings; - return settings; + T settings = Addressables.LoadAssetAsync(key).WaitForCompletion(); + + if (settings != null) + { + _settingsCache[type] = settings; + return settings; + } + } + catch (Exception e) + { + Debug.LogError($"Failed to load developer settings at '{key}': {e.Message}"); + } + + Debug.LogWarning($"Developer settings of type {type.Name} not found at addressable path '{key}'"); + + // Fallback to Resources for backward compatibility + T resourcesSettings = Resources.Load($"{_addressablePath}/{type.Name}"); + if (resourcesSettings != null) + { + Debug.Log($"Found developer settings in Resources instead of Addressables at '{_addressablePath}/{type.Name}'"); + _settingsCache[type] = resourcesSettings; + return resourcesSettings; } - Debug.LogWarning($"Developer settings of type {type.Name} not found in Resources/{_resourcesPath}"); return null; } diff --git a/Assets/Scripts/Core/Settings/DivingDeveloperSettings.cs b/Assets/Scripts/Core/Settings/DivingDeveloperSettings.cs index 708bd41e..b35e2412 100644 --- a/Assets/Scripts/Core/Settings/DivingDeveloperSettings.cs +++ b/Assets/Scripts/Core/Settings/DivingDeveloperSettings.cs @@ -54,11 +54,9 @@ namespace AppleHills.Core.Settings [Header("Obstacle System")] [Tooltip("Layer for obstacles to be placed on")] + [Layer] [SerializeField] private int obstacleLayer = 9; - [Tooltip("Layer mask for tile collision detection during obstacle spawn validation")] - [SerializeField] private LayerMask obstacleTileLayerMask = -1; - [Tooltip("Whether to use object pooling for obstacles")] [SerializeField] private bool obstacleUseObjectPooling = true; @@ -67,7 +65,73 @@ namespace AppleHills.Core.Settings [Tooltip("Total maximum size of obstacle pool across all prefab types")] [SerializeField] private int obstacleTotalMaxPoolSize = 15; - + + [Header("Trench Tile System")] + [Tooltip("Layer for trench tiles to be placed on")] + [Layer] + [SerializeField] private int trenchTileLayer = 13; // QuarryTrenchTile layer + + [Tooltip("Whether to use object pooling for trench tiles")] + [SerializeField] private bool trenchTileUseObjectPooling = true; + + [Tooltip("Maximum objects per prefab type in trench tile pool")] + [SerializeField] private int trenchTileMaxPerPrefabPoolSize = 2; + + [Tooltip("Total maximum size of trench tile pool across all prefab types")] + [SerializeField] private int trenchTileTotalMaxPoolSize = 10; + + [Header("Player Blink Behavior")] + [Tooltip("Color to blink to when taking damage (typically red for damage indication)")] + [SerializeField] private Color playerBlinkDamageColor = Color.red; + + [Tooltip("How fast to blink between normal and damage colors (seconds between color changes)")] + [SerializeField] private float playerBlinkRate = 0.15f; + + [Tooltip("Alpha value for the damage color (0 = transparent, 1 = opaque)")] + [Range(0f, 1f)] + [SerializeField] private float playerDamageColorAlpha = 0.7f; + + [Header("Player Wobble Behavior")] + [Tooltip("Frequency of wobble (higher = faster rocking)")] + [SerializeField] private float playerWobbleFrequency = 1.5f; + + [Tooltip("Base wobble amplitude in degrees from horizontal")] + [SerializeField] private float playerBaseWobbleAmplitude = 8f; + + [Tooltip("How much speed affects amplitude")] + [SerializeField] private float playerSpeedToAmplitude = 2f; + + [Tooltip("Maximum allowed rotation in degrees")] + [SerializeField] private float playerMaxRotationLimit = 45f; + + [Tooltip("Frequency of vertical bobbing")] + [SerializeField] private float playerVerticalFrequency = 0.5f; + + [Tooltip("How far the object moves up/down")] + [SerializeField] private float playerVerticalAmplitude = 0.5f; + + [Tooltip("How quickly velocity changes are smoothed")] + [SerializeField] private float playerVelocitySmoothing = 10f; + + [Tooltip("How quickly rotation is smoothed")] + [SerializeField] private float playerRotationSmoothing = 10f; + + [Header("Collision Settings")] + [Tooltip("Layer mask for obstacle detection - configure which layers contain obstacles")] + [LayerMask] + [SerializeField] private LayerMask playerObstacleLayerMask = -1; + + [Tooltip("Whether to block player input during damage immunity period")] + [SerializeField] private bool blockInputDuringImmunity = true; + + [Tooltip("Type of bump response: 0=Impulse, 1=SmoothToCenter")] + [SerializeField] private int bumpMode = 0; + + [Tooltip("Animation curve controlling bump movement over time")] + [SerializeField] private AnimationCurve bumpCurve = new AnimationCurve( + new Keyframe(0f, 0f, 0f, 2f), + new Keyframe(1f, 1f, 0f, 0f)); + // Bubble properties access public bool BubbleUseObjectPooling => bubbleUseObjectPooling; public int BubbleInitialPoolSize => bubbleInitialPoolSize; @@ -86,10 +150,36 @@ namespace AppleHills.Core.Settings // Obstacle properties access public int ObstacleLayer => obstacleLayer; - public LayerMask ObstacleTileLayerMask => obstacleTileLayerMask; public bool ObstacleUseObjectPooling => obstacleUseObjectPooling; public int ObstacleMaxPerPrefabPoolSize => obstacleMaxPerPrefabPoolSize; public int ObstacleTotalMaxPoolSize => obstacleTotalMaxPoolSize; + + // Trench Tile System properties + public int TrenchTileLayer => trenchTileLayer; + public bool TrenchTileUseObjectPooling => trenchTileUseObjectPooling; + public int TrenchTileMaxPerPrefabPoolSize => trenchTileMaxPerPrefabPoolSize; + public int TrenchTileTotalMaxPoolSize => trenchTileTotalMaxPoolSize; + + // Player Blink Behavior properties + public Color PlayerBlinkDamageColor => playerBlinkDamageColor; + public float PlayerBlinkRate => playerBlinkRate; + public float PlayerDamageColorAlpha => playerDamageColorAlpha; + + // Player Wobble Behavior properties + public float PlayerWobbleFrequency => playerWobbleFrequency; + public float PlayerBaseWobbleAmplitude => playerBaseWobbleAmplitude; + public float PlayerSpeedToAmplitude => playerSpeedToAmplitude; + public float PlayerMaxRotationLimit => playerMaxRotationLimit; + public float PlayerVerticalFrequency => playerVerticalFrequency; + public float PlayerVerticalAmplitude => playerVerticalAmplitude; + public float PlayerVelocitySmoothing => playerVelocitySmoothing; + public float PlayerRotationSmoothing => playerRotationSmoothing; + + // Collision Settings properties + public LayerMask PlayerObstacleLayerMask => playerObstacleLayerMask; + public bool BlockInputDuringImmunity => blockInputDuringImmunity; + public int BumpMode => bumpMode; + public AnimationCurve BumpCurve => bumpCurve; public override void OnValidate() { @@ -122,6 +212,26 @@ namespace AppleHills.Core.Settings // Validate obstacle settings obstacleMaxPerPrefabPoolSize = Mathf.Max(1, obstacleMaxPerPrefabPoolSize); obstacleTotalMaxPoolSize = Mathf.Max(obstacleMaxPerPrefabPoolSize, obstacleTotalMaxPoolSize); + + // Validate Trench Tile settings + trenchTileMaxPerPrefabPoolSize = Mathf.Max(1, trenchTileMaxPerPrefabPoolSize); + trenchTileTotalMaxPoolSize = Mathf.Max(trenchTileMaxPerPrefabPoolSize, trenchTileTotalMaxPoolSize); + + // Validate Player Blink settings + playerBlinkRate = Mathf.Max(0.01f, playerBlinkRate); + playerDamageColorAlpha = Mathf.Clamp01(playerDamageColorAlpha); + + // Validate Player Wobble settings + playerWobbleFrequency = Mathf.Max(0.01f, playerWobbleFrequency); + playerBaseWobbleAmplitude = Mathf.Max(0f, playerBaseWobbleAmplitude); + playerMaxRotationLimit = Mathf.Max(0f, playerMaxRotationLimit); + playerVerticalFrequency = Mathf.Max(0.01f, playerVerticalFrequency); + playerVerticalAmplitude = Mathf.Max(0f, playerVerticalAmplitude); + playerVelocitySmoothing = Mathf.Max(0.1f, playerVelocitySmoothing); + playerRotationSmoothing = Mathf.Max(0.1f, playerRotationSmoothing); + + // Validate Collision settings + bumpMode = Mathf.Clamp(bumpMode, 0, 1); } } } diff --git a/Assets/Scripts/Core/Settings/DivingMinigameSettings.cs b/Assets/Scripts/Core/Settings/DivingMinigameSettings.cs index 769f9618..b53e3103 100644 --- a/Assets/Scripts/Core/Settings/DivingMinigameSettings.cs +++ b/Assets/Scripts/Core/Settings/DivingMinigameSettings.cs @@ -105,6 +105,19 @@ namespace AppleHills.Core.Settings [Tooltip("Maximum movement speed for spawned obstacles")] [SerializeField] private float endlessDescenderObstacleMaxMoveSpeed = 4f; + [Header("Endless Descender - Collision Handling")] + [Tooltip("Duration in seconds of damage immunity after being hit")] + [SerializeField] private float endlessDescenderDamageImmunityDuration = 1.0f; + + [Tooltip("Force strength for impulse bumps - higher values push further toward center")] + [SerializeField] private float endlessDescenderBumpForce = 5.0f; + + [Tooltip("Speed for smooth movement to center (units per second)")] + [SerializeField] private float endlessDescenderSmoothMoveSpeed = 8.0f; + + [Tooltip("Whether to block player input during bump movement")] + [SerializeField] private bool endlessDescenderBlockInputDuringBump = true; + // IMinigameSettings implementation - Basic Movement public float EndlessDescenderLerpSpeed => endlessDescenderLerpSpeed; public float EndlessDescenderMaxOffset => endlessDescenderMaxOffset; @@ -149,6 +162,12 @@ namespace AppleHills.Core.Settings public float EndlessDescenderObstacleMinMoveSpeed => endlessDescenderObstacleMinMoveSpeed; public float EndlessDescenderObstacleMaxMoveSpeed => endlessDescenderObstacleMaxMoveSpeed; + // IMinigameSettings implementation - Collision Handling + public float EndlessDescenderDamageImmunityDuration => endlessDescenderDamageImmunityDuration; + public float EndlessDescenderBumpForce => endlessDescenderBumpForce; + public float EndlessDescenderSmoothMoveSpeed => endlessDescenderSmoothMoveSpeed; + public bool EndlessDescenderBlockInputDuringBump => endlessDescenderBlockInputDuringBump; + public override void OnValidate() { base.OnValidate(); @@ -205,6 +224,11 @@ namespace AppleHills.Core.Settings endlessDescenderObstacleSpawnCollisionRadius = Mathf.Max(0.1f, endlessDescenderObstacleSpawnCollisionRadius); endlessDescenderObstacleMinMoveSpeed = Mathf.Max(0.1f, endlessDescenderObstacleMinMoveSpeed); endlessDescenderObstacleMaxMoveSpeed = Mathf.Max(endlessDescenderObstacleMinMoveSpeed, endlessDescenderObstacleMaxMoveSpeed); + + // Validate collision settings + endlessDescenderDamageImmunityDuration = Mathf.Max(0.1f, endlessDescenderDamageImmunityDuration); + endlessDescenderBumpForce = Mathf.Max(0.1f, endlessDescenderBumpForce); + endlessDescenderSmoothMoveSpeed = Mathf.Max(0.1f, endlessDescenderSmoothMoveSpeed); } } } diff --git a/Assets/Scripts/Core/Settings/LayerAttributes.cs b/Assets/Scripts/Core/Settings/LayerAttributes.cs new file mode 100644 index 00000000..a4b5fafa --- /dev/null +++ b/Assets/Scripts/Core/Settings/LayerAttributes.cs @@ -0,0 +1,20 @@ +using UnityEngine; + +namespace AppleHills.Core.Settings +{ + /// + /// Attribute to indicate a field should be drawn using the layer selector dropdown + /// + public class LayerAttribute : PropertyAttribute + { + // No properties needed - this is a marker attribute + } + + /// + /// Attribute to indicate a field should be drawn using the layer mask selector dropdown + /// + public class LayerMaskAttribute : PropertyAttribute + { + // No properties needed - this is a marker attribute + } +} diff --git a/Assets/Scripts/Core/Settings/LayerAttributes.cs.meta b/Assets/Scripts/Core/Settings/LayerAttributes.cs.meta new file mode 100644 index 00000000..b4edc908 --- /dev/null +++ b/Assets/Scripts/Core/Settings/LayerAttributes.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7c64dbd728524f23bda766b57a388207 +timeCreated: 1758711688 \ No newline at end of file diff --git a/Assets/Scripts/Core/Settings/SettingsInterfaces.cs b/Assets/Scripts/Core/Settings/SettingsInterfaces.cs index 71a10c64..dc77aaa9 100644 --- a/Assets/Scripts/Core/Settings/SettingsInterfaces.cs +++ b/Assets/Scripts/Core/Settings/SettingsInterfaces.cs @@ -88,5 +88,11 @@ namespace AppleHills.Core.Settings float EndlessDescenderObstacleSpawnCollisionRadius { get; } float EndlessDescenderObstacleMinMoveSpeed { get; } float EndlessDescenderObstacleMaxMoveSpeed { get; } + + // Endless Descender - Collision Handling + float EndlessDescenderDamageImmunityDuration { get; } + float EndlessDescenderBumpForce { get; } + float EndlessDescenderSmoothMoveSpeed { get; } + bool EndlessDescenderBlockInputDuringBump { get; } } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleCollision.cs b/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleCollision.cs index 30eaf7ee..c1aca90b 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleCollision.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleCollision.cs @@ -8,6 +8,24 @@ namespace Minigames.DivingForPictures /// public class ObstacleCollision : PlayerCollisionBehavior { + protected override void OnEnable() + { + base.OnEnable(); + + // Subscribe to immunity events + OnImmunityStarted += HandleImmunityStarted; + OnImmunityEnded += HandleImmunityEnded; + } + + protected override void OnDisable() + { + // Unsubscribe from immunity events + OnImmunityStarted -= HandleImmunityStarted; + OnImmunityEnded -= HandleImmunityEnded; + + base.OnDisable(); + } + protected override void HandleCollisionResponse(Collider2D obstacle) { // Mark the obstacle as having dealt damage to prevent multiple hits @@ -21,21 +39,20 @@ namespace Minigames.DivingForPictures } /// - /// Override to prevent input blocking during damage immunity - /// Since obstacles pass through the player, we don't want to block input + /// Handler for immunity started event - replaces OnImmunityStart method /// - protected override void OnImmunityStart() + private void HandleImmunityStarted() { - Debug.Log($"[ObstacleCollision] Damage immunity started for {damageImmunityDuration} seconds"); + Debug.Log($"[ObstacleCollision] Damage immunity started for {_gameSettings.EndlessDescenderDamageImmunityDuration} seconds"); // Don't block input for obstacle damage - let player keep moving // The shared immunity system will handle the collision prevention } /// - /// Override to handle immunity end + /// Handler for immunity ended event - replaces OnImmunityEnd method /// - protected override void OnImmunityEnd() + private void HandleImmunityEnded() { Debug.Log($"[ObstacleCollision] Damage immunity ended"); // No special handling needed - shared immunity system handles collider re-enabling diff --git a/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleSpawner.cs index 0e48d095..27860d65 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleSpawner.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleSpawner.cs @@ -298,8 +298,9 @@ namespace Minigames.DivingForPictures /// private bool IsValidSpawnPosition(Vector3 position) { - // Use OverlapCircle to check for collisions with tiles - Collider2D collision = Physics2D.OverlapCircle(position, _settings.EndlessDescenderObstacleSpawnCollisionRadius, _devSettings.ObstacleTileLayerMask); + // Use OverlapCircle to check for collisions with tiles using just the layer + // Convert the single layer to a layer mask inline (1 << layerNumber) + Collider2D collision = Physics2D.OverlapCircle(position, _settings.EndlessDescenderObstacleSpawnCollisionRadius, 1 << _devSettings.TrenchTileLayer); return collision == null; } diff --git a/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerBlinkBehavior.cs b/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerBlinkBehavior.cs index 022a779b..ca62c174 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerBlinkBehavior.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerBlinkBehavior.cs @@ -1,5 +1,6 @@ using UnityEngine; using System.Collections; +using AppleHills.Core.Settings; namespace Minigames.DivingForPictures { @@ -9,28 +10,27 @@ namespace Minigames.DivingForPictures /// public class PlayerBlinkBehavior : MonoBehaviour { - [Header("Blink Settings")] - [Tooltip("Color to blink to when taking damage (typically red for damage indication)")] - [SerializeField] private Color damageBlinkColor = Color.red; - - [Tooltip("How fast to blink between normal and damage colors (seconds between color changes)")] - [SerializeField] private float blinkRate = 0.15f; - - [Tooltip("Alpha value for the damage color (0 = transparent, 1 = opaque)")] - [Range(0f, 1f)] - [SerializeField] private float damageColorAlpha = 0.7f; - [Header("References")] [Tooltip("The SpriteRenderer to apply blink effects to (auto-assigned if empty)")] [SerializeField] private SpriteRenderer targetSpriteRenderer; + // Developer settings reference + private DivingDeveloperSettings _devSettings; + private bool _isBlinking; private bool _isShowingDamageColor; private Coroutine _blinkCoroutine; - private Color _originalColor; // Missing field declaration + private Color _originalColor; private void Awake() { + // Get developer settings + _devSettings = GameManager.GetDeveloperSettings(); + if (_devSettings == null) + { + Debug.LogError("[PlayerBlinkBehavior] Failed to load developer settings!"); + } + // Auto-assign sprite renderer if not set if (targetSpriteRenderer == null) { @@ -51,192 +51,101 @@ namespace Minigames.DivingForPictures return; } - // Store original color + // Store the original color _originalColor = targetSpriteRenderer.color; - - // Ensure damage color has the correct alpha - damageBlinkColor.a = damageColorAlpha; } - + private void OnEnable() { - // Subscribe to immunity events (renamed from damage events) - PlayerCollisionBehavior.OnImmunityStarted += StartBlinking; - PlayerCollisionBehavior.OnImmunityEnded += StopBlinking; + // Subscribe to damage events + PlayerCollisionBehavior.OnDamageTaken += StartBlinkEffect; } - + private void OnDisable() { - // Unsubscribe from immunity events - PlayerCollisionBehavior.OnImmunityStarted -= StartBlinking; - PlayerCollisionBehavior.OnImmunityEnded -= StopBlinking; + // Unsubscribe to prevent memory leaks + PlayerCollisionBehavior.OnDamageTaken -= StartBlinkEffect; - // Stop any ongoing blink effect if (_blinkCoroutine != null) { StopCoroutine(_blinkCoroutine); _blinkCoroutine = null; } - // Restore original color - RestoreOriginalColor(); - } - - /// - /// Starts the red blinking effect when damage begins - /// - private void StartBlinking() - { - if (targetSpriteRenderer == null) return; - - Debug.Log("[PlayerBlinkBehavior] Starting damage blink effect"); - - // Stop any existing blink coroutine - if (_blinkCoroutine != null) - { - StopCoroutine(_blinkCoroutine); - } - - _isBlinking = true; - _isShowingDamageColor = false; - - // Start the blink coroutine - _blinkCoroutine = StartCoroutine(BlinkCoroutine()); - } - - /// - /// Stops the blinking effect when damage ends - /// - private void StopBlinking() - { - Debug.Log("[PlayerBlinkBehavior] Stopping damage blink effect"); - - _isBlinking = false; - - // Stop the blink coroutine - if (_blinkCoroutine != null) - { - StopCoroutine(_blinkCoroutine); - _blinkCoroutine = null; - } - - // Restore original color - RestoreOriginalColor(); - } - - /// - /// Coroutine that handles the blinking animation - /// - private IEnumerator BlinkCoroutine() - { - while (_isBlinking && targetSpriteRenderer != null) - { - // Toggle between original and damage colors - if (_isShowingDamageColor) - { - targetSpriteRenderer.color = _originalColor; - _isShowingDamageColor = false; - } - else - { - targetSpriteRenderer.color = damageBlinkColor; - _isShowingDamageColor = true; - } - - // Wait for blink interval - yield return new WaitForSeconds(blinkRate); - } - } - - /// - /// Restores the sprite renderer to its original color - /// - private void RestoreOriginalColor() - { + // Restore original color if disabled during blinking if (targetSpriteRenderer != null) { targetSpriteRenderer.color = _originalColor; - _isShowingDamageColor = false; } } - + /// - /// Updates the original color reference (useful if sprite color changes during gameplay) + /// Start the blinking effect coroutine /// - public void UpdateOriginalColor() + private void StartBlinkEffect() { - if (targetSpriteRenderer != null && !_isBlinking) - { - _originalColor = targetSpriteRenderer.color; - } - } - - /// - /// Public method to change blink color at runtime - /// - public void SetDamageBlinkColor(Color newColor) - { - damageBlinkColor = newColor; - damageBlinkColor.a = damageColorAlpha; - } - - /// - /// Public method to change blink rate at runtime - /// - public void SetBlinkRate(float rate) - { - blinkRate = rate; - } - - /// - /// Check if currently blinking - /// - public bool IsBlinking => _isBlinking; - - /// - /// Manually trigger blink effect (useful for testing or other damage sources) - /// - public void TriggerBlink(float duration) - { - if (_blinkCoroutine != null) + if (targetSpriteRenderer == null || _devSettings == null) return; + + // If already blinking, stop the current coroutine + if (_isBlinking && _blinkCoroutine != null) { StopCoroutine(_blinkCoroutine); } - StartCoroutine(ManualBlinkCoroutine(duration)); + // Start a new blink coroutine + _blinkCoroutine = StartCoroutine(BlinkCoroutine()); } - + /// - /// Coroutine for manually triggered blink effects + /// Coroutine that handles the blink effect timing /// - private IEnumerator ManualBlinkCoroutine(float duration) + private IEnumerator BlinkCoroutine() { _isBlinking = true; _isShowingDamageColor = false; - float elapsed = 0f; + // Create damage color with configured alpha + Color damageColor = _devSettings.PlayerBlinkDamageColor; + damageColor.a = _devSettings.PlayerDamageColorAlpha; - while (elapsed < duration && targetSpriteRenderer != null) + // Wait for immunity to end + PlayerCollisionBehavior.OnImmunityEnded += StopBlinking; + + // Blink while immunity is active + while (_isBlinking) { - // Toggle between original and damage colors + // Toggle between original and damage color if (_isShowingDamageColor) { targetSpriteRenderer.color = _originalColor; - _isShowingDamageColor = false; } else { - targetSpriteRenderer.color = damageBlinkColor; - _isShowingDamageColor = true; + targetSpriteRenderer.color = damageColor; } - yield return new WaitForSeconds(blinkRate); - elapsed += blinkRate; + _isShowingDamageColor = !_isShowingDamageColor; + + // Wait for next blink + yield return new WaitForSeconds(_devSettings.PlayerBlinkRate); } - // Ensure we end with original color - RestoreOriginalColor(); + // Restore original color when done blinking + targetSpriteRenderer.color = _originalColor; + } + + /// + /// Called when immunity ends to stop the blinking effect + /// + private void StopBlinking() + { + // Unsubscribe from the event to avoid memory leaks + PlayerCollisionBehavior.OnImmunityEnded -= StopBlinking; + _isBlinking = false; + + // No need to stop the coroutine, it will exit naturally + // This avoids race conditions if immunity ends during a blink cycle } } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerCollisionBehavior.cs b/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerCollisionBehavior.cs index d7c7a3be..858dc5d4 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerCollisionBehavior.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerCollisionBehavior.cs @@ -1,6 +1,7 @@ using UnityEngine; using System; using System.Collections; +using AppleHills.Core.Settings; namespace Minigames.DivingForPictures { @@ -10,17 +11,6 @@ namespace Minigames.DivingForPictures /// public abstract class PlayerCollisionBehavior : MonoBehaviour { - [Header("Collision Settings")] - [Tooltip("Duration in seconds of damage immunity after being hit")] - [SerializeField] protected float damageImmunityDuration = 1.0f; - - [Tooltip("Layer mask for obstacle detection - configure which layers contain obstacles")] - [SerializeField] protected LayerMask obstacleLayerMask = -1; - - [Header("Input Blocking")] - [Tooltip("Whether to block player input during damage immunity period")] - [SerializeField] protected bool blockInputDuringImmunity; - [Header("References")] [Tooltip("The player character GameObject (auto-assigned if empty)")] [SerializeField] protected GameObject playerCharacter; @@ -28,6 +18,10 @@ namespace Minigames.DivingForPictures [Tooltip("Reference to the PlayerController component (auto-assigned if empty)")] [SerializeField] protected PlayerController playerController; + // Settings references + protected IDivingMinigameSettings _gameSettings; + protected DivingDeveloperSettings _devSettings; + // Static shared immunity state across all collision behaviors private static bool _isGloballyImmune; private static Coroutine _globalImmunityCoroutine; @@ -67,305 +61,152 @@ namespace Minigames.DivingForPictures OnDamageTaken?.Invoke(); } - protected bool wasInputBlocked; - - protected virtual void Awake() + /// + /// Called when the component is enabled + /// + protected virtual void OnEnable() { - // Auto-assign if not set in inspector + _allInstances.Add(this); + + // Auto-assign references if needed if (playerCharacter == null) playerCharacter = gameObject; if (playerController == null) playerController = GetComponent(); - // Set up shared collider reference (only once) + // Initialize the shared player collider if not already set if (_sharedPlayerCollider == null) { _sharedPlayerCollider = GetComponent(); if (_sharedPlayerCollider == null) { - _sharedPlayerCollider = GetComponentInChildren(); - if (_sharedPlayerCollider != null) - { - Debug.Log($"[PlayerCollisionBehavior] Found collider on child object: {_sharedPlayerCollider.gameObject.name}"); - } - } - - if (_sharedPlayerCollider == null) - { - Debug.LogError($"[PlayerCollisionBehavior] No Collider2D found on this GameObject or its children!"); + Debug.LogError("[PlayerCollisionBehavior] No Collider2D found on this GameObject!"); } } + + // Load settings + _gameSettings = GameManager.GetSettingsObject(); + _devSettings = GameManager.GetDeveloperSettings(); + + if (_gameSettings == null) + { + Debug.LogError("[PlayerCollisionBehavior] Failed to load game settings!"); + } + + if (_devSettings == null) + { + Debug.LogError("[PlayerCollisionBehavior] Failed to load developer settings!"); + } + } - // Set up coroutine runner (use first instance) + /// + /// Called when the component is disabled + /// + protected virtual void OnDisable() + { + _allInstances.Remove(this); + } + + /// + /// Called when a Collider enters the trigger + /// + protected virtual void OnTriggerEnter2D(Collider2D other) + { + // Don't process collisions if already immune + if (_isGloballyImmune) + return; + + // Check if the collider is an obstacle + if ((_devSettings.PlayerObstacleLayerMask.value & (1 << other.gameObject.layer)) != 0) + { + HandleObstacleCollision(other); + } + } + + /// + /// Process collision with an obstacle + /// + protected virtual void HandleObstacleCollision(Collider2D obstacle) + { + // Trigger global damage and start immunity + TriggerDamageAndImmunity(); + + // Call the specific collision response for the derived class + HandleCollisionResponse(obstacle); + } + + /// + /// Abstract method for derived classes to implement specific collision responses + /// + protected abstract void HandleCollisionResponse(Collider2D obstacle); + + /// + /// Trigger damage event and start immunity period + /// + protected virtual void TriggerDamageAndImmunity() + { + // Make sure we're not already in immunity period + if (_isGloballyImmune) + return; + + // Trigger damage event for all listeners (like visual effects) + OnDamageTaken?.Invoke(); + + // Start immunity period + StartImmunity(); + } + + /// + /// Start the immunity period for all collision behaviors + /// + protected virtual void StartImmunity() + { + // Don't start if already immune + if (_isGloballyImmune) + return; + + // Set global immune state + _isGloballyImmune = true; + + // Store this instance to run the coroutine if needed if (_coroutineRunner == null) { _coroutineRunner = this; } - // Register this instance - _allInstances.Add(this); - } - - private void OnDestroy() - { - // Unregister this instance - _allInstances.Remove(this); - - // Clean up static references if this was the coroutine runner - if (_coroutineRunner == this) + // Block input if configured + if (_devSettings.BlockInputDuringImmunity && playerController != null) { - if (_globalImmunityCoroutine != null) - { - StopCoroutine(_globalImmunityCoroutine); - _globalImmunityCoroutine = null; - } - _coroutineRunner = null; - - // Find a new coroutine runner if there are other instances - foreach (var instance in _allInstances) - { - if (instance != null) - { - _coroutineRunner = instance; - break; - } - } - } - } - - /// - /// Called when another collider enters this trigger collider - /// - /// The other collider that entered the trigger - private void OnTriggerEnter2D(Collider2D other) - { - Debug.Log($"[{GetType().Name}] OnTriggerEnter2D called with collider: {other.gameObject.name} on layer: {other.gameObject.layer}"); - - // Check if the other collider is on one of our obstacle layers and we're not immune - if (IsObstacleLayer(other.gameObject.layer) && !_isGloballyImmune) - { - OnCollisionDetected(other); - } - } - - /// - /// Called when a collision with an obstacle is detected - /// - /// The obstacle collider that was hit - protected virtual void OnCollisionDetected(Collider2D obstacle) - { - if (_isGloballyImmune) return; - - // Trigger damage taken event first - OnDamageTaken?.Invoke(); - - // Start shared immunity period - StartGlobalImmunity(); - - // Call the specific collision response - HandleCollisionResponse(obstacle); - } - - /// - /// Starts the shared immunity period across all collision behaviors - /// - private void StartGlobalImmunity() - { - if (_isGloballyImmune) return; // Already immune - - _isGloballyImmune = true; - - // Disable the shared collider to prevent further collisions - if (_sharedPlayerCollider != null) - { - _sharedPlayerCollider.enabled = false; + // Notify player controller to block input } - // Stop any existing immunity coroutine + // Trigger event for all listeners + OnImmunityStarted?.Invoke(); + + // Stop existing coroutine if one is running if (_globalImmunityCoroutine != null && _coroutineRunner != null) { _coroutineRunner.StopCoroutine(_globalImmunityCoroutine); } - // Start new immunity coroutine - if (_coroutineRunner != null) - { - _globalImmunityCoroutine = _coroutineRunner.StartCoroutine(ImmunityCoroutine()); - } - - // Notify all instances about immunity start - foreach (var instance in _allInstances) - { - if (instance != null) - { - instance.OnImmunityStart(); - } - } - - // Broadcast immunity start event - OnImmunityStarted?.Invoke(); + // Start immunity timer coroutine on this instance + _globalImmunityCoroutine = StartCoroutine(ImmunityTimerCoroutine()); } /// - /// Coroutine that handles the immunity timer + /// Coroutine to handle the immunity duration timer /// - private IEnumerator ImmunityCoroutine() + private IEnumerator ImmunityTimerCoroutine() { - Debug.Log($"[PlayerCollisionBehavior] Starting immunity coroutine for {damageImmunityDuration} seconds"); - - yield return new WaitForSeconds(damageImmunityDuration); - - Debug.Log($"[PlayerCollisionBehavior] Immunity period ended"); - - // End immunity + // Wait for the immunity duration + yield return new WaitForSeconds(_gameSettings.EndlessDescenderDamageImmunityDuration); + + // Reset immunity state _isGloballyImmune = false; - _globalImmunityCoroutine = null; - // Re-enable the shared collider - if (_sharedPlayerCollider != null) - { - _sharedPlayerCollider.enabled = true; - } - - // Notify all instances about immunity end - foreach (var instance in _allInstances) - { - if (instance != null) - { - instance.OnImmunityEnd(); - } - } - - // Broadcast immunity end event + // Trigger event for all listeners OnImmunityEnded?.Invoke(); } - - /// - /// Override this method to implement specific collision response behavior - /// - /// The obstacle that was collided with - protected abstract void HandleCollisionResponse(Collider2D obstacle); - - /// - /// Called when damage immunity starts (called on all instances) - /// - protected virtual void OnImmunityStart() - { - Debug.Log($"[{GetType().Name}] Damage immunity started for {damageImmunityDuration} seconds"); - - // Block input if specified - if (blockInputDuringImmunity) - { - BlockPlayerInput(); - } - } - - /// - /// Called when damage immunity ends (called on all instances) - /// - protected virtual void OnImmunityEnd() - { - Debug.Log($"[{GetType().Name}] Damage immunity ended"); - - // Restore input if it was blocked - if (wasInputBlocked) - { - RestorePlayerInput(); - } - } - - /// - /// Restores player input after immunity - /// - protected virtual void RestorePlayerInput() - { - if (playerController != null && wasInputBlocked) - { - playerController.enabled = true; - wasInputBlocked = false; - - // Update the controller's target position to current position to prevent snapping - UpdateControllerTarget(); - - Debug.Log($"[{GetType().Name}] Player input restored after immunity"); - } - } - - /// - /// Blocks player input during immunity - /// - protected virtual void BlockPlayerInput() - { - if (playerController != null && playerController.enabled) - { - playerController.enabled = false; - wasInputBlocked = true; - Debug.Log($"[{GetType().Name}] Player input blocked during immunity"); - } - } - - /// - /// Updates the PlayerController's internal target to match current position - /// - protected virtual void UpdateControllerTarget() - { - if (playerController != null && playerCharacter != null) - { - // Use reflection to update the private _targetFingerX field - var targetField = typeof(PlayerController) - .GetField("_targetFingerX", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - - if (targetField != null) - { - targetField.SetValue(playerController, playerCharacter.transform.position.x); - } - } - } - - /// - /// Checks if the given layer is included in our obstacle layer mask - /// - /// The layer to check - /// True if the layer is included in the obstacle layer mask - private bool IsObstacleLayer(int layer) - { - return (obstacleLayerMask.value & (1 << layer)) != 0; - } - - /// - /// Public property to check if player is currently immune (shared across all instances) - /// - public static bool IsImmune => _isGloballyImmune; - - /// - /// Public method to manually end immunity (affects all collision behaviors) - /// - public static void EndImmunity() - { - if (_isGloballyImmune && _globalImmunityCoroutine != null && _coroutineRunner != null) - { - _coroutineRunner.StopCoroutine(_globalImmunityCoroutine); - _globalImmunityCoroutine = null; - _isGloballyImmune = false; - - // Re-enable the shared collider - if (_sharedPlayerCollider != null) - { - _sharedPlayerCollider.enabled = true; - } - - // Notify all instances - foreach (var instance in _allInstances) - { - if (instance != null) - { - instance.OnImmunityEnd(); - } - } - - OnImmunityEnded?.Invoke(); - } - } } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/Player/TileBumpCollision.cs b/Assets/Scripts/Minigames/DivingForPictures/Player/TileBumpCollision.cs index ab9efb77..d177e4be 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Player/TileBumpCollision.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Player/TileBumpCollision.cs @@ -1,5 +1,6 @@ using UnityEngine; using System.Collections; +using AppleHills.Core.Settings; namespace Minigames.DivingForPictures { @@ -9,286 +10,159 @@ namespace Minigames.DivingForPictures /// public class TileBumpCollision : PlayerCollisionBehavior { - [Header("Bump Settings")] - [Tooltip("Type of bump response: Impulse pushes with force, SmoothToCenter moves directly to center")] - [SerializeField] private BumpMode bumpMode = BumpMode.Impulse; - - [Tooltip("Force strength for impulse bumps - higher values push further toward center")] - [SerializeField] private float bumpForce = 5.0f; - - [Tooltip("Speed for smooth movement to center (units per second)")] - [SerializeField] private float smoothMoveSpeed = 8.0f; - - [Tooltip("Animation curve controlling bump movement over time")] - [SerializeField] private AnimationCurve bumpCurve = new AnimationCurve(new Keyframe(0f, 0f, 0f, 2f), new Keyframe(1f, 1f, 0f, 0f)); - - [Tooltip("Whether to block player input during bump movement")] - [SerializeField] private bool blockInputDuringBump = true; - - public enum BumpMode - { - Impulse, // Force-based push toward center (distance depends on force) - SmoothToCenter // Smooth movement to center with configurable speed - } - private bool _isBumping; private Coroutine _bumpCoroutine; private bool _bumpInputBlocked; // Tracks bump-specific input blocking protected override void HandleCollisionResponse(Collider2D obstacle) { - switch (bumpMode) + // Use bump mode from developer settings + switch (_devSettings.BumpMode) { - case BumpMode.Impulse: + case 0: // Impulse mode StartImpulseBump(); break; - case BumpMode.SmoothToCenter: + case 1: // SmoothToCenter mode StartSmoothMoveToCenter(); break; } - Debug.Log($"[TileBumpCollision] Collision handled with {bumpMode} mode"); + Debug.Log($"[TileBumpCollision] Collision handled with {(_devSettings.BumpMode == 0 ? "Impulse" : "SmoothToCenter")} mode"); } /// - /// Starts an impulse bump toward the center with force-based distance + /// Applies an impulse force toward center /// private void StartImpulseBump() { - if (playerCharacter == null) return; - - float currentX = playerCharacter.transform.position.x; - - // Calculate bump distance based on force and current position - float directionToCenter = currentX > 0 ? -1f : 1f; // Direction toward center - - // Calculate target position - closer to center based on bump force - float bumpDistance = bumpForce * 0.2f; // Scale factor for distance - float targetX = currentX + (directionToCenter * bumpDistance); - - // Clamp to center if we would overshoot - if ((currentX > 0 && targetX < 0) || (currentX < 0 && targetX > 0)) - { - targetX = 0f; - } - - float bumpDuration = 0.5f; // Fixed duration for impulse - - StartBump(currentX, targetX, bumpDuration); - - Debug.Log($"[TileBumpCollision] Starting impulse bump from X={currentX} to X={targetX} (force={bumpForce})"); - } - - /// - /// Starts smooth movement to the center - /// - private void StartSmoothMoveToCenter() - { - if (playerCharacter == null) return; - - float currentX = playerCharacter.transform.position.x; - float distanceToCenter = Mathf.Abs(currentX); - - float targetX = 0f; // Always move to center - float bumpDuration = distanceToCenter / smoothMoveSpeed; // Duration based on distance and speed - - StartBump(currentX, targetX, bumpDuration); - - Debug.Log($"[TileBumpCollision] Starting smooth move to center from X={currentX} (speed={smoothMoveSpeed}, duration={bumpDuration:F2}s)"); - } - - /// - /// Common bump initialization using coroutines - /// - private void StartBump(float startX, float targetX, float duration) - { - // Stop any existing bump - if (_bumpCoroutine != null) - { - StopCoroutine(_bumpCoroutine); - _bumpCoroutine = null; - } + if (_isBumping || playerCharacter == null) + return; _isBumping = true; - // Block player input if enabled (use bump-specific blocking) - if (blockInputDuringBump && playerController != null && playerController.enabled) + // Block input during bump if configured + if (_gameSettings.EndlessDescenderBlockInputDuringBump && playerController != null) { - playerController.enabled = false; _bumpInputBlocked = true; - Debug.Log("[TileBumpCollision] Player input blocked during bump"); + // TODO: Implement input blocking } - // Start bump coroutine - _bumpCoroutine = StartCoroutine(BumpCoroutine(startX, targetX, duration)); + // Calculate direction to center (X = 0) + float directionToCenter = Mathf.Sign(-playerCharacter.transform.position.x); + + // Start impulse bump coroutine + if (_bumpCoroutine != null) + StopCoroutine(_bumpCoroutine); + + _bumpCoroutine = StartCoroutine(ImpulseBumpCoroutine(directionToCenter)); + + Debug.Log($"[TileBumpCollision] Started impulse bump with force {_gameSettings.EndlessDescenderBumpForce} in direction {directionToCenter}"); } /// - /// Coroutine that handles the bump movement over time + /// Smoothly moves the player to center /// - private IEnumerator BumpCoroutine(float startX, float targetX, float duration) + private void StartSmoothMoveToCenter() + { + if (_isBumping || playerCharacter == null) + return; + + _isBumping = true; + + // Block input during bump if configured + if (_gameSettings.EndlessDescenderBlockInputDuringBump && playerController != null) + { + _bumpInputBlocked = true; + // TODO: Implement input blocking + } + + // Start smooth move coroutine + if (_bumpCoroutine != null) + StopCoroutine(_bumpCoroutine); + + _bumpCoroutine = StartCoroutine(SmoothMoveToCenterCoroutine()); + + Debug.Log($"[TileBumpCollision] Started smooth move to center with speed {_gameSettings.EndlessDescenderSmoothMoveSpeed}"); + } + + /// + /// Coroutine to handle impulse bump movement + /// + private IEnumerator ImpulseBumpCoroutine(float direction) { float elapsedTime = 0f; + float bumpDuration = 0.5f; // Fixed duration for impulse + Vector3 startPosition = playerCharacter.transform.position; - while (elapsedTime < duration) + while (elapsedTime < bumpDuration) { elapsedTime += Time.deltaTime; - // Calculate progress and apply curve - float progress = elapsedTime / duration; - float curveValue = bumpCurve.Evaluate(progress); + // Use evaluation time from curve for non-linear movement + float t = elapsedTime / bumpDuration; + float curveValue = _devSettings.BumpCurve.Evaluate(t); - // Interpolate position - float currentX = Mathf.Lerp(startX, targetX, curveValue); + // Calculate movement distance based on force and curve + float distance = _gameSettings.EndlessDescenderBumpForce * curveValue * Time.deltaTime; - // Apply the position to the player character - if (playerCharacter != null) - { - Vector3 currentPos = playerCharacter.transform.position; - playerCharacter.transform.position = new Vector3(currentX, currentPos.y, currentPos.z); - } + // Move the player toward center + Vector3 newPosition = playerCharacter.transform.position; + newPosition.x += direction * distance; + + // Clamp to valid range + newPosition.x = Mathf.Clamp(newPosition.x, _gameSettings.EndlessDescenderClampXMin, _gameSettings.EndlessDescenderClampXMax); + + // Apply the position + playerCharacter.transform.position = newPosition; yield return null; } - // Ensure we end exactly at target - if (playerCharacter != null) - { - Vector3 currentPos = playerCharacter.transform.position; - playerCharacter.transform.position = new Vector3(targetX, currentPos.y, currentPos.z); - } - - // Bump finished + // Finish bump _isBumping = false; + _bumpInputBlocked = false; _bumpCoroutine = null; + } + + /// + /// Coroutine to handle smooth movement to center + /// + private IEnumerator SmoothMoveToCenterCoroutine() + { + Vector3 startPosition = playerCharacter.transform.position; + Vector3 targetPosition = new Vector3(0f, startPosition.y, startPosition.z); - if (_bumpInputBlocked) + // Calculate distance to center and expected duration + float distanceToCenter = Mathf.Abs(startPosition.x); + float expectedDuration = distanceToCenter / _gameSettings.EndlessDescenderSmoothMoveSpeed; + float elapsedTime = 0f; + + // Move until we reach the center + while (elapsedTime < expectedDuration) { - RestoreBumpInput(); + elapsedTime += Time.deltaTime; + + // Calculate progress based on time and curve + float t = elapsedTime / expectedDuration; + float curveValue = _devSettings.BumpCurve.Evaluate(t); + + // Calculate interpolated position + Vector3 newPosition = Vector3.Lerp(startPosition, targetPosition, curveValue); + + // Apply the position + playerCharacter.transform.position = newPosition; + + yield return null; } - Debug.Log("[TileBumpCollision] Bump movement completed"); - } - - /// - /// Restores player input after bump movement - /// - private void RestoreBumpInput() - { - if (_bumpInputBlocked && playerController != null) - { - playerController.enabled = true; - _bumpInputBlocked = false; - - // Update the controller's target position to current position to prevent snapping - UpdateControllerTarget(); - - Debug.Log("[TileBumpCollision] Player input restored after bump"); - } - } - - /// - /// Override to handle bump-specific input blocking during immunity - /// - protected override void OnImmunityStart() - { - Debug.Log($"[TileBumpCollision] Damage immunity started for {damageImmunityDuration} seconds"); - - // Block input if specified (in addition to any bump input blocking) - if (blockInputDuringImmunity && !_bumpInputBlocked) - { - BlockPlayerInput(); - } - } - - /// - /// Override to handle immunity end and bump cleanup - /// - protected override void OnImmunityEnd() - { - base.OnImmunityEnd(); + // Ensure we end at exactly the center + playerCharacter.transform.position = targetPosition; - // Stop any ongoing bump if immunity ends - if (_isBumping && _bumpCoroutine != null) - { - StopCoroutine(_bumpCoroutine); - _bumpCoroutine = null; - _isBumping = false; - - if (_bumpInputBlocked) - { - RestoreBumpInput(); - } - - Debug.Log("[TileBumpCollision] Bump interrupted by immunity end"); - } - } - - /// - /// Called when component is destroyed - cleanup coroutines - /// - private void OnDestroy() - { - if (_bumpCoroutine != null) - { - StopCoroutine(_bumpCoroutine); - _bumpCoroutine = null; - } - } - - /// - /// Public method to change bump mode at runtime - /// - public void SetBumpMode(BumpMode mode) - { - bumpMode = mode; - } - - /// - /// Public method to change bump force at runtime - /// - public void SetBumpForce(float force) - { - bumpForce = force; - } - - /// - /// Public method to change smooth move speed at runtime - /// - public void SetSmoothMoveSpeed(float speed) - { - smoothMoveSpeed = speed; - } - - /// - /// Check if player is currently being bumped - /// - public bool IsBumping => _isBumping; - - /// - /// Check if input is currently blocked by bump - /// - public bool IsBumpInputBlocked => _bumpInputBlocked; - - /// - /// Public method to manually stop bump movement - /// - public void StopBump() - { - if (_isBumping && _bumpCoroutine != null) - { - StopCoroutine(_bumpCoroutine); - _bumpCoroutine = null; - _isBumping = false; - - if (_bumpInputBlocked) - { - RestoreBumpInput(); - } - - Debug.Log("[TileBumpCollision] Bump manually stopped"); - } + // Finish bump + _isBumping = false; + _bumpInputBlocked = false; + _bumpCoroutine = null; } } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/Player/WobbleBehavior.cs b/Assets/Scripts/Minigames/DivingForPictures/Player/WobbleBehavior.cs index 8d55e8de..469d38e3 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Player/WobbleBehavior.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Player/WobbleBehavior.cs @@ -1,38 +1,13 @@ using UnityEngine; +using AppleHills.Core.Settings; /// /// Adds a wobble (rocking and vertical movement) effect to the object, based on speed and time. /// public class WobbleBehavior : MonoBehaviour { - [Header("Wobble Settings")] - public float wobbleFrequency = 1.5f; - /// - /// Max degrees from horizontal. - /// - public float baseWobbleAmplitude = 8f; - /// - /// How much speed affects amplitude. - /// - public float speedToAmplitude = 2f; - /// - /// Maximum allowed rotation in degrees. - /// - public float maxRotationLimit = 45f; - - [Header("Vertical Movement Settings")] - public float verticalFrequency = 0.5f; - /// - /// How far the object moves up/down. - /// - public float verticalAmplitude = 0.5f; - - [Header("Smoothing Settings")] - public float velocitySmoothing = 10f; - /// - /// How quickly rotation is smoothed. - /// - public float rotationSmoothing = 10f; + // Developer settings reference + private DivingDeveloperSettings _devSettings; private Vector3 lastPosition; private float wobbleTime; @@ -46,47 +21,61 @@ public class WobbleBehavior : MonoBehaviour /// The current velocity of the object (horizontal only). /// public float Velocity => velocity; + /// /// The current vertical offset due to wobble. /// public float VerticalOffset => verticalOffset; - void Start() + private void Awake() { + // Get developer settings + _devSettings = GameManager.GetDeveloperSettings(); + if (_devSettings == null) + { + Debug.LogError("[WobbleBehavior] Failed to load developer settings!"); + } + + // Initialize lastPosition = transform.position; - smoothedVelocity = 0f; - smoothedAngle = 0f; + basePosition = transform.position; } - void Update() + private void Update() { - // Calculate movement speed (exclude vertical wobble from velocity calculation) - Vector3 horizontalPosition = transform.position; - horizontalPosition.y = 0f; // Ignore Y for velocity if only horizontal movement matters - Vector3 horizontalLastPosition = lastPosition; - horizontalLastPosition.y = 0f; - velocity = (horizontalPosition - horizontalLastPosition).magnitude / Time.deltaTime; + if (_devSettings == null) return; + + // Calculate velocity based on position change + Vector3 positionDelta = transform.position - lastPosition; + velocity = positionDelta.x / Time.deltaTime; lastPosition = transform.position; + basePosition = transform.position; - // Smooth velocity to prevent jitter - smoothedVelocity = Mathf.Lerp(smoothedVelocity, velocity, velocitySmoothing * Time.deltaTime); + // Smooth velocity changes + smoothedVelocity = Mathf.Lerp(smoothedVelocity, velocity, Time.deltaTime * _devSettings.PlayerVelocitySmoothing); - // Wobble amplitude scales with smoothed speed, but always has a base value - float amplitude = baseWobbleAmplitude + smoothedVelocity * speedToAmplitude; - amplitude = Mathf.Min(amplitude, maxRotationLimit); // Prevent amplitude from exceeding limit + // Calculate wobble rotation based on velocity and time + wobbleTime += Time.deltaTime * _devSettings.PlayerWobbleFrequency; + float rawWobble = Mathf.Sin(wobbleTime); - // Oscillate around horizontal (0 degrees) - wobbleTime += Time.deltaTime * wobbleFrequency; - float targetAngle = Mathf.Sin(wobbleTime) * amplitude; - targetAngle = Mathf.Clamp(targetAngle, -maxRotationLimit, maxRotationLimit); + // Calculate wobble amplitude based on velocity + float velocityFactor = Mathf.Abs(smoothedVelocity) * _devSettings.PlayerSpeedToAmplitude; + float wobbleAmplitude = _devSettings.PlayerBaseWobbleAmplitude + velocityFactor; + + // Clamp to maximum rotation limit + wobbleAmplitude = Mathf.Min(wobbleAmplitude, _devSettings.PlayerMaxRotationLimit); + + // Calculate target angle + float targetAngle = rawWobble * wobbleAmplitude; + + // Smooth angle changes + smoothedAngle = Mathf.Lerp(smoothedAngle, targetAngle, Time.deltaTime * _devSettings.PlayerRotationSmoothing); + + // Apply rotation + transform.rotation = Quaternion.Euler(0f, 0f, smoothedAngle); - // Smooth the rotation angle - smoothedAngle = Mathf.Lerp(smoothedAngle, targetAngle, rotationSmoothing * Time.deltaTime); - - // Apply rotation (Z axis for 2D) - transform.localRotation = Quaternion.Euler(0f, 0f, smoothedAngle); - - // Calculate vertical up/down movement (wave riding) only once - verticalOffset = Mathf.Sin(wobbleTime * verticalFrequency) * verticalAmplitude; + // Calculate vertical bobbing + float time = Time.time * _devSettings.PlayerVerticalFrequency; + verticalOffset = Mathf.Sin(time) * _devSettings.PlayerVerticalAmplitude; } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/Tiles/TrenchTileSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/Tiles/TrenchTileSpawner.cs index 28905763..1d5f9ee5 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Tiles/TrenchTileSpawner.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Tiles/TrenchTileSpawner.cs @@ -17,11 +17,6 @@ namespace Minigames.DivingForPictures [Tooltip("List of possible trench tile prefabs.")] [SerializeField] private List tilePrefabs; - [Header("Object Pooling")] - [SerializeField] private bool useObjectPooling = true; - [SerializeField] private int maxPerPrefabPoolSize = 2; - [SerializeField] private int totalMaxPoolSize = 10; - [Header("Events")] [FormerlySerializedAs("OnTileSpawned")] public UnityEvent onTileSpawned; @@ -29,8 +24,9 @@ namespace Minigames.DivingForPictures [FormerlySerializedAs("OnTileDestroyed")] public UnityEvent onTileDestroyed; - // Settings reference + // Settings references private IDivingMinigameSettings _settings; + private DivingDeveloperSettings _devSettings; // Private fields private readonly Dictionary _tileHeights = new Dictionary(); @@ -67,11 +63,18 @@ namespace Minigames.DivingForPictures // Get settings from GameManager _settings = GameManager.GetSettingsObject(); + _devSettings = GameManager.GetDeveloperSettings(); + if (_settings == null) { Debug.LogError("[TrenchTileSpawner] Failed to load diving minigame settings!"); } + if (_devSettings == null) + { + Debug.LogError("[TrenchTileSpawner] Failed to load diving developer settings!"); + } + _baseMoveSpeed = _settings?.EndlessDescenderMoveSpeed ?? 3f; // Store the original base speed // Calculate tile heights for each prefab @@ -80,7 +83,7 @@ namespace Minigames.DivingForPictures // Ensure all prefabs have Tile components ValidateTilePrefabs(); - if (useObjectPooling) + if (_devSettings != null && _devSettings.TrenchTileUseObjectPooling) { InitializeObjectPool(); } @@ -196,9 +199,9 @@ namespace Minigames.DivingForPictures poolGO.transform.SetParent(transform); _tilePool = poolGO.AddComponent(); - // Set up the pool configuration - _tilePool.maxPerPrefabPoolSize = maxPerPrefabPoolSize; - _tilePool.totalMaxPoolSize = totalMaxPoolSize; + // Set up the pool configuration using developer settings + _tilePool.maxPerPrefabPoolSize = _devSettings.TrenchTileMaxPerPrefabPoolSize; + _tilePool.totalMaxPoolSize = _devSettings.TrenchTileTotalMaxPoolSize; // Convert the GameObject list to a Tile list List prefabTiles = new List(tilePrefabs.Count); @@ -374,7 +377,7 @@ namespace Minigames.DivingForPictures _activeTiles.RemoveAt(0); onTileDestroyed?.Invoke(topTile); - if (useObjectPooling && _tilePool != null) + if (_devSettings != null && _devSettings.TrenchTileUseObjectPooling && _tilePool != null) { // Find the prefab index for this tile int prefabIndex = GetPrefabIndex(topTile); @@ -505,7 +508,7 @@ namespace Minigames.DivingForPictures GameObject tile; - if (useObjectPooling && _tilePool != null) + if (_devSettings != null && _devSettings.TrenchTileUseObjectPooling && _tilePool != null) { tile = _tilePool.GetTile(prefabIndex); if (tile == null) @@ -537,7 +540,7 @@ namespace Minigames.DivingForPictures { int prefabCount = tilePrefabs.Count; List weights = new List(prefabCount); - + for (int i = 0; i < prefabCount; i++) { int lastUsed = _tileLastUsed.TryGetValue(i, out var value) ? value : -prefabCount; @@ -545,13 +548,13 @@ namespace Minigames.DivingForPictures float weight = Mathf.Clamp(age, 1, prefabCount * 2); // More unused = higher weight weights.Add(weight); } - + float totalWeight = 0f; foreach (var weight in weights) { totalWeight += weight; } - + float randomValue = Random.value * totalWeight; for (int i = 0; i < prefabCount; i++) { @@ -561,10 +564,10 @@ namespace Minigames.DivingForPictures } randomValue -= weights[i]; } - + return Random.Range(0, prefabCount); // fallback } - + /// /// Gets the height of a tile based on its prefab or renderer bounds /// @@ -592,18 +595,18 @@ namespace Minigames.DivingForPictures } } } - + // If not found, calculate it from the renderer Renderer renderer = tile.GetComponentInChildren(); if (renderer != null) { return renderer.bounds.size.y; } - + // Fallback return DefaultTileHeight; } - + /// /// Gets the index of the prefab that was used to create this tile /// @@ -619,7 +622,7 @@ namespace Minigames.DivingForPictures for (int i = 0; i < tilePrefabs.Count; i++) { if (tilePrefabs[i] == null) continue; - + if (tile.name.StartsWith(tilePrefabs[i].name)) { return i; diff --git a/Assets/Settings/Developer.meta b/Assets/Settings/Developer.meta new file mode 100644 index 00000000..683ed765 --- /dev/null +++ b/Assets/Settings/Developer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dfde9ecdb3e084d47b97f511323c4a77 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/Developer/DivingDeveloperSettings.asset b/Assets/Settings/Developer/DivingDeveloperSettings.asset new file mode 100644 index 00000000..7ec0415b --- /dev/null +++ b/Assets/Settings/Developer/DivingDeveloperSettings.asset @@ -0,0 +1,78 @@ +%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: 033961b12e7b4289838d554c2264bacd, type: 3} + m_Name: DivingDeveloperSettings + m_EditorClassIdentifier: + bubbleUseObjectPooling: 1 + bubbleInitialPoolSize: 10 + bubbleMaxPoolSize: 30 + bubbleSpawnInterval: 0.5 + bubbleSpeedRange: {x: 0.5, y: 2} + bubbleScaleRange: {x: 0.3, y: 0.7} + bubbleWobbleSpeedRange: {x: 1, y: 3} + bubbleWobbleAmountRange: {x: 0.05, y: 0.15} + bubbleSpawnXMin: -3.5 + bubbleSpawnXMax: 3.5 + bubbleSpawnY: -5 + bubbleWobbleMinScale: 0.2 + bubbleWobbleMaxScale: 1.2 + bubbleSurfacingSpeedFactor: 0.5 + obstacleLayer: 9 + obstacleTileLayerMask: + serializedVersion: 2 + m_Bits: 0 + obstacleUseObjectPooling: 1 + obstacleMaxPerPrefabPoolSize: 3 + obstacleTotalMaxPoolSize: 15 + trenchTileUseObjectPooling: 1 + trenchTileMaxPerPrefabPoolSize: 2 + trenchTileTotalMaxPoolSize: 10 + playerBlinkDamageColor: {r: 1, g: 0, b: 0, a: 1} + playerBlinkRate: 0.15 + playerDamageColorAlpha: 0.7 + playerWobbleFrequency: 1.5 + playerBaseWobbleAmplitude: 8 + playerSpeedToAmplitude: 2 + playerMaxRotationLimit: 45 + playerVerticalFrequency: 0.5 + playerVerticalAmplitude: 0.5 + playerVelocitySmoothing: 10 + playerRotationSmoothing: 10 + playerObstacleLayerMask: + serializedVersion: 2 + m_Bits: 4294967295 + blockInputDuringImmunity: 1 + bumpMode: 0 + bumpCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 2 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 diff --git a/Assets/Settings/Developer/DivingDeveloperSettings.asset.meta b/Assets/Settings/Developer/DivingDeveloperSettings.asset.meta new file mode 100644 index 00000000..3e80e618 --- /dev/null +++ b/Assets/Settings/Developer/DivingDeveloperSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 328ce914b893df646be3ad3c62755453 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset index 3436af9a..992d7402 100644 --- a/ProjectSettings/TagManager.asset +++ b/ProjectSettings/TagManager.asset @@ -21,7 +21,7 @@ TagManager: - Interactable - QuarryObstacle - QuarryMonster - - + - QuarryTrenchTile - - -