241222

lililllilillll·2024년 12월 22일

개발 일지

목록 보기
28/350

✅ 오늘 한 일


  • C# 교과서 읽기
  • Project Etude


📝 배운 것들


🏷️ C# : 메서드 이름 간단하게 줄여서 쓰는 방법

using으로 별칭 지정

using printf = System.Console.WriteLine;

람다 표현식으로 간단히 지정

var printf = (string msg) => Console.WriteLine(msg);

Action<T> 대리자에 넣어서 사용

Action<string> printf = Console.WriteLine;

간단한 메서드 정의

static void printf(string msg) => Console.WriteLine(msg);


🎮 Project Etude


판정 문제

트리거 엔터하면 힙이나 리스트에 들어감
트리거 엑시트하면 빠져나오는데 미션 인덱스가 남아있으면 게임오버
키 누르면 미션 인덱스 찾아서 삭제

정도를 생각하고 있었는데

다시 찬찬히 생각해보고 검토해보니까 그게 문제가 아니었음
맨 처음 계획은 행 순서에 따라 z축 위치를 달라지게 해서 raycast 판정이 제대로 되게 하는거였는데
아직 z축 위치 반영을 MapEditor.cs에 안 넣어줘서 그랬음
200 프레임 넘겨서 개빠른것도 아닌데 판정 문제가 일어날리가 없지
블럭 반절도 안 넘기는데 게임오버 나는건 뭔가 이상했잖아
키 입력도 프레임 단위로 받는데 말이야

안 갈아엎어도 된다

처음에 생각했던 z축 기반 순서로 하면 무리 없이 작동한다.
나중에 엄청 빨라지면 collider 늘린 후에 계산식 살짝만 바꿔주면 될거임.
그리고 블럭 히트 success했으면 collider도 꺼줘야 됨. 원래 계획 그거였다.
(근데 이건 나중에 hit effect 종류도 추가해야됨. 긴 블럭이라고 안 겹치는게 아니다.)

부동소수점으로 인한 ad-hoc

return directionVector * displacement + new Vector3(0, 0, 0.000001f * (NoteAllocateIndex + 1));

0.000001f * (NoteAllocateIndex + 1) 이 부분에 1 더한 이유
: 1 안더하니까 부동 소수점 연산때문인지 첫번째랑 두번째 블럭이 둘 다 z축 좌표가 0이 됐음.

과하게 후한 판정 해결

1/16 박자 블럭 주루룩 깔아놓고 실험했는데
첫 시작에 방향키 연타하니까 2번째 블럭까지 바로 성공

Debug.DrawRay(frontPos, rayDirection * rayLength, Color.red);
Debug.DrawRay(backPos, rayDirection * rayLength, Color.blue);

DrawRay로 확인해봤더니
판정 허용치가 태평양보다 넓다

범인은 Scale이었다.

float boundSize = transform.GetComponent<SpriteRenderer>().bounds.size.x * transform.localScale.x;

Scale 곱해줬더니 무난히 해결

콜라이더 끄기, 게임 오버되면 다시 켜기

    public void DisableCollider()
    {
        GetComponentInChildren<BoxCollider>().enabled = false;
    }

활성화 꺼버렸더니
게임 오버 후 다시 돌아왔을 때 게임 시작 다시 못하는 문제 발생
게임 오버가 되면 그동안 껐던 콜라이더들을 다시 켜줘야됨

