UIManager라는 UI를 관리하는 스크립트를 UIManager폴더를 만들어 저장후 Canvas에 어태치합니다.

앞에서 만든 아이템이나 HP표시 UI를 갱신하는 기능을 수행합니다.

UIManager의 내용은 다음과 같습니다.

변수

UI를 연결한 리소스의 참조들입니다.

public GameObject arrowText;        //화살의 수를 표시하는 Text
public GameObject keyText;          //열쇠 수를 표시하는Text
public GameObject hpImage;          //HP의 수를 표시하는 Image
public Sprite life3Image;           //HP3 이미지
public Sprite life2Image;           //HP2 이미지
public Sprite life1Image;           //HP1 이미지
public Sprite life0Image;           //HP0 이미지
public GameObject mainImage;        // 이미지를 가지는 GameObject
public GameObject resetButton;      // 리셋 버튼
public Sprite gameOverSpr;          // GAME OVER 이미지
public Sprite gameClearSpr;         // GAME CLEAR 이미지
public GameObject inputPanel;       //버추얼 패드와 공격 버튼을 배치한 조작 패널

소지품의 개수, 플레이어의 HP, 재시도하는 씬이름등입니다.

int hasKeys = 0;                    //열쇠 수
int hasArrows = 0;                  //화살 소지 수
int hp = 0;                         //플레이어의 HP

public string retrySceneName = "";  //재시도하는 씬 이름

Start()

아이템수와 HP를 갱신하기 위해 UpdateItemCont()와 UpdateHP()를 호출합니다. 기타 필요없는 버튼을 숨깁니다.

void Start()
{
    UpdateItemCount();  //아이템 수 갱신
    UpdateHP();         //HP갱신
    //이미지를 숨기기
    Invoke("InactiveImage", 1.0f);
    resetButton.SetActive(false);  //버튼 숨기기
}

Update()

Start()와 똑같이 아이템수와 HP를 갱신하기 위해 UpdateItemCont()와 UpdateHP()를 호출합니다. 

void Update()
{
    UpdateItemCount();  //아이템 수 갱심
    UpdateHP();         //HP갱신
}

UpdateItemCount()

static 변수인 ItemKeeper가 가진 각 아이템수가 현재UI와 다르면 UI를 변경합니다.

void UpdateItemCount() {
    //화살
    if (hasArrows != ItemKeeper.hasArrows)  {
        arrowText.GetComponent<Text>().text = ItemKeeper.hasArrows.ToString();
        hasArrows = ItemKeeper.hasArrows;
    }
    //열쇠
    if (hasKeys != ItemKeeper.hasKeys)  {
        keyText.GetComponent<Text>().text = ItemKeeper.hasKeys.ToString();
        hasKeys = ItemKeeper.hasKeys;
    }
}

UpdateHP()

레벨의 플레이어를 찾아 플레이어의 HP와 UI의 HP가 다르다면 갱신합니다. 만일 플레이어가 사망했다면 게임종료 상태로 변경하고 적절한 UI 를 표시합니다.

void UpdateHP() {
    //Player 가져오기
    if (PlayerController.gameState != "gameend")   {
        GameObject player = GameObject.FindGameObjectWithTag("Player");
        if (player != null)   {
            if (PlayerController.hp != hp)  {
                hp = PlayerController.hp;
                if (hp <= 0)  {
                    hpImage.GetComponent<Image>().sprite = life0Image;
                    //플레이어 사망!!
                    resetButton.SetActive(true);    //버튼 표시
                    mainImage.SetActive(true);      //이미지 표시
                                                    // 이미지 설정
                    mainImage.GetComponent<Image>().sprite = gameOverSpr;
                    inputPanel.SetActive(false);      //조작 UI 숨기기
                    PlayerController.gameState = "gameend";   //게임 종료
                }  else if (hp == 1) {
                    hpImage.GetComponent<Image>().sprite = life1Image;
                } else if (hp == 2) {
                    hpImage.GetComponent<Image>().sprite = life2Image;
                }   else {
                    hpImage.GetComponent<Image>().sprite = life3Image;
                }
            }
        }
    }
}

Retry()

게임오버가 되면 첫번째 Stage를 로딩합니다.

public void Retry()
{
    //HP 되돌리기
    PlayerPrefs.SetInt("PlayerHP", 3);
    //게임 중으로 설정
    SceneManager.LoadScene(retrySceneName);   //씬 이동
}

