구현/개선한 사항

  • 코루틴을 사용하여 파티클의 지나친 Update 호출 방지
  • 3Match 후 노드가 자리를 채우러 내려갈 때, 코루틴을 사용한 딜레이를 추가하여 시인성 개선
  • 게임오버 화면 구현
    • 게임오버 후 메인으로 돌아갔다가 -> 다시 플레이 가능하도록
  • Node, Particle 생성을 Simple Factory 패턴을 적용한 별도의 Factory에서 전담하도록 하였다.
    • MonoBehavior를 상속하지 않은, Singleton Class에서 구현
    • MonoBehavior를 상속하지 않은 Class에서 Instantiate 사용시, GameObject.Instantiate() 로 사용해야함을 유의

앞으로 구현 필요한 사항

  • 이펙트 개선
    • Match시 어느 부분이 매치되었는지 표시해주는 이펙트 추가
  • 스테이지 기능 구현
    • 스코어 이외의, 특정 블록을 N개 획득하여 클리어하는 종류의 스테이지 추가
    • 메인에서 스테이지를 선택할 수 있도록.
    • 아이템/움직이지못하는 블록 등 추가
  • 사운드 추가
    • BGM, Effect

메모

알고리즘이나 유니티 공부도 좋지만, 게임개발도 하루에 일정 시간 이상은 할 수 있도록 하자.

문제

 

9465번: 스티커

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 n (1 ≤ n ≤ 100,000)이 주어진다. 다음 두 줄에는 n개의 정수가 주어지며, 각 정수는 그 위치에 해당하는 스티커의

www.acmicpc.net

 

풀이 전략

  • i번째 줄의 스티커를 뗄 때, 경우의 수는 다음과 같이 나타낼 수 있다
    • i-1번째 줄에서 스티커를 떼지 않음
    • i-1번째 줄에서 위쪽 줄 스티커를 뗌
    • i-1번째 줄에서 위쪽 줄 스티커를 떼지 않음.
  • 그리고, i-1번쨰 줄에서 다음 상태였을때, i번째 줄의 각 상태로 이행할 수 있다.
    • i-1번째 줄에서 떼지 않음/위쪽을 뗌 -> 아래쪽을 뗄 수 있음
    • i-1번째 줄에서 뗴지 않음/아래쪽을 뗌 -> 위쪽을 뗄 수 있음
    • 모든 경우의 수 -> 떼지 않음
      • 하지만 i-1번째 줄에서 떼지 않은 경우의 스코어 < i-1번째 줄에서 위쪽/아래쪽을 뗀 경우의 스코어이기 때문에, 위쪽/아래쪽을 뗀 경우만만을 비교한다
  • for루프를 사용하여 1~N번째 줄까지, 3가지 상태에 대하여 도출가능한 이전 상태 중 높은 값을 선택하는 식으로 계산을 이어나간다.
// 0 : 위쪽을 뗌
// 1 : 아래쪽을 뗌
// 2 : 떼지 않음
unsigned int stickerMap[100000][2];
unsigned int maxPoint[100000][3];

	for (int i = 1; i < N; i++)
	{
		//up
		if (maxPoint[i-1][1] > maxPoint[i-1][2])
		{
			maxPoint[i][0] = maxPoint[i - 1][1] + stickerMap[i][0];
		}
		else
			maxPoint[i][0] = maxPoint[i - 1][2] + stickerMap[i][0];
		//down
		if (maxPoint[i - 1][0] > maxPoint[i - 1][2])
		{
			maxPoint[i][1] = maxPoint[i - 1][0] + stickerMap[i][1];
		}
		else
			maxPoint[i][1] = maxPoint[i - 1][2] + stickerMap[i][1];
		//none
		if (maxPoint[i - 1][0] > maxPoint[i - 1][1])
			maxPoint[i][2] = maxPoint[i - 1][0];
		else
			maxPoint[i][2] = maxPoint[i - 1][1];
	}
  • 계산이 끝난 후, maxPoint[N-1]의 0~2번째 값 중 큰 값을 리턴한다.

전체 소스 코드

더보기
#include <iostream>
#include <memory.h>
using namespace std;

unsigned int stickerMap[100000][2];
unsigned int maxPoint[100000][3];

int testCases = 0;
int N = 0;

