전 세계가 초토화 되고 좀비들의 습격이 시작됩니다 미친 좀비들이 사정없이 공격하여 옵니다 쳐들어 오는 좀비를 철조망이 무너지지 않게 좀비들을 무찌릅니다 좀비들을 없애면 랜덤으로 골드가 생성됩니다 생성된 골드를 획득하고 메인 요새 화면으로 돌아가 캐릭터를 강력하게 할수 있는 무기를 구매합니다 총알도 강력하게 업그래드 할 총알을 구매할수 있습니다 무기를 구매하여 포켓 지갑에 넣고 인간 요새를 지키기 위해 철조먕을 사이에 두고 좀비들을 무찌릅니다 스테이지가 올라갈수록 좀비들의 숫자가 늘어나고 좀비들의 체력이 올라 감니다 그리고 강력한 좀비 보스들이 출현 합니다 확끈하게 좀비들을 쳐부술수 있는 무기를 구해하여 좀비들을 없애고 스테이지를 올려 점점 강력한 좀비들과 대결을 하세요 지구의 운명이 당신에게 있습니다
카드 판정에 따른 게임의 상태를 표시하고 카드가 모두 열리면 다음 스테이지로 이동하는 프로그램을 만들어 보겠습니다
먼제 GameManager 스크립트를 열고 수정합니다
GameManager 스크립트 수정
게임의 상태를 설정할 수 있는 enum 열거형의 자료를 설정하고 Star() 함수에 모바일 기기를 위한 설정을 추가합니다
초기의 스테이지 만드는 문장도 삭제합니다
그리고 Update() 함수에 state 값을 바꾸어서 반복 호출하지 않도록 MakeStage() 함수에 처음 state = STATE.WAIT을 추가하고 마지막에 state = STATE.IDLE을 추가합니다.
또한 카드 판정할 수 있는 CheckCard() 함수를 추가하여 카드를 판정하고 카드가 서로 다르면 CloseTwoCards() 함수를 추가하여 카드를 닫습니다
그래서 스테이지 카드가 모두 열리면 StageClear() 함수를 추가하여 스테이지를 클리어하고 다음 스테이지로 넘어가는 함수를 만듭니다
GameManager 스크립트 수정
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
//클릭한 카드 번호
static public int cardNum;
//직전의 카드 번호
int lastNum = 0;
//스테이지의 전체 카드수
int cardCnt;
//카드 클릭 횟수
int hitCnt = 0;
//스테이지 번호
static public int stageNum = 1;
//스테이지 수
int stageCnt = 6;
//카드 배열 카드 섞기용
int[] arCards = new int[50];
//게임 시작 시간
float startTime;
// 스테이지 경과 시간
float stageTime;
//열거형 의 자료를 설정하는 정의문 나열의 자료는 0,1,2....의 값이 할당된다
public enum STATE
{
START, HIT, WAIT, IDLE, CLEAR
};
static public STATE state = STATE.START;
// Start is called before the first frame update
void Start()
{
Screen.orientation = ScreenOrientation.LandscapeRight;
Screen.sleepTimeout = SleepTimeout.NeverSleep;
startTime = stageTime = Time.time;
//시간초기화
startTime = stageTime = Time.time;
//스테이지 만들기
// StartCoroutine(MakeStage());
}
void Update()
{
switch (state)
{
//스테이지 만들기
case STATE.START:
StartCoroutine(MakeStage());
break;
// 같은 그림인지 판정
case STATE.HIT:
CheckCard();
break;
//스테이지를 클리어 하고 다음 스테이지를 만듬
case STATE.CLEAR:
StartCoroutine(StageClear());
break;
}
//게임중지
if (Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}
}
void CheckCard()
{
state = STATE.WAIT;
//첫 번째 카드
if (lastNum == 0)
{
//현재 카드 보존
lastNum = cardNum;
state = STATE.IDLE;
return;
}
//이미지 찾기
int img1 = (cardNum + 1) / 2;
int img2 = (lastNum + 1) / 2;
//다른 카드 이면 카드를 닫게한다
if (img1 != img2)
{
StartCoroutine(CloseTwoCards());
lastNum = 0;
state = STATE.IDLE;
return;
}
//같은 카드면
hitCnt += 2;
//카드가 모두 열리면 스테이지를 클리어 한다
if (hitCnt == cardCnt)
{
state = STATE.CLEAR;
return;
}
//카드가 남아있으면 다른 카드를 조사한다
lastNum = 0;
state = STATE.IDLE;
}
//카드 닫기
IEnumerator CloseTwoCards()
{
//테그로 카드 찾기
GameObject card1 = GameObject.FindWithTag("card" + lastNum);
GameObject card2 = GameObject.FindWithTag("card" + cardNum);
//카드 닫기
yield return new WaitForSeconds(0.2f);
card1.SendMessage("CloseCard", SendMessageOptions.DontRequireReceiver);
card2.SendMessage("CloseCard", SendMessageOptions.DontRequireReceiver);
}
//스테이지를 클리어 한다
IEnumerator StageClear()
{
state = STATE.WAIT;
yield return new WaitForSeconds(2);
//스테이지 카드 제거
for (int i = 1; i <= cardCnt; i++)
{
GameObject card = GameObject.FindWithTag("card" + i);
Destroy(card);
}
//다음 스테이지 번호
++stageNum;
if (stageNum > stageCnt)
{
//Application.LoadLevel("GameStart");
//return;
}
//스테이지 초기화
stageTime = Time.time;
lastNum = 0;
hitCnt = 0;
state = STATE.START;
}
IEnumerator MakeStage()
{
//현재 작업중으로 설정
state = STATE.WAIT;
//시작카드의 x좌표
float sx = 0;
//시작카드의 z좌표
float sz = 0;
SetCardPos(out sx, out sz);
//카드섞기
ShuffleCard();
//시작카드의 번호
int n = 1;
//카드배열 읽기 배열의 1행을 읽고 변수 t에 할당한다
string[] str = StageSet.stage[stageNum - 1];
//배열의 행의 수만큼 반복
foreach (string t in str)
{
// 각행의 문자열을 단일 문자 배열로 변환(문자열 좌우의 공백 제거) , 변수t의 자우 공백을 제거(Trim)하고 단일 문자배열로 변환
char[] ch = t.Trim().ToCharArray();
//카드의 x축 좌표
float x = sx;
//1행의 문자열 길이만큼 반복
//배열의 ch의 한문자를 읽고 변수 c에 할당한다
foreach (char c in ch)
{
switch (c)
{
//맵의 내용이 * 이면 그위치에 카드 만들어서 배치
case '*':
//카드 만들기
GameObject card = Instantiate(Resources.Load("Prefab/Card")) as GameObject;
//카드 좌표설정
card.transform.position = new Vector3(x, 0, sz);
//태그 달기
//card.tag = "card" + n++;
//섞인카드
card.tag = "card" + arCards[n++];
x++;
break;
//빈칸 처리
case '.':
x++;
break;
//반 칸 공백처리
case '>':
x += 0.5f;
break;
//반 줄 행간 처리
case '^':
sz += 0.5f;
break;
}
//카드를 표시한 후에는 지연 시간을 두어 카드가 배치되는 과정이 보이도록함
if (c == '*')
{
yield return new WaitForSeconds(0.03f);
}
}
//한줄 아래로 이동
sz--;
}
//입력 대기중으로 설정
state = STATE.IDLE;
}
//카드의 시작 위치 계산
void SetCardPos(out float sx, out float sz)
{
//가로 카드수 반 칸 공백 포함
float x = 0;
//세로 행수 반줄 행간 포함
float z = 0;
//가로 카드 최대수
float maxX = 0;
//스테이지 전체 카드수
cardCnt = 0;
//카드 배열 조사 맵 배열 을 읽음
string[] str = StageSet.stage[stageNum - 1];
//행의 수만큼 반복
for (int i = 0; i < str.Length; i++)
{
//1행읽기
string t = str[i].Trim();
//각행의 카드수
x = 0;
//각행의 글자 수만큼 반복
for (int j = 0; j < t.Length; j++)
{
//문자열(string)은 단일 문자(char)의 배열로 취급할수 있음
switch (t[j])
{
case '.':
case '*':
//카드배치에 필요한 공간
x++;
if (t[j] == '*')
{
//전체 카드수
cardCnt++;
}
break;
case '>':
x += 0.5f;
break;
case '^':
z -= 0.5f;
break;
}
}
//각 행의 최대 카드수 계산
if (x > maxX)
{
maxX = x;
}
//전체 행의 수
z++;
}
//카드 가로 시작 위치
sx = -maxX / 2;
sz = (z - 1) / 2;
// StartCoroutine(CardOpen(cardCnt));
}
void ShuffleCard()
{
for (int i = 1; i <= cardCnt; i++)
{
arCards[i] = i;
}
//return;
//카드섞기 15회정도 반복
for (int i = 1; i <= 15; i++)
{
//임의의 난수를 두개 만든다
int n1 = Random.Range(1, cardCnt + 1);
int n2 = Random.Range(1, cardCnt + 1);
//교환
int t = arCards[n1];//배열의 값을 바꾼다
arCards[n1] = arCards[n2];
arCards[n2] = t;
//StartCoroutine(CardOpen(arCards[n1]));
}
}
}
카드와 게임 메니져 GameManager 스크립트와 통신하기 위해서 CardCtrl 스크립트를 수정합니다
CardCtrl 스크립트 수정
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CardCtrl : MonoBehaviour
{
//이미지 번호
int imgNum = 1;
//카드 뒷면 이미지 번호
int backNum = 1;
// 오픈된 카드의 판별여부
bool isOpen = false;
Animator anim;
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
}
void Update()
{
//왼쪽 마우스버튼 클릭 모바일에선 터치
if (Input.GetButtonDown("Fire1") && GameManager.state == GameManager.STATE.IDLE)
{
CheckCard();
}
}
//카드체크
void CheckCard()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//터치한 카드 식별
if (Physics.Raycast(ray, out hit, Mathf.Infinity))
{
string tag = hit.transform.tag;
if (tag.Substring(0, 4) == "card")
{
//터치한 카드의 OpenCard()함수 실행
hit.transform.SendMessage("OpenCard", SendMessageOptions.DontRequireReceiver);
}
}
}
void OpenCard()
{
//열린 카드는 처리 없음
if (isOpen) return;
//열린 카드 참거짓 판정
isOpen = true;
// 카드 번호 Substring() 문자열의 일부분을 추출하는 함수 , 카드 번호(tag)는 card0~card32 있으므로 문자4번째부터(card~) 끝까지 추출한다
int cardNum = int.Parse(transform.tag.Substring(4));
//이미지 번호 카드 두장에 하나씩 같은 이미지를 할당하므로 이미지 번호는 (카드번호 +1)/2 로 구한다 정수 /정수 = 정수 이므로 소수 이하는 자동으로 잘림
imgNum = (cardNum + 1) / 2;
//카드 애니메이션 실행
anim.Play("aniOpen");
GameManager.cardNum = cardNum;
GameManager.state = GameManager.STATE.HIT;
}
void CloseCard()
{
anim.Play("aniClose");
isOpen = false;
}
//카드 앞면을 이미지을 가지고 온다
void ShowImage()
{
transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("card" + imgNum) as Texture2D;
}
//카드 뒷면 이미지을 가지고 온다
void HideImage()
{
transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("back" + backNum) as Texture2D;
}
}
카드를 Touch 및 클릭하여 카드를 뒤집고 카드를 리소스의 순번대로 나오는 것을 랜덤으로 섞는 프로그램을 만들어 보겠습니다
먼저 CardCtrl 스크립트를 열고 수정합니다
CardCtrl 스크립트 수정
Update()와 CheckCard() 함수 부분을 수정 및 추가합니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CardCtrl : MonoBehaviour
{
//이미지 번호
int imgNum = 1;
//카드 뒷면 이미지 번호
int backNum = 1;
// 오픈된 카드의 판별여부
bool isOpen = false;
Animator anim;
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
}
void Update()
{
//왼쪽 마우스버튼 클릭 모바일에선 터치
if (Input.GetButtonDown("Fire1") )
{
CheckCard();
}
}
//카드체크
void CheckCard()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//터치한 카드 식별
if (Physics.Raycast(ray, out hit, Mathf.Infinity))
{
string tag = hit.transform.tag;
if (tag.Substring(0, 4) == "card")
{
//터치한 카드의 OpenCard()함수 실행
hit.transform.SendMessage("OpenCard", SendMessageOptions.DontRequireReceiver);
}
}
}
void OpenCard()
{
if (isOpen) return;
isOpen = true;
// 카드 번호 Substring() 문자열의 일부분을 추출하는 함수 , 카드 번호(tag)는 card0~card32 있으므로 문자4번째부터(card~) 끝까지 추출한다
int cardNum = int.Parse(transform.tag.Substring(4));
//이미지 번호 카드 두장에 하나씩 같은 이미지를 할당하므로 이미지 번호는 (카드번호 +1)/2 로 구한다 정수 /정수 = 정수 이므로 소수 이하는 자동으로 잘림
imgNum = (cardNum + 1) / 2;
//카드 애니메이션 실행
anim.Play("aniOpen");
//GameManager.cardNum = cardNum;
//GameManager.state = GameManager.STATE.HIT;
}
void CloseCard()
{
anim.Play("aniClose");
isOpen = false;
}
//카드 앞면을 이미지을 가지고 온다
void ShowImage()
{
transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("card" + imgNum) as Texture2D;
}
//카드 뒷면 이미지을 가지고 온다
void HideImage()
{
transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("back" + backNum) as Texture2D;
}
}
아래 동영상과 카드를 터치하였을 때 카드가 열립니다 그런데 카드가 섞이지 않고 순번대로 열립니다
GameManager 스크립트 수정합니다
GameManager 스크립트 수정
카드를 섞는 함수를 작성
ShuffleCard() 함수 작성 및 카드 태그 인식 부분 수정
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
//클릭한 카드 번호
static public int cardNum;
//직전의 카드 번호
int lastNum = 0;
//스테이지의 전체 카드수
int cardCnt;
//카드 클릭 횟수
int hitCnt = 0;
//스테이지 번호
static public int stageNum = 1;
//스테이지 수
int stageCnt = 6;
//카드 배열 카드 섞기용
int[] arCards = new int[50];
//게임 시작 시간
float startTime;
// 스테이지 경과 시간
float stageTime;
// Start is called before the first frame update
void Start()
{
//시간초기화
startTime = stageTime = Time.time;
//스테이지 만들기
StartCoroutine(MakeStage());
}
void Update()
{
}
IEnumerator MakeStage()
{
//시작카드의 x좌표
float sx = 0;
//시작카드의 z좌표
float sz = 0;
SetCardPos(out sx, out sz);
//카드섞기
ShuffleCard();
//시작카드의 번호
int n = 1;
//카드배열 읽기 배열의 1행을 읽고 변수 t에 할당한다
string[] str = StageSet.stage[stageNum - 1];
//배열의 행의 수만큼 반복
foreach (string t in str)
{
// 각행의 문자열을 단일 문자 배열로 변환(문자열 좌우의 공백 제거) , 변수t의 자우 공백을 제거(Trim)하고 단일 문자배열로 변환
char[] ch = t.Trim().ToCharArray();
//카드의 x축 좌표
float x = sx;
//1행의 문자열 길이만큼 반복
//배열의 ch의 한문자를 읽고 변수 c에 할당한다
foreach (char c in ch)
{
switch (c)
{
//맵의 내용이 * 이면 그위치에 카드 만들어서 배치
case '*':
//카드 만들기
GameObject card = Instantiate(Resources.Load("Prefab/Card")) as GameObject;
//카드 좌표설정
card.transform.position = new Vector3(x, 0, sz);
//태그 달기
//card.tag = "card" + n++;
//섞인카드
card.tag = "card" + arCards[n++];
x++;
break;
//빈칸 처리
case '.':
x++;
break;
//반 칸 공백처리
case '>':
x += 0.5f;
break;
//반 줄 행간 처리
case '^':
sz += 0.5f;
break;
}
//카드를 표시한 후에는 지연 시간을 두어 카드가 배치되는 과정이 보이도록함
if (c == '*')
{
yield return new WaitForSeconds(0.03f);
}
}
//한줄 아래로 이동
sz--;
}
}
//카드의 시작 위치 계산
void SetCardPos(out float sx, out float sz)
{
//가로 카드수 반 칸 공백 포함
float x = 0;
//세로 행수 반줄 행간 포함
float z = 0;
//가로 카드 최대수
float maxX = 0;
//스테이지 전체 카드수
cardCnt = 0;
//카드 배열 조사 맵 배열 을 읽음
string[] str = StageSet.stage[stageNum - 1];
//행의 수만큼 반복
for (int i = 0; i < str.Length; i++)
{
//1행읽기
string t = str[i].Trim();
//각행의 카드수
x = 0;
//각행의 글자 수만큼 반복
for (int j = 0; j < t.Length; j++)
{
//문자열(string)은 단일 문자(char)의 배열로 취급할수 있음
switch (t[j])
{
case '.':
case '*':
//카드배치에 필요한 공간
x++;
if (t[j] == '*')
{
//전체 카드수
cardCnt++;
}
break;
case '>':
x += 0.5f;
break;
case '^':
z -= 0.5f;
break;
}
}
//각 행의 최대 카드수 계산
if (x > maxX)
{
maxX = x;
}
//전체 행의 수
z++;
}
//카드 가로 시작 위치
sx = -maxX / 2;
sz = (z - 1) / 2;
// StartCoroutine(CardOpen(cardCnt));
}
void ShuffleCard()
{
for (int i = 1; i <= cardCnt; i++)
{
arCards[i] = i;
}
//return;
//카드섞기 15회정도 반복
for (int i = 1; i <= 15; i++)
{
//임의의 난수를 두개 만든다
int n1 = Random.Range(1, cardCnt + 1);
int n2 = Random.Range(1, cardCnt + 1);
//교환
int t = arCards[n1];//배열의 값을 바꾼다
arCards[n1] = arCards[n2];
arCards[n2] = t;
//StartCoroutine(CardOpen(arCards[n1]));
}
}
}
하이 라키 뷰에 GameManager 오브젝트를 만들고 GameManager 스크립트를 생성하여 오브젝트에 붙입니다
GameManager 스크립트 작성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
//클릭한 카드 번호
static public int cardNum;
//직전의 카드 번호
int lastNum = 0;
//스테이지의 전체 카드수
int cardCnt;
//카드 클릭 횟수
int hitCnt = 0;
//스테이지 번호
static public int stageNum = 1;
//스테이지 수
int stageCnt = 6;
//카드 배열 카드 섞기용
int[] arCards = new int[50];
//게임 시작 시간
float startTime;
// 스테이지 경과 시간
float stageTime;
// Start is called before the first frame update
void Start()
{
//시간초기화
startTime = stageTime = Time.time;
//스테이지 만들기
StartCoroutine(MakeStage());
}
void Update()
{
}
IEnumerator MakeStage()
{
//시작카드의 x좌표
float sx = 0;
//시작카드의 z좌표
float sz = 0;
SetCardPos(out sx, out sz);
//시작카드의 번호
int n = 1;
//카드배열 읽기 배열의 1행을 읽고 변수 t에 할당한다
string[] str = StageSet.stage[stageNum - 1];
//배열의 행의 수만큼 반복
foreach (string t in str)
{
// 각행의 문자열을 단일 문자 배열로 변환(문자열 좌우의 공백 제거) , 변수t의 자우 공백을 제거(Trim)하고 단일 문자배열로 변환
char[] ch = t.Trim().ToCharArray();
//카드의 x축 좌표
float x = sx;
//1행의 문자열 길이만큼 반복
//배열의 ch의 한문자를 읽고 변수 c에 할당한다
foreach (char c in ch)
{
switch (c)
{
//맵의 내용이 * 이면 그위치에 카드 만들어서 배치
case '*':
//카드 만들기
GameObject card = Instantiate(Resources.Load("Prefab/Card")) as GameObject;
//카드 좌표설정
card.transform.position = new Vector3(x, 0, sz);
//태그 달기
card.tag = "card" + n++;
// card.tag = "card" + arCards[n++];
x++;
break;
//빈칸 처리
case '.':
x++;
break;
//반 칸 공백처리
case '>':
x += 0.5f;
break;
//반 줄 행간 처리
case '^':
sz += 0.5f;
break;
}
//카드를 표시한 후에는 지연 시간을 두어 카드가 배치되는 과정이 보이도록함
if (c == '*')
{
yield return new WaitForSeconds(0.03f);
}
}
//한줄 아래로 이동
sz--;
}
}
//카드의 시작 위치 계산
void SetCardPos(out float sx, out float sz)
{
//가로 카드수 반 칸 공백 포함
float x = 0;
//세로 행수 반줄 행간 포함
float z = 0;
//가로 카드 최대수
float maxX = 0;
//스테이지 전체 카드수
cardCnt = 0;
//카드 배열 조사 맵 배열 을 읽음
string[] str = StageSet.stage[stageNum - 1];
//행의 수만큼 반복
for (int i = 0; i < str.Length; i++)
{
//1행읽기
string t = str[i].Trim();
//각행의 카드수
x = 0;
//각행의 글자 수만큼 반복
for (int j = 0; j < t.Length; j++)
{
//문자열(string)은 단일 문자(char)의 배열로 취급할수 있음
switch (t[j])
{
case '.':
case '*':
//카드배치에 필요한 공간
x++;
if (t[j] == '*')
{
//전체 카드수
cardCnt++;
}
break;
case '>':
x += 0.5f;
break;
case '^':
z -= 0.5f;
break;
}
}
//각 행의 최대 카드수 계산
if (x > maxX)
{
maxX = x;
}
//전체 행의 수
z++;
}
//카드 가로 시작 위치
sx = -maxX / 2;
sz = (z - 1) / 2;
// StartCoroutine(CardOpen(cardCnt));
}
}
하이 라키 뷰에 MaskCamera를 잠시 꺼두고 아래 그림과 같이 Transform 값을 바꿉니다
Materials 폴더에 Material을 생성하고 이름을 card_back으로 하고 Card 오브젝트 에 메터리얼을 드래그하여 붙이고 메터리얼 Shader를 Lagacy Shaders -> Transparent -> Diffuse를 선택합니다
그리고 그림을 Resources 파일에 있는 back 5를 붙입니다
Card 오브젝트에 붙일 CardCtrl 스크립트를 생성하여 스크립트 작업을 합니다
CardCtrl 스크립트 작성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CardCtrl : MonoBehaviour
{
//이미지 번호
int imgNum = 1;
//카드 뒷면 이미지 번호
int backNum = 1;
// 오픈된 카드의 판별여부
bool isOpen = false;
Animator anim;
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
;
}
//카드 앞면을 이미지을 가지고 온다
void ShowImage()
{
transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("card" + imgNum) as Texture2D;
}
//카드 뒷면 이미지을 가지고 온다
void HideImage()
{
transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("back" + backNum) as Texture2D;
}
}
작성한 스크립트를 Card 오브젝트에 붙입니다
메인 카메라를 선택하고 Transform을 그림과 같이 바꿉니다
BackManager 자식으로 Camera 오브젝트를 생성하여 이름을 MaskCamera 라하고 Transform을 아래 그림과 같이 바꿉니다