Models/Barrel 폴더를 보면 Barrel이 2개 있다. 화살표가 달려있는 확장자 FBX인 원본 3D로 작업한다.

선택해보면 프로젝트뷰 아래 확장자가 표시된다.

Barrel을 하이라키로 끌어다 놓고 Transform을 Reset하면 캐릭터에 비해 큰 Barrel이 보인다. 캐럭터 크기에 맞게 스케일을 조정해보자

Scale Factor

외부에서 임포트한 3D모델은 반드시 Transform의 Scale속성을 수정하지 말고 FBX Import setting의 Scale Factor속성을 수정한다. 프로젝트 뷰의 Barrel 3D모델을 선택하면 인스펙터 뷰의 3D모델의 여러정보(FBX Import Settigns)가 표시된다. 첫번째(Model)맵을 선택하고 Scale Factor를 0.4로 설정하면 현재 Barrel모델의 크기를 40% 크기로 조정할 수 있다. 스케일을 변경하고 하단에 있는  [Apply] 버튼을 클릭해 저장한다. (나의 경우 화면에 Apply 팝업창이 떳다)

이제 캐릭터와 배럴의 크기가 비슷해졌고, 배럴의 하이라키속 transform.Scale의 속성은 (1,1,1) 그대로이다.

하이라키뷰의 Barrel에 충돌처리를 위해 Capsule Collider와 Rigidbody컴포넌트를 추가한다. 잘보면 영역을 나타내는 구역이 작다.

Capsule Collider의 속성은 다음과 같이 수정해서 맞춘다. 

Rigidbody 컴포넌트는 질량 mass는 20으로 하고 Constraints는 위치와 회전에 제한을 거는 속성으로 Freeze Rotation x와 z축을 체크한다. x축 z축으로 회전을 금지한다. 넘어지지 않게한다.

게임 Play 후 총알을 발사해 보면 배럴이 살짝 뒤로 움직인다.

이제 Barrel에 충돌한 총알을 제거하고 스파크 효과를 만든다. Wall에 추가한 RemoveBullet스크립트를 재활용한다. Barrel에 RemoveBullet 스크립트를 추가하고 SparksEffect를 연결한다.

이제 실행해보면 배럴에 스파크 효과가 발생하고 Bullet이 없어진다.

배럴에 총알이 3번 충돌하면 배럴이 폭발하게 해보겠다. BarrelCtrl이라는 스크립트를 생성하고 다음과 같이 작성하고 하이라키 뷰의 Barrel에 추가한다.

BarrelCtrl의 ExpEffect속성에 폭발 효과를 내는 파티클을 연결한다. 03. Prefabs/ EffectExamples/ FireExplosionEffects/ Prefabs 폴더에 있는 SmallExplosionEffect를 연결한다. .새로 연결하면 Bullet의 Tag가 변경되는 이상한 증상이 있어 다시 만들어주고 연결해줬다.  폴더내 없는 분들은 Unity Particle Pack에서

 

 import 한후 새로만들어진 폴더에서 기존의 Prefabs폴더로 옮긴다.

SmallExplosionEffect는 프리팹을 선택하고 차일드를 포함하여 모든 Looping 속성을 모두 언체크한다.

실행해보면 잘된다.

 

하이라키뷰의 간략화

Barrel을 03.Prefabs폴더로 이동시켜 프리팹으로 변환하다.

이 프리팹을 이용해 20개정도 배럴을 배치하려고 하는데 그러면 하이라키뷰가 지저분해진다.

따라서 _Stage라는 EmptyObject를 만드고 하위에 복제하면 간단히 정리된다.

하이라키뷰에 빈게임오브젝트를 하나 만들고 이름을  _STAGE로 저장한다. _를 붙이는 이유는 하이라키뷰의 맨위로 보내기 위해서다.

이제 Barrel 프리팹을 _STAGE로 옮긴후 Ctrl-D를 눌러 20개 복사하고 위치를 바궈도 되지만 스크립트를 이용해 자동으로 배치해 보겠다.

StageCtrl 스크립트를 하나 만들어 _Stage에 적용시킨다.

x,z좌표를 랜덤으로 발생해서 Barrel을 배치한면서 Parent를 _Stage 의 transform으로 한다.

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

