1. Manager 관련 코드에는 monobehavior은 안쓰는게 좋다.

-굳이 필요가 없다

2. UI요소들의 크기를 조정할 때는 Scale을 조정하기보다 Anchor와 width height로 조절하는게 맞다

- Scale은 곱연산으로 중첩되어 들어가기 때문에 각 요소들의 크기를 결정하기 쉽지 않지만 Anchor와 width, height를 조정하면 지정한 값으로 들어가기 때문에 훨씬 좋다.

3. 코드를 잘 읽고 ctrl + 12 + 함수 및 매서드 정의 잘 보기

 해당 함수가 사용된 곳을 찾으면서 어떻게 함수가 쓰이는지 어떤 방식으로 구현되어 있는 지 확인하고 이를 활용하여 자신만의 코드를 만드는게 효율적이다.

 

계속 업데이트 예정...

그동안 협업으로 프로젝트를 진행 해본적은 있지만 애자일방법론 같은 정말 협업에 사용되는 방법을 따라 개발해본적은 없어 이번 프로젝트를 통해 기획단계에서 부터 클래스 다이어그램, 시퀸스 다이어그램, 요구사항 명세서등을 어떻게 작성하는 지 익히고 사용해보기로 하였다.

1. 클래스 다이어그램

클래스 다이어그램의 정의를 보자

클래스 다이어그램은 구조 다이어그램으로 클래스 내부 구성요소 및 클래스 간의 관계를 도식화하여 시스템의 특정 모듈이나 일부 및 전체를 구조화한다.

클래스 다이어그램에서 클래스는 이름, 속성(변수), 메소드 순으로 나열하고 속성과 메소드 사이에 선을 그어 분류한다.

변수 선언

+ public
-  private
#  protected 
~  default 
{readonly} final
밑줄 static
[*] or [0..1] 리스트 사이즈 

 

클래스 간 관계

이미지 참조:  https://en.wikipedia.org/wiki/Class_diagram

●Association

Association은 참조 관계일때 사용하고  A -> B 이면 A가 B를 참조 A - B 이면 A가 B를 B가 A를 참조할 수 도 있고 둘 다 참조 이거나 둘 다 참조가 아니다.

 

Inheritance

Inheritance는 상속관계로 부모 클래스와 자식 클래스 간의 상속관계에 사용

 

Realization

Realization은 인터페이스를 상속하여 클래스에서 실제 기능을 구현할 때 사용

 

Dependency

Dependency는 클래스간의 참조관계에서 사용하지만 Association은 변수로 다른 클래스와 연관이 있을 때 사용하지만 Dependency는 메소드의 파라미터나 반환에 사용되는 클래스 관계에서 사용

 

Aggregation

Aggregation은 집합관계에서 사용

 

Composition

Aggregation과 비슷하게 전체 - 부분의 집합 관계를 나타낼 때 사용하지만 Aggregation 보다는 더 강력한 집합을 의미할 때 사용합니다. 합성 관계에서는 부분이 전체에 종속적이고 라이프 사이클을 관리한다 라고 볼 수 있다.

우리는 유투브 강의를 보고 따라서 Vampire Survivor라이크류 게임을 만들어보았다. 이제 우리만의 스테이지와 몹을 만들고 이를 통해 데이터를 모아서 AI모델을 통해 유저의 생존시간과 시도 횟수를 예측해보기로 하였다.
 
1. 우리만의 스테이지
기존의 강의에서는 스테이지가 하나밖에 없어서 여러 스테이지를 깨고 그 다음스테이지를 예측해야하는 우리에게는 여러가지 스테이지가 필요했다. 그렇게 여러 스테이지를 생성하는 법을 공부하였는데 이때 사용하는 것이 맵을 게임오브젝트로 만들어 두고 GameManager에서 Stages라는 Gameobject 배열로 만들고 이곳에 여러 스테이지를 저장한 뒤 기존의 스테이지가 끝나면 스테이지맵의 게임오브젝트 Active를 false로 바꿔주고 다음  스테이지맵의 게임 오브젝트 Active를 true로 만들어 주면 된다. 
Stages[stageIndex].SetActive(false);
stageIndex++;
Stages[stageIndex].SetActive(true);
 
이러한 코드를 GameManager 코드에 NextStage() 메서드로 구현해두었다.  또한 Player의 위치를 다시 원점으로 돌려서 스테이지를 플레이할 수 있게 
player.transform.position = Vector3.zero;
 
