노래찍기 기능추가 및 박스 파괴가 아니라 베이도록 수정

This commit is contained in:
jongjae0305
2026-04-30 17:42:25 +09:00
parent 0b6374511c
commit a00ab7e32d
54 changed files with 2756 additions and 74 deletions
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 18614e0561d48a748a839a354f0ff42a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
using System.Collections.Generic;
[System.Serializable]
public class NoteData
{
public float time;
public int position;
}
[System.Serializable]
public class BeatMapData
{
public List<NoteData> notes = new List<NoteData>();
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 67b13dfc4dac3674380379246b8f6220
@@ -0,0 +1,40 @@
using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections.Generic;
using System.IO;
public class MapRecorder : MonoBehaviour
{
public AudioSource audioSource;
private List<NoteData> recordedNotes = new List<NoteData>();
void Update()
{
if (audioSource.isPlaying && Keyboard.current != null)
{
// 키패드 4, 5, 1, 2를 각각 0, 1, 2, 3번 인덱스에 매핑
if (Keyboard.current.numpad4Key.wasPressedThisFrame) RecordNote(0); // 좌상
if (Keyboard.current.numpad5Key.wasPressedThisFrame) RecordNote(1); // 우상
if (Keyboard.current.numpad1Key.wasPressedThisFrame) RecordNote(2); // 좌하
if (Keyboard.current.numpad2Key.wasPressedThisFrame) RecordNote(3); // 우하
}
}
void RecordNote(int pos)
{
NoteData note = new NoteData { time = audioSource.time, position = pos };
recordedNotes.Add(note);
// 로그로 어떤 키를 눌렀는지 확인하기
string posName = (pos == 0) ? "좌상(4)" : (pos == 1) ? "우상(5)" : (pos == 2) ? "좌하(1)" : "우하(2)";
Debug.Log($"기록됨: {note.time:F2}초 / 위치: {posName}");
}
public void SaveBeatMap()
{
BeatMapData data = new BeatMapData { notes = recordedNotes };
string json = JsonUtility.ToJson(data, true);
string path = Path.Combine(Application.streamingAssetsPath, "Map_Life.json");
File.WriteAllText(path, json);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3dde370502e0759409a88f7bbe9f6f00
@@ -0,0 +1,52 @@
using UnityEngine;
using System.Collections.Generic;
using System.IO;
public class MusicSpawner : MonoBehaviour
{
public AudioSource audioSource;
public GameObject[] cube;
public Transform[] point;
// List<float> 대신 List<NoteData>를 사용합니다.
private List<NoteData> spawnNotes = new List<NoteData>();
private int nextSpawnIndex = 0;
void Start()
{
string filePath = Path.Combine(Application.streamingAssetsPath, "Map_Life.json");
if (File.Exists(filePath))
{
string jsonString = File.ReadAllText(filePath);
// JSON을 읽어서 NoteData 리스트로 가져옵니다.
BeatMapData data = JsonUtility.FromJson<BeatMapData>(jsonString);
spawnNotes = data.notes;
}
if (audioSource != null) audioSource.Play();
}
void Update()
{
// 리스트가 비어있지 않은지 확인
if (audioSource.isPlaying && nextSpawnIndex < spawnNotes.Count)
{
// 이제 리스트의 요소는 NoteData 객체이므로 .time과 .position에 접근 가능합니다.
var currentNote = spawnNotes[nextSpawnIndex];
if (audioSource.time >= currentNote.time)
{
SpawnCube(currentNote.position);
nextSpawnIndex++;
}
}
}
void SpawnCube(int pos)
{
// pos가 point 배열의 범위를 넘지 않도록 예외 처리
if (pos >= 0 && pos < point.Length)
{
Instantiate(cube[0], point[pos].position, point[pos].rotation);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c9e23740cc2c239489d946a0270e4ffd
@@ -1,33 +1,111 @@
using System.ComponentModel.Design.Serialization;
using UnityEngine;
using EzySlice;
public class Saber : MonoBehaviour
{
public LayerMask layer;
Vector3 prevPos;
//손잡이랑 검끝 사이를 레이 쏠 예정(검날)
[Header("슬라이스용 직렬화")]
public Transform startSlicePoint;
public Transform endSlicePoint;
public VelocityEstimator velocityEstimator; //속도측정기(검 끝에 달림)
public LayerMask sliceableLayer; //하는김에 얘도 그냥 직렬화
void Start()
{
}
[Header("이펙트 세팅")]
public Material cuttingMaterial; //절단면
public float cutForce = 100f; //잘린 조각 튕기기
void Update()
[Header("슬라이스 조건 설정")]
[Range(60f, 160f)]
public float sliceAngle = 100f; // 필요 각도 (직렬화해서 인스펙터에서 조절 가능하게)
[Tooltip("이 속도보다 빨라야만 슬라이싱이 작동합니다.")]
public float swingSpeedThreshold = 2.0f; // 필요 최소 속도 (이 값을 조절해서 '갖다 대기'를 방지)
private void FixedUpdate()
{
RaycastHit hit;
if(Physics.Raycast(transform.position, transform.forward, out hit, 1, layer))
//Raycast는 위치값을 받는 클래스로 뒤의 인수는 위치, 방향, 저장되는 변수, 거리, layer에 해당되는것만
//손잡이와 검 끝 사이에 선을 그어 체크
bool hasHit = Physics.Linecast(
startSlicePoint.position,
endSlicePoint.position,
out RaycastHit hit,
sliceableLayer);
//체크됐으면
if (hasHit)
{
Vector3 v1 = transform.position - prevPos; // 현재위치 - 이전 위치 = 이동방향
if(Vector3.Angle(v1, hit.transform.up) > 130)
//두 벡터 사이의 벌어진 각도를 구하는 함수 이동방향v1과 충돌한 물체의 위쪽방향
//hit 된 오브젝트를 타겟으로 자르기 메서드 실행
GameObject target = hit.transform.gameObject;
Vector3 swingVelocity = velocityEstimator.Velocity;
float currentSwingSpeed = swingVelocity.magnitude;
if (currentSwingSpeed > swingSpeedThreshold) // 1단계: 속도 체크
{
Destroy(hit.transform.gameObject);
// 5. 각도 체크
// swingVelocity.normalized를 쓰면 크기를 무시하고 방향만 비교할 수 있어 더 정확합니다.
float angle = Vector3.Angle(swingVelocity.normalized, target.transform.up);
if (angle > sliceAngle) // 2단계: 각도 체크
{
Slice(target);
}
}
}
prevPos = transform.position;
}
//자르는 메서드
public void Slice(GameObject target)
{
//1. 자를 평면 방향 구하기
//1-1. 검 방향 벡터
Vector3 saberDirection = endSlicePoint.position - startSlicePoint.position;
//1-2. 검 속도벡터(velocityEstimator.Velocity)
Vector3 velocity = velocityEstimator.Velocity;
//1-3. 두 벡터의 수직인 벡터를 구해서 정규화 = 자르는 면의 방향
Vector3 planeNormal = Vector3.Cross(saberDirection, velocity).normalized;
//2. 자르기
SlicedHull hull = target.Slice(endSlicePoint.position, planeNormal, cuttingMaterial);
if (hull == null)
{
return;
}
//3. 위아래조각 생성(여기부턴 동일)
GameObject upperHull = hull.CreateUpperHull(target, cuttingMaterial);
GameObject lowerHull = hull.CreateLowerHull(target, cuttingMaterial);
//4. 조각들에 SetupHull 물리부여
SetupHull(upperHull);
SetupHull(lowerHull);
//5. 원본은 삭제
Destroy(target);
}
//물리 부여
//얜 거의 안 건드리고 레이어 바꾸는 거만 추가
void SetupHull(GameObject hull)
{
Rigidbody rb = hull.AddComponent<Rigidbody>();
MeshCollider collider = hull.AddComponent<MeshCollider>();
collider.convex = true;//물리충돌용
//잘린 면 방향으로 힘을 가해 튕기게
rb.AddExplosionForce(cutForce, hull.transform.position, 1f);
//레이어 바꿔서 계속 잘리는 거 방지(소리엉킴)
hull.layer = LayerMask.NameToLayer("Default");
Destroy(hull, 3f); // 3초 뒤 삭제
}
}
@@ -7,13 +7,6 @@ public class Spawner : MonoBehaviour
public float beat = 1;
float timer = 0f;
void Start()
{
}
// Update is called once per frame
void Update()
{
if(timer > beat)
@@ -21,10 +14,14 @@ public class Spawner : MonoBehaviour
int c = Random.Range(0, 2);
int p = Random.Range(0, 4);
GameObject obj = Instantiate(cube[c], point[p]); //beatBox를 랜덤하게 생성
obj.transform.localPosition = Vector3.zero; //박스의 벡터(방향을 초기화)
obj.transform.Rotate(transform.forward, 90 * Random.Range(0, 4)); //박스의 회전(방향, 회전률)
GameObject obj = Instantiate(cube[c], point[p].position, point[p].rotation);
//GameObject obj = Instantiate(cube[c], point[p]); //beatBox를 랜덤하게 생성
obj.transform.Rotate(transform.forward, 90 * Random.Range(0, 4));
//obj.transform.localPosition = Vector3.zero; //박스의 벡터(방향을 초기화)
//obj.transform.Rotate(transform.forward, 90 * Random.Range(0, 4)); //박스의 회전(방향, 회전률)
timer -= beat;
@@ -0,0 +1,23 @@
using UnityEngine;
//검 끝에 붙일 스크립트
public class VelocityEstimator : MonoBehaviour
{
//속도값 전달용
public Vector3 Velocity { get; private set; }
private Vector3 previousPosition;
private void OnEnable()
{
previousPosition = transform.position;
}
//물리 연산 주기에 맞춰 속도 계산
private void FixedUpdate()
{
//(현재 위치 - 이전 위치) / 시간 = 속도
Velocity = (transform.position - previousPosition) / Time.fixedDeltaTime;
previousPosition = transform.position;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c611db3f29508604b8d70ad30f9b4f3c