크래프톤 정글 TIL : 1025

lazyArtisan·2024년 10월 25일
0

정글 TIL

목록 보기
117/147

👾 나만무


그니까 2. 에서 얘기하는게 프리팹1에 프리팹2 할당해놓으면 씬에 프리팹2으로 만든 오브젝트 있을 때 무조건 프리팹1에 할당된다는 얘기지?

아니요, 그런 의미는 아닙니다.

인스펙터에서 프리팹1의 변수에 프리팹2를 미리 할당하는 것과 씬에 프리팹2로 만든 오브젝트가 생성되는 것은 별개의 개념입니다. Unity에서 프리팹2를 인스펙터에서 프리팹1의 변수에 미리 할당해 두었다고 해서, 씬에 있는 프리팹2로 만든 인스턴스가 자동으로 할당되는 것은 아닙니다.

정확히 말하자면:

  1. 프리팹1의 스크립트 변수에 프리팹2를 인스펙터에서 할당하면, 그 변수에는 프리팹 자체가 할당됩니다. 즉, 씬에서 인스턴스화된 프리팹2의 구체적인 오브젝트가 아니라, 프리팹 템플릿 자체를 참조하는 것입니다.

    • 이 경우, 프리팹1의 변수는 프리팹 템플릿을 참조하고 있으며, 씬에서 프리팹2 인스턴스가 생성되든 말든 프리팹1의 변수에 할당된 참조는 변하지 않습니다.
  2. 씬에서 프리팹2가 인스턴스화되면, 그것은 프리팹 템플릿으로부터 복사된 독립적인 오브젝트입니다. Unity는 자동으로 해당 인스턴스를 프리팹1의 변수에 연결해 주지 않습니다.

요약:

  • 프리팹1에 프리팹2를 미리 할당해두면, 그 변수는 프리팹 템플릿을 가리킵니다.
  • 씬에 있는 프리팹2의 인스턴스는 프리팹1의 변수에 자동으로 할당되지 않습니다.
  • 만약 프리팹2 인스턴스를 동적으로 할당하려면, 코드에서 GameObject.Find나 Instantiate 등을 사용해 직접 할당해야 합니다.

따라서 씬에 프리팹2가 있더라도, 그 오브젝트를 자동으로 프리팹1의 변수에 연결하려면 명시적으로 코드를 통해 할당해야 합니다.

다른 스크립트가 퍼블릭으로 선언돼있으면 굳이 다른 오브젝트에 달려있는거에 접근하는게 아니라 그 스크립트에 바로 접근할 수 있지 않음?

Unity에서는 스크립트가 퍼블릭으로 선언되어 있다고 해서, 해당 스크립트에 직접적으로 접근할 수 있는 것은 아닙니다. 스크립트 자체는 특정 게임 오브젝트에 붙어있는 컴포넌트로 동작하며, 이를 통해 접근할 수 있습니다.

SpectatorManager.GetComponent<SpectatorManager>().StartSpectating()가 필요한가?

Unity의 MonoBehaviour 스크립트는 게임 오브젝트에 부착된 컴포넌트로 동작합니다. 즉, 스크립트는 그 자체로 독립적인 객체가 아니라, 항상 어떤 게임 오브젝트에 부착된 상태로 존재합니다. 따라서 해당 스크립트에 접근하기 위해서는:
1. 그 스크립트가 부착된 게임 오브젝트를 찾아야 하고,
2. 해당 오브젝트의 컴포넌트로 있는 스크립트를 GetComponent로 가져와야 합니다.

SpectatorManager.GetComponent<SpectatorManager>().StartSpectating();SpectatorManager 게임 오브젝트에서 SpectatorManager 컴포넌트를 가져와 메서드를 호출하는 방식입니다.

스크립트에 직접 접근할 수 없다는 이유:

  • MonoBehaviour 스크립트는 게임 오브젝트에 종속적입니다. 스크립트가 실제로 메모리에 존재하려면, 그 스크립트를 포함한 게임 오브젝트가 씬에 존재해야 합니다.
  • 퍼블릭 변수는 외부에서 그 스크립트가 속한 게임 오브젝트의 컴포넌트에 접근했을 때 수정하거나 사용할 수 있도록 열려있다는 의미이지, 정적(static)으로 선언되지 않은 한 해당 클래스를 바로 접근할 수 있는 것은 아닙니다.