Cavas에 어태치된 UIManger 스크립트의 publc 변수들을 하이라키에서 끌어다 적용시킵니다.

 

여기까지 만들었으면 Cavas를 프로젝트뷰 UIManager에 놓고 프리팹으로 만듭니다.

 

다른씬에 프리팹 배치하기

dunjeon1 씬을 로딩하고 Canvas를 끌어다 놓습니다. 

Canvas의 인스펙터뷰에서 RenderCamera에 MainCamera를 설정합니다. 이제서야 UI가 나옵니다.ㅎ

마지막으로 하이라키의 +>UI>EventSystem을 선택해 추가해줍니다. 이미 있다면 생략해도 됩니다.

 

게임과 UI를 관리하는 시스템을 만들어 봅시다. UI관련 데이터는 UIManager폴더를 만들고 저장합니다.

 

게임 UI만들기

먼저 게임에 필요한 UI를 만듭니다. 각 아이템들의 개수와 현재 HP를 표시할 UI, 게임 오버 표시 재시도 버튼 그리고 사이드 뷰 게임과 마찬가지로 터치 조작으로 플레이어를 조작할 수 있게 버추얼 패드와 공격 버튼을 만듭니다.

 

아이템 소지수 표시하기

화살, 열쇠의 소지 수를 표시할 Image와 Text오브젝트를 만듭니다. 먼저 계측뷰에서 +>UI>Image를 선택후 Canvas와Image를 추가합니다. 이름을 ItemImage로 변경합니다.

 

Canvas오브젝트의 Render Mode를 Screen Space-Camera로 변경하고 Main Camera를 Renderer Camera로 Canvas의 Order in Layer를 10으로 설정합니다.

ItemImage오브젝트의 Image컴포넌트의 SoruceImage를 ItemImage로 설정하고, Presever Aspect, Set Native Size를 클릭합니다. Anchor Preset 위치를 ALT를 눌러 좌상으로 설정합니다.

ItemImage의 자식으로 UI>Text를 2개 추가하여 KeyText, ArrowText로 이름짓습니다. Width:120, Height:100정도로하고 Text내용은 000,Font Size 64정도 중앙정렬로 해서 맞춥니다. Text위치도 Image에 맞게 조정해줍니다.

플레이어 HP표시하기

플레이어의 HP를 표시할 이미지를 만듭니다. Life라는 Image입니다. 멀티스프라이트이므로 SpriteEditor에서 Automatic으로 분할합니다.

Automatic으로 분할하면 4개로 분할됩니다.

Canvas에 Image를 추가하고 이름을 HPImage로 하고 Source Image에 Life_0를 설정합니다. RectTransform Width:300, Height:100으로 하고 원본이미지의 크기로 설정하고 Canvas 우상단에 위치 시킵니다.

게임 진행상태 표시하기

Canvas 아래에 Image를 추가합니다. SourceImage로 GameStart 이미지를 적용시키고 Set Native Size버튼을 클릭합니다.

위치는 중앙 약간 위치쪽으로 설정하고 Rect Transform Anchor Presets를 중앙으로 합니다.

 

재시도 버튼 추가

Canvas중앙아래 Button을 추가하고 Button Image를 적용시키고 Set Native Size를 적용시킵니다. 이름 RetryButton이라고 합니다. Button의 자식 Text는 RETRY라고 하고 폰트사이즌는 64로 합니다.

 

스마트폰용 UI만들기

 

360도 버추얼패드 설정하기 

이미지는 사이드 뷰 게임과 다르지만  UI구성은 같습니다.

Canvas 아래에 Panel을 배치하고 자식으로 버추얼패드를 배치합니다. UI>Image를 총 2개 추가 화면 아래에 배치하고 이름은 VirtualPadBase로 합니다. ImageSource는 VirtualPad4D를 사용합니다. 자식으로 이미지를 하나도 배치하고 VirtualPad로 이름을 바꿉니다. VirtualPadTab이미지를 사용합니다.

공격버튼은 이미지가 아니라 UI>Button을 추가한후 이미지로 AttackButton을 사용하고 이름을 AttackButtond으로 바꾼후 다음과 같이 Panel의 자식 하이라키를 만듭니다.

Virtual Pad 스크립트는 사이드뷰에서 만든것을 그대로 사용합니다. VirtualPad에 어태치하고 Is4DPad를 체크합니다.

VirtualPad.cs
0.00MB