void run()
{
	maxPoint[0][0] = stickerMap[0][0];
	maxPoint[0][1] = stickerMap[0][1];
	maxPoint[0][2] = 0;

	for (int i = 1; i < N; i++)
	{
		//up
		if (maxPoint[i-1][1] > maxPoint[i-1][2])
		{
			maxPoint[i][0] = maxPoint[i - 1][1] + stickerMap[i][0];
		}
		else
			maxPoint[i][0] = maxPoint[i - 1][2] + stickerMap[i][0];
		//down
		if (maxPoint[i - 1][0] > maxPoint[i - 1][2])
		{
			maxPoint[i][1] = maxPoint[i - 1][0] + stickerMap[i][1];
		}
		else
			maxPoint[i][1] = maxPoint[i - 1][2] + stickerMap[i][1];
		//none
		if (maxPoint[i - 1][0] > maxPoint[i - 1][1])
			maxPoint[i][2] = maxPoint[i - 1][0];
		else
			maxPoint[i][2] = maxPoint[i - 1][1];
	}

}

int main()
{
	int numb, temp;
	//입출력 속도 증가
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> testCases;

	for (int i = 0; i < testCases; i++)
	{
		unsigned int tempMaxValue = 0;
		//input
		cin >> N;
		for (int j = 0; j < N; j++)
		{
			cin >> stickerMap[j][0];
		}
		for (int j = 0; j < N; j++)
		{
			cin >> stickerMap[j][1];
		}
		//run
		run();

		for(int j = 0; j < 3; j++)
		{
			if (tempMaxValue < maxPoint[N-1][j])
				tempMaxValue = maxPoint[N-1][j];
		}
		cout << tempMaxValue << "\n";
	}
	return 0;
}

 

Note

  • 이전 계산값을 사용하지 않기때문에, maxPoint를 배열 대신 변수 6개(current, next)로 사용하는 방법도 생각할만했다. 다만 그 경우 계산량이 부족하여, 문제의 메모리 제한(256MB)을 최대한 활용하는 방향으로 풀이했다.
  • 입력을 받아오는 양이 많기때문에, sync_with_studio, cin.tie(NULL)을 사용하여 입력을 최적화해주지 않으면 시간제한에 걸린다.

 

'Problem Solving > Backjoon' 카테고리의 다른 글

[백준][C++]2467 - 용액  (0) 2022.04.06
[백준][C++]1036 - 36진수  (0) 2022.03.08
[백준][C++]1562 - 계단 수  (0) 2022.03.08
[백준][C++]1043 - 거짓말  (0) 2022.03.03
[백준][C++]9663 - N-Queen  (0) 2022.03.02

코루틴이란?

코루틴을 사용하면 여러 프레임에 현재 함수의 작업을 분산하여 진행할 수 있다. 함수 실행 중 yield return을 사용하여 작업을 일시 중단한 후, 다른 프레임/시간에 제어권을 다시 넘겨받아 사용하는 식이다.

 

쓰레드와의 차이점은, 코루틴은 메인 스레드상에서 수행된다는 것이다. 

쓰레드의 경우 둘, 혹은 여러개의 쓰레드가 각자의 작업을 맡아 수행한다면,

코루틴의 경우 한 개의 쓰레드가 ABABABAB...형식으로 각각의 작업을 조금씩 나누어 수행한다.

쓰레드와 코루틴의 동작 차이

쓰레드

  • 별개 쓰레드에서 동작한다.
  • 동시에 여러개의 일을 처리할 수 있다.
  • 쓰레드는 선점형이다.
    • 일을 하는동안에도 다른 쓰레드가 돌아가고 있다.
    • 공유 메모리를 사용할 경우, 서로의 접근이 겹칠 수 있다.
      • Lock을 사용할 경우, 먼저 접근한 쓰레드가 우선적으로 사용하게 된다.
  • 비동기적이다 = 동시에 수행된다.

코루틴

  • 단일 쓰레드에서 동작한다.
  • 여러개의 일을 나누어, 하나씩 처리한다.
  • 코루틴은 비선점형이다.
    • 현재 코루틴 동작이 끝날때까지, 다른 코루틴들은 대기하게 된다.
    • 동시에 동작하지 않으므로, 메모리 접근이 겹칠 일이 없다.
      • 현재 동작하고 있는 코루틴이 메모리를 사용하고 있을 것이다.
  • 비동기적이 아니다 = 동시에 수행되지 않는다.