public class StageCtrl : MonoBehaviour
{
    public GameObject barrel;
    void Start()
    {
        for(int i = 0; i < 20; i++) {
            float x = (float) Random.Range(-24f, 24f);
            float z = (float) Random.Range(-24f, 24f);
            Instantiate(barrel,new Vector3(x,5f,z), Quaternion.identity).transform.SetParent(transform);
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

왼쪽이 실행전 오른쪽이 실행후이다.동적으로 생성된다.

사실 배럴의 좌표는 Random이므로 겹칠수도 있다. 따라서 49x49개의 좌표를 리스트로 만들어 놓고 랜던으로 하나씩 뽑으면서 배치하고 배치한건 리스트에서 지우주면 겹치치않게 만들수 있다.

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

public class StageCtrl : MonoBehaviour
{
    public GameObject barrel;
    void Start()
    {
        List<Vector3> posList = new List<Vector3>();
        for (int ix= -24; ix < 24; ix++) {
            for (int iz = -24; iz < 24; iz++) {
                posList.Add(new Vector3(ix, 0f, iz));
            }
        }
        for(int i = 0; i < 20; i++) {
            int randomIndex = Random.Range(0,posList.Count-1);
            Instantiate(barrel, posList[randomIndex], Quaternion.identity).transform.SetParent(transform);
            posList.RemoveAt(randomIndex);
        }
    }
}

 

 

 

다운로드후 모든걸 import한다 총탄충돌효과 말고도 나중에 배럴을 폭파할 효과도 필요하기 때문이다

 import후 다음같은 폴더내에 sparksEffect.prefab을 Prefabs폴더로 옮겨주고 하이라키나, 씬뷰에 끌어다 주면 불꽃튀는 효과가 보인다.

스파크효과는 총알이 벽에 충돌했을때 발생한 스파크 효과는 RemoveBullet 스크립트에서 구현한다. RemoveBullet스크립트를 다음과 같이 수정한다.

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

public class RemoveBullet : MonoBehaviour
{
    public GameObject sparkEffect;
    private void OnCollisionEnter(Collision coll) {
        if(coll.collider.tag == "Bullet") {
            Instantiate(sparkEffect, coll.transform.position, Quaternion.identity);
            Destroy(coll.gameObject);
        }
    }
}

Wall Prefab인스펙터창의 RemoveBullet스크립트컴포넌트의 sparkEffect참조를 끌어다 연결한다. 실행하기전 하이리키의 Bullet와 sparkle은 지운다.

실행해보면 충돌각도에 관계없이 스파클의 각도가 고정되어 있다. 이유는 sparksEffect를 만들때 Instantiate( , , Quaternion.identity)로 sparksEffect의 원래각도로 만들어서 그렇다. 

따라서 충돌한 지점에서 법선 벡터를 구해 해당 방향으로 스파크가 튀게 변경해보자, 법선 벡터는 두 물체가 충돌한 평면 또는구면의 점접에서 수직 방향으로 바라보는 벡터를 말한다.

RemoveBullet 스크립트를 다음과 같이 수정해 Bullet과 Wall의 정확한 충돌 지점과 해당 접점에서의 법선 벡터를 구해 스파크 파티클을 회전시켜보자

Instantiate()의 세번째 인자는 각도를 서정하며 Quatenion타입이다. Vector3()를 넣면 안된다.

다음 스크립트를 보면 충돌지점 정보는 Collision.GetContact, GetContacts함수를 통해 알수 있다.

ContactPoint cp = coll.GetContact(0);

충돌지점으로 리턴되는 ContactPoint의 프라퍼티는 다음과 같다.

normal Normal of the contact point.
otherCollider The other collider in contact at the point.
point The point of contact.
separation The distance between the colliders at the contact point.
thisCollider The first collider in contact at the point.

Quaternion rot = Quaternion.LookRotation(-cp.normal);   //충돌한 총알의 법선 벡터를 쿼터니언 타입으로 변환

Quaternion.LookRotation()의 정의를 보면 Vector라 cp.normal (노말벡터)를 넣는다.

        public static Quaternion LookRotation(Vector3 forward) {
            return LookRotation(forward, Vector3.up);
        }

cp.normal (노말벡터)는 충돌체의 진행방향이라고 생각하는게 편할듯하다. 이펙트는 총알의 반대방향으로 되야하니 -를 곱하고 쿼터니언으로 바꿔 rot에 넣는다. 이 rot를 이용해 이펙트를 초기화 한다.

사실 총알의 진입방향의 반대라는게 생각하면 이상하다. 벽에 부딪치면 반사되서 반대방향으로 가는 효과는 아닌거다. 이건 나중에 생각하자.

실행해보면 총알의 입사각의 반대로 스파크 효과가 난다. 그러나 없어지지 않는다.

SparksEffect 프리팹을 씬뷰에 끌어다 놓고 하이라키에서 눌러 보면 다음과 같이 3개의 효과가 들어 있다.

위에서 부터 순서대로 눌러 인스펙터뷰에서  Duration은 2 Loop는 꺼준다.

맨마지막꺼는 루프만 꺼주었다.

이렇게 하면 효과는 2초후 꺼지지만 하이라키뷰에서 오브젝트이름은 사라지지 않는다. 따라서 스크립트에서

Instantiate()이후 Destroy(spark,0.5f)로 0.5초후에 파괴되라고 예약을 걸었다.

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

public class RemoveBullet : MonoBehaviour {
    public GameObject sparkEffect;
    private void OnCollisionEnter(Collision coll) {
        if (coll.collider.CompareTag("Bullet")) {
            //첫번째 충돌 지점의 정보추출
            ContactPoint cp = coll.GetContact(0);
            //충돌한 총알의 법선 벡터를 쿼터니언 타입으로 변환
            Quaternion rot = Quaternion.LookRotation(-cp.normal);
            // 스파크 파티클을 동적으로 생성, 총알의 법선벡터의 반대방향으로 
            GameObject spark = Instantiate(sparkEffect, cp.point, rot);
            //충돌한 오브젝트제거
            Destroy(spark,0.5f);
            Destroy(coll.gameObject);
        }
    }
}

RPG류 게임에서 주인공이 휘두르는 검에 표현되는 궤적이나 총알을 발사했을때 총알이 날아가는 궤적을 만들어 보겠다. 

유니티는 이런 효과를 할수 있는 Trail Rendere를 제공한다.

 

Trail Renderer

 

 

Bullet Prefab을 끌어다 씬에 놓는다.

하이러키 뷰에서 Bullet을 선택하고 메뉴에서 Component>Effects>Trail Renderer를 선택해 Trail Renderer를 추가한다. Bullet의 Z축을 잡고 앞으로 드래그하면 동적으로 생성된 메시를 볼수 있다. 이때 씬뷰의 렌더링모드를 Wireframe으로 변경하면 동적으로 만들어진 메시의 형태를 볼 수 있다.

동적으로 생성된 메시는 위와 같이 노란색이 아니라 분홍색이다. 아직 머터리얼이 없어서 그렇다.

Resources/Textures 폴더에 Trail.png를 04.Images폴더에 끌어다 놓는다.\

Images/Material 폴더에 머터리얼을 새로 만들고 BulletTrail으로 이름을 바꾼다.

Shader를 Mobile/Particles/Additive로 변경하면 Particle Texture속성이 나타난다. 여기에 trail.png를 끌어다 연결한다.

완성된 Material을 Bullet에 추가된 Trail Rendere컴포넌트의 Material속성중 Element0에 끌어다 놓는다. 이러면 분홍색에 텍스처가 입려진다.

Trail Rendere컴포넌트의 디테일이다. Width는 곡선의 작은 동그라미를 마우스로 잡고 위치를 바꿀수 있다. 시작두께와 끝나는 지점의 두께를 조정할 수 있다. 더블클릭하면 새로운 노드가 생긴다.

 

타임은 Trail의 지속시간 Min Vertex Distance는 Trail의 진동시간이다. 길면 파도가 길어진다.

칼라를 누르면 다음과 같은 창이 뜨는데 왼쪽끝과 오른쪽 끝 아래 잉크를 눌러 색을 바꾸고 위잉크를 누르면  불투명도를 바꿀수 있다. 앞쪽을 노란색으로 하고 뒤쪽의 불투명도를 180으로 하겠다.

프리팹의 변경내용 조회및 저장

Bullet의 Prefab이 아닌 씬위의 인스턴스를 변경했기 때문에 변화를 Prefab에 저장할 필요가 있다.

하이라키의 Bullet을 선택하고  인스펙터의 Overrides를 클릭하면 변경된 부분이 나타나면 Apply All하면 Prefab에 적용된다. Revert All하면 인스턴스는 다시 Prefab상태로 돌아간다.

변경전 변경된 내용을 클릭하면 다음과 같이 변경된 부분을 비교하면서 확인할 수 있다.

프로젝트뷰의 Bullet Prefab을 눌러 인스펙트뷰에서 Scale을 (0.3,0.3,0.3), 

총알을 빠르게 하기위해서 Rigidbody컴포의 Mass(무게)를 0.1로 해봤는데 파티클이펙트가 안되는 경우가 많아 다시 1로 되돌렸다. 

그냥 총알에 AddForce()를 크게 해주는게 좋을듯 하다.

총기가 밑에 CreateEmpty를 만든후 이름을 FirePos로 변경한고 자식으로 한다.

여기에 MyGizmo라는 Script를 하나 넣어준다.

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

public class MyGizmo : MonoBehaviour
{
    public Color _color = Color.yellow;
    public float _radius = 0.1f;
    private void OnDrawGizmos() {
        Gizmos.color = _color;
        Gizmos.DrawSphere(transform.position, _radius);
    }
}

총끝에 노란공이 보인다. 너무크다

인스펙터뷰에의 My Gizmo Script변수 Radius를 0.02 Color를 클릭해 A를50으로 바꾼다. 

다음같이 예쁘게 바뀐다. 이 기즈모는 씬뷰에서만 보이고 프리뷰에서는 안보인다.

그런데 노란공은 총구와 위치가 잘 않맞는다.

FirePos의 위치를 총구쪽으로 이동시켜준다. 그런데 총이 비스듬하게 되어 있어 되는듯하지만 맞추기 어렵다.

 

GamePlay를 누른후 포즈를 누른다.

그럼 다음같이 총구를 맞추기 쉬운 자세로 드래그및 로테이션을 할수 있다.. 이상태에서 기즈모를 하이리체서 선택해 끌어다 맞춘다. 잘되었으면 그냥 나오면 힘들게 맞춘게 틀어진다. 따라서 현재의 파라메터를 기록해야한다.

Transfor 오른쪽 삼점을 누르면 Copy>Component 할수 있다 플레이를 멈춘후  Paste해 준다. 잘 달라붙는다

 

이렇게 기즈모 스크립트를 작성하지 않아도 유니티에서 좌상 육각형을 눌러보면 아이콘을 고를 수 있다. 대신 이건 사이즈나 투명도를 조정할 수는 없다.

그리고 하이라키뷰의 Player를 선택하고 육각형 Icon을 눌러 위쪽으 가로로긴 원 Lable을 선택하면 게임오브젝트의 이름이 씬뷰에만 표시된다.

 

FireCtrl

BulletCtrl도 원래대로한다.

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

public class BulletCtrl : MonoBehaviour
{
    public float damage = 20.0f;
    public float force = 1500.0f;
    private Rigidbody rb;
    void Start() {
        rb = GetComponent<Rigidbody>();
        rb.AddForce(transform.forward * force);
    }
}

이전에 만든 AddForceCtrl.cs 을 제거하고 프로젝트뷰에서 아예 지워버리고 마우스좌클릭을 하면 총알을 발사하기 위해 FireCtrl.cs 스크립트을 다시 작성해서 Player에 적용할 것이다. 총알은 게임중 동적으로 생성할것이므로 하이라키의 Bullet은 지운다. 지워도 아까 프리팹으로 만들어 놨기때문에 괜찮다. 

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class FireCtrl : MonoBehaviour
{
    public GameObject bullet;
    public Transform firePos;

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0)) {
            fire();
        }
    }
    void fire() {
        Instantiate(bullet,firePos.position, firePos.rotation);
    }
}

실행시키기전 글로벌변수를 인스펙트에서 끌어다 놔야한다.  프리팹의 Bullet과 하이라키 총밑에 있는 FirePos를 끌어다 연결해준다.

전 실행해보니 벽에 맞은 총알이 안없어지고 튕겨서 wall prefab에 removeBullet Script를 다시 적용해 줬다.

 

이번 장을 정리해보면 Player가 좌클릭을 감지하면 Fire()로 Bullet객체를 하나 만들어 준다.

만들어진 Bullet은 start()이벤트에서 AddForce()로 스스로 앞으로 날라간다.

 

좌클릭을 많이하면 하이라키뷰에 Bullet인스턴스가 많이 보일것다. 벽에 부딪치면 없어질것이고

총알이 Wall에 충돌했을 때 Wall의 관점에서 생각해 보면 Collider 컴포넌트를 가진 어떤 게임오브젝트가 날아와서 충돌이 일어났는지 알 필요가 있다. 물론 충돌한 오브젝트의 이름으로 판단할 수도 있지만 인스턴트의 경우 번호가 붙으므로 프리팹에 Tag를 지정하고  Tag명식별하는 법이 있다. 

Tag는 기본적으로는 Untagged이지만 디폴트로 생성된 이름을 선택할 수도 있고 Tag 콤보박스를 클릭한다음 Add Tag로 만들수 있다.  대소문자를 식별한다.

 

Tags의 +를 누르면 이름을 추가할 수 있다.

Inspector의 루트로 빠져나오면 Tab에 Bullet이 추가되어 있고 선택하면 Untagged에서 Bullet로 변경된다.

총알이 벽에 충돌할때 발생되는 이벤트함수가 호출되는지 확인해보자 Wall prefab에 스크립트를 추가한후 RemoveBullet라고 이름바꾼다. 이러면 하이라키의 4개의 wall에 다 적용된다.

코드를 보면 총알이 벽에 부딪치면 다음같은 이벤트가 발생되고 Collision 객체가 전달된다.

이 Collision은 충돌 객체및 Tag이름, 충돌위치, 충돌속도가 들어 있다

 

코드는 이중 tag를 식별해 충돌된 Bullet 오브젝트를 제거한다.
    private void OnCollisionEnter(Collision coll) {
        if(coll.collider.tag == "Bullet") {
            Destroy(coll.gameObject);
        }
    }

 

