Humility

아무리 노력해도 최고가 되지 못할 수 있다⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀그럼에도 노력하는자가 가장 겸손한 것 아닌가

공부하는 블로그

유니티/기능구현

[Unity] 유니티 C#) 투척무기 구현을 어떻게 할까? ( 궤적 표시 )

새벽_글쓴이 2024. 8. 10. 23:50
반응형

지난번에 이어서 투척무기 구현을 마무리 하려고 한다.

 

우선 마우스 왼쪽키를 누르고 있을 때, 궤적 표시를 해주자.

궤적을 표시해주는 것은 3가지의 투척무기가 모두 공통적일 것이므로

3가지의 부모가 되는 ThrowingWeapon 클래스에서 정의해주자

 

1. 우선 궤도에 관한 변수 설정과 라인 렌더러를 설정해보자

public abstract class ThrowingWeapon : MonoBehaviour
{
    public float throwForce = 10f;              // 던지는 힘
    public LineRenderer trajectoryLine;         // 궤적 라인
    int trajectoryLinePoint = 40;               // 궤적 포인트 갯수

    protected virtual void Awake()
    {
        trajectoryLine = GetComponent<LineRenderer>();
        trajectoryLine.enabled = false;
    }

    // 라인 렌더러 설정
    void SetupTrajectoryLine()
    {
        trajectoryLine.startWidth = 0.1f;
        trajectoryLine.endWidth = 0.1f;
        trajectoryLine.startColor = Color.red;
        trajectoryLine.endColor = Color.red;
    }
}

 

 

라인렌더러와 관련된 변수로는 2가지를 추가로 설정해줬다. 

라인렌더러 컴퍼넌트를 가져올 변수와 궤적의 찍힐 포인트 갯수이다

public LineRenderer trajectoryLine;         // 궤적 라인
int trajectoryLinePoint = 40;               // 궤적 포인트 갯수

 

 

Awake()에서 enabled를 false로 해서 라인을 꺼주자.

초반에 꺼주지 않으면 수류탄에서 라인이 하나 그려질 것이다

protected virtual void Awake()
    {
        trajectoryLine = GetComponent<LineRenderer>();
        trajectoryLine.enabled = false;
    }

 

 

그 후엔 라인렌더러 설정을 해줬다

나는 간단하게 라인의 처음과 끝이 넓이를 0.1로 만들어줬지만

더욱 다양하고 멋진 라인을 만들고 싶다면 아래 프로퍼티 기능을 이용해 변경해주면 된다

void SetupTrajectoryLine()
{
    trajectoryLine.startWidth = 0.1f;
    trajectoryLine.endWidth = 0.1f;
    trajectoryLine.startColor = Color.red;
    trajectoryLine.endColor = Color.red;
}

 

라인 설정

프로퍼티기능

Loop 이 설정을 활성화하면 라인의 처음과 끝 포지션을 연결하여 닫힌 루프를 만듭니다.
Positions 연결할 Vector3 포인트 배열입니다.
Width 길이를 따라 위치한 라인의 너비를 조절하려면 이 프로퍼티에서 너비 값 및 커브 값을 지정하면 됩니다.

커브는 각각의 버텍스에서 샘플링되므로 정확도는 라인 내 존재하는 버텍스 수로 제한됩니다. 라인의 전반적인 너비는 너비 값으로 조정할 수 있습니다.
Color 그레디언트를 지정하여 길이를 따라 라인의 색상을 조정합니다.

Unity는 각 버텍스에서 컬러 그레디언트의 컬러를 샘플링하고, 각 버텍스 사이의 컬러에 선형 보간을 적용합니다. 라인에 더 많은 버텍스를 추가하면 더욱 세밀한 그레디언트를 구현할 수 있습니다.
Corner Vertices 이 프로퍼티는 라인에 코너를 그리는 경우, 얼마나 많은 추가 버텍스가 사용되는지 지정합니다. 이 값을 늘리면 라인 코너가 더 둥글게 보입니다.
End Cap Vertices 이 프로퍼티는 라인에 끝부분을 그리는 경우, 얼마나 많은 추가 버텍스가 사용되는지 지정합니다. 이 값을 늘리면 라인 끝부분이 더 둥글게 보입니다.
Alignment 라인이 향하는 방향을 설정합니다.
  View 라인이 카메라를 향합니다.
  TransformZ 라인이 Transform 컴포넌트의 Z축을 향합니다.