정적(static)으로 선언한 경우:

만약 정적(static)으로 선언된 변수나 메서드라면, 게임 오브젝트에 부착되지 않고도 해당 클래스에 바로 접근할 수 있습니다.

예를 들어, SpectatorManager 클래스에 있는 메서드가 정적으로 선언되어 있으면, 인스턴스화된 객체 없이도 클래스 자체로 접근할 수 있습니다.

public class SpectatorManager : MonoBehaviour
{
    public static void StartSpectating()
    {
        // 로직
    }
}

이렇게 하면 다음과 같이 직접적으로 접근할 수 있습니다:

SpectatorManager.StartSpectating();  // 바로 접근 가능

요약:

  • 퍼블릭 변수나 메서드가 있어도, 그 스크립트가 게임 오브젝트의 컴포넌트로 동작하는 한, GetComponent를 통해 해당 게임 오브젝트에 부착된 컴포넌트에 접근해야 합니다.
  • 정적(static)으로 선언된 변수나 메서드는 인스턴스화 없이 클래스 자체로 직접 접근할 수 있습니다.

그럼 다 static으로 선언하면 되는거 아님? static으로 선언하는거의 단점이 뭐야?

모든 것을 static으로 선언하는 것은 몇 가지 장점이 있긴 하지만, Unity와 같은 프레임워크에서 static을 남용하면 여러 가지 단점과 문제가 발생할 수 있습니다. 정적(static)으로 선언된 변수와 메서드는 인스턴스화 없이 전역적으로 접근할 수 있으므로 편리해 보일 수 있지만, 여러 가지 중요한 단점이 존재합니다.

1. 객체 지향적 설계의 파괴

  • Unity는 기본적으로 객체 지향 프로그래밍(OOP)을 기반으로 설계되었습니다. MonoBehaviour와 같은 컴포넌트 시스템도 객체 지향 원칙에 맞게 설계되었습니다.
  • Static은 객체 지향의 인스턴스화 개념을 무시하게 되어 유연한 객체 설계를 어렵게 만듭니다.
    • 예를 들어, 여러 개의 SpectatorManager가 필요할 때, 각각 다른 상태를 관리해야 할 경우가 있을 수 있습니다. 하지만 static을 사용하면 이런 상황에서 각각의 인스턴스를 만들고 독립적으로 사용할 수 없게 됩니다.

2. 상태 공유의 문제

  • static 변수는 모든 클래스 인스턴스가 동일한 값을 공유합니다. 이것은 간단한 상태 관리에는 유용할 수 있지만, 만약 여러 개의 인스턴스가 각각 독립된 상태를 가져야 한다면 문제가 발생할 수 있습니다.
  • 싱글톤 패턴을 사용하더라도, 전역 상태가 복잡해지면 어디서 상태가 변했는지 추적하기가 어려워질 수 있습니다.
  • 동시성 문제가 발생할 수 있으며, 여러 오브젝트가 동시에 같은 static 변수나 메서드를 변경할 경우 예기치 않은 충돌이 발생할 수 있습니다.

3. 메모리 관리 문제

  • static으로 선언된 변수는 애플리케이션이 실행되는 동안 메모리에 상주합니다. 그렇기 때문에 더 이상 필요하지 않은 상태라도 명시적으로 해제하지 않으면 메모리 누수가 발생할 수 있습니다.
  • 특히 Unity에서는 게임 오브젝트가 파괴되거나 씬이 전환되더라도 static 데이터는 유지되기 때문에, 씬 전환 시 의도치 않게 잘못된 데이터를 참조하거나 남아있는 경우가 발생할 수 있습니다.

