Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

동구의_C# & Unity_개발일지

2024.02.05 내일배움캠프 30일차 TIL_Unity (숙련, 알고리즘) 본문

Unity

2024.02.05 내일배움캠프 30일차 TIL_Unity (숙련, 알고리즘)

mongle_0l 2024. 2. 5. 09:59

유니티 숙련 주차에 이어 2D에서 3D로 넘어간다!

강의를 들으며 개인과제을 하였다.

강의로 3D게임을 만들며 개인과제로 ATM 시스템을 만들어 볼 것이다.

필수요구사항에 ATM 화면 구성을 구현하였다. 

 

필수요구사항

1. ATM 화면 구성 (완료)

2. 입금 기능 (미완료)

3. 출금 기능 (미완료)

 

선택요구사항

1. 통화 단위 적용 (난이도 - ★☆☆☆☆)

2. 금액 입력시 숫자만 입력 (난이도 - ★☆☆☆☆)

3. 로그인 기능 (난이도 - ★★☆☆☆)

4. 회원가입 (난이도 - ★★★★☆)

5. 송금 기능 (난이도 - ★★★★☆)


알고리즘 코드카타 16일차

정수 내림차순으로 배치하기

문제 설명
함수 solution은 정수 n을 매개변수로 입력받습니다. n의 각 자릿수를 큰것부터 작은 순으로 정렬한 새로운 정수를 리턴해주세요. 예를들어 n이 118372면 873211을 리턴하면 됩니다.

 


3D 게임 기초 개발

Survial 프로젝트 셋팅
프레이어 만들기
플레이어 상태 및 UI
플레이어 데미지 처리

 

01. 핵심 내용

1-1) Light 컴포넌트

  1. 라이트 소스: 게임 또는 3D 랜더링에 광원을 추가하는 데 사용됩니다. 이것은 특정 위치 또는 방향에서 발생하는 빛을 나타냅니다.
  2. 유형: 라이트는 여러 유형이 있습니다. 이러한 유형 중 몇 가지는 다음과 같습니다:
    • 점 광원(Point Light): 특정 지점에서 모든 방향으로 빛을 발산하는 라이트입니다.
    • 방향성 라이트(Directional Light): 특정 방향에서 모든 객체를 비추는 라이트입니다. 이 라이트는 거리에 관계없이 동일한 강도로 모든 객체를 비춥니다.
    • 스포트라이트(Spot Light): 특정 방향으로 원뿔형의 빛을 발산하는 라이트입니다.
    • 영역 라이트(Area Light): 특정 영역에서 발생하고 그 주변에 빛을 발산하는 라이트입니다(예: 실내 조명).
  3. 속성: 각 라이트에는 여러 속성이 있습니다. 이러한 속성에는 위치, 방향, 강도(intensity), 색상(color), 범위(range), 각도(angle) 등이 포함됩니다.
  4. 그림자: 라이트는 그림자를 생성할 수 있습니다. 라이트와 객체 사이의 관계에 따라 그림자는 라이트가 부딪히는 객체 뒤에 생성됩니다.
  5. 성능: 라이트는 렌더링 성능에 큰 영향을 미칩니다. 많은 라이트를 사용하면 특히 동적 그림자가 포함된 경우 렌더링 성능에 부정적인 영향을 미칠 수 있습니다. 따라서 최적화는 중요한 고려사항입니다.
  6. 빛 반사 및 산란: 라이트는 표면에 부딪히고 반사되거나 다른 방향으로 산란되어 재질과 표면의 실제성을 나타냅니다. 이러한 효과는 물리 기반 렌더링(PBR)에서 중요한 요소입니다.

1-2) 스카이박스

게임 세계의 배경을 둘러싸는 환경 매핑 기술입니다. 큐브 맵(Cube Map)과 구체형 스카이박스(Sphere Map) 등이 있으며, 주로 다음과 같은 특징을 가집니다:

 

  • 스카이박스는 6개의 텍스처로 구성된 큐브 맵 또는 하나의 구체로 텍스처가 매핑된 구체형 스카이박스로 구성됩니다.
  • Unity에서는 씬의 배경으로 사용되며, 게임 환경을 확장시키는데 활용됩니다.
  • 주로 하늘, 구름, 산 등의 자연적인 배경을 표현하는 데 사용됩니다.
  • 미리 만들어진 스카이박스를 사용하거나 직접 만들어서 Unity에서 적용할 수 있습니다.
  • 게임 중에 스카이박스를 동적으로 변경하여 낮과 밤 등의 시간대나 특정 이벤트에 맞게 배경을 변화시킬 수 있습니다.
  • 성능에 영향을 미치므로 최적화에 주의해야 합니다.