코루틴 선언법

    IEnumerator ParticleFadeOut()
    {
        SpriteRenderer Renderer = this.GetComponent<SpriteRenderer>();
        Color color = Renderer.color;
        while(color.a > 0.0f)
        {
            color.a -= 0.2f;
            Renderer.color = color;
            yield return new WaitForSeconds(0.1f);
        }
        
    }
  • 코루틴의 리턴 타입은 IEnumerator(열거자)로 선언한다.
  • 함수 동작 중 수행을 중단할 때, yield return으로 return한다.
    • 해당 방식으로 리턴할 경우, 그 위치에서 활동이 중단된 후 다시 돌아온다.
  • Yield의 종류는 다음과 같다
  • yield return null; -> 다음 프레임에 바로 시작
  • yield return new WaitForSeconds(float) -> 일정 시간동안 대기
  • yield return break; -> 코루틴 종료
  • yield return new WaitForEndOfFrame();
    • 모든 카메라와 GUI가 렌더링을 완료한, 프레임의 바로 직전에 다시 실행한다.
    • 현재 화면을 저장하여 활용할 필요가 있을때 사용할 수 있다.
  • yield return new WaitForFixedUpdate();
    • 다음 FixedUpdate()가 호출될 때까지 기다린다.
  • yield return new WaitUntil(조건문);
    • 해당 조건문이 true가 될 때까지 기다린다.
    • 조건문은 Update ~ LateUpdate 사이에 실행되어진다.
  • yield return new WaitWhile(조건문);
    • 해당 조건문이 true인 동안 기다린다
    • 조건문은 Update ~ LateUpdate 사이에 실행된다.

코루틴의 사용법

//호출방법
StartCoroutine(ParticleFadeOut());
StartCoroutine("ParticleFadeOut");

//중단방법
Coroutine particleCorutine;
particleCorutine = StartCoroutine(ParticleFadeOut());
StopCoroutine(particleCorutine);
  • 코루틴 호출시에는, 함수명을 호출하거나 함수명을 String으로 호출하는 형태로 선언한다.
  • 코루틴 중지시에는 Corutine 변수가 필요하므로, 중단할 필요가 있을 경우 미리 선언하여 코루틴 시작 시 저장해둔다.

주의 사항

  • 코루틴이 돌아가고 있던 객체가 파괴될 시에는 코루틴이 자동으로 중단되지만, 스크립트가 Disable될 때에는 중단되지 않는다.
    • 스크립트가 중단될 일이 있을 경우 별도의 종료 호출이 필요하다.
  • 오브젝트가 활성화 되어있지 않은 상태에서 코루틴이 활성화될 경우 에러가 발생할 수 있다.
    • 오브젝트를 생성(Instantiate)된 직후에 별도의 함수로 코루틴을 호출할 경우, 에러가 발생할 수 있다.
    • 프리팹으로 생성한 오브젝트에서 코루틴을 호출할 일이 있다면, Start()에서 코루틴을 호출하게 만들어 확실히 생성된 후 코루틴을 생성하게 만드는 것이 좋을것이다.

코루틴 사용의 장점

  • Update 루프를 최적화 할 수 있다.
  • 코드 정리를 수행할 수 있다.

코루틴 사용의 단점

  • 가비지가 많이 생성된다.
    • StartCoroutine을 사용할 때, 엔진 내부적으로 가비지가 다량 생성된다고 한다. -> 확인 필요
    • 위의 소스 코드를 확인하면, yield return new ~ 의 형식으로 대기가 진행됨을 알 수 있다. yield return이 진행될때마다 새로운 인스턴스이 생성되기 때문에, 가비지 생성 문제가 생기게된다.
      • 해당 문제를 해결하기 위해, Wait 인스턴스를 미리 만들어둔 뒤 캐싱하여 사용할 수 있다.

 

같이 보기

https://docs.unity3d.com/Manual/Coroutines.html

 

Unity - Manual: Coroutines

Coroutines A coroutine allows you to spread tasks across several frames. In Unity, a coroutine is a method that can pause execution and return control to Unity but then continue where it left off on the following frame. In most situations, when you call a

docs.unity3d.com

 

