Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

메피카타츠의 블로그

Tales Saga Chronicle Blast 개발일지 16 - 총알 적중(Trigger), 배경화면 등 구현 본문

개발/개발일지

Tales Saga Chronicle Blast 개발일지 16 - 총알 적중(Trigger), 배경화면 등 구현

메피카타츠 2023. 3. 18. 00:57

오늘 추가한 도트는 이 5가지로, 배경에 사용할 별 모양이다. 배경을 어떻게 구성하면 좋을까 생각할수록 점점 고민만 깊어지는 것 같아서 막무가내로 찍어보았다. 수정을 거치다보니 그럭저럭 봐줄만하게 나왔다. 잘해야 한다는 부담감과, 익숙하지 않은 것을 해야 하는 두려움이 겹쳐지다보니 괜히 걱정만 느는 것 같다. 확실히 나한테 있어 일러스트레이터는 소중한 존재다. 향후 함께 일하는 일러스트레이터들에게 아무리 감사를 표해도 모자라지 않을 것 같다.

 

그 외에는 어제 목표로 했던 함수 수정과 Collider를 활용하여 총알 적중 기능, 배경 화면을 구현했다.

public IEnumerator ShootMidoriBullet(Vector2 direction)
    {
        GameObject bullet = GetMidoriBullet();

        bullet.SetActive(true);
        bullet.GetComponent<RectTransform>().anchoredPosition = new Vector2(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.x, midoriAirplane.GetComponent<RectTransform>().anchoredPosition.y + 50);

        float bulletDuration = Vector2.Distance(bullet.GetComponent<RectTransform>().anchoredPosition, direction) / 1200;
        bullet.GetComponent<RectTransform>().DOLocalMove(direction, bulletDuration).SetEase(Ease.Linear);

        yield return new WaitForSeconds(bulletDuration);

        midoriBulletQueue.Enqueue(bullet);
    }

어제 계획했던 대로 목표 지점인 Vector2를 매개변수로 가지게끔 했다. 발사되는 위치와 목표 지점 사이의 거리를 구해서 속도를 일정하게 조절했다.

void Update()
    {
        // 상하좌우 이동, 화면 밖으로 벗어나지 않도록 움직임에 제한을 둠
        float moveSpeed = 500f * Time.deltaTime;

        if (Input.GetKey(KeyCode.UpArrow))
        {
            if (!(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.y >= 500))
            {
                midoriAirplane.GetComponent<RectTransform>().anchoredPosition = new Vector2(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.x, midoriAirplane.GetComponent<RectTransform>().anchoredPosition.y + moveSpeed);
            }
        }
        if (Input.GetKey(KeyCode.DownArrow))
        {
            if (!(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.y <= -500))
            {
                midoriAirplane.GetComponent<RectTransform>().anchoredPosition = new Vector2(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.x, midoriAirplane.GetComponent<RectTransform>().anchoredPosition.y - moveSpeed);
            }            
        }
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            if (!(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.x <= -500))
            {
                midoriAirplane.GetComponent<RectTransform>().anchoredPosition = new Vector2(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.x - moveSpeed, midoriAirplane.GetComponent<RectTransform>().anchoredPosition.y);
            }
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            if (!(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.x >= 500))
            {
                midoriAirplane.GetComponent<RectTransform>().anchoredPosition = new Vector2(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.x + moveSpeed, midoriAirplane.GetComponent<RectTransform>().anchoredPosition.y);
            }
        }
        // Z키를 눌러 총알을 발사
        if (Input.GetKeyDown(KeyCode.Z))
        {
            audioManager.PlaySFX("Shoot");
            StartCoroutine(ShootMidoriBullet(new Vector2(midoriAirplane.GetComponent<RectTransform>().anchoredPosition.x, 1200)));
        }
        if (Input.GetKey(KeyCode.X))
        {

        }
    }

Update 함수에서 플레이어가 이동하는 부분도 고쳤다. Collider를 추가하다보니 프레임이 많이 떨어졌는지 움직임이 엄청나게 느려졌다. 처음엔 왜 느려졌지? 하다가 Time.deltaTime의 존재를 깨닫고 수정했다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletController : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D other)
    {
        if(other.CompareTag("Enemy_Shooting"))
        {
            ShootingGameManager.instance.HitMidoriBullet(gameObject, other.gameObject);
        }
    }
}