collision.CompareTag()

 if(coll.collider.tag == "Bullet")  와 같이 게임오브젝트의 문자속성을 가져오는 코드는 실행시 문자열의 복사본을 생성한다. 따라서 가지비컬렉션이 발생하지 않는 coll.collider.comapreTag("bullet")를 추천한다.

 

Bullet의 Prefab 전환

총을을 발사해보면 벽과 충돌하면 Destroy()되면서 하이라키뷰에서 사라지기 때문에 한발이상 쏠수가 없다 이걸 해결해야한다.

Bullet을 끌어다 Prefab 폴더에 넣는다.

 

❤️Collision 설명 - Collider가 아닙니다. 충돌이 발생하면 리턴되는 인자입니다.

모든 충돌체의 기본 클래스입니다.

참조: BoxCollider , SphereCollider , CapsuleCollider , MeshCollider , PhysicMaterial , Rigidbody .

Collider가 있는 개체를 게임 플레이 중에 이동해야 하는 경우 개체에 Rigidbody 구성 요소도 연결해야 합니다. 개체가 다른 개체와 물리적 상호 작용을 하지 않도록 하려면 Rigidbody를 운동학적으로 설정할 수 있습니다 .

속성

attachedArticulationBody 콜라이더가 부착된 관절 본체입니다.
attachedRigidbody 콜라이더가 부착된 리지드바디 .
bounds 콜라이더의 세계 공간 경계 볼륨(읽기 전용).
contactOffset 이 콜라이더의 접촉 오프셋 값입니다.
enabled 활성화된 Collider는 다른 Collider와 충돌하지만 비활성화된 Collider는 충돌하지 않습니다.
hasModifiableContacts 이 Collider의 연락처를 수정할 수 있는지 여부를 지정합니다.
isTrigger 이 콜라이더가 트리거로 구성되었는지 지정합니다.
material 충돌체에서 사용되는 재료입니다.
sharedMaterial 이 충돌체의 공유 물리 물질입니다.

공개 방법

ClosestPoint 주어진 위치에 가장 가까운 충돌체의 지점을 반환합니다.
ClosestPointOnBounds 부착된 콜라이더의 경계 상자에 가장 가까운 지점입니다.
레이캐스트 이 충돌체를 제외한 모든 충돌체를 무시하는 광선을 던집니다.

메시지

OnCollisionEnter OnCollisionEnter는 이 collider/ rigidbody가 다른 rigidbody /collider 에 닿기 시작 했을 때 호출됩니다 .
OnCollisionExit OnCollisionExit는 이 collider/ rigidbody가 다른 rigidbody /collider와 접촉하는 것을 멈췄을 때 호출됩니다.
OnCollisionStay OnCollisionStay는 다른 Collider 또는 Rigidbody 에 닿는 모든 Collider 또는 Rigidbody 에 대해 프레임당 한 번씩 호출됩니다 .
OnTriggerEnter GameObject가 다른 GameObject와 충돌 하면 Unity는 OnTriggerEnter를 호출합니다 .
OnTriggerExit OnTriggerExit는 다른 Collider가 트리거 터치를 중지했을 때 호출됩니다.
OnTriggerStay OnTriggerStay는 트리거에 닿는 모든 Collider 다른 프레임에 대해 거의 모든 프레임이라고 합니다. 이 함수는 물리 타이머에 있으므로 모든 프레임에서 반드시 실행되는 것은 아닙니다.

상속된 구성원

속성

gameObject 이 구성 요소가 연결된 게임 개체입니다. 구성 요소는 항상 게임 개체에 연결됩니다.
tag 이 게임 오브젝트의 태그입니다.
transform GameObject 에 첨부된 Transform입니다 .
hideFlags 개체를 숨겨야 합니까, 장면과 함께 저장해야 합니까, 아니면 사용자가 수정할 수 있어야 합니까?
이름 개체의 이름입니다.

공개 방법

BroadcastMessage 이 게임 개체 또는 그 자식의 모든 MonoBehaviour 에서 methodName 이라는 메서드를 호출합니다 .
CompareTag 정의된 태그에 대해 GameObject의 태그를 확인합니다 .
GetComponent GameObject 에서 유형 T의 구성 요소에 대한 참조를 가져옵니다 .
GetComponentInChildren GameObject 의 자식과 동일한 GameObject 에서 유형 T의 구성 요소에 대한 참조를 가져옵니다 .
GetComponentInParent GameObject 의 부모와 동일한 GameObject 에 있는 T 유형의 구성 요소에 대한 참조를 가져옵니다 .
GetComponents GameObject 에서 유형 T의 모든 구성 요소에 대한 참조를 가져옵니다 .
GetComponentsInChildren GameObject 의 자식과 동일한 GameObject 에 있는 T 유형의 모든 구성 요소에 대한 참조를 가져옵니다 .
GetComponentsInParent GameObject 의 부모와 동일한 GameObject 에 있는 T 유형의 모든 구성 요소에 대한 참조를 가져옵니다 .
SendMessage 이 게임 개체의 모든 MonoBehaviour 에서 methodName 이라는 메서드를 호출합니다 .
SendMessageUpwards 이 게임 개체의 모든 MonoBehaviour 와 동작 의 모든 조상에 대해 methodName 이라는 메서드를 호출합니다 .
TryGetComponent 존재하는 경우 지정된 유형의 구성 요소를 가져옵니다.
GetInstanceID 개체의 인스턴스 ID를 가져옵니다.
ToString 객체의 이름을 반환합니다.

정적 메서드

Destroy GameObject , 구성 요소 또는 자산을 제거합니다 .
DestroyImmediate 개체 obj를 즉시 파괴합니다. 대신 Destroy를 사용하는 것이 좋습니다.
DontDestroyOnLoad 새 장면을 로드할 때 대상 개체를 파괴하지 마십시오.
FindAnyObjectByType 유형 의 활성 로드된 개체를 검색합니다 .
FindFirstObjectByType type 의 첫 번째 활성 로드된 개체를 검색합니다 .
FindObjectOfType 유형 의 첫 번째 활성 로드된 개체를 반환합니다 .
FindObjectsByType type 의 모든 로드된 객체 목록을 검색합니다 .
FindObjectsOfType type 의 모든 로드된 개체 목록을 가져옵니다 .
Instantiate 개체 원본을 복제하고 복제본을 반환합니다.

연산자

bool 개체가 존재합니까?
operator != 두 개체가 다른 개체를 참조하는지 비교합니다.
operator == 두 개체 참조를 비교하여 동일한 개체를 참조하는지 확인합니다.

충돌체

UnityEngine 의 클래스 / 상속 : 구성요소 / 구현 위치: UnityEngine.PhysicsModule

 

설명

모든 충돌체의 기본 클래스입니다.

참조: BoxCollider , SphereCollider , CapsuleCollider , MeshCollider , PhysicMaterial , Rigidbody .

Collider가 있는 개체를 게임 플레이 중에 이동해야 하는 경우 개체에 Rigidbody 구성 요소도 연결해야 합니다. 개체가 다른 개체와 물리적 상호 작용을 하지 않도록 하려면 Rigidbody를 운동학적으로 설정할 수 있습니다 .

 

 

충돌시 OnCollision()계열 이벤트함수가 발생한다. 

모든 Collider 컴포넌트에는 IsTrigger 속성이 있다. 이속성이 Eable되어 있으면 OnTrigger()이벤트함수가 발생하며, 충돌감지는 되지만 물리충돌은 이뤄지지않는다

 

메시지 (이벤트함수)

OnCollisionEnter OnCollisionEnter는 이 collider/ rigidbody가 다른 rigidbody /collider 에 닿기 시작 했을 때 호출됩니다 .
OnCollisionExit OnCollisionExit는 이 collider/ rigidbody가 다른 rigidbody /collider와 접촉하는 것을 멈췄을 때 호출됩니다.
OnCollisionStay OnCollisionStay는 다른 Collider 또는 Rigidbody 에 닿는 모든 Collider 또는 Rigidbody 에 대해 프레임당 한 번씩 호출됩니다 .
OnTriggerEnter GameObject가 다른 GameObject와 충돌 하면 Unity는 OnTriggerEnter를 호출합니다 .
OnTriggerExit OnTriggerExit는 다른 Collider가 트리거 터치를 중지했을 때 호출됩니다.
OnTriggerStay OnTriggerStay는 트리거에 닿는 모든 Collider 다른 프레임에 대해 거의 모든 프레임이라고 합니다. 이 함수는 물리 타이머에 있으므로 모든 프레임에서 반드시 실행되는 것은 아닙니다.

속성

attachedArticulationBody 콜라이더가 부착된 관절 본체입니다.
attachedRigidbody 콜라이더가 부착된 리지드바디 .
bounds 콜라이더의 세계 공간 경계 볼륨(읽기 전용).
contactOffset 이 콜라이더의 접촉 오프셋 값입니다.
enabled 활성화된 Collider는 다른 Collider와 충돌하지만 비활성화된 Collider는 충돌하지 않습니다.
hasModifiableContacts 이 Collider의 연락처를 수정할 수 있는지 여부를 지정합니다.
isTrigger 이 콜라이더가 트리거로 구성되었는지 지정합니다.
material 충돌체에서 사용되는 재료입니다.
sharedMaterial 이 충돌체의 공유 물리 물질입니다.

