From 30ce9cbd0e4a51548ea4bee3ddf28dfdd0876559 Mon Sep 17 00:00:00 2001 From: ohgushiyuga Date: Wed, 1 Jul 2026 14:08:37 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=9C=E3=82=B9=E3=81=AB=E7=9D=80=E6=89=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Animation/SpiderAnim.controller | 293 +++++++++++++++ Assets/Animation/SpiderAnim.controller.meta | 8 + Assets/Scenes/BossStage.unity | 95 ++++- Assets/Scripts/BossController.cs | 187 ++++++++++ Assets/Scripts/BossController.cs.meta | 2 + Assets/Scripts/SlowWebArea.cs | 36 ++ Assets/Scripts/SlowWebArea.cs.meta | 2 + Assets/Scripts/SpiderIdleState.cs | 334 ++++++++++++++++++ Assets/Scripts/SpiderIdleState.cs.meta | 2 + Assets/Scripts/VRPunchAttack.cs | 24 +- .../XR/Settings/OpenXRPackageSettings.asset | 20 +- ProjectSettings/NavMeshAreas.asset | 12 +- 12 files changed, 995 insertions(+), 20 deletions(-) create mode 100644 Assets/Animation/SpiderAnim.controller create mode 100644 Assets/Animation/SpiderAnim.controller.meta create mode 100644 Assets/Scripts/BossController.cs create mode 100644 Assets/Scripts/BossController.cs.meta create mode 100644 Assets/Scripts/SlowWebArea.cs create mode 100644 Assets/Scripts/SlowWebArea.cs.meta create mode 100644 Assets/Scripts/SpiderIdleState.cs create mode 100644 Assets/Scripts/SpiderIdleState.cs.meta diff --git a/Assets/Animation/SpiderAnim.controller b/Assets/Animation/SpiderAnim.controller new file mode 100644 index 0000000..b4da4b6 --- /dev/null +++ b/Assets/Animation/SpiderAnim.controller @@ -0,0 +1,293 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1102 &-7266032778200671909 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Blend Tree + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 9008741667183119791} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &-6552264309862256752 +AnimatorStateMachine: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: -7266032778200671909} + m_Position: {x: 270, y: 120, z: 0} + - serializedVersion: 1 + m_State: {fileID: 740941546296395993} + m_Position: {x: 250, y: 20, z: 0} + - serializedVersion: 1 + m_State: {fileID: -3278649130132006237} + m_Position: {x: 500, y: 20, z: 0} + - serializedVersion: 1 + m_State: {fileID: 6938184694430251129} + m_Position: {x: 533.9746, y: -54.257492, z: 0} + - serializedVersion: 1 + m_State: {fileID: 5024885928274401735} + m_Position: {x: 260, y: -50, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: -7266032778200671909} +--- !u!1102 &-3278649130132006237 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Roar + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 3d78caa6f53dc334dba3becf93cfb13a, type: 3} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: SpiderAnim + serializedVersion: 5 + m_AnimatorParameters: + - m_Name: Speed + m_Type: 1 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: Idle + m_Type: 9 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: Attack_Bite + m_Type: 9 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: Roar + m_Type: 9 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: ChargeStart + m_Type: 9 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: IsCharging + m_Type: 4 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: JumpUp + m_Type: 9 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: IsOnCeiling + m_Type: 4 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: Land + m_Type: 9 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: StunStart + m_Type: 9 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + - m_Name: IsStunned + m_Type: 4 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 9100000} + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: -6552264309862256752} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &740941546296395993 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Attack_Bite + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: dd0872a092d485a44adc45de8d253580, type: 3} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1102 &5024885928274401735 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Ceiling_Idle + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 42633f5aaf97ff24c8da19bbb13b9e0c, type: 3} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1102 &6938184694430251129 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: JumpUp + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: e1ac475a1e615a9438b6ab2806192baa, type: 3} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!206 &9008741667183119791 +BlendTree: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Blend Tree + m_Childs: + - serializedVersion: 2 + m_Motion: {fileID: 7400000, guid: d4caf76bd7e7b574bb0cd25e53f8feed, type: 3} + m_Threshold: 0 + m_Position: {x: 0, y: 0} + m_TimeScale: 1 + m_CycleOffset: 0 + m_DirectBlendParameter: Speed + m_Mirror: 0 + - serializedVersion: 2 + m_Motion: {fileID: 7400000, guid: 4969fb900faad2b449340e1b6607370c, type: 3} + m_Threshold: 0.5 + m_Position: {x: 0, y: 0} + m_TimeScale: 1 + m_CycleOffset: 0 + m_DirectBlendParameter: Speed + m_Mirror: 0 + - serializedVersion: 2 + m_Motion: {fileID: 7400000, guid: 209a4cfa82530a44a930056bb9f7457c, type: 3} + m_Threshold: 1 + m_Position: {x: 0, y: 0} + m_TimeScale: 1 + m_CycleOffset: 0 + m_DirectBlendParameter: Speed + m_Mirror: 0 + m_BlendParameter: Speed + m_BlendParameterY: Speed + m_MinThreshold: 0 + m_MaxThreshold: 1 + m_UseAutomaticThresholds: 1 + m_NormalizedBlendValues: 0 + m_BlendType: 0 diff --git a/Assets/Animation/SpiderAnim.controller.meta b/Assets/Animation/SpiderAnim.controller.meta new file mode 100644 index 0000000..f87518d --- /dev/null +++ b/Assets/Animation/SpiderAnim.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba91720f331c67548b9072c7454e7b3d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/BossStage.unity b/Assets/Scenes/BossStage.unity index aae057c..dbe0bbd 100644 --- a/Assets/Scenes/BossStage.unity +++ b/Assets/Scenes/BossStage.unity @@ -672,6 +672,56 @@ MeshFilter: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 126495982} m_Mesh: {fileID: 666016030126120679, guid: 9b368efa585468f40a29b39fce7500ec, type: 3} +--- !u!1 &139010306 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 1987720806421918, guid: a2dfe588a42016e40892b72d31fd1cf6, type: 3} + m_PrefabInstance: {fileID: 1129977831} + m_PrefabAsset: {fileID: 0} +--- !u!114 &139010307 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 139010306} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 60f7f85851ff9b544a35d5dbe1a21285, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::BossController + maxHp: 500 + currentHp: 500 + chaseRange: 15 + biteRange: 2.5 + ceilingAnchor: {fileID: 1626958826} + groundAnchor: {fileID: 1519524653} + attackRadius: 10 + warningMarkerPrefab: {fileID: 0} + webPrefab: {fileID: 0} + chargeSpeed: 12 + chargeDuration: 1.5 +--- !u!195 &139010308 +NavMeshAgent: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 139010306} + m_Enabled: 1 + m_AgentTypeID: 0 + m_Radius: 1.8 + m_Speed: 3.5 + m_Acceleration: 8 + avoidancePriority: 50 + m_AngularSpeed: 120 + m_StoppingDistance: 0 + m_AutoTraverseOffMeshLink: 1 + m_AutoBraking: 1 + m_AutoRepath: 1 + m_Height: 2 + m_BaseOffset: 0 + m_WalkableMask: 4294967295 + m_ObstacleAvoidanceType: 4 --- !u!1 &142265989 GameObject: m_ObjectHideFlags: 0 @@ -4109,7 +4159,13 @@ PrefabInstance: m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] - m_AddedComponents: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 1987720806421918, guid: a2dfe588a42016e40892b72d31fd1cf6, type: 3} + insertIndex: -1 + addedObject: {fileID: 139010308} + - targetCorrespondingSourceObject: {fileID: 1987720806421918, guid: a2dfe588a42016e40892b72d31fd1cf6, type: 3} + insertIndex: -1 + addedObject: {fileID: 139010307} m_SourcePrefab: {fileID: 100100000, guid: a2dfe588a42016e40892b72d31fd1cf6, type: 3} --- !u!1001 &1153141912 PrefabInstance: @@ -5590,6 +5646,42 @@ Transform: m_CorrespondingSourceObject: {fileID: 1030989063, guid: abc00000000014917668085838676610, type: 3} m_PrefabInstance: {fileID: 1509327661} m_PrefabAsset: {fileID: 0} +--- !u!4 &1519524653 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 1030989063, guid: abc00000000014917668085838676610, type: 3} + m_PrefabInstance: {fileID: 1341723605} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1626958825 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1626958826} + m_Layer: 0 + m_Name: GameObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1626958826 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1626958825} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 94, y: 40, z: -2} + 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 &1630667895 GameObject: m_ObjectHideFlags: 0 @@ -7793,3 +7885,4 @@ SceneRoots: - {fileID: 224817713} - {fileID: 1129977831} - {fileID: 1460420860} + - {fileID: 1626958826} diff --git a/Assets/Scripts/BossController.cs b/Assets/Scripts/BossController.cs new file mode 100644 index 0000000..1f38a54 --- /dev/null +++ b/Assets/Scripts/BossController.cs @@ -0,0 +1,187 @@ +using UnityEngine; +using UnityEngine.AI; +using System.Collections.Generic; + +[RequireComponent(typeof(NavMeshAgent))] +[RequireComponent(typeof(Animator))] +public class BossController : MonoBehaviour +{ + #region インスペクター設定項目 + [Header("ステータス設定")] + [SerializeField] private float maxHp = 500f; + [SerializeField] private float currentHp = 500f; + [SerializeField] public float chaseRange = 15f; + [SerializeField] public float biteRange = 2.5f; + + [Header("必殺技(天井)設定")] + [Tooltip("天井の待機座標となる空のGameObjectのTransformを指定してください")] + [SerializeField] public Transform ceilingAnchor; + [Tooltip("地上に戻る際の、中央の着地座標のTransformを指定してください")] + [SerializeField] public Transform groundAnchor; + [Tooltip("天井から糸を発射する範囲の広さ")] + [SerializeField] public float attackRadius = 10f; + [Tooltip("糸が降る前の予兆マークのPrefab")] + [SerializeField] private GameObject warningMarkerPrefab; + [Tooltip("地面に残る糸(コライダー&鈍足スクリプト付き)のPrefab")] + [SerializeField] private GameObject webPrefab; + + [Header("突進(体当たり)設定")] + [SerializeField] public float chargeSpeed = 12f; + [SerializeField] public float chargeDuration = 1.5f; + #endregion + + #region コンポーネント・キャッシュ用(ステートからアクセス可能) + [HideInInspector] public NavMeshAgent Agent { get; private set; } + [HideInInspector] public Animator Anim { get; private set; } + [HideInInspector] public Transform Player { get; private set; } + #endregion + + #region FSM(状態管理)システム + private ISpiderState currentState; + + // ステートのインスタンスをキャッシュ(ガベージコレクションの発生を抑える) + private Dictionary stateCache; + + // フェーズ管理用フラグ(50%と25%のトリガー管理) + private bool triggered50 = false; + private bool triggered25 = false; + public bool PendingUltimate { get; set; } = false; // 必殺技の発動権があるか + #endregion + + private void Awake() + { + Agent = GetComponent(); + Anim = GetComponent(); + + GameObject pObj = GameObject.FindGameObjectWithTag("Player"); + if (pObj != null) Player = pObj.transform; + + InitializeFSM(); + } + + private void InitializeFSM() + { + stateCache = new Dictionary + { + { typeof(SpiderIdleState), new SpiderIdleState(this) }, + { typeof(SpiderChaseState), new SpiderChaseState(this) }, + { typeof(SpiderBiteState), new SpiderBiteState(this) }, + { typeof(SpiderChargeState), new SpiderChargeState(this) }, + { typeof(SpiderJumpToCeilingState), new SpiderJumpToCeilingState(this) }, + { typeof(SpiderCeilingAttackState), new SpiderCeilingAttackState(this) }, + { typeof(SpiderStunState), new SpiderStunState(this) } + }; + + // 初期ステートをIdleに設定 + ChangeState(); + } + + private void Update() + { + // 現在のステートのUpdate処理を実行(ポリモーフィズム) + currentState?.UpdateState(); + } + + /// + /// オブジェクト指向に基づいた、型安全なステート遷移メソッド + /// + public void ChangeState() where T : ISpiderState + { + if (currentState != null) + { + currentState.ExitState(); + Debug.Log($"🕷️ BossSpider [Exit]: {currentState.GetType().Name}"); + } + + currentState = stateCache[typeof(T)]; + Debug.Log($"🕷️ BossSpider [Enter]: {currentState.GetType().Name}"); + currentState.EnterState(); + } + + /// + /// ダメージを受け取る外部窓口。HPトリガーによるフェーズ遷移をここで監視する + /// + public void TakeDamage(float damage) + { + currentHp -= damage; + currentHp = Mathf.Clamp(currentHp, 0, maxHp); + float hpPercentage = (currentHp / maxHp) * 100f; + + Debug.Log($"💥 ボスに {damage} ダメージ。残りHP: {hpPercentage:F1}%"); + + // 25%以下カットのトリガー + if (hpPercentage <= 25f && !triggered25) + { + triggered25 = true; + PendingUltimate = true; + TriggerPhaseTransition(); + } + // 50%以下カットのトリガー + else if (hpPercentage <= 50f && !triggered50 && hpPercentage > 25f) + { + triggered50 = true; + PendingUltimate = true; + TriggerPhaseTransition(); + } + } + + private void TriggerPhaseTransition() + { + // 咆哮、ジャンプ、スタン中以外であれば、現在の行動をキャンセルして即座に天井へエスケープ + if (currentState is not SpiderJumpToCeilingState && + currentState is not SpiderCeilingAttackState && + currentState is not SpiderStunState) + { + ChangeState(); + } + } + + /// + /// 【パリィ連携窓口】プレイヤーのVRInputPunchなどから、パリィ成功時にこのメソッドが叩かれる + /// + public void TriggerParryStun() + { + // パリィを受け付ける攻撃ステート(噛みつき、体当たり)のときのみスタンに移行する + if (currentState is SpiderBiteState || currentState is SpiderChargeState) + { + ChangeState(); + } + } + + #region ファクトリメソッド(エフェクトやオブジェクト生成の保守性を担保) + public void SpawnWarningAndWeb(Vector3 targetGroundPos, float delay, float webDuration) + { + StartCoroutine(SpawnWebRoutine(targetGroundPos, delay, webDuration)); + } + + private System.Collections.IEnumerator SpawnWebRoutine(Vector3 pos, float delay, float duration) + { + // 1. 地面に予兆マークを生成 + GameObject marker = null; + if (warningMarkerPrefab != null) + { + marker = Instantiate(warningMarkerPrefab, pos + new Vector3(0, 0.02f, 0), Quaternion.identity); + } + + yield return new WaitForSeconds(delay); + + // 2. 予兆マークを消し、本物の糸オブジェクトを生成 + if (marker != null) Destroy(marker); + + if (webPrefab != null) + { + GameObject web = Instantiate(webPrefab, pos, Quaternion.identity); + Destroy(web, duration); // 数秒後に自動消滅 + } + } + #endregion +} + +#region 1. ステート共通インターフェース +public interface ISpiderState +{ + void EnterState(); + void UpdateState(); + void ExitState(); +} +#endregion \ No newline at end of file diff --git a/Assets/Scripts/BossController.cs.meta b/Assets/Scripts/BossController.cs.meta new file mode 100644 index 0000000..df869c0 --- /dev/null +++ b/Assets/Scripts/BossController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 60f7f85851ff9b544a35d5dbe1a21285 \ No newline at end of file diff --git a/Assets/Scripts/SlowWebArea.cs b/Assets/Scripts/SlowWebArea.cs new file mode 100644 index 0000000..9f659ae --- /dev/null +++ b/Assets/Scripts/SlowWebArea.cs @@ -0,0 +1,36 @@ +using UnityEngine; + +public class SlowWebArea : MonoBehaviour +{ + [SerializeField] private float damagePerSecond = 5f; + private float damageTimer = 0f; + + private void OnTriggerStay(Collider other) + { + if (other.CompareTag("Player")) + { + // ★鈍足効果の適用演出 + // プレイヤーの移動スクリプトに対し、一時的な速度デバフ(例: Speed * 0.3f)を掛けます + // ここでは設計の切り離しのため、ログ出力のみとしています + Debug.LogWarning("🕸️ プレイヤーが蜘蛛の糸に囚われている! 【鈍足化デバフ発動中】"); + + // スリップダメージ(1秒ごとに継続ダメージ) + damageTimer += Time.deltaTime; + if (damageTimer >= 1.0f) + { + // プレイヤーのHealthコンポーネントにダメージを通知 + // other.GetComponent()?.TakeDamage(damagePerSecond); + Debug.Log($"🕸️ 糸によるスリップダメージ: {damagePerSecond}"); + damageTimer = 0f; + } + } + } + + private void OnTriggerExit(Collider other) + { + if (other.CompareTag("Player")) + { + Debug.Log("🏃 プレイヤーが糸の範囲から脱出。 鈍足効果解除。"); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/SlowWebArea.cs.meta b/Assets/Scripts/SlowWebArea.cs.meta new file mode 100644 index 0000000..1963327 --- /dev/null +++ b/Assets/Scripts/SlowWebArea.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0926e85a7d8a1a349a8e4e6e2ddfa0a5 \ No newline at end of file diff --git a/Assets/Scripts/SpiderIdleState.cs b/Assets/Scripts/SpiderIdleState.cs new file mode 100644 index 0000000..129526a --- /dev/null +++ b/Assets/Scripts/SpiderIdleState.cs @@ -0,0 +1,334 @@ +using UnityEngine; + +// ========================================== +// ■ STATE: IDLE (待機状態) +// ========================================== +public class SpiderIdleState : ISpiderState +{ + private BossController boss; + public SpiderIdleState(BossController owner) => boss = owner; + + public void EnterState() => boss.Anim.SetTrigger("Idle"); + public void ExitState() { } + + public void UpdateState() + { + if (boss.Player == null) return; + + // プレイヤーを検知したら即座に追跡へ + if (Vector3.Distance(boss.transform.position, boss.Player.position) <= boss.chaseRange) + { + boss.ChangeState(); + } + } +} + +// ========================================== +// ■ STATE: CHASE (追跡・行動分岐状態) +// ========================================== +public class SpiderChaseState : ISpiderState +{ + private BossController boss; + private float decisionCooldown = 0f; + + public SpiderChaseState(BossController owner) => boss = owner; + + public void EnterState() => boss.Agent.isStopped = false; + public void ExitState() => boss.Agent.isStopped = true; + + public void UpdateState() + { + if (boss.Player == null) return; + + // NavMeshでの追従 + boss.Agent.SetDestination(boss.Player.position); + boss.Anim.SetFloat("Speed", boss.Agent.velocity.magnitude); + + // ディシジョン(思考)タイマーの更新 + decisionCooldown -= Time.deltaTime; + if (decisionCooldown > 0f) return; + + float distance = Vector3.Distance(boss.transform.position, boss.Player.position); + + // 1. 至近距離 ➔ 通常噛みつき + if (distance <= boss.biteRange) + { + boss.ChangeState(); + decisionCooldown = 2f; + } + // 2. 中距離 ➔ 走って体当たり攻撃 + else if (distance > boss.biteRange && distance <= boss.chaseRange * 0.6f) + { + boss.ChangeState(); + decisionCooldown = 5f; + } + } +} + +// ========================================== +// ■ STATE: BITE (通常噛みつき - パリィ可) +// ========================================== +public class SpiderBiteState : ISpiderState +{ + private BossController boss; + private float stateTimer; + + public SpiderBiteState(BossController owner) => boss = owner; + + public void EnterState() + { + stateTimer = 1.5f; // 攻撃アニメーションの想定想定想定 + boss.Anim.SetTrigger("Attack_Bite"); + Debug.Log("🎯 蜘蛛ボス:噛みつき攻撃!(パリィ受付中)"); + } + + public void ExitState() { } + + public void UpdateState() + { + stateTimer -= Time.deltaTime; + if (stateTimer <= 0f) + { + // 攻撃が終わったら、フェーズ移行チェックを挟みつつ追跡に戻る + if (boss.PendingUltimate) boss.ChangeState(); + else boss.ChangeState(); + } + } +} + +// ========================================== +// ■ STATE: CHARGE (走って体当たり突進 - パリィ可) +// ========================================== +public class SpiderChargeState : ISpiderState +{ + private BossController boss; + private float roarTimer; + private float chargeTimer; + private Vector3 chargeDirection; + private bool isChargingPhase; + + public SpiderChargeState(BossController owner) => boss = owner; + + public void EnterState() + { + roarTimer = 1.5f; + chargeTimer = boss.chargeDuration; + isChargingPhase = false; + + boss.Agent.enabled = false; // 移動をマニュアル制御するためNavMeshを切る + + // 1. まず「威嚇の咆哮」アニメーションをトリガー + boss.Anim.SetTrigger("Attack_Roar"); + Debug.Log("🔊 蜘蛛ボス:走る前に威嚇の咆哮中...(この間にプレイヤーはパリィの構えが可能)"); + } + + public void ExitState() + { + boss.Anim.SetBool("IsCharging", false); + boss.Agent.enabled = true; + } + + public void UpdateState() + { + // フェーズ1:威嚇中(その場で動かない) + if (!isChargingPhase) + { + roarTimer -= Time.deltaTime; + + // 威嚇中もプレイヤーの方向をじっと見据える(ロックオン演出) + if (boss.Player != null) + { + Vector3 lookDir = (boss.Player.position - boss.transform.position).normalized; + lookDir.y = 0; + if (lookDir != Vector3.zero) boss.transform.rotation = Quaternion.Slerp(boss.transform.rotation, Quaternion.LookRotation(lookDir), Time.deltaTime * 5f); + } + + if (roarTimer <= 0f) + { + // 威嚇終了、ここから本番の突進フェーズへ + isChargingPhase = true; + boss.Anim.SetTrigger("ChargeStart"); + boss.Anim.SetBool("IsCharging", true); + + if (boss.Player != null) + { + chargeDirection = (boss.Player.position - boss.transform.position).normalized; + chargeDirection.y = 0; + boss.transform.rotation = Quaternion.LookRotation(chargeDirection); + } + } + return; + } + + // フェーズ2:突進中(直線移動を実行。ここからプレイヤーはパリィ可能) + boss.transform.position += chargeDirection * boss.chargeSpeed * Time.deltaTime; + + chargeTimer -= Time.deltaTime; + if (chargeTimer <= 0f) + { + boss.ChangeState(); + } + } +} + +// ========================================== +// ■ STATE: JUMP TO CEILING (天井への大ジャンプ) +// ========================================== +public class SpiderJumpToCeilingState : ISpiderState +{ + private BossController boss; + private float roarTimer; + private float jumpProgress; + private Vector3 startPos; + private bool isJumpingPhase; + + public SpiderJumpToCeilingState(BossController owner) => boss = owner; + + public void EnterState() + { + roarTimer = 2.0f; // ★天井に飛ぶ前の大威嚇の時間 (2.0秒) + jumpProgress = 0f; + startPos = boss.transform.position; + isJumpingPhase = false; + + boss.Agent.enabled = false; + + // 1. 必殺技移行を知らせる威嚇の咆哮 + boss.Anim.SetTrigger("Attack_Roar"); + Debug.Log("🚨 蜘蛛ボス:必殺技前の一大威嚇(咆哮)! このあと天井へ飛びます。"); + } + + public void ExitState() { } + + public void UpdateState() + { + // フェーズ1:飛び立つ前の威嚇ポーズ(その場で足止め) + if (!isJumpingPhase) + { + roarTimer -= Time.deltaTime; + if (roarTimer <= 0f) + { + isJumpingPhase = true; + boss.Anim.SetTrigger("JumpUp"); // ジャンプアニメーションへ + } + return; + } + + // フェーズ2:本来の大ジャンプ空中移動 + if (boss.ceilingAnchor == null) return; + + jumpProgress += Time.unscaledDeltaTime * 1.5f; + + Vector3 currentPos = Vector3.Lerp(startPos, boss.ceilingAnchor.position, jumpProgress); + currentPos.y += Mathf.Sin(jumpProgress * Mathf.PI) * 3f; + + boss.transform.position = currentPos; + + if (jumpProgress >= 1f) + { + boss.transform.position = boss.ceilingAnchor.position; + boss.ChangeState(); + } + } +} + +// ========================================== +// ■ STATE: CEILING ATTACK (必殺技:天井糸吐き) +// ========================================== +public class SpiderCeilingAttackState : ISpiderState +{ + private BossController boss; + private float stateDuration = 7.0f; // 必殺技全体の長さ(秒) + private float shotInterval = 0.4f; // 糸を降らせる間隔 + private float timer = 0f; + private float shotTimer = 0f; + + public SpiderCeilingAttackState(BossController owner) => boss = owner; + + public void EnterState() + { + timer = stateDuration; + shotTimer = 0f; + boss.Anim.SetBool("IsOnCeiling", true); + boss.PendingUltimate = false; // 発動権を消化 + Debug.Log("🕸️ 蜘蛛ボス:天井から無数の糸を乱射中!"); + } + + public void ExitState() + { + boss.Anim.SetBool("IsOnCeiling", false); + // 地上の着地座標へ位置をリセットしてNavMeshを復帰 + if (boss.groundAnchor != null) boss.transform.position = boss.groundAnchor.position; + boss.Agent.enabled = true; + boss.Anim.SetTrigger("Land"); + } + + public void UpdateState() + { + timer -= Time.deltaTime; + shotTimer -= Time.deltaTime; + + if (shotTimer <= 0f) + { + ExecuteRandomWebAttack(); + shotTimer = shotInterval; + } + + // 時間が来たら、地上へ復帰 + if (timer <= 0f) + { + boss.ChangeState(); + } + } + + private void ExecuteRandomWebAttack() + { + // ボスの中央(地上座標)を基準に、ランダムな円内に糸を降らせる + Vector2 randomCircle = Random.insideUnitCircle * boss.attackRadius; + Vector3 targetGroundPos = new Vector3( + boss.groundAnchor.position.x + randomCircle.x, + boss.groundAnchor.position.y, + boss.groundAnchor.position.z + randomCircle.y + ); + + // 予兆を出し、1.0秒後に2.5秒間残る糸を生成する + boss.SpawnWarningAndWeb(targetGroundPos, 1.0f, 2.5f); + } +} + +// ========================================== +// ■ STATE: STUN (チャンスタイム - パリィ成功時) +// ========================================== +public class SpiderStunState : ISpiderState +{ + private BossController boss; + private float stunTimer; + + public SpiderStunState(BossController owner) => boss = owner; + + public void EnterState() + { + stunTimer = 4.0f; // 4秒間の完全な無防備状態(チャンスタイム) + boss.Anim.SetTrigger("StunStart"); + boss.Anim.SetBool("IsStunned", true); + if (boss.Agent.enabled) boss.Agent.isStopped = true; + Debug.Log("🛡️ パリィ成功! 蜘蛛ボスがひっくり返ってスタン中! チャンスタイム!"); + } + + public void ExitState() + { + boss.Anim.SetBool("IsStunned", false); + if (boss.Agent.enabled) boss.Agent.isStopped = false; + } + + public void UpdateState() + { + stunTimer -= Time.deltaTime; + if (stunTimer <= 0f) + { + // スンが明けたら、もしHPトリガーが裏で引かれていれば優先して天井へ + if (boss.PendingUltimate) boss.ChangeState(); + else boss.ChangeState(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/SpiderIdleState.cs.meta b/Assets/Scripts/SpiderIdleState.cs.meta new file mode 100644 index 0000000..3a8d80c --- /dev/null +++ b/Assets/Scripts/SpiderIdleState.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 67c8b05cbdd96694cbb2b2ef0a5efdd6 \ No newline at end of file diff --git a/Assets/Scripts/VRPunchAttack.cs b/Assets/Scripts/VRPunchAttack.cs index ea21799..f4f0840 100644 --- a/Assets/Scripts/VRPunchAttack.cs +++ b/Assets/Scripts/VRPunchAttack.cs @@ -42,7 +42,7 @@ public class VRInputPunch : MonoBehaviour void Start() { Rigidbody rb = GetComponent(); - rb.isKinematic = true; + rb.isKinematic = true; rb.useGravity = false; GetComponent().isTrigger = true; } @@ -70,11 +70,17 @@ public class VRInputPunch : MonoBehaviour // パリィ成功用の強力な振動をプレイヤーに返す TriggerHaptics(parryHapticAmplitude, parryHapticDuration); - + Debug.Log($"🛡️ 【ジャストパリィ成功!】 敵の体勢を崩しました。スタン時間: {parryStunDuration}秒"); return; // パリィが成立した場合は以降のパンチ処理は行わない } } + BossController boss = other.GetComponentInParent(); + if (boss != null) + { + // ボスにパリィ成立を通知(噛みつき・突進中であれば自動でスタンへ移行) + boss.TriggerParryStun(); + } else { Debug.Log("🧱 ガード(または速度不足):敵の攻撃は防いだが、弾き返せなかった。"); @@ -100,27 +106,27 @@ public class VRInputPunch : MonoBehaviour info.amount = (speed * damageMultiplier) * 2f; info.isPoseBonus = true; - if(PlayerStatusManager.Instance != null) PlayerStatusManager.Instance.AddGauge(20f); + if (PlayerStatusManager.Instance != null) PlayerStatusManager.Instance.AddGauge(20f); } else { info.amount = speed * damageMultiplier; info.isPoseBonus = false; - if(PlayerStatusManager.Instance != null) PlayerStatusManager.Instance.AddGauge(10f); + if (PlayerStatusManager.Instance != null) PlayerStatusManager.Instance.AddGauge(10f); } info.hitPosition = other.transform.position; - info.punchDirection = velocity.normalized; + info.punchDirection = velocity.normalized; - if(PlayerStatusManager.Instance != null) + if (PlayerStatusManager.Instance != null) { info.amount *= PlayerStatusManager.Instance.CurrentAttackMultiplier; } - + damageble.TakeDamage(info); - + Debug.Log($"パンチ成功 ダメージ: {info.amount:F1}"); - + // 通常ヒットの振動を発生 TriggerHaptics(hapticAmplitude, hapticDuration); } diff --git a/Assets/XR/Settings/OpenXRPackageSettings.asset b/Assets/XR/Settings/OpenXRPackageSettings.asset index df42c32..af81028 100644 --- a/Assets/XR/Settings/OpenXRPackageSettings.asset +++ b/Assets/XR/Settings/OpenXRPackageSettings.asset @@ -565,8 +565,18 @@ MonoBehaviour: features: - {fileID: 8736661028334727731} - {fileID: 3598254934845347770} + - {fileID: -6832857813982530537} + - {fileID: -4559034614451708685} - {fileID: 1170704673688743257} + - {fileID: -1187533949232362847} + - {fileID: -949347770443697596} + - {fileID: -1529727133502117446} - {fileID: 5153017048313567732} + - {fileID: 265326132771859475} + - {fileID: 2834828863856360103} + - {fileID: -2851827796942056979} + - {fileID: -8128959895479975907} + - {fileID: 431653824124869831} - {fileID: 6481653220666705815} - {fileID: 7393116930172532905} - {fileID: 744375598501864353} @@ -1216,8 +1226,8 @@ MonoBehaviour: features: - {fileID: 7011443450567333355} - {fileID: -671281728444007487} - - {fileID: -901939997048047619} - - {fileID: -1734471456787174204} + - {fileID: 6956056911768161135} + - {fileID: 5091991234558418143} - {fileID: 8980318060753866109} - {fileID: -6559185765804338520} - {fileID: -4756202077021157709} @@ -1228,7 +1238,7 @@ MonoBehaviour: - {fileID: 348827886137839178} - {fileID: -7499073031166464939} - {fileID: 7666556398442971420} - - {fileID: -142859899272520151} + - {fileID: 1118878472687837173} - {fileID: 3985858319333531581} - {fileID: -6511185071715886993} - {fileID: -4363231183632145407} @@ -1634,7 +1644,7 @@ MonoBehaviour: nameUi: 'Android XR: AR Anchor' version: 0.1.0 featureIdInternal: com.unity.openxr.feature.arfoundation-androidxr-anchor - openxrExtensionStrings: XR_ANDROID_trackables XR_ANDROID_device_anchor_persistence + openxrExtensionStrings: XR_ANDROID_trackables company: Unity Technologies priority: 0 targetOpenXRApiVersion: @@ -2581,7 +2591,7 @@ MonoBehaviour: nameUi: 'Android XR: AR Anchor' version: 0.1.0 featureIdInternal: com.unity.openxr.feature.arfoundation-androidxr-anchor - openxrExtensionStrings: XR_ANDROID_trackables XR_ANDROID_device_anchor_persistence + openxrExtensionStrings: XR_ANDROID_trackables company: Unity Technologies priority: 0 targetOpenXRApiVersion: diff --git a/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset index 3b0b7c3..12e341c 100644 --- a/ProjectSettings/NavMeshAreas.asset +++ b/ProjectSettings/NavMeshAreas.asset @@ -71,10 +71,10 @@ NavMeshProjectSettings: cost: 1 m_LastAgentTypeID: -887442657 m_Settings: - - serializedVersion: 2 + - serializedVersion: 3 agentTypeID: 0 - agentRadius: 0.5 - agentHeight: 2 + agentRadius: 3 + agentHeight: 3 agentSlope: 45 agentClimb: 0.75 ledgeDropHeight: 0 @@ -84,8 +84,10 @@ NavMeshProjectSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 debug: m_Flags: 0 m_SettingNames: - - Humanoid + - Spider