Texture Mode 텍스처가 라인에 적용되는 방식을 제어합니다.
  Stretch 라인의 전체 길이를 따라 텍스처를 한 번 매핑합니다.
  Tile 월드 공간 단위의 길이에 기반하여 라인을 따라 텍스처를 반복합니다. 타일링 속도를 설정하려면 Material.SetTextureScale을 사용하십시오.
  DistributePerSegment 모든 버텍스의 간격이 균등하다는 가정하에 라인의 전체 길이를 따라 텍스처를 한 번 매핑합니다.
  RepeatPerSegment 라인을 따라 텍스처를 반복합니다. 반복 속도는 라인 세그먼트당 한 번입니다. 타일링 속도를 조정하려면 Material.SetTextureScale을 사용하십시오.
Shadow Bias 빌보드 지오메트리로 영역을 대략적으로 만들어서 발생한 그림자 결함을 제거하기 위해 광원에서 그림자를 움직일 정도를 설정합니다.
Generate Lighting Data 활성화하면 Unity가 포함된 노멀과 탄젠트를 사용하여 라인 지오메트리를 빌드합니다. 이를 통해 씬 조명이 사용하는 머티리얼을 사용할 수 있습니다.
Use World Space 활성화하면 포인트가 월드 공간 좌표로 간주됩니다. 비활성화하면 이 컴포넌트가 연결된 게임 오브젝트의 트랜스폼에 속합니다.

 