Virtual Pad를 선택하고 Event>Event Trigger컴포넌트를 추가합니다. 종류는 PointDown, Drag, PointUp을 하나씩 추가합니다.

추가된 EventType의 + 를 클릭히 None에 하이라키의 Virtual Pad를 추가해주고 None Function에 PadDown() PadDrag() PadUp()등 VirtualPad.cs의 메쏘드를 연결해줍니다.

공격버튼 설정하기

VirtualPad 클래스의 내용을 조금 변경해 봅니다. Attack 메서드를 추가합니다. Player 에 어태치된 ArrowShoot클래스의 Attack메서드를 호출합니다. VirtualPad클래스에 공격호출을 중계해 다른 씨에서 UI를 다시 사용하기 쉽습니다.

public void Attack()
{
    GameObject player = GameObject.FindGameObjectWithTag("Player");
    ArrowShoot shoot = player.GetComponent<ArrowShoot>();
    shoot.Attack();
}

VirtualPad 스크립트를 AttackButton에 어태치합니다. AttackButton을 선택하고 EventTrigger컴포넌트를 추가합니다. Add New EventType > PointDown을 추가합니다. +를 눌러 리스트를 추가하고 None에 하이라키의 VirtualPad를 추가하고 No Function에 풀다운메뉴에서 VirtualPad>Attack()를 선택해줍니다.

적 캐릭터는 가만히 있다가 플레이어가 근접하면 플레이어를 향해 이동하기 시작합니다. 접촉하면 플레이어에 대미지를 입힙니다.  Enemy 폴더를 만들어 관련 리소스를 저장합니다.

 

적캐릭터의 게임 오브젝트 만들기

Enemy.zip
0.02MB

적 캐릭터의 이미지도 멀티 스프라이트로 만들수 있습니다 . Image폴더에 있는 EnemyImage를 Enemy폴더로 옮깁니다. 32x32픽셀 도트이미지로 플레이어와 동일하게 스프라이트에디터에서 slice 합니다.

EnemyImage_9을 씬뷰에 끌어다 Enemy라는 게임오브젝트를 만듭니다.

Rigidbody2D, CircleCollider2D를 어태치합니다.

Enemy Tag를 만들어 설정하고 Sprite Renderer의 Order in Layer는 3으로 설정합니다. Rigidbody 2D의 Gravity Scale은 0으로 하고 Freeze Rotation의 Z를 체크합니다.

적캐릭터의 애니메이션 만들기

적 캐릭터의 게임오브젝트를 선택하고 애니메이션창을 엽니다. 중앙의 Create버튼을 클릭하면 애니메이션을 저장하는 창이나오고  Enemy폴더에 EnemyIdle라느 이름으로 저장합니다.

Enemy라는 애니메이션컨트롤러파일과 EnemyIdle이라는 이름으로 애니메이션 클립파일이 만들어졌습니다.

[Add Property]버튼을 눌러 Sprite Renderer>Sprite>+를 클릭해서 대기 애니메이션을 완성합니다.

다음과 같이 상하좌우 움직임 애니메이션도 만듭니다. 이전 플레이어 애니메이션과 비슷합니다. 해당 스프라이트를 동시에 씬뷰로 끌어다 놓으면 저절로 애니메이션이 만들어집니다.

EnemyDown : EnemyImage0~1 

EnemyUp : EnemyImage2~3 

EnemyLeft : EnemyImage4~5 

EnemyRight : EnemyImage6~7 

애니메이션 클립이 완성되면 씬뷰의 오브젝트들은 지우고 프로젝트뷰의 필요없는 컨트롤러는 제거합니다.  상하좌우애니메이션을 Enemy애니메이션 컨트롤러로 끌어다 놓습니다.

Player의 Dead Animation처럼 Enemy의 Dead 애니메이션도 만듭니다. Property에  Sprite Renderer.Color를 추가하고 마지막 프레임의 Alpha값을 0으로 설정합니다.

 

적 캐릭터 스크립트 만들기 

EnemyController 스크립트를 만들어 Enemy폴더에 저장하고 씬뷰의 Enemy에 어태치합니다.

EnemyController.cs
0.00MB

변수

Enemy 오브젝트의 이동관련 변수및 hp(생명력) 관련 변수 입니다.

// HP
public int hp = 3;
//이동 속도
public float speed = 0.5f;
// 반응 거리
public float reactionDistance = 4.0f;

