24.02.02 TIL - Unity : 입문 팀 과제 완성 | WorldToViewportPoint

JJwoo·2024년 1월 31일

팀원분이 작업하신 인트로 씬

추억의 게임 메탈슬러그를 재현한 프로젝트 팀과제, 닷지나 X피하기의 어레인지.

1주일 간 들이받은 끝에 게임 개발 팀과제를 완성 했다.

여러가지 후보군이 있었는데 메탈 슬러그 3의 로켓 미션이 생각나길래 그걸로 정했다.

📑 와이어 프레임


🧨 완성 시연 영상

https://www.youtube.com/watch?v=SrssHB43d1Q&feature=youtu.be


🎞 회고

회고에 앞서 유니티 게임 개발 입문 주차에 배운 부분을 다시 생각해보면

  • '입력과 캐릭터 이동' (Input Manager, Input System 등)

  • 충돌, 물리 처리 ( Collider, rigid body )

  • 투사체나 몬스터 스폰 ( prefab - Instantiate 함수

  • 스탯 { ( 공격력, 이동속도, 체력 등 ) 정보 저장 Scriptable Object }

  • 투사체 구현 ( 레이어 비트연산, 오브젝트 풀 )

  • 애니메이션 구현 ( Animator, String to Hash )

  • 적 구현

    • 플레이어 탐지 ( FindGameObjectWithTag )
    • Collider 간의 교차 탐지 ( Physics.Raycast 또는 Physics2D.Raycast )
    • 넉백 구현 ( rigidbody.velocity )
  • 파티클 시스템

  • 사운드 시스템 ( AudioListener, AudioSource, AudioClip

  • UI 제작

등이 기억이 난다.

대부분 강의에서 배운 것들을 통해 작업물에 구현하였다.

그리고 메탈슬러그 리소스 그래픽을 스프라이트로 사용했기에, 저작권 문제가 있을 것 같아 긴급 회의를하고 팀원이 SNK에 직접 전화하셔서 이야기를 나누었다.

본인 게임 플레이의 전반적인 몬스터 구현과 스폰을 담당했는데, 메탈슬러그의 그 대단한 도트 그래픽 애니메이션을 스프라이트로 따서 부드럽게 구현해보려니 쉽지 않았다.
(적이 죽는 애니메이션에 스프라이트가 21장이나 들아간다던가)

그 중에 특정 적을 구현하는 부분이 있었는데

운석이 빠르거나 느리게 아래 쪽 플레이어를 향해 랜덤한 각도로 떨어지는 적이었다.

이 부분이 처음에는 감히 잡히지 않아 일단 그림을 그려보았고

스폰 포인트가 2곳이다보니 그냥 30도 범위 정도로 날려대면 좋을듯 하여 아래와 같이 코드를 짰다.

using UnityEngine;

public class MeteorController : MonoBehaviour
{
    public float speed;

    private Vector3 moveDirection;

    // Start is called before the first frame update
    void Start()
    {
        // 랜덤 각도로 이동 방향 설정
        float randomAngle = Random.Range(-15.0f, 15.0f); 
        moveDirection = Quaternion.Euler(0, 0, randomAngle) * Vector3.down;
        speed = Random.Range(7.0f, 12.0f); //랜덤 속도
    }

    // Update is called once per frame
    void Update()
    {
        // 이동
        transform.Translate(moveDirection * speed * Time.deltaTime, Space.World);

        // 화면 밖으로 벗어났는지 확인
        if (IsOffScreen())
        {
            Destroy(gameObject);
        }
    }

    private bool IsOffScreen()
    {
        Vector3 screenPoint = Camera.main.WorldToViewportPoint(transform.position);
        return screenPoint.y < -10 || screenPoint.y > 10 || screenPoint.x < -5 || screenPoint.x > 1;
    }
}
  • Start() : 3개의 스폰 지점에서 랜덤 각도와 Vector3.down; + 랜덤 방향, 속도를 설정

  • Update() Translate를 통해 오브젝트 위치를 이동 시키고자 함.

    • 사용 이유: rigid body를 사용하자 않았고(몬스터들과의 충돌 방지) 간단하게 이동 시키고 싶었기에 이 방식을 채택.
  • 그리고 생성된 오브젝트는 IsOffScreen() 메서드를 만나면 Destroy 되게 하였다.

  • IsOffScreen() : 카메라 화면을 벗어났는지를 판단하기 위한 bool 함수, 벗어나면 Update 쪽에서 조건문을 통해 오브젝트를 파괴시킨다.

WorldToViewportPoint

카메라 클래스 함수로, 게임 오브젝트의 월드(World) 좌표를 카메라 뷰포트(Viewport) 좌표로 변환하는 데 사용

  • 뷰포트 좌표(Viewport Coordinates): 카메라의 뷰포트를 기준으로 게임 오브젝트의 상대적 위치를 나타내는 좌표이다.
    일반적으로 (0,0)에서 (1,1) 사이의 값으로 표현되며, 화면 크기와 상관없이 상대적인 위치를 나타낸다.

이 함수를 통해 오브젝트의 위치를 화면 상의 좌표로 변환하여 screenPoint라는 변수에 저장하였고, 화면 안인지, 밖인지의 여부를 통해 오브젝트를 파괴 할 지 말지 판단 및 시행 할 수 있었다.


🎩 입문 팀과제 후기

한 분이 개인 사정으로 참여 못하셔서 적은 인원으로 작업을 시작했었다.

시작은 어려웠지만 팀원분들이 다들 착하고 소통이 좋았어서 즐겁고 재미있게 작업하였다.

특히 git을 통한 유니티 커밋 - 병합을 처음 해보았는데,

코드 병합은 코드를 비교적 쉽게 알아볼 수 있기에 충돌이 발생해도 데이터 처리가 쉬우나

유니티에서의 충돌은 그 데이터가 어떤 내용인지 완벽하게 알기는 힘들었다.

다만 팀원들이 서로 파트 분담을 했었고 커밋하는 당사자와 팀원들이 서로 방송 키고 소통하면서 병합했기에 데이터 손실이나 충돌을 최소화 할 수 있었다.


시연 후 기억나는 튜터님의 말씀은

  1. GetComponent를 너무 남발하면 안된다
    ( 컴포넌트를 찾기 위해 오브젝트의 컴포넌트 목록을 검색해야 하므로 비용이 발생, 그런데 여기저기 남발되어서 성능 저하를 유발한듯 + 중복 호출 등)

  2. 코드에서의 귀차니즘이 느껴진다.
    ( 중복되는 기능 등은 적절한 구조를 통해 효율적으로 데이터를 처리 할 수 있었을텐데라는 말씀으로 생각했음)

  3. 공통적인 문제 : 싱글톤 패턴을 올바르게 구현하지 못했고, 여러 씬들을 제작하면서 시작씬부터 사운드 매니저 등의 오브젝트나 여러 데이터들을 DontDestroyOnLoad를 통해 넘겨가는 방식으로 하다보니 후반부 씬에서 게임 테스트를 하고 싶어도 시작부터 다시 해야하는 불편함이 발생했었음.


P.S : 적 구현 초기 계획서


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

[CreateAssetMenu(fileName = "EnemiesData_", menuName = "Data/EnemiesData", order = 1)]

public class EnemiesData : ScriptableObject
{
    public int Atk;
    public int Hp;
    public int Speed;
    public Sprite Image;
    public bool 
}


현재 ScriptableObject가 있으므로 
각자 몬스터는 고유의 체력과 스피드 데이터를 저 곳에 구현

attack 애니메이션과 dead 애니메이션 구현을 위한 스프라이트 이미지 스왑을 고려

---


현재 상황 

게임의 플레이 방식이나 적들의 작동 로직등 어느 정도 큰 틀은 생각하고 있는데, 이를 몇가지의 어떤 스크립트로 구현해야 할 지 감이 잡히질 않음

 
일단 위의 데이터 스크립트 구현 및 몬스터 구현 전에 몇가지 게임 정보를 오약


<공격 로직 조건>
몬스터들이 따로 공격 애니메이션이 있는 것이 아닌, 투사체 발사 전의 고유 애니메이션을 불러오고, 그것이  끝난 후 투사체를 소환해서 쏘는 형식임.
공격이 없는 몬스터도 있으며 대신 특수한 로직을 사용하는데 일단 이 부분은 패싱

< 이동 로직 조건 >
몬스터들이 화면 위 특정 스폰 오브젝트(UFO_SpawnPosition라는 이름)에서 나타나 이동하면서 등장,  공격 1회 후 이동, 다시 공격 1회 후, 이동을 반복
여기서 몬스터들은 화면의 수평으로 반을 나눠, 수평 위에서만 랜덤으로 이동함.
여기서 모든 몬스터나 오브젝트들은 점진적으로 이동하는 것이 아닌, 이동을 하면 바로 정해진 속도로 즉각 즉각 이동되어야 함.

<몬스터의 피격 조건>
플레이어의 공격에 피격 당함.




<기타 로직 조건>
몬스터들은 플레이어의 공격을 받아 hp가 0이되면 파괴 되며 사라짐

몬스터에 따라 바로 폭발하면서 사라지거나, 터지는 애니메이션 재생 후 사라지는 경우가 있어서 파괴될 경우 몬스터들에 따라 딜레이를 설정할 수 있으면 좋겠습니다.

그리고 몬스터를 공격하지 않는 경우, 어느 정도의 시간을 위의 로직대로 행동하며 공격-이동-공격 반복하다가 화면의 좌우 아래로 내려가서 사라지기도 함.
(몬스터 스폰 및 사라짐 시간은 추후 게임 플레이 내 절대 시간에 맞춰서 조정 예정, PASS)



<추가 내용>

몬스터의 스폰 포인트(오브젝트)는 플레이 화면  (1920 x 1080) 에는 보이지 않음. 
보이지 않는 상단에서 ( 좌측 중간 우측  3포인트로 만들어줄 예정) 스폰되어 각각 정해진 로직으로 이동하며 등장하게 할 것.

폭발 관련 애니메이션 로직 구현은 추후 구현(패싱)

몬스터들은 각각 고유의 체력과 스피드 값을 가지고 있다.
(스피드는 일단 다 5로 구현 후 추후 수정)

+ 이미 구현한 적(에네미)들과 관련 애니메이션들이 다수 있음.



<몬스터 목록>

1. Small meteor 

작은 운석. 체력5
내려오면서 플레이어에게 부딪히면 폭발하며 데미지를 주는 방식.
혹은 플레이어에게 공격 받아 체력이 다하면 파괴 될 수도 있다.
같은 개체이지만 내려오는 속도 값은 랜덤. (빠를수도 느릴수도 있음)
내려오는 방향 각도도 랜덤.
일단 가운데 위에서 스폰 된다고 한다면, 아래에 서술

<모든 몬스터 공통 스폰 포인트임>
중앙 스폰 오브젝트 : 아래를 바라보며 부채꼴 모양(90, 파이/2 라디안)으로 범위 내 랜덤한 각도의 방향으로 내려와야 한다.
(날아온다면 화면의 양측면으로 나가지 않고, 무조건 화면의 아랫면 방향으로만 진행되어 지나가는 각도)
(원래 게임은 4:3비율이라 추후 조정)

좌측 스폰 오브젝트 : (45, 파이/4 라디안) 위와 동일한 로직
우측 스폰 오브젝트 : 좌측 로직의 반전

애니메이션 1종을 보유 중
idle

---
2. Big Meteor

큰 운석.  체력 30
고유의 체력을 가지고 있고 천천히 동일한 속도로 아래로 내려옴. (파괴가능 장애물 역할)
플레이어에게 어느 정도 공격받으면 폭발.

애니메이션 4종 보유.
Idle  : 일반 스프라이트
hit : 플레이어에게 피격 시 피격 애니메이션 발동, 1회성
broken :  체력이 0이되면 순간 이 애니메이션이 실행되면서 폭발과 동시에 오브젝트가 디스트로이

위 Big Meteor 파괴 이후
BigMeteor_PieceLeft
BigMeteor_PieceRight
두 개의 오브젝트가 나타나 (평소에는 false 상태)
미리 애니메이터에서 정해둔 대로 각자 방향으로 좌우로 천천히 퍼져나가서 게임 화면 밖에서 사라짐. 
파괴 후 분리 된 오브젝트는 충돌판정 피격판정 x




3. Small UFO

접시형 UFO. 
화면의 중앙에서 수평으로 반을 가르고, 그 위의 좌표에서만 랜덤하게 이동하고 공격, 다시 이동하고 공격을 반복
공격 애니메이션 종료 후 수직으로 직선형 투사체 발사함 

애니메이션 2종 보유

Idle : 일반 스프라이트
Attack_ChargeBeam : 공격 사전 준비



4. Big UFO

거대한 구체형 UFO
공격기능은 없지만 화면 중간에서 작은 원으로 빙글빙글 돌면서 좌우로 이동, 간헐적으로 하단의 해치를 열고 (자식 오브젝트로 달아둠, 따로 애니메이션 작동)
Spawn UFO가 나오는 애니메이션 작동 후, 애니메이션이 끝난 그 위치에서 UFO용 스폰 오브젝트(투명함)를 통해 바로 Spawn UFO 생성



5. Spawn UFO

외게인이 탑승한 UFO
마찬가지로 랜덤으로 이동하면서 플레이어 방향으로 공격 충전 애니메이션 후 투사체 날림.
Big UFO에게서 스폰으로만 등장함.

(플레이어 타겟시 플레이어를 인식하는 로직이 필요한데 이는 추후 다른 팀원과 스크립트 합치고 구현 예정)

애니메이션 4종 보유

Idle: 일반
hit : 플레이어에게 맞을 때 true가 되면서 Idle과 스왑, 
일반 스프라이트와 같은 갯수여서 Idle과 같은 속도로 재생되고 있어야 함(중요)

Dead : 죽는 애니메이션 후 사ㅏㄹ짐

공격은 자리에 멈춰서 충전 후 원형 투사체 발사
투사체 애니메이션 따로 보유


6. Big UFO Combined with Boss

거대한 구체형 UFO와 같으나

체력이 다 되면 무적상태로 변하고, 가운데에서 폭발 애니메이션 재생 후 

고유 애니메이션( 날개접는 거 ) 작동 후 천천히 올라가서 보스 UFO와 결합

이후 아래 보스 페이즈 시작함.



7. Boss UFO

위에서 스폰되어 화면 최상단에 고정되어 있는 UFO,6번 몬스터 오브젝트가

Spawn UFO가 아닌 검은색 Spawn UFO를 생성하는데

검은색 Spawn UFO는 공격을 2회하고 이동함.4마리 소환 후 안테나가 나오면서 거대한 구체형 에너지 볼 공격함.

일단 6번까지 구현 완료하고 마지막에 작업..
profile
개발 모코코

0개의 댓글