본문 바로가기
Unity

프롭 + 데미지 시스템 , Instantiate(), Tag & Layer , Physics

by imagineer_jinny 2021. 7. 15.

프롭

물리적 기능 필요하면 뭐 넣는다? RigidBody!!!

튕겨 나갈 수 있도록 RigidBody를 넣기

Prop 스크립트 추가

 

* 기능

- 프롭은 기본적으로 체력을 가지고 있어서 일정 이상의 데미지를 받으면 파괴가 되어야 함

- 자기 자신이 파괴되었을 때 게임 매니저에게 자기 자신이 파괴 되었다는 것을 알리고 점수를 추가해줘야 함

- 자기 스스로 실행시키는 것이 아니라 외부에서 데미지를 주는 친구가 프롭에게 가서 TakeDamage 라는 함수를 발동시켜서 프롭에게 데미지를 줄 것임

 

Instantiate();

이 안에 원본 게임오브젝트를 넣어주면 그 원본 게임오브젝트를 새로 하나 복사해줌

새로 하나 찍어낼 때 옵션을 줄 수도 있음 (position, rotation)

위치 지정 안하면 랜덤한 위치나 미리 지정된 원점으로 이동

 

Prop Scripts

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

public class Prop : MonoBehaviour
{
    //자기 자신 파괴될 때 마다 올라갈 점수
    public int score = 5;

    //프롭이 파괴될 때 파티클에서 하나 찍어낼 것
    public ParticleSystem explosionParticle;

    //hp가 작아졌을 때 프롭이 파괴가 됨
    public float hp = 10f;

    public void TakeDamage(float damage)
    {
        hp -= damage;

        if(hp<=0)
        {
            ParticleSystem instance =Instantiate(explosionParticle, transform.position, transform.rotation);

            AudioSource explosionAudio = instance.GetComponent<AudioSource>();
            explosionAudio.Play();
            
            Destroy(instance.gameObject, instance.duration);
            
            //프롭이 많이 생길건데 매번 프롭을 Destroy하고 다음 라운드에 프롭을 다시 수백개씩 생성하면
            //렉이 걸릴 수 있음 
            //따라서 프롭을 껐다가 다시 켜는 방법으로 재활용 할 것임!
            //프롭은 파괴 되는 것이 아니라 off되는 것임
            gameObject.SetActive(false);
        }
    }
}

 

데미지 시스템 

Ball이 폭발할 때 자기 주변 반경에 있는 모든 prop들을 가져와서 데미지를 주고 동시에 그 친구들에게 힘을 줘서 밀어내야 함

 

-> 1. Ball을 중심으로 폭발 반경을 구로 그린다.

    2. 해당되는 모든 프롭들을 가져와서 만약 Prop이라는 스크립트를 가지고 있으면

       그 안의 TakeDamage 함수를 발동시킨다.

 

뭐 부터 해야하나? 

Ball이 폭발 반경 안에 있는 prop들을 어떻게 구별하나?

주변에 콜라이더가 굉장히 많은데 어떻게 prop을 구별하는가? Layer 사용!!!

 

Tag , Layer 차이?

Tag : 일반적인 경우에 사용, 1:1 비교만 가능

Layer : 물리처리에 주로 사용, 한번에 여러개 필터링 가능

 

LayerMask는 한 번에 여러개 선택해서 필터링 가능

여기서는 Prop만 체크하기

 

부딪혔을 때 한가지 처리를 더 해야함.

1) 나의 위치를 기준으로 가상의 구를 그려줘야함

이 기능은 기본적으로 물리 기능에 속함.

즉, 가상의 구를 그려서 겹치는 모든 친구를 가져오는 것을 가져오는 것은 Physics라는 물리 집합에 들어가있음

 

Physics : 유니티에서 지원하는 물리 관련 기능들이 들어가있는 함수집합

Physics.OverlapSphere : 위치를 지정해주면(여기서는 구의 중심과 반지름) 거기에 겹치는 모든 콜라이더들을 배열로 다 가져와줌

private void OnTriggerEnter(Collider other) { 
        
        //성능 아끼기 위해 필터링
        //OverlapSphere은 Collider들을 반환함!

        Collider[] colliders =Physics.OverlapSphere(transform.position, explosionRadius, whatIsProp);

 

2) 데미지 계산 함수 만들기

데미지를 차등으로 주려고 함 (폭발의 중심지에 가까울수록 많이(100%), 원의 가장자리에 갈수록 적게(0%))

알아야 할 것은 나의 위치에서 상대방까지의 거리(x)가 아니라 

