commit 1c6c9b2a7094b6b8323b4131503143dc7efcc738 Author: ifontosh Date: Tue Sep 8 17:09:27 2026 +0800 Initial Unity 6 scaffold for Cat Kirby Adventures. Add Phase A greybox vertical slice: third-person movement, forest path, checkpoints, story triggers, and light ability gate. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..553f38f --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# Unity generated +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Uu]ser[Ss]ettings/ +/[Mm]emoryCaptures/ +/[Rr]ecordings/ + +# IDE / generated projects +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# OS +.DS_Store +Thumbs.db +Desktop.ini + +# JetBrains / VS / VS Code +.idea/ +.vs/ +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json + +# Crash / dump +sysinfo.txt +crashlytics-build.properties +mono_crash.* + +# Addressables / Burst +/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* +/[Aa]sset[Ss]tore[Tt]ools* + +# Builds / packages +ExportedObj/ +.consulo/ +*.apk +*.aab +*.unitypackage +*.app + +# Plastic / Collab +*.private +*.private.meta + +# Rider +Plugins/Editor/JetBrains* + +# Gradle +.gradle/ + +# Scaffold helper (local) +_scaffold_write.py +_scaffold_*.py diff --git a/Assets/Scenes/Bootstrap.unity b/Assets/Scenes/Bootstrap.unity new file mode 100644 index 0000000..1ee8337 --- /dev/null +++ b/Assets/Scenes/Bootstrap.unity @@ -0,0 +1,342 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &519420028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 519420032} + - component: {fileID: 519420031} + - component: {fileID: 519420029} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &519420029 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 +--- !u!20 &519420031 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &519420032 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &705507993 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 705507995} + - component: {fileID: 705507994} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &705507994 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 1 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &705507995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 705507993} + serializedVersion: 2 + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &900000001 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 900000002} + m_Layer: 0 + m_Name: Bootstrap + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &900000002 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 900000001} + 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: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Scenes/Bootstrap.unity.meta b/Assets/Scenes/Bootstrap.unity.meta new file mode 100644 index 0000000..5e9b8a7 --- /dev/null +++ b/Assets/Scenes/Bootstrap.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a1b2c3d4e5f60718293a4b5c6d7e8f90 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/.gitkeep b/Assets/Settings/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Assets/_Project/Input/.gitkeep b/Assets/_Project/Input/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Assets/_Project/Prefabs/.gitkeep b/Assets/_Project/Prefabs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Assets/_Project/Scripts.meta b/Assets/_Project/Scripts.meta new file mode 100644 index 0000000..a88dfed --- /dev/null +++ b/Assets/_Project/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b1c2d3e4f5061728394a5b6c7d8e9f01 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Project/Scripts/Abilities/LightAbility.cs b/Assets/_Project/Scripts/Abilities/LightAbility.cs new file mode 100644 index 0000000..fae0982 --- /dev/null +++ b/Assets/_Project/Scripts/Abilities/LightAbility.cs @@ -0,0 +1,73 @@ +using UnityEngine; +using UnityEngine.InputSystem; + +namespace KirbyAdventures +{ + public class LightAbility : MonoBehaviour + { + public float Range = 18f; + public float Cooldown = 0.75f; + public Transform Origin; + public LayerMask HitMask = ~0; + + float _nextReadyTime; + LineRenderer _line; + float _flashUntil; + + void Awake() + { + _line = gameObject.AddComponent(); + _line.positionCount = 2; + _line.startWidth = 0.08f; + _line.endWidth = 0.02f; + _line.material = new Material(Shader.Find("Sprites/Default")); + _line.startColor = new Color(1f, 0.95f, 0.6f, 1f); + _line.endColor = new Color(1f, 0.85f, 0.2f, 0.2f); + _line.enabled = false; + } + + void Update() + { + if (GameFlow.Instance != null && (GameFlow.Instance.IsPaused || GameFlow.Instance.HasWon)) + return; + + if (_line.enabled && Time.time > _flashUntil) + _line.enabled = false; + + bool unlocked = GameFlow.Instance == null || GameFlow.Instance.LightAbilityUnlocked; + if (!unlocked) return; + if (Time.time < _nextReadyTime) return; + + var mouse = Mouse.current; + var kb = Keyboard.current; + bool fire = (mouse != null && mouse.leftButton.wasPressedThisFrame) || + (kb != null && kb.eKey.wasPressedThisFrame); + if (fire) + Fire(); + } + + void Fire() + { + _nextReadyTime = Time.time + Cooldown; + + Transform origin = Origin != null ? Origin : transform; + Vector3 start = origin.position + Vector3.up * 1.0f; + Vector3 dir = origin.forward; + if (Camera.main != null) + dir = Camera.main.transform.forward; + + Vector3 end = start + dir * Range; + if (Physics.Raycast(start, dir, out RaycastHit hit, Range, HitMask, QueryTriggerInteraction.Collide)) + { + end = hit.point; + var reactive = hit.collider.GetComponentInParent(); + reactive?.OnHitByLight(hit.point, dir); + } + + _line.SetPosition(0, start); + _line.SetPosition(1, end); + _line.enabled = true; + _flashUntil = Time.time + 0.12f; + } + } +} diff --git a/Assets/_Project/Scripts/Core/GameBootstrap.cs b/Assets/_Project/Scripts/Core/GameBootstrap.cs new file mode 100644 index 0000000..a921db2 --- /dev/null +++ b/Assets/_Project/Scripts/Core/GameBootstrap.cs @@ -0,0 +1,107 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + /// + /// Ensures a single bootstrap exists and builds the Phase A greybox level. + /// + public class GameBootstrap : MonoBehaviour + { + static bool _initialized; + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + static void AutoBootstrap() + { + if (_initialized) return; + if (FindFirstObjectByType() != null) + { + _initialized = true; + return; + } + + var go = new GameObject("GameBootstrap"); + go.AddComponent(); + _initialized = true; + } + + void Awake() + { + DontDestroyOnLoad(gameObject); + + if (GameFlow.Instance == null) + { + var flowGo = new GameObject("GameFlow"); + flowGo.AddComponent(); + DontDestroyOnLoad(flowGo); + } + + if (FindFirstObjectByType() == null) + { + var cp = new GameObject("CheckpointSystem"); + cp.AddComponent(); + DontDestroyOnLoad(cp); + } + + BuildUi(); + Level01ForestBuilder.Build(); + } + + void BuildUi() + { + var dialogue = DialogueUI.EnsureExists(); + var pause = CreateOverlay("PauseOverlay", "ПАУЗА\nEsc — продолжить", new Color(0f, 0f, 0f, 0.65f)); + pause.SetActive(false); + var win = CreateOverlay("WinOverlay", "Ворота открыты!\nДеревня ждёт Кирби.\n\nПобеда среза Phase A", new Color(0.05f, 0.2f, 0.1f, 0.8f)); + win.SetActive(false); + + if (GameFlow.Instance != null) + { + GameFlow.Instance.SetPauseOverlay(pause); + GameFlow.Instance.SetWinOverlay(win); + } + + // silence unused + _ = dialogue; + } + + static GameObject CreateOverlay(string name, string text, Color bg) + { + var canvasGo = new GameObject(name); + var canvas = canvasGo.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.sortingOrder = 100; + canvasGo.AddComponent().uiScaleMode = + UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize; + canvasGo.AddComponent(); + DontDestroyOnLoad(canvasGo); + + var panel = new GameObject("Panel"); + panel.transform.SetParent(canvasGo.transform, false); + var img = panel.AddComponent(); + img.color = bg; + var rt = panel.GetComponent(); + rt.anchorMin = Vector2.zero; + rt.anchorMax = Vector2.one; + rt.offsetMin = Vector2.zero; + rt.offsetMax = Vector2.zero; + + var textGo = new GameObject("Text"); + textGo.transform.SetParent(panel.transform, false); + var uiText = textGo.AddComponent(); + uiText.text = text; + uiText.alignment = TextAnchor.MiddleCenter; + uiText.fontSize = 36; + uiText.color = Color.white; + uiText.font = Resources.GetBuiltinResource("LegacyRuntime.ttf"); + if (uiText.font == null) + uiText.font = Resources.GetBuiltinResource("Arial.ttf"); + var trt = textGo.GetComponent(); + trt.anchorMin = new Vector2(0.1f, 0.2f); + trt.anchorMax = new Vector2(0.9f, 0.8f); + trt.offsetMin = Vector2.zero; + trt.offsetMax = Vector2.zero; + + return canvasGo; + } + } +} diff --git a/Assets/_Project/Scripts/Core/GameFlow.cs b/Assets/_Project/Scripts/Core/GameFlow.cs new file mode 100644 index 0000000..c771063 --- /dev/null +++ b/Assets/_Project/Scripts/Core/GameFlow.cs @@ -0,0 +1,86 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + /// + /// Global game state: pause, win, light ability unlock. + /// + public class GameFlow : MonoBehaviour + { + public static GameFlow Instance { get; private set; } + + public bool IsPaused { get; private set; } + public bool HasWon { get; private set; } + public bool LightAbilityUnlocked { get; private set; } + + GameObject _pauseOverlay; + GameObject _winOverlay; + + void Awake() + { + if (Instance != null && Instance != this) + { + Destroy(gameObject); + return; + } + + Instance = this; + DontDestroyOnLoad(gameObject); + } + + public void SetPauseOverlay(GameObject overlay) => _pauseOverlay = overlay; + public void SetWinOverlay(GameObject overlay) => _winOverlay = overlay; + + public void UnlockLightAbility() + { + LightAbilityUnlocked = true; + Debug.Log("[GameFlow] Light ability unlocked."); + } + + public void TogglePause() + { + if (HasWon) return; + SetPaused(!IsPaused); + } + + public void SetPaused(bool paused) + { + IsPaused = paused; + Time.timeScale = paused ? 0f : 1f; + if (_pauseOverlay != null) + _pauseOverlay.SetActive(paused); + + Cursor.lockState = paused ? CursorLockMode.None : CursorLockMode.Locked; + Cursor.visible = paused; + } + + public void TriggerWin() + { + if (HasWon) return; + HasWon = true; + IsPaused = true; + Time.timeScale = 0f; + if (_winOverlay != null) + _winOverlay.SetActive(true); + Cursor.lockState = CursorLockMode.None; + Cursor.visible = true; + Debug.Log("[GameFlow] Level complete — village gate opened."); + } + + void Update() + { + var kb = UnityEngine.InputSystem.Keyboard.current; + if (kb != null && kb.escapeKey.wasPressedThisFrame) + TogglePause(); + } + + void OnDestroy() + { + if (Instance == this) + { + Time.timeScale = 1f; + Instance = null; + } + } + } +} diff --git a/Assets/_Project/Scripts/Level/AbilityUnlockPedestal.cs b/Assets/_Project/Scripts/Level/AbilityUnlockPedestal.cs new file mode 100644 index 0000000..d4576ba --- /dev/null +++ b/Assets/_Project/Scripts/Level/AbilityUnlockPedestal.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + [RequireComponent(typeof(Collider))] + public class AbilityUnlockPedestal : MonoBehaviour + { + public string StoryLine = + "Внутри вспыхнул тихий свет — не снаружи, а из самого сердца Кирби."; + bool _used; + + void OnTriggerEnter(Collider other) + { + if (_used) return; + if (other.GetComponent() == null) return; + _used = true; + if (GameFlow.Instance != null) + GameFlow.Instance.UnlockLightAbility(); + DialogueUI.ShowTemporary(StoryLine, 5f); + + var rend = GetComponentInChildren(); + if (rend != null) + rend.material.color = new Color(1f, 0.92f, 0.35f); + } + } +} diff --git a/Assets/_Project/Scripts/Level/Checkpoint.cs b/Assets/_Project/Scripts/Level/Checkpoint.cs new file mode 100644 index 0000000..0d0bb65 --- /dev/null +++ b/Assets/_Project/Scripts/Level/Checkpoint.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + [RequireComponent(typeof(Collider))] + public class Checkpoint : MonoBehaviour + { + public Transform SpawnPoint; + public string CheckpointId = "cp"; + + void Reset() + { + var c = GetComponent(); + if (c != null) c.isTrigger = true; + } + + void OnTriggerEnter(Collider other) + { + if (!other.CompareTag("Player") && other.GetComponent() == null) + return; + + var point = SpawnPoint != null ? SpawnPoint : transform; + CheckpointSystem.SetCheckpoint(point.position, point.rotation, CheckpointId); + } + } +} diff --git a/Assets/_Project/Scripts/Level/CheckpointSystem.cs b/Assets/_Project/Scripts/Level/CheckpointSystem.cs new file mode 100644 index 0000000..b0be850 --- /dev/null +++ b/Assets/_Project/Scripts/Level/CheckpointSystem.cs @@ -0,0 +1,54 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + public class CheckpointSystem : MonoBehaviour + { + public static CheckpointSystem Instance { get; private set; } + + public Vector3 SpawnPosition { get; private set; } + public Quaternion SpawnRotation { get; private set; } = Quaternion.identity; + public string CurrentId { get; private set; } = "start"; + + void Awake() + { + if (Instance != null && Instance != this) + { + Destroy(gameObject); + return; + } + Instance = this; + } + + public static void SetCheckpoint(Vector3 pos, Quaternion rot, string id) + { + if (Instance == null) return; + Instance.SpawnPosition = pos; + Instance.SpawnRotation = rot; + Instance.CurrentId = id; + Debug.Log($"[Checkpoint] Saved: {id}"); + } + + public static void Respawn(Transform player, CharacterController cc = null) + { + if (Instance == null || player == null) return; + if (cc != null) cc.enabled = false; + player.SetPositionAndRotation(Instance.SpawnPosition, Instance.SpawnRotation); + if (cc != null) cc.enabled = true; + Physics.SyncTransforms(); + } + + public static void EnsureStart(Vector3 pos, Quaternion rot) + { + if (Instance == null) return; + if (Instance.CurrentId == "start" && Instance.SpawnPosition == Vector3.zero && pos != Vector3.zero + || Instance.CurrentId == "start") + { + // Always seed start if still default id + } + Instance.SpawnPosition = pos; + Instance.SpawnRotation = rot; + Instance.CurrentId = "start"; + } + } +} diff --git a/Assets/_Project/Scripts/Level/FallKillPlane.cs b/Assets/_Project/Scripts/Level/FallKillPlane.cs new file mode 100644 index 0000000..bcdd1ce --- /dev/null +++ b/Assets/_Project/Scripts/Level/FallKillPlane.cs @@ -0,0 +1,24 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + public class FallKillPlane : MonoBehaviour + { + public float KillY = -10f; + Transform _player; + CharacterController _cc; + + public void Bind(Transform player) + { + _player = player; + _cc = player != null ? player.GetComponent() : null; + } + + void Update() + { + if (_player == null) return; + if (_player.position.y < KillY) + CheckpointSystem.Respawn(_player, _cc); + } + } +} diff --git a/Assets/_Project/Scripts/Level/ILightReactive.cs b/Assets/_Project/Scripts/Level/ILightReactive.cs new file mode 100644 index 0000000..fa5ca22 --- /dev/null +++ b/Assets/_Project/Scripts/Level/ILightReactive.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + public interface ILightReactive + { + void OnHitByLight(Vector3 hitPoint, Vector3 direction); + } +} diff --git a/Assets/_Project/Scripts/Level/Level01ForestBuilder.cs b/Assets/_Project/Scripts/Level/Level01ForestBuilder.cs new file mode 100644 index 0000000..71fa641 --- /dev/null +++ b/Assets/_Project/Scripts/Level/Level01ForestBuilder.cs @@ -0,0 +1,294 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + /// + /// Procedural greybox for Phase A — Level_01_Forest. + /// + public static class Level01ForestBuilder + { + static Transform _root; + + public static void Build() + { + if (GameObject.Find("Level_01_Forest") != null) + return; + + _root = new GameObject("Level_01_Forest").transform; + + EnsureLighting(); + BuildGroundAndPath(); + BuildJumpPlatforms(); + var startPos = new Vector3(0f, 1.1f, 0f); + BuildCheckpoints(); + BuildStoryTriggers(); + BuildAbilityPedestal(); + BuildVillageGate(); + var player = BuildPlayer(startPos); + BuildCamera(player); + BuildKillPlane(player.transform); + + CheckpointSystem.EnsureStart(startPos, Quaternion.identity); + CheckpointSystem.SetCheckpoint(startPos, Quaternion.identity, "start"); + + DialogueUI.ShowTemporary("Кирби очнулся среди деревьев. Тропа ведёт к деревне — и к себе.", 4f); + } + + static void EnsureLighting() + { + // Keep scene light; add one if missing + if (Object.FindFirstObjectByType() == null) + { + var lightGo = new GameObject("Directional Light"); + lightGo.transform.SetParent(_root); + var light = lightGo.AddComponent(); + light.type = LightType.Directional; + light.color = new Color(1f, 0.95f, 0.85f); + light.intensity = 1.1f; + lightGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f); + } + + RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Trilight; + RenderSettings.ambientSkyColor = new Color(0.45f, 0.55f, 0.65f); + RenderSettings.ambientEquatorColor = new Color(0.35f, 0.4f, 0.3f); + RenderSettings.ambientGroundColor = new Color(0.15f, 0.12f, 0.1f); + } + + static void BuildGroundAndPath() + { + // Start platform + CreateBox("StartPlatform", new Vector3(0f, 0f, 0f), new Vector3(10f, 1f, 10f), + new Color(0.35f, 0.45f, 0.28f)); + + // Forest path stretches + CreateBox("Path_A", new Vector3(0f, 0f, 14f), new Vector3(6f, 1f, 16f), + new Color(0.32f, 0.42f, 0.26f)); + CreateBox("Path_B", new Vector3(0f, 0f, 32f), new Vector3(5f, 1f, 12f), + new Color(0.3f, 0.4f, 0.24f)); + + // Side tree markers (cylinders) + for (int i = 0; i < 8; i++) + { + float z = 4f + i * 5f; + float x = (i % 2 == 0) ? -5.5f : 5.5f; + var tree = CreatePrimitive(PrimitiveType.Cylinder, $"Tree_{i}", + new Vector3(x, 2f, z), new Vector3(0.6f, 2f, 0.6f), + new Color(0.2f, 0.28f, 0.15f)); + tree.transform.SetParent(_root); + } + } + + static void BuildJumpPlatforms() + { + // Gap then platforms + CreateBox("Pad_1", new Vector3(0f, 0.5f, 42f), new Vector3(3.5f, 1f, 3.5f), + new Color(0.45f, 0.38f, 0.28f)); + CreateBox("Pad_2", new Vector3(3f, 1.2f, 48f), new Vector3(3f, 1f, 3f), + new Color(0.48f, 0.4f, 0.3f)); + CreateBox("Pad_3", new Vector3(-2f, 2f, 54f), new Vector3(3f, 1f, 3f), + new Color(0.5f, 0.42f, 0.32f)); + CreateBox("Pad_4", new Vector3(0f, 2.5f, 61f), new Vector3(4f, 1f, 4f), + new Color(0.4f, 0.45f, 0.3f)); + + // Landing after mid section + CreateBox("Path_C", new Vector3(0f, 0f, 72f), new Vector3(8f, 1f, 14f), + new Color(0.33f, 0.42f, 0.27f)); + CreateBox("Path_D", new Vector3(0f, 0f, 90f), new Vector3(10f, 1f, 16f), + new Color(0.36f, 0.44f, 0.3f)); + CreateBox("VillageApproach", new Vector3(0f, 0f, 108f), new Vector3(12f, 1f, 14f), + new Color(0.4f, 0.4f, 0.35f)); + } + + static void BuildCheckpoints() + { + CreateCheckpoint("CP_Start", new Vector3(0f, 1.5f, 2f), new Vector3(4f, 2f, 2f), "cp_start"); + CreateCheckpoint("CP_Mid", new Vector3(0f, 3.5f, 61f), new Vector3(3f, 2f, 3f), "cp_mid"); + CreateCheckpoint("CP_NearGate", new Vector3(0f, 1.5f, 100f), new Vector3(5f, 2f, 3f), "cp_gate"); + } + + static void CreateCheckpoint(string name, Vector3 pos, Vector3 size, string id) + { + var go = new GameObject(name); + go.transform.SetParent(_root); + go.transform.position = pos; + var box = go.AddComponent(); + box.isTrigger = true; + box.size = size; + var cp = go.AddComponent(); + cp.CheckpointId = id; + + var marker = CreatePrimitive(PrimitiveType.Cube, name + "_Marker", + pos + Vector3.up * 0.5f, new Vector3(0.4f, 1.2f, 0.4f), + new Color(0.2f, 0.7f, 0.9f, 0.7f)); + marker.transform.SetParent(go.transform); + Object.Destroy(marker.GetComponent()); + cp.SpawnPoint = go.transform; + } + + static void BuildStoryTriggers() + { + CreateStory("Story_Fall", new Vector3(0f, 2f, 8f), new Vector3(6f, 3f, 3f), + "«Падение»\nЗемля приняла его мягко, будто лес давно ждал чёрного кота.", 5f); + CreateStory("Story_Road", new Vector3(0f, 2f, 28f), new Vector3(5f, 3f, 3f), + "«Путь в деревню»\nТропа узкая, но шаг за шагом — и чужой шум становится тише.", 5f); + CreateStory("Story_Source", new Vector3(0f, 3.5f, 74f), new Vector3(6f, 3f, 3f), + "«Внутренний источник»\nСвет не нужно искать в небе — он уже греет изнутри.", 5f); + } + + static void CreateStory(string name, Vector3 pos, Vector3 size, string quote, float secs) + { + var go = new GameObject(name); + go.transform.SetParent(_root); + go.transform.position = pos; + var box = go.AddComponent(); + box.isTrigger = true; + box.size = size; + var st = go.AddComponent(); + st.Quote = quote; + st.DisplaySeconds = secs; + } + + static void BuildAbilityPedestal() + { + var pedestal = CreatePrimitive(PrimitiveType.Cylinder, "LightPedestal", + new Vector3(0f, 1.2f, 78f), new Vector3(1.4f, 0.6f, 1.4f), + new Color(0.55f, 0.5f, 0.75f)); + pedestal.transform.SetParent(_root); + + var orb = CreatePrimitive(PrimitiveType.Sphere, "LightOrb", + new Vector3(0f, 2.4f, 78f), Vector3.one * 0.7f, + new Color(1f, 0.95f, 0.55f)); + orb.transform.SetParent(pedestal.transform); + Object.Destroy(orb.GetComponent()); + + var trigger = new GameObject("AbilityUnlock"); + trigger.transform.SetParent(_root); + trigger.transform.position = new Vector3(0f, 1.5f, 78f); + var col = trigger.AddComponent(); + col.isTrigger = true; + col.size = new Vector3(3.5f, 3f, 3.5f); + trigger.AddComponent(); + } + + static void BuildVillageGate() + { + var posts = CreateBox("GatePosts", new Vector3(0f, 3f, 116f), new Vector3(8f, 6f, 1.2f), + new Color(0.35f, 0.28f, 0.22f)); + // Hollow-ish: leave a door slab that moves + var door = CreateBox("GateDoor", new Vector3(0f, 2.5f, 116.2f), new Vector3(3.2f, 4.5f, 0.6f), + new Color(0.15f, 0.12f, 0.2f)); + var gate = door.AddComponent(); + gate.GateVisual = door.transform; + + // Invisible hit proxy in front + var hit = new GameObject("GateLightTarget"); + hit.transform.SetParent(_root); + hit.transform.position = new Vector3(0f, 2.5f, 115.5f); + var hc = hit.AddComponent(); + hc.size = new Vector3(3.5f, 4.5f, 1.5f); + // forward hits to LightGate via child relationship + hit.transform.SetParent(door.transform); + // LightGate is on door; GetComponentInParent will find it + } + + static GameObject BuildPlayer(Vector3 pos) + { + // Black capsule named Kirby + var body = CreatePrimitive(PrimitiveType.Capsule, "Kirby", pos, new Vector3(0.9f, 1f, 0.9f), + new Color(0.05f, 0.05f, 0.08f)); + body.tag = "Player"; + Object.Destroy(body.GetComponent()); + + var cc = body.AddComponent(); + cc.height = 2f; + cc.radius = 0.45f; + cc.center = new Vector3(0f, 0f, 0f); + cc.skinWidth = 0.08f; + + var yaw = new GameObject("YawPivot"); + yaw.transform.SetParent(body.transform); + yaw.transform.localPosition = Vector3.zero; + + var motor = body.AddComponent(); + motor.YawPivot = yaw.transform; + + body.AddComponent(); + + return body; + } + + static void BuildCamera(GameObject player) + { + Camera cam = Camera.main; + if (cam == null) + { + var camGo = new GameObject("Main Camera"); + cam = camGo.AddComponent(); + cam.tag = "MainCamera"; + camGo.AddComponent(); + } + + var follow = cam.GetComponent(); + if (follow == null) + follow = cam.gameObject.AddComponent(); + + var motor = player.GetComponent(); + Transform yaw = player.transform.Find("YawPivot"); + follow.Target = player.transform; + follow.YawPivot = yaw; + motor.CameraTransform = cam.transform; + motor.YawPivot = yaw; + + cam.transform.position = player.transform.position + new Vector3(0f, 2.5f, -5.5f); + cam.transform.LookAt(player.transform.position + Vector3.up * 1.2f); + } + + static void BuildKillPlane(Transform player) + { + var go = new GameObject("FallKillPlane"); + go.transform.SetParent(_root); + var kill = go.AddComponent(); + kill.KillY = -10f; + kill.Bind(player); + } + + static GameObject CreateBox(string name, Vector3 pos, Vector3 scale, Color color) + { + var go = CreatePrimitive(PrimitiveType.Cube, name, pos, scale, color); + go.transform.SetParent(_root); + return go; + } + + static GameObject CreatePrimitive(PrimitiveType type, string name, Vector3 pos, Vector3 scale, Color color) + { + var go = GameObject.CreatePrimitive(type); + go.name = name; + go.transform.position = pos; + go.transform.localScale = scale; + var rend = go.GetComponent(); + if (rend != null) + { + // URP Lit may exist after package import; fallback to Standard / Unlit + var shader = Shader.Find("Universal Render Pipeline/Lit"); + if (shader == null) shader = Shader.Find("Standard"); + if (shader == null) shader = Shader.Find("Sprites/Default"); + if (shader != null) + { + var mat = new Material(shader); + if (mat.HasProperty("_BaseColor")) + mat.SetColor("_BaseColor", color); + else if (mat.HasProperty("_Color")) + mat.SetColor("_Color", color); + else + mat.color = color; + rend.material = mat; + } + else + { + rend.material.color = color; + } + } + return go; + } + } +} diff --git a/Assets/_Project/Scripts/Level/LightGate.cs b/Assets/_Project/Scripts/Level/LightGate.cs new file mode 100644 index 0000000..f346893 --- /dev/null +++ b/Assets/_Project/Scripts/Level/LightGate.cs @@ -0,0 +1,50 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + /// + /// Village gate that opens when hit by Light after the ability is unlocked. + /// + public class LightGate : MonoBehaviour, ILightReactive + { + public bool IsOpen { get; private set; } + public Transform GateVisual; + public float OpenHeight = 4f; + public float OpenSpeed = 2.5f; + + Vector3 _closedPos; + Vector3 _openPos; + bool _opening; + + void Awake() + { + if (GateVisual == null) + GateVisual = transform; + _closedPos = GateVisual.localPosition; + _openPos = _closedPos + Vector3.up * OpenHeight; + } + + public void OnHitByLight(Vector3 hitPoint, Vector3 direction) + { + if (IsOpen) return; + if (GameFlow.Instance != null && !GameFlow.Instance.LightAbilityUnlocked) + { + DialogueUI.ShowTemporary("Свет ещё спит внутри... Найди источник на тропе.", 3f); + return; + } + + IsOpen = true; + _opening = true; + DialogueUI.ShowTemporary("Свет коснулся ворот — путь в деревню открыт.", 3.5f); + if (GameFlow.Instance != null) + GameFlow.Instance.TriggerWin(); + } + + void Update() + { + if (!_opening || GateVisual == null) return; + GateVisual.localPosition = Vector3.MoveTowards( + GateVisual.localPosition, _openPos, OpenSpeed * Time.unscaledDeltaTime); + } + } +} diff --git a/Assets/_Project/Scripts/Player/PlayerMotor.cs b/Assets/_Project/Scripts/Player/PlayerMotor.cs new file mode 100644 index 0000000..ae26c1e --- /dev/null +++ b/Assets/_Project/Scripts/Player/PlayerMotor.cs @@ -0,0 +1,93 @@ +using UnityEngine; +using UnityEngine.InputSystem; + +namespace KirbyAdventures +{ + [RequireComponent(typeof(CharacterController))] + public class PlayerMotor : MonoBehaviour + { + public float MoveSpeed = 6f; + public float JumpHeight = 1.4f; + public float Gravity = -22f; + public float GroundStick = -2f; + public Transform CameraTransform; + public Transform YawPivot; + + CharacterController _cc; + Vector3 _velocity; + bool _grounded; + + void Awake() + { + _cc = GetComponent(); + } + + void Update() + { + if (GameFlow.Instance != null && (GameFlow.Instance.IsPaused || GameFlow.Instance.HasWon)) + return; + + HandleRespawnInput(); + ReadMoveAndJump(); + } + + void HandleRespawnInput() + { + var kb = Keyboard.current; + if (kb != null && kb.rKey.wasPressedThisFrame) + CheckpointSystem.Respawn(transform, _cc); + } + + void ReadMoveAndJump() + { + var kb = Keyboard.current; + float h = 0f, v = 0f; + if (kb != null) + { + if (kb.aKey.isPressed || kb.leftArrowKey.isPressed) h -= 1f; + if (kb.dKey.isPressed || kb.rightArrowKey.isPressed) h += 1f; + if (kb.sKey.isPressed || kb.downArrowKey.isPressed) v -= 1f; + if (kb.wKey.isPressed || kb.upArrowKey.isPressed) v += 1f; + } + + _grounded = _cc.isGrounded; + if (_grounded && _velocity.y < 0f) + _velocity.y = GroundStick; + + Vector3 forward = Vector3.forward; + Vector3 right = Vector3.right; + if (YawPivot != null) + { + forward = YawPivot.forward; + right = YawPivot.right; + } + else if (CameraTransform != null) + { + forward = CameraTransform.forward; + right = CameraTransform.right; + } + + forward.y = 0f; + right.y = 0f; + forward.Normalize(); + right.Normalize(); + + Vector3 move = (forward * v + right * h); + if (move.sqrMagnitude > 1f) move.Normalize(); + + _cc.Move(move * MoveSpeed * Time.deltaTime); + + if (kb != null && kb.spaceKey.wasPressedThisFrame && _grounded) + _velocity.y = Mathf.Sqrt(JumpHeight * -2f * Gravity); + + _velocity.y += Gravity * Time.deltaTime; + _cc.Move(_velocity * Time.deltaTime); + + if (move.sqrMagnitude > 0.01f) + { + var look = Quaternion.LookRotation(move, Vector3.up); + transform.rotation = Quaternion.Slerp(transform.rotation, look, 12f * Time.deltaTime); + } + } + } +} diff --git a/Assets/_Project/Scripts/Player/SimpleCameraFollow.cs b/Assets/_Project/Scripts/Player/SimpleCameraFollow.cs new file mode 100644 index 0000000..a0b2b52 --- /dev/null +++ b/Assets/_Project/Scripts/Player/SimpleCameraFollow.cs @@ -0,0 +1,58 @@ +using UnityEngine; +using UnityEngine.InputSystem; + +namespace KirbyAdventures +{ + /// + /// Third-person follow camera with mouse look (avoids Cinemachine 3 API coupling). + /// + public class SimpleCameraFollow : MonoBehaviour + { + public Transform Target; + public Transform YawPivot; + public float Distance = 5.5f; + public float Height = 1.6f; + public float MouseSensitivity = 0.12f; + public float MinPitch = -30f; + public float MaxPitch = 55f; + public float Smooth = 12f; + + float _yaw; + float _pitch = 12f; + + public float Yaw => _yaw; + public float Pitch => _pitch; + + void Start() + { + Cursor.lockState = CursorLockMode.Locked; + Cursor.visible = false; + if (YawPivot != null) + _yaw = YawPivot.eulerAngles.y; + } + + void LateUpdate() + { + if (Target == null) return; + if (GameFlow.Instance != null && (GameFlow.Instance.IsPaused || GameFlow.Instance.HasWon)) + return; + + var mouse = Mouse.current; + if (mouse != null) + { + var delta = mouse.delta.ReadValue(); + _yaw += delta.x * MouseSensitivity; + _pitch -= delta.y * MouseSensitivity; + _pitch = Mathf.Clamp(_pitch, MinPitch, MaxPitch); + } + + if (YawPivot != null) + YawPivot.rotation = Quaternion.Euler(0f, _yaw, 0f); + + var rot = Quaternion.Euler(_pitch, _yaw, 0f); + var desired = Target.position + Vector3.up * Height + rot * (Vector3.back * Distance); + transform.position = Vector3.Lerp(transform.position, desired, 1f - Mathf.Exp(-Smooth * Time.deltaTime)); + transform.rotation = Quaternion.Slerp(transform.rotation, rot, 1f - Mathf.Exp(-Smooth * Time.deltaTime)); + } + } +} diff --git a/Assets/_Project/Scripts/Story/StoryTrigger.cs b/Assets/_Project/Scripts/Story/StoryTrigger.cs new file mode 100644 index 0000000..d53a110 --- /dev/null +++ b/Assets/_Project/Scripts/Story/StoryTrigger.cs @@ -0,0 +1,27 @@ +using UnityEngine; + +namespace KirbyAdventures +{ + [RequireComponent(typeof(Collider))] + public class StoryTrigger : MonoBehaviour + { + [TextArea] public string Quote; + public float DisplaySeconds = 4.5f; + public bool Once = true; + bool _shown; + + void OnTriggerEnter(Collider other) + { + if (Once && _shown) return; + if (other.GetComponent() == null) return; + _shown = true; + DialogueUI.ShowTemporary(Quote, DisplaySeconds); + } + + void OnTriggerExit(Collider other) + { + if (other.GetComponent() == null) return; + // Optional: hide early when leaving — keep simple, let timer finish + } + } +} diff --git a/Assets/_Project/Scripts/UI/DialogueUI.cs b/Assets/_Project/Scripts/UI/DialogueUI.cs new file mode 100644 index 0000000..44cecd3 --- /dev/null +++ b/Assets/_Project/Scripts/UI/DialogueUI.cs @@ -0,0 +1,86 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace KirbyAdventures +{ + public class DialogueUI : MonoBehaviour + { + public static DialogueUI Instance { get; private set; } + + Text _text; + Image _panel; + float _hideAt; + + public static DialogueUI EnsureExists() + { + if (Instance != null) return Instance; + var go = new GameObject("DialogueUI"); + DontDestroyOnLoad(go); + return go.AddComponent(); + } + + void Awake() + { + Instance = this; + Build(); + } + + void Build() + { + var canvas = gameObject.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.sortingOrder = 50; + gameObject.AddComponent().uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + gameObject.AddComponent(); + + var panelGo = new GameObject("Panel"); + panelGo.transform.SetParent(transform, false); + _panel = panelGo.AddComponent(); + _panel.color = new Color(0.05f, 0.07f, 0.1f, 0.82f); + var prt = _panel.rectTransform; + prt.anchorMin = new Vector2(0.12f, 0.05f); + prt.anchorMax = new Vector2(0.88f, 0.22f); + prt.offsetMin = Vector2.zero; + prt.offsetMax = Vector2.zero; + + var textGo = new GameObject("Quote"); + textGo.transform.SetParent(panelGo.transform, false); + _text = textGo.AddComponent(); + _text.font = Resources.GetBuiltinResource("LegacyRuntime.ttf"); + if (_text.font == null) + _text.font = Resources.GetBuiltinResource("Arial.ttf"); + _text.fontSize = 26; + _text.alignment = TextAnchor.MiddleCenter; + _text.color = new Color(0.95f, 0.93f, 0.88f); + _text.horizontalOverflow = HorizontalWrapMode.Wrap; + _text.verticalOverflow = VerticalWrapMode.Overflow; + var trt = _text.rectTransform; + trt.anchorMin = new Vector2(0.04f, 0.1f); + trt.anchorMax = new Vector2(0.96f, 0.9f); + trt.offsetMin = Vector2.zero; + trt.offsetMax = Vector2.zero; + + _panel.gameObject.SetActive(false); + } + + public static void ShowTemporary(string quote, float seconds) + { + EnsureExists(); + Instance.Show(quote, seconds); + } + + public void Show(string quote, float seconds) + { + if (_text == null) return; + _text.text = quote; + _panel.gameObject.SetActive(true); + _hideAt = Time.unscaledTime + seconds; + } + + void Update() + { + if (_panel != null && _panel.gameObject.activeSelf && Time.unscaledTime >= _hideAt) + _panel.gameObject.SetActive(false); + } + } +} diff --git a/Docs/ChapterMap.md b/Docs/ChapterMap.md new file mode 100644 index 0000000..3157aa4 --- /dev/null +++ b/Docs/ChapterMap.md @@ -0,0 +1,31 @@ +# Карта глав → уровни + +Книга разбита на 13 глав. Ниже — план будущих уровней. +**Phase A (текущий прототип):** только **Level_01_Forest**. + +| # | Глава (книга) | Будущий уровень | Статус | +|---|----------------|-----------------|--------| +| 1 | Падение | Level_01_Forest (вступление / тропа) | Phase A | +| 2 | Путь в деревню | Level_01_Forest (продолжение тропы) | Phase A | +| 3 | Внутренний источник | Level_01_Forest (пьедестал Света) | Phase A | +| 4 | Ворота / порог деревни | Level_01_Forest (LightGate, финиш среза) | Phase A | +| 5 | (глава 5) | Level_02_Village | Планируется | +| 6 | (глава 6) | Level_02_Village / Level_03 | Планируется | +| 7 | (глава 7) | Level_03 | Планируется | +| 8 | (глава 8) | Level_04 | Планируется | +| 9 | (глава 9) | Level_05 | Планируется | +| 10 | (глава 10) | Level_06 | Планируется | +| 11 | (глава 11) | Level_07 | Планируется | +| 12 | (глава 12) | Level_08 | Планируется | +| 13 | (глава 13) | Level_09_Finale | Планируется | + +## Phase A — Level_01_Forest + +Runtime-сборка (`Level01ForestBuilder`): + +- стартовая площадка и лесная тропа из примитивов; +- платформы и разрывы; +- 2–3 чекпоинта + kill-plane; +- сюжетные объёмы с цитатами в духе глав «Падение», «Путь в деревню», «Внутренний источник»; +- пьедестал разблокировки Света; +- ворота деревни с `LightGate` → победа. diff --git a/Docs/GDD.md b/Docs/GDD.md new file mode 100644 index 0000000..e12c1d5 --- /dev/null +++ b/Docs/GDD.md @@ -0,0 +1,44 @@ +# GDD — Приключения кота Кирби + +## Название + +**Приключения кота Кирби** (репозиторий: Cat-Kirby-Adventures). + +## Фэнтези + +Игрок ведёт чёрного кота Кирби по миру, где внешние препятствия отражают внутренний путь героя. +Способность **Свет** — проявление внутреннего источника: игрок учится «видеть» и открывать то, что закрыто страхом и тьмой. +Тон: тёплый, сказочный, с уважением к тексту книги; геймплей — понятный 3D-платформинг от третьего лица. + +## Основной цикл (core loop) + +1. Исследовать короткий сегмент уровня (тропа / платформы). +2. Читать сюжетные реплики в триггерах (атмосфера глав). +3. Преодолевать прыжки и пропасти; сохраняться на чекпоинтах. +4. Получить способность **Свет** и применить её к реактивным объектам. +5. Открыть ворота / пройти к следующей зоне → победа на срезе. + +## Способность «Свет» + +- **Состояние:** заблокирована до пьедестала на середине Level 01. +- **Ввод:** ЛКМ или E. +- **Поведение:** короткий raycast вперёд, вспышка LineRenderer, вызов `ILightReactive.OnHitByLight`. +- **Кулдаун:** ~0.75 с. +- **Смысл в срезе:** открыть `LightGate` у деревни и завершить уровень. + +## Камера + +Третье лицо: следование за Кирби, yaw/pitch от мыши, ограничение pitch. +В прототипе — `SimpleCameraFollow` (без жёсткой зависимости от API Cinemachine 3). + +## Объём вертикального среза (Phase A) + +| Включено | Не в срезе | +|----------|------------| +| Level_01_Forest (runtime greybox) | Полные уровни глав 5–13 | +| Движение, прыжок, чекпоинты | Боевая система, инвентарь | +| Свет + одни ворота | Много врагов / квесты | +| 2–3 сюжетных оверлея | Полный диалоговый UI / озвучка | +| Пауза, win-state | Сохранения на диск, меню настроек | + +Цель среза: доказать фан «книга → платформер» и петлю «движение → история → Свет → ворота». diff --git a/Packages/manifest.json b/Packages/manifest.json new file mode 100644 index 0000000..d12bff2 --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,46 @@ +{ + "dependencies": { + "com.unity.cinemachine": "3.1.3", + "com.unity.collab-proxy": "2.6.0", + "com.unity.ide.rider": "3.0.31", + "com.unity.ide.visualstudio": "2.0.22", + "com.unity.inputsystem": "1.11.2", + "com.unity.multiplayer.center": "1.0.0", + "com.unity.render-pipelines.universal": "17.0.3", + "com.unity.test-framework": "1.4.5", + "com.unity.timeline": "1.8.7", + "com.unity.ugui": "2.0.0", + "com.unity.visualscripting": "1.9.5", + "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" + } +} diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json new file mode 100644 index 0000000..18a1e41 --- /dev/null +++ b/Packages/packages-lock.json @@ -0,0 +1,3 @@ +{ + "dependencies": {} +} diff --git a/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..27287fe --- /dev/null +++ b/ProjectSettings/AudioManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 0 diff --git a/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..75628d4 --- /dev/null +++ b/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,40 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 14 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_DefaultMaxDepenetrationVelocity: 10 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0.1 + m_ClothInterCollisionStiffness: 0.2 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_SimulationMode: 0 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_InvokeCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ClothGravity: {x: 0, y: -9.81, z: 0} + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_EnableUnifiedHeightmaps: 1 + m_ImprovedPatchFriction: 0 + m_SolverType: 0 + m_DefaultMaxAngularSpeed: 50 + m_ScratchBufferChunkCount: 4 diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..7b4aa64 --- /dev/null +++ b/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,12 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Scenes/Bootstrap.unity + guid: a1b2c3d4e5f60718293a4b5c6d7e8f90 + m_configObjects: {} + m_UseUCBPForLocalBuild: 0 diff --git a/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..ffa3e86 --- /dev/null +++ b/ProjectSettings/EditorSettings.asset @@ -0,0 +1,45 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 2 + m_DefaultBehaviorMode: 0 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 0 + m_SpritePackerCacheSize: 10 + m_SpritePackerPaddingPower: 1 + m_Bc7TextureCompressor: 0 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref + m_ProjectGenerationRootNamespace: KirbyAdventures + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_EnableEditorAsyncCPUTextureLoading: 0 + m_AsyncShaderCompilation: 1 + m_PrefabModeAllowAutoSave: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_GameObjectNamingDigits: 1 + m_GameObjectNamingScheme: 0 + m_AssetNamingUsesSpace: 1 + m_InspectorUseIMGUIDefaultInspector: 0 + m_UseLegacyProbeSampleCount: 0 + m_SerializeInlineMappingsOnOneLine: 1 + m_DisableCookiesInLightmapper: 0 + m_AssetPipelineMode: 1 + m_RefreshImportMode: 0 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 + m_CacheServerEnableAuth: 0 + m_CacheServerEnableTls: 0 + m_CacheServerValidationMode: 2 diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..238d23f --- /dev/null +++ b/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,59 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 16 + m_Deferred: + m_Mode: 1 + m_LightProbeReferenceFrame: 0 + m_DeferredReflections: + m_Mode: 1 + m_LightProbeReferenceFrame: 0 + m_ScreenSpaceShadows: + m_Mode: 1 + m_LightProbeReferenceFrame: 0 + m_DepthNormals: + m_Mode: 1 + m_LightProbeReferenceFrame: 0 + m_MotionVectors: + m_Mode: 1 + m_LightProbeReferenceFrame: 0 + m_LightHalo: + m_Mode: 1 + m_LightProbeReferenceFrame: 0 + m_LensFlare: + m_Mode: 1 + m_LightProbeReferenceFrame: 0 + m_VideoShadersIncludeMode: 2 + m_AlwaysIncludedShaders: [] + m_PreloadedShaders: [] + m_PreloadShadersBatchTimeLimit: -1 + m_SpritesDefaultMaterial: {fileID: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_BrgStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_RenderPipelineGlobalSettingsMap: {} + m_LightsUseLinearIntensity: 1 + m_LightsUseColorTemperature: 1 + m_LogWhenShaderIsCompiled: 0 + m_LightProbeOutsideHullStrategy: 0 + m_CameraRelativeLightCulling: 0 + m_CameraRelativeShadowCulling: 0 diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..5d49588 --- /dev/null +++ b/ProjectSettings/InputManager.asset @@ -0,0 +1,151 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..dbedf5f --- /dev/null +++ b/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,112 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastLayerNavMeshBuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} diff --git a/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..344e978 --- /dev/null +++ b/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 6 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesCapacity: 100 + m_NewContactsCapacity: 2 + m_CollideContactsCapacity: 64 + m_ClearFlagsCapacity: 1000 + m_ClearBodyForcesCapacity: 1000 + m_SyncDiscreteFixturesCapacity: 32 + m_SyncContinuousFixturesCapacity: 32 + m_FindNearestContactsCapacity: 100 + m_UpdateTriggerContactsCapacity: 32 + m_IslandSolverCostCapacity: 100 + m_IslandSolverBodyCapacity: 100 + m_SimulationMode: 0 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 1 + m_AutoSyncTransforms: 0 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/ProjectSettings/PresetManager.asset b/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..67a94da --- /dev/null +++ b/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..b0bd7ca --- /dev/null +++ b/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,283 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 26 + productGUID: a1b2c3d4e5f60718293a4b5c6d7e8f91 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: ifontosh + productName: Приключения кота Кирби + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.12, g: 0.14, b: 0.18, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1920 + defaultScreenHeight: 1080 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 1 + unsupportedMSAAFallback: 0 + m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 + numberOfMipsStrippedPerMipmapLimitGroup: {} + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 1 + androidBlitType: 0 + androidResizableWindow: 0 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 1 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 1 + meshDeformation: 2 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchGpuScratchPoolGranularity: 2097152 + switchAllowGpuScratchShrinking: 0 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + switchNVNGraphicsFirmwareMemory: 32 + switchMaxWorkerMultiple: 8 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 1 + vulkanEnableLateAcquireNextImage: 0 + vulkanEnableCommandBufferRecycling: 1 + loadStoreDebugModeEnabled: 0 + bundleVersion: 0.1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + enableOpenGLProfilerGPURecorders: 1 + allowHDRDisplaySupport: 0 + useHDRDisplay: 0 + hdrBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + resetResolutionOnWindowResize: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: + Standalone: com.ifontosh.CatKirbyAdventures + buildNumber: + Standalone: 0 + VisionOS: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 0 + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 23 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + androidTVCompatibility: 0 + androidIsGame: 1 + androidAllowBackup: 1 + androidUseCustomActivity: 0 + androidGetPermissionSupport: 0 + androidApplicationEntry: 1 + scriptCompilationDefineSymbols: {} + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + il2cppCodeGeneration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + useDeterministicCompilation: 1 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: CatKirbyAdventures + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: Приключения кота Кирби + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} + metroSplashScreenUseBackgroundColor: 0 + syncCapabilities: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + XboxOneOverrideIdentityVersion: + vrEditorSettings: {} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + hmiPlayerDataPath: + hmiForceSRGBBlit: 1 + embeddedLinuxEnableGamepadInput: 1 + hmiLogStartupTiming: 0 + hmiCpuConfiguration: + apiCompatibilityLevel: 6 + activeInputHandler: 2 + windowsGamepadBackendHint: 0 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] + projectName: + organizationId: + cloudEnabled: 0 + legacyClampBlendShapeWeights: 0 + hmiLoadingImage: {fileID: 0} + platformRequiresReadableAssets: 0 + virtualTexturingSupportEnabled: 0 + insecureHttpOption: 0 diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..21f21e5 --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 6000.0.32f1 +m_EditorVersionWithRevision: 6000.0.32f1 (b3223fb5012f) diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..5a4795a --- /dev/null +++ b/ProjectSettings/QualitySettings.asset @@ -0,0 +1,50 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 0 + m_QualitySettings: + - serializedVersion: 3 + name: Medium + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 2 + globalTextureMipmapLimit: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 1 + useLegacyDetailDistribution: 0 + vSyncCount: 0 + realtimeGICPUUsage: 25 + lodBias: 1 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Standalone: 0 + Windows Store Apps: 0 diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..f4db558 --- /dev/null +++ b/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + m_PackageId: + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/ProjectSettings/VFXManager.asset b/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..f3c8102 --- /dev/null +++ b/ProjectSettings/VFXManager.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!141 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 + m_MaxScrubTime: 30 + m_CompiledVersion: 0 + m_RuntimeMode: 0 + m_BatchEmptyLifetime: 300 + m_InstancingDisabled: 0 diff --git a/ProjectSettings/XRSettings.asset b/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..0f144c4 --- /dev/null +++ b/ProjectSettings/XRSettings.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!72687755 &1 +XRSettings: + m_ObjectHideFlags: 0 + m_Enabled: 0 + m_GameViewRenderMode: 1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..946fdfc --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# Приключения кота Кирби + +Вертикальный срез 3D-платформера от третьего лица по главам 1–4 книги. +Целевая версия Unity: **6000.0.32f1** (Unity 6). + +## Установка Unity + +1. Установите [Unity Hub](https://unity.com/download). +2. В Hub → **Installs** → **Install Editor** → найдите **6000.0.32f1** + (или Archive: https://unity.com/releases/editor/archive). +3. При установке отметьте **Windows Build Support** (и IDE support по желанию). +4. В `Packages/manifest.json` уже указаны Universal RP, Input System и Cinemachine. + +## Открытие проекта + +1. Unity Hub → **Open** → папка `D:\\Work\\my_projects\\Cat-Kirby-Adventures` +2. Дождитесь импорта пакетов (первый запуск может занять несколько минут). +3. Откройте сцену `Assets/Scenes/Bootstrap.unity` (она в Build Settings). +4. Нажмите **Play**. + +Уровень **Level 01 (Лес)** собирается в runtime скриптом `Level01ForestBuilder`. + +## Управление + +| Действие | Клавиша | +|----------|---------| +| Ходьба | WASD | +| Камера / взгляд | Мышь | +| Прыжок | Пробел | +| Способность «Свет» | ЛКМ или E (после разблокировки) | +| Респаун на чекпоинте | R | +| Пауза | Escape | + +## Состав прототипа (Phase A) + +- Серый бокс лесной тропы с платформами и пропастями +- Чекпоинты и kill-plane (падение ниже Y = −10) +- Сюжетные триггеры с цитатами (главы «Падение», «Путь в деревню», «Внутренний источник») +- Пьедестал разблокировки способности «Свет» +- Ворота деревни (`LightGate`) — цель уровня + +Документация: `Docs/GDD.md`, `Docs/ChapterMap.md`. + +## Компания + +**ifontosh** — продукт: *Приключения кота Кирби*.