1-3) Rigidbody - ForceMode

  1. Force: 힘을 지속적으로 적용합니다.
    • Rigidbody.AddForce(Vector3 force, ForceMode.Force);
  2. Acceleration: 가속도를 적용합니다. 이전 힘의 누적에 따라서 점진적으로 더 빠르게 움직이게 됩니다.
    • Rigidbody.AddForce(Vector3 force, ForceMode.Acceleration);
  3. Impulse: 순간적인 힘을 적용합니다. 짧은 시간에 갑작스러운 움직임이 발생합니다.
    • Rigidbody.AddForce(Vector3 force, ForceMode.Impulse);
  4. VelocityChange: 변화하는 속도를 적용합니다. 물체의 현재 속도를 변경하면서 움직입니다.
    • Rigidbody.AddForce(Vector3 force, ForceMode.VelocityChange);

이러한 ForceMode를 적절히 활용하여 게임 오브젝트에 원하는 물리적인 움직임과 효과를 부여할 수 있습니다.

 

02. 핵심 내용

2-1) 인터페이스 복습

인터페이스를 통해 클래스들은 공통적인 동작을 정의하고, 이러한 동작들을 구현하는 클래스들은 해당 인터페이스를 구현(implement)함으로써 공통 규약을 준수할 수 있습니다.

인터페이스를 설명하는 주요 특징은 다음과 같습니다.

 

  1. 추상화: 인터페이스는 추상적인 개념으로, 실제로 구현된 메서드가 없고, 메서드의 시그니처만을 가집니다. 따라서 인터페이스는 인스턴스화될 수 없으며, 구현체가 필요합니다.
  2. 메서드 시그니처: 인터페이스는 구현 클래스가 반드시 구현해야 하는 메서드들의 시그니처를 정의합니다. 메서드의 이름, 매개변수, 반환 타입이 포함됩니다.
  3. 다중 상속 가능: 클래스는 하나의 클래스만 상속받을 수 있지만, 여러 인터페이스를 동시에 구현할 수 있습니다. 이를 통해 다중 상속을 흉내내는 것이 가능합니다.
  4. 강제적 구현: 클래스가 인터페이스를 구현하면, 인터페이스에서 정의한 모든 메서드를 반드시 구현해야 합니다. 이로 인해 클래스는 인터페이스에 정의된 동작을 강제로 구현하게 됩니다.
  5. 인터페이스 간 확장: 인터페이스는 다른 인터페이스를 확장(extends)할 수 있습니다. 이를 통해 더 큰 범위의 공통 동작을 정의할 수 있습니다.

03. 핵심 내용

3-1) Invoke

  • Invoke(string methodName, float time): 지정된 시간(time) 후에 지정된 메서드(methodName)를 실행합니다.
  • methodName: 실행할 메서드의 이름을 문자열로 지정합니다.
  • time: 메서드를 실행할 시간을 초 단위로 지정합니다.
using UnityEngine;

public class ExampleScript : MonoBehaviour
{
    private void Start()
    {
        Invoke("DelayedMethod", 2.0f);
    }

    private void DelayedMethod()
    {
        Debug.Log("This method is called after 2 seconds.");
    }
}

3-2) Invoke Repeating

  1. InvokeRepeating(string methodName, float time, float repeatRate): 지정된 시간(time) 후에 지정된 메서드(methodName)를 주기적으로 반복해서 실행합니다.
  2. methodName: 실행할 메서드의 이름을 문자열로 지정합니다.
  3. time: 메서드를 처음 실행할 때까지의 시간을 초 단위로 지정합니다
  4. .repeatRate: 메서드를 반복해서 실행할 주기를 초 단위로 지정합니다.
csharpCopy code
using UnityEngine;

public class ExampleScript : MonoBehaviour
{
    private void Start()
    {
        InvokeRepeating("RepeatingMethod", 2.0f, 3.0f);
    }

    private void RepeatingMethod()
    {
        Debug.Log("This method is called every 3 seconds after 2 seconds delay.");
    }
}

3-3) TryGetComponent

TryGetComponent는 Unity에서 사용하는 메서드로, 게임 오브젝트의 컴포넌트를 가져오는 기능을 제공합니다. 이 메서드를 사용하면 특정 컴포넌트가 게임 오브젝트에 연결되어 있는지 확인하고, 연결되어 있다면 해당 컴포넌트를 가져올 수 있습니다.

TryGetComponent 메서드의 형식은 다음과 같습니다.

public bool TryGetComponent<T>(out T component) where T : Component;
  • T: 가져오려는 컴포넌트의 타입입니다. MonoBehaviour를 상속한 컴포넌트는 모두 사용 가능합니다.
  • component: 컴포넌트를 가져올 때 사용되는 out 매개변수입니다.
# 사용법
using UnityEngine;

public class ExampleScript : MonoBehaviour
{
    private void Start()
    {
        // 게임 오브젝트에 Rigidbody 컴포넌트가 있는지 확인하고 가져옵니다.
        Rigidbody rb;
        if (TryGetComponent<Rigidbody>(out rb))
        {
            // Rigidbody 컴포넌트가 있다면 해당 컴포넌트로 원하는 동작을 수행합니다.
            rb.AddForce(Vector3.up * 100f);
        }
        else
        {
            // Rigidbody 컴포넌트가 없다면 다른 처리를 수행합니다.
            Debug.Log("Rigidbody component not found.");
        }
    }
}