Public Methods

ClosestPoint 주어진 위치에 가장 가까운 충돌체의 지점을 반환합니다.
ClosestPointOnBounds 부착된 콜라이더의 경계 상자에 가장 가까운 지점입니다.
Raycast 이 충돌체를 제외한 모든 충돌체를 무시하는 광선을 던집니다.

상속된 구성원 속성

gameObject 이 구성 요소가 연결된 게임 개체입니다. 구성 요소는 항상 게임 개체에 연결됩니다.
tag 이 게임 오브젝트의 태그입니다.
transform GameObject 에 첨부된 Transform입니다 .
hideFlags 개체를 숨겨야 합니까, 장면과 함께 저장해야 합니까, 아니면 사용자가 수정할 수 있어야 합니까?
이름 개체의 이름입니다.

Public Methods

BroadcastMessage 이 게임 개체 또는 그 자식의 모든 MonoBehaviour 에서 methodName 이라는 메서드를 호출합니다 .
CompareTag 정의된 태그에 대해 GameObject의 태그를 확인합니다 .
GetComponent GameObject 에서 유형 T의 구성 요소에 대한 참조를 가져옵니다 .
GetComponentInChildren GameObject 의 자식과 동일한 GameObject 에서 유형 T의 구성 요소에 대한 참조를 가져옵니다 .
GetComponentInParent GameObject 의 부모와 동일한 GameObject 에 있는 T 유형의 구성 요소에 대한 참조를 가져옵니다 .
GetComponents GameObject 에서 유형 T의 모든 구성 요소에 대한 참조를 가져옵니다 .
GetComponentsInChildren GameObject 의 자식과 동일한 GameObject 에 있는 T 유형의 모든 구성 요소에 대한 참조를 가져옵니다 .
GetComponentsInParent GameObject 의 부모와 동일한 GameObject 에 있는 T 유형의 모든 구성 요소에 대한 참조를 가져옵니다 .
SendMessage 이 게임 개체의 모든 MonoBehaviour 에서 methodName 이라는 메서드를 호출합니다 .
SendMessageUpwards 이 게임 개체의 모든 MonoBehaviour 와 동작 의 모든 조상에 대해 methodName 이라는 메서드를 호출합니다 .
TryGetComponent 존재하는 경우 지정된 유형의 구성 요소를 가져옵니다.
GetInstanceID 개체의 인스턴스 ID를 가져옵니다.
ToString 객체의 이름을 반환합니다.

정적 메서드

Destroy GameObject , 구성 요소 또는 자산을 제거합니다 .
DestroyImmediate 개체 obj를 즉시 파괴합니다. 대신 Destroy를 사용하는 것이 좋습니다.
DontDestroyOnLoad 새 장면을 로드할 때 대상 개체를 파괴하지 마십시오.
FindAnyObjectByType 유형 의 활성 로드된 개체를 검색합니다 .
FindFirstObjectByType type 의 첫 번째 활성 로드된 개체를 검색합니다 .
FindObjectOfType 유형 의 첫 번째 활성 로드된 개체를 반환합니다 .
FindObjectsByType type 의 모든 로드된 객체 목록을 검색합니다 .
FindObjectsOfType type 의 모든 로드된 개체 목록을 가져옵니다 .
Instantiate 개체 원본을 복제하고 복제본을 반환합니다.

연산자

bool 개체가 존재합니까?
operator != 두 개체가 다른 개체를 참조하는지 비교합니다.
operator == 두 개체 참조를 비교하여 동일한 개체를 참조하는지 확인합니다.

 

게임에서 충돌 감지는 매우 중요한 요소 중 하나다. 유니티는 충돌 감지를 위해 다양한 Collider 컴포넌트를 제공한다. Collider 컴포넌트는 충돌을 감지하는 일종의 센서라고 생각하면 이해하기 쉽고, 다음과 같이 여러 형태로 제공하다.

모양과 기능에 따라 Box, Sphere Capsule, Mesh, Wheel, Terrain 등이 있다

 

충돌 감지 조건

충돌을 일으키는 양쪽 게임오브젝트에는 모두 Collider 컴포넌트가 추가돼 있어야 한다.

두 게임오브젝트 중 움직이는 쪽에는 반드시 Rigidbody 컴포넌트가 있어야 한다.

사방의 벽인 Wall에는 기본적으로 Collider가 들어 있으나 Bullet에는 Rigidbody만 있고  Collider는 안들어 있다 . AddComponent로 Box Collider를 추가해준다.

박스가 총알에 비해 크므로 좀 줄여준다.

플레이 해보면 총알이 벽에 튕기는 걸 볼수 있다.

 

Collision action matrix

두 객체가 충돌하면 충돌하는 객체의 강체 구성에 따라 다양한 스크립트 이벤트가 발생할 수 있습니다. 아래 차트는 개체에 연결된 구성 요소를 기반으로 호출되는 이벤트 함수에 대한 세부 정보를 제공합니다. 일부 조합은 두 오브젝트 중 하나만 충돌의 영향을 받지만 일반적인 규칙은 Rigidbody 구성 요소가 첨부되지 않은 오브젝트에는 물리가 적용되지 않는다는 것입니다.

충돌 감지가 발생하고 충돌 시 메시지가 전송됩니다. 리지드바디는 트리거가 아닌 콜라이더와 반응한다.

  Static Collider Rigidbody Collider Kinematic Rigidbody Collider Static Trigger Collider Rigidbody Trigger Collider Kinematic Rigidbody Trigger Collider
Static Collider   Y        
Rigidbody Collider Y Y Y      
Kinematic Rigidbody Collider   Y        
Static Trigger Collider            
Rigidbody Trigger Collider            
Kinematic Rigidbody Trigger Collider            

트리거 메시지는 충돌 시 전송됩니다. 트리거는 트리거끼리 반응하는것 같다.

  Static Collider Rigidbody Collider Kinematic Rigidbody Collider Static Trigger Collider Rigidbody Trigger Collider Kinematic Rigidbody Trigger Collider
Static Collider         Y Y
Rigidbody Collider       Y Y Y
Kinematic Rigidbody Collider       Y Y Y
Static Trigger Collider   Y Y   Y Y
Rigidbody Trigger Collider Y Y Y Y Y Y
Kinematic Rigidbody Trigger Collider Y Y Y Y Y Y

Physics - 물리학

Unity는 프로젝트에서 물리를 시뮬레이션하여 객체가 올바르게 가속되고 충돌에 응답하도록 합니다. 중력 및 기타 다양한 힘. Unity는 3D, 2D, 객체 지향 또는 데이터 지향 등 프로젝트 요구에 따라 사용할 수 있는 다양한 물리 엔진 구현을 제공합니다. 

Built-in 3D Physics

유니티에는 엔비디아(Nvidia)사의 피직스(PhysX)엔진이 내부에 탑재가 되어 있어서 이를 토대로 다양한 컴포넌트들을 이용 물리 효과를 줄 수 있게 하였다.

Character control first-person와 third-person characters를 위한 물리 기반 캐릭터 제어 구성
Rigidbody physics GameObjects에 물리 기반 동작 적용
Collision
콜라이더를 사용하여 GameObject 간의 충돌을 구성합니다.
Joints
게임 오브젝트를 연결하고 피벗, 이동 및 제한을 위한 물리적 힘을 시뮬레이션하는 조인트를 적용 및 구성합니다.
Articulations 강체와 관절의 복잡한 시스템을 구성합니다.
Ragdoll physics 캐릭터에 대한 래그돌 물리학을 구성합니다.
Cloth
캐릭터 의류 및 기타 인앱 직물의 직물 움직임을 시뮬레이션합니다.
Multi-scene physics 여러 전용 물리 장면이 있는 하나의 프로젝트에서 다양한 물리 컨텍스트를 관리합니다.

Physics Reference 2D

  2D 물리 엔진으로는 유명한 Box2D 엔진이 탑재가 되어 있다. 다음 같은 2D 컴포넌트들이 있다.

Rigidbody 2D 리지드바디 2D의 작동 방식을 알아보세요.
Collider 2D Rigidbody 2D와 함께 사용할 수 있는 Collider 2D 구성 요소의 유형을 이해합니다.
Physics Material 2D
충돌하는 2D 물리 개체 사이의 마찰과 바운스를 제어합니다.
2D Joints 2D 게임 오브젝트와 함께 사용할 수 있는 2D 조인트 유형을 이해합니다.
Constant Force 2D 리지드바디 2D에 일정한 힘을 추가합니다.
Effectors 2D GameObject 충돌체가 접촉할 때 물리력을 지시합니다.

 

Physics 설정(메인 메뉴: Edit > Project Settings 에서 Physics 카테고리 선택 ) 을 사용하여 3D 물리에 대한 전역 설정을 적용합니다. 

참고: 2D 물리에 대한 전역 설정을 관리하려면 Physics 2D 설정을 대신 사용하십시오.

 

3D 물리 설정

이러한 설정은 물리적 시뮬레이션의 정확도에 대한 제한을 정의합니다. 일반적으로 더 정확한 시뮬레이션에는 더 많은 처리 오버헤드가 필요하므로 이러한 설정은 정확도와 성능을 절충할 수 있는 방법을 제공합니다. 자세한 내용은 매뉴얼의 물리학 섹션을 참조하십시오.