4. 테스트 및 유지 보수의 어려움

  • 모든 것을 static으로 선언하면, 테스트가 어려워집니다. 단위 테스트 시에는 각 객체가 독립적으로 테스트될 수 있어야 하지만, static 상태는 서로 다른 테스트 케이스에서 공유되기 때문에 테스트를 독립적으로 수행하기 어려워집니다.
  • 디버깅할 때 상태가 여러 곳에서 공유되기 때문에 상태를 추적하는 것이 복잡해지고, 잘못된 상태 변경을 찾는 것이 어려워집니다.
  • 유지보수가 힘들어집니다. 클래스 내의 static 멤버는 코드베이스 어디에서나 접근 가능하므로, 전역적으로 어떤 코드가 상태를 변경하고 있는지 추적하는 것이 어려워질 수 있습니다.

5. Unity의 컴포넌트 시스템과 충돌

  • Unity에서 컴포넌트(예: MonoBehaviour)는 게임 오브젝트의 인스턴스에 종속적입니다. static을 사용하면 컴포넌트와 게임 오브젝트 간의 관계가 흐려지고, Unity의 장점인 씬 기반 설계와 게임 오브젝트의 독립적인 처리가 어려워집니다.
  • Unity의 Awake(), Start(), Update() 같은 라이프사이클 함수는 인스턴스 기반으로 호출되기 때문에, static 메서드나 변수로 처리하면 Unity의 기본 흐름을 따르기 어려워질 수 있습니다.

결론: static 남용은 주의해야 함

  • static으로 선언된 멤버는 전역적으로 모든 인스턴스가 공유하는 특성 때문에 사용이 편리할 수 있지만, 잘못 사용하면 코드의 유연성과 유지보수성을 해치고, 다양한 문제를 야기할 수 있습니다.
  • static을 사용하는 경우는 전역적으로 한 번만 초기화되고 상태 공유가 필요한 경우(예: 싱글톤 패턴) 등에 제한하는 것이 좋습니다.
  • 대부분의 경우 인스턴스화된 객체로 상태와 로직을 처리하는 것이 유연하고, Unity의 설계 원칙에 맞습니다.

따라서, 꼭 필요한 경우에만 static을 사용하고, 다른 경우에는 가능한 객체 지향적인 설계에 맞추는 것이 장기적으로 코드 유지보수나 확장성에서 유리합니다.

기타 버그 픽스

Spectator 생성됐는데도 안 보임 : CinemachineBrain에 Camera 컴포넌트가 없었음
처음에 클릭 안 하면 관전 대상으로 이동을 안 함 : Start()에서 관전 대상 초기화 시켜줌
카메라가 부드럽게 움직임 : CinemachineBrain의 Blend를 Cut으로 바꾸고 Virtual Camera의 Transition - Damping들을 전부 0으로 바꿔줌

🍚 코테 준비


SQL

2020-03-01 00:00:00
날짜 포맷 왜 이렇게 나오는거

SQL에서 날짜를 다룰 때, 기본 출력 형식은 YYYY-MM-DD HH:MI:SS 형태로 표시됩니다. 00:00:00은 시간 정보가 없는 경우 SQL 엔진이 자동으로 추가하는 자정 시간입니다.

날짜 형식을 다르게 출력하려면, SQL에서 DATE_FORMAT 함수 (MySQL) 또는 TO_CHAR 함수 (Oracle) 등을 사용해 형식을 지정할 수 있습니다. 예를 들어, 날짜 부분만 출력하고 싶다면 다음과 같은 방식으로 수정할 수 있습니다.

MySQL 예시:

SELECT DATE_FORMAT(HIRE_YMD, '%Y-%m-%d') AS HIRE_DATE
FROM DOCTOR;

Oracle 예시:

SELECT TO_CHAR(HIRE_YMD, 'YYYY-MM-DD') AS HIRE_DATE
FROM DOCTOR;

이렇게 하면 시간 정보 없이 2020-03-01처럼 날짜 부분만 표시할 수 있습니다.

where과 having의 차이

WHEREHAVING은 모두 SQL에서 조건을 지정할 때 사용되지만, 적용되는 상황과 용도가 다릅니다. 주요 차이점은 다음과 같습니다.

1. 사용 시점

  • WHERE: 행을 테이블에서 가져오는 단계에서 조건을 적용합니다. 즉, 데이터가 그룹화되기 전에 개별 행을 필터링합니다.
  • HAVING: 행을 그룹화한 후에 조건을 적용합니다. GROUP BY와 함께 주로 사용하여 집계된 결과에 조건을 걸 때 유용합니다.

