A.1 삼각함수란?

직각삼감형의 각도에 따른 변 길이의 비율을 반환하는 함수 입니다. 

삼각함수로 사인, 코사인, 탄젠트라는 단어를 들어본 적이 있을 겁니다.

 

A.2 각도로 좌표 구하기 

플레이어가 적 캐릭터에게 사격을 하기위해 적과의 각도를 알면 다음과 같이 계산할수 있습니다.

float x = Mathf.Cos(라디안)
float y = Mathf.Sin(라디안)
Vector3 v = new Vector3(x,y) * shootSpeed;

유니티가 제공하는 삼각함수.

Mathf.cos, Mathf.sin Mathf.Tan

유니티가 제공하는 삼각함수는 각도를 매개변수로 사용하지 않고 라디안(호도법)을 사용합니다.

각도 = 라디안 x(180/원주율)

라디안=각도x(원주율/180)입니다.

유니티는 계산을 편하게 하기위해 Mathf.Deg2Rad, Mathf.Rad2Deg를 마련해 놨습니다.

 

라디안

아래 그림은 각도는 한바퀴를 360도라고 정의했을때 비율이다.

라디안은 반지름1인 원이 한바퀴 도는 둘레가 2πr인데 반지름이 1이름로 2π=360도 이다.

라디안은 결국 쉽게 말하면 한바퀴도는데 2π니까 1도 = 2π/360 = π/180 임.

역으로하면 360도 = 2π 니까 π=180도 뭐 그닥 와닫지는 않지만.

어려운건 아님.  각도는 바퀴고 라디안은 굴러간거리라고 생각하면 편함.

가만 생각해보면 원이 한바퀴도는건 파이=3.141592아니었나 생각되지만 그건 지름이 1일 경우이고 라디안은 반지름이1 지름이 2일 경우이다. ㅠㅠ

삼각함수도 반지름이 1일경우로 원을 그려 생각하면 엄청 쉬움

cosθ+sinθ=1 이게 원을 뜻한다.

x*x+y*y=1 도 원이지만. 

[네이버 지식백과]라디안[Radian] (물리학백과)

 

A.3 좌표에서 각도 구하기 

플레이어의 애니메이션을 선택하기 위해 플레이어의 이동각도를 알아야 할 때가 있습니다.

플레이어가 점 p1에서 p2로 이동할때 각도는 다음과 같습니다.

float dx = p2.x - p1.x;
float dy = p2.y - p1.y;
float angle = Mathf.Atan2(dy,dx)*Mathf.Rad2Deg;

Atan2는 좌표를 입력하면 각도를 구해주는 함수입니다.

 

A.4 자주쓰는 삼각함수 메서드

특정 게임 오브젝트 추적하기

Vector2 GetToVector(Vector2 pos) {
	float dx = pos.x-transform.position.x;
    float dy = pos.y-transform.position.y;
    float rad = Mathf.Atan2(dy,dx);
    //벡터만들기
    float x = Mathf.Cos(rad);
    float y = Mathf.Sin(rad);
    Vector2 v = new Vector2(x,y);  //방향
    return v;
}

특정 게임 오브젝트를 향하도록 회전시키기

GetAngle(Vector2 from, Vector2 to) {
	float dx = to.x-from.x;
    float dy = to.y-from.y;
    float rad = Mathf.Atan2(dy,dx);
    return rad * Mathf.Rad2Deg;
}

위함수를 Update()에서 호출해서 게임오브젝트을 로테이션 시킵니다.

Update(){
	float angle = GetAngle(transform.position, 이동 위치의 position);
    transform.rotation = Quaternion.Euler(0,0,angle);
}

다음 메소드는 이동속도를 회전각도로 반환합니다.

float velocityToAngle(){
	float r = 0;
    Rigidbody2D body = GetComponent<Rigidbody2D>();
    if(body !=null) {
    	Vector2 v = body.velocity;
        r = Mathf.Atan2(v.y, v.x) * Mathf.Rad2Deg;
    }
    retur r;
}

Update()에서 다음같이 사용됩니다.

Update() {
	float angle = velocityToAngle();
    transform.rotation = Quaternion.Euler(0,0,angle);
}

'유니티스크립팅 > 게임수학' 카테고리의 다른 글

벡터  (0) 2023.04.25

게임클리어 SE

Exit 스크립트의 OnTriggerEnter2D메서드에서 게임 클리어 SE를 재생합니다. 재생중인 BGM을 정지시키기 위해 StopBgm메서드도 호출합니다.