이후에는 총알에 Collider를 추가해서 적중 기능을 추가했다. 아군 총알과 아군 비행기에만 스크립트를 달아주면 될 것 같아서 그렇게 하기로 했다. 적 비행기랑 아군 총알 중에 어떤 것이 많이 소환될 지도 생각해봤다. 적 비행기가 나오는 패턴에 달려있겠지만, 플레이어가 제때 적들을 처리하지 못하면 많이 쌓일 것 같아서 총알에 다는 것이 조금 더 효율적이지 않을까 생각해서 총알에 스크립트를 달았다.

 

Collider는 Collider 2D를 사용했는데, 처음에 총알은 Polygon Collider로 이미지와 동일한 모양으로 만들었다. 그런데 만들기도 번거롭고 성능에 영향을 줄 것 같아서 조금 디테일을 줄였다. 비행기는 그나마 중요할 것 같아서 Polygon으로, 적 개체는 Box/Capsule Collider로 만들었다. 처음에는 OnTriggerEnter를 사용해서 왜 작동을 안하나 한참 찾아보고 고민을 했는데 알고보니 2D Collider를 사용할 때 쓰는 OnTriggerEnter2D가 따로 있었다.

 

아무튼, 적중하면 ShootingGameManager에 정보를 넘겨서 처리하게끔 했다. 이 과정에서 매 스크립트마다 찾기는 번거로울 것 같아서 instance를 활용했다. 싱글톤이라고 하는데... 아마 저 스크립트는 저것 하나만 사용할 것이라고 한정지어서 찾기 편하게끔 해주는 것이 아닐까 싶다. 정확히 알게 위해서 추후에 조금 더 찾아봐야겠다.

public void HitMidoriBullet(GameObject midoriBullet, GameObject enemy)
    {
        midoriBullet.SetActive(false);
        EnemyController enemyController = enemy.GetComponent<EnemyController>();

        if(enemyController.GetEnemyHP() > 1)
        {
            enemyController.SetEnemyHP(enemyController.GetEnemyHP() - 1);
            audioManager.PlaySFX("EnemyHit");
        }
        else
        {
            audioManager.PlaySFX("EnemyDestroy");
            enemy.SetActive(false);
        }
    }

 

피격 처리 함수 내용은 이렇다. 총알의 Active 상태를 false로 하고, 적의 체력을 1씩 깎아 0이 되면 적 개체를 없애버리는 식이다. 적 개체에도 오브젝트 풀링 기법을 적용할 계획이기 때문에 이 부분은 내일 수정할 것 같다. 그리고 적이 맞으면 피격음과 파괴되면 파괴되는 효과음을 넣었다. 피격음은 몰라도 파괴음은 약간 애매한 느낌이 있어서, 폭발하는 애니메이션을 넣을 예정이기 때문에 그와 걸맞는 효과음을 찾아서 넣어야겠다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyController : MonoBehaviour
{
    private int enemyHP = 5;

    public int GetEnemyHP()
    {
        return enemyHP;
    }

    public void SetEnemyHP(int hp)
    {
        enemyHP = hp;
    }
}

적의 체력 정보를 저장할 간단한 스크립트도 만들었다. 처음엔 EnemyManager라고 지었는데, 여러 개가 만들어질 것이다보니 Manager보단 Controller라는 이름이 맞을 것 같아서 변경했다. 이름을 어떻게 지으면 좋을지도 약간 고민이다. 지금도 함수 이름이 동사, 명사, 동명사 등 썩 일정하지가 못해서 나중에 프로그램이 커지면 헷갈릴 위험이 있을 것 같았다. 이런 건 찾아봐도 딱히 규칙같은 게 없는 것 같았다. 아마 조직마다 다르게 정해놓고 쓰지 않을까 싶다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using UnityEngine.UI;

public class StarColorController : MonoBehaviour
{
    [SerializeField] public float startDelayTime;

    private void Start()
    {
        gameObject.GetComponent<Image>().color = new Color(1, 1, 1, 0);
        Invoke(nameof(StarTwinkle), startDelayTime);
        Invoke(nameof(StarFallLoop), StarFallStart());
    }

    private void StarTwinkle()
    {
        Sequence starTwinkle = DOTween.Sequence();

        starTwinkle.Append(gameObject.GetComponent<Image>().DOFade(1, 1)).Append(gameObject.GetComponent<Image>().DOFade(0, 1)).SetLoops(-1, LoopType.Yoyo);
    }

