데카르트 좌표에서의 이동을 구면좌표 이동으로 mapping 하였다. 


데카르트 좌표는 x,y축만 있는 좌표를 의미한다. (2차원 좌표이다. )

구면 좌표는 구체 표면을 좌표화한 것으로 구체 표면에서의 움직임을 위하여 사용한다. 

*여담으로 양자역학에서 전자의 운동을 계산하기 위해서 사용한다고 한다! 슈뢰딩거~ 




<실습>

첫번째로, 할 일은 x축 y축으로만 이동하는 특정 범위 안에 있는 컨트롤러를 만드는 것이다. 

위와 관련된 스크립트는 지난번 포스팅에 마우스를 따라서 이동하는 개체 코드를 보면 된다. 

코드에 대해서 간략하게 설명하자면 rayCast를 받아서 좌표에 찍는 방식을 사용하였다. 

x축 y축만 제어하기 위해서 position.z는 0으로 두었다. 


두번째로, 할 일은 이제 위의 좌표 값을 받아와서 구면좌표에서 움직이는 개체를 만드는 것이다. 

데카르트 좌표를 구면 좌표로 만들기 위해서는 우선, 지도에 대해서 생각해보아야 한다. 

지도는 위도, 경도를 가지고 있고 이 2차원 좌표는 구를 표현하는 것이다. 


위도 경도를 구면에 mapping하는 코드 관련해서는 아래 링크를 참고하였다.

https://github.com/mapbox/mapbox-unity-sdk/issues/238

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 private Vector3 CoordinateTrans(float Lon, float Lat)
    {
        Vector3 result;
        float ltR = Lat * Mathf.Deg2Rad;
        float lnR = Lon * Mathf.Deg2Rad;
 
        float xPos = (radius * transform.lossyScale.z) * Mathf.Cos(ltR) * Mathf.Cos(lnR);
        float zPos = (radius * transform.lossyScale.z) * Mathf.Cos(ltR) * Mathf.Sin(lnR);
        float yPos = (radius * transform.lossyScale.z) * Mathf.Sin(ltR);
        Vector3 earthPositon = transform.position;
        result = new Vector3(xPos , yPos , zPos);
 
        return result;
    }
cs


여기에서 한 가지 발생하는 문제점은 위에 x축, y축을 만들어 두었던 좌표가 

위도와 경도의 범위인 


-90 =< 위도 =< 90

-180 =< 경도 =< 180


가 아니라는 것이다. 


이 문제를 해결하기 위해서 remap을 위한 코드를 짰다. 

*processing에 있는 map()이라는 함수를 떠올리면 된다.

코드는 아래와 같다. 

1
2
3
4
5
6
private float remap(float aValue, float from1, float to1, float from2, float to2)
    {
        float normal = Mathf.InverseLerp(from1, to1, aValue);
        float bValue = Mathf.Lerp(from2, to2, normal);
        return bValue;
    }
cs


잘 작동된다. 


전체 코드 

* <Manual>

1. cardesianMove 스크립트는 데카르트 좌표계로 움직이는 개체의 스크립트이다. 


2. radius는 기준 구체의 반지름이다. (기준 구체는 태양계 기준으로 설명하자면, 태양이다. )


3. sphereSize는 움직이는 개체의 크기이다. (움직이는 개체는 태양계 기준으로 지구라고 생각하면 된다. )

*sphereSize를 pos에서 나누어주는 이유는 size에 따라서 이동해야되는 크기가 다르기 때문이다. 


4. latitude(위도)의 범위를 -180에서 180으로 둔 이유는 위도로 360도 회전을 하기 위해서이다. 


5. remap함수는 본인의 좌표에 따라서 변경하면 된다. 

remap(input값, 기존 최솟값, 기존 최댓값, 맵핑할 최솟값, 맵핑할 최댓값 )


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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class sphericalMove : MonoBehaviour {
 
    public CardesianMove carde; //다른 스크립트에서 2d 위치 벡터 받아옴 
    public float radius = 2.0f;
    public float sphereSize = 0.1f;
 
    public float X;
    public float Y;
    public float Longitude;
    public float Latitude;
 
    private Vector3 convertPos = Vector3.zero;
 
    void Start () {
        carde = FindObjectOfType<CardesianMove>();
    }
    
    void Update () {
 
        X = carde.transform.position.x ;
        Y = carde.transform.position.y ;
 
        //remap
        Longitude = remap(X, -22-180180);//longitude
        Latitude = remap(Y, -22-180180);//latitude
 
 
        Vector3 pos = CoordinateTrans(Longitude, Latitude);
        transform.position = pos/sphereSize;
    }
 
    //mapping
    private float remap(float aValue, float from1, float to1, float from2, float to2)
    {
        float normal = Mathf.InverseLerp(from1, to1, aValue);
        float bValue = Mathf.Lerp(from2, to2, normal);
        return bValue;
    }
 
    private Vector3 CoordinateTrans(float Lon, float Lat)
    {
        Vector3 result;
        float ltR = Lat * Mathf.Deg2Rad;
        float lnR = Lon * Mathf.Deg2Rad;
 
        float xPos = (radius * transform.lossyScale.z) * Mathf.Cos(ltR) * Mathf.Cos(lnR);
        float zPos = (radius * transform.lossyScale.z) * Mathf.Cos(ltR) * Mathf.Sin(lnR);
        float yPos = (radius * transform.lossyScale.z) * Mathf.Sin(ltR);
        Vector3 earthPositon = transform.position;
        result = new Vector3(xPos , yPos , zPos);
 
        return result;
    }
 
}
 