Exit.cs
0.00MB

 Exit()

private void OnTriggerEnter2D(Collider2D collision) {
    if (collision.gameObject.tag == "Player") {
        if (doorNumber == 100) {
            //BGM 정지
            SoundManager.soundManager.StopBgm();
            //SE 재생 (게임 클리어)
            SoundManager.soundManager.SEPlay(SEType.GameClear);
            //게임 클리어
            GameObject.FindObjectOfType<UIManager>().GameClear();
        } else {
            string nowScene = PlayerPrefs.GetString("LastScene");
            SaveDataManager.SaveArrangeData(nowScene); // 배치데이터 저장
            RoomManager.ChangeScene(sceneName, doorNumber);
        }
    }
}

게임오버SE

PlayerController 스크립트의 GameOver메서드에서 게임오버 SE를 재생합니다.  재생중인 BGM을 정지시키기 위해 StopBgm메서드도 호출합니다.

void GameOver()
{
~생략
    //BGM 정지
    SoundManager.soundManager.StopBgm();
    //SE재생 (게임 오버)
    SoundManager.soundManager.SEPlay(SEType.GameOver);
}

활쏘기 SE

ArrowShoot스크립트의 Attack메서드에서 게임오버 SE를 재생합니다.

ArrowShoot::Attack()

public void Attack()
{
    //화살을 가지고 있음 & 공격중이 아님
    if (ItemKeeper.hasArrows > 0 && inAttack == false)
    {
		~생략

        //SE재생(활 쏘기)
        SoundManager.soundManager.SEPlay(SEType.Shoot);
    }
}

게임 플레이

책을 따라하다 보니 좀 엉망진창이 되어 잘 플레이가 안된다. 교재에서 CH10 Assets을 다운 받아 적당히 손질했다.

Assets.zip
15.17MB

블로거는 최신 2020.3.48f를 사용중이다. 

 

TitleManager 스크립트 변경 

TitleManager.cs
0.00MB

타이틀 화면의 BGM

타이틀 화면의 BGM은 TitleManager 스크립트에서 실행합니다. Start메서드에 다음과 같이 추가합니다. 

SoundManager클래스의 soundManager변수는 맨처음 생성된  SoundManager가 저장된 변수입니다.

이 값으로 SoundManager클래스의 메서드를 호출할 수 있습니다. BGMType.title을 인수로 전달해 PlayBgm메서드를 호출할 수있습니다.

void Start()  {
    string sceneName = PlayerPrefs.GetString("LastScene");      //저장 된 씬
    if (sceneName == "")
    {
~생략
    }
    //타이틀 BGM 재생
    SoundManager.soundManager.PlayBgm(BGMType.Title);
}

게임중/보스전 BGM

RoomManager.cs
0.00MB

게임중 각 씬의  BGM은 RoomManager의 Start 메서드에서 실행합니다. 현재 저장된 현재의 씬이름을 PlayerPrefs.GetString 으로 읽어 보스 스테이지의 씬 이름인지 확인후 재생할 BGM을 바꿉니다.

Start()

    void Start()
    {
        //플레이어 캐릭터 위치
        //출입구를 배열로 얻기
~생략
         // 씬 이름 얻기
        string scenename = PlayerPrefs.GetString("LastScene");
        if (scenename == "BossStage") {
            //보스 BGM 재생
            SoundManager.soundManager.PlayBgm(BGMType.InBoss);
        }   else{
            // 게임 중 BGM 재생
            SoundManager.soundManager.PlayBgm(BGMType.InGame);
        }
    }

재시도후 SE

UIManager클래스의 Retry 메서드에도 다음과 같이 사운드 재생코드를 추가합니다. soundManager의playingBGM에 BGMType.None을 설정한 후 초기화하면 씬을 불러온 뒤 각 스테이지의 BGM이 재생됩니다.

UIManager.cs
0.00MB

Retry()

    public void Retry()
    {
        //HP 되돌리기
        PlayerPrefs.SetInt("PlayerHP", 3);

        //BGM 제거
        SoundManager.plyingBGM = BGMType.None;
        
        //게임 중으로 설정
        SceneManager.LoadScene(retrySceneName);   //씬 이동
    }

 