float axisH;            //가로 축 값(-1.0 〜 0.0 〜 1.0)
float axisV;            //세로 축 값(-1.0 〜 0.0 〜 1.0)
Rigidbody2D rbody;      //Rigidbody 2D

bool isActive = false;      //활성 여부

public int arrangeId = 0;   //배치 식별에 사용

애니메이션 Play()를 관리할 애니메이션 이름들과 변수입니다.

//애니메이션 이름
public string idleAnime = "EnemyIdle";		// 정지
public string upAnime = "EnemyUp";          // 위
public string downAnime = "EnemyDown";		// 아래
public string rightAnime = "EnemyRight";    // 오른쪽
public string leftAnime = "EnemyLeft";		// 왼쪽
public string deadAnime = "EnemyDead";		// 사망
//현재 애니메이션現在のアニメーション
string nowAnimation = "";
//이전 애니메이션
string oldAnimation = "";

Start()

Rigidbody2D 참조를 저장해 놓습니다.

void Start() {
    //Rigidbody2D 가져오기
    rbody = GetComponent<Rigidbody2D>();
}

Enemy와 Player의 거리가 reactionDistance이내로 들어오면 각도를 구하고 해당 애니메이션을 설정하고 이동할 벡터를 구합니다.

void Update() {
    //Player 게임 오브젝트 가져오기
    GameObject player = GameObject.FindGameObjectWithTag("Player");
    if (player != null)  {
        if (isActive)  {
            //플레어이어와의 각도 구하기
            float dx = player.transform.position.x - transform.position.x;
            float dy = player.transform.position.y - transform.position.y;
            float rad = Mathf.Atan2(dy, dx);
            float angle = rad * Mathf.Rad2Deg;
            //이동 각도에 따른 애니메이션 설정
            if (angle > -45.0f && angle <= 45.0f) {
                nowAnimation = rightAnime;
            }  else if (angle > 45.0f && angle <= 135.0f) {
                nowAnimation = upAnime;
            } else if (angle >= -135.0f && angle <= -45.0f) {
                nowAnimation = downAnime;
            }  else {
                nowAnimation = leftAnime;
            }
            //이동할 벡터 만들기
            axisH = Mathf.Cos(rad) * speed;
            axisV = Mathf.Sin(rad) * speed;
        } else {
            //플레이어와의 거리 확인
            float dist = Vector2.Distance(transform.position, player.transform.position);
            if (dist < reactionDistance){
                isActive = true;    //활성으로 설정
            }
        }
    } else if (isActive) {
        isActive = false;
        rbody.velocity = Vector2.zero;
    }
}

FixedUpdate()

Update()에서 구해진 axisH, axisV를 이용해 리지드바디의 속도를 설정해서 이동하게 만듭니다. 애니메이션도 플레이해줍니다.

void FixedUpdate() {
    if (isActive && hp > 0)  {
        //이동
        rbody.velocity = new Vector2(axisH, axisV);
        if (nowAnimation != oldAnimation) {
            // 애니메이션 변경하기
            oldAnimation = nowAnimation;
            Animator animator = GetComponent<Animator>();
            animator.Play(nowAnimation);
        }
    }
}

OnCollisionEnter2D()

Arrow에 충돌하면 hp--를 해주고 0이 되면 사망처리를 합니다. Dead 애니메이션도 플레이하고 Destry()해줍니다.

private void OnCollisionEnter2D(Collision2D collision)  {
    if (collision.gameObject.tag == "Arrow")  {
        //데미지
        hp--;
        if (hp <= 0)  {
            //사망!
            //=====================
            //사망 연출
            //=====================
            //출돌 판정 비활성
            GetComponent<CircleCollider2D>().enabled = false;
            //이동 정지
            rbody.velocity = new Vector2(0, 0);
            // 애나메이션 변경
            Animator animator = GetComponent<Animator>();
            animator.Play(deadAnime);
            //0.5초 후에 제거
            Destroy(gameObject, 0.5f);
        }
    }
}

스크립트를 Enemy에 적용하고 전역변수 스트링을 적어줍니다. 스트링은 애니메이션의 이름입니다.(애니메이션객체가 이닙니다.)

여기가지 작업하고 씬뷰의 Enemy오브젝트를 Enemy폴더로 끌어다 프리팹으로 만들어 줍니다.

