상세 컨텐츠

본문 제목

첫번째 프로그램. RVC ( Rolling Virtual Creatures )

HEAP_Project/Develope

by hwano 2014. 5. 22. 20:32

본문

유니티를 배울 겸 첫번째 테스트 프로그램을 만들어보자.

가상의 진화 목표를 만들고 목표에 가장 부합하는 조합을 진화 알고리즘으로

찾아보려고 한다.

 

 ----------------------------------------------------------------------------------------------------

 

프로그램 개요

 

여러 도형을 임의로 조립하여 가상 생명체를 만든다.

가상 생명체 중 가장 먼저 목표에 도달하는 생명체 1,2위를 교배시켜 같은 실험을 반복한다.

 

 

환경설정

 

1. 생명체 생성

Sphere, Cube, Cylinder, Pyramid, plane 5개 도형을 임의로 조합시키고 Z 방향으로 굴러가도록

force를 적용한다.

 

2. 교배

1위 생명체 중 임의로 3개, 2위 생명체 중 임의로 2개의 도형을 조합하여 새로운 생명체를 만든다.

부모에게서 승계받은 도형들은 10도 범위에서 임의의 각으로 회전시킨다.

 

 

 

 

 씬 : 01_Start  ( 랜덤조합 버튼을 만들어보자 ) 

 

 

하나의 생명체를 미들, 업, 다운, 포워드, 백, 라이트, 레프트 7개의 도형의 조합으로 만들려고 한다.

아마도 지금 생각으로는 각 위치에 empty GameObject를 만들고 랜덤버튼을 누르면

Mesh filter / Mesh에  각기 다른 오브젝트들이 연결되도록 스크립트로 구현해야될거 같다.

 

 

스크립트 연습삼아 첫번째로 우선 랜덤버튼을 누르면 위치가 이동하도록 해보았다.

 

void Update () {
  Check_Random_Button();
 }

 // Random_Button을 눌렀을때
 void Check_Random_Button()
 {
  if( Input.GetMouseButtonDown(0)){
   if( Random_Button.HitTest( new Vector3( Input.mousePosition.x, Input.mousePosition.y, 0 ))){
    Middle_Body.transform.Translate( 1,0,0 );}}}

 

Translate는 매프레임마다 뒤에 적어준 x,y,z값만큼 이동하는 명령어인데 ( relative )

포지션값을 직접 적용하는 건 어떻게 하는지 모르겠다. ( absolute )

 

 

 

 

위와 같은데 랜덤버튼을 누르면 위치가 랜덤으로 이동하도록 해보았다.

 

void Update () {
  Check_Random_Button();
 }

 // Random_Button을 눌렀을때
 void Check_Random_Button()
 {
  if( Input.GetMouseButtonDown(0)){
   if( Random_Button.HitTest( new Vector3( Input.mousePosition.x, Input.mousePosition.y, 0 ))){
    float random_ = UnityEngine.Random.Range( -1.0f, 1.0f );
    Middle_Body.transform.Translate( random_,0,0 );}}}

 

유니티에서 random을 찾아보니 random이 유니티엔진과 .net(? 요건 뭔지 모르겠다 암튼)에 두가지가 있어서

앞에 명확하게 네임스페이스를 UnityEngine.Random.Range( -1.0f, 1.0f ); 이런식으로 붙여주어야 한다고 한다.

 

 

 

다시 랜덤버튼을 누르면 쉐이더의 컬러가 랜덤하게 바뀌도록 해보았다.

 

void Update () {
  Check_Random_Button();
 }

 // Random_Button을 눌렀을때
 void Check_Random_Button()
 {
  if( Input.GetMouseButtonDown(0)){
   if( Random_Button.HitTest( new Vector3( Input.mousePosition.x, Input.mousePosition.y, 0 ))){
    float random_1 = UnityEngine.Random.Range( 0.0f, 1.0f );
    float random_2 = UnityEngine.Random.Range( 0.0f, 1.0f );
    float random_3 = UnityEngine.Random.Range( 0.0f, 1.0f );
    Middle_Body.transform.Translate( random_1,0,0 );
    Color color_ = new Color( random_1, random_2, random_3 );
    Middle_Body.renderer.material.SetColor("_Color", color_);
   }}}

 

각 컬러값을 받아오는 것은 GetColor를 이용하면 되고 컬러의 어트리뷰트네임은

아래와 같다.

 

Common color names used by Unity's builtin shaders:
"_Color" is the main color of a material. This can also be accessed via color property.
"_SpecColor" is the specular color of a material (used in specular/vertexlit shaders).
"_Emission" is the emissive color of a material (used in vertexlit shaders).
"_ReflectColor" is the reflection color of the material (used in reflective shaders).

 

 