출처 : 유니티 - 매뉴얼 ( https://docs.unity3d.com/kr/2021.2/Manual/class-LineRenderer.html )

 

스크립트 내에서 코드를 작성하다보면

startWidth, endWidth 이 아니라 SetWidth 이라는 메서드가 나올 것이다

시작과 끝을 따로 정하지 않고 한번에 정할 수가 있지만 이는 권장하지 않는다

 

이유는 몇가지가 있는데

 

1. 호환성

  • Set 기능은 이전 버전에서 사용 되던 방식이기 때문이다
  • 그렇기 때문에, 이전 버전의 유니티를 사용하거나 레거시 코드와의 호환성이 필요한 경우에만 사용한다

2. 유연성

  • 선의 시작과 끝에 다른 값으로 설정을 할 수가 있기 때문에 보다 유연하다
  • 예시: 시작은 빨간색 끝은 파랑색으로 그라데이션 효과가 있는 선을 만들 수 있다

3. 성능

  • 아무래도 Set 기능은 이전 버전에서 사용되었다 보니 최신 방식이 일반적으로 더 최적화가 되어있다

결론적으로, 현재 개발중인 Unity의 버전이 이전이라면 Set. 최신이라면 현재 권장되는 방식을 사용하는게 좋다


2. 라인을 세팅이 끝났으니 이제 라인을 그려보자.

먼저 위에서 설정 해줬던 라인렌더러의 세팅을 가져오고

라인을 켜주자

 

일단 코드는 아래와 같다 ( _firePos는 메인카메라 값 )

// 궤적 업데이트
public void UpdateTrajectory(Transform _firePos)
{
    SetupTrajectoryLine();
    
    Vector3 _velocity = _firePos.forward * throwForce;
    Vector3 _localStartPos = new Vector3(0, 0.5f, 1f);      
    Vector3 _position = _firePos.TransformPoint(_localStartPos);

    // 라인렌더러 점 카운트
    trajectoryLine.positionCount = trajectoryLinePoint;
    // 등가속도 운동 값
    float _timeStpe = 1f/15f;

    // 속도와 중력에 의해 변화를 계산해서 점 찍기
    for (int i = 0; i < trajectoryLinePoint; i++)
    {
        trajectoryLine.SetPosition(i, _position);
        _velocity += Physics.gravity * _timeStpe;
        _position += _velocity * _timeStpe;
    }
    
    trajectoryLine.enabled = true; // 궤적 활성화
}

 

 

그리고 수류탄은 등가속도 운동의 원리. 즉 포물선 형태로 날아가야 하는 것을 알고 있을 것이다.

 

등가속도 운동의 원리란?

가속도가 일정하게 유지되는 운동이다

이는 투척 물체의 궤적 계산, 점프 메커니즘, 간단한 차량 물리 등에서 사용된다

 

주요 수식:

v: 최종 속도, v₀: 초기 속도, a: 가속도, t: 시간, x: 위치, x₀: 초기 위치

  • v = v₀ + at
  • x = x₀ + v₀t + ½at²
  • v² = v₀² + 2a(x - x₀)

주요 특성:

속도는 시간에 따라 선형적으로 변한다

위치는 시간에 따라 2차 함수(포물선)형태로 변한다

 

이런 원리가 있다는걸 알고 다시 코드를 봐보자

 

먼저 시작 속도와 시작 위치값을 구해주자

나는 시작 위치를 다르게 하고 싶었으므로 중간에 시작 위치값을 하나 만들어서 _position 값에 넣었다

    Vector3 _velocity = _firePos.forward * throwForce;
    Vector3 _localStartPos = new Vector3(0, 0.5f, 1f);
    Vector3 _position = _firePos.TransformPoint(_localStartPos);

 

 

그 다음 라인렌더러의 찍힐 점의 갯수를 대입하고

등가속도 운동 값을 만들어 for문을 돌렸다

혹시나 for문 안의 내용이 머리속에 그려지지 않는다면 바로 위에 그래프를 보면 이해가 될 것이다

// 라인렌더러 점 카운트
trajectoryLine.positionCount = trajectoryLinePoint;
// 등가속도 운동 값
float _timeStpe = 1f/15f;

// 속도와 중력에 의해 변화를 계산해서 점 찍기
for (int i = 0; i < trajectoryLinePoint; i++)
{
    trajectoryLine.SetPosition(i, _position);
    _velocity += Physics.gravity * _timeStpe;
    _position += _velocity * _timeStpe;
}
 
trajectoryLine.enabled = true; // 궤적 활성화

 

_timeStep은 궤적 계산에서 각 단계 사이시간 간격을 말하는 것이다 ( 그래프에서는 점이 찍히는 순간 들의 간격 )

 

특징 :

이 값을 줄이면 더 정확해지지만 계산량이 증가한다 ( 예: 1/30f )

이 값을 늘리면 성능은 향상되지만 정확도가 감소한다 ( 예: 1/5f )

 

값을 줄일경우 (1/30f로 만든 경우) 라인 렌더러의 길이도 짧아지는 현상을 볼 수 있을 것이다

 

이는 당연하지만 점이 찍히는 간격이 줄었기 때문이다

그렇기 때문에 단순하게 생각하면

처음에 trajectoryLinePoint 값늘려주면 된다

 

좀 더 동적으로 생각한다면

 

1. 시뮬레이션 시간 설정

  • 시간 간격에 관계없이 일정한 총 시간을 시뮬레이션합니다.
  • 점의 수를 시간 간격에 따라 동적으로 조정합니다.

2. 점의 수 증가

  • 시간 간격을 줄일 때 비례하여 점의 수를 증가시킵니다.

3. 거리 기반 종료 조건:

  • 최대 거리에 도달하면 시뮬레이션을 종료합니다.

이정도 방법이 있을 것 같다.

 

3. 궤적 부분까지 전체코드

public abstract class ThrowingWeapon : MonoBehaviour
{
    public float explosiondelay = 3f;       // 폭발 시간
    public float explosionRadius = 5f;      // 폭발 반경
    public float throwForce = 10f;          // 던지는 힘

    public LineRenderer trajectoryLine;     // 궤적 라인
    int trajectoryLinePoint = 40;           // 궤적 포인트 갯수

    protected  void Awake()
    {
        trajectoryLine = GetComponent<LineRenderer>();
        trajectoryLine.enabled = false;
    }

    // 라인 렌더러 설정
    void SetupTrajectoryLine()
    {
        trajectoryLine.startWidth = 0.1f;
        trajectoryLine.endWidth = 0.1f;
        trajectoryLine.startColor = Color.red;
        trajectoryLine.endColor = Color.red;
    }

    // 궤적 업데이트
    public void UpdateTrajectory(Transform _firePos)
    {
        SetupTrajectoryLine();
        
         Vector3 _velocity = _firePos.forward * throwForce;
        Vector3 _localStartPos = new Vector3(0, 0.5f, 1f);      
        Vector3 _position = _firePos.TransformPoint(_localStartPos);
        
        // 라인렌더러 점 카운트
        trajectoryLine.positionCount = trajectoryLinePoint;
         // 등가속도 운동 값
        float _timeStep = 1f/15f;

        // 속도와 중력에 의해 변화를 계산해서 점 찍기
        for (int i = 0; i < trajectoryLinePoint; i++)
        {
            trajectoryLine.SetPosition(i, _position);
            _velocity += Physics.gravity * _timeStep;
            _position += _velocity * _timeStep;
        }
        // 궤적 활성화
        trajectoryLine.enabled = true;
    }

    // 폭발
    public void Explosion()
    {
        StartCoroutine(Explode());
    }

    protected abstract IEnumerator Explode();

}
반응형