다행히도 각 블럭들끼리는 다 연결해놨으니까,
게임 오버됐던 마지막 블럭부터 다시 다 켜주면 됨.

    private void GameOver()
    {
        NoteBlock turnOnBlock = missionBlockCollider.GetComponentInParent<NoteBlock>();
        while (turnOnBlock.noteBlockIndex != 0)
        {
            turnOnBlock = turnOnBlock.prevNoteBlock.GetComponent<NoteBlock>();
            turnOnBlock.EnableCollider();
        }

기존 정보 이용해서 게임 오버에 초기화 로직 추가

곡 구하기

곡 만들고
맵 만들고
노트 피드백 효과 넣으면 데모 완성

유튜브 알고리즘이 추천해준 곡을 듣다가 든 생각인데
클래식보다 이런 곡들이 훨씬 플레이하는게 재밌을 거 같음
loyalty free 음악 중에 흥겨운 음악 있는지 찾아보는게 좋을듯

loyalty free 음악?

여러 사이트들 뒤져보고 다녔는데,
일단 돈을 받지 않는 것에는 그만한 이유들이 다 있고,
좀 쓸만한 것들은 구독을 해야 하는데
사실 그것들마저도 성에 안 찬다

ai 음악?

ai 음악 써도 로열티 지불해야 하는 건 똑같고,
원하는 퀄리티의 음악은 여기도 안 나옴.
그리고 멜로디에서 설명하기 힘든 불쾌한 골짜기가 느껴짐.

작곡?

어떤 느낌일까 맛만 봤는데
엄두가 안남

그냥 클래식 쓰는게 맞을듯

Bills 듣고 클래식 들었을 땐 뭐 이런 수면제가 다 있나 했는데
loyalty free 음악들 듣다가 클래식 다시 들으니까 시대를 초월한 명작들로 들림

cakewalk export시 소리 작음

소리 작게 나오는게 정상이라고 함
그래서 마스터링이라는 걸 해야 한다고 함

loudmax라는 리미터 플러그인으로 음량 높이기
b 누르면 플러그인 브라우저 나옴

버스를 추가하고
LoudMax를 버스에 끌어다 추가하고
Thresh라는 걸 -30db로 설정했더니 소리가 커졌다

해결 완료

bpm 일정하게 만들기

저번에 했던 스냅으로 시도해봤는데 안됨
사실 되는게 말이 안됐음 그냥 다른거 건드리다가 됐던거 같음
snap은 그냥 마디마다 마우스 조절하는 뭐 그런거 같은데

아무튼 views > tempo > 우클릭 > Clear All > 왼쪽 Tempo Track에서 BPM 수정



📖 C# 교과서


40 대리자

40.1 대리자(위임/델리케이트)

대리자는 delegate 키워드를 사용하여 만든다.
대리자는 함수 자체를 데이터 하나로 보고 다른 메서드를 대신 실행하는 기능이다.
(간단하게 말하면 함수 포인터에 좀 더 기능 추가된거)

class DelegateDemo
{
    static void Hi() => Console.WriteLine("안녕하세요.");

    delegate void SayDelegate();

    static void Main()
    {
    	// 방법 1
        SayDelegate say = Hi;
        say();

		// 방법 2
        var hi = new SayDelegate(Hi);
        hi();
    }
}

Hi 대신 say로 호출할 수 있다.

40.2 대리자를 사용하여 메서드 대신 호출하기

대리자를 생성했다면 해당 대리자를 사용하여 구조가 동일한 다른 메서드를 대신 호출할 수 있다.

대리자 변수 = new 대리자(메서드이름);

대리자 형식 변수에 메서드를 등록하는 코드
(간단하게 new 대리자() 키워드는 생략하고 함수 이름만 써서 등록할수도 있다)

대리자 변수 += new 대리자(메서드이름);

대리자에는 += 연산자를 사용하여 대신할 메서드를 하나 이상 등록할 수 있다.

대리자로 함수 대신 호출

class DelegateNote
{
    delegate void SayPointer();
    static void Hello() => Console.WriteLine("Hello Delegate");

    static void Main()
    {
        SayPointer sayPointer = new SayPointer(Hello); // 실행할 함수 전달

        sayPointer(); // 대리자 변수에 괄호 붙여서 메서드 호출
        sayPointer.Invoke(); // Invoke() 붙여서 명시적 호출
    }
}

invoke()는 붙여도 되고 안 붙여도 됨

이름 없는 메서드를 대신 호출

class AnonymousDelegate
{
    delegate void SayDelegate();
    static void Main()
    {
        // delegate 키워드로 함수를 바로 정의해서 사용
        SayDelegate say = delegate ()
        {
            Console.WriteLine("반갑습니다.");
        };
        say();
    }
}

익명 메서드 만들어서 넣을 수도 있다
근데 이거보단 람다식을 자주쓴다고 저자가 언급

람다식?
(입력 파라미터) => { 실행문장 블럭 };

강력한 형식의 대리자

> public delegate double DelegateType(double x, double y);
> DelegateType pow = Math.Pow;
> double actual = pow(2, 10);
> actual
1024

매개변수랑 반환 값만 맞으면 Math.Pow 같은 것도 가져다 넣을 수 있다.

40.3 대리자를 사용하여 메서드 여러 개를 다중 호출하기

GoHome go = new GoHome(CarDriver.GoLeft);
go += new GoHome(CarDriver.GoForward);
go += new GoHome(CarDriver.GoRight);
go += new GoHome(CarDriver.GoLeft);
go += new GoHome(CarDriver.GoLeft);
go();

대리자에 메서드 여러개 묶어놓고 한 번에 쓸 수 있다.
매개변수와 반환값은 같아야 함.

40.5 메서드의 매개변수에 대리자 형식 사용하기

class DelegateParameter
{
    delegate void Runner();

    static void Main()
    {
        RunnerCall(new Runner(Go));
        RunnerCall(new Runner(Back));
    }

    static void RunnerCall(Runner runner) => runner();
    static void Go() => Console.WriteLine("직진");
    static void Back() => Console.WriteLine("후진");
}

new Runner()은 생략하고 메서드만 바로 넣어도 됨

40.6 Action, Func, Predicate 대리자

  • Action 대리자: 반환값이 없는 메서드를 대신 호출한다.
  • Func 대리자: 매개변수와 반환값이 있는 메서드를 대신 호출한다.
  • Predicate 대리자: T 매개변수에 대한 bool 값을 반환하는 메서드를 대신 호출한다.

닷넷 API에 내장된 유용한 제네릭 대리자들이다.

Action<T> 대리자 사용하기

> Action<string> printf = Console.WriteLine;
> printf("메서드 대신 호출");
메서드 대신 호출

Func<T> 대리자 사용하기

> Func<int, int> abs = Math.Abs;
> abs(-10)
10

Func<매개변수형식, 반환값형식>으로 특정 메서드 또는 익명 메서드를 대신 호출할 수 있는 대리자 개체를 만들 수 있다.

> Func<double, double, double> pow = Math.Pow;
> pow(2, 20)
1048576

입력 매개변수를 2개 받는 Math.Pow() 메서드를 대신 호출하는 pow() 함수 만들기

> Func<int, int> anonymous = delegate (int x) { return x * x; }; // 익명 메서드 담기
> anonymous(2)
4
> Func<int, double> lambda = x => x / (double)2; // 람다 식 담기
> lambda(3)
1.5

Func 대리자와 람디 식을 사용하면 LINQ처럼 C#에서 함수형 프로그래밍 스타일로 개발할 수 있다.
Func 대리자는 이미 있는 메서드 이외에 익명 메서드 및 람다 식을 담아 사용할 수 있다.

Predicate 대리자 사용하기

> Predicate<string> isNullOrEmpty = String.IsNullOrEmpty;
> isNullOrEmpty("Not Null")
false
> Predicate<Type> isPrimitive = t => t.IsPrimitive;
> isPrimitive(typeof(int))
true

Predicate<T> 대리자는 T를 매개변수로 받아 어떤 로직을 수행한 후 그 결과를 bool 형식으로 반환하는 메서드를 대신 호출

40.7 메서드의 매개변수로 메서드 전달하기

class PassMethodAsParameter
{
    static int StringLength(string data) => data.Length;

    static void StringLengthPrint(Func<string, int> stringLength, string message)
        => Console.WriteLine($"메시지의 크기는 {stringLength(message)}입니다.");

    static void Main() => StringLengthPrint(StringLength, "안녕하세요.");
}

닷넷에 내장된 제네릭 대리자인 Func를 사용하면 메서드의 매개변수로 메서드 이름 자체를 지정해서 넘겨줄 수 있다.

Expression<T>

Func<T>가 바로 실행 가능한 대리자 개체를 생성한다면,
Expression<T>는 대리자 구문 자체를 담은 개체를 만들고, 이를 사용하려면 Compile() 같은 추가 메서드를 호출하여 대리자 개체를 만든다.
이게 뭔지 알려면 Expression Tree 개념을 알아야 한다고 함.

41 이벤트

이벤트는 메서드 등록과 해지만 가능하고 직접 호출은 불가능한 delegate이다.

42 클래스 기타

42.1 부분 클래스

파일 하나에 클래스 구현하는게 힘들면
partial 키워드를 붙여 CS 파일 여러 개로 클래스를 분할할 수 있다.

같은 파일 내에서 멤버나 메서드를 동일한 이름의 클래스에 나누어 관리할 때도 사용할 수 있다.

public partial class Person
{
	public string Name { get; set; }
    public int Age { get; set; }
}

public partial class Person
{
	public void Print() => Console.WriteLine($"{Name} : {Age}");
}

42.2 정적 클래스

정적 클래스

  • static 키워드를 붙임
  • static 멤버만 가짐
  • 인스턴스화될 수 없음
  • 유틸리티 클래스 용도로 사용
  • 팩터리 클래스

42.4 함수형 프로그래밍 스타일: 메서드 체이닝

> class Point
. {
.     public readonly int x;
.     public readonly int y;
.     public Point(int x, int y)
.     {
.         this.x = x;
.         this.y = y;
.     }
. 
.     public Point MoveBy(int dx, int dy)
.     {
.         return new Point(x + dx, y + dy);
.     }
. }
> 
> var p = (new Point(0, 0)).MoveBy(10, 10).MoveBy(20, 20).MoveBy(30, 30);
> $"X : {p.x}, Y : {p.y}"
"X : 60, Y : 60"

메서드의 반환값을 자신의 클래스 형식으로 지정하면 메서드를 계속 반복해서 호출하는 함수형 프로그래밍 스타일을 제공할 수 있다.



profile
너 정말 **핵심**을 찔렀어

0개의 댓글