2. 집계 함수 사용 가능 여부

  • WHERE: 집계 함수 (SUM, AVG, COUNT 등)를 사용할 수 없습니다. 개별 행 단위로 조건을 검사합니다.
  • HAVING: 집계 함수 결과에 조건을 걸 수 있습니다. 즉, 그룹화된 데이터에 대한 요약 정보를 바탕으로 조건을 지정할 수 있습니다.

예제

WHERE 사용 예제

부서가 'CS'인 의사들만 필터링하여 조회하려고 할 때:

SELECT DR_NAME, DR_ID, MCDP_CD, HIRE_YMD 
FROM DOCTOR
WHERE MCDP_CD = 'CS';

HAVING 사용 예제

각 부서별 의사의 수가 5명 이상인 부서만 조회하려고 할 때:

SELECT MCDP_CD, COUNT(DR_ID) AS NUM_DOCTORS
FROM DOCTOR
GROUP BY MCDP_CD
HAVING COUNT(DR_ID) >= 5;

여기서 HAVING 절은 COUNT(DR_ID) >= 5 조건을 그룹화한 결과에 적용하여, 부서별 의사 수가 5명 이상인 경우만 반환합니다.

그니까 where은 전체에서 조건 때리는거고 having은 group by에서 조건 때리는거라고 보면 되나?

네, 정확하게 이해하셨습니다!

  • WHERE데이터를 그룹화하기 전에 조건을 적용하여 테이블 전체에서 행을 필터링하는 역할을 합니다.
  • HAVING데이터를 그룹화한 후 각 그룹에 조건을 적용하는 데 사용됩니다. 그래서 GROUP BY와 함께 주로 사용되며, 집계 함수가 포함된 조건도 적용할 수 있습니다.

이렇게 보시면 됩니다:

  • WHERE: 개별 행을 필터링.
  • HAVING: 그룹화된 결과를 필터링


⚔️ 백준


📌 1697 숨바꼭질

from collections import deque
N, K = map(int,input().split())
visited=[False for _ in range(100001)]
q=deque()
q.append(N)
cnt=0
found = False
while not found:
    cur_depth_len=len(q)
    cnt+=1
    for _ in range(cur_depth_len):
        n = q.popleft()
        visited[n]=True
        if n==K:
            found=True
            break
        if 0 <= (n-1) and (not visited[n-1]): q.append(n-1)
        if (n+1) <= 100000 and (not visited[n+1]): q.append(n+1)
        if (2*n) <= 100000 and (not visited[2*n]): q.append(2*n)
print(cnt-1)

겨우 실버 1 문제인데 indexError 때문에 쩔쩔매다가
내일이 코테라 시간 아까워서 gpt한테 물어봄

이게 문제였음. visited를 K까지만 탐색하도록 하면 안됐던거임...

📌 7576 토마토

이거 통과시키게 바꾸니까 맞았습니다 뜸.

'토마토'가 하나 이상 있는거지
'익은 토마토'가 하나 이상 있는게 아니다.

from collections import deque
M,N=map(int,input().split())
L=[list(map(int,input().split())) for _ in range(N)]
q=deque() # 토마토 전염 시작
for i in range(N):
    for j in range(M):
        if L[i][j]==1:
            q.append((i,j))
dy=[1,-1,0,0]
dx=[0,0,1,-1]

already_ripe=False
if not q: already_ripe=True

day=-1
while q:
    depth_len = len(q)
    day+=1
    for _ in range(depth_len):
        y,x = q.popleft()
        for i in range(4):
            next_x, next_y = x+dx[i], y+dy[i]
            if 0<=next_x<M and 0<=next_y<N:
                if L[next_y][next_x]==0:
                    L[next_y][next_x]=1
                    q.append((next_y,next_x))

all_ripe=True
for i in range(N):
    for j in range(M):
        if L[i][j]==0:
            all_ripe=False

if not all_ripe:
    print(-1)
elif already_ripe:
    print(0)
else:
    print(day)

0개의 댓글