본문 바로가기
Unreal Engine4 or 5/코드

UE4 lerp를 이용한 자연스러운 회전과 angle 구하기

by 눈야옹 2016. 4. 20.
Step 1. Angle값 구하기.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    FVector Dest = FVector(GoalPosition.X, GoalPosition.Y, 0.0f);
    FVector Start = FVector(GetTransform().GetLocation().X, GetTransform().GetLocation().Y, 0.0f);
 
    FVector dir = Dest - Start;
 
    GoalDirection = dir.SafeNormal();
    //노말라이징한 두개의 백터를 dot한다. //여기서 축을 Z축으로 하기 위해 두백터의 Z값을 0.0f로 넣어 주었다.
    float dot = FVector::DotProduct(FVector::ForwardVector, GoalDirection);
    float AcosAngle = FMath::Acos(dot);    // dot한 값을 아크코사인 계산해 주면 0 ~ 180도 사이의 값 (0 ~ 1)의 양수 값만 나온다.
    float angle = FMath::RadiansToDegrees(AcosAngle); //그값은 degrees 값인데 이것에 1라디안을 곱해주면 60분법의 도가 나온다.
    
    //여기서 두 백터를 크로스 하여 회전할 축을 얻게 된다.
    //이 크로스 백터는 Axis회전의 회전축이 되며 , 그 양수 음수로 회전 방향 왼쪽(음수), 오른쪽(양수)를 알수 있다.
    FVector cross = FVector::CrossProduct(FVector::ForwardVector, GoalDirection); 
    FString lr = "center    //";
    if(cross.Z > 0)
    {
        lr = "Right    //";
 
        TurnAngle = angle;
    }
    else if (cross.Z < 0)
    {
        lr = "Left    //";
        //TurnAngle = 360 - angle; //360에서 뺴게되면 양수로 각을 리턴하게 된다.
        TurnAngle = -angle;
    }
 
    FString str = FString::Printf(TEXT("AcosAngle : %f, angle : %f //    "), AcosAngle, angle);
 
    //출력해보기
    GEngine->AddOnScreenDebugMessage(-1100.0f, FColor::Green, str+ lr + GoalDirection.ToString());
 
cs


Step 2. 구해진 Angle값 으로 회전하기.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
    //먼저 받아온 앵글 값을 절대값으로 바꾼다.
    float absTurnAngle = FMath::Abs(TurnAngle);
    // 총 회전할 시간 =  최종회전각 / 초당회전 스피드
    float RotationTime = absTurnAngle / TurnSpeedForDeltaTime;
    // 현재 회전 시간을 계속 더해준다.
    CurrentRotationTime += TurnDeltaTime;
    // 현재 lerp에 들어갈 알파값 = 현재 시간 / 총 회전할 시간
    float t = CurrentRotationTime / RotationTime;
 
    //1보다 작을 경우 아직 회전중이라는 의미이다.
    if (t < 1)
    {
        float angle = 0.0f;
        //받아온 angle값이 음수일 경우 왼쪽으로 회전 양수일경우 오른쪽으로 회전하기위해 구분하여 준다.
        if (TurnAngle < 0)
        {
            angle = -1 * (FMath::Lerp<floatfloat>(0, absTurnAngle, t)) ;
        }
        else if (TurnAngle > 0)
        {
            angle = FMath::Lerp<floatfloat>(0, absTurnAngle, t);
        }
        
        RootComponent->SetWorldRotation(FRotator(0.0f, angle, 0.0f).Quaternion());
        return false;
    }
    else
    {
        //시간이 1을 이상일 경우 각도를 그냥 넣어준다. 
        RootComponent->SetWorldRotation(FRotator(0.0f, TurnAngle, 0.0f).Quaternion());
        return true;
    }
cs

위 코드는 0도에서 X 도로 돌아가고 다시 0도에서 X도로 돌아가는 코드

아래 코드는 현재 가지고 있는 angle에서 회전해야할 만큼 값을 더해서 회전한다.