재산 기능
중력 Gravity: Rigidbody 구성 요소 에 적용되는 중력의 양을 설정합니다 . 사실적인 중력 설정을 위해 y축에 음수를 적용합니다. 중력은 제곱초당 세계 단위로 정의됩니다.
참고 : 중력을 높이면 안정적인 접촉을 유지하기 위해 기본 솔버 반복 값도 늘려야 할 수 있습니다.
기본 재료 Default Material: Collider에 할당된 것이 없는 경우 사용할 기본 Physics Material에 대한 참조를 설정합니다 .
바운스 임계값 Bounce Threshold: 속도 값을 설정합니다. 충돌하는 두 개체의 상대 속도가 이 값보다 낮으면 서로 튕기지 않습니다. 이 값은 또한 지터를 감소시키므로 매우 낮은 값으로 설정하지 않는 것이 좋습니다.
기본 최대 침투 속도 Default Max Depenetration Velocity: 침투 속도의 기본값을 정의합니다 (솔버가 다른 바디와 겹치지 않도록 바디를 당기는 동안 바디에 설정할 수 있는 속도).
수면 임계값 Sleep Threshold : 비운동학적 리지드바디 (즉, 물리 시스템에 의해 제어되지 않는 리지드바디)가 잠들 수 있는 전역 에너지 임계값을 설정합니다. Rigidbody 가 잠자고 있을 때 매 프레임마다 업데이트되지 않으므로 리소스를 덜 사용합니다. Rigidbody의 운동 에너지를 질량으로 나눈 값이 이 임계값 미만 이면 수면 후보입니다.
기본 접촉 오프셋 Default Contact Offset : 충돌 감지 시스템이 충돌 접촉을 생성하는 데 사용하는 거리를 설정합니다 . 값은 양수여야 하며 0에 너무 가깝게 설정하면 지터가 발생할 수 있습니다. 기본적으로 0.01로 설정됩니다. Collider는 거리가 접촉 오프셋 값의 합보다 작은 경우에만 충돌 접촉을 생성합니다.
기본 솔버 반복 Default Solver Iterations 모든 물리 프레임에서 Unity가 실행하는 솔버 프로세스 수를 정의합니다 . 솔버는 관절의 움직임 또는 겹치는 Rigidbody 구성 요소 간의 접촉 관리와 같은 여러 물리적 상호 작용을 결정하는 작은 물리 엔진 작업입니다 . 이것은 솔버 출력의 품질에 영향을 미치며 기본값이 아닌 Time.fixedDeltaTime이 사용되거나 구성이 추가로 요구되는 경우 속성을 변경하는 것이 좋습니다 . 일반적으로 관절이나 접촉으로 인한 지터를 줄이는 데 사용됩니다.
기본 솔버 속도 반복 Default Solver Velocity Iterations : 솔버가 각 물리 프레임에서 수행하는 속도 프로세스 수를 설정합니다 . 솔버가 수행하는 프로세스가 많을수록 리지드바디 바운스 후 결과 출구 속도의 정확도가 높아집니다. 접합된 Rigidbody 구성 요소 또는 Ragdoll이 충돌 후 너무 많이 움직이는 문제가 발생하면 이 값을 늘려 보십시오.
쿼리 적중 뒷면 Queries Hit Backfaces : 물리 쿼리(예: Physics.Raycast )가 MeshColliders 의 후면 삼각형 이 있는 적중을 감지하려면 이 옵션을 활성화합니다 . 기본적으로 이 설정은 비활성화되어 있습니다.
쿼리 적중 트리거 Queries Hit Triggers : 물리 히트 테스트(예: Raycasts , SphereCasts 및 SphereTests )가 Trigger로 표시된 Collider와 교차할 때 히트를 반환하도록 하려면 이 옵션을 활성화합니다. 개별 레이캐스트는 이 동작을 재정의할 수 있습니다. 기본적으로 이 설정은 활성화되어 있습니다.
어댑티브 포스 활성화 Enable Adaptive Force : 적응형 힘을 활성화하려면 이 옵션을 활성화합니다. 적응형 힘은 물체 더미나 스택을 통해 힘이 전달되는 방식에 영향을 주어 보다 사실적인 동작을 제공합니다 . 기본적으로 이 설정은 비활성화되어 있습니다.
연락처 생성 Contacts Generation : 연락처 생성 방법을 선택합니다.
레거시 연락처 생성 Unity 5.5 이전에 Unity는 축 분리 정리( SAT . PCM이 더 효율적이지만 이전 프로젝트의 경우 물리학을 약간 다시 조정할 필요가 없도록 SAT를 계속 사용하는 것이 더 쉬울 수 있습니다 . PCM은 결과를 초래할 수 있습니다. (즉, OnCollisionEnter , OnCollisionStay OnCollisionExit 에 전달된 Collision 인스턴스에서 얻은 배열 )
업그레이드 조언 : Unity 2018.2로 만든 프로젝트를 마이그레이션하려면 또는 더 낮으면 매니폴드에서 패치를 병합하고 연락처를 선택하는 코드와 함께 작동하도록 스크립트를 업데이트해야 할 수 있습니다.
영구 접점 매니폴드(PCM) Persistent Contacts Manifold (PCM) : 매 물리 프레임마다 더 적은 수의 접촉을 생성하고 더 많은 접촉 데이터가 프레임 간에 공유됩니다. PCM 접촉 생성 경로도 더 정확하며 일반적으로 대부분의 경우 더 나은 충돌 피드백을 생성합니다. 자세한 내용은 Persistent Contact Manifold에 대한 Nvidia 설명서를 참조하십시오 . 이것이 기본값입니다.
자동 시뮬레이션 물리 시뮬레이션을 자동으로 실행하거나 이에 대한 명시적 제어를 허용하려면 이 옵션을 활성화합니다.
자동 동기화 변환 Transform 구성 요소가 변경될 때마다 물리 시스템과 변환 변경 사항을 자동으로 동기화하려면 이 옵션을 활성화하십시오 . 기본적으로 이 설정은 비활성화되어 있습니다.
연락처 쌍 모드 Contact Pairs Mode : 사용할 접점 쌍 생성 유형을 선택하십시오.
기본 연락처 쌍 kinematic-kinematic 및 kinematic-static 쌍을 제외한 모든 접촉 쌍에서 충돌 및 트리거 이벤트를 수신합니다.
운동학 쌍 활성화 Enable Kinematic Kinematic Pairs : 운동학적-운동학적 접촉 쌍에서 충돌 및 트리거 이벤트를 수신합니다.
운동학적 정적 쌍 활성화 Enable Kinematic Static Pairs : 운동학-정적 접촉 쌍에서 충돌 및 트리거 이벤트를 수신합니다.
모든 연락처 쌍 활성화 Enable All Contact Pairs : 모든 연락처 쌍에서 충돌 및 트리거 이벤트를 수신합니다.
광상 유형 Broadphase Type : 물리 시뮬레이션에서 사용할 광역 위상 알고리즘을 선택합니다 . PhysX SDK Rigid Body Collision 에 대한 NVIDIA 문서를 참조하십시오 .
브로드페이즈 스윕 및 가지치기 Sweep and Prune Broadphase : 스윕 앤 프룬(sweep-and-prune) 광역 위상 충돌 방법(즉, 멀리 떨어져 있는 쌍을 확인해야 하는 것을 배제하기 위해 단일 축을 따라 객체를 정렬)을 사용합니다.
멀티박스 프루닝 Broadphase Multibox Pruning Broadphase: Multi-box pruning은 그리드를 사용하며 각 그리드 셀은 스위프&프루닝을 수행합니다. 예를 들어 평평한 세계에 많은 게임 오브젝트가 있는 경우 일반적으로 성능을 향상시키는 데 도움이 됩니다 .
자동 상자 가지치기 세계 경계와 분할 수를 자동으로 계산할 수 있다는 점을 제외하면 Multibox Pruning 과 유사합니다 . 그리드 셀 세트를 유지하고 정기적인 스윕 앤 프룬 접근 방식을 사용하여 잠재적으로 겹칠 수 있는 충돌체 쌍을 해결합니다. 일반적 으로 단일 Sweep 및 Prune이 많은 추가 오탐지를 생성하는 큰 장면 에 도움이 됩니다 .
세계 경계 World Bounds : Multibox Pruning Broadphase 알고리즘 을 사용할 때 멀리 떨어져 있는 개체가 서로 영향을 주지 않도록 세계를 둘러싸는 2D 그리드를 정의합니다 . 이 옵션은 Broadphase Type을 Multibox Pruning Broadphase 로 설정한 경우에만 사용됩니다 .
월드 세분화 World Subdivisions : 2D 그리드 알고리즘에서 x축과 z축을 따라 있는 셀의 수입니다. 이 옵션은 Broadphase Type을 Multibox Pruning Broadphase 로 설정한 경우에만 사용됩니다 .
마찰 유형 Friction Type : 시뮬레이션에 사용되는 마찰 알고리즘을 선택합니다.
패치 마찰 유형 Patch Friction Type : 일반적으로 낮은 솔버 반복 횟수에서 가장 안정적인 결과로 이어지는 기본적인 강력한 마찰 알고리즘입니다. 이 방법은 접촉하는 객체 쌍당 최대 4개의 스칼라 솔버 제약 조건만 사용합니다.
한 방향 마찰 유형 Coulomb 마찰 모델의 단순화로, 주어진 접촉점에 대한 마찰이 접촉 법선의 교차하는 접선 방향으로 적용 됩니다 . 이를 위해서는 패치 마찰보다 더 많은 솔버 반복이 필요하지만 2방향 모델만큼 정확하지는 않습니다. 관절 바디가 이 마찰 유형과 함께 작동하도록 하려면 솔버 유형을 Temporal Gauss Seidel 로 설정하십시오 .
두 방향 마찰 유형 단방향 모델과 같지만 두 접선 방향으로 동시에 마찰을 적용합니다. 이렇게 하려면 더 많은 솔버 반복이 필요하지만 더 정확합니다. 모든 접점에 적용되기 때문에 접점이 많은 시나리오의 경우 패치 마찰보다 비용이 많이 듭니다. 관절 바디가 이 마찰 유형과 함께 작동하도록 하려면 솔버 유형을 Temporal Gauss Seidel 로 설정하십시오 .
향상된 결정성 활성화 게임이 결정론적 순서로 액터를 삽입한다면 장면의 시뮬레이션은 액터가 존재하는지에 관계없이 일관됩니다. 이 모드는 이러한 추가 결정성을 보장하기 위해 일부 성능을 희생합니다.
통합 하이트맵 활성화 Enable Unified Heightmaps : 메시 충돌 과 동일한 방식으로 지형 충돌을 처리하려면 이 옵션을 활성화합니다 .
솔버 유형 Solver Type : 시뮬레이션에 사용할 PhysX 솔버 유형을 선택합니다.
투영된 가우스 자이델 Projected Gauss Seidel : 기본 PhysX 솔버입니다.
시간적 가우스 자이델 Temporal Gauss Seidel : 조인트 의 저항을 향상시킵니다 . 일반적으로 기본 솔버로 시뮬레이션하는 동안 일부 비정상적인 동작이 발생할 때 도움이 됩니다.
레이어 충돌 매트릭스 Layer Collision Matrix : 레이어 기반 충돌 감지 시스템의 작동 방식을 정의합니다 . Collision Matrix에서 레이어를 확인하여 다른 레이어와 상호 작용하는 레이어를 선택합니다.
Cloth Inter-Collision : 옷감 상호 충돌
거리 상호 충돌하는 각 Cloth 파티클 주위의 구형 직경을 정의합니다 . Unity는 이러한 구체가 시뮬레이션 중에 겹치지 않도록 합니다. 거리는 구성에서 두 입자 사이의 최소 거리보다 작아야 합니다. 거리가 더 크면 옷감 충돌이 일부 거리 제한을 위반하여 지터링이 발생할 수 있습니다.
단단함 상호 충돌하는 천 파티클 사이의 분리 임펄스의 강도  HYPERLINK "https://docs.unity3d.com/2021.3/Documentation/Manual/class-Cloth.html" \l "intercollision" 입니다. 천 솔버는 이것을 계산하고 파티클을 분리된 상태로 유지하기에 충분해야 합니다.

BulletCtrl이라는 스크립트를 하나 만듭니다.

Start() 함수에서 Rigidbody 참조를 rb에 저장한후 Addforce()메서드로 힘을 한번준다.

Update()함수가 없는데도 플레이해보면 총알이 발사된다.

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

public class BulletCtrl : MonoBehaviour
{
    public float damage = 20.0f;
    public float force = 1500.0f;
    private Rigidbody rb;
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.AddForce(transform.forward * force);
    }
}