사이드뷰 게임에서는 씬이 바뀔때 마다 BGM을 처음부터 재생했습니다. 이번 예제에서는 씬이 바귀어도 게임이 이어지기 때문에 타이틀화면, 게임화면, 보스전등에서만 BGM이 바뀌도록 합니다.

게임 클리어, 게임오버, 활을 쏠때만 SE(사운드이펙트)를 적용합니다. 예제에서는 버튼이 눌릴때 문이열릴때 문이 닫힐대 아이템을얻을때 대미지를 받을때 적 사망,보스사망등 다양한 곳에 SE를 적용했습니다.

 

사운드 재생 오브젝트와 스크립트 만들기

하이라키에 빈오브젝트를 만들고 이름을 SoundManager라고 합니다.  SoundManager스크립트를 만들어 하이라키의 SoundManager게임오브젝트에 어태치합니다. 

AddComponent로 Audio Source를 추가하고 Play On Awake 체크해제하고 Loop는 체크합니다. BGM제어는 나중에 스크립트에서 제어합니다. Audio 클립은 비워둡니다.

 

씬이 바뀌어도 BGM이 끊어지지 않게 재생한다.

 

씬이 바뀌어도 BGM이 끊어지지 않게 재생하려면 SoundManager가 같은 게임 오브젝트로 존재해야 합니다.

열거형

public enum BGMType
{
    None,       //없음
    Title,      //타이틀
    InGame,     //게임 중
    InBoss,     //보스전
}
//SE 종류
public enum SEType
{
    GameClear,  //게임 클리어
    GameOver,   //게임 오버
    Shoot,      //활 쏘기
}

변수

public AudioClip bgmInTitle;    //타이틀 BGM
public AudioClip bgmInGame;     //게임 중 BGM
public AudioClip bgmInBoss;     //보스전 BGM

public AudioClip meGameClear;   //게임 클리어
public AudioClip meGameOver;    //게임 오버
public AudioClip seShoot;       //활 쏘기

static 변수

씬이 바뀌어도 값이 변화되지 않게 static변수를 마련합니다. 자신을 저장하는 soundManager와 재생중이 BGMType을 저장합니다.

public static SoundManager soundManager;    //첫 SoundManager를 갖는 변수
public static BGMType plyingBGM = BGMType.None;    //재생 중인 BGM

Awake()

Start()보다 앞에서 호출되는  Awake()에서 이전에 실행되었던 soundManager가 있으면 정리합니다.

게임이 실행되고 맨처음 SoundManager가 실행되면 soundManager변수는null입니다. 그때 DontDestroyOnLoad메서드를 호출해 게임 오브젝트가 파기되지 않도록 합니다. soundManager변수는 static이므로 씬이 바뀌어도 값이 유지됩니다.

DontDestroyOnLoad(gameObject)를 이용 씬이 바뀌어도  

private void Awake()
{
    //BGM 재생
    if (soundManager == null)
    {
        soundManager = this;  //static 변수에 자기 자신을 저장
        //씬이 바뀌어도 게임 오브젝트를 파기하지 않음
        DontDestroyOnLoad(gameObject);
    }
    else
    {
        Destroy(gameObject);//게임 오브젝트르 파기
    }
}

PlayBgm()

플레이할 BGM타입이 다르면 AudioSource컴포넌트를 이용해 오디오 클립의 이름을 바꿔서 Play()합니다.

public void PlayBgm(BGMType type)
{
    if (type != plyingBGM)
    {
        plyingBGM = type;
        AudioSource audio = GetComponent<AudioSource>();
        if (type == BGMType.Title)
        {
            audio.clip = bgmInTitle;    //타이틀
        }
        else if (type == BGMType.InGame)
        {
            audio.clip = bgmInGame;     //게임 중
        }
        else if (type == BGMType.InBoss)
        {
            audio.clip = bgmInBoss;     //보스전
        }
        audio.Play();
    }
}

StopBgm()

BGM을 정지합니다. 

public void StopBgm()
{
    GetComponent<AudioSource>().Stop();
    plyingBGM = BGMType.None;
}

SEPlay(SEType type)

상황에 맞는 사운드 이펙트를 재생합니다.

public void SEPlay(SEType type)
{
    if (type == SEType.GameClear)
    {
        GetComponent<AudioSource>().PlayOneShot(meGameClear);   //게임 클리어
    }
    else if (type == SEType.GameOver)
    {
        GetComponent<AudioSource>().PlayOneShot(meGameOver);   //게임 오버
    }
    else if (type == SEType.Shoot)
    {
        GetComponent<AudioSource>().PlayOneShot(seShoot);       //활 쏘기
    }
}

