Compare commits
6 Commits
load_save_
...
diving_obs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc1083adb | ||
|
|
9be8d1619b | ||
|
|
c5ce2ae1af | ||
|
|
3f52de9e1e | ||
|
|
44c1e48b82 | ||
| 50070651c5 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -99,6 +99,8 @@ InitTestScene*.unity*
|
||||
|
||||
# Auto-generated scenes by play mode tests
|
||||
/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
|
||||
.idea/.idea.AppleHillsProduction/.idea/indexLayout.xml
|
||||
.vscode/extensions.json
|
||||
.vscode/launch.json
|
||||
.vscode/settings.json
|
||||
.idea/.idea.AppleHillsProduction/.idea/indexLayout.xml
|
||||
|
||||
@@ -43,6 +43,9 @@ namespace Editor.Utilities
|
||||
[Tooltip("Color used for previewing colliders in the scene view")]
|
||||
private Color previewColor = new Color(0.2f, 1f, 0.3f, 0.5f);
|
||||
|
||||
[Tooltip("Layer to assign to GameObjects when colliders are generated")]
|
||||
private int targetLayer = 0;
|
||||
|
||||
private List<Mesh> previewMeshes = new List<Mesh>();
|
||||
|
||||
[MenuItem("Tools/Sprite Collider Generator")]
|
||||
@@ -51,8 +54,29 @@ namespace Editor.Utilities
|
||||
GetWindow<SpriteColliderGenerator>("Sprite Collider Generator");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Subscribe to scene change events to clear invalid object references
|
||||
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += OnSceneOpened;
|
||||
UnityEditor.SceneManagement.EditorSceneManager.sceneClosed += OnSceneClosed;
|
||||
|
||||
// Also subscribe to playmode changes as they can invalidate references
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
|
||||
// Subscribe to prefab stage changes (Unity 2018.3+)
|
||||
UnityEditor.SceneManagement.PrefabStage.prefabStageOpened += OnPrefabStageOpened;
|
||||
UnityEditor.SceneManagement.PrefabStage.prefabStageClosing += OnPrefabStageClosing;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// Unsubscribe from events
|
||||
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened -= OnSceneOpened;
|
||||
UnityEditor.SceneManagement.EditorSceneManager.sceneClosed -= OnSceneClosed;
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
UnityEditor.SceneManagement.PrefabStage.prefabStageOpened -= OnPrefabStageOpened;
|
||||
UnityEditor.SceneManagement.PrefabStage.prefabStageClosing -= OnPrefabStageClosing;
|
||||
|
||||
// Clean up any preview meshes when window is closed
|
||||
foreach (var mesh in previewMeshes)
|
||||
{
|
||||
@@ -63,7 +87,53 @@ namespace Editor.Utilities
|
||||
}
|
||||
previewMeshes.Clear();
|
||||
}
|
||||
|
||||
private void OnSceneOpened(UnityEngine.SceneManagement.Scene scene, UnityEditor.SceneManagement.OpenSceneMode mode)
|
||||
{
|
||||
// Clear selected objects when a scene is opened
|
||||
ClearInvalidReferences();
|
||||
}
|
||||
|
||||
private void OnSceneClosed(UnityEngine.SceneManagement.Scene scene)
|
||||
{
|
||||
// Clear selected objects when a scene is closed
|
||||
ClearInvalidReferences();
|
||||
}
|
||||
|
||||
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
// Clear references when entering/exiting play mode as they become invalid
|
||||
if (state == PlayModeStateChange.ExitingEditMode || state == PlayModeStateChange.ExitingPlayMode)
|
||||
{
|
||||
ClearInvalidReferences();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPrefabStageOpened(UnityEditor.SceneManagement.PrefabStage stage)
|
||||
{
|
||||
// Clear selected objects when entering a prefab stage
|
||||
ClearInvalidReferences();
|
||||
}
|
||||
|
||||
private void OnPrefabStageClosing(UnityEditor.SceneManagement.PrefabStage stage)
|
||||
{
|
||||
// Clear selected objects when exiting a prefab stage
|
||||
ClearInvalidReferences();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears invalid GameObject references from the selected objects list
|
||||
/// </summary>
|
||||
private void ClearInvalidReferences()
|
||||
{
|
||||
if (selectedObjects.Count > 0)
|
||||
{
|
||||
selectedObjects.Clear();
|
||||
ClearPreviews(); // Also clear any preview meshes
|
||||
Repaint(); // Refresh the window UI
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.BeginVertical();
|
||||
@@ -146,6 +216,12 @@ namespace Editor.Utilities
|
||||
new GUIContent("Generate Trigger Colliders", "When enabled, creates trigger colliders instead of solid colliders."),
|
||||
generateTriggerColliders);
|
||||
|
||||
// Layer selection
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(new GUIContent("Target Layer:", "Layer to assign to GameObjects when colliders are generated. Leave as 'Nothing' to keep current layer."), GUILayout.Width(180));
|
||||
targetLayer = EditorGUILayout.LayerField(targetLayer);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// Offset option
|
||||
offsetFromCenter = EditorGUILayout.Toggle(
|
||||
new GUIContent("Offset From Center", "When enabled, allows scaling the collider outward or inward from the sprite center."),
|
||||
@@ -445,8 +521,24 @@ namespace Editor.Utilities
|
||||
polygonCollider.SetPath(i, paths[i]);
|
||||
}
|
||||
|
||||
// Set the layer on the GameObject if a specific layer is selected
|
||||
SetTargetLayer(targetObject);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the target layer on the GameObject if a layer is selected
|
||||
/// </summary>
|
||||
/// <param name="targetObject">The GameObject to set the layer on</param>
|
||||
private void SetTargetLayer(GameObject targetObject)
|
||||
{
|
||||
if (targetLayer != 0)
|
||||
{
|
||||
Undo.RecordObject(targetObject, "Set GameObject Layer");
|
||||
targetObject.layer = targetLayer;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Vector2[]> GetSpritePaths(Sprite sprite, float tolerance)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &4743746373562280435
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7889589254526244618}
|
||||
- component: {fileID: 2565520060777160406}
|
||||
- component: {fileID: 586678365535466516}
|
||||
- component: {fileID: 3635110434097976059}
|
||||
m_Layer: 11
|
||||
m_Name: FloatingObstacle
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &7889589254526244618
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4743746373562280435}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -0.62358, y: 4.23222, z: 0}
|
||||
m_LocalScale: {x: 5, y: 5, z: 5}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &2565520060777160406
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4743746373562280435}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 32718083aef44be2a4318681fcdf5b2e, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
prefabIndex: 0
|
||||
damage: 1
|
||||
moveSpeed: 2
|
||||
enableMovement: 1
|
||||
spawner: {fileID: 0}
|
||||
--- !u!212 &586678365535466516
|
||||
SpriteRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4743746373562280435}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 0
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Color: {r: 0.9150943, g: 0, b: 0, a: 1}
|
||||
m_FlipX: 0
|
||||
m_FlipY: 0
|
||||
m_DrawMode: 0
|
||||
m_Size: {x: 0.16, y: 0.16}
|
||||
m_AdaptiveModeThreshold: 0.5
|
||||
m_SpriteTileMode: 0
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!61 &3635110434097976059
|
||||
BoxCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4743746373562280435}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0.049999997, y: 0.049999997, z: 0.049999997, w: 0.049999997}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 0.16, y: 0.16}
|
||||
newSize: {x: 0.16, y: 0.16}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Size: {x: 0.16, y: 0.16}
|
||||
m_EdgeRadius: 0
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 315a624eb99600444a51bb1d37c51742
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -12,7 +12,7 @@ GameObject:
|
||||
- component: {fileID: 8447572436637192077}
|
||||
- component: {fileID: 4998672042618199381}
|
||||
- component: {fileID: 3714732064953161914}
|
||||
m_Layer: 0
|
||||
m_Layer: 12
|
||||
m_Name: QuarryMonster
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
|
||||
@@ -10,8 +10,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 7111145574660306503}
|
||||
- component: {fileID: 3889795708575321074}
|
||||
- component: {fileID: 7249681423942450184}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 8688779957837852254}
|
||||
m_Layer: 6
|
||||
m_Name: Left_Tile2_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -88,7 +88,7 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &7249681423942450184
|
||||
--- !u!60 &8688779957837852254
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@@ -155,8 +155,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 1003080013996268193}
|
||||
- component: {fileID: 4856205316150460481}
|
||||
- component: {fileID: 2843103852598642252}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 8615330168962481848}
|
||||
m_Layer: 6
|
||||
m_Name: Right_Tile1_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -233,7 +233,7 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &2843103852598642252
|
||||
--- !u!60 &8615330168962481848
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@@ -282,8 +282,6 @@ PolygonCollider2D:
|
||||
- - {x: -0.48499998, y: 2.5}
|
||||
- {x: -0.49499997, y: 2.19}
|
||||
- {x: 0.035, y: 1.66}
|
||||
- {x: 0.035, y: 1.42}
|
||||
- {x: -0.24499999, y: 1.15}
|
||||
- {x: -0.285, y: 0.90999997}
|
||||
- {x: -0.13499999, y: 0.45999998}
|
||||
- {x: -1.115, y: -0.03}
|
||||
|
||||
@@ -10,7 +10,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 7111145574660306503}
|
||||
- component: {fileID: 3889795708575321074}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 8698932925238153222}
|
||||
m_Layer: 6
|
||||
m_Name: Left_Tile2_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -87,6 +88,63 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &8698932925238153222
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 864595161669782950}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.37, y: 5}
|
||||
newSize: {x: 2.37, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: 0.575, y: -1.5}
|
||||
- {x: 0.185, y: -0.63}
|
||||
- {x: 1.145, y: 0.85999995}
|
||||
- {x: 1.185, y: 1.41}
|
||||
- {x: 0.835, y: 1.8499999}
|
||||
- {x: 0.635, y: 2.5}
|
||||
- {x: -1.185, y: 2.5}
|
||||
- {x: -1.185, y: -2.5}
|
||||
- {x: 0.635, y: -2.5}
|
||||
- {x: 0.625, y: -1.64}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2171518497100337372
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -97,7 +155,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 1003080013996268193}
|
||||
- component: {fileID: 4856205316150460481}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 1368284191430742655}
|
||||
m_Layer: 6
|
||||
m_Name: Right_Tile1_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -174,6 +233,67 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &1368284191430742655
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2171518497100337372}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.65, y: 5}
|
||||
newSize: {x: 2.65, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: -0.48499998, y: 2.5}
|
||||
- {x: -0.49499997, y: 2.19}
|
||||
- {x: 0.035, y: 1.66}
|
||||
- {x: -0.285, y: 0.90999997}
|
||||
- {x: -0.13499999, y: 0.45999998}
|
||||
- {x: -1.115, y: -0.03}
|
||||
- {x: -1.3249999, y: -0.35}
|
||||
- {x: -1.2049999, y: -0.84999996}
|
||||
- {x: -0.36499998, y: -1.0699999}
|
||||
- {x: -0.585, y: -1.27}
|
||||
- {x: -0.625, y: -1.65}
|
||||
- {x: -0.48499998, y: -2.5}
|
||||
- {x: 1.3249999, y: -2.5}
|
||||
- {x: 1.3249999, y: 2.5}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2956826569642009690
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -10,7 +10,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 7111145574660306503}
|
||||
- component: {fileID: 3889795708575321074}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 6919687589473331973}
|
||||
m_Layer: 6
|
||||
m_Name: Left_Tile2_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -87,6 +88,72 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &6919687589473331973
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 864595161669782950}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.65, y: 5}
|
||||
newSize: {x: 2.37, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: 0.635, y: -1.78}
|
||||
- {x: 0.585, y: -1.27}
|
||||
- {x: 0.36499998, y: -1.14}
|
||||
- {x: 0.415, y: -1.05}
|
||||
- {x: 1.2149999, y: -0.85999995}
|
||||
- {x: 1.3249999, y: -0.7}
|
||||
- {x: 1.3249999, y: -0.35}
|
||||
- {x: 1.145, y: -0.049999997}
|
||||
- {x: 0.13499999, y: 0.45999998}
|
||||
- {x: 0.275, y: 0.87}
|
||||
- {x: 0.265, y: 1.0699999}
|
||||
- {x: -0.044999998, y: 1.41}
|
||||
- {x: -0.035, y: 1.66}
|
||||
- {x: 0.48499998, y: 2.2}
|
||||
- {x: 0.49499997, y: 2.48}
|
||||
- {x: -1.3249999, y: 2.5}
|
||||
- {x: -1.3249999, y: -2.5}
|
||||
- {x: 0.505, y: -2.5}
|
||||
- {x: 0.555, y: -2.12}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2171518497100337372
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -97,7 +164,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 1003080013996268193}
|
||||
- component: {fileID: 4856205316150460481}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 1911291775322313535}
|
||||
m_Layer: 6
|
||||
m_Name: Right_Tile1_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -174,6 +242,65 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &1911291775322313535
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2171518497100337372}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.37, y: 5}
|
||||
newSize: {x: 2.65, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: -0.635, y: 2.5}
|
||||
- {x: -0.835, y: 1.8499999}
|
||||
- {x: -1.185, y: 1.42}
|
||||
- {x: -1.185, y: 1.06}
|
||||
- {x: -1.055, y: 0.69}
|
||||
- {x: -0.185, y: -0.65999997}
|
||||
- {x: -0.175, y: -0.78999996}
|
||||
- {x: -0.585, y: -1.49}
|
||||
- {x: -0.675, y: -2.08}
|
||||
- {x: -0.635, y: -2.5}
|
||||
- {x: 1.185, y: -2.5}
|
||||
- {x: 1.185, y: 2.5}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2956826569642009690
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -10,7 +10,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 7111145574660306503}
|
||||
- component: {fileID: 3889795708575321074}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 3320970211105392876}
|
||||
m_Layer: 6
|
||||
m_Name: Left_Tile2_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -87,6 +88,72 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &3320970211105392876
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 864595161669782950}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.65, y: 5}
|
||||
newSize: {x: 2.37, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: 0.635, y: -1.78}
|
||||
- {x: 0.585, y: -1.27}
|
||||
- {x: 0.36499998, y: -1.14}
|
||||
- {x: 0.415, y: -1.05}
|
||||
- {x: 1.2149999, y: -0.85999995}
|
||||
- {x: 1.3249999, y: -0.7}
|
||||
- {x: 1.3249999, y: -0.35}
|
||||
- {x: 1.145, y: -0.049999997}
|
||||
- {x: 0.13499999, y: 0.45999998}
|
||||
- {x: 0.275, y: 0.87}
|
||||
- {x: 0.265, y: 1.0699999}
|
||||
- {x: -0.044999998, y: 1.41}
|
||||
- {x: -0.035, y: 1.66}
|
||||
- {x: 0.48499998, y: 2.2}
|
||||
- {x: 0.49499997, y: 2.48}
|
||||
- {x: -1.3249999, y: 2.5}
|
||||
- {x: -1.3249999, y: -2.5}
|
||||
- {x: 0.505, y: -2.5}
|
||||
- {x: 0.555, y: -2.12}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2171518497100337372
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -97,7 +164,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 1003080013996268193}
|
||||
- component: {fileID: 4856205316150460481}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 3931598674086383704}
|
||||
m_Layer: 6
|
||||
m_Name: Right_Tile1_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -174,6 +242,65 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &3931598674086383704
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2171518497100337372}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.37, y: 5}
|
||||
newSize: {x: 2.65, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: -0.635, y: 2.5}
|
||||
- {x: -0.835, y: 1.8499999}
|
||||
- {x: -1.185, y: 1.42}
|
||||
- {x: -1.185, y: 1.06}
|
||||
- {x: -1.055, y: 0.69}
|
||||
- {x: -0.185, y: -0.65999997}
|
||||
- {x: -0.175, y: -0.78999996}
|
||||
- {x: -0.585, y: -1.49}
|
||||
- {x: -0.675, y: -2.08}
|
||||
- {x: -0.635, y: -2.5}
|
||||
- {x: 1.185, y: -2.5}
|
||||
- {x: 1.185, y: 2.5}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2956826569642009690
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -10,7 +10,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 7111145574660306503}
|
||||
- component: {fileID: 3889795708575321074}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 1709932086434338450}
|
||||
m_Layer: 6
|
||||
m_Name: Left_Tile2_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -87,6 +88,72 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &1709932086434338450
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 864595161669782950}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.65, y: 5}
|
||||
newSize: {x: 2.37, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: 0.635, y: -1.78}
|
||||
- {x: 0.585, y: -1.27}
|
||||
- {x: 0.36499998, y: -1.14}
|
||||
- {x: 0.415, y: -1.05}
|
||||
- {x: 1.2149999, y: -0.85999995}
|
||||
- {x: 1.3249999, y: -0.7}
|
||||
- {x: 1.3249999, y: -0.35}
|
||||
- {x: 1.145, y: -0.049999997}
|
||||
- {x: 0.13499999, y: 0.45999998}
|
||||
- {x: 0.275, y: 0.87}
|
||||
- {x: 0.265, y: 1.0699999}
|
||||
- {x: -0.044999998, y: 1.41}
|
||||
- {x: -0.035, y: 1.66}
|
||||
- {x: 0.48499998, y: 2.2}
|
||||
- {x: 0.49499997, y: 2.48}
|
||||
- {x: -1.3249999, y: 2.5}
|
||||
- {x: -1.3249999, y: -2.5}
|
||||
- {x: 0.505, y: -2.5}
|
||||
- {x: 0.555, y: -2.12}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2171518497100337372
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -97,7 +164,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 1003080013996268193}
|
||||
- component: {fileID: 4856205316150460481}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 9036823987933958098}
|
||||
m_Layer: 6
|
||||
m_Name: Right_Tile1_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -174,6 +242,70 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &9036823987933958098
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2171518497100337372}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.65, y: 5}
|
||||
newSize: {x: 2.65, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: -0.48499998, y: 2.5}
|
||||
- {x: -0.49499997, y: 2.19}
|
||||
- {x: 0.035, y: 1.66}
|
||||
- {x: 0.035, y: 1.42}
|
||||
- {x: -0.24499999, y: 1.15}
|
||||
- {x: -0.285, y: 0.90999997}
|
||||
- {x: -0.13499999, y: 0.45999998}
|
||||
- {x: -1.115, y: -0.03}
|
||||
- {x: -1.3249999, y: -0.35}
|
||||
- {x: -1.3249999, y: -0.71}
|
||||
- {x: -1.2049999, y: -0.84999996}
|
||||
- {x: -0.36499998, y: -1.0699999}
|
||||
- {x: -0.585, y: -1.27}
|
||||
- {x: -0.625, y: -1.65}
|
||||
- {x: -0.48499998, y: -2.5}
|
||||
- {x: 1.3249999, y: -2.5}
|
||||
- {x: 1.3249999, y: 2.5}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2956826569642009690
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -10,7 +10,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 7111145574660306503}
|
||||
- component: {fileID: 3889795708575321074}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 6967660003360049346}
|
||||
m_Layer: 6
|
||||
m_Name: Left_Tile2_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -87,6 +88,72 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &6967660003360049346
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 864595161669782950}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.65, y: 5}
|
||||
newSize: {x: 2.37, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: 0.635, y: -1.78}
|
||||
- {x: 0.585, y: -1.27}
|
||||
- {x: 0.36499998, y: -1.14}
|
||||
- {x: 0.415, y: -1.05}
|
||||
- {x: 1.2149999, y: -0.85999995}
|
||||
- {x: 1.3249999, y: -0.7}
|
||||
- {x: 1.3249999, y: -0.35}
|
||||
- {x: 1.145, y: -0.049999997}
|
||||
- {x: 0.13499999, y: 0.45999998}
|
||||
- {x: 0.275, y: 0.87}
|
||||
- {x: 0.265, y: 1.0699999}
|
||||
- {x: -0.044999998, y: 1.41}
|
||||
- {x: -0.035, y: 1.66}
|
||||
- {x: 0.48499998, y: 2.2}
|
||||
- {x: 0.49499997, y: 2.48}
|
||||
- {x: -1.3249999, y: 2.5}
|
||||
- {x: -1.3249999, y: -2.5}
|
||||
- {x: 0.505, y: -2.5}
|
||||
- {x: 0.555, y: -2.12}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2171518497100337372
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -97,7 +164,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 1003080013996268193}
|
||||
- component: {fileID: 4856205316150460481}
|
||||
m_Layer: 0
|
||||
- component: {fileID: 5503039050943823342}
|
||||
m_Layer: 6
|
||||
m_Name: Right_Tile1_0
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
@@ -174,6 +242,70 @@ SpriteRenderer:
|
||||
m_WasSpriteAssigned: 1
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!60 &5503039050943823342
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2171518497100337372}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 0
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0.5, y: 0.5}
|
||||
oldSize: {x: 2.65, y: 5}
|
||||
newSize: {x: 2.65, y: 5}
|
||||
adaptiveTilingThreshold: 0.5
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: -0.48499998, y: 2.5}
|
||||
- {x: -0.49499997, y: 2.19}
|
||||
- {x: 0.035, y: 1.66}
|
||||
- {x: 0.035, y: 1.42}
|
||||
- {x: -0.24499999, y: 1.15}
|
||||
- {x: -0.285, y: 0.90999997}
|
||||
- {x: -0.13499999, y: 0.45999998}
|
||||
- {x: -1.115, y: -0.03}
|
||||
- {x: -1.3249999, y: -0.35}
|
||||
- {x: -1.3249999, y: -0.71}
|
||||
- {x: -1.2049999, y: -0.84999996}
|
||||
- {x: -0.36499998, y: -1.0699999}
|
||||
- {x: -0.585, y: -1.27}
|
||||
- {x: -0.625, y: -1.65}
|
||||
- {x: -0.48499998, y: -2.5}
|
||||
- {x: 1.3249999, y: -2.5}
|
||||
- {x: 1.3249999, y: 2.5}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!1 &2956826569642009690
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -492,6 +492,73 @@ Transform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &323864663
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 323864665}
|
||||
- component: {fileID: 323864664}
|
||||
m_Layer: 0
|
||||
m_Name: ObstacleSpawner
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &323864664
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 323864663}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 49ec62157fd945fab730193e9ea0bff7, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
obstaclePrefabs:
|
||||
- {fileID: 4743746373562280435, guid: 315a624eb99600444a51bb1d37c51742, type: 3}
|
||||
spawnInterval: 3
|
||||
spawnIntervalVariation: 1
|
||||
maxSpawnAttempts: 10
|
||||
spawnCollisionRadius: 1
|
||||
spawnDistanceBelowScreen: 2
|
||||
spawnRangeX: 8
|
||||
minMoveSpeed: 1
|
||||
maxMoveSpeed: 4
|
||||
useObjectPooling: 1
|
||||
maxPerPrefabPoolSize: 15
|
||||
totalMaxPoolSize: 30
|
||||
tileLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 576
|
||||
obstacleLayer: 11
|
||||
onObstacleSpawned:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
onObstacleDestroyed:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!4 &323864665
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 323864663}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -0.66745, y: 0.68592, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &424805724
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -531,8 +598,8 @@ MonoBehaviour:
|
||||
probabilityIncreaseRate: 0.01
|
||||
guaranteedSpawnTime: 10
|
||||
spawnCooldown: 5
|
||||
basePoints: 100
|
||||
depthMultiplier: 10
|
||||
basePoints: 10
|
||||
depthMultiplier: 2
|
||||
--- !u!4 &424805726
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -646,9 +713,14 @@ GameObject:
|
||||
- component: {fileID: 747976397}
|
||||
- component: {fileID: 747976398}
|
||||
- component: {fileID: 747976399}
|
||||
- component: {fileID: 747976400}
|
||||
- component: {fileID: 747976401}
|
||||
- component: {fileID: 747976402}
|
||||
- component: {fileID: 747976403}
|
||||
- component: {fileID: 747976404}
|
||||
m_Layer: 0
|
||||
m_Name: BottleMarine
|
||||
m_TagString: Untagged
|
||||
m_TagString: Player
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
@@ -701,6 +773,181 @@ MonoBehaviour:
|
||||
verticalAmplitude: 0.2
|
||||
velocitySmoothing: 10
|
||||
rotationSmoothing: 10
|
||||
--- !u!114 &747976400
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 747976396}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8222f0e3aeeb4fc4975aaead6cf7afbe, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
damageImmunityDuration: 1
|
||||
obstacleLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 64
|
||||
blockInputDuringImmunity: 0
|
||||
playerCharacter: {fileID: 747976396}
|
||||
playerController: {fileID: 747976398}
|
||||
bumpMode: 0
|
||||
bumpForce: 5
|
||||
smoothMoveSpeed: 8
|
||||
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
|
||||
blockInputDuringBump: 1
|
||||
--- !u!114 &747976401
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 747976396}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: c9c18dbd013d42ae8c221e6205e4d49c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
damageImmunityDuration: 1
|
||||
obstacleLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 2048
|
||||
blockInputDuringImmunity: 0
|
||||
playerCharacter: {fileID: 747976396}
|
||||
playerController: {fileID: 747976398}
|
||||
--- !u!60 &747976402
|
||||
PolygonCollider2D:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 747976396}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_Density: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_ForceSendLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ForceReceiveLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_ContactCaptureLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_CallbackLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_IsTrigger: 1
|
||||
m_UsedByEffector: 0
|
||||
m_CompositeOperation: 0
|
||||
m_CompositeOrder: 0
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_SpriteTilingProperty:
|
||||
border: {x: 0, y: 0, z: 0, w: 0}
|
||||
pivot: {x: 0, y: 0}
|
||||
oldSize: {x: 0, y: 0}
|
||||
newSize: {x: 0, y: 0}
|
||||
adaptiveTilingThreshold: 0
|
||||
drawMode: 0
|
||||
adaptiveTiling: 0
|
||||
m_AutoTiling: 0
|
||||
m_Points:
|
||||
m_Paths:
|
||||
- - {x: -0.7990411, y: 2.6669068}
|
||||
- {x: -0.9294033, y: 2.502293}
|
||||
- {x: -1.005722, y: 2.1038642}
|
||||
- {x: -0.9038467, y: 1.652195}
|
||||
- {x: -0.7700032, y: 1.5415951}
|
||||
- {x: 0.28712562, y: 1.5507066}
|
||||
- {x: 0.48471105, y: 1.6954134}
|
||||
- {x: 0.5636723, y: 1.8504347}
|
||||
- {x: 0.63941646, y: 1.8455681}
|
||||
- {x: 0.69998366, y: 1.761524}
|
||||
- {x: 0.95461226, y: 1.7980554}
|
||||
- {x: 1.0003967, y: 2.1063333}
|
||||
- {x: 0.94308287, y: 2.433347}
|
||||
- {x: 0.8147154, y: 2.464337}
|
||||
- {x: 0.6922814, y: 2.465095}
|
||||
- {x: 0.6549096, y: 2.3498883}
|
||||
- {x: 0.5677627, y: 2.3522122}
|
||||
- {x: 0.38462818, y: 2.6102822}
|
||||
- {x: 0.20396256, y: 2.6596293}
|
||||
m_UseDelaunayMesh: 0
|
||||
--- !u!50 &747976403
|
||||
Rigidbody2D:
|
||||
serializedVersion: 5
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 747976396}
|
||||
m_BodyType: 1
|
||||
m_Simulated: 1
|
||||
m_UseFullKinematicContacts: 0
|
||||
m_UseAutoMass: 0
|
||||
m_Mass: 1
|
||||
m_LinearDamping: 0
|
||||
m_AngularDamping: 0.05
|
||||
m_GravityScale: 1
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_Interpolate: 0
|
||||
m_SleepingMode: 1
|
||||
m_CollisionDetection: 0
|
||||
m_Constraints: 0
|
||||
--- !u!114 &747976404
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 747976396}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d8ea29cc80524de8affe17b930cd75c1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
damageBlinkColor: {r: 1, g: 0, b: 0, a: 1}
|
||||
blinkRate: 0.15
|
||||
damageColorAlpha: 0.7
|
||||
targetSpriteRenderer: {fileID: 730962734}
|
||||
--- !u!1 &824396214
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1847,3 +2094,4 @@ SceneRoots:
|
||||
- {fileID: 424805726}
|
||||
- {fileID: 116234201}
|
||||
- {fileID: 824396217}
|
||||
- {fileID: 323864665}
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Pooling;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single bubble, handling its movement, wobble effect, scaling, and sprite assignment.
|
||||
/// Uses coroutines for better performance instead of Update() calls.
|
||||
/// </summary>
|
||||
public class Bubble : MonoBehaviour, IPoolableWithReference<BubblePool>
|
||||
{
|
||||
public float speed = 1f;
|
||||
public float wobbleSpeed = 1f;
|
||||
private SpriteRenderer spriteRenderer;
|
||||
private SpriteRenderer bubbleSpriteRenderer; // Renamed from bottleSpriteRenderer
|
||||
private SpriteRenderer bubbleSpriteRenderer;
|
||||
private float timeOffset;
|
||||
private float minScale = 0.2f;
|
||||
private float maxScale = 1.2f;
|
||||
private float baseScale = 1f; // Added to store the initial scale
|
||||
private float baseScale = 1f;
|
||||
|
||||
private Camera mainCamera; // Cache camera reference
|
||||
private BubblePool parentPool; // Reference to the pool this bubble came from
|
||||
private Camera mainCamera;
|
||||
private BubblePool parentPool;
|
||||
|
||||
// Coroutine references
|
||||
private Coroutine _movementCoroutine;
|
||||
private Coroutine _wobbleCoroutine;
|
||||
private Coroutine _offScreenCheckCoroutine;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// Cache references and randomize time offset for wobble
|
||||
spriteRenderer = GetComponent<SpriteRenderer>();
|
||||
timeOffset = Random.value * 100f;
|
||||
|
||||
// Find the child named "BubbleSprite" and get its SpriteRenderer
|
||||
Transform bubbleSpriteTransform = transform.Find("BubbleSprite");
|
||||
if (bubbleSpriteTransform != null)
|
||||
@@ -36,20 +44,101 @@ namespace Minigames.DivingForPictures
|
||||
mainCamera = Camera.main;
|
||||
}
|
||||
|
||||
void Update()
|
||||
private void OnEnable()
|
||||
{
|
||||
// Move bubble upward
|
||||
transform.position += Vector3.up * (speed * Time.deltaTime);
|
||||
|
||||
// Wobble effect (smooth oscillation between min and max scale)
|
||||
float t = (Mathf.Sin((Time.time + timeOffset) * wobbleSpeed) + 1f) * 0.5f; // t in [0,1]
|
||||
float wobbleFactor = Mathf.Lerp(minScale, maxScale, t);
|
||||
transform.localScale = Vector3.one * (baseScale * wobbleFactor);
|
||||
|
||||
// Destroy when off screen - using cached camera reference
|
||||
if (mainCamera != null && transform.position.y > mainCamera.orthographicSize + 2f)
|
||||
StartBubbleBehavior();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
StopBubbleBehavior();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts all bubble behavior coroutines
|
||||
/// </summary>
|
||||
private void StartBubbleBehavior()
|
||||
{
|
||||
_movementCoroutine = StartCoroutine(MovementCoroutine());
|
||||
_wobbleCoroutine = StartCoroutine(WobbleCoroutine());
|
||||
_offScreenCheckCoroutine = StartCoroutine(OffScreenCheckCoroutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops all bubble behavior coroutines
|
||||
/// </summary>
|
||||
private void StopBubbleBehavior()
|
||||
{
|
||||
if (_movementCoroutine != null)
|
||||
{
|
||||
OnBubbleDestroy();
|
||||
StopCoroutine(_movementCoroutine);
|
||||
_movementCoroutine = null;
|
||||
}
|
||||
|
||||
if (_wobbleCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_wobbleCoroutine);
|
||||
_wobbleCoroutine = null;
|
||||
}
|
||||
|
||||
if (_offScreenCheckCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_offScreenCheckCoroutine);
|
||||
_offScreenCheckCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles bubble upward movement
|
||||
/// </summary>
|
||||
private IEnumerator MovementCoroutine()
|
||||
{
|
||||
while (enabled && gameObject.activeInHierarchy)
|
||||
{
|
||||
// Move bubble upward
|
||||
transform.position += Vector3.up * (speed * Time.deltaTime);
|
||||
|
||||
// Wait for next frame
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles the wobble scaling effect
|
||||
/// </summary>
|
||||
private IEnumerator WobbleCoroutine()
|
||||
{
|
||||
while (enabled && gameObject.activeInHierarchy)
|
||||
{
|
||||
// Wobble effect (smooth oscillation between min and max scale)
|
||||
float t = (Mathf.Sin((Time.time + timeOffset) * wobbleSpeed) + 1f) * 0.5f; // t in [0,1]
|
||||
float wobbleFactor = Mathf.Lerp(minScale, maxScale, t);
|
||||
transform.localScale = Vector3.one * (baseScale * wobbleFactor);
|
||||
|
||||
// Wait for next frame
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that checks if bubble has moved off-screen
|
||||
/// Runs at a lower frequency for better performance
|
||||
/// </summary>
|
||||
private IEnumerator OffScreenCheckCoroutine()
|
||||
{
|
||||
const float checkInterval = 0.1f; // Check every 100ms instead of every frame
|
||||
|
||||
while (enabled && gameObject.activeInHierarchy)
|
||||
{
|
||||
// Check if bubble is off screen
|
||||
if (mainCamera != null && transform.position.y > mainCamera.orthographicSize + 2f)
|
||||
{
|
||||
OnBubbleDestroy();
|
||||
yield break; // Exit coroutine since bubble is being destroyed
|
||||
}
|
||||
|
||||
// Wait for the check interval
|
||||
yield return new WaitForSeconds(checkInterval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +232,24 @@ namespace Minigames.DivingForPictures
|
||||
minScale = min;
|
||||
maxScale = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the movement speed at runtime
|
||||
/// </summary>
|
||||
/// <param name="newSpeed">New movement speed</param>
|
||||
public void SetSpeed(float newSpeed)
|
||||
{
|
||||
speed = newSpeed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the wobble speed at runtime
|
||||
/// </summary>
|
||||
/// <param name="newWobbleSpeed">New wobble speed</param>
|
||||
public void SetWobbleSpeed(float newWobbleSpeed)
|
||||
{
|
||||
wobbleSpeed = newWobbleSpeed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the bubble state for reuse from object pool
|
||||
|
||||
283
Assets/Scripts/Minigames/DivingForPictures/FloatingObstacle.cs
Normal file
283
Assets/Scripts/Minigames/DivingForPictures/FloatingObstacle.cs
Normal file
@@ -0,0 +1,283 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Pooling;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Complete floating obstacle component that handles movement and pooling.
|
||||
/// Obstacles move upward toward the surface. Collision detection is handled by the player.
|
||||
/// Once an obstacle hits the player, its collider is disabled to prevent further collisions.
|
||||
/// Uses coroutines for better performance instead of Update() calls.
|
||||
/// </summary>
|
||||
public class FloatingObstacle : MonoBehaviour, IPoolable
|
||||
{
|
||||
[Header("Obstacle Properties")]
|
||||
[Tooltip("Index of the prefab this obstacle was created from")]
|
||||
[SerializeField] private int prefabIndex;
|
||||
|
||||
[Tooltip("Movement speed of this obstacle")]
|
||||
[SerializeField] private float moveSpeed = 2f;
|
||||
|
||||
[Header("Movement")]
|
||||
[Tooltip("Whether this obstacle moves (can be disabled for static obstacles)")]
|
||||
[SerializeField] private bool enableMovement = true;
|
||||
|
||||
[Header("References")]
|
||||
[Tooltip("Reference to the spawner that created this obstacle")]
|
||||
[SerializeField] private ObstacleSpawner spawner;
|
||||
|
||||
// Public properties
|
||||
public int PrefabIndex
|
||||
{
|
||||
get => prefabIndex;
|
||||
set => prefabIndex = value;
|
||||
}
|
||||
|
||||
public float MoveSpeed
|
||||
{
|
||||
get => moveSpeed;
|
||||
set => moveSpeed = value;
|
||||
}
|
||||
|
||||
// Private fields
|
||||
private Collider2D _collider;
|
||||
private Camera _mainCamera;
|
||||
private float _screenTop;
|
||||
private Coroutine _movementCoroutine;
|
||||
private Coroutine _offScreenCheckCoroutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_collider = GetComponent<Collider2D>();
|
||||
|
||||
if (_collider == null)
|
||||
{
|
||||
_collider = GetComponentInChildren<Collider2D>();
|
||||
}
|
||||
|
||||
if (_collider == null)
|
||||
{
|
||||
Debug.LogError($"[FloatingObstacle] No Collider2D found on {gameObject.name}!");
|
||||
}
|
||||
|
||||
_mainCamera = Camera.main;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
StartObstacleBehavior();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
StopObstacleBehavior();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the obstacle behavior coroutines
|
||||
/// </summary>
|
||||
private void StartObstacleBehavior()
|
||||
{
|
||||
if (enableMovement)
|
||||
{
|
||||
_movementCoroutine = StartCoroutine(MovementCoroutine());
|
||||
}
|
||||
|
||||
_offScreenCheckCoroutine = StartCoroutine(OffScreenCheckCoroutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops all obstacle behavior coroutines
|
||||
/// </summary>
|
||||
private void StopObstacleBehavior()
|
||||
{
|
||||
if (_movementCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_movementCoroutine);
|
||||
_movementCoroutine = null;
|
||||
}
|
||||
|
||||
if (_offScreenCheckCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_offScreenCheckCoroutine);
|
||||
_offScreenCheckCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles obstacle movement
|
||||
/// </summary>
|
||||
private IEnumerator MovementCoroutine()
|
||||
{
|
||||
while (enabled && gameObject.activeInHierarchy)
|
||||
{
|
||||
// Move the obstacle upward
|
||||
transform.position += Vector3.up * (moveSpeed * Time.deltaTime);
|
||||
|
||||
// Wait for next frame
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that checks if obstacle has moved off-screen
|
||||
/// Runs at a lower frequency than movement for better performance
|
||||
/// </summary>
|
||||
private IEnumerator OffScreenCheckCoroutine()
|
||||
{
|
||||
const float checkInterval = 0.2f; // Check every 200ms instead of every frame
|
||||
|
||||
while (enabled && gameObject.activeInHierarchy)
|
||||
{
|
||||
CheckIfOffScreen();
|
||||
|
||||
// Wait for the check interval
|
||||
yield return new WaitForSeconds(checkInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the collider after hitting the player to prevent further collisions
|
||||
/// This is more performant than tracking hit state
|
||||
/// </summary>
|
||||
public void MarkDamageDealt()
|
||||
{
|
||||
if (_collider != null && _collider.enabled)
|
||||
{
|
||||
_collider.enabled = false;
|
||||
Debug.Log($"[FloatingObstacle] Obstacle {gameObject.name} hit player - collider disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the obstacle has moved off-screen and should be despawned
|
||||
/// </summary>
|
||||
private void CheckIfOffScreen()
|
||||
{
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
if (_mainCamera == null) return;
|
||||
}
|
||||
|
||||
// Always recalculate screen bounds to ensure accuracy
|
||||
Vector3 topWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 1f, _mainCamera.transform.position.z));
|
||||
_screenTop = topWorldPoint.y;
|
||||
|
||||
// Check if obstacle is significantly above screen top (obstacles move upward)
|
||||
// Use a larger buffer to ensure obstacles are truly off-screen before returning to pool
|
||||
if (transform.position.y > _screenTop + 5f)
|
||||
{
|
||||
Debug.Log($"[FloatingObstacle] {gameObject.name} off-screen at Y:{transform.position.y:F2}, screen top:{_screenTop:F2}");
|
||||
ReturnToPool();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns this obstacle to the spawner's pool
|
||||
/// </summary>
|
||||
private void ReturnToPool()
|
||||
{
|
||||
// CRITICAL: Stop all behavior first to prevent race conditions
|
||||
// This ensures no more off-screen checks or movement happen during pool return
|
||||
StopObstacleBehavior();
|
||||
|
||||
if (spawner != null)
|
||||
{
|
||||
spawner.ReturnObstacleToPool(gameObject, prefabIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find the spawner instead of destroying the object
|
||||
ObstacleSpawner foundSpawner = FindFirstObjectByType<ObstacleSpawner>();
|
||||
if (foundSpawner != null)
|
||||
{
|
||||
Debug.LogWarning($"[FloatingObstacle] Obstacle {gameObject.name} lost spawner reference, found replacement spawner");
|
||||
spawner = foundSpawner;
|
||||
spawner.ReturnObstacleToPool(gameObject, prefabIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No spawner found - just deactivate the object instead of destroying it
|
||||
Debug.LogWarning($"[FloatingObstacle] No spawner found for {gameObject.name}, deactivating safely");
|
||||
gameObject.SetActive(false);
|
||||
|
||||
// Move to a safe location to avoid interference
|
||||
transform.position = new Vector3(1000f, 1000f, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the spawner reference for this obstacle
|
||||
/// </summary>
|
||||
/// <param name="obstacleSpawner">The spawner that created this obstacle</param>
|
||||
public void SetSpawner(ObstacleSpawner obstacleSpawner)
|
||||
{
|
||||
spawner = obstacleSpawner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the obstacle is retrieved from the pool
|
||||
/// </summary>
|
||||
public void OnSpawn()
|
||||
{
|
||||
// Reset all state first
|
||||
_screenTop = 0f; // Reset cached screen bounds
|
||||
_mainCamera = Camera.main; // Refresh camera reference
|
||||
|
||||
// Re-enable the collider for reuse
|
||||
if (_collider != null)
|
||||
{
|
||||
_collider.enabled = true;
|
||||
}
|
||||
|
||||
Debug.Log($"[FloatingObstacle] Obstacle {gameObject.name} spawned from pool");
|
||||
|
||||
// Note: Don't start coroutines here - OnEnable() will handle that when SetActive(true) is called
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the obstacle is returned to the pool
|
||||
/// </summary>
|
||||
public void OnDespawn()
|
||||
{
|
||||
// Stop all coroutines before returning to pool
|
||||
StopObstacleBehavior();
|
||||
|
||||
// Re-enable collider for next use (in case it was disabled)
|
||||
if (_collider != null)
|
||||
{
|
||||
_collider.enabled = true;
|
||||
}
|
||||
|
||||
Debug.Log($"[FloatingObstacle] Obstacle {gameObject.name} despawned to pool");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to manually trigger return to pool (for external systems)
|
||||
/// </summary>
|
||||
public void ForceReturnToPool()
|
||||
{
|
||||
ReturnToPool();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to enable/disable movement at runtime
|
||||
/// </summary>
|
||||
public void SetMovementEnabled(bool enabled)
|
||||
{
|
||||
if (enableMovement == enabled) return;
|
||||
|
||||
enableMovement = enabled;
|
||||
|
||||
// Restart coroutines to apply movement change
|
||||
if (gameObject.activeInHierarchy)
|
||||
{
|
||||
StopObstacleBehavior();
|
||||
StartObstacleBehavior();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32718083aef44be2a4318681fcdf5b2e
|
||||
timeCreated: 1758117709
|
||||
@@ -0,0 +1,44 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Collision behavior that handles damage from mobile obstacles.
|
||||
/// Uses trigger-based collision detection with shared immunity state.
|
||||
/// </summary>
|
||||
public class ObstacleCollision : PlayerCollisionBehavior
|
||||
{
|
||||
protected override void HandleCollisionResponse(Collider2D obstacle)
|
||||
{
|
||||
// Mark the obstacle as having dealt damage to prevent multiple hits
|
||||
FloatingObstacle obstacleComponent = obstacle.GetComponent<FloatingObstacle>();
|
||||
if (obstacleComponent != null)
|
||||
{
|
||||
obstacleComponent.MarkDamageDealt();
|
||||
}
|
||||
|
||||
Debug.Log($"[ObstacleCollision] Player hit by obstacle {obstacle.gameObject.name}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to prevent input blocking during damage immunity
|
||||
/// Since obstacles pass through the player, we don't want to block input
|
||||
/// </summary>
|
||||
protected override void OnImmunityStart()
|
||||
{
|
||||
Debug.Log($"[ObstacleCollision] Damage immunity started for {damageImmunityDuration} seconds");
|
||||
|
||||
// Don't block input for obstacle damage - let player keep moving
|
||||
// The shared immunity system will handle the collision prevention
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to handle immunity end
|
||||
/// </summary>
|
||||
protected override void OnImmunityEnd()
|
||||
{
|
||||
Debug.Log($"[ObstacleCollision] Damage immunity ended");
|
||||
// No special handling needed - shared immunity system handles collider re-enabling
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9c18dbd013d42ae8c221e6205e4d49c
|
||||
timeCreated: 1758116850
|
||||
54
Assets/Scripts/Minigames/DivingForPictures/ObstaclePool.cs
Normal file
54
Assets/Scripts/Minigames/DivingForPictures/ObstaclePool.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
using Pooling;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages a pool of floating obstacle objects to reduce garbage collection overhead.
|
||||
/// Optimized for handling a large number of different obstacle prefab types.
|
||||
/// </summary>
|
||||
public class ObstaclePool : MultiPrefabPool<FloatingObstacle>
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns an obstacle to the pool
|
||||
/// </summary>
|
||||
/// <param name="obstacle">The obstacle to return to the pool</param>
|
||||
/// <param name="prefabIndex">The index of the prefab this obstacle was created from</param>
|
||||
public void ReturnObstacle(GameObject obstacle, int prefabIndex)
|
||||
{
|
||||
if (obstacle != null)
|
||||
{
|
||||
FloatingObstacle obstacleComponent = obstacle.GetComponent<FloatingObstacle>();
|
||||
if (obstacleComponent != null)
|
||||
{
|
||||
Debug.Log($"[ObstaclePool] Returning obstacle {obstacle.name} to pool");
|
||||
Return(obstacleComponent, prefabIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Attempted to return a GameObject without a FloatingObstacle component: {obstacle.name}");
|
||||
Destroy(obstacle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an obstacle from the pool, or creates a new one if the pool is empty
|
||||
/// </summary>
|
||||
/// <returns>An obstacle instance ready to use</returns>
|
||||
public GameObject GetObstacle(int prefabIndex)
|
||||
{
|
||||
Debug.Log($"[ObstaclePool] GetObstacle called for prefab index {prefabIndex}");
|
||||
FloatingObstacle obstacleComponent = Get(prefabIndex);
|
||||
|
||||
if (obstacleComponent == null)
|
||||
{
|
||||
Debug.LogError($"[ObstaclePool] Get() returned null for prefab index {prefabIndex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.Log($"[ObstaclePool] Get() returned obstacle {obstacleComponent.name}, active state: {obstacleComponent.gameObject.activeInHierarchy}");
|
||||
return obstacleComponent.gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a53ba79246a94dc4a71d2fb0d7214cfb
|
||||
timeCreated: 1758116804
|
||||
486
Assets/Scripts/Minigames/DivingForPictures/ObstacleSpawner.cs
Normal file
486
Assets/Scripts/Minigames/DivingForPictures/ObstacleSpawner.cs
Normal file
@@ -0,0 +1,486 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Pooling;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Spawns and manages mobile obstacles for the diving minigame.
|
||||
/// Uses object pooling and validates spawn positions to avoid colliding with tiles.
|
||||
/// </summary>
|
||||
public class ObstacleSpawner : MonoBehaviour
|
||||
{
|
||||
[Header("Obstacle Prefabs")]
|
||||
[Tooltip("List of possible obstacle prefabs to spawn")]
|
||||
[SerializeField] private List<GameObject> obstaclePrefabs;
|
||||
|
||||
[Header("Spawn Settings")]
|
||||
[Tooltip("Time interval between spawn attempts (in seconds)")]
|
||||
[SerializeField] private float spawnInterval = 2f;
|
||||
|
||||
[Tooltip("Random variation in spawn timing (+/- seconds)")]
|
||||
[SerializeField] private float spawnIntervalVariation = 0.5f;
|
||||
|
||||
[Tooltip("Maximum number of spawn position attempts before skipping")]
|
||||
[SerializeField] private int maxSpawnAttempts = 10;
|
||||
|
||||
[Tooltip("Radius around spawn point to check for tile collisions")]
|
||||
[SerializeField] private float spawnCollisionRadius = 1f;
|
||||
|
||||
[Header("Spawn Position")]
|
||||
[Tooltip("How far below screen to spawn obstacles")]
|
||||
[SerializeField] private float spawnDistanceBelowScreen = 2f;
|
||||
|
||||
[Tooltip("Horizontal spawn range (distance from center)")]
|
||||
[SerializeField] private float spawnRangeX = 8f;
|
||||
|
||||
[Header("Obstacle Properties Randomization")]
|
||||
[Tooltip("Minimum movement speed for spawned obstacles")]
|
||||
[SerializeField] private float minMoveSpeed = 1f;
|
||||
|
||||
[Tooltip("Maximum movement speed for spawned obstacles")]
|
||||
[SerializeField] private float maxMoveSpeed = 4f;
|
||||
|
||||
[Header("Object Pooling")]
|
||||
[Tooltip("Whether to use object pooling for obstacles")]
|
||||
[SerializeField] private bool useObjectPooling = true;
|
||||
|
||||
[Tooltip("Maximum objects per prefab type in pool")]
|
||||
[SerializeField] private int maxPerPrefabPoolSize = 3;
|
||||
|
||||
[Tooltip("Total maximum pool size across all prefab types")]
|
||||
[SerializeField] private int totalMaxPoolSize = 15;
|
||||
|
||||
[Header("Layer Settings")]
|
||||
[Tooltip("Layer mask for tile collision detection during spawn position validation")]
|
||||
[SerializeField] private LayerMask tileLayerMask = -1; // Let user configure which layers to avoid
|
||||
|
||||
[Tooltip("Target layer for spawned obstacles - obstacles will be placed on this layer")]
|
||||
[SerializeField] private int obstacleLayer = 11; // Default to layer 11, but configurable
|
||||
|
||||
[Header("Events")]
|
||||
[Tooltip("Called when an obstacle is spawned")]
|
||||
public UnityEvent<GameObject> onObstacleSpawned;
|
||||
|
||||
[Tooltip("Called when an obstacle is returned to pool")]
|
||||
public UnityEvent<GameObject> onObstacleDestroyed;
|
||||
|
||||
// Private fields
|
||||
private ObstaclePool _obstaclePool;
|
||||
private Camera _mainCamera;
|
||||
private float _screenBottom;
|
||||
private float _spawnRangeX;
|
||||
private Coroutine _spawnCoroutine;
|
||||
private readonly List<GameObject> _activeObstacles = new List<GameObject>();
|
||||
private int _obstacleCounter = 0; // Counter for unique obstacle naming
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
|
||||
// Validate obstacle prefabs
|
||||
ValidateObstaclePrefabs();
|
||||
|
||||
if (useObjectPooling)
|
||||
{
|
||||
InitializeObjectPool();
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
CalculateScreenBounds();
|
||||
StartSpawning();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
StopSpawning();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that all prefabs have required components
|
||||
/// </summary>
|
||||
private void ValidateObstaclePrefabs()
|
||||
{
|
||||
for (int i = 0; i < obstaclePrefabs.Count; i++)
|
||||
{
|
||||
if (obstaclePrefabs[i] == null) continue;
|
||||
|
||||
// Check if the prefab has a FloatingObstacle component
|
||||
if (obstaclePrefabs[i].GetComponent<FloatingObstacle>() == null)
|
||||
{
|
||||
Debug.LogWarning($"Obstacle prefab {obstaclePrefabs[i].name} does not have a FloatingObstacle component. Adding one automatically.");
|
||||
obstaclePrefabs[i].AddComponent<FloatingObstacle>();
|
||||
}
|
||||
|
||||
// Ensure the prefab is on the correct layer (using configurable obstacleLayer)
|
||||
if (obstaclePrefabs[i].layer != obstacleLayer)
|
||||
{
|
||||
Debug.LogWarning($"Obstacle prefab {obstaclePrefabs[i].name} is not on the configured obstacle layer ({obstacleLayer}). Setting layer automatically.");
|
||||
SetLayerRecursively(obstaclePrefabs[i], obstacleLayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the layer of a GameObject and all its children
|
||||
/// </summary>
|
||||
private void SetLayerRecursively(GameObject obj, int layer)
|
||||
{
|
||||
obj.layer = layer;
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
SetLayerRecursively(child.gameObject, layer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the object pool system
|
||||
/// </summary>
|
||||
private void InitializeObjectPool()
|
||||
{
|
||||
GameObject poolGO = new GameObject("ObstaclePool");
|
||||
poolGO.transform.SetParent(transform);
|
||||
_obstaclePool = poolGO.AddComponent<ObstaclePool>();
|
||||
|
||||
// Set up pool configuration
|
||||
_obstaclePool.maxPerPrefabPoolSize = maxPerPrefabPoolSize;
|
||||
_obstaclePool.totalMaxPoolSize = totalMaxPoolSize;
|
||||
|
||||
// Convert GameObject list to FloatingObstacle list
|
||||
List<FloatingObstacle> prefabObstacles = new List<FloatingObstacle>(obstaclePrefabs.Count);
|
||||
foreach (var prefab in obstaclePrefabs)
|
||||
{
|
||||
if (prefab != null)
|
||||
{
|
||||
FloatingObstacle obstacleComponent = prefab.GetComponent<FloatingObstacle>();
|
||||
if (obstacleComponent != null)
|
||||
{
|
||||
prefabObstacles.Add(obstacleComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Obstacle prefab {prefab.name} is missing a FloatingObstacle component!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the pool
|
||||
_obstaclePool.Initialize(prefabObstacles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate screen bounds in world space dynamically
|
||||
/// </summary>
|
||||
private void CalculateScreenBounds()
|
||||
{
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
Debug.LogError("[ObstacleSpawner] No main camera found!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate screen bottom (Y spawn position will be 2 units below this)
|
||||
Vector3 bottomWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 0f, _mainCamera.nearClipPlane));
|
||||
_screenBottom = bottomWorldPoint.y;
|
||||
|
||||
// Calculate screen width in world units
|
||||
Vector3 leftWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0f, 0.5f, _mainCamera.nearClipPlane));
|
||||
Vector3 rightWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(1f, 0.5f, _mainCamera.nearClipPlane));
|
||||
float screenWidth = rightWorldPoint.x - leftWorldPoint.x;
|
||||
|
||||
// Calculate spawn range based on 80% of screen width (40% on each side from center)
|
||||
_spawnRangeX = (screenWidth * 0.8f) / 2f;
|
||||
|
||||
Debug.Log($"[ObstacleSpawner] Screen calculated - Width: {screenWidth:F2}, Bottom: {_screenBottom:F2}, Spawn Range X: ±{_spawnRangeX:F2}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the obstacle spawning coroutine
|
||||
/// </summary>
|
||||
public void StartSpawning()
|
||||
{
|
||||
if (_spawnCoroutine == null)
|
||||
{
|
||||
_spawnCoroutine = StartCoroutine(SpawnObstaclesCoroutine());
|
||||
Debug.Log("[ObstacleSpawner] Started spawning obstacles");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the obstacle spawning coroutine
|
||||
/// </summary>
|
||||
public void StopSpawning()
|
||||
{
|
||||
if (_spawnCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_spawnCoroutine);
|
||||
_spawnCoroutine = null;
|
||||
Debug.Log("[ObstacleSpawner] Stopped spawning obstacles");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main spawning coroutine that runs continuously
|
||||
/// </summary>
|
||||
private IEnumerator SpawnObstaclesCoroutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Calculate next spawn time with variation
|
||||
float nextSpawnTime = spawnInterval + Random.Range(-spawnIntervalVariation, spawnIntervalVariation);
|
||||
nextSpawnTime = Mathf.Max(0.1f, nextSpawnTime); // Ensure minimum interval
|
||||
|
||||
yield return new WaitForSeconds(nextSpawnTime);
|
||||
|
||||
// Attempt to spawn an obstacle
|
||||
TrySpawnObstacle();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn an obstacle at a valid position
|
||||
/// </summary>
|
||||
private void TrySpawnObstacle()
|
||||
{
|
||||
Debug.Log($"[ObstacleSpawner] TrySpawnObstacle called at {Time.time:F2}");
|
||||
|
||||
if (obstaclePrefabs == null || obstaclePrefabs.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[ObstacleSpawner] No obstacle prefabs available for spawning!");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 spawnPosition;
|
||||
bool foundValidPosition = false;
|
||||
|
||||
// Try to find a valid spawn position
|
||||
for (int attempts = 0; attempts < maxSpawnAttempts; attempts++)
|
||||
{
|
||||
spawnPosition = GetRandomSpawnPosition();
|
||||
|
||||
if (IsValidSpawnPosition(spawnPosition))
|
||||
{
|
||||
Debug.Log($"[ObstacleSpawner] Found valid position at {spawnPosition} after {attempts + 1} attempts");
|
||||
SpawnObstacleAt(spawnPosition);
|
||||
foundValidPosition = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"[ObstacleSpawner] Position {spawnPosition} invalid (attempt {attempts + 1}/{maxSpawnAttempts})");
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundValidPosition)
|
||||
{
|
||||
Debug.LogWarning($"[ObstacleSpawner] SPAWN MISSED: Could not find valid spawn position after {maxSpawnAttempts} attempts at {Time.time:F2}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random spawn position below the screen
|
||||
/// </summary>
|
||||
private Vector3 GetRandomSpawnPosition()
|
||||
{
|
||||
// Use dynamically calculated spawn range (80% of screen width)
|
||||
float randomX = Random.Range(-_spawnRangeX, _spawnRangeX);
|
||||
// Spawn 2 units below screen bottom
|
||||
float spawnY = _screenBottom - 2f;
|
||||
|
||||
return new Vector3(randomX, spawnY, 0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a spawn position is valid (not colliding with tiles)
|
||||
/// </summary>
|
||||
private bool IsValidSpawnPosition(Vector3 position)
|
||||
{
|
||||
// Use OverlapCircle to check for collisions with tiles
|
||||
Collider2D collision = Physics2D.OverlapCircle(position, spawnCollisionRadius, tileLayerMask);
|
||||
return collision == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an obstacle at the specified position
|
||||
/// </summary>
|
||||
private void SpawnObstacleAt(Vector3 position)
|
||||
{
|
||||
Debug.Log($"[ObstacleSpawner] SpawnObstacleAt called for position {position}");
|
||||
|
||||
// Select random prefab
|
||||
int prefabIndex = Random.Range(0, obstaclePrefabs.Count);
|
||||
GameObject prefab = obstaclePrefabs[prefabIndex];
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"[ObstacleSpawner] SPAWN FAILED: Obstacle prefab at index {prefabIndex} is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject obstacle;
|
||||
|
||||
// Spawn using pool or instantiate directly
|
||||
if (useObjectPooling && _obstaclePool != null)
|
||||
{
|
||||
Debug.Log($"[ObstacleSpawner] Requesting obstacle from pool (prefab index {prefabIndex})");
|
||||
obstacle = _obstaclePool.GetObstacle(prefabIndex);
|
||||
if (obstacle == null)
|
||||
{
|
||||
Debug.LogError($"[ObstacleSpawner] SPAWN FAILED: Failed to get obstacle from pool for prefab index {prefabIndex}!");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"[ObstacleSpawner] Got obstacle {obstacle.name} from pool, active state: {obstacle.activeInHierarchy}");
|
||||
|
||||
// FORCE ACTIVATION - bypass pool issues
|
||||
if (!obstacle.activeInHierarchy)
|
||||
{
|
||||
Debug.LogWarning($"[ObstacleSpawner] Pool returned inactive object {obstacle.name}, force activating!");
|
||||
obstacle.SetActive(true);
|
||||
Debug.Log($"[ObstacleSpawner] After force activation, {obstacle.name} active state: {obstacle.activeInHierarchy}");
|
||||
}
|
||||
|
||||
obstacle.transform.position = position;
|
||||
obstacle.transform.rotation = prefab.transform.rotation;
|
||||
obstacle.transform.SetParent(transform);
|
||||
Debug.Log($"[ObstacleSpawner] After positioning, obstacle {obstacle.name} active state: {obstacle.activeInHierarchy}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"[ObstacleSpawner] Instantiating new obstacle (pooling disabled)");
|
||||
obstacle = Instantiate(prefab, position, prefab.transform.rotation, transform);
|
||||
}
|
||||
|
||||
// Assign unique name with counter
|
||||
_obstacleCounter++;
|
||||
string oldName = obstacle.name;
|
||||
obstacle.name = $"Obstacle{_obstacleCounter:D3}";
|
||||
Debug.Log($"[ObstacleSpawner] Renamed obstacle from '{oldName}' to '{obstacle.name}', active state: {obstacle.activeInHierarchy}");
|
||||
|
||||
// Configure the obstacle
|
||||
ConfigureObstacle(obstacle, prefabIndex);
|
||||
Debug.Log($"[ObstacleSpawner] After configuration, obstacle {obstacle.name} active state: {obstacle.activeInHierarchy}");
|
||||
|
||||
// Track active obstacles
|
||||
_activeObstacles.Add(obstacle);
|
||||
|
||||
// Invoke events
|
||||
onObstacleSpawned?.Invoke(obstacle);
|
||||
Debug.Log($"[ObstacleSpawner] After events, obstacle {obstacle.name} active state: {obstacle.activeInHierarchy}");
|
||||
|
||||
Debug.Log($"[ObstacleSpawner] Successfully spawned obstacle {obstacle.name} at {position}. Active count: {_activeObstacles.Count}, Final active state: {obstacle.activeInHierarchy}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures an obstacle with randomized properties
|
||||
/// </summary>
|
||||
private void ConfigureObstacle(GameObject obstacle, int prefabIndex)
|
||||
{
|
||||
FloatingObstacle obstacleComponent = obstacle.GetComponent<FloatingObstacle>();
|
||||
if (obstacleComponent != null)
|
||||
{
|
||||
// Set prefab index
|
||||
obstacleComponent.PrefabIndex = prefabIndex;
|
||||
|
||||
// Randomize properties
|
||||
obstacleComponent.MoveSpeed = Random.Range(minMoveSpeed, maxMoveSpeed);
|
||||
|
||||
// Set spawner reference (since FloatingObstacle has this built-in now)
|
||||
obstacleComponent.SetSpawner(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an obstacle to the pool (called by FloatingObstacle)
|
||||
/// </summary>
|
||||
public void ReturnObstacleToPool(GameObject obstacle, int prefabIndex)
|
||||
{
|
||||
if (obstacle == null) return;
|
||||
|
||||
Debug.Log($"[ObstacleSpawner] ReturnObstacleToPool called for {obstacle.name}, active state: {obstacle.activeInHierarchy}");
|
||||
|
||||
// Remove from active list
|
||||
_activeObstacles.Remove(obstacle);
|
||||
|
||||
// Invoke events
|
||||
onObstacleDestroyed?.Invoke(obstacle);
|
||||
|
||||
// Return to pool or destroy
|
||||
if (useObjectPooling && _obstaclePool != null)
|
||||
{
|
||||
Debug.Log($"[ObstacleSpawner] Returning {obstacle.name} to pool");
|
||||
_obstaclePool.ReturnObstacle(obstacle, prefabIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"[ObstacleSpawner] Destroying {obstacle.name} (pooling disabled)");
|
||||
Destroy(obstacle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to change spawn interval at runtime
|
||||
/// </summary>
|
||||
public void SetSpawnInterval(float interval)
|
||||
{
|
||||
spawnInterval = interval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to change spawn range at runtime
|
||||
/// </summary>
|
||||
public void SetSpawnRange(float range)
|
||||
{
|
||||
spawnRangeX = range;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to set speed range at runtime
|
||||
/// </summary>
|
||||
public void SetSpeedRange(float min, float max)
|
||||
{
|
||||
minMoveSpeed = min;
|
||||
maxMoveSpeed = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to recalculate screen bounds (useful if camera changes)
|
||||
/// </summary>
|
||||
public void RecalculateScreenBounds()
|
||||
{
|
||||
CalculateScreenBounds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of currently active obstacles
|
||||
/// </summary>
|
||||
public int ActiveObstacleCount => _activeObstacles.Count;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// Only draw if screen bounds have been calculated
|
||||
if (_spawnRangeX > 0f)
|
||||
{
|
||||
// Draw spawn area using dynamic calculations
|
||||
Gizmos.color = Color.yellow;
|
||||
Vector3 center = new Vector3(0f, _screenBottom - 2f, 0f);
|
||||
Vector3 size = new Vector3(_spawnRangeX * 2f, 1f, 1f);
|
||||
Gizmos.DrawWireCube(center, size);
|
||||
|
||||
// Draw collision radius at spawn point
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(center, spawnCollisionRadius);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49ec62157fd945fab730193e9ea0bff7
|
||||
timeCreated: 1758116903
|
||||
@@ -0,0 +1,242 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles visual feedback for player damage by making the sprite renderer blink red.
|
||||
/// Automatically subscribes to damage events from PlayerCollisionBehavior components.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
private bool _isBlinking;
|
||||
private bool _isShowingDamageColor;
|
||||
private Coroutine _blinkCoroutine;
|
||||
private Color _originalColor; // Missing field declaration
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Auto-assign sprite renderer if not set
|
||||
if (targetSpriteRenderer == null)
|
||||
{
|
||||
targetSpriteRenderer = GetComponent<SpriteRenderer>();
|
||||
if (targetSpriteRenderer == null)
|
||||
{
|
||||
targetSpriteRenderer = GetComponentInChildren<SpriteRenderer>();
|
||||
if (targetSpriteRenderer != null)
|
||||
{
|
||||
Debug.Log($"[PlayerBlinkBehavior] Found SpriteRenderer on child object: {targetSpriteRenderer.gameObject.name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetSpriteRenderer == null)
|
||||
{
|
||||
Debug.LogError("[PlayerBlinkBehavior] No SpriteRenderer found on this GameObject or its children!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Store 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;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// Unsubscribe from immunity events
|
||||
PlayerCollisionBehavior.OnImmunityStarted -= StartBlinking;
|
||||
PlayerCollisionBehavior.OnImmunityEnded -= StopBlinking;
|
||||
|
||||
// Stop any ongoing blink effect
|
||||
if (_blinkCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_blinkCoroutine);
|
||||
_blinkCoroutine = null;
|
||||
}
|
||||
|
||||
// Restore original color
|
||||
RestoreOriginalColor();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the red blinking effect when damage begins
|
||||
/// </summary>
|
||||
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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the blinking effect when damage ends
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles the blinking animation
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the sprite renderer to its original color
|
||||
/// </summary>
|
||||
private void RestoreOriginalColor()
|
||||
{
|
||||
if (targetSpriteRenderer != null)
|
||||
{
|
||||
targetSpriteRenderer.color = _originalColor;
|
||||
_isShowingDamageColor = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the original color reference (useful if sprite color changes during gameplay)
|
||||
/// </summary>
|
||||
public void UpdateOriginalColor()
|
||||
{
|
||||
if (targetSpriteRenderer != null && !_isBlinking)
|
||||
{
|
||||
_originalColor = targetSpriteRenderer.color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to change blink color at runtime
|
||||
/// </summary>
|
||||
public void SetDamageBlinkColor(Color newColor)
|
||||
{
|
||||
damageBlinkColor = newColor;
|
||||
damageBlinkColor.a = damageColorAlpha;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to change blink rate at runtime
|
||||
/// </summary>
|
||||
public void SetBlinkRate(float rate)
|
||||
{
|
||||
blinkRate = rate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if currently blinking
|
||||
/// </summary>
|
||||
public bool IsBlinking => _isBlinking;
|
||||
|
||||
/// <summary>
|
||||
/// Manually trigger blink effect (useful for testing or other damage sources)
|
||||
/// </summary>
|
||||
public void TriggerBlink(float duration)
|
||||
{
|
||||
if (_blinkCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_blinkCoroutine);
|
||||
}
|
||||
|
||||
StartCoroutine(ManualBlinkCoroutine(duration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine for manually triggered blink effects
|
||||
/// </summary>
|
||||
private IEnumerator ManualBlinkCoroutine(float duration)
|
||||
{
|
||||
_isBlinking = true;
|
||||
_isShowingDamageColor = false;
|
||||
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < duration && targetSpriteRenderer != null)
|
||||
{
|
||||
// Toggle between original and damage colors
|
||||
if (_isShowingDamageColor)
|
||||
{
|
||||
targetSpriteRenderer.color = _originalColor;
|
||||
_isShowingDamageColor = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetSpriteRenderer.color = damageBlinkColor;
|
||||
_isShowingDamageColor = true;
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(blinkRate);
|
||||
elapsed += blinkRate;
|
||||
}
|
||||
|
||||
// Ensure we end with original color
|
||||
RestoreOriginalColor();
|
||||
_isBlinking = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8ea29cc80524de8affe17b930cd75c1
|
||||
timeCreated: 1758114229
|
||||
@@ -0,0 +1,371 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for handling player collisions with world obstacles.
|
||||
/// Uses trigger-based collision detection with shared immunity state across all collision behaviors.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
[Tooltip("Reference to the PlayerController component (auto-assigned if empty)")]
|
||||
[SerializeField] protected PlayerController playerController;
|
||||
|
||||
// Static shared immunity state across all collision behaviors
|
||||
private static bool _isGloballyImmune;
|
||||
private static Coroutine _globalImmunityCoroutine;
|
||||
private static MonoBehaviour _coroutineRunner;
|
||||
private static Collider2D _sharedPlayerCollider;
|
||||
|
||||
// Events for immunity and damage state changes
|
||||
public static event Action OnImmunityStarted;
|
||||
public static event Action OnImmunityEnded;
|
||||
public static event Action OnDamageTaken;
|
||||
|
||||
// Instance tracking for shared state management
|
||||
private static readonly System.Collections.Generic.HashSet<PlayerCollisionBehavior> _allInstances =
|
||||
new System.Collections.Generic.HashSet<PlayerCollisionBehavior>();
|
||||
|
||||
/// <summary>
|
||||
/// Public static method to trigger immunity start event from external classes
|
||||
/// </summary>
|
||||
public static void TriggerImmunityStarted()
|
||||
{
|
||||
OnImmunityStarted?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public static method to trigger immunity end event from external classes
|
||||
/// </summary>
|
||||
public static void TriggerImmunityEnded()
|
||||
{
|
||||
OnImmunityEnded?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public static method to trigger damage taken event from external classes
|
||||
/// </summary>
|
||||
public static void TriggerDamageTaken()
|
||||
{
|
||||
OnDamageTaken?.Invoke();
|
||||
}
|
||||
|
||||
protected bool wasInputBlocked;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
// Auto-assign if not set in inspector
|
||||
if (playerCharacter == null)
|
||||
playerCharacter = gameObject;
|
||||
|
||||
if (playerController == null)
|
||||
playerController = GetComponent<PlayerController>();
|
||||
|
||||
// Set up shared collider reference (only once)
|
||||
if (_sharedPlayerCollider == null)
|
||||
{
|
||||
_sharedPlayerCollider = GetComponent<Collider2D>();
|
||||
if (_sharedPlayerCollider == null)
|
||||
{
|
||||
_sharedPlayerCollider = GetComponentInChildren<Collider2D>();
|
||||
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!");
|
||||
}
|
||||
}
|
||||
|
||||
// Set up coroutine runner (use first instance)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when another collider enters this trigger collider
|
||||
/// </summary>
|
||||
/// <param name="other">The other collider that entered the trigger</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a collision with an obstacle is detected
|
||||
/// </summary>
|
||||
/// <param name="obstacle">The obstacle collider that was hit</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the shared immunity period across all collision behaviors
|
||||
/// </summary>
|
||||
private void StartGlobalImmunity()
|
||||
{
|
||||
if (_isGloballyImmune) return; // Already immune
|
||||
|
||||
_isGloballyImmune = true;
|
||||
|
||||
// Disable the shared collider to prevent further collisions
|
||||
if (_sharedPlayerCollider != null)
|
||||
{
|
||||
_sharedPlayerCollider.enabled = false;
|
||||
}
|
||||
|
||||
// Stop any existing immunity coroutine
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles the immunity timer
|
||||
/// </summary>
|
||||
private IEnumerator ImmunityCoroutine()
|
||||
{
|
||||
Debug.Log($"[PlayerCollisionBehavior] Starting immunity coroutine for {damageImmunityDuration} seconds");
|
||||
|
||||
yield return new WaitForSeconds(damageImmunityDuration);
|
||||
|
||||
Debug.Log($"[PlayerCollisionBehavior] Immunity period ended");
|
||||
|
||||
// End immunity
|
||||
_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
|
||||
OnImmunityEnded?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to implement specific collision response behavior
|
||||
/// </summary>
|
||||
/// <param name="obstacle">The obstacle that was collided with</param>
|
||||
protected abstract void HandleCollisionResponse(Collider2D obstacle);
|
||||
|
||||
/// <summary>
|
||||
/// Called when damage immunity starts (called on all instances)
|
||||
/// </summary>
|
||||
protected virtual void OnImmunityStart()
|
||||
{
|
||||
Debug.Log($"[{GetType().Name}] Damage immunity started for {damageImmunityDuration} seconds");
|
||||
|
||||
// Block input if specified
|
||||
if (blockInputDuringImmunity)
|
||||
{
|
||||
BlockPlayerInput();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when damage immunity ends (called on all instances)
|
||||
/// </summary>
|
||||
protected virtual void OnImmunityEnd()
|
||||
{
|
||||
Debug.Log($"[{GetType().Name}] Damage immunity ended");
|
||||
|
||||
// Restore input if it was blocked
|
||||
if (wasInputBlocked)
|
||||
{
|
||||
RestorePlayerInput();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores player input after immunity
|
||||
/// </summary>
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blocks player input during immunity
|
||||
/// </summary>
|
||||
protected virtual void BlockPlayerInput()
|
||||
{
|
||||
if (playerController != null && playerController.enabled)
|
||||
{
|
||||
playerController.enabled = false;
|
||||
wasInputBlocked = true;
|
||||
Debug.Log($"[{GetType().Name}] Player input blocked during immunity");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the PlayerController's internal target to match current position
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given layer is included in our obstacle layer mask
|
||||
/// </summary>
|
||||
/// <param name="layer">The layer to check</param>
|
||||
/// <returns>True if the layer is included in the obstacle layer mask</returns>
|
||||
private bool IsObstacleLayer(int layer)
|
||||
{
|
||||
return (obstacleLayerMask.value & (1 << layer)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public property to check if player is currently immune (shared across all instances)
|
||||
/// </summary>
|
||||
public static bool IsImmune => _isGloballyImmune;
|
||||
|
||||
/// <summary>
|
||||
/// Public method to manually end immunity (affects all collision behaviors)
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6c959bca2e24e72bf22e92439580d79
|
||||
timeCreated: 1758109598
|
||||
@@ -31,7 +31,7 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
public void OnTap(Vector2 worldPosition)
|
||||
{
|
||||
Debug.Log($"[EndlessDescenderController] OnTap at {worldPosition}");
|
||||
// Debug.Log($"[EndlessDescenderController] OnTap at {worldPosition}");
|
||||
_targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
||||
_isTouchActive = true;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
public void OnHoldStart(Vector2 worldPosition)
|
||||
{
|
||||
Debug.Log($"[EndlessDescenderController] OnHoldStart at {worldPosition}");
|
||||
// Debug.Log($"[EndlessDescenderController] OnHoldStart at {worldPosition}");
|
||||
_targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
||||
_isTouchActive = true;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
public void OnHoldMove(Vector2 worldPosition)
|
||||
{
|
||||
Debug.Log($"[EndlessDescenderController] OnHoldMove at {worldPosition}");
|
||||
// Debug.Log($"[EndlessDescenderController] OnHoldMove at {worldPosition}");
|
||||
_targetFingerX = Mathf.Clamp(worldPosition.x, GameManager.Instance.EndlessDescenderClampXMin, GameManager.Instance.EndlessDescenderClampXMax);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
public void OnHoldEnd(Vector2 worldPosition)
|
||||
{
|
||||
Debug.Log($"[EndlessDescenderController] OnHoldEnd at {worldPosition}");
|
||||
// Debug.Log($"[EndlessDescenderController] OnHoldEnd at {worldPosition}");
|
||||
_isTouchActive = false;
|
||||
}
|
||||
|
||||
|
||||
294
Assets/Scripts/Minigames/DivingForPictures/TileBumpCollision.cs
Normal file
294
Assets/Scripts/Minigames/DivingForPictures/TileBumpCollision.cs
Normal file
@@ -0,0 +1,294 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Collision behavior that bumps the player toward the center of the trench.
|
||||
/// Uses trigger-based collision detection with coroutine-based bump timing.
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
case BumpMode.Impulse:
|
||||
StartImpulseBump();
|
||||
break;
|
||||
|
||||
case BumpMode.SmoothToCenter:
|
||||
StartSmoothMoveToCenter();
|
||||
break;
|
||||
}
|
||||
|
||||
Debug.Log($"[TileBumpCollision] Collision handled with {bumpMode} mode");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts an impulse bump toward the center with force-based distance
|
||||
/// </summary>
|
||||
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})");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts smooth movement to the center
|
||||
/// </summary>
|
||||
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)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Common bump initialization using coroutines
|
||||
/// </summary>
|
||||
private void StartBump(float startX, float targetX, float duration)
|
||||
{
|
||||
// Stop any existing bump
|
||||
if (_bumpCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_bumpCoroutine);
|
||||
_bumpCoroutine = null;
|
||||
}
|
||||
|
||||
_isBumping = true;
|
||||
|
||||
// Block player input if enabled (use bump-specific blocking)
|
||||
if (blockInputDuringBump && playerController != null && playerController.enabled)
|
||||
{
|
||||
playerController.enabled = false;
|
||||
_bumpInputBlocked = true;
|
||||
Debug.Log("[TileBumpCollision] Player input blocked during bump");
|
||||
}
|
||||
|
||||
// Start bump coroutine
|
||||
_bumpCoroutine = StartCoroutine(BumpCoroutine(startX, targetX, duration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles the bump movement over time
|
||||
/// </summary>
|
||||
private IEnumerator BumpCoroutine(float startX, float targetX, float duration)
|
||||
{
|
||||
float elapsedTime = 0f;
|
||||
|
||||
while (elapsedTime < duration)
|
||||
{
|
||||
elapsedTime += Time.deltaTime;
|
||||
|
||||
// Calculate progress and apply curve
|
||||
float progress = elapsedTime / duration;
|
||||
float curveValue = bumpCurve.Evaluate(progress);
|
||||
|
||||
// Interpolate position
|
||||
float currentX = Mathf.Lerp(startX, targetX, curveValue);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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
|
||||
_isBumping = false;
|
||||
_bumpCoroutine = null;
|
||||
|
||||
if (_bumpInputBlocked)
|
||||
{
|
||||
RestoreBumpInput();
|
||||
}
|
||||
|
||||
Debug.Log("[TileBumpCollision] Bump movement completed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores player input after bump movement
|
||||
/// </summary>
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to handle bump-specific input blocking during immunity
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override to handle immunity end and bump cleanup
|
||||
/// </summary>
|
||||
protected override void OnImmunityEnd()
|
||||
{
|
||||
base.OnImmunityEnd();
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when component is destroyed - cleanup coroutines
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_bumpCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_bumpCoroutine);
|
||||
_bumpCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to change bump mode at runtime
|
||||
/// </summary>
|
||||
public void SetBumpMode(BumpMode mode)
|
||||
{
|
||||
bumpMode = mode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to change bump force at runtime
|
||||
/// </summary>
|
||||
public void SetBumpForce(float force)
|
||||
{
|
||||
bumpForce = force;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method to change smooth move speed at runtime
|
||||
/// </summary>
|
||||
public void SetSmoothMoveSpeed(float speed)
|
||||
{
|
||||
smoothMoveSpeed = speed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if player is currently being bumped
|
||||
/// </summary>
|
||||
public bool IsBumping => _isBumping;
|
||||
|
||||
/// <summary>
|
||||
/// Check if input is currently blocked by bump
|
||||
/// </summary>
|
||||
public bool IsBumpInputBlocked => _bumpInputBlocked;
|
||||
|
||||
/// <summary>
|
||||
/// Public method to manually stop bump movement
|
||||
/// </summary>
|
||||
public void StopBump()
|
||||
{
|
||||
if (_isBumping && _bumpCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_bumpCoroutine);
|
||||
_bumpCoroutine = null;
|
||||
_isBumping = false;
|
||||
|
||||
if (_bumpInputBlocked)
|
||||
{
|
||||
RestoreBumpInput();
|
||||
}
|
||||
|
||||
Debug.Log("[TileBumpCollision] Bump manually stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8222f0e3aeeb4fc4975aaead6cf7afbe
|
||||
timeCreated: 1758109619
|
||||
@@ -100,7 +100,7 @@ namespace Pooling
|
||||
/// <returns>An object ready to use</returns>
|
||||
public virtual T Get(int prefabIndex)
|
||||
{
|
||||
T obj;
|
||||
T obj = null;
|
||||
|
||||
// Track usage frequency
|
||||
if (prefabUsageCount.ContainsKey(prefabIndex))
|
||||
@@ -112,27 +112,71 @@ namespace Pooling
|
||||
prefabUsageCount[prefabIndex] = 1;
|
||||
}
|
||||
|
||||
// Try to get a valid object from the pool, cleaning up any destroyed objects
|
||||
if (pooledObjects.ContainsKey(prefabIndex) && pooledObjects[prefabIndex].Count > 0)
|
||||
{
|
||||
obj = pooledObjects[prefabIndex].Pop();
|
||||
totalPooledCount--;
|
||||
Debug.Log($"[{GetType().Name}] Found {pooledObjects[prefabIndex].Count} objects in pool for prefab index {prefabIndex}");
|
||||
|
||||
// Keep trying until we find a valid object or the pool is empty
|
||||
while (pooledObjects[prefabIndex].Count > 0)
|
||||
{
|
||||
obj = pooledObjects[prefabIndex].Pop();
|
||||
totalPooledCount--;
|
||||
|
||||
// Check if the object is still valid (not destroyed)
|
||||
if (obj != null && obj.gameObject != null)
|
||||
{
|
||||
Debug.Log($"[{GetType().Name}] Retrieved valid object {obj.name} from pool, current active state: {obj.gameObject.activeInHierarchy}");
|
||||
break; // Found a valid object
|
||||
}
|
||||
else
|
||||
{
|
||||
// Object was destroyed, continue looking
|
||||
Debug.LogWarning($"[{GetType().Name}] Found destroyed object in pool, removing it");
|
||||
obj = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new object without adding to pool
|
||||
T prefab = prefabs[prefabIndex];
|
||||
obj = Instantiate(prefab, transform);
|
||||
Debug.Log($"[{GetType().Name}] No objects in pool for prefab index {prefabIndex}, creating new one");
|
||||
}
|
||||
|
||||
// If we couldn't find a valid object in the pool, create a new one
|
||||
if (obj == null)
|
||||
{
|
||||
T prefab = prefabs[prefabIndex];
|
||||
obj = Instantiate(prefab, transform);
|
||||
Debug.Log($"[{GetType().Name}] Created new object {obj.name} from prefab, active state: {obj.gameObject.activeInHierarchy}");
|
||||
}
|
||||
|
||||
// Ensure the object is valid before proceeding
|
||||
if (obj == null || obj.gameObject == null)
|
||||
{
|
||||
Debug.LogError($"[{GetType().Name}] Failed to create valid object for prefab index {prefabIndex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// CRITICAL FIX: Reset position to safe location BEFORE activation
|
||||
// This prevents off-screen checks from triggering during spawn process
|
||||
Vector3 originalPosition = obj.transform.position;
|
||||
obj.transform.position = new Vector3(0f, -1000f, 0f);
|
||||
Debug.Log($"[{GetType().Name}] Moved object {obj.name} from {originalPosition} to safe position before activation");
|
||||
|
||||
Debug.Log($"[{GetType().Name}] About to activate object {obj.name}, current state: {obj.gameObject.activeInHierarchy}");
|
||||
obj.gameObject.SetActive(true);
|
||||
Debug.Log($"[{GetType().Name}] After SetActive(true), object {obj.name} state: {obj.gameObject.activeInHierarchy}");
|
||||
|
||||
// Call OnSpawn for IPoolable components
|
||||
IPoolable poolable = obj.GetComponent<IPoolable>();
|
||||
if (poolable != null)
|
||||
{
|
||||
Debug.Log($"[{GetType().Name}] Calling OnSpawn for object {obj.name}");
|
||||
poolable.OnSpawn();
|
||||
Debug.Log($"[{GetType().Name}] After OnSpawn, object {obj.name} state: {obj.gameObject.activeInHierarchy}");
|
||||
}
|
||||
|
||||
Debug.Log($"[{GetType().Name}] Returning object {obj.name} with final state: {obj.gameObject.activeInHierarchy}");
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -152,33 +196,61 @@ namespace Pooling
|
||||
poolable.OnDespawn();
|
||||
}
|
||||
|
||||
// Check if we're under the maximum pool size for this prefab type
|
||||
bool keepObject = totalPooledCount < totalMaxPoolSize;
|
||||
// Always deactivate and parent the object
|
||||
obj.gameObject.SetActive(false);
|
||||
obj.transform.SetParent(transform);
|
||||
|
||||
// Additional constraint: don't keep too many of any single prefab type
|
||||
if (pooledObjects.ContainsKey(prefabIndex) &&
|
||||
pooledObjects[prefabIndex].Count >= maxPerPrefabPoolSize)
|
||||
// Initialize stack if it doesn't exist
|
||||
if (!pooledObjects.ContainsKey(prefabIndex))
|
||||
{
|
||||
keepObject = false;
|
||||
pooledObjects[prefabIndex] = new Stack<T>();
|
||||
}
|
||||
|
||||
if (keepObject)
|
||||
// Check if we need to trim this specific prefab type's pool
|
||||
if (pooledObjects[prefabIndex].Count >= maxPerPrefabPoolSize)
|
||||
{
|
||||
obj.gameObject.SetActive(false);
|
||||
obj.transform.SetParent(transform);
|
||||
|
||||
if (!pooledObjects.ContainsKey(prefabIndex))
|
||||
// Remove the oldest object from this prefab's pool to make room
|
||||
if (pooledObjects[prefabIndex].Count > 0)
|
||||
{
|
||||
pooledObjects[prefabIndex] = new Stack<T>();
|
||||
T oldestObj = pooledObjects[prefabIndex].Pop();
|
||||
if (oldestObj != null && oldestObj.gameObject != null)
|
||||
{
|
||||
Destroy(oldestObj.gameObject);
|
||||
totalPooledCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check global pool limit
|
||||
if (totalPooledCount >= totalMaxPoolSize)
|
||||
{
|
||||
// Find the prefab type with the most pooled objects and remove one
|
||||
int maxCount = 0;
|
||||
int prefabToTrim = -1;
|
||||
|
||||
foreach (var kvp in pooledObjects)
|
||||
{
|
||||
if (kvp.Value.Count > maxCount)
|
||||
{
|
||||
maxCount = kvp.Value.Count;
|
||||
prefabToTrim = kvp.Key;
|
||||
}
|
||||
}
|
||||
|
||||
pooledObjects[prefabIndex].Push(obj);
|
||||
totalPooledCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(obj.gameObject);
|
||||
if (prefabToTrim >= 0 && pooledObjects[prefabToTrim].Count > 0)
|
||||
{
|
||||
T oldestObj = pooledObjects[prefabToTrim].Pop();
|
||||
if (oldestObj != null && oldestObj.gameObject != null)
|
||||
{
|
||||
Destroy(oldestObj.gameObject);
|
||||
totalPooledCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now add the current object to the pool
|
||||
pooledObjects[prefabIndex].Push(obj);
|
||||
totalPooledCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,8 +18,8 @@ TagManager:
|
||||
- Pulver
|
||||
- WorldBoundary
|
||||
- Interactable
|
||||
-
|
||||
-
|
||||
- QuarryObstacle
|
||||
- QuarryMonster
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
Reference in New Issue
Block a user