TryGetComponent는 컴포넌트가 없어도 예외를 발생시키지 않고, 컴포넌트가 있으면 해당 컴포넌트를 가져와서 사용할 수 있습니다. 이를 통해 더 안전하게 컴포넌트를 가져오고 사용할 수 있습니다.


중요한 기능들을 정리해서 알아보자!
private void Move()
{
    Vector3 dir = transform.forward * curMovementInput.y + transform.right * curMovementInput.x;
    dir *= moveSpeed;
    dir.y = _rigidbody.velocity.y;

    _rigidbody.velocity = dir;
}

 

플레이어를 이동시키는 기능을 구현한 코드이다.

현재 입력값(curMovementInput)을 기반으로 플레이어의 이동 방향을 결정한다.
dir *= moveSpeed : 이동 방향(dir)에 이동 속도(moveSpeed)를 곱하여 실제로 플레이어가 이동할 속도를 결정한다.
void CameraLook()
{
    camCurXRot += mouseDelta.y * lookSensitivity;
    camCurXRot = Mathf.Clamp(camCurXRot,minXLook,maxXLook);
    cameraContainer.localEulerAngles = new Vector3(-camCurXRot, 0, 0);

    transform.eulerAngles += new Vector3(0, mouseDelta.x * lookSensitivity, 0);
}
카메라의 회전을 제어하는 메서드이다.
private bool IsGrounded()
{
    Ray[] rays = new Ray[4]
    {
        new Ray(transform.position + (transform.forward * 0.2f) + (Vector3.up * 0.01f) , Vector3.down),
        new Ray(transform.position + (-transform.forward * 0.2f)+ (Vector3.up * 0.01f), Vector3.down),
        new Ray(transform.position + (transform.right * 0.2f) + (Vector3.up * 0.01f), Vector3.down),
        new Ray(transform.position + (-transform.right * 0.2f) + (Vector3.up * 0.01f), Vector3.down),
    };
    
    for(int i = 0; i < rays.Length; i++)
    {
        if (Physics.Raycast(rays[i], 0.1f, groundLayerMask))
        {
            return true;
        }
    }

    return false;
}
이 코드는 플레이어가 땅에 닿았는지 여부를 확인하기 위해 레이캐스트를 사용하는 메서드이다.

Physics.Raycast(rays[i], 0.1f, groundLayerMask) : 각 레이를 이용하여 땅과의 충돌을 검사한다. rays[i]는 현재 레이캐스트가 사용할 레이를 나타낸다. 0.1f는 레이의 최대 거리를 나타낸다. 레이가 땅과 충돌하는지 확인하기 위해 사용된다. groundLayerMask는 충돌을 검사할 레이어를 지정한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CampFire : MonoBehaviour
{
    public int damage;
    public float damageRate;

    private List<IDamagable> thingsToDamage = new List<IDamagable>();

    private void Start()
    {
        InvokeRepeating("DealDamage", 0, damageRate);
    }

    void DealDamage()
    {
        for(int i =0;i<thingsToDamage.Count;i++)
        {
            thingsToDamage[i].TakePhysicalDamage(damage);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if(other.gameObject.TryGetComponent(out IDamagable damagable))
        {
            thingsToDamage.Add(damagable);
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.gameObject.TryGetComponent(out IDamagable damagable))
        {
            thingsToDamage.Remove(damagable);
        }
    }

}
캠프 파이어을 만들 수 있는 코드이다.
public class DamageIndicator : MonoBehaviour
{
    public Image image;
    public float flashSpeed;

    private Coroutine coroutine;

    public void Flash()
    {
        if(coroutine != null)
        {
            StopCoroutine(coroutine);
        }

        image.enabled = true;
        image.color = Color.red;
        coroutine = StartCoroutine(FadeAway());
    }

    private IEnumerator FadeAway()
    {
        float startAlpha = 0.3f;
        float a = startAlpha;

        while(a > 0.0f)
        {
            a -= (startAlpha / flashSpeed) * Time.deltaTime;
            image.color = new Color(1.0f, 0.0f, 0.0f, a);
            yield return null;
        }

        image.enabled = false;
    }
}
이 코드는 데미지를 입었을 때 화면에 빨간색 플래시를 표시하는 기능을 구현한 코드이다.

private IEnumerator FadeAway() : 플래시가 서서히 사라지는 애니메이션을 구현하는 코루틴이다. 시작할 때, startAlpha값을 사용하여 초기 투명도를 설정한다. while 루프를 통해 투명도를 서서히 감소시킨다. 각 루프마다 flashSpeed와 Time.deltaTime 값을 곱하여 투명도를 조절한다. 투명도가 0 이하로 떨어지면 이미지를 비활성화하여 화면에서 숨긴다.

 


개인 과제 - 화면 구성


Git-Hub READM 작성

지금까지 이뤄왔던 ERADME을 전체적으로 작성해 보았다.