이번에는 게임에 필요한 아이템을 만들어보겠습니다.  맵에 아이템을 배치해 플레이어 캐릭터가 접촉하면 얻을 수 있도록 합니다. 보물 상자를 만들어 그 안에 아이템을 숨겨두는 장치도 만들겠습니다.  이번 예제에서 만들 아이템은 열쇠, 화살, 생명입니다. 멀티 스프라이트로 아이템의 이미지 에셋을 준비했습니다. Item이미지 에셋을 Item폴더에 옮기고Multiple Unit:32로 설정하고 [Sprite Editor]에서 32x32픽셀로 자릅니다.

 

아이템게임 오브젝트 만들기

Item을 Multi로 짤랐기 대문에 프로젝트뷰에서 items오른쪽▶를 누르면 아이템들이 보입니다.

Items_0~3을 끌어다 씬뷰에 놓고 Key, Arrow, Life로 이름을 바꿉니다.

각각의 아이템에 Rigidbody2D를 어태치하고 Gravity Scale:0, Circle Collider2D도 어태치하고 isTrigger 체크합니다. 충돌판정 범위는 오브젝트 크기보다 약간 크게 합니다. 나중에 보물에서 아이템이 나왔을때 플레이어에 충돌판정이 적용되도록 하기 위해서입니다. 

 

아이템 관리 스크립트 만들기

ItemData 스크립트를 만들어 Item폴더에 만들고 Item오브젝트에 어태치합니다. Item폴더로 끌어 프리팹으로 만듭니다. Item폴더에는 이미 프리팹이 만들어져 있었는데 Sprite에 이미지가 연결안되어 있어 연결해 주었습니다.

ItemData.cs
0.00MB

enum 열거형

//아이템 종류
public enum ItemType{
    arrow,      //화살
    key,        //열쇠
    life,	   //생명
}

변수

public ItemType type;           //아이템의 종류
public int count = 1;           //아이템 수
public int arrangeId = 0;       //식별을 위한 값

OnTriggerEnter2D()

 

private void OnTriggerEnter2D(Collider2D collision) {
    if (collision.gameObject.tag == "Player") {
        if (type == ItemType.key)  {
            //열쇠
            ItemKeeper.hasKeys += 1;
        }  else if (type == ItemType.arrow)  {
            //화살
            ArrowShoot shoot = collision.gameObject.GetComponent<ArrowShoot>();
            ItemKeeper.hasArrows += count;
        }  else if (type == ItemType.life) {
            //생명
            if (PlayerController.hp < 3) {
                //HP가 3이하면 추가
                PlayerController.hp++;
                //HP 갱신
                PlayerPrefs.SetInt("PlayerHP", PlayerController.hp);
            }
        }
        //++++ 아이템 획득 연출 ++++
        //충돌 판정 비활성
        gameObject.GetComponent<CircleCollider2D>().enabled = false;
        //아이템의 Rigidbody2D가져오기
        Rigidbody2D itemBody = GetComponent<Rigidbody2D>();
        //중력 젹용
        itemBody.gravityScale = 2.5f;
        //위로 튀어오르는 연출
        itemBody.AddForce(new Vector2(0, 6), ForceMode2D.Impulse);
        //0.5초 뒤에 제거
        Destroy(gameObject, 0.5f);
    }
}

보물 상자 만들기

아이템을 담아두는 상자를 만듭니다. 아이템 상자 이미지를 씬 뷰에 끌어다 게임 오브젝트를 만듭니다. 게임오브젝트 이름은 ItemBox로 변경하고 Itembox태그를 만들어 설정한후 Sprite Renderer의 Order in Layer는 2로 합니다.

플레이어가 움직일때 되도록 걸리지 않게 Circle Collide 2D로 설정합니다. 

 

보물상자 관리 스크립트 만들기

ItemBox 스크립트를 Item폴더에 만들고 ItemBox에 어태치합니다.

ItemBox 스크립트에  OpenImage를 연결해준다. ItemPrefab은 Key 프리팹을 연결한다.

다만들어진 ItemBox 오브젝트를 Item폴더로 끌어서 프리팹으로 만들어 줍니다. 교재에 Itembox 프리팹이 미리 만들어져어져 있어 SpriteRenderer의 Sprite에 item_3를 연결해주었습니다.

 

변수

public Sprite openImage;        //열린 이미지
public GameObject itemPrefab;   //담겨있는 아이템의 프리펩
public bool isClosed = true;    //true=닫혀있다 false=열려 있다.
public int arrangeId = 0;       //배치 식별에 사용

