본문 바로가기

종스크롤 슈팅게임(1942)

11. 탄막슈팅 보스만들기

보스 오브젝트를 생성하고 위치를 0, 0으로 초기화 합니다.

필요한 컴포넌트를 넣어주고 이름을 수정합니다. 저는 Enemy B라고 지었습니다.

Rigidbody는 Gravity Scale 0으로 설정

캡슐 콜라이더의 범위는 아래처럼 지정했습니다.

캡슐콜라이더 범위 지정

다음으로 애니메이션을 추가합니다. Idle 상태와 피격상태 애니메이션을 보스 오브젝트에 드래그 앤 드랍해서 추가합니다.

Idle 애니메이션
Hit 애니메이션

파라미터는 트리거 형태의 onHit하나만 생성했으며, 아래처럼 Transition(화살표)를 이어줍니다.

Hit로 가는 화살표 Conditions 설정하기
Conditions에 onHit 트리거 설정

이제 프리펩 폴더에 보스 오브젝트를 드래그 앤 드랍해서 프리펩으로 저장합니다. 그 후에 Enemy 스크립트를 넣어주고, 태그를 수정합니다.

프리펩 저장
Enemy 스크립트 넣어주기
태그 Enemy로 변경

보스가 쓸 총알도 프리펩을 생성합니다.

프리펩 적 총알 아무것이나 클릭 후 Crtl+D로 복사본을 만들어줍니다.

아무 EnemyBullet 프리펩 클릭 후 복사

보스의 총알은 위에서 아래로 쏘아지니 Flip Y를 클릭해 켜줍니다.

이름과 스프라이트 교체 & FilpY 클릭

똑같이 보스 총알을 하나 더 만들어줍니다.

보스 총알 2종

보스를 프리펩으로 저장했으니 오브젝트 풀링에서 사용할 수 있도록 ObjectManager 스크립트에 코드를 추가합니다.

    public GameObject enemyBPrefab;
        public GameObject bulletBossAPrefab;
    public GameObject bulletBossBPrefab;
        
        GameObject[] enemyB;
            GameObject[] bulletBossA;
    GameObject[] bulletBossB;
    
        void Awake()
    {
        //Enemy
		...
        enemyB = new GameObject[20];
		...
        //Bullet
		...
        bulletBossA = new GameObject[20];
        bulletBossB = new GameObject[500];

        generate();
    
    }
    
    .
    .
    .
    .
    .

맵핑