이 코드도 함께 넣어 구현해 주었다. 

Backyard Top-Down Tileset(FREE)
https://assetstore.unity.com/packages/2d/environments/backyard-top-down-tileset-53854

 

Backyard Top-Down Tileset | 2D 주변환경 | Unity Asset Store

Elevate your workflow with the Backyard Top-Down Tileset asset from Kittens and Elves at Work. Find this & more 주변환경 on the Unity Asset Store.

assetstore.unity.com

https://assetstore.unity.com/packages/2d/characters/top-down-2d-rpg-assets-pack-188718

 

Top-Down 2D RPG Assets Pack | 2D 캐릭터 | Unity Asset Store

Elevate your workflow with the Top-Down 2D RPG Assets Pack asset from Goldmetal. Find this & more 캐릭터 on the Unity Asset Store.

assetstore.unity.com

 
 
골드메탈님의 강의에서 무한맵처럼 보이게 맵을 이동하는 것을 코드로 구현했었는데 이 코드는 그대로 사용하고 각 스테이지별 기존 맵과 동일한 크기의 타일맵을 생성하여서 스테이지가 달라지는 것을 알 수 있도록 구현하였다. 
타일맵을 생성하는데에는 위의 두 Asset을 활용하여 만들었다.
 
private void OnTriggerExit2D(Collider2D collision)
{
 
    switch (transform.tag)
    {
        case "Ground":
            float diffX = playerPos.x - myPos.x;
            float diffY = playerPos.y - myPos.y;
            float dirX = diffX < 0 ? -1 : 1;
            float dirY = diffY < 0 ? -1 : 1;
            diffX=Mathf.Abs(diffX);
            diffY=Mathf.Abs(diffY);
            if (diffX > diffY)
            {
                transform.Translate(Vector3.right * dirX * 40);
            }
            else if (diffX < diffY)
            {
                transform.Translate(Vector3.up * dirY * 40);
            }
            break;
    }

}

만약 플레이어 밑에 있는 Area 오브젝트가 Ground의 끝을 만났을때 맵을 그 길이만큼 왼쪽이나 오른쪽 또는 위 아래로 움직여주는 코드이다. 
 
2.우리만의 몹 만들기
기존의 강의에서는 몹이 2개만 생성가능했기 때문에 우리는 총 4개의 스테이지에 맞는 여러 몬스터를 더 넣고자 했다. 

https://assetstore.unity.com/packages/2d/characters/2d-monster-undead-spum-premium-addon-pack-200884

 

2D Monster Undead - SPUM Premium Addon Pack | 2D 캐릭터 | Unity Asset Store

Elevate your workflow with the 2D Monster Undead - SPUM Premium Addon Pack asset from soonsoon. Find this & more 캐릭터 on the Unity Asset Store.

assetstore.unity.com

Undead Survivor에 맞게 좀비 Asset을 찾아서 넣어주었다. 가격은 $9.99정도이다. 이 Asset을 처음 받았을 때 기존에 강의에서 활용하던 몬스터 Asset과 구조가 많이 달라서 사용법을 찾는데 오래 걸렸다. 기존에 Asset은 제일 상위 오브젝트에 Animator을 붙이고 이를 통해 Animation이 작동했었는데 이 Asset은 하위에 UnitRoot가 있고 이 아래에 몬스터의 몸을 이루는 부분들이 있어서 이 UnitRoot를 통해 해당 몬스터의 Animation이 작동하는 것을 알았다. 그래서 이 UnitRoot에 기존에 가지고 있던 Animator에 Hit라는 Trigger과 Dead라는 Bool 값을 넣어서 만약 유저에게 데미지를 입었을때의 Animation과 몬스터의 체력이상의 공격을 당했을 때 Dead에 해당하는 Animation이 작동하도록 했다.
void OnTriggerEnter2D(Collider2D collision)
{
    if (!collision.CompareTag("Bullet"))
        return;
    health -= collision.GetComponent<Bullet>().damage;
    StartCoroutine(KnockBack_1());
    if (health > 0)
    {
        anim.SetTrigger("Hit");
        AudioManager.instance.PlaySfx(AudioManager.Sfx.Hit);
    }
    else
    {
        isLive = false;
        coll.enabled = false;
        rigid.simulated = false;
        anim.SetBool("Dead", true);
        StartCoroutine(Dead());
        GameManager.instance.kill++;
        GameManager.instance.GetExp();

        if (GameManager.instance.isLive)
            AudioManager.instance.PlaySfx(AudioManager.Sfx.Dead);
    }
}
 
