Sprite의 모양
렌더링 최적화
만들어만 놓고 쓰지는 않았던 게임 매니저를 써먹을 시간.
endblock 판정되면 게임 매니저 호출해서 다음 맵으로 넘겨달라고 하기.
다음 맵은 일단 5개밖에 없고 데모판이니까 배열 인덱스로 관리하면 되지 않을까

Observer 패턴이랑 State 패턴이 뭔데 씹덕아
다른 오브젝트의 스크립트에 있는 코루틴 호출하고 자기 자신은 enable 꺼버리면 코루틴 마저 돌아감?
이유
코루틴은 호출된 스크립트에 종속되지 않음
코루틴은 Unity의 MonoBehaviour에서 시작되지만, 실행 자체는 Unity의 코루틴 시스템에 의해 관리됩니다. 따라서 호출한 스크립트가 비활성화되더라도 이미 실행된 코루틴은 영향을 받지 않습니다.
호출된 오브젝트의 상태가 중요함
호출된 코루틴의 대상이 되는 오브젝트가 비활성화되면, 해당 오브젝트와 관련된 동작은 중단될 수 있습니다. 하지만 호출자 자체가 비활성화되는 것은 코루틴의 실행에 영향을 미치지 않습니다.
특정 동작을 1초 기다린 후에 실행시키려고 하는데 코루틴 말곤 답 없냐 이거
코루틴을 사용하지 않고도 특정 동작을 일정 시간 뒤에 실행하려면 여러 방법을 활용할 수 있습니다. 아래는 코루틴을 사용하지 않는 대안들입니다.
Invoke 메서드 사용Unity의 Invoke 메서드를 활용하면 간단하게 일정 시간 뒤에 함수를 호출할 수 있습니다.
void Start()
{
Invoke("DelayedAction", 1f); // 1초 후에 DelayedAction 호출
}
void DelayedAction()
{
Debug.Log("1초 후 실행!");
}
장점: 간단하고 읽기 쉽습니다.
단점: 인자가 있는 함수 호출은 지원하지 않으며, 캔슬링은 별도로 구현해야 합니다.
Timer 활용 (C# 기본 클래스)Unity의 Update를 사용해 타이머를 구현하는 방법입니다.
private float delay = 1f;
private float timer = 0f;
private bool startTimer = false;
void Update()
{
if (startTimer)
{
timer += Time.deltaTime;
if (timer >= delay)
{
ExecuteDelayedAction();
startTimer = false;
timer = 0f;
}
}
}
public void StartTimer()
{
startTimer = true;
}
void ExecuteDelayedAction()
{
Debug.Log("1초 후 실행!");
}
장점: 업데이트 루프에서 세밀한 시간 제어가 가능하며, 상태를 추적하기 쉽습니다.
단점:Update를 사용하기 때문에 불필요하게 호출될 수 있어 성능에 영향을 줄 수 있습니다.
UnityEvent와 람다 표현식 활용Unity의 UnityEvent 또는 간단한 람다를 활용하는 방법입니다.
void Start()
{
StartCoroutine(DelayedAction(() => Debug.Log("1초 후 실행!")));
}
IEnumerator DelayedAction(System.Action action)
{
yield return new WaitForSeconds(1f);
action.Invoke();
}
장점: 익명 함수나 람다를 쉽게 사용할 수 있어 유연함.
단점: 여전히 코루틴에 의존.
Task.Delay 사용 (비동기 프로그래밍)비동기 프로그래밍을 사용할 수 있다면, Task.Delay를 활용할 수 있습니다.
using System.Threading.Tasks;
async void Start()
{
await DelayAction();
}
private async Task DelayAction()
{
await Task.Delay(1000); // 1초 대기
Debug.Log("1초 후 실행!");
}
장점: 비동기 코드를 작성하기에 적합하며, 직관적.
단점: Unity의Update와 혼용할 때 주의가 필요.
애니메이션 타임라인에서 특정 시간 후에 이벤트를 발생시킬 수도 있습니다. 이 방식은 트리거로 활용될 수 있습니다.
| 방법 | 간단함 | 유연성 | 취소 가능 여부 | 주의점 |
|---|---|---|---|---|
| Invoke | 최고 | 낮음 | 없음 | 복잡한 로직에는 부적합 |
| Update + Timer | 보통 | 높음 | 가능 | 관리가 필요함 |
| Task.Delay | 보통 | 높음 | 가능 | 비동기 컨텍스트 주의 필요 |
| Animation/Event | 낮음 | 보통 | 없음 | 애니메이션 기반 설계 필요 |
Invoke 또는 Task.Delay 사용.Update와 타이머 직접 관리.상황에 따라 적합한 방법을 선택하면 됩니다!
다른 오브젝트의 스크립트에 있는 Invoke호출하고 자기 자신은 enable 꺼버리면 Invoke마저 돌아감?
Unity에서 다른 오브젝트의 스크립트에 있는 Invoke를 호출한 후, 호출한 스크립트(자기 자신)의 enabled를 끄더라도 Invoke는 정상적으로 실행됩니다. 이는 Invoke가 호출된 스크립트의 enabled 상태와는 독립적으로 Unity의 내부 타이머 시스템에서 관리되기 때문입니다.
Invoke("SceneManager.LoadScene(sceneList[currentSceneIndex])", 3.0f);
이렇게도 되는거임?
아니요, Invoke는 문자열로 메서드 이름을 받아 호출하기 때문에 SceneManager.LoadScene(sceneList[currentSceneIndex])처럼 직접 메서드 호출과 인자를 함께 전달할 수는 없습니다.
endblock일 때 게임을 끝내버리지 않으면서도
판정이 성공하면서도
이전에 0번째 블럭이었던 곳으로 다시 돌아가야 하는데
nextBlock은 서로 연결시켜 놓으면 player_move는 갈 것 같긴 한데
판정이 문제
offset 모드 켜져있으면 무조건 판정 성공으로 받아들이면 되지 않을까
void Update()
{
RaycastFrontAndBack();
CheckInbox();
if (isInBox) KeyPressJudge();
CheckMusic();
if (game_ongoing)
{
player_move(); // dspTime 기반 플레이어 이동
follow_show_player(); // 보여지는 플레이어
}
else
{
show_player.transform.position = transform.position;
show_player.transform.rotation = transform.rotation;
}
DebugText();
}
로직이 얽혀있어서 무작정 하기가 좀 그렇다
폭탄 해체하는 기분
private void OffsetTest()
{
if (missionBlockIndex == 2)
{
WakeUpAllBlocks();
missionBlockIndex = 0;
}
offsetTest.GetOneOffset(distanceToCenter);
}
코드 읽어보니까 이대로면 원래 돼야하는데? 왜 멈추지?

서로 연결 시켜줬더니 이동은 하는데
WakeUp이 안되네

로그 걸어놨는데 아예 호출을 안함
아 index가 2가 아니라 1이어야겠구나
갱신을 안해줬었네
while (turnOnBlock != null)
{
turnOnBlock.EnableCollider();
turnOnBlock = (turnOnBlock.prevNoteBlock != null)
? turnOnBlock.prevNoteBlock.GetComponent<NoteBlock>() : null;
}
이거 무한반복 돌겠네 생각하고 있었는데
직접 돌리니까 에디터가 고장나버림
1번째 블럭하고 2번째 블럭의 prev랑 next를 둘 다 서로로 지정해놨는데
그렇게 하면 안되고 1번째 블럭에는 지정을 풀어야될듯

그랬더니 wake up 호출되긴 하는데
2번째 블럭 콜라이더를 enable을 안해줌
private void WakeUpAllBlocks()
{
if (missionBlockCollider == null) return;
NoteBlock turnOnBlock = missionBlockCollider.GetComponentInParent<NoteBlock>();
while (turnOnBlock != null)
{
Debug.Log("Wake Up");
turnOnBlock.EnableCollider();
turnOnBlock = (turnOnBlock.prevNoteBlock != null)
? turnOnBlock.prevNoteBlock.GetComponent<NoteBlock>() : null;
if (turnOnBlock != null) Debug.Log("Next Block : " + turnOnBlock.noteBlockIndex);
}
}

뇌 좀 썼어야 했는데 로그를 보고 현상을 파악하려고만 했다
아무튼 원인은 알아냄
루프는 두 번 돌지만
turnOnBlock은 두번째엔 null이 된다
?? wake up이 두 번 호출됐다는 건 enablecollider도 두 번 호출됐다는거 아님?
두 번 호출됐다는 건 turnOnBlock이 null이 아닌 조건이 2번 있었다는거잖아
뭐임
private void OffsetTest()
{
if (missionBlockIndex == 1)
{
missionBlockCollider.GetComponentInParent<NoteBlock>().EnableCollider();
missionBlockCollider.GetComponentInParent<NoteBlock>().prevNoteBlock.GetComponent<NoteBlock>().EnableCollider();
missionBlockIndex = 0;
}
offsetTest.GetOneOffset(distanceToCenter);
}
몰라 여기서 해결하면 아무튼 되잖음
근데도 안됨

hitsuccess에서 missionindex를 더해버려서 인식이 안됐던거
그럼 missionBlockindex를 -1로 두면?
이번엔 재활성화가 안되네
private void OffsetTest()
{
if (missionBlockIndex == 0)
{
missionBlockCollider.GetComponentInParent<NoteBlock>().EnableCollider();
missionBlockIndex = 0;
}
else if (missionBlockIndex == 1)
{
missionBlockCollider.GetComponentInParent<NoteBlock>().EnableCollider();
missionBlockIndex = -1;
}
offsetTest.GetOneOffset(distanceToCenter);
}
그냥 이렇게 해결하자

아니 호출이 되셨잖아요 왜 안 켜주시는건데요
if (OffsetTestMode && game_ongoing) OffsetTest(); 애초에 game_ongoing을 박았던 이유가 첫 시작은 무조건 offset 0이니까 배제하려고 했던거고 이렇게 되면 제대로 작동하더라도 첫번째 블럭을 못 킴
두번째 블럭을 쳤을 때 첫번째 블럭까지 같이 활성화돼야됨 원래처럼
아예 활성화가 안되고 있다는게 문제지만 지금은
원인을 알아냈다
켠 다음에 바로 꺼버리는 거였음
private void OffsetTest()
{
if (missionBlockIndex == 0)
{
missionBlockCollider.GetComponentInParent<NoteBlock>().nextNoteBlock.GetComponent<NoteBlock>().EnableCollider();
missionBlockIndex = 0;
}
else if (missionBlockIndex == 1)
{
missionBlockCollider.GetComponentInParent<NoteBlock>().prevNoteBlock.GetComponent<NoteBlock>().EnableCollider();
missionBlockIndex = -1;
}
offsetTest.GetOneOffset(distanceToCenter);
}
그럼 자기꺼가 아니라 다른 놈 걸 켜버리면?

정상 작동 나이스
가 아니라 중간에 실패하면 둘 다 꺼지는 경우의 수가 생김
GameOver에는 아직 기존 WakeUp 밖에 안 들어있어서
2번째 블럭에서 죽으면 1번째 블럭이 enable되는데
역은 성립 안함
한참 우아한 해결책을 생각해봤는데, 좀 지저분한 해결책밖에 안 떠오름
GameOver()에서도 OffsetTest를 호출하는데,
상태 플래그와 함께 호출.
GameOver에서는 0, HitSuccess에서는 1
if (code == 1)
offsetTest.GetOneOffset((float)delta_dspTime);
code가 1일 때만 offset 구하고
아니면 일단 collider enable
offsetTest.GetOneOffset(distanceToCenter); 여기서 distanceToCenter 이게 레거시 이동 로직에만 있는 부분

원래는 nowDirection 기반으로 계산했었는데
이젠 delta_dspTime 기반으로 계산하면 될듯
delta_dspTime = AudioSettings.dspTime - lastBlockStartTime;
delta_dspTime 자체가 정확한 타이밍과의 오프셋을 의미하니까
offsetTest.GetOneOffset((float)delta_dspTime); 그냥 냅다 넣으면 되지 않나

되기야 하는데... 소수점 자릿수가 너무 수상하다.

얼불춤은 0.1초 정도 나왔음
그리고 얼불춤은 10번이 아니라 훨씬 많이 함

내 오프셋 측정은 0.01초가 나온다

ㅇㅋ 완벽하게 이해했어

전혀 아니잖니 이자식아

애초에 이동 로직이 잘못된건가 하고 의심했는데, 그건 아니고 그냥 보간 때문에 그렇게 보였던거.
분명 맞아야 되는건데 값이 이상하게 나오고 뭐가 진짜 맞는 값인지를 모르겠으니 어떻게 해야할질 모르겠다
일단 보류하고 itch.io같은 곳에 올렸는데 오프셋이 이상하게 나오면 그때 해결하는게 맞을듯
언어 변경
음악 볼륨 조절
효과음 볼륨 조절
효과음 변경
디버그 텍스트 활성화

오른쪽의 tif 파일을 메뉴 텍스처로 쓰려고 했는데 (스샷도 찌그러지네) 유니티에 넣었더니 왼쪽처럼 아예 이상하게 변해버림

그냥 win shift s로 스샷 찍고 넣었더니 멀쩡하게 잘 보인다. 파일 형식 문제였을지도?
근데 넣고보니까 맘에 안듦.

직접 만들어봐도 텍스처 없는게 훨씬깔끔한듯

kenney 확인해보니 크기는 48픽셀로 하는듯 이래도 모서리 둥글기 살아남나?
간격 지킬게 있어서 난 256 256으로 해야할듯

만들기 전에 느낌만 봤는데
나쁘진 않는데 샤프한 느낌이 떨어지는 느낌
세련되게 만드는게 어려운거라 둥글둥글하게 우회한거긴 한데
한 번 트라이만?
https://cactus.tistory.com/306
보기엔 예쁘고 일본어 폰트도 있어서 완벽한데

윽 구려

이래도 아직 뭔가 애매한데
여기서 만족해야 할듯
설정할 수 있는 내용물이 별로 없어서 그런 것도 있는 것 같음
체계도 없고 항목 몇 개만 덜렁 있으니까
디자인 자체로는 그냥저냥 평타임
UI가 굳이 특출날 필요도 없고 사실

아니 이거 왜 이러냐고

https://www.youtube.com/watch?v=y8-GWxmosxo
ㅇㅋ 여기 있었구나

드롭다운 개구리네
공수 좀 많이 들어가더라도
클릭하면 바뀌게 하고 싶음
디자인 구린게 문제가 아니라
기능상으로 드롭다운이 한 눈에 보고 바로 고를 수 있으니까 편한데
왜 레퍼런스 보면 다들 화살표나 클릭하면 바뀌는 걸로 했지 생각이 들긴 함
게임 패드 때문이라고 해도 그냥 클릭하면 열리게 하면 되는거 아닌가
취소 동작이 애매한가

아니네 드롭다운으로 한 게임도 있네
드롭다운 디자인 예쁘니까 채용

와 아이콘 진짜 너무 구린데 어떡하지 여기까지 손대고 싶진 않았는데

직접 만들었더니 더 쓰레기가 나왔다

디자인 기반으로 유니티에 만드는 것까지 완료
내일은 이 메뉴들을 위한 구현을 하면 된다
그냥 기능 연결 연결만 하면 다가 아니라
판정 로직을 잠시 멈춰야되는데
dspTime을 어떻게 멈출지도 문제
게임 종료 버튼도 만들어야됨