스크립트를 하이라키의 Bullet에 끌어다 넣는다. 자식인 lowpolybullet에 넣으면 안됩니다.

플레이 해보면 순식간에 없어져서 확인하기 어렵습니다.

Bullet 인스펙터뷰의 BulletCtrl스크립트컴포넌트의 force를 100으로 하면 천천히 움직이는 걸 볼수 있다. 확인후 다시 1500으로 돌려준다. 플레이중 100을 입력하면 실행이 끝난후 자동으로 1500으로 돌아간다.

플레이전에 값을 바꾸면 영구적으로 바뀌게 되고 스크립트의 초기화 보다 우선하게 된다. 이게 가끔 문제를 일으킨다. 코드에서 초기값을 바꾸어도 변화가 없기 때문에 찾는데 애를 먹는다.

 

AddForce()의 인자 Vector3는 월드좌표기준으로 힘을 가하므로 게임오브젝트의 Y축이 회전해도 항상 같은 월드좌표의 Z축방향으로 날라간다. 로컬좌표를 기준으로 힘을 주려면 transform.forward * force로 지정하거나 AddRelativeForce()함수를 사용해야 한다.

AddForce(Vector3) : 월드좌표로 힘을준다
AddForce(transform.foward)  : 게임오브젝트의 forward로 힘을준다.
AddRelativeForce(Vector3) : 게임오브젝트의 로컬좌표로 힘을준다.

좀 직관적이지 않는데

만일 AddForce(new Vector3(0,0,1f)) 하면 오브젝트의 방향과 상관없이 월드좌표로 앞으로 날라간다.

 AddForce(transform.foward)  오브젝트의 로테이션된 방향으로 날라간다. transform.foward 는 객체의 회전을 반영한 앞 방향을 나타닌다. 로컬좌표 앞방향이다.

 AddRelativeForce(new Vector3(0,0,1f)) 하면 오브젝트의 로테이션된 방향으로 날라간다.

 

아래 실험은 책에는 없는건데 개인적으로 해본거다 여러분은 하지 말아주길 바란다. 잘못하면 코드가 꼬인다.

 

 이걸 실험해 볼려고 코드를 한번 짜 봤다.

AddForceCtrl이라는 스크립트를 하나 만들어 하이라키의 Player에 적용한다. 

코드는 G:글로벌좌표 F:forward R:Relative 를 누르면 해당 함수가 실행된다. 이 함수는 BulletCtrl에 추가했다. 세 함수를 보면 인자가 조금씩 다르다.

 

AddForceCtrl.cs 스크립트 다음2개의 글로벌변수에 Bullet,Player오브젝트를 끌어다 연결해준다.

    public GameObject bullet;  //총알의 life를 참조하기 위함
    public Transform playerTr;  //player와 총알의 회전을 맞추기 위해 참조

GameManager에서 BulletCtrl의 메서드를 불러서 사용한다. 외부스크립트를 참조하기위해서는 Bullet을 인스펙터창에서 연결해준후 script컴포넌트의 참조를 참조변수에 지정해서 사용하면 된다. 스크립트의 참조는 GameObject가 아닌 스크립트이름이다. 스크립트 이름으로 class가 생성되기 때문이다.

 

  public GameObject bullet;  //인스펙터창에서 연결해야함.

 private BulletCtrl sc;   //BulletCtrl 스크립트 클래스의 참조
    void Start()
    {
        sc = bullet.GetComponent<BulletCtrl>();  //BulletCtrl 스크립트클래스참조를 받아온다
    }

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class AddForceCtrl : MonoBehaviour
{
    public GameObject bullet;  //총알의 life를 참조하기 위함
    public Transform playerTr;  //player와 총알의 회전을 맞추기 위해 참조
    private BulletCtrl sc;   //BulletCtrl 스크립트 클래스의 참조
    void Start()
    {
        sc = bullet.GetComponent<BulletCtrl>();  //BulletCtrl 스크립트클래스참조를 받아온다
    }

    // Update is called once per frame
    void Update() {
        if (sc.GetLife() == false) {
            if (Input.GetKeyDown(KeyCode.G)) {
                sc.AddForceG(playerTr);
                Debug.Log("g");
            }
            if (Input.GetKeyDown("f")) {
                sc.AddForceF(playerTr);
                Debug.Log("F");
            }
            if (Input.GetKeyDown("r")) {
                sc.AddForceR(playerTr);
                Debug.Log("R");
            }
        } 
    }
}

 

Bullet에 적용된 BulletCtrl.cs 

 인스펙터에서 public Transform rifleTr; 에 하이라키뷰의 Player의 라이플을 끌어다 놔준다.  Bullet을 총 앞쪽에 놓을려고 총의 위치를 참조하려고 한다. 

총알의 발사속도를 빠르게 하기위해 public float force = 1500.0f;로 수정해 힘을 쎄게한다.

 

GameManager에서 사용한 3개의 함수가 추가되었다. Addforce의 인자가 조금씩 다르다.

 rb.AddForce(Vector3.forward*force);   //월드좌표로 앞으로만 날라간다.

 rb.AddRelativeForce(Vector3.forward * force);   //똑같은 Vector3.forward인데 로컬좌표로 앞으로 날라간다

 rb.AddForce(transform.forward * force);   //로컬좌표로 앞으로 날라간다

 

Update()함수 안을 보면

총알이 발사되는 life=true로 설정해 관리한다.

총알이 발사된시간을 lastTime에 저장해 1초가 지나면 false로 해준고 

 

코드는 총알의 생명 life를 참조해 2가지 처리를 하는데

총알이 발사되고 1초가 지나면 속도를 0으로 하고 .

        if(life && (Time.time > lastTime + 1f)) {
            rb.velocity = Vector3.zero;
            lastTime = Time.time;  // lastTime을 갱신해준다.
            life = false;
        } 