여기서 Bullet은 유저의 무기이다. 

 
 
Enemy에 들어갈 Animator도 수정하였다. 또한 Spawner에 spawn 메서드를 통해 몬스터가 생성되는데 Stage에 따라 시작 몬스터와 몇초마다 다른 몬스터가 생성되는지를 코드로 구현하여 스테이지마다 몬스터가 바뀔수 있도록 구현하였다.
 
 void spawn()                        //0번째부터시작 1,2번째는 총알이라 놔두기 
 {
     GameObject enemy;
     if (GameManager.instance.stageIndex == 0)           //1스테이지는 레벨에 따라
     {
         if (level == 0)
         {
             enemy = GameManager.instance.pool.Get(level);    //SpawnData 인스펙터창
         }
         else
         {
             enemy = GameManager.instance.pool.Get(level + 2);    //SpawnData 인스펙터창
         }
         enemy.transform.position = spawnPoint[Random.Range(1, spawnPoint.Length)].position;
         enemy.GetComponent<Enemy_new>().Init(spawnData[level]);
     }
     else
     {                                                           //2스테이지부터는 레벨+스테이지 인덱스번째의 몹부터 소환
         if (level == 0)
         {
             enemy = GameManager.instance.pool.Get(level+2+GameManager.instance.stageIndex);    //SpawnData 인스펙터창
         }
         else
         {
             enemy = GameManager.instance.pool.Get(level +2+ GameManager.instance.stageIndex-1);    //SpawnData 인스펙터창
             if((level + 2 + GameManager.instance.stageIndex) == GameManager.instance.pool.prefabs.Length)
             {
                 enemy = GameManager.instance.pool.Get(GameManager.instance.pool.prefabs.Length-1);    //SpawnData 인스펙터창
             }
         }
         enemy.transform.position = spawnPoint[Random.Range(1, spawnPoint.Length)].position;
         enemy.GetComponent<Enemy_new>().Init(spawnData[level]);
     }
 }
 
또한 몬스터마다 체력, 스폰시간, 체력, 속도등을 정하여 입력해 주었다. 

 

내가 코딩을 배우고 코딩으로 무엇인가를 만드는 걸 목표로 하게 되었을 때부터 내 오랜 목표는 누구나 쉽고 재밌게 즐길 수 있는 게임(RPG)를 만드는 것이다. 그리고 나는 현실처럼 많은 상호작용을 게임안에서 할 수 있고 또 다른 세상을 유저가 만들어가기도 하고 게임을 진행하는 데 있어서 하나의 루트만 존재하는 것이 아니라 유저에 따라 여러루트가 존재할 수 있는 그런 게임을 만들고 싶다.

그렇게 꿈을 향해서 나아가던 중에 몰입에 있어서 중요한 역활을 하는 게임 난이도를 AI가 바꿀 수 있으면 어떨까라는 생각을 친구와 나누게 되었고 이 아이디어를 실제로 학기 중에 공부하고 구현해보자 라는 계획을 세우게 되었다. 그래서 나와 친구는 학교에 직접 한 학기의 수업을 만들어서 진행할 수 있는 것이 있어서  유저의 게임실력을 받아서 게임의 난이도를 유저에 맞게 변경해주는 것을 만들기로 하였다. 

처음에는 rpg게임으로 구현을 해보려 했지만 생각보다 시간이 많이 소요될 것 같고 게임보다는 실제로 작동하는 AI모델을 만들어 보려고했기때문에 이러한 데이터를 더 잘 받을 수있는 뱀서라이크 게임을 만들어서 모델에 사용하기로 하였다. 그래서 뱀서라이크 게임을 만드는 영상을 찾다가 골드메탈님의 영상을 보게 되었고 이것을 바탕으로 우리의 팀만의 차별성(여러스테이지, 몬스터종류의 차별성)을 조금 넣어서 모델에 넣어보자는 결론이 나왔다. 

 

밑에는 해당 강좌의 시작 영상이 있다.

https://www.youtube.com/watch?v=MmW166cHj54&list=PLO-mt5Iu5TeZF8xMHqtT_DhAPKmjF6i3x&index=1&t=12s

 

 

 

+ Recent posts