전체 페이지뷰

2017년 9월 7일 목요일

Unity Tutorial: Tanks, part 4


Shell Creation



이제 포탄으로 사용될 shell prefab을 생성해 보려고 합니다.

이 포탄에는 여러가지의 컴포넌트들이 적용되어 있으며, 탱크 가까이에 떨어질수록 더 큰 데미지를 주게 될 것입니다. 다음 순서에서 실제 발사에 대한 것을 다루게 될 것이고, 여기서는 일단 데미지의 구현을 다루게 될 것입니다.

Assets>Models 폴더에 보면 Shell이라는 모델이 있습니다. 이것을 드래그하여 Hierarchy 상에 넣습니다. 그러면 오리진 위치또는 그 근방에 포탄이 위치하게 되는데 y축으로 조금 끌어올려 탱크 앞 공중에 위치하게 합니다.


나중에 포탄은 프리팹화되어 탱크에서 발사될 것이므로 지금의 위치는 단순히 대미지 구현 실험을 위한 곳일 뿐이므로 정확한 위치는 신경쓰지 않아도 됩니다.

이 포탄이 다른 오브젝트와 상호 작용할 수 있도록 캡슐 콜라이더를 추가합니다. 이 포탄이 다른 물체와 부딪혀서 튕겨 나오거나 하는 일이 없이 데미지 판정만을 하도록 Is Trigger에 체크하고, 위치와 크기를 조절합니다.


물리 법칙 적용을 위해 Rigidbody도 추가합니다.

그런 다음, 폭발의 효과를 주기 위해 Prefabs폴더의 ShellExplosion을 드래그하여 Shell에 들어가게 합니다.


Shell의 자식으로 들어간 ShellExplosion을 선택하고, Add Component로 Audio Source를 추가한 후 Clip으로 ShellExplosion을 선택, Play On Awake는 체크 해제합니다.

이 황금색의 포탄은 사막 모래 배경으로 잘 보이지 않을 수 있습니다. 다시 Shell을 선택한 상태에서 Add Component>Rendering>Light를 선택하여 조명을 하나 추가합니다.

이제 이 모든 것을 컨트롤 하기 위한 스크립트 차례입니다. Scripts>Shell폴더에 ShellExplosion이라는 스크립트가 들어가 있습니다. 이것을 Shell에 연결하고 에디터로 엽니다.

public LayerMask m_TankMask;
public ParticleSystem m_ExplosionParticles;       
public AudioSource m_ExplosionAudio;              
public float m_MaxDamage = 100f;                  
public float m_ExplosionForce = 1000f;            
public float m_MaxLifeTime = 2f;                  
public float m_ExplosionRadius = 5f;  
cs

스크립트는 여러 개의 퍼블릭 변수들로 시작합니다. 전에 우리는 탱크의 Layer를 Players로 지정했습니다. 첫번째 m_TankMask라는 레이어 마스크는 우리의 포탄이 어느 레이어의 오브젝트에 영향을 줄것인가를 손쉽게 결정하게 해 줍니다. 폭발 시의 파티클과 오디오에 대한 레퍼런스가 이어지고, 포탄의 최대 데미지와 터지기까지의 시간, 폭발 반경 등의 변수가 이어집니다.

private void Start()
{
    Destroy(gameObject, m_MaxLifeTime);
}
cs

Start() 내에서는 생성된 오브젝트를 m_MaxLifeTime 후에 파괴하도록 하고 있습니다. 이 말은 그 시간이 지나면 어딘가에 부딪히지 않아도 무조건 소멸한다는 뜻입니다.

private void OnTriggerEnter(Collider other)
{
    // Find all the tanks in an area around the shell and damage them.
    Collider[] colliders = Physics.OverlapSphere(
        transform.position, m_ExplosionRadius, m_TankMask);
    for (int i = 0; i < colliders.Length; i++)
    {
        Rigidbody targetRigidbody = colliders[i].GetComponent<Rigidbody>();
        if (!targetRigidbody)
            continue;
        targetRigidbody.AddExplosionForce(
            m_ExplosionForce, transform.position, m_ExplosionRadius);
        TankHealth targetHealth = targetRigidbody.GetComponent<TankHealth>();
        if (!targetHealth)
            continue;
        float damage = CalculateDamage(targetRigidbody.position);
        targetHealth.TakeDamage(damage);
    }
    m_ExplosionParticles.transform.parent = null;
    m_ExplosionParticles.Play();
    m_ExplosionAudio.Play();
    Destroy(m_ExplosionParticles.gameObject, m_ExplosionParticles.duration);
    Destroy(gameObject);
}
cs

폭발 반경 내에 있는 모든 콜라이더를 찾아 배열 내에 저장하고, 그 배열 내를 순회하면서 rigidbody를 갖고 있지 않으면 다음 순서로 넘어가고, 가지고 있다면 폭발력을 가해주고 데미지를 계산해서 적용해 줍니다.

주의해야 할 것은
m_ExplosionParticles.transform.parent = null;
부분인데, 폭발이 되면 포탄이 사라져야 하는데, 바로 포탄을 Destroy하면 그 안에 포함된 파티클시스템, 폭발음 등도 함께 사라지므로 플레이할 수가 없습니다. 따라서 소리와 파티클들을 사용할수 있도록, 부모 부분만 null로 만들어 주는 것입니다.

그 이후 파티클 효과와 소리를 플레이하고, 오브젝트를 파괴합니다.

다음은 CalculateDamage() 차례입니다.

private float CalculateDamage(Vector3 targetPosition)
{
    // Calculate the amount of damage a target should take based on it's position.
    Vector3 explosionToTarget = targetPosition - transform.position;
    float explosionDistance = explosionToTarget.magnitude;
    float relativeDistance = (m_ExplosionRadius - explosionDistance) / m_ExplosionRadius;
    float damage = relativeDistance * m_MaxDamage;
    damage = Mathf.Max(0f, damage);
    return damage;
}
cs

타겟과 포탄 사이의 벡터를 계산하고, 그로부터 거리를 얻습니다. 그 거리로부터 폭발 반경과의 상대거리를 계산하면, 폭발의 중심은 1, 반경 끝에서는 0, 반경 밖에선 마이너스 값이 얻어집니다.

마이너스 값이 나오면 데미지를 0으로 나오게 Mathf.Max()를 사용해서 처리하고 데미지 값을 반환하는 코드입니다.

저장하고 유니티로 돌아옵니다.


레이어 마스크는 Players로 지정(여태까지의 프로퍼티와 달리 드랍다운으로 되어 있습니다)하고, Explosion Particle과 Audio에 공히 ShellExplosion을 끌어다 놓으면 유니티가 알아서 필요한 컴포넌트를 연결합니다.

이렇게 해서 Shell의 구현이 끝났으므로 Hierarchy의 Shell을 Prefabs폴더로 드래그하여 프리팹화 합니다.

플레이 해보니 포탄이 떨어지고 탱크의 데미지가 크게 깎이면서 뒤로 밀려납니다.

프리팹화 했고 작동하는 것을 테스트했으니 Shell을 Hierarchy에서 지우고 씬을 저장합니다.

댓글 없음:

댓글 쓰기