오늘 학습 키워드

The Stack 클론 코딩, TopDown 클론 코딩

오늘 학습 한 내용을 나만의 언어로 정리하기

The Stack

프로젝트 세팅

카메라

  • Perspective : 원근감을 표현하는 3D 카메라

라이트

  • Directional Light : 화면에 전역적으로 비춰지는 빛

블록 적층 로직

Lerp

  • “Linear Interpolation”의 약자. 선형 보간이라는 뜻
  • Unity에서 주로 사용되는 Mathf.Lerp, Vector3.Lerp는 시작 값과 끝 값 사이의 값을 t 비율 (0~1) 로 계산해 반환함
  • 기본 공식 : Lerp(a, b, t) = a + (b - a) * t

  • 색을 코드에서 바꿀 때에는 0~1사이의 값을 사용하기 때문에, 원하는 색을 RGB코드로 바로 넣으면 안되고 다음과 같이 넣어야 함
// 255로 나눠줘야함
Color color = new Color(250f / 255f, 100f / 255f, 48f/ 255f)
  • 색은 마테리얼이 담당함.

블록 이동, 배치 로직

핑퐁

  • Mathf.PingPong(t, length) : 0부터 지정해 준 사이즈까지 순환
void MoveBlock()
{
    blockTransition += Time.deltaTime * BlockMovingSpeed;
    float movePosition = Mathf.PingPong(blockTransition, BoundSize) - BoundSize / 2;
// 중심을 기준으로 전체 BoundSize만큼 왔다 갔다 할 수 있는 크기
}

  • 핑퐁 함수가 이해가 잘 안가서 챗지피티한테 시간별 분석 시킴… 움직임의 중심축은 0으로 두면서도 진동효과를 주는 방법이라고 함
  • 믿지마셈
  • 튜터님 피셜 : 아래 그래프가 맞다

파편 만들기

  • 기존의 있는 블록을 쪼개는게 아니고, 새로운 블록을 만들어서 눈속임
void CreateRubble(Vector3 pos, Vector3 scale)
{
    GameObject go = Instantiate(lastBlock.gameObject);
    go.transform.parent = this.transform;
 
    go.transform.localPosition = pos;
    go.transform.localScale = scale;
    go.transform.localRotation = Quaternion.identity;
 
    go.AddComponent<Rigidbody>(); // 중력 영향 받아서 떨어질 수 있도록
    go.name = "Rubble";
}
CreateRubble(
    new Vector3(
        isNegativeNum
        ? lastPosition.x + stackBounds.x / 2 + rubbleHalfScale // 마지막 블록의 중심점 +  생성될 블록 중심점 이동
        : lastPosition.x - stackBounds.x / 2 - rubbleHalfScale,
        lastPosition.y, 
        lastPosition.z),
    new Vector3(deltaX, 1, stackBounds.y));

점수와 콤보

  • PlayerPrefs로 저장

연출

게임 오버 시 연출

void GameOverEffect()
{
    int childCount = this.transform.childCount; // 하위 오브젝트 개수 (블럭, 러블들)
 
    for(int i = 1; i < 20; i++)
    {
        if (childCount < i) break;
 
        GameObject go = transform.GetChild(childCount - i).gameObject; // childCount에서 i를 빼는 이유는, 뒤에서부터 순환하려고
        if (go.name.Equals("Rubble")) continue;
 
        Rigidbody rigid = go.AddComponent<Rigidbody>();
 
		// 힘 줘서 날려버리기
        rigid.AddForce(
            (Vector3.up * Random.Range(0, 10f) + Vector3.right * (Random.Range(0, 10f) - 5f)) * 100f);
    }
}

파티클 시스템

  • 파티클 시스템 : VFX를 구현하는데 사용되는 도구.

  • Start LifeTime : 파티클 하나 하나의 생존 시간 설정

  • Start Speed : 파티클 이동속도 설정

  • Start Size : 파티클 크기 설정

  • Shape : 파티클이 발사되는 모양

포스트 프로세싱

  • 포스트 프로세싱 : 게임 렌더링이 완료된 화면에 추가적으로 그래픽 효과를 적용함. 렌더링 파이프라인 마지막 단계에서 이루어짐.

  • 사용한 방법

    1. 빈 오브젝트에 PostProcessVolume 컴포넌트를 붙이고, Add Effect로 원하는 이펙트를 설정
    2. PostProcess 레이어를 생성해서, 1에서 생성한 오브젝트에 설정해줌
    3. 메인 카메라로 가서, PostProcessLayer 컴포넌트를 붙이고, Layer를 PostProcess로 설정하면 끝
  • 주요 기능

    • 블룸
    • 색 보정
    • 초점 효과
    • 모션 블러
    • 환경 광 차폐
    • 비네트
    • 색수차
    • 렌즈 왜곡
  • 장점

    • 시각적 품질 향상
    • 감정, 분위기 전달
    • 유연성
  • 단점/주의점

    • 성능 문제
    • 과도한 사용 주의
    • 효과의 우선순위