'Programming > Unity' 카테고리의 다른 글

유니티 오브젝트 파괴 / Gameobject.Destroy()  (0) 2022.03.02

문제

 

1036번: 36진수

첫째 줄에 수의 개수 N이 주어진다. 둘째 줄부터 N개의 줄에 수가 주어진다. N은 최대 50이고, 수의 길이도 최대 50이다. 마지막 줄에 K가 주어진다. K는 36보다 작거나 같은 자연수 또는 0이다.

www.acmicpc.net

풀이 전략

  • 36진수를 저장할 수 있는 구조체를 생성한 후, 해당 구조체를 사용하는 덧셈 함수를 만든다
  • 각 36진수를 입력받을때, sum 이외의 각 자리수가 Z로 바뀌었을때 더해지는 값을 저장할 배열을 생성하여 관리한다.

EX : 36진수 'Z120'

struct sum, digitsum[36]

sum += 'Z120'

 Z = (Z-Z) * 36^3 -> digitsum[Z->35]에 더함

1 = (Z-1) * 36^2 -> digitsum[1]에 더함

2 = (Z-2) * 36 -> digitsum[2]에 더함

0 = (Z-0) * 1 -> digitsum[0]에 더함

  • 모든 단어를 입력받은 후, 배열을 크기순으로 정렬하여 N개의 36진수를 SUM값에 더한다.

전체 소스 코드

더보기
//Backjoon Problem No.1036
//https://www.acmicpc.net/problem/1036
#include <iostream>
#include <string>
#include <queue>

using namespace std;

struct decimal36
{
	unsigned int cipher[60] = { 0, };
	int maxDigit = 0;

	bool operator<(const decimal36& t) const
	{
		if (maxDigit != t.maxDigit)
			return maxDigit < t.maxDigit;
		for (int i = maxDigit; i >= 0; i--)
		{
			if (cipher[i] != t.cipher[i])
				return cipher[i] < t.cipher[i];
		}
		return false;
	}
};

priority_queue<decimal36> decQueue;

decimal36 byCipher[36];
decimal36 total;

void arrangeCipher(decimal36 &de)
{
	for (int i = 0; i < 60; i++)
	{
		if (de.cipher[i] > 0)
		{
			de.maxDigit = i;
			if (de.cipher[i] > 35)  //
			{
				de.cipher[i+1] += (int)(de.cipher[i] / 36);
				de.cipher[i] = de.cipher[i] % 36;
			}
		}
	}
}

int charTode36(char c)
{
	int a = (int)c;
	if (a < 60) //0 = 48, 9 = 57;
		return a - 48;
	else //A = 65; Z = 90;
		return a - 55;
}
char cipherTochar(int i)
{
	if (i < 10)
		return (char)(i + 48);
	else
		return (char)(i + 55);
}

int N, K;