OnCollisionEnter2D()

플레이어와 접축하면 openImage로 스프라이트를 교체해줘 열린 효과를 줍니다. itemPrefab을 이용해서 게임오브젝트를 배치합니다.

//접촉 (물리)
private void OnCollisionEnter2D(Collision2D collision) {
    if (isClosed && collision.gameObject.tag == "Player"){
        //상자가 닫혀 있는 상태에서 플레이어와 접촛
        GetComponent<SpriteRenderer>().sprite = openImage;
        isClosed = false;   //열린 상태로 하기
        if (itemPrefab != null) {
            //프리펩으로 아이템 만들기
            Instantiate(itemPrefab, transform.position, Quaternion.identity);
        }
    }
}

게임 실행하기

플레이어가 보물상자를 접촉하면 보물상자가 열리고 아이템을 만들어줍니다. 아이템이 생기면 Player의 ItemData를 설정해주고 Y방향으로 0.5초 튀어오르다 중력으로 떨어집니다.

9.1 씬에서 씬으로 이동하기

레벨의 출입구와 연동해서 레벨을 이동하도록 합니다.   RoomManager라는 폴더를 만들어 관련 리소스를 저장합니다.

 

출입구 게임 오브젝트와 스크립트 만들기

플레이어가 접촉하면 씬을 이동하기 위한 게임 오브젝트와 스크립트를 만듭니다.

WorldMap 씬을 로딩합니다.

플레이어 프리팹을 배치합니다.

계층 뷰의 + > Create Empty를 클릭하고 이름을 Exit로 하고 알기쉽게 칼라아이콘을 붙입니다. Exit라는 Tag도 만들어 적용합니다.

CircleCollider2D 컴포넌트를 추가하고 타일에 맞게 크기를 조금 작게 합니다. 크기는 EditCollider를 클릭후 원위의 점을 끌면 됩니다.  IsTrigger를 체크합니다. 출입구 타일 위에 배치합니다. 칼라아이콘을 적용해 아이콘위에 이름이 보여 쉽게 식별이 가능합니다.

RoomManager폴더에 다음과 같이 Exit 스크립트를 만들어 저장합니다.

Exit.cs
0.00MB

enum ExitDirection

ExitDirection라는 enum Type 에 4개의 방향을 정의했습니다. 방향을 0,1,2,3같은 숫자로 사용하는 것보다 직관적입니다.

//출입구 위치
public enum ExitDirection
{
    right,  //오른쪽
    left,   //왼쪽
    down,   //아래쪽
    up,     //위쪽
}

변수

이동할 씬, 씬의 문번호, 캐릭터가 바라볼 방향을 뜻합니다.

public string sceneName = "";   //이동할 씬 이름
public int doorNumber = 0;      //캐릭터가 위치할 문 번호
public ExitDirection direction = ExitDirection.down;//문에서 나온 캐릭터가 바라보는 방향

OnTriggerEnter2D()

충돌이 일어나고 Tag가 Player라면 정해진 씬과 doorNumber를 전달하고 ChangeScene()을 호출한다.

private void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.tag == "Player")
    {
        RoomManager.ChangeScene(sceneName, doorNumber);
    }
}

 

방을 관리하는 게임 오브젝트 만들기

방을 관리하는 게임오브젝트와 클래스를 만듭니다. 빈오브젝트를 씬에 배치하고 이름을 RoomManager로 합니다.

RoomManager 스크립트를 RoomManager 폴더에 만들고 씬뷰의 RoomManager오브젝트에 어태치합니다.

RoomManager.cs
0.00MB

선언

씬을 처리하기 위해 선언합니다.

using UnityEngine.SceneManagement;

변수

doorNumber를 static으로 선업합니다. 그래야 씬이 바뀌어도 유지됩니다.

// static 변수
public static int doorNumber = 0;   //문 번

Start()

Exit 라는 Tag를 전부찾아 enters라는 배열에 저장한다

c#에서는 Type[] name; 으로 배열을 선언한다.

배열에서 GameObject를 꺼내 Exit 컴포넌트의 doorNumber와 static doorNumber를 비교해서 찾은 door의 위치에 플레이어를 배치하고 도어의 뚫린 방향으로 이동시켜준다. 그래야 조금 나와 있는것 처럼 보이니까.

