파티클 활용, 폭발, random texture,폭발력 적용(AddExplosionForce)
2023. 8. 21.

벽에 총알 출동시 불꽃이 튀는 효과 구현

-파티클은 표현하고자 하는 텍스처가 적용된 Mesh를 입자(Emitter)처럼 생성후 이동 및 회전시켜 효과를 표현한다.

=>입자가 많아질수록 성능에 부하를 준다.

remove bullet 수정해 프리팹 인스턴스를 생성해준다.

-지금은 스파크가 튀는 방향이 항상 일정하게 생성되므로, 충돌한 지점에서 법선 벡터(Normal Vector)를 구해 해당 방향으로 스파크가 튀도록 변경한다.

https://docs.unity3d.com/ScriptReference/Quaternion.LookRotation.html

 

Unity - Scripting API: Quaternion.LookRotation

Z axis will be aligned with forward, X axis aligned with cross product between forward and upwards, and Y axis aligned with cross product between Z and X. Returns identity if the magnitude of forward is zero. If forward and upwards are colinear, or if the

docs.unity3d.com

 

 

드럼통 오브젝트를 추가한 후, 총에 맞으면 폭발하는 것을 구현하기 위해 barrelController 스크립트를 추가했다.

드럼통 프리팹의 색을 랜덤으로 변경

 

-폭발하는 순간 주변의 물체를 추출하고,드럼통에만 데미지를 적용한다.

드럼통의 레이어를 barrel로 설정한다.

Physics.OverlapSphere 함수는 폭발하는 드럼통 주위에 어떤 드럼통이 있는지 판단한다. 

=> 추출하려는 반경 이내에 들어와있는 것 중에 특정 레이어만 검출한다.

 

비트연산자로 세번째 레이어에 접근(배럴 레이어)

 

-OverlapSphereNonAlloc을 쓰는 걸 권장한다. 이 함수는 결괏값을 저장할 정적 배열을 미리 선언해 사용해야 하며 실행 중에 배열의 크기를 변경할 수 없다: 메모리 Garbage가 발생하지 않도록한다.

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

public class BarrelController : MonoBehaviour
{
    private int hitCount = 0;
    private Transform tr;
    [SerializeField] private GameObject expEffect;   //프리팹
    [SerializeField] private Texture[] textures;
    [SerializeField] private float radius = 3f;
    private new MeshRenderer renderer;
    private Rigidbody rBody;

    private void Start()
    {
        this.tr = GetComponent<Transform>();
        this.rBody = this.GetComponent<Rigidbody>();

        int idx = Random.Range(0, this.textures.Length);
        this.renderer = this.gameObject.GetComponentInChildren<MeshRenderer>();
        this.renderer.material.mainTexture = this.textures[idx];
    }

    //총알에 세발 맞으면 폭파한다 
    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Bullet"))
        {
            //횟수를 증가 
            this.hitCount++;
            Debug.LogFormat("{0}회 맞았다!", this.hitCount);
            if (this.hitCount >= 3)
            {
                this.ExpBarrel();
            }
        }
    }

    void ExpBarrel()
    {
        Debug.Log("폭파");
        //연출 
        //동적으로 폭파 이펙트 생성하기 
        GameObject exp = Instantiate(this.expEffect, this.transform.position, Quaternion.identity);
        Destroy(exp, 0.5f);

        this.rBody.mass = 1f;//무게를 가볍게 
        this.rBody.AddForce(Vector2.up * 1500f);//위로 힘줘서 날려버리기 

        this.IndirectDamage(tr.position);//간접 폭발력 전달
        //3초후 드럼통 제거 
        Destroy(this.gameObject, 3.0f);
    }
    private void IndirectDamage(Vector3 pos)
    {
        //주변에 있는 모든 드럼통을 추출
        Collider[] colls = Physics.OverlapSphere(pos, this.radius, 1 << 3);
        foreach (Collider coll in colls)
        {
            var rb = coll.GetComponent<Rigidbody>();
            rb.mass = 1f;
            rb.constraints = RigidbodyConstraints.None;//freez rotation해제
            rb.AddExplosionForce(1500.0f, pos, radius, 1200.0f);//폭발력을 전달
        }
    }
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(this.transform.position, this.radius);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RemoveBullet : MonoBehaviour
{
    [SerializeField] GameObject sparkEffect;
    private void OnCollisionEnter(Collision collision)
    {
        Debug.Log(collision);

        if (collision.collider.CompareTag("Bullet"))
        {
            //충돌 지점의 법선 벡터를 구하기 위함
            ContactPoint contactPoint = collision.GetContact(0);//첫 번째 충돌 지점의 정보 추출
            DrawArrow.ForDebug(contactPoint.point, -1*contactPoint.normal, 5f, Color.green, ArrowType.Solid);
            Quaternion rotation = Quaternion.LookRotation(-contactPoint.normal);
            //충돌한 타총알의 법선 벡터를 쿼터니언 타입으로 변환
            //point:위치, normal:법선
            GameObject spark = Instantiate(this.sparkEffect,contactPoint.point,rotation);//스파크 파티클을 동적생성
            Destroy(spark, 0.5f);// remove efffect
            Destroy(collision.gameObject); //remove bullet
            
        }
        
    }
}

 

-연습 씬에서 플레이어에 가장 가까이에 있는 무기를 착용하는 코드를 작성해 보았다.

가장 가까이에 있는 무기 착용


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

public class OverlapPlayer : MonoBehaviour
{
    [SerializeField] private float radius = 1f;
    [SerializeField] private Transform weaponTrans;
    private Collider[] colls = new Collider[6];
    private float distance;
    private float minDistance=10;
    // Start is called before the first frame update
    void Start()
    {

        int layerMask = 1 << LayerMask.NameToLayer("Gun");
        int cnt = Physics.OverlapSphereNonAlloc(this.transform.position, this.radius, colls, layerMask);
        Debug.Log(cnt);
        foreach (Collider col in colls)
        {
            //Debug.Log(col.gameObject.name);
            var tr = col.GetComponent<Transform>();
           // Debug.Log(col.gameObject.transform.position);
            
            this.distance = Vector3.Distance(this.transform.position, col.gameObject.transform.position);
            Debug.LogFormat("{1}-distance:{0}", col.gameObject.name,distance);
            if (distance < minDistance)
            {
                minDistance = distance;
                Debug.Log(minDistance);
                col.gameObject.transform.position = this.weaponTrans.position;
                col.gameObject.transform.rotation = this.weaponTrans.rotation;
            }
           
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(this.transform.position, this.radius);
    }
}

-collider인데 콜라이더 안해놓고 멍때리지 말자..ㅋㅋ

딕셔너리에 저장해서 접근하는 방법

myoskin