cs


Posted by 도이(doi)
,

구면좌표계 개념 관련

http://underwatch.tistory.com/47


구면좌표로 이동하는 개체와 총알 스크립트

https://answers.unity.com/questions/41050/how-can-i-make-movement-on-a-sphere.html


Posted by 도이(doi)
,

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
37
38
39
40
41
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class CardesianMove : MonoBehaviour {
 
    public Vector3 position;
 
    private RaycastHit hit;
    private Vector3 movePos;
 
    private void Start()
    {
        movePos = transform.position;
    }
    
    private void Update()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit, 10000))
        {
            movePos = hit.point;
        }
 
        //movePos로 이동하는 벡터의 절대값이 
        if ((movePos - transform.position).sqrMagnitude >= 0.3f * 0.3f)
        {
            Vector3 lookDir = hit.point - transform.position;
            lookDir.z = 0;
            transform.rotation = Quaternion.LookRotation(lookDir);
            transform.Translate(Vector3.forward * Time.deltaTime * 10.0f);
 
            Vector3 pos = transform.position;
            pos.z = 0;
            transform.position = pos;
 
            position = transform.position;
        }
    }
}
 
cs


미래의 내가 아마 정리하겠지...


sqrMagnitude를 왜 사용해야되는지 잘 정리해둔 블로그 

http://teddy.tistory.com/19


간략히 정리하자면 sqrMagnitude는 거리와 관련된 계산을 위해서 사용함. 

Magnitude에 비해서 루트 연산을 하지 않기 때문에 계산이 더 빠르다고 한다. 

Posted by 도이(doi)
,
flocking코드를 분석하면서 GetNeighbors함수 부분에서 class를 타입으로 받아오는 함수가 있었다. 
처음에 이해가 안돼서 분석을 하였다. 아래는 선생님이 알려주신 부분이다. 

우선 Enemy라는 클래스가 하나 따로 존재하고 있다고 생각하자. 
클래스의 개념은 함수와 변수의 집합으로 생각하면 편하다. 
일반적으로 기본적인 스크립트의 구조는 하나의 클래스로 존재한다. 
public class Enemy
{
    public float hp = 100.0f;
    public float speed = 10.0f;
    public Vector3 direction;
    public UnityEngine.GameObject prefab;
}

EnemyInit라는 우리의 메인 클래스를 보자. 
EnemyInit에서 우선 변수 타입이 List<Enemy>인 enemies라는 변수를 만든다. 
   public List<Enemy> enemies = new List<Enemy>();
*여기에서 List<Enemy>의 뜻은 Enemy클래스를 list로 받아온다는 의미이다. 
즉, 이 말은 Enemy클래스가 마치 엑셀처럼 줄이 리스트 형태로 가져와 진다는 의미이다. 

start함수에서 enemies리스트에 enemy를 생성하여서 추가한다. 
 void Start()
    {
        //Add Eenemy ...
        for(int i=0; i<5;i++)
        {
            Enemy em = new Enemy();
            em.hp = 100.0f * i;
            em.speed = 10.0f * i;
            enemies.Add(em);
        }

그 다음,
타입이 List<Enemy>인 GetEnemyList()라는 반환함수를 만든다. 
     List<Enemy> GetEnemyList()
*우리가 일반적으로 만드는 void붙는 함수는 호출함수라고 한다.
반환함수는 특정 값을 반환하는 함수를 의미하며 주로 매개변수와 함께 사용한다.
* 매개 변수는 변화하는 변수라고 하여,
함수를 정의할 때 함께 지정해주는 변수값에 들어가는 값을 매개변수라 이해하면 된다. 

반환할 값이 list타입이기 때문에 반환할 리스트인 returnEnemies라는 리스트를 새로만든다. 
foreach 반복문에서 enemies리스트에 안에 있는 변수를 enemy변수로 대입한다. 
List<Enemy> returnEnemies = new List<Enemy>();
 
        foreach (Enemy enemy in enemies)
        {
          
        }

만약 hp가 200.0이상이라면 enemy를 아까 생성해둔 returnEnemies리스트 안에 넣는다. 
 if (enemy.hp >= 200.0f)
            {
                returnEnemies.Add(enemy);
            }

그리고 반환 함수는 항상 return값을 반환해야 한다. 
        return returnEnemies;


리스트 안에 리스트 있고,,, 또 리스트 불러오고.... 으아아아아아악....


<전체코드>

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
37
38
39
40
41
42
public class Enemy
{
    public float hp = 100.0f;
    public float speed = 10.0f;
    public Vector3 direction;
    public UnityEngine.GameObject prefab;
}
 
public class EnemyInit : MonoBehaviour
{
    public List<Enemy> enemies = new List<Enemy>();
 
    void Start()
    {
        //Add Eenemy ...
        for(int i=0; i<5;i++)
        {
            Enemy em = new Enemy();
            em.hp = 100.0f * i;
            em.speed = 10.0f * i;
            enemies.Add(em);
        }
 
        //가까이 있는 적을 추출한다.
        List<Enemy> closestEnemy = GetEnemyList();
    }
 
    List<Enemy> GetEnemyList()
    {
        List<Enemy> returnEnemies = new List<Enemy>();
 
        foreach (Enemy enemy in enemies)
        {
            if (enemy.hp >= 200.0f)
            {
                returnEnemies.Add(enemy);
            }
        }
 
        return returnEnemies;
    }
}
cs


Posted by 도이(doi)
,