UIManager스크립트 수정

보스 스테이지의 마지막 기능입니다. 보스를 쓰러뜨리고 열쇠로 문을 열고 나가면  "GAME CLEAR" 표시하고 게임을 종료합니다. 게임 클리어 표시는 UIManager 스크립트에서 처리합니다.

UIManager.cs
0.00MB

GameClear()

doorNumber가 100이면 게임 클리어로 판단돼 Exit스크립트에서 호출됩니다. 보스 방 맨 안쪽 문의 doorNum를 100으로 설정합니다.

//게임 클리어
public void GameClear()
{
    //화면 표시
    mainImage.SetActive(true);
    mainImage.GetComponent<Image>().sprite = gameClearSpr;//「GAMR CLEAR」 설정
    //조작 UI 숨기기
    inputPanel.SetActive(false);
    //게임 클ㄹ어 
    PlayerController.gameState = "gameclear";
    //3초 뒤에 타이틀 화면으로 이동
    Invoke("GoToTitle", 3.0f);
}

GoToTitle()

//타이틀 화면으로 돌아가기
void GoToTitle()
{
    PlayerPrefs.DeleteKey("LastScene");     //저장되어있는 씬을 제거
    SceneManager.LoadScene("Title");        //타이틀 씬으로 돌아가기
}

Exit스크립트 수정

doorNumber가 100이면 게임 클리어로 판단돼 Exit스크립트에서 호출합니다. 그렇지 않으면 원래대로 처리합니다.

private void OnTriggerEnter2D(Collider2D collision){
    if (collision.gameObject.tag == "Player") {
        if (doorNumber == 100)   {
            //BGM 정지
            SoundManager.soundManager.StopBgm();
            //SE 재생 (게임 클리어)
            SoundManager.soundManager.SEPlay(SEType.GameClear);
            //게임 클리어
            GameObject.FindObjectOfType<UIManager>().GameClear();
        } else  {
            string nowScene = PlayerPrefs.GetString("LastScene");
            SaveDataManager.SaveArrangeData(nowScene); // 배치데이터 저장
            RoomManager.ChangeScene(sceneName, doorNumber);
        }
    }
}

 

지금 까지 만든 보스 캐릭터로 최종 보스 스테이지를 만들겠습니다.

스테이지에 입장하면 방 안쪽에 보스가 있다 입장후 나올수 없다

보스 캐릭터 뒤에 열쇠가 있는 보물상자가 있다.

반 안쪽에 문이 있다.

보스를 쓸어트리고 보물상자에서 키를 얻어야 방을 나가고 클리어 됩니다.

보스는 무한히 총을 쏠수 있고 플레이어는 돌아 다니면서 화살을 줏어야 보충할 수 있습니다.

화살의 위치는 랜덤으로 몇군데 배치하겠습니다.

최종 보스용씬은 BossStage라는 이름으로 씬을 만들고 BuildSetting에 등록합니다. 타일맵으로 맵을 만듭니다.

BossStage.unity
0.12MB

예제를 다운로드하고 로딩하면 SoundManager에러가 나는데 일단 씬뷰에서 지워줍니다. 전 Cavas도 에러가 났는데 교재에서 다운받아 UIManager에 깔아줬습니다.

Canvas.prefab
0.04MB

 

게임 스테이지로 설정하기 위해 지금까지 만들었던 프리팹을 배치합니다.

Player : 프리팹을 추가하면 됩니다.

RoomManager : UIManager의 Retry Scene Name에 다시 시도할 때 불러올 씬 이름을 설정합니다.

Canvas : Main Camera를 설정합니다.

EventSystem :  +>UI>Event System

 

아이템을 자동으로 배치하는 기능 만들기

이제 아이템(화살)을 자동으로 배치하는 기능을 추가합니다.

화살의 수를 항상 확인한다

화살의 수가 0이 되면 화살을 랜덤위치에 배치합니다.

 

오브젝트 발생기

계측뷰 +>CreateEmpty를 클릭해 빈게임오브젝트를 만들오 Boss폴더에 이름을 ObjGen으로 하고 칼라아이콘을 추가합니다.

 

ObjectGenPoint 스크립트를 만들기

ObjectGenPoint.cs
0.00MB

ObjGen에 어태치합니다. 씬에 배치할 위치와 프리팹을 지정해 오브젝트를 만드는 스크립트입니다.