상대방이 원의 가쪽에서 얼마만큼 안쪽으로 들어왔는지(radius-x).

왜? 원 바깥쪽에서 원의 안쪽으로 들어올 수록 damage를 더 많이 받기 때문.

 

x=0 이면 파란색 점이 정 중앙에 있어서 1.0, x=radius이면 0

 

 

주의할 점: 폭발 반경에 걸치는 collider가 있음

collider의 실제 위치는 가상의 구 안에 들어오지 않는데 양쪽으로 부피가 있어서 겹치는 경우.

이 때 겹치는 건 - %가 됨. 

Damage가 - 라는 것은 체력이 오히려 회복된다는 얘기!

 

Ball.cs 안의 CalculateDamage함수

// 이 함수는 어떤 위치를 주면 내 위치에서 그 위치사이까지
    // 거리를 재서 데미지를 얼마만큼 받을지 결정할 것임
    //targetPosition은 상대방 위치
    private float CalculateDamage(Vector3 targetPosition)
    {
        //나의 위치에서 상대방까지의 거리를 Vector3 x, y, z로 쪼갬
        // (x, y, z)로 되어 있음.
        Vector3 explosionToTarget = targetPosition - transform.position;

        //Vector3는 내장 기능으로써 자기 자신의 길이를 가져다줌(피타고라스 법칙 사용해서 실제 하나의 숫자로 뽑아내줌)
        //magnitude는 벡터의 길이
        //폭발의 중심지와 상대방 사이의 거리
        float distance = explosionToTarget.magnitude;

        //알아야 할 것은 나의 위치에서 상대방까지의 거리가 아니라
        //원의 가장자리에서 얼마나 안쪽으로 들어가있냐 알아야함
        float edgeToCenterDistance = explosionRadius - distance;

        float percentage = edgeToCenterDistance / explosionRadius;

        //최종 데미지
        float damage = maxDamage * percentage;

        //- damage : 체력 회복 방지
        // 두 수를 넣었을 때 그 중에서 큰 값을 반환
        damage = Mathf.Max(0,damage);
        
        return damage;


    }

 

3) CalculateDamage 함수 사용해서 각각 colliders들 위치 파악 + 그 colliders는 어느 만큼 데미지 입어야 하는지 계산

 

Rigidbody.AddExplosionForce() :

어떤 지점의 폭발의 위치와 폭발력, 반경 지정해주면 나 자신의 위치가 폭발의 원점으로부터 얼만큼 떨어져있는지 등을 계산해서 스스로 튕겨나가는 작용 적용해줌

 

Ball.cs 안의 OnTriggerEnter 함수

   private void OnTriggerEnter(Collider other) { 
        
        //성능 아끼기 위해 필터링
        //OverlapSphere은 Collider들을 반환함!

        Collider[] colliders =Physics.OverlapSphere(transform.position, explosionRadius, whatIsProp);


        for(int i=0; i<colliders.Length; i++ )
        {
            //데미지 주기 전에 상대방 Rigidbody 가져오기
            Rigidbody targetRigidbody = colliders[i].GetComponent<Rigidbody>();
            //Rigidbody는 편의 기능 중 하나로 폭발의 원점을 지정해주면 
            //폭발물로부터 내가 스스로 얼만큼 떨어져있는지를 계산해서 내가 얼만큼 튕겨나가야할지를 계산해주는 편의기능 함수가 있음

            
            targetRigidbody.AddExplosionForce(explosionForce, transform.position, explosionRadius);


            //현재 순번의 콜라이더로부터 현재 순번의 프롭을 가져옴
            Prop targetProp = colliders[i].GetComponent<Prop>();

            //데미지 주기
            float damage = CalculateDamage(colliders[i].transform.position);

            targetProp.TakeDamage(damage);

        }


        //부모 자식 관계는 transform이 관리함
        explosionParticle.transform.parent = null;
        explosionParticle.Play();
        explosionAudio.Play();

        Destroy(explosionParticle.gameObject, explosionParticle.duration); //파티클 파괴
        Destroy(gameObject); //자기자신 파괴

    }

 

hp(체력) 100으로 높여서 테스트(근거리 원거리 데미지 차이 나는지)

 

 

'Unity' 카테고리의 다른 글

파워 슬라이더  (0) 2021.07.17
Package Manager에 Preview 버전이 안보인다면?  (0) 2021.07.16
회전 포신, 포탄 만들기  (0) 2021.07.15
코루틴  (0) 2021.07.13
UI / 오디오 / 최종빌드  (0) 2021.07.13

댓글