    private float StarFallStart()
    {
        Sequence starFall = DOTween.Sequence();

        float spendTime = (gameObject.GetComponent<RectTransform>().anchoredPosition.y + 600) / 300;

        starFall.Append(gameObject.GetComponent<RectTransform>().DOLocalMoveY(-600, spendTime).SetEase(Ease.Linear))
            .Append(gameObject.GetComponent<RectTransform>().DOLocalMoveY(600, 0f));

        return spendTime;
    }

    private void StarFallLoop()
    {
        Sequence starFall = DOTween.Sequence();

        float spendTime = (600 + 600) / 300;

        starFall.Append(gameObject.GetComponent<RectTransform>().DOLocalMoveY(-600, spendTime).SetEase(Ease.Linear))
            .Append(gameObject.GetComponent<RectTransform>().DOLocalMoveY(600, 0f)).SetLoops(-1);
    }
}

이외에는 배경에서 별이 반짝이면서 움직이는 효과를 추가했다. 목표로 하진 않았으니 내일 할까 싶다가 왠지 의욕이 붙어서 오늘 구현했다. 예전에 직장에서 일을 할 때 비슷한 효과를 넣은 적이 있는데, 당시에는 개수가 적어 일일히 처리하는 것이 빠르겠다 싶어서 각각의 개체에 애니메이션을 따로 달아주었는데, 오히려 그게 시간이 더 오래 걸렸다. 이번에는 개수도 많다보니 스크립트로 알아서 처리하게끔 만들었다. 처음에 불투명도를 0으로 해서 안보이게 한 후, 별마다 다른 시작 딜레이를 주고, 1초마다 깜빡이게끔 해서 각기 다른 타이밍에 깜빡거리도록 했다. 그리고 현재 위치를 고려해서 별을 밑으로 내려오게 하고, 이후에는 위에서 아래로 내려오는 것을 반복하도록 했다. 근데 그러다보니 Sequence가 너무 많아져서 DOTween의 기능을 너무 많이 사용하게 되었다. 기본적으로 얼만큼 사용할 것인지 크기가 정해져 있나본데, 그 크기가 부족해서 확장했다고, 스크립트에 해당 내용을 넣어서 확장하라는 경고가 떴다.

DOTween.SetTweensCapacity(500, 125);

 

비효율적인건가? 싶은 생각도 들었다. 하지만 그래봤자 애니메이션으로 변경 정도만 가능할 것 같았다. 이외에는 배경을 외부에서 만들어서 재생하는 것. 아마 이 많은 별들을 따로 처리하는 것보다는 외부 프로그램에서 만든 이미지 혹은 동영상을 재생하는 것이 효율적일 것 같았다. 하지만 그런 걸 나 혼자 만들 자신이 없어서 지금은 이대로 가기로 했다.

 

그래서 오늘까지 완성된 부분은 다음과 같다.

 

슬슬 조금씩 게임같은 느낌이 나는 것 같다. 그래도 아직 갈 길이 먼 것 같기도 하다. 적 움직임, 스폰, 스킬 구현, 보스 구현, UI 구현 등등... 움직일 때 효과도 넣으면 좋을 것 같은데 어떻게 할지 아직 잘 모르겠다. 내일도 필요한 부분들을 구현해나가면서 고민해봐야겠다.

 

내일의 목표는 이렇다.

1. 아리스 도트 헤일로 찍기

2. 어울리는 배경음악 찾아서 넣기

3. 배경화면 속도 조금 늦추기

4. 적 개체에 오브젝트 풀링 기법 적용하기

5. 오브젝트 풀링 기법을 활용해서 스킬 구현하기(가능하다면)

 

 

오늘은 굉장히 설렁설렁 한 것 같은데, 배경 화면 움직임을 구현하면서 조금 분량이 채워진 것 같다. 이전처럼 엄청난 열정을 가지고 하거나 이런 것은 아닌 것 같다. 그래도 오늘까지 재개한 후 8일 동안은 꾸준히 해와서 다행이다. 앞으로도 꾸준히 매일 이런 작업들을 쌓아나가면 분명 많이 성장할 수 있을 것이라고 생각한다. 그러면서도 과연 이게 맞는 길일까 하는 고민이나 걱정이 들기도 한다. 내일은 그런 생각들을 정리해보는 것도 좋을 것 같다. 생각이 정해지고 목표가 정해지면 그만큼 의욕도 생길 것이다.