life가 flase라면 위치와 방향을 총을 참조해 초기화 한다

else if(!life){
            transform.position = rifleTr.position + rifleTr.forward +rifleTr.up * 0.3f;
            transform.rotation = rifleTr.rotation;
        }

그리고 bullet에 Rigidbody컴포넌트가 추가되어있어 FixedUpdate()를 사용했는데 실행시 버벅거려서 LateUpdate()를 사용하니 해결되었다 FollowCam도 LateUpdate()를 사용하는 문제가 되는지는 잘 모르겠다.

버벅거리는 이유는 아마 .Bullet이 rifle(총)의 position과 rotation을 참조하는데 이게 같은 Update()에서 이루어지기 때문에 우선순위가 없어 생기는 일인것 같다. 따라서 LateUpdate()로 Player처리후 맨 마지막에 하니 문제가 해결되는것 같다. 

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using static Unity.VisualScripting.Member;

public class BulletCtrl : MonoBehaviour
{
    public Transform rifleTr;
    public float damage = 20.0f;
    public float force = 1500.0f;  //빠르게 하기위해
    private bool life = false;
    private Rigidbody rb;
    private float lastTime=0f;
    void Start()
    {
        transform.position = rifleTr.position;
        rb = GetComponent<Rigidbody>();  
    }
    private void LateUpdate() {
        if(life && (Time.time > lastTime + 1f)) {
            rb.velocity = Vector3.zero;
            lastTime = Time.time;
            life = false;
        } else if(!life){
            transform.position = rifleTr.position + rifleTr.forward +rifleTr.up * 0.3f;
            transform.rotation = rifleTr.rotation;
        }
    }

    public void AddForceG(Transform tr) {
        rb.AddForce(Vector3.forward*force);
        life = true;
        lastTime = Time.time;
        Debug.Log("g");
    }
    public void AddForceF(Transform tr) {
        rb.AddForce(transform.forward * force);
        life = true;
        lastTime = Time.time;
    }
    public void AddForceR(Transform tr) {
        rb.AddRelativeForce(Vector3.forward * force);
        life = true;
        lastTime = Time.time;
    }
    public bool GetLife() {
        return life;
    }
}

전망을 좋게하기위해 FollowCam.cs의 public float height = 3.0f;로 조금 높혀준다.

실행화면이다.

G, F ,R을 각각 2번씩 눌러 총알을 발사했다.

G를 누르면 캐릭터방향과 상관없이 앞으로만 발사된다.

F와R를 누르면 캐릭터의 방향을 따라 발사된다.

실험때문에 좀 스크립트가 복잡하게 되었다. ㅠㅠ

앞에서는 게임오브젝트의 이동을 Transform을 이용했는데 이제 물리엔진의 힘을 이용해 발사해보자. 그러기 위해서는 Ridigbody 컴포넌트가 필요하다. 

하이라키의 Bullet오브젝트를 선택한후 인스펙터뷰에서 Ridigbody컴포넌트를 추가한다.

실행해보면 총알이 바닥으로 떨어지는데 Rigidbody 컴포넌트의 Use Gravity속성이 체크되어 중력의 영향을 받았기 때문이다. 이속성을 언체크해서 안 떨어지게 한다.

Rigidbody는 물리학 용어의 강체를 의미한다. 충돌감지및 물리 시뮬레이션을 위한 중요한 컴포넌트다. 아래 내용은 지금은 넘어가도 되지만 꼭 한번 읽어보시기 바랍니다. Rigidbody는 Transform에 영향을 주므로 둘을 같이 제어하면 안됩니다.

 

리지드바디 (유니티매뉴얼 2021.3)

Rigidbody  GameObject 가 물리 제어로 동작하게 합니다. 리지드바디는 힘과 토크를 받아 오브젝트가 사실적으로 움직이도록 해줍니다. 리지드바디가 포함된 모든 게임 오브젝트는 중력의 영향을 받아야 하며 스크립팅을 통해 가해진 힘으로 움직이거나 NVIDIA PhysX 물리 엔진을 통해 다른 오브젝트와 상호 작용해야 합니다.

프로퍼티

프로퍼티:기능:

Mass 오브젝트의 질량입니다(디폴트값은 킬로그램).
Drag 오브젝트가 힘에 의해 움직일 때 공기 저항이 영향을 미치는 정도를 나타냅니다. 0이면 공기 저항이 없으며 무한대라면 오브젝트가 즉시 정지합니다.
Angular Drag 오브젝트가 토크로 회전할 때 공기 저항이 영향을 미치는 정도를 나타냅니다. 0이면 공기 저항이 없습니다. 앵글 드래그를 무한대로 설정한다고 해서 오브젝트의 회전이 멈추지는 않으니 주의해야 합니다.
Use Gravity 활성화되면 오브젝트는 중력의 영향을 받습니다.
Is Kinematic 활성화되면 오브젝트는 물리 엔진으로 제어되지 않고 오로지 Transform 으로만 조작됩니다. 플랫폼을 옮기는 경우나 HingeJoint 가 추가된 리지드바디를 애니메이션화하는 경우에 유용합니다.
Interpolate 리지드바디의 움직임이 어색해 보일 경우 다음 옵션 중에서 하나를 시도해보아야 합니다.
- None 보간이 적용되지 않습니다.
- Interpolate 이전 프레임의 트랜스폼에 맞게 움직임을 부드럽게 처리합니다.
- Extrapolate 다음 프레임의 트랜스폼을 추정해 움직임을 부드럽게 처리합니다.
Collision Detection 빠르게 움직이는 오브젝트가 충돌의 감지 없이 다른 오브젝트를 지나쳐가는 것을 방지합니다.
- Discrete 씬에서 다른 모든 콜라이더에 대해 불연속 충돌 검사를 사용합니다. 다른 콜라이더는 충돌 테스트에 불연속 충돌 검사를 사용합니다. 일반적인 충돌에 활용됩니다(기본값).
- Continuous (리지드바디가 있는) 동적 콜라이더에 불연속 충돌 검사를, (리지드바디가 없는) 정적 콜라이더에 스위핑 기반 연속 충돌 검사를 사용합니다. Continuous Dynamic__으로 설정된 리지드바디는 이 리지드바디와의 충돌을 테스트할 때 연속 충돌 검사를 사용합니다. 다른 리지드바디는 불연속 충돌 검사를 합니다. Continuous Dynamic__ 검사로 충돌해야 하는 오브젝트에 사용됩니다(물리 퍼포먼스에 큰 영향을 미치므로 빠른 오브젝트의 충돌 문제가 없다면 Discrete__으로 설정합니다). 
Continuous 및 Continuous Dynamic 충돌로 설정된 게임 오브젝트에 스위핑 기반 연속 충돌 검사를 사용합니다. 또한 (리지드바디가 없는) 정적 콜라이더에도 연속 충돌 검사를 사용합니다. 그 외 다른 콜라이더에는 불연속 충돌 검사를 사용합니다. 빠르게 움직이는 오브젝트에 쓰입니다.
- Continuous Speculative 리지드바디와 콜라이더에 추측성 연속 충돌 검사를 사용합니다. 키네마틱 바디를 설정할 수 있는 유일한 CCD 모드입니다. 이 메서드는 스위핑 기반 연속 충돌 검사보다 리소스를 덜 소모합니다.
Constraints 리지드바디의 움직임에 대한 제약사항입니다.-
- Freeze Position 월드 좌표계의 X, Y, Z 축에서 이동하는 리지드바디를 선택적으로 중지시킵니다.
- Freeze Rotation 로컬 좌표계의 X, Y, Z 축에서 회전하는 리지드바디를 선택적으로 중지시킵니다.

세부 정보

리지드바디를 사용하면 게임 오브젝트의 동작이 물리 엔진으로 제어됩니다. 따라서 사실적인 충돌과 다양한 조인트 타입 같은 동작이 가능해집니다. 리지드바디에 힘을 더해서 게임 오브젝트를 조작하면 Transform 컴포넌트 를 직접 조정할 때와 느낌과 모양이 상당히 다릅니다. 일반적으로 동일한 게임 오브젝트의 리지드바디와 트랜스폼을 모두 조작하지 않고 둘 중 하나만 조작해야 합니다.

트랜스폼 조작과 리지드바디 조작의 가장 큰 차이점은 힘의 사용입니다. 리지드바디는 힘과 토크를 받을 수 있지만 트랜스폼은 그렇지 않습니다. 트랜스폼도 트랜스폼되고 회전할 수는 있지만 물리를 사용할 때와는 다릅니다. 리지드바디에 힘/토크를 더하면 오브젝트 Transform 컴포넌트의 포지션과 회전을 바꿉니다. 그러므로 둘 중에서 하나만을 사용해야 하는 것입니다. 트랜스폼을 바꾸면서 물리를 사용하면 충돌 및 기타 연산에 문제가 발생할 수 있습니다.

리지드바디는 물리 엔진의 영향을 받기 전에 게임 오브젝트에 명시적으로 추가되어야 합니다. 메뉴의 Components->Physics->Rigidbody 에서 선택한 오브젝트에 리지드바디를 추가할 수 있습니다. 이제 오브젝트에 물리 연산이 일어날 수 있게 된 것입니다. 중력의 영향을 받고 스크립팅을 통해 힘을 받을 수 있지만 원하는 대로 정확하게 동작시키려면 Collider 나 조인트를 추가해야 합니다.