Enemy 스크립트에 보스 로직 추가

    //Boss Vars
    public int patternIndex; 
    public int curPatternCount;
    public int[] maxPatternCount;
    
        void Awake()
    {
        spriteRenderer = GetComponent<SpriteRenderer>();
        if (enemyName == "B") {
            anim = GetComponent<Animator>();
        }
    }
    
        void OnEnable() //전에 리스폰 됬을 때 플레이어 총알에 맞고 Helath가 0이 되었거나, Helath가 깎인 상태로 맵끝에서 비활성화 되었을때로, 다시 풀피로 리스폰시키기 위해 적 전투기 활성화 시 Health 초기화
    {
        CancelInvoke("stop");
        switch (enemyName) { //Health처럼 소모되는 변수는 활성화 때, 다시 초기화 필요
            case "L":
                health = 40;
                break;
            case "M":
                health = 20;
                break;
            case "S":
                health = 5;
                break;
            case "B":
                health = 550;
                Invoke("stop", 2);
                break;
        }
    }
    
        void Update()
    {
        if (enemyName == "B") {
            return;
        }
        fire();
        reload();
    }
    
        void stop() {
        if (!gameObject.activeSelf) //게임 시작 후 프리펩이 생성되고 바로 비활성화 되기 때문에 stop함수가 2번 호출되는 것을 방지
        {
            return;
        }
        Rigidbody2D rigid = GetComponent<Rigidbody2D>();
        rigid.velocity = Vector2.zero;
        Invoke("think", 2);

    }

    void think() {
        patternIndex = patternIndex == 3 ? 0 : patternIndex + 1;
        curPatternCount = 0;
        switch (patternIndex) {
            case 0:
                fireForward();
                break;
            case 1:
                fireShot();
                break;
            case 2:
                fireArc();
                break;
            case 3:
                fireAround();
                break;
        }
    }

    void fireForward() 
    {
        //Fire 4 Bullet Forward
        GameObject bulletLL = objectManager.makeObj("BulletBossA");
        GameObject bulletL = objectManager.makeObj("BulletBossA");
        GameObject bulletR = objectManager.makeObj("BulletBossA");
        GameObject bulletRR = objectManager.makeObj("BulletBossA");

        bulletLL.transform.position = transform.position + new Vector3(-0.4f, -1f, 0);
        bulletL.transform.position = transform.position + new Vector3(-0.2f, -1f, 0);
        bulletR.transform.position = transform.position + new Vector3(0.2f, -1f, 0);
        bulletRR.transform.position = transform.position + new Vector3(0.4f, -1f, 0);

        Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
        Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
        Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
        Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();

        Vector2 dirVecLL = player.transform.position - bulletLL.transform.position;
        Vector2 dirVecL = player.transform.position - bulletL.transform.position;
        Vector2 dirVecR = player.transform.position - bulletR.transform.position;
        Vector2 dirVecRR = player.transform.position - bulletRR.transform.position;

        rigidLL.AddForce(dirVecLL.normalized * 6, ForceMode2D.Impulse);
        rigidL.AddForce(dirVecL.normalized * 6, ForceMode2D.Impulse);
        rigidR.AddForce(dirVecR.normalized * 6, ForceMode2D.Impulse);
        rigidRR.AddForce(dirVecRR.normalized * 6, ForceMode2D.Impulse);

        //Pattern Counting
        curPatternCount++;
        if (curPatternCount <= maxPatternCount[patternIndex])
        {
            Invoke("fireForward", 2);
        }
        else { 
            Invoke("think", 3);
        }
    }

    void fireShot()
    {
        //Shotgun Fire
        for (int i = 0; i < 8; i++) {
            GameObject bullet = objectManager.makeObj("BulletEnemyB");
            bullet.transform.position = transform.position + new Vector3(0, -1f, 0);
            Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
            Vector2 dirVec = player.transform.position - transform.position;
            Vector2 ranVec = new Vector2(Random.Range(-1f, 1f), Random.Range(0f, 3f));
            dirVec += ranVec;
            rigid.AddForce(dirVec.normalized * 4, ForceMode2D.Impulse);
        }


        //Pattern Counting
        curPatternCount++;
        if (curPatternCount <= maxPatternCount[patternIndex])
        {
            Invoke("fireShot", 2f);
        }
        else {
            Invoke("think", 3);
        }
    }

    void fireArc()
    {
        //Fire
        GameObject bullet = objectManager.makeObj("BulletEnemyA");
        bullet.transform.position = transform.position + new Vector3(0, -0.7f, 0);
        bullet.transform.rotation = Quaternion.identity;

        Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
        Vector2 dirVec = new Vector2(Mathf.Cos(Mathf.PI * 10 * curPatternCount/maxPatternCount[patternIndex]), -1);
    
        rigid.AddForce(dirVec.normalized * 5, ForceMode2D.Impulse);
        
        //Pattern Counting
        curPatternCount++;
        if (curPatternCount <= maxPatternCount[patternIndex])
        {
            Invoke("fireArc", 0.15f);
        }
        else { 
            Invoke("think", 3);
        }
    }

    void fireAround()
    {
        //Fire
        int roundNumA = 40;
        int roundNumB = 30;
        int roundNum = curPatternCount % 2 == 0 ? roundNumA : roundNumB;
        for (int i = 0; i < roundNum; i++) { 
            GameObject bullet = objectManager.makeObj("BulletBossB");
            bullet.transform.position = transform.position;
            bullet.transform.rotation = Quaternion.identity;

            Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
            Vector2 dirVec = new Vector2(Mathf.Cos(Mathf.PI * 2 * i / roundNum), Mathf.Sin(Mathf.PI * 2 * i / roundNum));

            rigid.AddForce(dirVec.normalized * 2, ForceMode2D.Impulse);

            Vector3 rotVec = Vector3.forward * 360 * i / roundNum + Vector3.forward * 90;
            bullet.transform.Rotate(rotVec); 
        }

        //Pattern Counting
        curPatternCount++;
        if (curPatternCount <= maxPatternCount[patternIndex])
        {
            Invoke("fireAround", 1f);
        }
        else { 
            Invoke("think", 3);
        }
    }

맵핑

 

결과