Log Stash

as an Industrial Personnel

프로그래밍/삽질

위도 경도에 맞춰서 지구본 돌리기 - unity

SavvyTuna 2015. 2. 12. 14:40


유니티에서 지구 텍스쳐가 씌워진 스피어를 주어진 위도 경도를 찾아가도록 회전시켜야 할 일이 있었다. 일단 옛날에 플레이하던 록맨x7 스테이지 셀렉트 장면 가운데에 지구굴러가는 모습이 떠올랐다.


록맨X7의 스테이지 셀렉트씬, 1분17초 부터 지구가 굴러간다.


어차피 2D처럼 화면에 그려지느라 카메라 위치는 고정되어있고, 다른 요인으로 회전값이 바뀌거나 하지 않으니 (뷰 스페이스에서 간섭이 없으니) 그냥 월드 스페이스에서 회전시키기만 하면 됐었다.


그러면 위도, 경도를 이용해서 회전각을 알아내야 하는데, 이것도 중간에 삽질을 많이 해서 그렇지 그렇게 어렵진 않다. 위도, 경도는 구면 좌표계상의 한 점을 표현할때 쓰는 값이니 (고도는 그냥 1로 퉁친다) 이걸 변환 공식을 통해 직교 좌표계로 바꿀 수 있다. 처음 위도, 경도(0, 0)[각주:1] 의 직교 좌표계 값과 목표 지점의 직교 좌표계값을 보간해서 위도, 경도 각각의 회전각을 구하고, 순서에 맞게 곱해준다. (아래 코드의 Update 루틴에서 이걸 한다)



이런식으로 굴러간다.


아래는 전체코드 (나중엔 요구조건이 추가되어서 다른 코드들도 많이 들어갔다. 이건 예전 리비전 찾아서 써놓은거.. 후새드)

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

public class RotateEarth : MonoBehaviour
{
	private Vector3 initialCoord3D = new Vector3(0, 0, 1);
	
	private Vector2 lastCoord = Vector2.zero;
	private Vector2 initialCoord = Vector2.zero;
	
	private Quaternion initialRotation;
	
	public const float ANIMATION_TIME = 3.0f;
	
	private float elapsedTime = 0;
	private Interpolate.Function EasingFunction; 
	
	void Start()
	{
		initialRotation = this.transform.rotation;
		EasingFunction = Interpolate.Ease(Interpolate.EaseType.EaseInOutQuad);

		StartRotation (37, 127);
	}
	
	void Update()
	{
		elapsedTime += Time.deltaTime;
		float interpolatedWeight = EasingFunction(0, 1, elapsedTime, ANIMATION_TIME);
		
		Vector2 targetCoord = Vector2.Lerp(initialCoord, lastCoord, interpolatedWeight);
		
		Vector3 targetCoordLong = this.LatLongToVector3(initialCoord.x, targetCoord.y); // y is long
		Vector3 targetCoordLat = this.LatLongToVector3(targetCoord.x, initialCoord.y);  // x is lat
		
		Quaternion rotateLong = Quaternion.FromToRotation(initialCoord3D, targetCoordLong);
		Quaternion rotateLat = Quaternion.FromToRotation(initialCoord3D, targetCoordLat);
		
		// 곱셈 순서 조심
		this.transform.rotation = rotateLat * rotateLong * initialRotation;
	}
	
	public void StartRotation(double lat, double lng)
	{
		elapsedTime = 0;
		lastCoord = new Vector2((float)lat, (float)lng);
		Debug.Log("" + lat + ", " + lng);
	}
	
	private Vector3 LatLongToVector3(double lat, double lon)
	{
		double phi = (lat) * Math.PI / 180;
		double theta = (lon - 180) * Math.PI / 180;
		
		double x = -1 * Math.Cos(phi) * Math.Cos(theta);
		double y = Math.Sin(phi);
		double z = Math.Cos(phi) * Math.Sin(theta);
		
		// set to UNITY3D's axis system
		return new Vector3((float)-z, (float)y, (float)x);
	}
}

구면 보간이 아니라 최단 거리는 아니지만.. 그래도 그림이 그럭저럭 나오니까 패스.


변환이나 공식 자체는 쉽지만, 쿼터니온 곱셈 성질을 잘못 이해하고 삽질을 많이 해서 써놓음.



  1. 위도, 경도가 0,0이 될 수 있도록 게임 오브젝트를 미리 회전시켜 놓는다. [본문으로]