TopDown

스프라이트 관리 + 플레이어 이동 구현

스프라이트

  • PPU 16으로 한 이유 : 에셋이 하나당 16 * 16이라서 16으로 하면 딱 1 unit에 에셋 하나가 알맞게 들어감

플레이어

  • [SerializedField] 를 하면, private여도 유니티 인스펙터 창에서 수정 가능함
  • Mathf.Atan2(y, x) : y랑 x 주면 그 사이 각을 구해냄. 그 값은 라디안으로 줌. Degree로 바꿔주는게 편함
  • 두 벡터를 빼는 이유 :

// 넉백 구현부분
public void ApplyKnockback(Transform other, float power, float duration)
{
    knockbackDuration = duration;
    knockback = -(other.position - transform.position).normalized * power;
}
 
// (other.position - transform.position) : transform이 other를 보는 방향
// -(other.position - transform.position) : 넉백은 보는 방향의 반대로 적용해야 함
// ... normalized : 벡터를 방향은 유지하되, 크기가 1인 단위 벡터로 변환함
// ... * power : 단위벡터 * 밀려나는 힘

Fixed Delta Time과 Delta Time의 차이 Fixed Update와 Update 간의 관계

  • 정리 :

    • Fixed Delta Time은 Fixed Update 내에서 사용됨.
    • Fixed Update 에서는 일반 Delta Time에 접근이 불가능하지만, Update 에서는 Fixed Delta Time에 접근이 가능함.
    • 따라서, 그냥 DeltaTime으로 쓰는게 안전하다. (Fixed Update에서 deltaTime = fixedDeltaTime)
  • 입력 처리 : Edit > Project Settings > Input Manager > Axes 에서 설정 가능

충돌 처리 및 맵 구성

타일맵

  • 타일맵 : 격자 기반 맵을 생성하고 관리할 수 있는 기능. 타일이라는 작은 사각형 조각들을 사용해 게임 맵을 효율적으로 구성할 수 있음
  • 주요 구성 요소
    • 타일 : 개별 타일 맵을 구성하는 가장 작은 단위
    • 타일맵 : 여러 타일이 배치된 2D 격자
    • 그리드 : 타일맵을 배치하는 기반 격자 구조

애니메이션 처리

  • 문자열 직접 비교보다 숫자 비교가 나음
private static readonly int IsMoving = Animator.StringToHash("IsMove");
private static readonly int IsDamage = Animator.StringToHash("IsDamage");
animator.SetBool(IsDamage, true); // 이런식으로 쓸 수 있음

스탯

// 최소 1에서 100까지 설정 할 수 있다는 의미
[Range(1, 100)][SerializeField] private int health = 10;

원거리 공격

  • Animator의 Parameter에서 Trigger는 한 번 켜졌다 알아서 꺼지는 역할임
public override void Attack()
{
    base.Attack();
 
    float projectileAngleSpace = multipleProjectilesAngle; // 탄 퍼짐 각도
    int numberOfProjectilePerShot = numberOfProjectilesPerShot; // 탄환의 개수
 
    float minAngle = -(numberOfProjectilePerShot / 2f) * projectileAngleSpace; // 최소 
 
    for (int i = 0; i < numberOfProjectilesPerShot; i++)
    {
        float angle = minAngle + projectileAngleSpace * i;
        float randomSpread = Random.Range(-spread, spread);
        angle += randomSpread;
        CreatProjectile(Controller.LookDirection, angle);   
    }
 
}

잊지 말자

  1. PlayerPrefs는 보안 수준이 높지 않다. 비밀번호 같은거 저장하지 마셈
  2. GetComponentInChildren<>()의 매개변수로, 꺼져있는 오브젝트도 넣을지 말지 하는 bool 값을 넣을 수 있다.
  3. transform 안에 있는 Find 함수는 경로로 오브젝트를 찾는다.
  4. Quaternion * vector 는 되지만, 그 반대는 안된다. 교환법칙 성립 안함!

내일 학습 할 것은 무엇인지

내일은 진짜 강의 끝낸다…