이번엔 mesh filter에 들어가는 mesh값을 다른걸로 바꿔보자

 

public Mesh Mesh_Cube;
public Mesh Mesh_Cylinder;
public Mesh Mesh_Plane;
public Mesh Mesh_Pyramid;
public Mesh Mesh_Sphere;

~

 

void Check_Random_Button()
 {
  if( Input.GetMouseButtonDown(0)){
   if( Random_Button.HitTest( new Vector3( Input.mousePosition.x, Input.mousePosition.y, 0 ))){
    float random_ = UnityEngine.Random.Range( 0, 5 );
    if( random_ == 0 ){Middle_Body.GetComponent<MeshFilter>().mesh = Mesh_Cube;}
    if( random_ == 1 ){Middle_Body.GetComponent<MeshFilter>().mesh = Mesh_Cylinder;}
    if( random_ == 2 ){Middle_Body.GetComponent<MeshFilter>().mesh = Mesh_Plane;}
    if( random_ == 3 ){Middle_Body.GetComponent<MeshFilter>().mesh = Mesh_Pyramid;}
    if( random_ == 4 ){Middle_Body.GetComponent<MeshFilter>().mesh = Mesh_Sphere;}
   }}}

 

mesh값을 받을 public mesh attribute들을 만들어서 그걸로 적용해 주었다.

 

random값은 경우는 ()안에 float를 쓰면 float가 나가고 int형으로 쓰면 int가 나간다.

( 0, 5 ) 최대값을 5까지로 잡아야 숫자 4까지 반환된다.

 

회전값과 mesh값을 변경하는 기본 세팅을 완료한 최종

 

 

 

 

 

 씬 : 02_Rolling  ( 경주 라인을 굴러가도록 만들어보자. ) 

 

 

Scene_01 에서 만들어지는 오브젝트를 5개로 늘리고 5개의 오브젝트가 경주라인을 굴러가도록 만든다.

결승전밖으로 떨어지는 1위 2위의 오브젝트를 골라낸다.

 

 

오브젝트를 굴러가도록 만들어보자.

 

씬을 나눠서 작업을 하려고 하는데 Scene 01에서 랜덤하게 만들어진 오브젝트를 가져 오는건 어떻게

하는지 몰라서 우선 Rolling 씬에서 오브젝트를 별도로 만들어 굴려볼려고 한다.

 

아우,,  예전부터 3D로 피직스 할때 마다 느꼈던게 여기 나오네,

첨에 rotate로 회전시키려고 했는데 rotate할려는 반대쪽으로 오히려 큐브가 미끄러져 가버린다.

 

기준축이 없는 상황에서 힘이 3D의 한계로 이상한 방향으로 적용이 되는데

유니티에서는 토크값이 따로 있다. rotate로 해볼려고 해서 한참 헤맸네.

 

void Update () {
  rigidbody.AddTorque( -50,0,0 );
 }

 

게임엔진이라서 개념을 조금 달리해야 할게

로테이션에 값을 50도 준다와

앞으로 회전하려는 힘을 50도 준다는 다르게 생각해야 한다.  그냥 로테이션값은 경우는

마치 무중력 상태처럼 힘이 반작용으로 작용하거나 해서 원하는 계산이 안된다.

아마 이런 부분이 많이 나올거 같다.

 

중심 오브젝트에 토크값을 걸어주고, 중심오브젝트에 다른 오브젝트들을 fix joint로 걸어주니 뭐 어느 정도는

원하는데로 작동을 한다.  fix를 spring으로 바꾸고 다시 테스트 해보았다.

 

그나저나 유니티 헬프문서 폰트 왜이렇게 조그맣냐.

나이먹어 눈이 침침해 죽겐네.

 

 

이래뵈도 내가 만든 첫번째 virtual creature 되시겠다.

 

 

씬 01에서 만든 오브젝트를 씬 02으로 가져오자.

 

prefab으로 가져오는 건가? 

 

프리팹 공유를 테스트 해봤는데 잘 안된다.

프리팹을 만들고 scene01과 scene02의 하이라키에 마우스로 끌어왔다.

 

랜덤 버튼을 누르면 prefab의 meshfilter가 바뀌어 mesh가 다른 것으로 바뀌어야 한다.

근데 화면상으로 적용이 안된다.  

 

스크립트에 mesh가 변경될 body용 gameObject들을 연결 시켰는데

하이라키에서 가져올수도 있고 프로젝트창에서 바로 가져올 수도 있다.  하이라키에서 가져온

씬에 적용된 prefab을 변경하면 프로젝트 play시에도 메쉬가 바뀌며 잘 작동한다.

그런데 프로젝트창에서 prefab의 원본을 가져와 링크를 시켜 놓으면 play시 적용이 안된다.

 