부모 설정

오브젝트가 물리 제어를 받으면 트랜스폼 부모가 움직이는 방식에 의존하게 됩니다. 부모를 움직이면 리지드바디의 자식이 같이 따라갑니다. 하지만 리지드바디는 여전히 중력때문에 떨어지고 충돌 이벤트에 반응합니다.

스크립트

리지드바디를 제어하기 위해 기본적으로 스크립트를 사용하여 힘이나 토크를 추가합니다. 이 작업은 AddForce()  AddTorque() 를 리지드바디의 오브젝트에 호출해야 합니다. 물리를 사용할 때는 오브젝트의 트랜스폼을 직접 변경하면 안 된다는 사실을 기억해야 합니다.

애니메이션(Animation)

래그돌 효과를 만드는 등 일부 상황에서는 애니메이션과 물리 간 오브젝트의 제어를 전환해야 합니다. 이를 위해 리지드바디는 isKinematic 으로 지정될 수 있습니다. 리지드바디가 isKinematic 으로 정해진 경우 충돌, 힘이나 어떤 물리 연산에도 영향을 받지 않습니다. 이 경우 트랜스폼 컴포넌트를 직접 조작해서 오브젝트를 제어할 수밖에 없습니다. 키네마틱 리지드바디는 다른 오브젝트에 영향을 주지만 그들 자체는 물리의 영향을 받지 않습니다. 예를 들어, 키네마틱 오브젝트에 추가된 조인트는 이에 추가된 다른 리지드바디를 제한하지만 키네마틱 리지드바디는 충돌을 통해 다른 리지드바디에 영향을 미칩니다.

콜라이더(Colliders)

콜라이더는 충돌이 일어나게 만들기 위해 리지드바디에 함께 추가해야 하는 또 다른 유형의 컴포넌트입니다. 두 개의 리지드바디가 서로 충돌하더라도 두 오브젝트 모두 콜라이더가 추가되어 있지 않으면 물리 엔진은 충돌을 연산하지 않습니다. 콜라이더가 없는 리지드바디는 물리 시뮬레이션 동안 서로를 지나쳐가기만 합니다.

콜라이더는 리지드바디의 물리적 경계를 정의합니다.

Component->Physics 메뉴에서 콜라이더를 추가합니다. 보다 자세한 내용은 각 콜라이더의 컴포넌트 레퍼런스 페이지를 참조하십시오.

복합 콜라이더

복합 콜라이더는 기본 콜라이더의 조합으로, 단일 리지드바디처럼 함께 움직입니다. 정확하게 시뮬레이션하기에는 성능 측면에서 너무 복잡하거나 비용이 큰 모델을 가지고 있어 간단한 근사값을 사용해 최적의 방법으로 셰이프 충돌을 시뮬레이션할 때 유용합니다. 컴파운드 콜라이더를 생성하기 위해서는 충돌 오브젝트의 자식 오브젝트를 만든 후 Collider 컴포넌트를 각각의 자식 오브젝트에 추가합니다. 이렇게 하면 각 콜라이더의 포지션, 회전, 스케일을 손쉽고 개별적으로 수행할 수 있습니다. 자체적인 컴파운드 콜라이더를 여러 기본 콜라이더 및/또는 볼록한 메시 콜라이더로 빌드할 수 있습니다.

실제 월드 복합 콜라이더 설정

위의 그림에서 총 모델 게임 오브젝트에는 리지드바디가 추가되어 있고 여러 기본 콜라이더가 자식 게임 오브젝트로 있습니다. 힘에 의해 부모 리지드바디가 이동하면 자식 콜라이더도 따라서 이동합니다. 프리미티브 콜라이더는 환경에서 메시 콜라이더와 충돌하며, 부모 리지드바디는 자식 콜라이더가 다른 콜라이더와 씬에서 상호작용하는 방식을 기반으로 이동 방식을 변경합니다.

일반적으로 메시 콜라이더는 서로 충돌하지 않습니다. 메시 콜라이더가 Convex 로 지정되면 다른 메시 콜라이더와 충돌할 수 있습니다. 일반적인 해결책은 움직이는 모든 오브젝트에는 기본 콜라이더를 사용하고 정적 배경 오브젝트에는 메시 콜라이더를 쓰는 것입니다.

참고: 충돌 콜백을 사용하는 경우 복합 콜라이더는 각 콜라이더 충돌 페어에 대한 개별 콜백을 반환합니다.

연속 충돌 검사(Collision Detection)

연속 충돌 검사는 빠르게 움직이는 콜라이더가 서로 통과하는 것을 방지하는 기능입니다. 일반적인(Discrete) 충돌 검사를 사용할 때 이런 일이 발생할 수 있는데 한 프레임에서 오브젝트 하나가 콜라이더의 이쪽 편에 있고 다음 프레임에서 이미 콜라이더를 지나쳐버리는 것입니다. 빨리 움직이는 오브젝트의 리지드바디에서 연속 충돌 검사를 활성화하면 문제를 해결할 수 있습니다. 충돌 검사 모드를 Continuous 로 설정해 리지드바디가 다른 정적 (즉, 리지드바디가 아닌)메시 콜라이더를 통과하지 않게 합니다. Continuous Dynamic 으로 설정해도 리지드바디가 Continuous 또는 Continuous Dynamic 으로 충돌 검사 모드가 설정된 리지드바디를 통과하지 않습니다. 연속 충돌 검사는 박스-, 구체- 및 캡슐 콜라이더 용으로 지원됩니다. 연속 충돌 검사는 오브젝트가 서로 통과하는 경우에 충돌을 잡기 위한 안전망 역할을 하나 물리적으로 정확한 충돌 결과를 내지는 않습니다. 그러므로 빠르게 움직이는 오브젝트와 관련해 문제가 생기면 TimeManager 인스펙터의 고정 타임 스텝 값을 줄여 보다 정확한 시뮬레이션을 구현하는 방안을 고려할 수 있습니다.

올바른 크기의 사용

게임 오브젝트의 메시 크기는 리지드바디의 질량보다 훨씬 중요합니다. 리지드바디가 천천히 움직이거나, 떠 있거나, 제대로 충돌하지 않는 등 예상한 대로 동작하지 않으면 메시 에셋의 스케일을 조정해 보아야 합니다. Unity의 기본 단위 스케일은 1 unit = 1 미터이므로 임포트한 메시의 스케일은 유지되며 물리 연산에 적용됩니다. 예를 들어 무너지고 있는 고층 빌딩은 장난감 블록으로 쌓아 올린 탑과는 붕괴되는 양상이 상당히 다를 것입니다. 그러므로 다른 크기의 오브젝트를 정확한 스케일로 모델링해야 합니다.

사람을 모델링한다면 Unity에서 모델의 신장이 약 2미터인지 확인해야 합니다. 오브젝트가 올바른 크기인지 여부를 확인하려면 기본 큐브와 비교해봅니다. 큐브는 GameObject > 3D Object > Cube 로 생성할 수 있습니다. 큐브의 높이는 정확히 1미터이기 때문에 사람은 큐브보다 두 배 정도 커야 합니다.

메시 자체를 조정할 수 없다면 Project View 를 선택하고 메뉴에서 Assets->Import Settings… 에서 특정 메시 에셋의 균등한 스케일을 바꿀 수 있습니다. 여기에서는 스케일을 변경하고 메시를 다시 임포트할 수 있습니다.

게임 오브젝트를 다른 스케일로 인스턴스화할 필요가 있는 경우 트랜스폼의 스케일 축 값을 조정해도 됩니다. 이 때 단점은 오브젝트가 인스턴스화될 때 물리 시뮬레이션이 더 많은 작업을 해서 게임 퍼포먼스를 저하시킬 수 있다는 점입니다. 상당한 손실까지는 아니지만 다른 두 가지 옵션으로 스케일을 마무리하는 것만큼 효율적이지는 않습니다. 또한 불균등한 스케일은 부모가 사용될 때 원하지 않는 동작을 수행하게 할 수 있습니다. 그렇기 때문에 언제나 오브젝트를 모델링 애플리케이션에 맞게 정확한 스케일로 생성하는 것이 좋습니다.

힌트

  • 두 리지드바디의 상대 Mass 는 둘이 서로 충돌했을 때 어떻게 반응할지를 결정합니다.
  • 한 리지드바디의 Mass 를 다른 리지드바디보다 크게 만들어도 자유 낙하 시 더 빨리 낙하하지 않습니다. 이 경우 Drag 를 사용해야 합니다.
  • Drag 값이 작으면 오브젝트가 무거워 보이고 값이 크면 가벼워 보입니다. Drag 의 일반적인 값은 .001(단단한 금속 덩어리)과 10(깃털) 사이입니다.
  • 오브젝트의 Transform 컴포넌트를 직접 조작하면서도 물리 연산이 필요하다면 리지드바디를 추가하고 키네마틱으로 만듭니다.
  • Transform 컴포넌트를 통해 게임 오브젝트를 이동시키면서도 충돌/트리거 메시지를 받고자 한다면 이동하는 오브젝트에 리지드바디를 추가해야 합니다.
  • 앵글 드래그를 무한대로 설정하는 것 만으로는 오브젝트의 회전을 멈추게할 수는 없습니다.

+ Recent posts