Working state for minigameobstacles
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -99,6 +99,8 @@ InitTestScene*.unity*
|
|||||||
|
|
||||||
# Auto-generated scenes by play mode tests
|
# Auto-generated scenes by play mode tests
|
||||||
/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
|
/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
|
||||||
|
.idea/.idea.AppleHillsProduction/.idea/indexLayout.xml
|
||||||
.vscode/extensions.json
|
.vscode/extensions.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
.vscode/settings.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")]
|
[Tooltip("Color used for previewing colliders in the scene view")]
|
||||||
private Color previewColor = new Color(0.2f, 1f, 0.3f, 0.5f);
|
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>();
|
private List<Mesh> previewMeshes = new List<Mesh>();
|
||||||
|
|
||||||
[MenuItem("Tools/Sprite Collider Generator")]
|
[MenuItem("Tools/Sprite Collider Generator")]
|
||||||
@@ -51,8 +54,29 @@ namespace Editor.Utilities
|
|||||||
GetWindow<SpriteColliderGenerator>("Sprite Collider Generator");
|
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()
|
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
|
// Clean up any preview meshes when window is closed
|
||||||
foreach (var mesh in previewMeshes)
|
foreach (var mesh in previewMeshes)
|
||||||
{
|
{
|
||||||
@@ -64,6 +88,52 @@ namespace Editor.Utilities
|
|||||||
previewMeshes.Clear();
|
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()
|
private void OnGUI()
|
||||||
{
|
{
|
||||||
EditorGUILayout.BeginVertical();
|
EditorGUILayout.BeginVertical();
|
||||||
@@ -146,6 +216,12 @@ namespace Editor.Utilities
|
|||||||
new GUIContent("Generate Trigger Colliders", "When enabled, creates trigger colliders instead of solid colliders."),
|
new GUIContent("Generate Trigger Colliders", "When enabled, creates trigger colliders instead of solid colliders."),
|
||||||
generateTriggerColliders);
|
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
|
// Offset option
|
||||||
offsetFromCenter = EditorGUILayout.Toggle(
|
offsetFromCenter = EditorGUILayout.Toggle(
|
||||||
new GUIContent("Offset From Center", "When enabled, allows scaling the collider outward or inward from the sprite center."),
|
new GUIContent("Offset From Center", "When enabled, allows scaling the collider outward or inward from the sprite center."),
|
||||||
@@ -445,9 +521,25 @@ namespace Editor.Utilities
|
|||||||
polygonCollider.SetPath(i, paths[i]);
|
polygonCollider.SetPath(i, paths[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the layer on the GameObject if a specific layer is selected
|
||||||
|
SetTargetLayer(targetObject);
|
||||||
|
|
||||||
return true;
|
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)
|
private List<Vector2[]> GetSpritePaths(Sprite sprite, float tolerance)
|
||||||
{
|
{
|
||||||
List<Vector2[]> result = new List<Vector2[]>();
|
List<Vector2[]> result = new List<Vector2[]>();
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 7111145574660306503}
|
- component: {fileID: 7111145574660306503}
|
||||||
- component: {fileID: 3889795708575321074}
|
- component: {fileID: 3889795708575321074}
|
||||||
- component: {fileID: 7249681423942450184}
|
- component: {fileID: 8688779957837852254}
|
||||||
m_Layer: 0
|
m_Layer: 6
|
||||||
m_Name: Left_Tile2_0
|
m_Name: Left_Tile2_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -88,7 +88,7 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 0
|
m_SpriteSortPoint: 0
|
||||||
--- !u!60 &7249681423942450184
|
--- !u!60 &8688779957837852254
|
||||||
PolygonCollider2D:
|
PolygonCollider2D:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -155,8 +155,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 1003080013996268193}
|
- component: {fileID: 1003080013996268193}
|
||||||
- component: {fileID: 4856205316150460481}
|
- component: {fileID: 4856205316150460481}
|
||||||
- component: {fileID: 2843103852598642252}
|
- component: {fileID: 8615330168962481848}
|
||||||
m_Layer: 0
|
m_Layer: 6
|
||||||
m_Name: Right_Tile1_0
|
m_Name: Right_Tile1_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -233,7 +233,7 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 0
|
m_SpriteSortPoint: 0
|
||||||
--- !u!60 &2843103852598642252
|
--- !u!60 &8615330168962481848
|
||||||
PolygonCollider2D:
|
PolygonCollider2D:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -282,8 +282,6 @@ PolygonCollider2D:
|
|||||||
- - {x: -0.48499998, y: 2.5}
|
- - {x: -0.48499998, y: 2.5}
|
||||||
- {x: -0.49499997, y: 2.19}
|
- {x: -0.49499997, y: 2.19}
|
||||||
- {x: 0.035, y: 1.66}
|
- {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.285, y: 0.90999997}
|
||||||
- {x: -0.13499999, y: 0.45999998}
|
- {x: -0.13499999, y: 0.45999998}
|
||||||
- {x: -1.115, y: -0.03}
|
- {x: -1.115, y: -0.03}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 7111145574660306503}
|
- component: {fileID: 7111145574660306503}
|
||||||
- component: {fileID: 3889795708575321074}
|
- component: {fileID: 3889795708575321074}
|
||||||
m_Layer: 0
|
- component: {fileID: 8698932925238153222}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Left_Tile2_0
|
m_Name: Left_Tile2_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -87,6 +88,63 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2171518497100337372
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -97,7 +155,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 1003080013996268193}
|
- component: {fileID: 1003080013996268193}
|
||||||
- component: {fileID: 4856205316150460481}
|
- component: {fileID: 4856205316150460481}
|
||||||
m_Layer: 0
|
- component: {fileID: 1368284191430742655}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Right_Tile1_0
|
m_Name: Right_Tile1_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -174,6 +233,67 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2956826569642009690
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 7111145574660306503}
|
- component: {fileID: 7111145574660306503}
|
||||||
- component: {fileID: 3889795708575321074}
|
- component: {fileID: 3889795708575321074}
|
||||||
m_Layer: 0
|
- component: {fileID: 6919687589473331973}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Left_Tile2_0
|
m_Name: Left_Tile2_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -87,6 +88,72 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2171518497100337372
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -97,7 +164,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 1003080013996268193}
|
- component: {fileID: 1003080013996268193}
|
||||||
- component: {fileID: 4856205316150460481}
|
- component: {fileID: 4856205316150460481}
|
||||||
m_Layer: 0
|
- component: {fileID: 1911291775322313535}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Right_Tile1_0
|
m_Name: Right_Tile1_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -174,6 +242,65 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2956826569642009690
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 7111145574660306503}
|
- component: {fileID: 7111145574660306503}
|
||||||
- component: {fileID: 3889795708575321074}
|
- component: {fileID: 3889795708575321074}
|
||||||
m_Layer: 0
|
- component: {fileID: 3320970211105392876}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Left_Tile2_0
|
m_Name: Left_Tile2_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -87,6 +88,72 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2171518497100337372
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -97,7 +164,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 1003080013996268193}
|
- component: {fileID: 1003080013996268193}
|
||||||
- component: {fileID: 4856205316150460481}
|
- component: {fileID: 4856205316150460481}
|
||||||
m_Layer: 0
|
- component: {fileID: 3931598674086383704}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Right_Tile1_0
|
m_Name: Right_Tile1_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -174,6 +242,65 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2956826569642009690
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 7111145574660306503}
|
- component: {fileID: 7111145574660306503}
|
||||||
- component: {fileID: 3889795708575321074}
|
- component: {fileID: 3889795708575321074}
|
||||||
m_Layer: 0
|
- component: {fileID: 1709932086434338450}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Left_Tile2_0
|
m_Name: Left_Tile2_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -87,6 +88,72 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2171518497100337372
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -97,7 +164,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 1003080013996268193}
|
- component: {fileID: 1003080013996268193}
|
||||||
- component: {fileID: 4856205316150460481}
|
- component: {fileID: 4856205316150460481}
|
||||||
m_Layer: 0
|
- component: {fileID: 9036823987933958098}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Right_Tile1_0
|
m_Name: Right_Tile1_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -174,6 +242,70 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2956826569642009690
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 7111145574660306503}
|
- component: {fileID: 7111145574660306503}
|
||||||
- component: {fileID: 3889795708575321074}
|
- component: {fileID: 3889795708575321074}
|
||||||
m_Layer: 0
|
- component: {fileID: 6967660003360049346}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Left_Tile2_0
|
m_Name: Left_Tile2_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -87,6 +88,72 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2171518497100337372
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -97,7 +164,8 @@ GameObject:
|
|||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 1003080013996268193}
|
- component: {fileID: 1003080013996268193}
|
||||||
- component: {fileID: 4856205316150460481}
|
- component: {fileID: 4856205316150460481}
|
||||||
m_Layer: 0
|
- component: {fileID: 5503039050943823342}
|
||||||
|
m_Layer: 6
|
||||||
m_Name: Right_Tile1_0
|
m_Name: Right_Tile1_0
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
@@ -174,6 +242,70 @@ SpriteRenderer:
|
|||||||
m_WasSpriteAssigned: 1
|
m_WasSpriteAssigned: 1
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_SpriteSortPoint: 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
|
--- !u!1 &2956826569642009690
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
2075
Assets/Scenes/MiniGames/DivingForPictures_obstaclebackup.unity
Normal file
2075
Assets/Scenes/MiniGames/DivingForPictures_obstaclebackup.unity
Normal file
File diff suppressed because it is too large
Load Diff
233
Assets/Scripts/Minigames/DivingForPictures/FloatingObstacle.cs
Normal file
233
Assets/Scripts/Minigames/DivingForPictures/FloatingObstacle.cs
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Pooling;
|
||||||
|
|
||||||
|
namespace Minigames.DivingForPictures
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Complete floating obstacle component that handles data, movement, collision detection, and pooling.
|
||||||
|
/// Obstacles move upward toward the surface and detect collisions with the player.
|
||||||
|
/// </summary>
|
||||||
|
public class FloatingObstacle : MonoBehaviour, IPoolable
|
||||||
|
{
|
||||||
|
[Header("Obstacle Properties")]
|
||||||
|
[Tooltip("Index of the prefab this obstacle was created from")]
|
||||||
|
[SerializeField] private int prefabIndex;
|
||||||
|
|
||||||
|
[Tooltip("Damage this obstacle deals to the player")]
|
||||||
|
[SerializeField] private float damage = 1f;
|
||||||
|
|
||||||
|
[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("Collision Detection")]
|
||||||
|
[Tooltip("Layer mask for player detection - should match Player layer")]
|
||||||
|
[SerializeField] private LayerMask playerLayerMask = 1 << 7; // Player layer
|
||||||
|
|
||||||
|
[Tooltip("How often to check for collisions (in seconds)")]
|
||||||
|
[SerializeField] private float collisionCheckInterval = 0.1f;
|
||||||
|
|
||||||
|
[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 Damage
|
||||||
|
{
|
||||||
|
get => damage;
|
||||||
|
set => damage = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float MoveSpeed
|
||||||
|
{
|
||||||
|
get => moveSpeed;
|
||||||
|
set => moveSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasDealtDamage => _hasDealtDamage;
|
||||||
|
|
||||||
|
// Private fields
|
||||||
|
private Collider2D _collider;
|
||||||
|
private float _collisionCheckTimer;
|
||||||
|
private bool _hasDealtDamage;
|
||||||
|
private Camera _mainCamera;
|
||||||
|
private float _screenTop;
|
||||||
|
|
||||||
|
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 Update()
|
||||||
|
{
|
||||||
|
if (enableMovement)
|
||||||
|
{
|
||||||
|
HandleMovement();
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleCollisionDetection();
|
||||||
|
CheckIfOffScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the obstacle upward based on its speed
|
||||||
|
/// </summary>
|
||||||
|
private void HandleMovement()
|
||||||
|
{
|
||||||
|
transform.position += Vector3.up * (moveSpeed * Time.deltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for collisions with the player at regular intervals
|
||||||
|
/// </summary>
|
||||||
|
private void HandleCollisionDetection()
|
||||||
|
{
|
||||||
|
if (_hasDealtDamage || _collider == null) return;
|
||||||
|
|
||||||
|
_collisionCheckTimer -= Time.deltaTime;
|
||||||
|
if (_collisionCheckTimer <= 0f)
|
||||||
|
{
|
||||||
|
_collisionCheckTimer = collisionCheckInterval;
|
||||||
|
CheckForPlayerCollision();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this obstacle is colliding with the player
|
||||||
|
/// </summary>
|
||||||
|
private void CheckForPlayerCollision()
|
||||||
|
{
|
||||||
|
Collider2D[] overlapping = new Collider2D[5];
|
||||||
|
ContactFilter2D filter = new ContactFilter2D();
|
||||||
|
filter.SetLayerMask(playerLayerMask);
|
||||||
|
filter.useTriggers = true;
|
||||||
|
|
||||||
|
int count = _collider.Overlap(filter, overlapping);
|
||||||
|
|
||||||
|
if (count > 0 && !_hasDealtDamage)
|
||||||
|
{
|
||||||
|
// Found collision with player
|
||||||
|
OnPlayerCollision(overlapping[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when this obstacle collides with the player
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="playerCollider">The player's collider</param>
|
||||||
|
private void OnPlayerCollision(Collider2D playerCollider)
|
||||||
|
{
|
||||||
|
_hasDealtDamage = true;
|
||||||
|
|
||||||
|
// Trigger damage through events (following the existing pattern)
|
||||||
|
Debug.Log($"[FloatingObstacle] Obstacle dealt {damage} damage to player");
|
||||||
|
|
||||||
|
// Broadcast damage event using the static method
|
||||||
|
PlayerCollisionBehavior.TriggerDamageStart();
|
||||||
|
|
||||||
|
// Continue moving upward - don't destroy or stop the obstacle
|
||||||
|
Debug.Log($"[FloatingObstacle] Obstacle {gameObject.name} hit player and continues moving");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the obstacle has moved off-screen and should be despawned
|
||||||
|
/// </summary>
|
||||||
|
private void CheckIfOffScreen()
|
||||||
|
{
|
||||||
|
if (_mainCamera == null) return;
|
||||||
|
|
||||||
|
// Calculate screen top if not cached
|
||||||
|
if (_screenTop == 0f)
|
||||||
|
{
|
||||||
|
Vector3 topWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 1f, _mainCamera.nearClipPlane));
|
||||||
|
_screenTop = topWorldPoint.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if obstacle is above screen
|
||||||
|
if (transform.position.y > _screenTop + 2f) // Extra buffer for safety
|
||||||
|
{
|
||||||
|
ReturnToPool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns this obstacle to the spawner's pool
|
||||||
|
/// </summary>
|
||||||
|
private void ReturnToPool()
|
||||||
|
{
|
||||||
|
if (spawner != null)
|
||||||
|
{
|
||||||
|
spawner.ReturnObstacleToPool(gameObject, prefabIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[FloatingObstacle] Cannot return {gameObject.name} to pool - missing spawner reference");
|
||||||
|
Destroy(gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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()
|
||||||
|
{
|
||||||
|
_hasDealtDamage = false;
|
||||||
|
_collisionCheckTimer = 0f;
|
||||||
|
_screenTop = 0f; // Reset cached screen bounds
|
||||||
|
|
||||||
|
// Ensure the obstacle is active and visible
|
||||||
|
gameObject.SetActive(true);
|
||||||
|
|
||||||
|
Debug.Log($"[FloatingObstacle] Obstacle {gameObject.name} spawned");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the obstacle is returned to the pool
|
||||||
|
/// </summary>
|
||||||
|
public void OnDespawn()
|
||||||
|
{
|
||||||
|
_hasDealtDamage = false;
|
||||||
|
_collisionCheckTimer = 0f;
|
||||||
|
|
||||||
|
Debug.Log($"[FloatingObstacle] Obstacle {gameObject.name} despawned");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public method to manually trigger return to pool (for external systems)
|
||||||
|
/// </summary>
|
||||||
|
public void ForceReturnToPool()
|
||||||
|
{
|
||||||
|
ReturnToPool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 32718083aef44be2a4318681fcdf5b2e
|
||||||
|
timeCreated: 1758117709
|
||||||
44
Assets/Scripts/Minigames/DivingForPictures/ObstaclePool.cs
Normal file
44
Assets/Scripts/Minigames/DivingForPictures/ObstaclePool.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
FloatingObstacle obstacleComponent = Get(prefabIndex);
|
||||||
|
return obstacleComponent.gameObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a53ba79246a94dc4a71d2fb0d7214cfb
|
||||||
|
timeCreated: 1758116804
|
||||||
452
Assets/Scripts/Minigames/DivingForPictures/ObstacleSpawner.cs
Normal file
452
Assets/Scripts/Minigames/DivingForPictures/ObstacleSpawner.cs
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
[Tooltip("Minimum damage dealt by obstacles")]
|
||||||
|
[SerializeField] private float minDamage = 0.5f;
|
||||||
|
|
||||||
|
[Tooltip("Maximum damage dealt by obstacles")]
|
||||||
|
[SerializeField] private float maxDamage = 2f;
|
||||||
|
|
||||||
|
[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 - should match WorldObstacle layer")]
|
||||||
|
[SerializeField] private LayerMask tileLayerMask = 1 << 6; // WorldObstacle layer
|
||||||
|
|
||||||
|
[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 Coroutine _spawnCoroutine;
|
||||||
|
private readonly List<GameObject> _activeObstacles = new List<GameObject>();
|
||||||
|
|
||||||
|
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
|
||||||
|
if (obstaclePrefabs[i].layer != 11) // QuarryObstacle layer
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Obstacle prefab {obstaclePrefabs[i].name} is not on QuarryObstacle layer (11). Setting layer automatically.");
|
||||||
|
SetLayerRecursively(obstaclePrefabs[i], 11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
|
||||||
|
// Periodically trim the pool
|
||||||
|
InvokeRepeating(nameof(TrimExcessPooledObstacles), 15f, 30f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate screen bounds in world space
|
||||||
|
/// </summary>
|
||||||
|
private void CalculateScreenBounds()
|
||||||
|
{
|
||||||
|
if (_mainCamera == null)
|
||||||
|
{
|
||||||
|
_mainCamera = Camera.main;
|
||||||
|
if (_mainCamera == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[ObstacleSpawner] No main camera found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 bottomWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 0f, _mainCamera.nearClipPlane));
|
||||||
|
_screenBottom = bottomWorldPoint.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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()
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
SpawnObstacleAt(spawnPosition);
|
||||||
|
foundValidPosition = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundValidPosition)
|
||||||
|
{
|
||||||
|
Debug.Log($"[ObstacleSpawner] Could not find valid spawn position after {maxSpawnAttempts} attempts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a random spawn position below the screen
|
||||||
|
/// </summary>
|
||||||
|
private Vector3 GetRandomSpawnPosition()
|
||||||
|
{
|
||||||
|
float randomX = Random.Range(-spawnRangeX, spawnRangeX);
|
||||||
|
float spawnY = _screenBottom - spawnDistanceBelowScreen;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Select random prefab
|
||||||
|
int prefabIndex = Random.Range(0, obstaclePrefabs.Count);
|
||||||
|
GameObject prefab = obstaclePrefabs[prefabIndex];
|
||||||
|
|
||||||
|
if (prefab == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[ObstacleSpawner] Obstacle prefab at index {prefabIndex} is null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject obstacle;
|
||||||
|
|
||||||
|
// Spawn using pool or instantiate directly
|
||||||
|
if (useObjectPooling && _obstaclePool != null)
|
||||||
|
{
|
||||||
|
obstacle = _obstaclePool.GetObstacle(prefabIndex);
|
||||||
|
if (obstacle == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[ObstacleSpawner] Failed to get obstacle from pool!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obstacle.transform.position = position;
|
||||||
|
obstacle.transform.rotation = prefab.transform.rotation;
|
||||||
|
obstacle.transform.SetParent(transform);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
obstacle = Instantiate(prefab, position, prefab.transform.rotation, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the obstacle
|
||||||
|
ConfigureObstacle(obstacle, prefabIndex);
|
||||||
|
|
||||||
|
// Track active obstacles
|
||||||
|
_activeObstacles.Add(obstacle);
|
||||||
|
|
||||||
|
// Invoke events
|
||||||
|
onObstacleSpawned?.Invoke(obstacle);
|
||||||
|
|
||||||
|
Debug.Log($"[ObstacleSpawner] Spawned obstacle {obstacle.name} at {position}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
obstacleComponent.Damage = Random.Range(minDamage, maxDamage);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Remove from active list
|
||||||
|
_activeObstacles.Remove(obstacle);
|
||||||
|
|
||||||
|
// Invoke events
|
||||||
|
onObstacleDestroyed?.Invoke(obstacle);
|
||||||
|
|
||||||
|
// Return to pool or destroy
|
||||||
|
if (useObjectPooling && _obstaclePool != null)
|
||||||
|
{
|
||||||
|
_obstaclePool.ReturnObstacle(obstacle, prefabIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Destroy(obstacle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called periodically to trim excess pooled obstacles
|
||||||
|
/// </summary>
|
||||||
|
private void TrimExcessPooledObstacles()
|
||||||
|
{
|
||||||
|
if (_obstaclePool != null)
|
||||||
|
{
|
||||||
|
_obstaclePool.TrimExcess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 set damage range at runtime
|
||||||
|
/// </summary>
|
||||||
|
public void SetDamageRange(float min, float max)
|
||||||
|
{
|
||||||
|
minDamage = min;
|
||||||
|
maxDamage = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the count of currently active obstacles
|
||||||
|
/// </summary>
|
||||||
|
public int ActiveObstacleCount => _activeObstacles.Count;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private void OnDrawGizmosSelected()
|
||||||
|
{
|
||||||
|
// Draw spawn area
|
||||||
|
Gizmos.color = Color.yellow;
|
||||||
|
Vector3 center = new Vector3(0f, _screenBottom - spawnDistanceBelowScreen, 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 damage events
|
||||||
|
PlayerCollisionBehavior.OnDamageStart += StartBlinking;
|
||||||
|
PlayerCollisionBehavior.OnDamageEnd += StopBlinking;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
// Unsubscribe from damage events
|
||||||
|
PlayerCollisionBehavior.OnDamageStart -= StartBlinking;
|
||||||
|
PlayerCollisionBehavior.OnDamageEnd -= 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,234 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Minigames.DivingForPictures
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Collision behavior that bumps the player toward the center of the trench.
|
||||||
|
/// Provides two modes: impulse (force-based push) or smooth movement to center.
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerBumpCollisionBehavior : 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 float _bumpTimer;
|
||||||
|
private float _bumpStartX;
|
||||||
|
private float _bumpTargetX;
|
||||||
|
private float _bumpDuration;
|
||||||
|
private bool _bumpInputBlocked; // Tracks bump-specific input blocking
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// Handle bump movement
|
||||||
|
if (_isBumping)
|
||||||
|
{
|
||||||
|
_bumpTimer -= Time.deltaTime;
|
||||||
|
|
||||||
|
if (_bumpTimer <= 0f)
|
||||||
|
{
|
||||||
|
// Bump finished
|
||||||
|
_isBumping = false;
|
||||||
|
if (_bumpInputBlocked)
|
||||||
|
{
|
||||||
|
RestoreBumpInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we end exactly at target
|
||||||
|
if (playerCharacter != null)
|
||||||
|
{
|
||||||
|
Vector3 currentPos = playerCharacter.transform.position;
|
||||||
|
playerCharacter.transform.position = new Vector3(_bumpTargetX, currentPos.y, currentPos.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Apply bump movement
|
||||||
|
float progress = 1f - (_bumpTimer / _bumpDuration);
|
||||||
|
float curveValue = bumpCurve.Evaluate(progress);
|
||||||
|
|
||||||
|
float currentX = Mathf.Lerp(_bumpStartX, _bumpTargetX, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleCollisionResponse(Collider2D obstacle)
|
||||||
|
{
|
||||||
|
switch (bumpMode)
|
||||||
|
{
|
||||||
|
case BumpMode.Impulse:
|
||||||
|
StartImpulseBump();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BumpMode.SmoothToCenter:
|
||||||
|
StartSmoothMoveToCenter();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"[PlayerBumpCollisionBehavior] 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set bump parameters
|
||||||
|
_bumpStartX = currentX;
|
||||||
|
_bumpTargetX = targetX;
|
||||||
|
_bumpDuration = 0.5f; // Fixed duration for impulse
|
||||||
|
|
||||||
|
StartBump();
|
||||||
|
|
||||||
|
Debug.Log($"[PlayerBumpCollisionBehavior] Starting impulse bump from X={_bumpStartX} to X={_bumpTargetX} (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);
|
||||||
|
|
||||||
|
// Set bump parameters
|
||||||
|
_bumpStartX = currentX;
|
||||||
|
_bumpTargetX = 0f; // Always move to center
|
||||||
|
_bumpDuration = distanceToCenter / smoothMoveSpeed; // Duration based on distance and speed
|
||||||
|
|
||||||
|
StartBump();
|
||||||
|
|
||||||
|
Debug.Log($"[PlayerBumpCollisionBehavior] Starting smooth move to center from X={_bumpStartX} (speed={smoothMoveSpeed}, duration={_bumpDuration:F2}s)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Common bump initialization
|
||||||
|
/// </summary>
|
||||||
|
private void StartBump()
|
||||||
|
{
|
||||||
|
_isBumping = true;
|
||||||
|
_bumpTimer = _bumpDuration;
|
||||||
|
|
||||||
|
// Block player input if enabled (use bump-specific blocking)
|
||||||
|
if (blockInputDuringBump && playerController != null && playerController.enabled)
|
||||||
|
{
|
||||||
|
playerController.enabled = false;
|
||||||
|
_bumpInputBlocked = true;
|
||||||
|
Debug.Log("[PlayerBumpCollisionBehavior] Player input blocked during bump");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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("[PlayerBumpCollisionBehavior] Player input restored after bump");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnImmunityEnd()
|
||||||
|
{
|
||||||
|
base.OnImmunityEnd();
|
||||||
|
|
||||||
|
// Stop any ongoing bump if immunity ends
|
||||||
|
if (_isBumping)
|
||||||
|
{
|
||||||
|
_isBumping = false;
|
||||||
|
if (_bumpInputBlocked)
|
||||||
|
{
|
||||||
|
RestoreBumpInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8222f0e3aeeb4fc4975aaead6cf7afbe
|
||||||
|
timeCreated: 1758109619
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Minigames.DivingForPictures
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for handling player collisions with world obstacles.
|
||||||
|
/// Detects collisions between Player layer (7) and WorldObstacle layer (6).
|
||||||
|
/// </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 - should match WorldObstacle layer")]
|
||||||
|
[SerializeField] protected LayerMask obstacleLayerMask = 1 << 6; // WorldObstacle layer
|
||||||
|
|
||||||
|
[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;
|
||||||
|
|
||||||
|
// Events for damage state changes
|
||||||
|
public static event Action OnDamageStart;
|
||||||
|
public static event Action OnDamageEnd;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public static method to trigger damage start event from external classes
|
||||||
|
/// </summary>
|
||||||
|
public static void TriggerDamageStart()
|
||||||
|
{
|
||||||
|
OnDamageStart?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public static method to trigger damage end event from external classes
|
||||||
|
/// </summary>
|
||||||
|
public static void TriggerDamageEnd()
|
||||||
|
{
|
||||||
|
OnDamageEnd?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool isImmune;
|
||||||
|
protected float immunityTimer;
|
||||||
|
protected Collider2D playerCollider;
|
||||||
|
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>();
|
||||||
|
|
||||||
|
// Look for collider on this GameObject first, then in children
|
||||||
|
playerCollider = GetComponent<Collider2D>();
|
||||||
|
if (playerCollider == null)
|
||||||
|
{
|
||||||
|
playerCollider = GetComponentInChildren<Collider2D>();
|
||||||
|
if (playerCollider != null)
|
||||||
|
{
|
||||||
|
Debug.Log($"[{GetType().Name}] Found collider on child object: {playerCollider.gameObject.name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerCollider == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[{GetType().Name}] No Collider2D found on this GameObject or its children!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Update()
|
||||||
|
{
|
||||||
|
// Handle immunity timer
|
||||||
|
if (isImmune)
|
||||||
|
{
|
||||||
|
immunityTimer -= Time.deltaTime;
|
||||||
|
if (immunityTimer <= 0f)
|
||||||
|
{
|
||||||
|
isImmune = false;
|
||||||
|
OnImmunityEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for collisions if not immune
|
||||||
|
if (!isImmune && playerCollider != null)
|
||||||
|
{
|
||||||
|
CheckForCollisions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for collisions with obstacle layer objects
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void CheckForCollisions()
|
||||||
|
{
|
||||||
|
// Get all colliders overlapping with the player
|
||||||
|
Collider2D[] overlapping = new Collider2D[10];
|
||||||
|
ContactFilter2D filter = new ContactFilter2D();
|
||||||
|
filter.SetLayerMask(obstacleLayerMask);
|
||||||
|
filter.useTriggers = true;
|
||||||
|
|
||||||
|
int count = playerCollider.Overlap(filter, overlapping);
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
// Found collision, trigger response
|
||||||
|
OnCollisionDetected(overlapping[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 (isImmune) return;
|
||||||
|
|
||||||
|
// Start immunity period
|
||||||
|
isImmune = true;
|
||||||
|
immunityTimer = damageImmunityDuration;
|
||||||
|
|
||||||
|
// Call the specific collision response
|
||||||
|
HandleCollisionResponse(obstacle);
|
||||||
|
|
||||||
|
// Notify about immunity start
|
||||||
|
OnImmunityStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnImmunityStart()
|
||||||
|
{
|
||||||
|
Debug.Log($"[{GetType().Name}] Damage immunity started for {damageImmunityDuration} seconds");
|
||||||
|
|
||||||
|
// Block input if specified
|
||||||
|
if (blockInputDuringImmunity)
|
||||||
|
{
|
||||||
|
BlockPlayerInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast damage start event
|
||||||
|
OnDamageStart?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when damage immunity ends
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnImmunityEnd()
|
||||||
|
{
|
||||||
|
Debug.Log($"[{GetType().Name}] Damage immunity ended");
|
||||||
|
|
||||||
|
// Restore input if it was blocked
|
||||||
|
if (wasInputBlocked)
|
||||||
|
{
|
||||||
|
RestorePlayerInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast damage end event
|
||||||
|
OnDamageEnd?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
/// Public property to check if player is currently immune
|
||||||
|
/// </summary>
|
||||||
|
public bool IsImmune => isImmune;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remaining immunity time
|
||||||
|
/// </summary>
|
||||||
|
public float RemainingImmunityTime => immunityTimer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e6c959bca2e24e72bf22e92439580d79
|
||||||
|
timeCreated: 1758109598
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Minigames.DivingForPictures
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Collision behavior that handles damage from mobile obstacles.
|
||||||
|
/// Unlike bump collisions, this only deals damage without physical response.
|
||||||
|
/// Detects collisions between Player layer (7) and QuarryObstacle layer (11).
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerDamageCollisionBehavior : PlayerCollisionBehavior
|
||||||
|
{
|
||||||
|
[Header("Damage Settings")]
|
||||||
|
[Tooltip("Base damage amount dealt by obstacles")]
|
||||||
|
[SerializeField] private float baseDamage = 1f;
|
||||||
|
|
||||||
|
[Tooltip("Whether to use the obstacle's individual damage value or the base damage")]
|
||||||
|
[SerializeField] private bool useObstacleDamageValue = true;
|
||||||
|
|
||||||
|
protected override void Awake()
|
||||||
|
{
|
||||||
|
base.Awake();
|
||||||
|
|
||||||
|
// Override the obstacle layer mask to target QuarryObstacle layer (11)
|
||||||
|
obstacleLayerMask = 1 << 11; // QuarryObstacle layer
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleCollisionResponse(Collider2D obstacle)
|
||||||
|
{
|
||||||
|
float damageAmount = baseDamage;
|
||||||
|
|
||||||
|
// Try to get damage from the obstacle component if enabled
|
||||||
|
if (useObstacleDamageValue)
|
||||||
|
{
|
||||||
|
FloatingObstacle obstacleComponent = obstacle.GetComponent<FloatingObstacle>();
|
||||||
|
if (obstacleComponent != null)
|
||||||
|
{
|
||||||
|
damageAmount = obstacleComponent.Damage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply damage (this could be extended to integrate with a health system)
|
||||||
|
ApplyDamage(damageAmount);
|
||||||
|
|
||||||
|
Debug.Log($"[PlayerDamageCollisionBehavior] Player took {damageAmount} damage from obstacle {obstacle.gameObject.name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies damage to the player
|
||||||
|
/// Override this method to integrate with your health/damage system
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="damage">Amount of damage to apply</param>
|
||||||
|
protected virtual void ApplyDamage(float damage)
|
||||||
|
{
|
||||||
|
// For now, just log the damage
|
||||||
|
// In a full implementation, this would reduce player health, trigger UI updates, etc.
|
||||||
|
Debug.Log($"[PlayerDamageCollisionBehavior] Applied {damage} damage to player");
|
||||||
|
|
||||||
|
// TODO: Integrate with health system when available
|
||||||
|
// Example: playerHealth.TakeDamage(damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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($"[PlayerDamageCollisionBehavior] Damage immunity started for {damageImmunityDuration} seconds");
|
||||||
|
|
||||||
|
// Don't block input for obstacle damage - let player keep moving
|
||||||
|
// Only broadcast the damage event
|
||||||
|
TriggerDamageStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override to handle immunity end without input restoration
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnImmunityEnd()
|
||||||
|
{
|
||||||
|
Debug.Log($"[PlayerDamageCollisionBehavior] Damage immunity ended");
|
||||||
|
|
||||||
|
// Broadcast damage end event
|
||||||
|
TriggerDamageEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public method to set base damage at runtime
|
||||||
|
/// </summary>
|
||||||
|
public void SetBaseDamage(float damage)
|
||||||
|
{
|
||||||
|
baseDamage = damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public method to toggle between base damage and obstacle-specific damage
|
||||||
|
/// </summary>
|
||||||
|
public void SetUseObstacleDamage(bool useObstacleDamage)
|
||||||
|
{
|
||||||
|
useObstacleDamageValue = useObstacleDamage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c9c18dbd013d42ae8c221e6205e4d49c
|
||||||
|
timeCreated: 1758116850
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Minigames.DivingForPictures
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Collision behavior that grants temporary immunity and allows the player to phase through obstacles.
|
||||||
|
/// During immunity, the player can pass through all obstacles without further collision detection.
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerPhaseCollisionBehavior : PlayerCollisionBehavior
|
||||||
|
{
|
||||||
|
[Header("Phase Settings")]
|
||||||
|
[Tooltip("Whether to disable the player's collider during immunity to allow phasing through obstacles")]
|
||||||
|
[SerializeField] private bool disableColliderDuringImmunity = true;
|
||||||
|
|
||||||
|
[Tooltip("How fast the player sprite blinks during phase mode (seconds between blinks)")]
|
||||||
|
[SerializeField] private float visualFeedbackBlinkRate = 0.1f;
|
||||||
|
|
||||||
|
private SpriteRenderer _spriteRenderer;
|
||||||
|
private bool _originalColliderState;
|
||||||
|
private bool _isBlinking;
|
||||||
|
private float _blinkTimer;
|
||||||
|
private Color _originalColor;
|
||||||
|
private float _originalAlpha;
|
||||||
|
|
||||||
|
protected override void Awake()
|
||||||
|
{
|
||||||
|
base.Awake();
|
||||||
|
|
||||||
|
// Get sprite renderer from player character
|
||||||
|
if (playerCharacter != null)
|
||||||
|
{
|
||||||
|
_spriteRenderer = playerCharacter.GetComponent<SpriteRenderer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_spriteRenderer != null)
|
||||||
|
{
|
||||||
|
_originalColor = _spriteRenderer.color;
|
||||||
|
_originalAlpha = _originalColor.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerCollider != null)
|
||||||
|
{
|
||||||
|
_originalColliderState = playerCollider.enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// Handle visual feedback blinking during immunity
|
||||||
|
if (_isBlinking && _spriteRenderer != null)
|
||||||
|
{
|
||||||
|
_blinkTimer -= Time.deltaTime;
|
||||||
|
|
||||||
|
if (_blinkTimer <= 0f)
|
||||||
|
{
|
||||||
|
// Toggle visibility
|
||||||
|
Color currentColor = _spriteRenderer.color;
|
||||||
|
currentColor.a = currentColor.a > 0.5f ? 0.3f : _originalAlpha;
|
||||||
|
_spriteRenderer.color = currentColor;
|
||||||
|
|
||||||
|
_blinkTimer = visualFeedbackBlinkRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CheckForCollisions()
|
||||||
|
{
|
||||||
|
// Override to skip collision detection entirely during immunity if collider is disabled
|
||||||
|
if (isImmune && disableColliderDuringImmunity)
|
||||||
|
{
|
||||||
|
return; // Skip collision detection completely
|
||||||
|
}
|
||||||
|
|
||||||
|
base.CheckForCollisions();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleCollisionResponse(Collider2D obstacle)
|
||||||
|
{
|
||||||
|
Debug.Log("[PlayerPhaseCollisionBehavior] Collision detected - entering phase mode");
|
||||||
|
|
||||||
|
// No immediate physical response - just start immunity period
|
||||||
|
// The immunity will be handled by the base class
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnImmunityStart()
|
||||||
|
{
|
||||||
|
base.OnImmunityStart();
|
||||||
|
|
||||||
|
// Disable collider to allow phasing through obstacles
|
||||||
|
if (disableColliderDuringImmunity && playerCollider != null)
|
||||||
|
{
|
||||||
|
playerCollider.enabled = false;
|
||||||
|
Debug.Log("[PlayerPhaseCollisionBehavior] Collider disabled - entering phase mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start visual feedback
|
||||||
|
StartVisualFeedback();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnImmunityEnd()
|
||||||
|
{
|
||||||
|
base.OnImmunityEnd();
|
||||||
|
|
||||||
|
// Re-enable collider
|
||||||
|
if (playerCollider != null)
|
||||||
|
{
|
||||||
|
playerCollider.enabled = _originalColliderState;
|
||||||
|
Debug.Log("[PlayerPhaseCollisionBehavior] Collider re-enabled - exiting phase mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop visual feedback
|
||||||
|
StopVisualFeedback();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the visual feedback to indicate immunity/phase mode
|
||||||
|
/// </summary>
|
||||||
|
private void StartVisualFeedback()
|
||||||
|
{
|
||||||
|
if (_spriteRenderer != null)
|
||||||
|
{
|
||||||
|
_isBlinking = true;
|
||||||
|
_blinkTimer = 0f;
|
||||||
|
|
||||||
|
// Start with reduced opacity
|
||||||
|
Color immunityColor = _originalColor;
|
||||||
|
immunityColor.a = 0.3f;
|
||||||
|
_spriteRenderer.color = immunityColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops the visual feedback and restores original appearance
|
||||||
|
/// </summary>
|
||||||
|
private void StopVisualFeedback()
|
||||||
|
{
|
||||||
|
_isBlinking = false;
|
||||||
|
|
||||||
|
if (_spriteRenderer != null)
|
||||||
|
{
|
||||||
|
// Restore original color and alpha
|
||||||
|
_spriteRenderer.color = _originalColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public method to toggle collider behavior during immunity
|
||||||
|
/// </summary>
|
||||||
|
public void SetColliderDisabling(bool disable)
|
||||||
|
{
|
||||||
|
disableColliderDuringImmunity = disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if player is currently in phase mode
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPhasing => isImmune && disableColliderDuringImmunity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manually trigger phase mode (useful for testing or special abilities)
|
||||||
|
/// </summary>
|
||||||
|
public void TriggerPhaseMode(float duration = -1f)
|
||||||
|
{
|
||||||
|
if (duration > 0f)
|
||||||
|
{
|
||||||
|
isImmune = true;
|
||||||
|
immunityTimer = duration;
|
||||||
|
OnImmunityStart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isImmune = true;
|
||||||
|
immunityTimer = damageImmunityDuration;
|
||||||
|
OnImmunityStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d849a517ce3a41249ae9f37d2722cefa
|
||||||
|
timeCreated: 1758109641
|
||||||
@@ -18,7 +18,7 @@ TagManager:
|
|||||||
- Pulver
|
- Pulver
|
||||||
- WorldBoundary
|
- WorldBoundary
|
||||||
- Interactable
|
- Interactable
|
||||||
-
|
- QuarryObstacle
|
||||||
-
|
-
|
||||||
-
|
-
|
||||||
-
|
-
|
||||||
|
|||||||
Reference in New Issue
Block a user