기록을 살펴보면 스크립트가 작동은 하는거 같은데 원본에서 변화가 이러나도

바로바로 scene01의 prefab으로 갱신은 안되는거 같다.

 

prefab을 하이라키로 가져와 놓으므로서 복사가 된거지만 또 어느부분은 링크가 끊어지는 이상한 관계가

되는거 같다.  아직 이 부분은 정확히

 

잘 모르겠음.

 

암튼,  그래서 다른 방법으로

 

스크립트에 하이라키의 prefab을 연결시켜서 play시 화면에 변화가 보일 수 있도록 한다.

그리고 mesh가 바뀐뒤 prefab의 apply버튼을 누르는 스크립트를 하나더 적용해서

원본의 prefab도 변경될수 있도록 해보자.

 

--------------------------------------------------------------------------------------------------------

 

위 처럼 작동하게 하려면

 

using UnityEditor;

PrefabUtility.ReplacePrefab( Object, ParentPrefab, ReplacePrefabOptions.ConnectToPrefab );

 

위 구문을 쓰면 된다.

using UnityEditor는 아래 PrefabUtility를 불러오기 위한 namespace다. python의 import같은 그런거.

( 프로그래밍 언어 통일 좀 해줘 )

 

Object는 하이라키의 apply를 누를 프리팹의 인스턴스오브젝트이고 ParentPrefab은 링크된 원본 prefab을

넣어주면 된다.

 

public GameObject ParentPrefab;

 

이렇게 받아오거나

 

Object  ParentPrefab= UnityEditor.PrefabUtility.GetPrefabParent( TestObject );

 

이렇게 받아면 된다.  앞에 클래스가 GameObject가 아닌 Object인걸 확인하자.

 

---------------------------------------------------------------------------------------------------

 

 

위와 같이 해서 적용하면 작동은 하게 되는데 또 프리팹 관계를 이해를 못하겠다.

예를 들어 3d 오브젝트를 하나 가져와서 하이라키에 놓은뒤 (아직 프리팹은 아닌거고 asset상태 )

position X를 10으로 만들어보자.

 

그런 다음 프리팹으로 만들고 특정 버튼을 누르면 오브젝트위치가 +=10이 되게 하고 위 구문으로 apply

되도록 해보자.  버튼을 누르다 플레이를 중지시키면 프리팹원본의 position X는 스크립트대로 10씩

증가하였지만 하이라키에 있는 오브젝트는 처음대로 position X 10으로 돌아가 버린다.

 

프리팹 원본과 값이 달라졌으니 ( override되었으니 ) position이 굵은 글씨가 되면서..    

 ... 왜 이렇게 비직관적이고어렵게 만들어 놨을까.?

 

---------------------------------------------------------------------------------------------------

 

 

또 잘못한 부분이 있음.

 

 

내가 프리팹을 구성한 방식은 Body_01이라는 빈 GameObject를 만들고

그 밑으로 화면에 보여질 Body오브젝트들을 child시켰다.

 

이 상태에서 위에 썼던대로 apply를 시키는 구문을 적용하니 replace되는 프리팹이 내가 예상한 것과

달랐다. 무슨말인고 하니 난 Left_Body를 apply버튼을 누르고 싶었는데

 

Object  ParentPrefab= UnityEditor.PrefabUtility.GetPrefabParent( Left_Body );

 

위 구문으로 얻어낸 ParentPrefab는 Left_Body프리팹이 아니라 Body_01 프리팹이다.

어디서 확인하는고 하니 밑에 있는 프로젝트창을 잘 보면 Left_Body를 선택해도 밑에

Body_01.prefab이라고 설명이 뜬다.

 

 

즉,  각 Body들을 각각 apply해야한다고 생각했던건 잘못된거고 body_01을 한번만 통으로

apply해주면 된다.

 

물어볼 사람도 없고 책도 없이 공부할래니,,  개념이 너무 복잡하다.   복잡하다기 보다

prefab시스템이 좀 엉성해보임,,

 

검색해보면 위와 같은 케이스인지는 모르지만 ( 맞는거 같네 ) 

prefab 하위의 prefab즉 prefab끼리의 계층구조를 지원하지 않아서 그걸 구현해주는 script가

어셋스토어에 올라와 있는 모양이다. 앞으로 유니티에 기본적으로 들어온다고는 하는데

 

조금 만져본 느낌으로 유니티 자체가 개인이 대충만든 프로그램이 어찌어찌 하다보니 덩치만 커진것 같은

느낌이 든다 마치 마야 마냥. 그냥 다른 프로그램으로 넘어가야하나 좀 고민이 든다.

 

 

 

 

 

 

 

 

 

관련글 더보기