Step 1. Angle값 구하기.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
bool ARollingBall::IsTurnOk()
{
    //먼저 받아온 NextTurnAngle을 절대값으로 바꾼다.
    float absNextTurnAngle = FMath::Abs(NextTurnAngle);
    // 총 회전할 시간 =  최종회전각 / 초당회전 스피드
    float RotationTime = absNextTurnAngle / TurnSpeedForDeltaTime;
    // 현재 회전 시간을 계속 더해준다.
    CurrentRotationTime += TurnDeltaTime;
    // 현재 lerp에 들어갈 알파값 = 현재 시간 / 총 회전할 시간
    float t = CurrentRotationTime / RotationTime;
 
    //1보다 작을 경우 아직 회전중이라는 의미이다.
    if (t < 1)
    {
        // 이전에 즉 회전하기전 가지고 있는 Angle을 기본으로 두고 그값에 보간된 값을 더한다.
        float angle = PrevTurnAngle;
        //받아온 angle값이 음수일 경우 왼쪽으로 회전 양수일경우 오른쪽으로 회전하기위해 구분하여 준다.
        if (NextTurnAngle < 0)
        {
            angle += -1 * (FMath::Lerp<floatfloat>(0, absNextTurnAngle, t)) ;
        }
        else if (NextTurnAngle > 0)
        {
            angle += FMath::Lerp<floatfloat>(0, absNextTurnAngle, t);
        }
        
        RootComponent->SetWorldRotation(FRotator(0.0f, angle, 0.0f).Quaternion());
        return false;
    }
    else
    {
        //시간이 1을 이상일 경우 각도(기존에 가지고 있던값 + 회전 해야할 값)를 그냥 넣어준다. 
        RootComponent->SetWorldRotation(FRotator(0.0f, PrevTurnAngle + NextTurnAngle, 0.0f).Quaternion());
        return true;
    }
}
cs

Step 2. 회전하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void ARollingBall::SetNewGoalDirection()
{
    //일단 기존에 가지고 있던 Direction과 Angle을 이전 값으로 해준다. 
    PrevGoalDirection = CurrentGoalDirection;
    PrevTurnAngle = GetTransform().GetRotation().Euler().Z;
 
    //먼저 현재 지점에서부터 목표지점을 향한 Direction을 구한다.
    FVector Dest = FVector(GoalPosition.X, GoalPosition.Y, 0.0f);
    FVector Start = FVector(GetTransform().GetLocation().X, GetTransform().GetLocation().Y, 0.0f);
    FVector newDir = Dest - Start;
    CurrentGoalDirection = newDir.GetSafeNormal();
 
    //노말라이징한 두개의 백터를 dot한다. //여기서 축을 Z축으로 하기 위해 두백터의 Z값을 0.0f로 넣어 주었다.
    float dot = FVector::DotProduct(PrevGoalDirection, CurrentGoalDirection);
    float AcosAngle = FMath::Acos(dot);    // dot한 값을 아크코사인 계산해 주면 0 ~ 180도 사이의 값 (0 ~ 1)의 양수 값만 나온다.
    float angle = FMath::RadiansToDegrees(AcosAngle); //그값은 degrees 값인데 이것에 1라디안을 곱해주면 60분법의 도가 나온다.
    
    //여기서 두 백터를 크로스 하여 회전할 축을 얻게 된다.
    //이 크로스 백터는 Axis회전의 회전축이 되며 , 그 양수 음수로 회전 방향 왼쪽(음수), 오른쪽(양수)를 알수 있다.
    FVector cross = FVector::CrossProduct(PrevGoalDirection, CurrentGoalDirection);
    FString lr = "center    //";
    if(cross.Z > 0)
    {
        lr = "Right    //";
 
        NextTurnAngle = angle;
    }
    else if (cross.Z < 0)
    {
        lr = "Left    //";
        //NextTurnAngle = 360 - angle; //360에서 뺴게되면 양수로 각을 리턴하게 된다.
        NextTurnAngle = -angle;
    }
}
cs