int main()
{
	//입출력 속도 증가
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N;

	for (int i = 0; i < N; i++)
	{
		string temp;
		cin >> temp;
		if (temp.size() == 1 && temp.at(0) == '0')
			continue;
		for (int j = 0; j < temp.size(); j++)
		{
			int target = charTode36(temp.at(j));
			byCipher[target].cipher[temp.size() - j - 1] += 35 - target;
			total.cipher[temp.size() - j - 1] += target;
		}
	}

	for (int i = 0; i < 36; i++)
	{
		arrangeCipher(byCipher[i]);
		decQueue.push(byCipher[i]);
	}
	cin >> K;

	for (int i = 0; i < K; i++)
	{
		decimal36 temp = decQueue.top();
		for (int j = 0; j < 60; j++)
		{
			total.cipher[j] = temp.cipher[j] + total.cipher[j];
		}
		decQueue.pop();
	}
	arrangeCipher(total);

	for (int i = total.maxDigit; i >= 0; i--)
	{
		cout << cipherTochar(total.cipher[i]);
	}

	return 0;

Note

 

'Problem Solving > Backjoon' 카테고리의 다른 글

[백준][C++]2467 - 용액  (0) 2022.04.06
[백준][C++]9465 - 스티커  (0) 2022.03.14
[백준][C++]1562 - 계단 수  (0) 2022.03.08
[백준][C++]1043 - 거짓말  (0) 2022.03.03
[백준][C++]9663 - N-Queen  (0) 2022.03.02

문제

 

1562번: 계단 수

첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

풀이 전략

  • 모든 경우의 수를 구하는 문제
  • 현재 수를 구할 필요는 없다(0~9까지의 숫자가 모두 등장하는지만을 확인하면 된다)
    • 비트마스킹으로 모든 수가 등장하는지를 확인한다.
  • 0~8에서는 다음 수로, 1~9에서는 이전 수로 이동할 수 있다(9->0, 0->9는 불가능하다)
  • 남은 인덱스, 현재 숫자, 비스마스크된 숫자를 함수 인자로 활용한다.
  • unsigned long run(int lastDigit, int idx, unsigned int cBitMask)
  • N의 값이 높을 경우 중복되는 함수 호출이 많아 속도를 확보하지 못하므로, 배열을 사용하여 메모이제이션해준다.
    • 답의 범위는 (0 ~ 999999999) * 2이므로, unsigned long으로 처리 가능하다.
      • *2 하는 이유는 경우의수를 구하는 과정에서 두 run의 값을 더하기 떄문이다.
    • bistmask : 0~1023, idx : 0~100, lastDigit : 0~9 이므로 1024*101*10 = 약 백만개의 변수를 사용한다.
      • 문제의 메모리 제한이 128MB이므로, 충분히 사용 가능하다.
    • 남은 숫자(idx)가 0이고, Digit이 모두 찬 경우(1023)는 경우의 수가 1이므로, 1로 선언하여둔다.
    • idx가 0인 나머지경우는 조건을 만족하지 못했으므로 0으로 처리하고, 남은 경우는 계산하여 저장한다.

전체 소스 코드

더보기
//Backjoon Problem No.1562
//https://www.acmicpc.net/problem/1562
//Mist16, 2022-03-08

#include <iostream>

using namespace std;

unsigned int answerBitmask = 0b1111111111;

unsigned long answerBoard[1024][101][10] = { 0, };

void initialize()
{
	for (int i = 0; i <= 9; i++)
	{
		answerBoard[answerBitmask][0][i] = 1;
	}
}


unsigned long run(int lastDigit, int idx, unsigned int cBitMask)
{
	unsigned long answer = 0;
	if (answerBoard[cBitMask][idx][lastDigit] != 0)
		return answerBoard[cBitMask][idx][lastDigit];
	if (idx == 0)
		return 0;
	//+1
	if (lastDigit < 9)
	{
		answer += run(lastDigit + 1, idx - 1, cBitMask | (1 << (lastDigit + 1)));
	}
	//-1
	if (lastDigit > 0)
	{
		answer += run(lastDigit - 1, idx - 1, cBitMask | (1 << (lastDigit - 1)));
	}	
	answer = answer % 1000000000;
	answerBoard[cBitMask][idx][lastDigit] = answer;
	return answer;
}

int main()
{
	unsigned long long tAnswer = 0;
	unsigned long answer = 0;
	int N;
	cin >> N;

	initialize();

	for (int i = 1; i <= 9; i++)
	{
		answer += run(i, N - 1, (1 << i));
		answer = answer % 1000000000;
	}
	cout << answer;
	return 0;
}

Note

  • 생각보다 큰 사이즈의 배열이라도 메모이제이션을 써야 할 경우가 있다.
    • 경우의 수는 크지만 실제 도출되는 계산의 양이 적은 경우는, STL map등을 사용하여 메모리를 절약할 수 있다.

문제

 

1043번: 거짓말

지민이는 파티에 가서 이야기 하는 것을 좋아한다. 파티에 갈 때마다, 지민이는 지민이가 가장 좋아하는 이야기를 한다. 지민이는 그 이야기를 말할 때, 있는 그대로 진실로 말하거나 엄청나게

www.acmicpc.net

 

풀이 전략

  • 한 파티 안에 진실을 아는 사람이 없다면, 그 파티에서는 거짓말을 할 수 있다.
  • 한 파티 안의 멤버 하나라도 진실을 안다면, 그 파티 사람들은 모두 진실을 아는 사람이 된다.
    • 바이러스 전염과 유사.
  • 각 파티를 set으로 구성하여, vector에 넣어둔다.
  • 진실을 아는 사람을 Queue(Vector로 구현함)에 넣어둔다.
  • Queue가 비거나 남은 파티가 없어질때까지, 다음 행동을 반복한다.
    • Queue에서 한 사람을 꺼냄
    • 남은 파티에 해당 인원이 참가한 경우, 다음 행동 수행
      • 해당 파티에 있는 아직 진실을 모르는 사람을 Queue에 집어넣음
      • 해당 파티를 파티 Vector에서 제거
  • 루프가 끝난 후, Vector 내에 남은 파티의 수가 정답이다.

 

전체 소스 코드

더보기
//Backjoon Problem No.1043
//https://www.acmicpc.net/problem/1043
//Mist16, 2022-03-01
#include <iostream>
#include <vector>
#include <set>

using namespace std;

int M = 0, N = 0;

int isAffected[51] = { 0, };

vector<set<int>> vParty;
vector<int> vAffected;

int main()
{
	int numb, temp;
	//입출력 속도 증가
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N >> M >> numb;
	//진실을 아는 사람
	for (int i = 0; i < numb; i++)
	{
		cin >> temp;
		vAffected.push_back(temp);
		isAffected[temp] = 1;
	}
	//파티 입력
	for (int i = 0; i < M; i++)
	{
		set<int> cParty;

		cin >> numb;
		for (int j = 0; j < numb; j++)
		{
			cin >> temp;
			cParty.insert(temp);
		}
		vParty.push_back(cParty);
	}

	while (!vAffected.empty() && !vParty.empty())
	{
		int aPerson = vAffected.back();

		vAffected.pop_back();

		for (auto iter = vParty.begin(); iter != vParty.end(); iter)
		{
			if (iter->find(aPerson) != iter->end())
			{
				for (int i : *iter)
				{
					if (isAffected[i] == 0)
					{
						isAffected[i] = 1;
						vAffected.push_back(i);
					}
				}
				iter = vParty.erase(iter);
			}
			else
				iter++;
		}

	}
	cout << vParty.size();
	return 0;
}

 

Note

 

함수 선언

public static void Destroy ( obj , t = 0.0F);

  • UnityEngine.object Obj : 삭제할 object
  • [DefaultValue("0.0F")] float t : 해당 시간이 지난 이후 삭제, 기본값일 경우(= 0.0) 현재 Update Loop가 끝난 후 삭제된다. 

사용

    public void OnMouseUp()
    {
        Destroy(this.gameObject);
    }

Node 오브젝트들에 해당 함수를 구현하고 사용하고 오브젝트 클릭시, 다음과 같이 삭제됨을 확인할 수 있었다.

클릭한 오브젝트가 삭제됨

  • Destroy() 함수가 호출될 시, OnDisable()과 OnDestroy()가 호출된다.
    • 삭제시 수행할 작업이 있다면 해당 함수를 구현하여 처리할 수 있다.
public void OnMouseUp()
{
	Debug.Log("Call Destroy Function");
	Destroy(this.gameObject);
	Debug.Log("Call Destroy Function End");
}

public void OnDestroy()
{
	Debug.Log("OnDestroy");
}

public void OnDisable()
{
	Debug.Log("OnDisable");
}

위 방식으로 실행한 결과 Log

주의 사항

  • 스크립트 내부에서 Destroy(this) 로 삭제를 구현할시 해당 클래스, 즉 스크립트만 삭제됨을 확인할 수 있다.

이렇게 되어버린다

  • 게임오브젝트를 다른 곳에서 리스트에 넣어서 관리하고 있었을 때, 삭제 후 해당 오브젝트를 관리할떄 조심해야 한다
    • GameObject로 비교 시 null값이 나오지만, object로 비교시에는 GC가 메모리를 초기화해주기 전까지 null값이 나오지 않는다.(Fake NULL 이슈)
Debug.Log("Check!");

if ((GameObject)NodeBoard[0, 0] == null)
	Debug.Log("Null GameObject Found!!!");
if ((object)NodeBoard[0, 0] == null)
	Debug.Log("Null Object Found!!!");
if (!NodeBoard[0, 0])
	Debug.Log("Null by BOOLcheck Found!!!");
    
Debug.Log("Check End!");

삭제 이후 Log, object 비교는 실패한다.

  • 같은 이유로, 소멸자도 삭제 이후에 따로 출력된다.

4초 후 호출된 소멸자

같이보기

https://docs.unity3d.com/ScriptReference/Object.Destroy.html

 

Unity - Scripting API: Object.Destroy

The object obj is destroyed immediately after the current Update loop, or t seconds from now if a time is specified. If obj is a Component, this method removes the component from the GameObject and destroys it. If obj is a GameObject, it destroys the GameO

docs.unity3d.com

 

NOTE

  • Fake Null 이슈에 관해서는 더 깊게 정리해둘 필요가 있을 듯.

 

'Programming > Unity' 카테고리의 다른 글

유니티 코루틴 / Unity Coroutine  (0) 2022.03.11

문제

 

9663번: N-Queen

N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다. N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.

www.acmicpc.net

 

풀이 전략

  • 퀸의 경우 상하좌우, 대각선으로 길이 제한 없이 이동할 수 있다.
    • -> 한 줄에 퀸은 하나만 존재할 수 있다.
  • 1~N번째 행까지, 퀸을을 놓을 수 있는 모든 열에 퀸을 두어서, 경우의 수를 모두 더한다.
int queenBoard(int idx, QueenBoard qBoard)
{
	int answer = 0;

	if (idx == N)
		return 1;
	for (int i = 0; i < N; i++)
	{
    	answer += queenBoard(idx + 1, tempBoard);
	}
    return answer;
}
  • 대각선 / 다른 행에 다른 줄의 퀸이 있을 경우 퀸을 놓을 수 없으므로, 퀸을 둘 때마다 보드에 해당 범위를 체크한다.
    • int [N][N]으로 구현할 경우 함수 호출 인자가 지나치게 기므로, 비트마스킹으로 구현하였다.
    • 퀸을 놓기 전, 해당 칸의 비트마스크가 0인지 확인하고, 0일 경우에 해당 퀸에 영향을 받는 칸을 비트마스킹 한 후 함수를 재귀 호출해준다.
    • 비트마스킹해야할 사항은 다음과 같다
      • Queen을 놓은 곳의 가로선
      • Queen을 놓은 곳으로부터의 대각선(X모양)
if ((qBoard.n[i] & (1 << idx)) == 0)
{
	QueenBoard tempBoard = qBoard;
	tempBoard.n[i] = tempBoard.n[i] | (1 << idx);
	for (int j = 1; j < N - idx; j++)
	{
		tempBoard.n[i] = tempBoard.n[i] | (1 << (idx+j));
	
		if (i - j >= 0)
		{
			tempBoard.n[i - j] = tempBoard.n[i - j] | (1 << (idx + j));
		}
		if (i + j < N)
		{
			tempBoard.n[i + j] = tempBoard.n[i + j] | (1 << (idx + j));
		}
	}
}

전체 소스 코드

더보기
더보기
//Backjoon Problem No.09663
//https://www.acmicpc.net/problem/9663
//Mist16, 2022-02-06
#include <iostream>

using namespace std;

int N;

struct QueenBoard
{
	unsigned int n[15] = { 0, };
};

int queenBoard(int idx, QueenBoard qBoard)
{
	int answer = 0;

	if (idx == N)
		return 1;
	for (int i = 0; i < N; i++)
	{
		if ((qBoard.n[i] & (1 << idx)) == 0)
		{
			QueenBoard tempBoard = qBoard;
			tempBoard.n[i] = tempBoard.n[i] | (1 << idx);
			for (int j = 1; j < N - idx; j++)
			{
				tempBoard.n[i] = tempBoard.n[i] | (1 << (idx+j));
				
				if (i - j >= 0)
				{
					tempBoard.n[i - j] = tempBoard.n[i - j] | (1 << (idx + j));
				}
				if (i + j < N)
				{
					tempBoard.n[i + j] = tempBoard.n[i + j] | (1 << (idx + j));
				}
			}
			answer += queenBoard(idx + 1, tempBoard);
		}
	}
	return answer;
}


int main()
{
	QueenBoard blankboard;
	//입출력 속도 증가
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	cin >> N;
	cout << queenBoard(0, blankboard);
	
	return 0;
}

 

Note

  • 비트마스킹을 쓰지 않을 경우 시간 초과로 실패하였다. 함수 호출이 잦을 경우, 함수로 전달되는 인자의 양에도 신경을 써야 할 것.

+ Recent posts