변수

public GameObject objPrefab;    //발생 시킬 Prefab 데이터

ObjectCreate() 메소드

현재 위치에 프리팹으로 게임오브젝트를 배치합니다. 외부에서 호출합니다.

public void ObjectCreate()  {
    Vector3 pos = new Vector3(transform.position.x,transform.position.y,-1.0f);
    //Prefab으로 GameObject 만들기
    Instantiate(objPrefab, pos, Quaternion.identity);

Item 폴더의 Arrow 프리팹을 끌어다 Obj Prefab에 지정합니다.

ObjGen을 Boss폴더로 끌어다 프리팹으로 만듭니다.

프리팹을 끌어다 씬뷰에 10개정도 추가합니다. 화살이 생성될 위치입니다.

 

ObjectGenManager스크립트

ObjectGenManager.cs
0.00MB

다음으로 배치된 ObjectGenPoint를 관리하고 화살의 수를 감시해 화살의 갯수가 0이 되면 배치하는 기능입니다.

완성후 하이라키뷰의 RoomManager에 어태치합니다. 프리팹이 아닌 씬에 배치된 오브젝트에 어태치해야 해당 씬에서만 스크립트가 동작합니다.

 

변수

씬에 배치된 objGen들의 참조를 담을 배열입니다.

ObjectGenPoint[] objGens;   //씬에 배치되어있는 ObjectGenPoint 배열

Start()

ObjectGenPoint 타입을 모두 찾아 objGens에 전달합니다.

void Start() {
    objGens = GameObject.FindObjectsOfType<ObjectGenPoint>();
}

Update()  

씬에서 Item을 찾아 화살이 있다면 리턴하고 없고 플레이어도 화살을 가지고 있지 않다면 임의의 위치에 화살을 배치합니다.

void Update()   {
    //ItemData 찾기
    ItemData[] items = GameObject.FindObjectsOfType<ItemData>();
    //반복문으로 화살 찾기
    for (int i = 0; i < items.Length; i++)  {
        ItemData item = items[i];
        if (item.type == ItemType.arrow) {
            return; //화살이 있으면 아무것도 하지 않고 메서드에서 빠져나가기
        }
    }
    //플레이어가 존재하는지와 화살의 수 확인
    GameObject player = GameObject.FindGameObjectWithTag("Player");
    if (ItemKeeper.hasArrows == 0 && player != null) {
        //화살 개수가 0이고 플레이어가 존재하면
        //배열의 개수 범위 안에서 난수 생성
        int index = Random.Range(0, objGens.Length);
        ObjectGenPoint objgen = objGens[index];
        objgen.ObjectCreate();   //아이템 배치
    }
}

 

Main Camera에 어태치된 CameraManager 클래스는 플레이어를 화면 중앙에 오도록 카메라를 조정합니다. 이게보스보드에서는 불편합니다. 따라서 Player폴더의 CameraManager  스크립트를 수정해 플레이어와 보스 중앙을 비추도록 합니다.

 

변수

otherTarget에 외부에서 Boss를 연결합니다.

public GameObject otherTarget;

Update()

두 오브젝트를 사이의 위치를 반환하는 Lerp()메서드의 세번째 인자를 0.5f를 사용해 otherTarget와 Player사이의 중간 지점을 구합니다. 

void Update() {
    GameObject player = GameObject.FindGameObjectWithTag("Player");
    if (player != null)  {
        if(otherTarget != null)  {
            Vector2 pos = Vector2.Lerp(player.transform.position,
                                       otherTarget.transform.position,
                                       0.5f);
            //플레이어 위치와 연동
            transform.position = new Vector3(pos.x, pos.y, -10);
        }  else{
            //플레이어 위치와 연동
            transform.position = new Vector3(player.transform.position.x,
                             player.transform.position.y,
                                             -10);
        }
    }
}

 게임 실행하기

이제 카메라가 보스와 플레이어 중간을 비춥니다.

BossController.cs로 스크립트를 만들어 Boss 폴더에 저장합고 Boss에 어태치합니다.

BossController.cs
0.00MB

변수

hp나 반응거리는 적캐릭터와 같습니다. 발사하는 총알의 속성도 같습니다. inAttack변수는 공격중인지 여부를 판단하는 값입니다.

// 체력
public int hp = 10;
// 반응 거리
public float reactionDistance = 7.0f;

public GameObject bulletPrefab;     //총알
public float shootSpeed = 5.0f;     //총알 속도

//공격중인지 여부
bool inAttack = false;

Update()

"Player" Tag로 Player를 찾아서 공격범위이면 inAttack=true, 공격 애니메이션 Play("BossAttack")등 공격처리를 합니다. 

void Update() {
    if (hp > 0)  {
        //Player 게임 오브젝트 가져오기
        GameObject player = GameObject.FindGameObjectWithTag("Player");
        if (player != null) {
            //플레이어와의 거리 확인
            Vector3 plpos = player.transform.position;
            float dist = Vector2.Distance(transform.position, plpos);
            if (dist <= reactionDistance && inAttack == false)  {
                //범위 안 & 공격 중이 아니면 공격 애니메이션
                inAttack = true;
                // 애니메이션 변경
                GetComponent<Animator>().Play("BossAttack");
            }  else if (dist > reactionDistance && inAttack) {
                inAttack = false;
                // 애니메이션 변경
                GetComponent<Animator>().Play("BossIdle");
            }
        } else {
            inAttack = false;
            // 애니메이션 변경
            GetComponent<Animator>().Play("BossIdle");
        }
    }
}

OnCollisionEnter2D()

화살과 충돌하면 hp--처리하고 사망했다면 Play("BossDead")후 Destry()로 지우는 사망처리를 합니다.

private void OnCollisionEnter2D(Collision2D collision) {
    if (collision.gameObject.tag == "Arrow")  {
        //데미지
        hp--;
        if (hp <= 0)  {
            //사망!
            //충돌 판정 비활성
            GetComponent<CircleCollider2D>().enabled = false;
            // 애니메이션 변경
            GetComponent<Animator>().Play("BossDead");
            //1초 뒤에 제거
            Destroy(gameObject, 1);
        }
    }
}

Attack()

플레이어와의 각도를 구해 총알을 발사합니다.

void Attack()  {
    //발사 위치 오브젝트 가져오기
    Transform tr = transform.Find("gate");
    GameObject gate = tr.gameObject;
    //총알을 발사할 벡터 만들기
    GameObject player = GameObject.FindGameObjectWithTag("Player");
    if (player != null) {
        float dx = player.transform.position.x - gate.transform.position.x;
        float dy = player.transform.position.y - gate.transform.position.y;
        //아크 탄젠트2 함수로 각도(라디안) 구하기
        float rad = Mathf.Atan2(dy, dx);
        //라디안을 각도로 변환
        float angle = rad * Mathf.Rad2Deg;
        //Prefab으로 총알 오브젝트 만들기(진행 방향으로 회전)
        Quaternion r = Quaternion.Euler(0, 0, angle);
        GameObject bullet = Instantiate(bulletPrefab, gate.transform.position, r);
        float x = Mathf.Cos(rad);
        float y = Mathf.Sin(rad);
        Vector3 v = new Vector3(x, y) * shootSpeed;
        //발사
        Rigidbody2D rbody = bullet.GetComponent<Rigidbody2D>();
        rbody.AddForce(v, ForceMode2D.Impulse);
    }
}

 

애니메이션 이벤트 설정하기

씬뷰의 보스 캐릭터를 선택하고 애니메이션 창을 엽니다.  팝업메뉴에서 BossAttack를 선택하고 타임라인을 클릭후 이벤트추가▮+를 클릭하면  이벤트가 추가되고 추가된 이벤트를 선택하면 게임오브젝트에 어태치된 메서드 Attack()을 선택할 수 있습니다.

이제 애니메이션이 해당 프레임에 도달하면 Attack()메소드가 호출되면서 총알이 발사 됩니다.

Boss오브젝트  BossController.cs 컴포넌트의 Bullet Prefab에 프로젝트뷰 Boss폴더의 Bullet Prefab을 끌어다 놓습니다.

 

게임 실행하기

보스캐릭터가 플레이어 캐릭터를 향해 총알을 발사 합니다. 총알속도 반응 거리는 스크립트에서 설정할수 있습니다.

보스캐릭터를 Boss 폴더로 끌어다 프리팹으로 만듭니다.

 

BulletController.cs
0.00MB

보스 캐릭터가 발사하는 총알을 만들어봅니다. 기본적으로 포탄이나 화살과 같습니다.

Images폴더의 Bullet 이미지를 선택하고 Pixel Per Unit:32, Filter Mode: Point(no filter)로 설정합니다.

씬뷰로 끌어서 게임오브젝트를 만듭니다. 

Tag:Enemy, Layer:Bullet으로 설정합니다. 없으면 만들어서 합니다.

Order in Layer를 3으로 설정합니다. Rigidbody2D를 어태치하고 Gravity Scale을 0으로 합니다 CircleCollider2D를 어태치하고 EditCollider를 눌 범위를 조정합니다.

 총알의 접촉 설정

보스 캐릭터와 총알이 접촉하지 않도록 Proejct Settings > Physics2D탭의 LayerCollisionMatrix에서 Enemy와 Bullet이 교차하는 부분의 체크를 해제합니다. Bullet과 Bullet도 체크해제 합니다.

총알을 제어하기 위한 스크립트 만들기

스크립트를 하나 만들고 BulletController로 이름 변경하고 bullet에 어태치합니다.

스크립트는 간단합니다. 발사후 일정시간후 자신을 제거하거나 어딘가 충돌해도 제거합니다.

 

Start()

설정된 시간후 자신을 제거합니다.

void Start() {
    Destroy(gameObject, deleteTime);    //제거 설정
}

OnCollision2D()

접촉이 발생하면  자신을 제거합니다.

private void OnCollisionEnter2D(Collision2D collision)  {
    Destroy(gameObject);   //접촉이 발생하면 즉시 제거
}

 

스테이지 마지막에 싸울 보스 캐릭터와 보스 스테이지를 위한 씬을 만들어봅시다. 보스 캐릭터는 다음과 같이 동작합니다.

플레이어 캐릭터와 접촉하면 플레이어가 대미지를 받는다.

플레이어 캐릭터가 접근하면 총알을 발사해 공격한다.

HP를 설정할 수 있다. 화살을 일정량 맞으면 쓰러진다.

보스 캐릭터와 관련된 데이터는  Boss폴더를 만들고 저장합니다. 사용할 이미지도 Boss 폴더로 옮겨둡니다. 지금 바로 Boss폴더를 만듭니다.

 

보스 캐릭터 게임 오브젝트 만들기

먼저 보스 캐릭터의 이미지 에셋을 준비합니다. Boss가 보스 캐릭터용 이미지입니다.

Boss_0~1:대기, Boss_2~3:공격 ,Boss_4: 사망 애니메이션입니다.

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

대기애니메이션 만들기

Boss_0~1을 씬뷰에 끌어다 놓으면 애니메이션 데이터를 만듭니다. 클립 이름을 BossIdel로 합니다. 배치한 보스 캐릭터 게임 오브젝트의 이름은 Boss, 애니메이션 컨트롤러의 이름은 BossAnime로 이름을 변경합니다.

Sprite Renderer의 Order in Layer를 2로 설정하고, Circle Collider2D를 어태치 합니다. 보스 캐릭터는 움직이지 않으니 Rigidbody2D는 쓰지 않습니다. 보스 캐릭터에 Enemy태그와 Enemy Layer를 만들어 적용합니다. 나중에 보스가 발사하는 총알의 충돌 판정에 사용합니다.

CreateEmpty를 하나만들어 gate로 이름 바꾸고 Boss의 자식으로 하고 가슴쯤에 배치합니다. 씬뷰에서 구별하기 쉽게 칼라아이콘을 설정합니다. 총알이 발사될 위치로 사용합니다.

BossIdle 애니메이션을 열고 하이라키의 Boss를 선택한후 Samples를 2로 설정합니다. ▶클릭해서 확인해봅니다.

공격 애니메이션

Animation창에서 BossIdel을 클릭하고 Create New Clip을 선택하고 창이 뜨면 이름은 BossAttack으로 합니다.

Boss_2~3을 끌어 씬 뷰에 끌어다 애니메이션 클립을 만듭니다. 

Sample을 4로 합니다. 약간 포즈를 취한뒤 공격을 하기 위해 0프레임의 키프레임을 카피해 그 다음 프레임어 넣어줍니다.

 

사망 애니메이션

Boss_4 이미지를 이용해 사망애니메이션을 만들고 이름을 BossDead로 합니다. Sprite Renderer.Color Property를 추가하가 마지막 프레임은 알파값이 0이 되도록 저장합니다.

 

애니메이션 컨트롤러

BossAnime을 열고 3개의 애니메이션이 다 있는지 확인합니다. 없으면 끌어다 놓습니다.

 

 

+ Recent posts