Merge branch 'main' of https://www.nakjungit.site/sharedacc520k/WhaleAdventure_VR
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class CollectionManager : MonoBehaviour
|
||||
public class CollectionManager : MonoBehaviour, ISceneInitializable
|
||||
{
|
||||
public static CollectionManager Instance;
|
||||
|
||||
@@ -28,6 +28,22 @@ void Start()
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
// SceneLoadManager가 씬 로드마다 호출. 새 씬의 StarPieceHud를 다시 찾아 연결한다.
|
||||
public void OnSceneLoaded()
|
||||
{
|
||||
FindStarPieceHud();
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
private void FindStarPieceHud()
|
||||
{
|
||||
// 비활성(토글로 꺼진) HUD도 찾을 수 있도록 Include
|
||||
_starPieceHud = FindFirstObjectByType<StarPieceHud>(FindObjectsInactive.Include);
|
||||
|
||||
if (_starPieceHud == null)
|
||||
Debug.LogWarning("[CollectionManager] 현재 씬에서 StarPieceHud를 찾지 못했습니다.", this);
|
||||
}
|
||||
|
||||
public void AddStar(int amount)
|
||||
{
|
||||
_currentStars += amount;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class StarPickup : MonoBehaviour
|
||||
{
|
||||
@@ -19,7 +20,11 @@ public class StarPickup : MonoBehaviour
|
||||
|
||||
[Header("Sound")]
|
||||
public AudioClip pickupSound;
|
||||
|
||||
|
||||
[Header("Events")]
|
||||
[Tooltip("별을 먹었을 때 발생하는 이벤트입니다.")]
|
||||
public UnityEvent onCollected;
|
||||
|
||||
private Vector3 startPosition;
|
||||
|
||||
void Awake()
|
||||
@@ -63,11 +68,13 @@ public void CollectStar()
|
||||
Debug.LogWarning("CollectionManager not found.");
|
||||
}
|
||||
|
||||
if (pickupSound != null && pickupSound != null)
|
||||
if (pickupSound != null)
|
||||
{
|
||||
SoundManager.Instance.PlaySFX(pickupSound);
|
||||
}
|
||||
|
||||
onCollected?.Invoke();
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47ef309091683cf409e48c93663f025b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,296 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
public class GeppettoRunTriggerZone : MonoBehaviour
|
||||
{
|
||||
[Header("Trigger")]
|
||||
public string playerTag = "Player";
|
||||
|
||||
[Header("Geppetto")]
|
||||
public Transform geppetto;
|
||||
public Animator geppettoAnimator;
|
||||
|
||||
[Header("Animation")]
|
||||
public string runStateName = "Running";
|
||||
public bool playRunAnimationOnTrigger = true;
|
||||
|
||||
[Header("Move")]
|
||||
public float runSpeed = 5f;
|
||||
public float rotateSpeed = 12f;
|
||||
public bool keepStartY = true;
|
||||
|
||||
[Header("Player Lock")]
|
||||
public bool lockPlayerOnTrigger = true;
|
||||
public Transform playerRootToLock; // 비워두면 감지된 VRPlayer root 자동 사용
|
||||
public bool lockPlayerRotation = true;
|
||||
public Behaviour[] disableWhileEvent; // 이동/회전 관련 컴포넌트 넣고 싶으면 여기에
|
||||
|
||||
[Header("VR Black Fade")]
|
||||
public Camera vrCamera;
|
||||
public float fadeDuration = 5f;
|
||||
public float quadDistance = 0.45f;
|
||||
public float quadScale = 3f;
|
||||
|
||||
[Header("Scene")]
|
||||
public string nextSceneName;
|
||||
|
||||
private Transform targetPlayer;
|
||||
private Transform lockedPlayerRoot;
|
||||
private Vector3 lockedPlayerPosition;
|
||||
private Quaternion lockedPlayerRotation;
|
||||
|
||||
private bool started = false;
|
||||
private bool sceneChanging = false;
|
||||
private float startY;
|
||||
|
||||
private GameObject blackQuad;
|
||||
private Material blackMat;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (geppetto != null)
|
||||
startY = geppetto.position.y;
|
||||
|
||||
if (geppettoAnimator != null)
|
||||
geppettoAnimator.applyRootMotion = false;
|
||||
|
||||
CreateBlackQuad();
|
||||
SetBlackAlpha(0f);
|
||||
|
||||
if (blackQuad != null)
|
||||
blackQuad.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (started)
|
||||
return;
|
||||
|
||||
if (!IsPlayer(other))
|
||||
return;
|
||||
|
||||
Debug.Log("제페토 이벤트 구역 진입: " + other.name);
|
||||
|
||||
lockedPlayerRoot = playerRootToLock != null ? playerRootToLock : other.transform.root;
|
||||
targetPlayer = lockedPlayerRoot;
|
||||
|
||||
if (lockPlayerOnTrigger && lockedPlayerRoot != null)
|
||||
{
|
||||
lockedPlayerPosition = lockedPlayerRoot.position;
|
||||
lockedPlayerRotation = lockedPlayerRoot.rotation;
|
||||
|
||||
foreach (Behaviour b in disableWhileEvent)
|
||||
{
|
||||
if (b != null)
|
||||
b.enabled = false;
|
||||
}
|
||||
|
||||
Debug.Log("플레이어 위치 고정: " + lockedPlayerRoot.name);
|
||||
}
|
||||
|
||||
started = true;
|
||||
|
||||
PlayRunAnimation();
|
||||
|
||||
Collider col = GetComponent<Collider>();
|
||||
if (col != null)
|
||||
col.enabled = false;
|
||||
|
||||
StartCoroutine(FadeWhileRunning());
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!started || sceneChanging)
|
||||
return;
|
||||
|
||||
if (geppetto == null || targetPlayer == null)
|
||||
return;
|
||||
|
||||
Vector3 dir = targetPlayer.position - geppetto.position;
|
||||
dir.y = 0f;
|
||||
|
||||
if (dir.sqrMagnitude > 0.001f)
|
||||
{
|
||||
Quaternion targetRot = Quaternion.LookRotation(dir);
|
||||
geppetto.rotation = Quaternion.Slerp(
|
||||
geppetto.rotation,
|
||||
targetRot,
|
||||
Time.deltaTime * rotateSpeed
|
||||
);
|
||||
|
||||
Vector3 nextPos = geppetto.position + dir.normalized * runSpeed * Time.deltaTime;
|
||||
|
||||
if (keepStartY)
|
||||
nextPos.y = startY;
|
||||
|
||||
geppetto.position = nextPos;
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!started || sceneChanging)
|
||||
return;
|
||||
|
||||
if (!lockPlayerOnTrigger || lockedPlayerRoot == null)
|
||||
return;
|
||||
|
||||
lockedPlayerRoot.position = lockedPlayerPosition;
|
||||
|
||||
if (lockPlayerRotation)
|
||||
{
|
||||
lockedPlayerRoot.rotation = lockedPlayerRotation;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayRunAnimation()
|
||||
{
|
||||
if (!playRunAnimationOnTrigger)
|
||||
return;
|
||||
|
||||
if (geppettoAnimator == null)
|
||||
{
|
||||
Debug.LogWarning("Geppetto Animator가 연결되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(runStateName))
|
||||
{
|
||||
Debug.LogWarning("Run State Name이 비어있습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
geppettoAnimator.applyRootMotion = false;
|
||||
geppettoAnimator.Play(runStateName, 0, 0f);
|
||||
geppettoAnimator.Update(0f);
|
||||
|
||||
Debug.Log("달리기 애니메이션 즉시 실행: " + runStateName);
|
||||
}
|
||||
|
||||
private IEnumerator FadeWhileRunning()
|
||||
{
|
||||
if (blackQuad != null)
|
||||
blackQuad.SetActive(true);
|
||||
|
||||
SetBlackAlpha(0f);
|
||||
|
||||
float t = 0f;
|
||||
|
||||
while (t < fadeDuration)
|
||||
{
|
||||
t += Time.deltaTime;
|
||||
float alpha = Mathf.Clamp01(t / fadeDuration);
|
||||
SetBlackAlpha(alpha);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetBlackAlpha(1f);
|
||||
sceneChanging = true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(nextSceneName))
|
||||
{
|
||||
SceneManager.LoadScene(nextSceneName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Next Scene Name이 비어있습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateBlackQuad()
|
||||
{
|
||||
Camera cam = vrCamera != null ? vrCamera : Camera.main;
|
||||
|
||||
if (cam == null)
|
||||
{
|
||||
Debug.LogError("VR Camera가 없습니다. Inspector에 Main Camera를 넣어주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
blackQuad = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||
blackQuad.name = "VR_Black_Fade_Quad";
|
||||
|
||||
Collider col = blackQuad.GetComponent<Collider>();
|
||||
if (col != null)
|
||||
Destroy(col);
|
||||
|
||||
blackQuad.transform.SetParent(cam.transform);
|
||||
blackQuad.transform.localPosition = new Vector3(0f, 0f, quadDistance);
|
||||
blackQuad.transform.localRotation = Quaternion.identity;
|
||||
blackQuad.transform.localScale = new Vector3(quadScale, quadScale, 1f);
|
||||
|
||||
MeshRenderer renderer = blackQuad.GetComponent<MeshRenderer>();
|
||||
blackMat = CreateTransparentBlackMaterial();
|
||||
renderer.material = blackMat;
|
||||
|
||||
renderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
renderer.receiveShadows = false;
|
||||
}
|
||||
|
||||
private Material CreateTransparentBlackMaterial()
|
||||
{
|
||||
Shader shader =
|
||||
Shader.Find("Universal Render Pipeline/Unlit") ??
|
||||
Shader.Find("Unlit/Transparent") ??
|
||||
Shader.Find("Sprites/Default") ??
|
||||
Shader.Find("Standard");
|
||||
|
||||
Material mat = new Material(shader);
|
||||
mat.renderQueue = 5000;
|
||||
|
||||
if (mat.HasProperty("_BaseColor"))
|
||||
mat.SetColor("_BaseColor", new Color(0f, 0f, 0f, 0f));
|
||||
|
||||
if (mat.HasProperty("_Color"))
|
||||
mat.SetColor("_Color", new Color(0f, 0f, 0f, 0f));
|
||||
|
||||
if (mat.HasProperty("_Surface"))
|
||||
mat.SetFloat("_Surface", 1f);
|
||||
|
||||
if (mat.HasProperty("_Blend"))
|
||||
mat.SetFloat("_Blend", 0f);
|
||||
|
||||
if (mat.HasProperty("_SrcBlend"))
|
||||
mat.SetFloat("_SrcBlend", (float)BlendMode.SrcAlpha);
|
||||
|
||||
if (mat.HasProperty("_DstBlend"))
|
||||
mat.SetFloat("_DstBlend", (float)BlendMode.OneMinusSrcAlpha);
|
||||
|
||||
if (mat.HasProperty("_ZWrite"))
|
||||
mat.SetFloat("_ZWrite", 0f);
|
||||
|
||||
mat.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
|
||||
mat.EnableKeyword("_ALPHABLEND_ON");
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private void SetBlackAlpha(float alpha)
|
||||
{
|
||||
if (blackMat == null)
|
||||
return;
|
||||
|
||||
Color c = new Color(0f, 0f, 0f, alpha);
|
||||
|
||||
if (blackMat.HasProperty("_BaseColor"))
|
||||
blackMat.SetColor("_BaseColor", c);
|
||||
|
||||
if (blackMat.HasProperty("_Color"))
|
||||
blackMat.SetColor("_Color", c);
|
||||
}
|
||||
|
||||
private bool IsPlayer(Collider other)
|
||||
{
|
||||
if (other.CompareTag(playerTag))
|
||||
return true;
|
||||
|
||||
if (other.transform.root.CompareTag(playerTag))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9147beba2b87e4d4b9644fb0647ef6b2
|
||||
27
Assets/02_Scripts/MazeRoom/zepettoTrigger.cs
Normal file
27
Assets/02_Scripts/MazeRoom/zepettoTrigger.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.VFX;
|
||||
|
||||
public class zepe : MonoBehaviour
|
||||
{
|
||||
public VisualEffect vfx;
|
||||
public Animator zepettoAnimator;
|
||||
public string animationName;
|
||||
public float delayBeforeVFX;
|
||||
|
||||
private bool hasPlayed = false;
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (hasPlayed) return;
|
||||
if (!other.CompareTag("Player")) return;
|
||||
|
||||
hasPlayed = true;
|
||||
zepettoAnimator.Play(animationName);
|
||||
Invoke("PlayVFX", delayBeforeVFX);
|
||||
}
|
||||
|
||||
private void PlayVFX()
|
||||
{
|
||||
vfx.SendEvent("OnPlay");
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/MazeRoom/zepettoTrigger.cs.meta
Normal file
2
Assets/02_Scripts/MazeRoom/zepettoTrigger.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0b9356ed1cc2c746a48ea28d0537dc8
|
||||
95
Assets/02_Scripts/UI/CollisionButton.cs
Normal file
95
Assets/02_Scripts/UI/CollisionButton.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
/// <summary>
|
||||
/// VR 손이 콜라이더에 닿을(OnTriggerEnter) 때마다 작동하는 버튼.
|
||||
/// XR UI 포인터/포크 상호작용이 불안정할 때, 트리거 콜라이더로 직접 눌리도록 한다.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class CollisionButton : MonoBehaviour
|
||||
{
|
||||
[Header("Hand Detection")]
|
||||
[Tooltip("이 태그를 가진 콜라이더를 손으로 인식합니다.")]
|
||||
[SerializeField] private string handTag = "PlayerHand";
|
||||
|
||||
[Tooltip("태그가 달라도 XRHandMarker가 붙어 있으면 손으로 인식합니다.")]
|
||||
[SerializeField] private bool detectByHandMarker = true;
|
||||
|
||||
[Header("Press Behavior")]
|
||||
[Tooltip("한 번 눌린 뒤 다시 눌리기까지의 최소 간격(초). 중복/연타 입력을 막습니다.")]
|
||||
[SerializeField] private float cooldown = 0.4f;
|
||||
|
||||
[Tooltip("꺼두면 손이 닿아도 반응하지 않습니다.")]
|
||||
[SerializeField] private bool interactable = true;
|
||||
|
||||
[Header("Events")]
|
||||
[Tooltip("손이 닿아 눌릴 때마다 발생합니다.")]
|
||||
public UnityEvent onPressed;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool showDebugLog = false;
|
||||
|
||||
// timeScale=0(일시정지)에서도 동작하도록 unscaled 시간 사용
|
||||
private float _lastPressTime = -999f;
|
||||
|
||||
// 에디터에서 컴포넌트를 처음 추가할 때 콜라이더를 트리거로 자동 설정
|
||||
private void Reset()
|
||||
{
|
||||
Collider col = GetComponent<Collider>();
|
||||
if (col != null)
|
||||
col.isTrigger = true;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Collider col = GetComponent<Collider>();
|
||||
if (col != null && !col.isTrigger)
|
||||
{
|
||||
col.isTrigger = true;
|
||||
if (showDebugLog)
|
||||
Debug.Log("[CollisionButton] Collider가 트리거가 아니어서 강제로 트리거로 설정했습니다.", this);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (!interactable)
|
||||
return;
|
||||
|
||||
if (!IsHand(other))
|
||||
return;
|
||||
|
||||
// 양손 콜라이더가 거의 동시에 들어오거나 빠르게 재진입할 때 중복 발동 방지
|
||||
if (Time.unscaledTime - _lastPressTime < cooldown)
|
||||
return;
|
||||
|
||||
Press();
|
||||
}
|
||||
|
||||
private void Press()
|
||||
{
|
||||
_lastPressTime = Time.unscaledTime;
|
||||
|
||||
if (showDebugLog)
|
||||
Debug.Log("[CollisionButton] 손이 닿아 버튼이 눌렸습니다.", this);
|
||||
|
||||
onPressed?.Invoke();
|
||||
}
|
||||
|
||||
private bool IsHand(Collider other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
if (!string.IsNullOrEmpty(handTag) && other.CompareTag(handTag))
|
||||
return true;
|
||||
|
||||
if (detectByHandMarker && other.GetComponentInParent<XRHandMarker>() != null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 런타임에 활성/비활성 토글이 필요할 때 (UnityEvent 등에서 호출)
|
||||
public void SetInteractable(bool value) => interactable = value;
|
||||
}
|
||||
2
Assets/02_Scripts/UI/CollisionButton.cs.meta
Normal file
2
Assets/02_Scripts/UI/CollisionButton.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c966c32e4e695c44795f9eb58d43d772
|
||||
@@ -8,4 +8,5 @@ public enum Zone
|
||||
Seaside,
|
||||
BlackjackGame,
|
||||
BlackBgm,
|
||||
MazeRoomSound
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user