From 962128fb7f959713a76c814e9acebcfcb3a5e7d2 Mon Sep 17 00:00:00 2001 From: Danil Iarosh Date: Tue, 9 Sep 2025 13:20:23 +0300 Subject: [PATCH] Completed all projectiles work --- Assets/Resources/PerformanceTestRunInfo.json | 1 + .../PerformanceTestRunInfo.json.meta | 7 + .../Resources/PerformanceTestRunSettings.json | 1 + .../PerformanceTestRunSettings.json.meta | 7 + .../Prefabs/PickableProjectile.prefab | 23 +- Assets/Resources/Prefabs/Player.prefab | 37 +- Assets/Resources/Scenes/Levels/1-2.unity | 116 +++---- .../Scripts/Behaviour/PlayerFireScript.cs | 72 ++-- Assets/Resources/Scripts/EnemyController.cs | 2 +- Assets/Resources/Scripts/GameManager.cs | 8 +- .../Scripts/Projectiles/PickableProjectile.cs | 257 +++++++------- Assets/Resources/Scripts/Temp.cs | 319 ++++++++++++++++++ Assets/Resources/Scripts/Temp.cs.meta | 2 + ProjectSettings/ProjectSettings.asset | 3 +- 14 files changed, 600 insertions(+), 255 deletions(-) create mode 100644 Assets/Resources/PerformanceTestRunInfo.json create mode 100644 Assets/Resources/PerformanceTestRunInfo.json.meta create mode 100644 Assets/Resources/PerformanceTestRunSettings.json create mode 100644 Assets/Resources/PerformanceTestRunSettings.json.meta create mode 100644 Assets/Resources/Scripts/Temp.cs create mode 100644 Assets/Resources/Scripts/Temp.cs.meta diff --git a/Assets/Resources/PerformanceTestRunInfo.json b/Assets/Resources/PerformanceTestRunInfo.json new file mode 100644 index 0000000..25c30b7 --- /dev/null +++ b/Assets/Resources/PerformanceTestRunInfo.json @@ -0,0 +1 @@ +{"TestSuite":"","Date":0,"Player":{"Development":false,"ScreenWidth":0,"ScreenHeight":0,"ScreenRefreshRate":0,"Fullscreen":false,"Vsync":0,"AntiAliasing":0,"Batchmode":false,"RenderThreadingMode":"MultiThreaded","GpuSkinning":false,"Platform":"","ColorSpace":"","AnisotropicFiltering":"","BlendWeights":"","GraphicsApi":"","ScriptingBackend":"IL2CPP","AndroidTargetSdkVersion":"AndroidApiLevelAuto","AndroidBuildSystem":"Gradle","BuildTarget":"WebGL","StereoRenderingPath":"MultiPass"},"Hardware":{"OperatingSystem":"","DeviceModel":"","DeviceName":"","ProcessorType":"","ProcessorCount":0,"GraphicsDeviceName":"","SystemMemorySizeMB":0},"Editor":{"Version":"6000.0.56f1","Branch":"6000.0/staging","Changeset":"9c7b5e468860","Date":1754996063},"Dependencies":["com.unity.collab-proxy@2.9.2","com.unity.feature.2d@2.0.1","com.unity.ide.rider@3.0.36","com.unity.ide.visualstudio@2.0.23","com.unity.inputsystem@1.14.2","com.unity.multiplayer.center@1.0.0","com.unity.render-pipelines.universal@17.0.4","com.unity.test-framework@1.5.1","com.unity.timeline@1.8.9","com.unity.ugui@2.0.0","com.unity.visualscripting@1.9.7","com.unity.modules.accessibility@1.0.0","com.unity.modules.ai@1.0.0","com.unity.modules.androidjni@1.0.0","com.unity.modules.animation@1.0.0","com.unity.modules.assetbundle@1.0.0","com.unity.modules.audio@1.0.0","com.unity.modules.cloth@1.0.0","com.unity.modules.director@1.0.0","com.unity.modules.imageconversion@1.0.0","com.unity.modules.imgui@1.0.0","com.unity.modules.jsonserialize@1.0.0","com.unity.modules.particlesystem@1.0.0","com.unity.modules.physics@1.0.0","com.unity.modules.physics2d@1.0.0","com.unity.modules.screencapture@1.0.0","com.unity.modules.terrain@1.0.0","com.unity.modules.terrainphysics@1.0.0","com.unity.modules.tilemap@1.0.0","com.unity.modules.ui@1.0.0","com.unity.modules.uielements@1.0.0","com.unity.modules.umbra@1.0.0","com.unity.modules.unityanalytics@1.0.0","com.unity.modules.unitywebrequest@1.0.0","com.unity.modules.unitywebrequestassetbundle@1.0.0","com.unity.modules.unitywebrequestaudio@1.0.0","com.unity.modules.unitywebrequesttexture@1.0.0","com.unity.modules.unitywebrequestwww@1.0.0","com.unity.modules.vehicles@1.0.0","com.unity.modules.video@1.0.0","com.unity.modules.vr@1.0.0","com.unity.modules.wind@1.0.0","com.unity.modules.xr@1.0.0","com.unity.modules.subsystems@1.0.0","com.unity.modules.hierarchycore@1.0.0","com.unity.ext.nunit@2.0.5","com.unity.render-pipelines.core@17.0.4","com.unity.shadergraph@17.0.4","com.unity.render-pipelines.universal-config@17.0.3","com.unity.2d.animation@10.2.1","com.unity.2d.pixel-perfect@5.0.3","com.unity.2d.psdimporter@9.1.0","com.unity.2d.sprite@1.0.0","com.unity.2d.spriteshape@10.0.7","com.unity.2d.tilemap@1.0.0","com.unity.2d.tilemap.extras@4.1.0","com.unity.2d.aseprite@1.1.10","com.unity.searcher@4.9.3","com.unity.burst@1.8.24","com.unity.mathematics@1.3.2","com.unity.collections@2.5.1","com.unity.rendering.light-transport@1.0.1","com.unity.2d.common@9.1.1","com.unity.nuget.mono-cecil@1.11.4","com.unity.test-framework.performance@3.1.0"],"Results":[]} \ No newline at end of file diff --git a/Assets/Resources/PerformanceTestRunInfo.json.meta b/Assets/Resources/PerformanceTestRunInfo.json.meta new file mode 100644 index 0000000..69e9bce --- /dev/null +++ b/Assets/Resources/PerformanceTestRunInfo.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3f79429828f617f4e928d4bfd1a938e2 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/PerformanceTestRunSettings.json b/Assets/Resources/PerformanceTestRunSettings.json new file mode 100644 index 0000000..49438ae --- /dev/null +++ b/Assets/Resources/PerformanceTestRunSettings.json @@ -0,0 +1 @@ +{"MeasurementCount":-1} \ No newline at end of file diff --git a/Assets/Resources/PerformanceTestRunSettings.json.meta b/Assets/Resources/PerformanceTestRunSettings.json.meta new file mode 100644 index 0000000..15cf8fb --- /dev/null +++ b/Assets/Resources/PerformanceTestRunSettings.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 91efd83a093a02a4291e2c5043f20400 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Prefabs/PickableProjectile.prefab b/Assets/Resources/Prefabs/PickableProjectile.prefab index 8fc48c4..ccd8dd5 100644 --- a/Assets/Resources/Prefabs/PickableProjectile.prefab +++ b/Assets/Resources/Prefabs/PickableProjectile.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 1452460312376183192} - component: {fileID: -640855425046532091} - component: {fileID: 8375585699851212105} - - component: {fileID: -7990430535451479416} + - component: {fileID: 5633092123120389871} m_Layer: 0 m_Name: PickableProjectile m_TagString: Projectile @@ -47,7 +47,7 @@ Rigidbody2D: m_Simulated: 1 m_UseFullKinematicContacts: 0 m_UseAutoMass: 0 - m_Mass: 1 + m_Mass: 5 m_LinearDamping: 0 m_AngularDamping: 0.05 m_GravityScale: 1 @@ -98,7 +98,7 @@ CircleCollider2D: m_CompositeOrder: 0 m_Offset: {x: 0, y: 0} m_Radius: 0.5 ---- !u!114 &-7990430535451479416 +--- !u!114 &5633092123120389871 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -110,31 +110,36 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 5bf80b3f74c7fe04e8064ebca52b7ad4, type: 3} m_Name: m_EditorClassIdentifier: - initialSpeed: 20 + initialSpeed: 25 maxLifetime: 5 alignRotationToVelocity: 1 knockback: 0 hitLayers: serializedVersion: 2 - m_Bits: 0 + m_Bits: 4294967295 maxPierce: 0 useGravity: 1 - gravityScale: 3 + gravityScale: 1 owner: {fileID: 0} hitVfxPrefab: {fileID: 0} hitSfx: {fileID: 0} sfxVolume: 1 heldLocalOffset: {x: 0.25, y: 0.1, z: 0} - ignoreOwnerTimeOnThrow: 0.15 + snapExactlyToAttachPoint: 1 + ignoreOwnerTimeOnThrow: 0.25 attachPointChildName: HandPoint sleepVelocity: 0.05 lockWhenLanded: 1 - maxBounces: 20 - minBounceSpeed: 0.8 + maxBounces: 100 + minBounceSpeed: 8 restitution: 0.6 tangentDamping: 0.85 groundUpTolerance: 0.25 stickDelayAfterImpact: 0.03 + recallStartDistance: 10 + recallArriveDistance: 1.4 + recallMaxSpeed: 16 + recallAcceleration: 60 drawGizmos: 1 gizmoColor: {r: 0.2, g: 0.8, b: 1, a: 0.35} --- !u!1 &7253843866260996112 diff --git a/Assets/Resources/Prefabs/Player.prefab b/Assets/Resources/Prefabs/Player.prefab index cb22961..108e8b3 100644 --- a/Assets/Resources/Prefabs/Player.prefab +++ b/Assets/Resources/Prefabs/Player.prefab @@ -32,6 +32,37 @@ Transform: - {fileID: 6550761235546795890} m_Father: {fileID: 6550761235875199946} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &5503244841086142080 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1082696284330290386} + m_Layer: 0 + m_Name: HandPoint + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1082696284330290386 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5503244841086142080} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 6550761234898366363} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &6550761233853193377 GameObject: m_ObjectHideFlags: 0 @@ -1841,6 +1872,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 6550761235822222727} + - {fileID: 1082696284330290386} m_Father: {fileID: 6550761235013283179} m_LocalEulerAnglesHint: {x: 0, y: 0, z: -25.939} --- !u!212 &6550761234898366361 @@ -1910,9 +1942,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 57c008f954fe54a8bb972de1018a2cb8, type: 3} m_Name: m_EditorClassIdentifier: - m_RootBone: {fileID: 6550761235822222727} + m_RootBone: {fileID: 6550761234898366363} m_BoneTransforms: - - {fileID: 6550761235822222727} + - {fileID: 0} m_Bounds: m_Center: {x: 0.00043550134, y: -0.000074744225, z: 0} m_Extent: {x: 0.376185, y: 0.435875, z: 0} @@ -2656,7 +2688,6 @@ MonoBehaviour: attachPoint: {fileID: 6550761234898366363} actionKey: 102 throwSpeed: 20 - pickupRadius: 1.2 --- !u!1 &6550761235546795891 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Resources/Scenes/Levels/1-2.unity b/Assets/Resources/Scenes/Levels/1-2.unity index 467ad71..c0d13e7 100644 --- a/Assets/Resources/Scenes/Levels/1-2.unity +++ b/Assets/Resources/Scenes/Levels/1-2.unity @@ -60189,63 +60189,6 @@ SpriteRenderer: m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 ---- !u!1001 &1409395841 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalPosition.x - value: -3.1233063 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalPosition.y - value: -19.482407 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalPosition.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalRotation.w - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalRotation.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalRotation.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalRotation.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 5993946430821065449, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} - propertyPath: m_Name - value: PickableProjectile - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} --- !u!1001 &1503664770 PrefabInstance: m_ObjectHideFlags: 0 @@ -60891,6 +60834,63 @@ Transform: - {fileID: 1604648029} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1968702169 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + serializedVersion: 3 + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalPosition.x + value: 4.7835865 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalPosition.y + value: 2.5 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1452460312376183192, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5993946430821065449, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} + propertyPath: m_Name + value: PickableProjectile + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 7fd3a37dc702ece42b51e5867473f0ff, type: 3} --- !u!1001 &2013056424 PrefabInstance: m_ObjectHideFlags: 0 @@ -61107,4 +61107,4 @@ SceneRoots: - {fileID: 872032055} - {fileID: 2041263421} - {fileID: 2013056424} - - {fileID: 1409395841} + - {fileID: 1968702169} diff --git a/Assets/Resources/Scripts/Behaviour/PlayerFireScript.cs b/Assets/Resources/Scripts/Behaviour/PlayerFireScript.cs index dc2b6bd..633e2c2 100644 --- a/Assets/Resources/Scripts/Behaviour/PlayerFireScript.cs +++ b/Assets/Resources/Scripts/Behaviour/PlayerFireScript.cs @@ -1,5 +1,4 @@ -using Projectiles2D; -using UnityEngine; +using UnityEngine; public class PlayerFireScript : MonoBehaviour { @@ -11,74 +10,65 @@ public class PlayerFireScript : MonoBehaviour public Transform attachPoint; public KeyCode actionKey = KeyCode.F; public float throwSpeed = 20f; - public float pickupRadius = 1.2f; - private PickableProjectile P => PickableProjectile.Instance; + private PickableProjectile pickableProj; void Awake() { if (!attachPoint) - { - var hand = transform.Find("HandPoint"); - if (hand) attachPoint = hand; - } + attachPoint = FindDeepChild(transform, "HandPoint"); + + if (!attachPoint) + Debug.LogWarning("PlayerFireScript: attachPoint не задан и HandPoint не найден. Будет использован корень игрока."); } - void Update() + void Update() { - var proj = P; - - if (proj != null && proj.CurrentState != PickableProjectile.State.Held) - { - Vector3 grabFrom = attachPoint ? attachPoint.position : transform.position; - float dist = Vector2.Distance(proj.transform.position, grabFrom); - - if (dist <= pickupRadius) - { - proj.TryPickup(gameObject, attachPoint); - return; - } - } - if (!Input.GetKeyDown(actionKey)) return; - proj = P; + if (pickableProj == null) + pickableProj = PickableProjectile.Instance; - if (proj == null) + if (pickableProj == null) { if (!spawnIfMissing || projectilePrefab == null) { - Debug.Log("FireScript2D: projectile missing; set spawnIfMissing=true and assign projectilePrefab."); + Debug.Log("PlayerFireScript: projectile missing; enable spawnIfMissing and assign projectilePrefab."); return; } Vector3 spawnPos = attachPoint ? attachPoint.position : transform.position; - proj = Instantiate(projectilePrefab, spawnPos, Quaternion.identity); - proj.TryPickup(gameObject, attachPoint); + pickableProj = Instantiate(projectilePrefab, spawnPos, Quaternion.identity); + pickableProj.TryPickup(gameObject, attachPoint); return; } - if (proj.CurrentState == PickableProjectile.State.Held && proj.owner == gameObject) + // Бросок + if (pickableProj.CurrentState == PickableProjectile.State.Held && pickableProj.owner == gameObject) { float sx = transform.localScale.x; float facing = Mathf.Sign(sx == 0 ? 1 : sx); Vector2 dir = facing > 0 ? Vector2.right : Vector2.left; Vector2 launchPos = (Vector2)transform.position + new Vector2(1.4f * facing, 0f); - proj.transform.localScale = new Vector3(-facing * proj.transform.localScale.x, proj.transform.localScale.y, proj.transform.localScale.z); - proj.transform.position = launchPos; - proj.Launch(dir, throwSpeed, gameObject); - return; + var s = pickableProj.transform.localScale; + pickableProj.transform.localScale = new Vector3(Mathf.Abs(s.x) * -facing, s.y, s.z); + + pickableProj.transform.position = launchPos; + pickableProj.Launch(dir, throwSpeed, gameObject); } - } -#if UNITY_EDITOR - void OnDrawGizmosSelected() + public void BindProjectile(PickableProjectile proj) => pickableProj = proj; + + private Transform FindDeepChild(Transform parent, string name) { - Gizmos.color = new Color(0.2f, 0.8f, 1f, 0.35f); - Vector3 center = attachPoint ? attachPoint.position : transform.position; - Gizmos.DrawWireSphere(center, pickupRadius); + foreach (Transform child in parent) + { + if (child.name == name) return child; + var r = FindDeepChild(child, name); + if (r) return r; + } + return null; } -#endif -} +} \ No newline at end of file diff --git a/Assets/Resources/Scripts/EnemyController.cs b/Assets/Resources/Scripts/EnemyController.cs index 133ef6c..1126f5d 100644 --- a/Assets/Resources/Scripts/EnemyController.cs +++ b/Assets/Resources/Scripts/EnemyController.cs @@ -9,7 +9,7 @@ public class EnemyController : MonoBehaviour public float moveSpeed = 3f; public float acceleration = 10f; - private LayerMask obstacleMask; + private LayerMask obstacleMask = ~0; private Animator animator; private Rigidbody2D rb; private Vector2 targetVelocity = Vector2.zero; diff --git a/Assets/Resources/Scripts/GameManager.cs b/Assets/Resources/Scripts/GameManager.cs index 3032c04..92a4cbe 100644 --- a/Assets/Resources/Scripts/GameManager.cs +++ b/Assets/Resources/Scripts/GameManager.cs @@ -16,7 +16,8 @@ public class GameManager : MonoBehaviour public int lives { get; private set; } = 3; public int coins { get; private set; } = 0; - public GameObject playerObj; + public GameObject playerObj { get; private set; } + [SerializeField] private GameObject playerPrefab; [SerializeField] private string playerPrefabResourcePath = "Prefabs/Player"; @@ -25,6 +26,8 @@ public class GameManager : MonoBehaviour private void OnEnable() => SceneManager.sceneLoaded += OnSceneLoaded; private void OnDisable() => SceneManager.sceneLoaded -= OnSceneLoaded; + public event Action PlayerSpawned; + public event Action CoinsChanged; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] @@ -117,6 +120,7 @@ public class GameManager : MonoBehaviour { SceneManager.LoadScene("Level UI", LoadSceneMode.Additive); } + else _shouldSpawnPlayer = false; } @@ -140,6 +144,8 @@ public class GameManager : MonoBehaviour playerObj = Instantiate(prefab, pos, rot); SceneManager.MoveGameObjectToScene(playerObj, SceneManager.GetActiveScene()); + + PlayerSpawned?.Invoke(playerObj); } public void NextLevel() diff --git a/Assets/Resources/Scripts/Projectiles/PickableProjectile.cs b/Assets/Resources/Scripts/Projectiles/PickableProjectile.cs index 78a68c4..f502c27 100644 --- a/Assets/Resources/Scripts/Projectiles/PickableProjectile.cs +++ b/Assets/Resources/Scripts/Projectiles/PickableProjectile.cs @@ -1,5 +1,4 @@ using UnityEngine; -using UnityEngine.SceneManagement; [DisallowMultipleComponent] [RequireComponent(typeof(Rigidbody2D))] @@ -13,6 +12,7 @@ public class PickableProjectile : BasicProjectile [Header("Pickup/Attach")] public Vector3 heldLocalOffset = new(0.25f, 0.1f, 0f); + public bool snapExactlyToAttachPoint = true; public float ignoreOwnerTimeOnThrow = 0.25f; public string attachPointChildName = "HandPoint"; @@ -21,47 +21,30 @@ public class PickableProjectile : BasicProjectile public bool lockWhenLanded = true; [Header("Bouncing")] - [Tooltip("Максимум отскоков прежде чем лечь на землю.")] public int maxBounces = 3; - [Tooltip("Минимальная скорость для отскока.")] public float minBounceSpeed = 0.8f; - [Tooltip("Коэффициент упругости (0..1).")] [Range(0f, 1f)] public float restitution = 0.6f; - [Tooltip("Демпфирование вдоль поверхности (0..1), меньше — сильнее гасит скольжение.")] [Range(0f, 1f)] public float tangentDamping = 0.85f; - [Tooltip("Насколько «вверх» должна смотреть средняя нормаль, чтобы считать поверхность землёй.")] [Range(0f, 1f)] public float groundUpTolerance = 0.25f; - [Tooltip("Маленькая пауза, чтобы не прилипнуть в кадр столкновения.")] public float stickDelayAfterImpact = 0.03f; - [Header("Persistence / Recall")] - [Tooltip("Сохранять снаряд между сценами.")] - public bool persistAcrossScenes = true; - [Tooltip("Если перед загрузкой новой сцены снаряд был в руке — автоматически прицепиться к игроку на новой сцене.")] - public bool reattachIfWasHeldOnLoad = true; - [Tooltip("Старт возврата, если дальше этой дистанции от точки руки.")] public float recallStartDistance = 8f; [Tooltip("На этой дистанции от руки считаем, что прилетел и подбираем.")] - public float recallArriveDistance = 1.4f; - [Tooltip("Максимальная скорость «магнита».")] + public float recallArriveDistance = 1f; public float recallMaxSpeed = 16f; - [Tooltip("Ускорение «магнита».")] public float recallAcceleration = 60f; [Header("Gizmos")] public bool drawGizmos; public Color gizmoColor = new Color(0.2f, 0.8f, 1f, 0.35f); - private GameObject holder; private Transform holderAttachPoint; - private int bounceCounter = 0; private float lastImpactTime = -999f; + private Vector3 baseLocalScale = Vector3.one; - // «Когда-либо был в руке» — разрешает бумеранг private bool hadBeenHeld = false; - // Запоминаем последнего игрока для возврата private GameObject lastKnownHolder; protected override void Awake() @@ -73,114 +56,52 @@ public class PickableProjectile : BasicProjectile Destroy(gameObject); return; } + Instance = this; - - if (persistAcrossScenes) - DontDestroyOnLoad(gameObject); - - SceneManager.sceneLoaded += OnSceneLoaded; - - rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous; - col.isTrigger = false; - - BecomeGrounded(null); } private void OnDestroy() { if (Instance == this) Instance = null; - SceneManager.sceneLoaded -= OnSceneLoaded; } - private void OnSceneLoaded(Scene scene, LoadSceneMode mode) - { - Debug.Log("OnSceneLoaded"); - Debug.Log($"Owner: {owner}"); - Debug.Log($"LastKnownHolder: {lastKnownHolder}"); - if (!reattachIfWasHeldOnLoad) return; - // Если до загрузки мы были в руке — прицепляемся к игроку на новой сцене - if (CurrentState == State.Held || (hadBeenHeld && owner != null)) + private void Start() + { + rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous; + col.isTrigger = false; + + if (CurrentState != State.Held) + BecomeGrounded(null); + } + + protected override void FixedUpdate() + { + base.FixedUpdate(); + + if (!lockWhenLanded && CurrentState == State.Airborne) { - var playerGo = - (GameManager.Instance && GameManager.Instance.playerObj) ? - GameManager.Instance.playerObj : - GameObject.FindWithTag("Player"); - - Debug.Log($"PlayerObj: {playerGo}"); - - if (!playerGo) return; - - var attach = FindAttachPointOn(playerGo) ?? playerGo.transform; - - transform.SetParent(null, true); - TryPickup(playerGo, attach); + if (rb.linearVelocity.sqrMagnitude < (sleepVelocity * sleepVelocity)) + BecomeGrounded(null); } - } - public bool TryPickup(GameObject newHolder, Transform optionalAttachPoint = null) - { - if (CurrentState == State.Held) return true; - - holder = newHolder; - lastKnownHolder = newHolder; - - holderAttachPoint = optionalAttachPoint ?? FindAttachPointOn(holder); - if (holderAttachPoint == null) holderAttachPoint = holder.transform; - - rb.linearVelocity = Vector2.zero; - rb.angularVelocity = 0f; - rb.bodyType = RigidbodyType2D.Kinematic; - col.enabled = false; - - transform.SetParent(holderAttachPoint, worldPositionStays: false); - transform.localPosition = heldLocalOffset; - transform.localRotation = Quaternion.identity; - transform.localScale = Vector3.one * 2; // оставляю как у тебя - - // Игнор коллизии владельца нужен при броске, не при «магните» - StartCoroutine(IgnoreOwnerTemporarily(holder, ignoreOwnerTimeOnThrow)); - - CurrentState = State.Held; - launched = false; - owner = holder; - - hadBeenHeld = true; // разрешаем бумеранг в будущем - return true; - } - - public void Launch(Vector2 direction, float speed, GameObject throwOwner = null) - { - if (CurrentState == State.Held) - transform.SetParent(null, true); - - holder = throwOwner; - lastKnownHolder = throwOwner; - holderAttachPoint = null; - - rb.bodyType = RigidbodyType2D.Dynamic; - col.enabled = true; - - launched = true; - owner = throwOwner; - - Vector2 dir = direction.sqrMagnitude > 0.0001f ? direction.normalized : Vector2.right; - float finalSpeed = (speed > 0f) ? speed : initialSpeed; - - rb.linearVelocity = Vector2.zero; - rb.angularVelocity = 0f; - rb.AddForce(dir * finalSpeed * rb.mass, ForceMode2D.Impulse); - transform.right = dir; - - if (throwOwner != null && ignoreOwnerTimeOnThrow > 0f) - StartCoroutine(IgnoreOwnerTemporarily(throwOwner, ignoreOwnerTimeOnThrow)); - - CurrentState = State.Airborne; - bounceCounter = 0; + // Бумеранг только если снаряд когда-то уже был в руке + if (hadBeenHeld && CurrentState != State.Held) + HandleRecallToPlayer(); } protected override void OnCollisionEnter2D(Collision2D collision) { + var otherRb = collision.rigidbody ? collision.rigidbody : collision.otherRigidbody; + var otherGo = (collision.collider ? collision.collider.attachedRigidbody?.gameObject : null) + ?? otherRb?.gameObject + ?? collision.gameObject; + + if (otherGo && otherGo.CompareTag("Player") && CurrentState != State.Held) + { + if (TryPickup(otherGo)) return; + } + base.OnCollisionEnter2D(collision); if (CurrentState == State.Airborne) @@ -195,6 +116,83 @@ public class PickableProjectile : BasicProjectile } } + public bool TryPickup(GameObject newHolder, Transform optionalAttachPoint = null) + { + if (CurrentState == State.Held) return true; + + owner = newHolder; + lastKnownHolder = newHolder; + + holderAttachPoint = optionalAttachPoint ?? FindAttachPoint(newHolder); + var fireScript = newHolder.GetComponent(); + if (holderAttachPoint == null && fireScript != null) + holderAttachPoint = fireScript.attachPoint; + if (holderAttachPoint == null) + holderAttachPoint = newHolder.transform; + + rb.linearVelocity = Vector2.zero; + rb.angularVelocity = 0f; + rb.bodyType = RigidbodyType2D.Kinematic; + col.enabled = false; + + transform.SetParent(holderAttachPoint, worldPositionStays: true); + transform.position = holderAttachPoint.TransformPoint(heldLocalOffset); + transform.rotation = holderAttachPoint.rotation; + transform.localScale = baseLocalScale; + + if (fireScript != null) fireScript.BindProjectile(this); + + StartCoroutine(IgnoreOwnerTemporarily(owner, ignoreOwnerTimeOnThrow)); + + CurrentState = State.Held; + launched = false; + hadBeenHeld = true; + + return true; + } + + private Transform ResolveAttachPoint(GameObject holder, Transform preferred = null) + { + if (preferred) return preferred; + + var fs = holder.GetComponent(); + if (fs && fs.attachPoint) return fs.attachPoint; + + return holder.transform; + } + + public void Launch(Vector2 direction, float speed, GameObject throwOwner = null) + { + if (CurrentState == State.Held) + transform.SetParent(null, worldPositionStays: true); + + owner = throwOwner; + lastKnownHolder = throwOwner; + holderAttachPoint = null; + + rb.bodyType = RigidbodyType2D.Dynamic; + col.enabled = true; + + launched = true; + + Vector2 dir = direction.sqrMagnitude > 0.0001f ? direction.normalized : Vector2.right; + float finalSpeed = (speed > 0f) ? speed : initialSpeed; + + rb.linearVelocity = Vector2.zero; + rb.angularVelocity = 0f; + rb.AddForce(dir * finalSpeed * rb.mass, ForceMode2D.Impulse); + + transform.right = dir; + transform.localScale = baseLocalScale; + + if (throwOwner != null && ignoreOwnerTimeOnThrow > 0f) + StartCoroutine(IgnoreOwnerTemporarily(throwOwner, ignoreOwnerTimeOnThrow)); + + CurrentState = State.Airborne; + bounceCounter = 0; + } + + protected override void OnImpactFinal(Collision2D collision) { if (!TryBounce(collision)) @@ -265,35 +263,16 @@ public class PickableProjectile : BasicProjectile bounceCounter = 0; } - private Transform FindAttachPointOn(GameObject who) + private Transform FindAttachPoint(GameObject who) { if (string.IsNullOrWhiteSpace(attachPointChildName)) return null; var t = who.transform.Find(attachPointChildName); return t; } - protected override void FixedUpdate() - { - base.FixedUpdate(); - - if (!lockWhenLanded && CurrentState == State.Airborne) - { - if (rb.linearVelocity.sqrMagnitude < (sleepVelocity * sleepVelocity)) - BecomeGrounded(null); - } - - // Бумеранг только если снаряд когда-то уже был в руке - if (hadBeenHeld && CurrentState != State.Held) - HandleRecallToPlayer(); - } - private void HandleRecallToPlayer() { - // Целимся в актуального игрока, предпочтительно последнего владельца - GameObject playerGo = null; - - if (lastKnownHolder != null) - playerGo = lastKnownHolder; + GameObject playerGo = lastKnownHolder; if (playerGo == null || !playerGo.activeInHierarchy) playerGo = (GameManager.Instance && GameManager.Instance.playerObj) @@ -302,20 +281,18 @@ public class PickableProjectile : BasicProjectile if (!playerGo) return; - var hand = FindAttachPointOn(playerGo) ?? playerGo.transform; + var hand = ResolveAttachPoint(playerGo); Vector2 toHand = (Vector2)hand.position - rb.position; float dist = toHand.magnitude; if (dist > recallStartDistance) { - // Начинаем «магнит» if (CurrentState != State.Returning) { CurrentState = State.Returning; rb.bodyType = RigidbodyType2D.Dynamic; col.enabled = true; - // ВАЖНО: не игнорируем коллизию с игроком здесь, чтобы не проскальзывать через него } Vector2 desired = toHand.normalized * recallMaxSpeed; @@ -326,13 +303,11 @@ public class PickableProjectile : BasicProjectile transform.right = rb.linearVelocity.normalized; if (dist <= recallArriveDistance) - TryPickup(playerGo, hand); + TryPickup(playerGo); } - else + else if (CurrentState == State.Returning) { - // Вышли из «дальней» зоны — возвращаем обычное состояние - if (CurrentState == State.Returning) - CurrentState = rb.bodyType == RigidbodyType2D.Dynamic ? State.Airborne : State.Grounded; + CurrentState = rb.bodyType == RigidbodyType2D.Dynamic ? State.Airborne : State.Grounded; } } diff --git a/Assets/Resources/Scripts/Temp.cs b/Assets/Resources/Scripts/Temp.cs new file mode 100644 index 0000000..1a1a4cb --- /dev/null +++ b/Assets/Resources/Scripts/Temp.cs @@ -0,0 +1,319 @@ +using UnityEngine; + +[DisallowMultipleComponent] +[RequireComponent(typeof(Rigidbody2D))] +[RequireComponent(typeof(Collider2D))] +public class PickableProjectileTemp : BasicProjectile +{ + // --- Singleton --- + public static PickableProjectileTemp Instance { get; private set; } + + public enum State { Held, Airborne, Grounded, Returning } + public State CurrentState { get; private set; } = State.Grounded; + + [Header("Pickup/Attach")] + public Vector3 heldLocalOffset = new(0.25f, 0.1f, 0f); + public float ignoreOwnerTimeOnThrow = 0.25f; + public string attachPointChildName = "HandPoint"; + + [Header("Physics on ground")] + public float sleepVelocity = 0.05f; + public bool lockWhenLanded = true; + + [Header("Bouncing")] + public int maxBounces = 3; + public float minBounceSpeed = 0.8f; + [Range(0f, 1f)] public float restitution = 0.6f; + [Range(0f, 1f)] public float tangentDamping = 0.85f; + [Range(0f, 1f)] public float groundUpTolerance = 0.25f; + public float stickDelayAfterImpact = 0.03f; + + [Header("Recall")] + public float recallStartDistance = 8f; + public float recallArriveDistance = 1f; + public float recallMaxSpeed = 16f; + public float recallAcceleration = 60f; + + [Header("Gizmos")] + public bool drawGizmos; + public Color gizmoColor = new Color(0.2f, 0.8f, 1f, 0.35f); + + private Transform holderAttachPoint; + private int bounceCounter = 0; + private float lastImpactTime = -999f; + + private bool hadBeenHeld = false; + private GameObject lastKnownHolder; + + // локальный «таймер» игнора владельца после броска, + // чтобы не подбирать мгновенно владельцем при столкновении + private float ignoreOwnerUntil = -999f; + + protected override void Awake() + { + base.Awake(); + if (Instance != null && Instance != this) + { + Destroy(gameObject); + return; + } + Instance = this; + } + + private void OnDestroy() + { + if (Instance == this) Instance = null; + } + + private void Start() + { + rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous; + col.isTrigger = false; + + if (CurrentState != State.Held) + BecomeGrounded(null); + } + + public bool TryPickup(GameObject newHolder, Transform optionalAttachPoint = null) + { + if (CurrentState == State.Held) return true; + + owner = newHolder; + lastKnownHolder = newHolder; + + holderAttachPoint = optionalAttachPoint ?? FindAttachPointOn(owner); + if (holderAttachPoint == null) holderAttachPoint = owner.transform; + + rb.linearVelocity = Vector2.zero; + rb.angularVelocity = 0f; + rb.bodyType = RigidbodyType2D.Kinematic; + col.enabled = false; + + transform.SetParent(holderAttachPoint, worldPositionStays: false); + transform.localPosition = heldLocalOffset; + transform.localRotation = Quaternion.identity; + transform.localScale = Vector3.one * 2; + + // чуть-чуть игнорим владельца, чтобы не словить повторные коллизии + ignoreOwnerUntil = Time.time + ignoreOwnerTimeOnThrow; + + StartCoroutine(IgnoreOwnerTemporarily(owner, ignoreOwnerTimeOnThrow)); + + CurrentState = State.Held; + launched = false; + + hadBeenHeld = true; + + // передадим ссылку игроку (если у него есть PlayerFireScript), + // чтобы он знал «свой» снаряд + var pfs = owner.GetComponent(); + //if (pfs) pfs.BindProjectile(this); + + return true; + } + + public void Launch(Vector2 direction, float speed, GameObject throwOwner = null) + { + if (CurrentState == State.Held) + transform.SetParent(null, true); + + owner = throwOwner; + lastKnownHolder = throwOwner; + holderAttachPoint = null; + + rb.bodyType = RigidbodyType2D.Dynamic; + col.enabled = true; + + launched = true; + + Vector2 dir = direction.sqrMagnitude > 0.0001f ? direction.normalized : Vector2.right; + float finalSpeed = (speed > 0f) ? speed : initialSpeed; + + rb.linearVelocity = Vector2.zero; + rb.angularVelocity = 0f; + rb.AddForce(dir * finalSpeed * rb.mass, ForceMode2D.Impulse); + transform.right = dir; + + if (throwOwner != null && ignoreOwnerTimeOnThrow > 0f) + { + ignoreOwnerUntil = Time.time + ignoreOwnerTimeOnThrow; + StartCoroutine(IgnoreOwnerTemporarily(throwOwner, ignoreOwnerTimeOnThrow)); + } + + CurrentState = State.Airborne; + bounceCounter = 0; + } + + protected override void OnCollisionEnter2D(Collision2D collision) + { + // 1) если врезались в игрока — пробуем подобрать до логики рикошета + var otherRb = collision.rigidbody ? collision.rigidbody : collision.otherRigidbody; + var otherGo = (collision.collider ? collision.collider.attachedRigidbody?.gameObject : null) + ?? otherRb?.gameObject + ?? collision.gameObject; + + if (otherGo && otherGo.CompareTag("Player") && CurrentState != State.Held) + { + // не подбираем мгновенно того же владельца, если ещё в «игноре» + if (otherGo != owner || Time.time >= ignoreOwnerUntil) + { + var hand = FindAttachPointOn(otherGo) ?? otherGo.transform; + if (TryPickup(otherGo, hand)) return; // успешно подобрали — выходим + } + } + + // 2) обычная логика попадания/рикошета + base.OnCollisionEnter2D(collision); + + if (CurrentState == State.Airborne) + { + if (TryBounce(collision)) + { + lastImpactTime = Time.time; + return; + } + + BecomeGrounded(collision); + } + } + + protected override void OnImpactFinal(Collision2D collision) + { + if (!TryBounce(collision)) + BecomeGrounded(collision); + } + + protected override void OnImpactFinal(Collider2D other) + { + if (Time.time - lastImpactTime > stickDelayAfterImpact) + BecomeGrounded(null); + } + + private bool TryBounce(Collision2D collision) + { + if (Time.time - lastImpactTime < stickDelayAfterImpact) + return true; + + var v = rb.linearVelocity; + float speed = v.magnitude; + if (speed < minBounceSpeed) return false; + + Vector2 avgNormal = Vector2.up; + if (collision.contactCount > 0) + { + avgNormal = Vector2.zero; + for (int i = 0; i < collision.contactCount; i++) + avgNormal += collision.GetContact(i).normal; + avgNormal.Normalize(); + } + + Vector2 reflected = Vector2.Reflect(v, avgNormal) * restitution; + + Vector2 n = avgNormal; + Vector2 vn = Vector2.Dot(reflected, n) * n; + Vector2 vt = reflected - vn; + reflected = vn + vt * tangentDamping; + + rb.linearVelocity = reflected; + rb.position += n * 0.005f; + + bounceCounter++; + + if (bounceCounter >= maxBounces) + return false; + + CurrentState = State.Airborne; + return true; + } + + private void BecomeGrounded(Collision2D collision) + { + launched = false; + CurrentState = State.Grounded; + + if (lockWhenLanded) + { + rb.linearVelocity = Vector2.zero; + rb.angularVelocity = 0f; + rb.bodyType = RigidbodyType2D.Kinematic; + } + else + { + rb.bodyType = RigidbodyType2D.Dynamic; + } + + col.enabled = true; + transform.SetParent(null, true); + bounceCounter = 0; + } + + private Transform FindAttachPointOn(GameObject who) + { + if (string.IsNullOrWhiteSpace(attachPointChildName)) return null; + return who.transform.Find(attachPointChildName); + } + + protected override void FixedUpdate() + { + base.FixedUpdate(); + + if (!lockWhenLanded && CurrentState == State.Airborne) + { + if (rb.linearVelocity.sqrMagnitude < (sleepVelocity * sleepVelocity)) + BecomeGrounded(null); + } + + if (hadBeenHeld && CurrentState != State.Held) + HandleRecallToPlayer(); + } + + private void HandleRecallToPlayer() + { + GameObject playerGo = lastKnownHolder; + + if (playerGo == null || !playerGo.activeInHierarchy) + playerGo = GameObject.FindWithTag("Player"); + + if (!playerGo) return; + + var hand = FindAttachPointOn(playerGo) ?? playerGo.transform; + + Vector2 toHand = (Vector2)hand.position - rb.position; + float dist = toHand.magnitude; + + if (dist > recallStartDistance) + { + if (CurrentState != State.Returning) + { + CurrentState = State.Returning; + rb.bodyType = RigidbodyType2D.Dynamic; + col.enabled = true; + } + + Vector2 desired = toHand.normalized * recallMaxSpeed; + rb.linearVelocity = Vector2.MoveTowards( + rb.linearVelocity, desired, recallAcceleration * Time.fixedDeltaTime); + + if (rb.linearVelocity.sqrMagnitude > 0.01f) + transform.right = rb.linearVelocity.normalized; + + if (dist <= recallArriveDistance) + TryPickup(playerGo, hand); + } + else + { + if (CurrentState == State.Returning) + CurrentState = rb.bodyType == RigidbodyType2D.Dynamic ? State.Airborne : State.Grounded; + } + } + +#if UNITY_EDITOR + private void OnDrawGizmosSelected() + { + if (!drawGizmos) return; + Gizmos.color = gizmoColor; + Gizmos.matrix = transform.localToWorldMatrix; + Gizmos.DrawCube(new Vector3(heldLocalOffset.x, heldLocalOffset.y, 0), new Vector3(0.12f, 0.12f, 0.02f)); + } +#endif +} diff --git a/Assets/Resources/Scripts/Temp.cs.meta b/Assets/Resources/Scripts/Temp.cs.meta new file mode 100644 index 0000000..86cead7 --- /dev/null +++ b/Assets/Resources/Scripts/Temp.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cfcb58e623aec0347a14faffcc5b438c \ No newline at end of file diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 57c4495..16ed536 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -141,7 +141,8 @@ PlayerSettings: visionOSBundleVersion: 1.0 tvOSBundleVersion: 1.0 bundleVersion: 1.0 - preloadedAssets: [] + preloadedAssets: + - {fileID: -944628639613478452, guid: 2bcd2660ca9b64942af0de543d8d7100, type: 3} metroInputSource: 0 wsaTransparentSwapchain: 0 m_HolographicPauseOnTrackingLoss: 1