void Start() {
    //플레이어 캐릭터 위치
    //출입구를 배열로 얻기
    GameObject[] enters = GameObject.FindGameObjectsWithTag("Exit");
    for (int i = 0; i < enters.Length; i++)  {
        GameObject doorObj = enters[i]; //배열에서 꺼내기
        Exit exit = doorObj.GetComponent<Exit>();   //Exit 클래스 변수
        if (doorNumber == exit.doorNumber)  {
            //==== 같은 문  번호 ====
            //플레이어 캐릭터를 출입구로 이동
            float x = doorObj.transform.position.x;
            float y = doorObj.transform.position.y;
            if (exit.direction == ExitDirection.up)  {
                y += 1;
            }  else if (exit.direction == ExitDirection.right)  {
                x += 1;
            } else if (exit.direction == ExitDirection.down) {
                y -= 1;
            }  else if (exit.direction == ExitDirection.left) {
                x -= 1;
            }
            GameObject player = GameObject.FindGameObjectWithTag("Player");
            player.transform.position = new Vector3(x, y);
            break;  //반복문 빠나오기
        }
    }
}

public static void ChangeScene()

doornum을 doorNumber에 저장하고 scenename의 씬을 호출합니다. 

ChangeScene()은  외부에서 static doorNumber를 변경시키므로 public static 으로 선언됩니다.

public static void ChangeScene(string scnename, int doornum)
{
    doorNumber = doornum;   //문 번호를 static 변수에 저장

    //string nowScene = PlayerPrefs.GetString("LastScene");
    //if (nowScene != "")
    //{
    //    SaveDataManager.saveArrangeData(nowScene);      //配置データを保存
    //}
    //PlayerPrefs.SetString("LastScene", scnename);   //シーン名を保存
    //PlayerPrefs.SetInt("LastDoor", doornum);        //ドア番号保存
    //ItemKeeper.SaveItem();                          //アイテムを保存

    SceneManager.LoadScene(scnename);   //シーン移動
}

여기까지 완료한 RoomManager와 Exit를 RoomManager폴더로 끌어 프리팹으로 만듭니다. (예제폴더를 사용하시면 이미 만들어져 있습니다.)

 

출입구 배치하기

레벨의 출입구에 Exit프리팹을 배치하고 스크립트에 SceneName을 적고 Door Number를 순서대로 0,1,2... 으로 적습니다. direction은 플레이어가 나갈 방향을 정합니다.

 

문 만들기

출입구를 막는 문을 만듭니다. 열쇠가 있다면 열립니다. 문은 나중에 만들 Item과 비스하므로 Item폴더를 만들고 저장합니다.

Item.zip
0.02MB

문 이미지는 Door입니다. Door 이미지도 Item폴더로 옮깁니다. 32x32픽셀의 도트 이미지이므로 Pixel Per Unit를 32로 하고 Filter Mode를 Point로 합니다.

이미지 설정후 Door를 씬뷰로 끌어다 놓고 BoxCollider2D와 Order In Layer 1로 하고 Door Tag를 만들어 적용합니다.

열쇠를 가지고 있으면 열수 있도록 스크립트를 만들어 봅니다.

Door 스크립트를 Item폴더에 저장하고 Door오브젝트에 어태치합니다.

Door.cs
0.00MB

변수

public int arrangeId = 0;       //식별에 사용하기 위한 값

OnCollisionEnter2D(Collision2D collision)

"Player"와 충돌이 있으면 열쇠를 하나 감소 시키고 문을 열어 줍니다.

void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.tag == "Player")
    {
        //열쇠를 가지고있으면
        if (ItemKeeper.hasKeys > 0)
        {
            ItemKeeper.hasKeys--;       //열쇠를 하나 감소
            Destroy(this.gameObject);   //문 열기
        }
    }
}

 

Door를 배치합니다.

Door를 배치하면 키가 없으면 들어갈수 없습니다. 테스트로 ItemKeeper 스크립트의 변수를 바꿔주면

public static int hasKeys = 1;          //열쇠 수
public static int hasArrows = 10;        //화살 소지수

처음 한번은 통과하지만 dungeon1으로 갔다 다시 와서 들어가려고 하면 키가 없어 못들어가는걸 확인할 수 있습니다.

 

dungeon1씬에도 Exit배치

dungeon1 씬에도  Exit를 배치하고 스크립트에 WorldMap과 방향을 down으로 하면 dungeon1에서 WorldMap으로도 이동할 수 있습니다.

+ Recent posts