2014. 11. 3.

유니티에서 캐릭터의 Rigidbody rotation 고정하기

게임을 만들다보면, 캐릭터가 넘어지는걸 방지해야할 경우가 있습니다. 넘어진다는건 오브젝트의 Rotation 값이 변한다는걸 의미하는데, 이 Rotation 값만 변하지 않도록 고정시켜주면, 캐릭터가 더 이상 넘어지지 않을 것입니다.

유니티 3D에서는 Rigidbody를 고정하기 위해서 Freeze Rotation 속성을 이용합니다.
Rigidbody 컴포넌트 안에 있는 속성으로 x, y, z 축을 bool 형태로 지니고 있습니다. 체크를 해주면, 값이 고정이 되어 더 이상 체크된 축의 rotate 값이 변하지 않습니다. 위의 그림에서는 탱크는 넘어지는것이 필요하기에 Freeze Rotation이 전부 해제가 되어있습니다.

유니티 2D에서는 Rigidbody2D가 사용되는데, 이 컴포턴트는 Freeze Rotation 속성이 존재하지 않습니다. 2D다 보니 x,y,z 축을 각각 조절하는 것이 아닌 Fixed Angle이라는 속성을 통해 x,y의 Rotation만 고정시켜 버립니다.
2D인만큼 좀 더 편리하게 만든 기능 같은데 이 때문에 기존의 Rigidbody의 Freeze Rotation을 찾던 사람들은 더 시간을 소비하게 되는 결과가 발생했습니다. 위 그림처럼 Fixed Angle에 체크를 해주시면, 캐릭터의 Rotation값이 고정되어, 어떤 상황에서도 꿋꿋하게 서 있습니다.

2014. 10. 26.

Console 에서 Android KeyStore 생성하기

안드로이드 앱(게임)을 만들고, 배포하기 위해서는 KeyStore를 생성해야 합니다. KeyStore는 이 앱의 배포자임을 증명하는 서명 파일입니다. 처음 등록할때 사용한 KeyStore는 반드시 백업을 해두어야 하며, 이 KeyStore를 분실하게 되면, 더 이상 해당 앱의 배포자를 증명할 수 없음으로 앱 업데이트가 불가능한 상황에 이를 수 있습니다.

보통 KeyStore는 개발툴에서 자체적으로 제공합니다. 안드로이드 ADT Eclipse라던가 Unity 같은 툴은 자체 기능으로 KeyStore를 생성할 수 있으니 해당 키워드로 구글에 검색하여 그 방법을 찾으시면 됩니다. 반면, 위 툴을 이용하지 않고 KeyStore를 생성하려면 Java의 Keytool을 사용하게 됩니다.

Java에서 제공하는 기능들을 이용하려면 Console에서 실행해야 됩니다. Console은 윈도우 실행 창에서 cmd 라고 입력해주시면 실행 됩니다. cmd에서 cd 키워드를 이용하여 java/bin 폴더로 이동해주세요.
cmd 창에서 cd 를 선언하고 뒤의 주소는 java 폴더안의 bin 폴더를 드래그하면 자동으로 위처럼 경로가 입력됩니다. 엔터를 치면, java/bin 경로로 이동합니다.

bin 폴더 안에는 keytool이 있는데 이것을 이용해 KeyStore를 생성합니다. 아래와 같이 명령어를 입력하세요.
keytool -genkey -v -keystore [키스토어 파일명] -alias [alias 이름] -keyalg [암호화방식] -keysize [key 크기] -validity [유효기간]
실제 사용 예제는 아래와 같습니다.
이런식으로 세부 정보를 적도록 하는데, 선택사항이니 적어도 되고 그냥 공백으로 넘기셔도 됩니다. 세부 정보 입력이 끝나면, 마지막으로 암호를 물어보고 [alias.keysotre 저장중] 이라는 문구가 뜨면서 java/bin 폴더 안에 KeyStore 파일이 생성됩니다.

2014. 10. 13.

Gameobject.Find() 로 Inactive Object를 찾는 방법

유니티에서 Gameobject.Find(string name) 함수를 사용하면, 해당 name과 일치하는 오브젝트를 찾아줍니다. 무척 편리한 기능이지만, 활성화된 오브젝트만 찾아준다는 단점이 있습니다. 물론, 비활성화된 오브젝트까지 전부 검색하기엔 속도가 느려지기에 이런 기능이 적합하지만, 때로는 비활성화된 오브젝트를 포함하여 검색해야할 경우도 종종 나오게 됩니다.
이미 2007년부터 많은 외국인 개발자들이 Gameobject.Find(string name, bool active) 함수를 오버로딩해달라는 요청을 했지만, 7년이 지난 지금까지도 받아들여지지 않고 있습니다. 유니티 정책 방향과 맞지 않기 때문일텐데, 어떠한 이유 때문인지는 잘 모르겠네요. 비활성화된 오브젝트를 찾는 비용보다 타입별로 찾는 비용이 더 적게 든다고 생각한걸까요? 여하튼, 유니티에서 공시적인 API를 지원해주지 않는다면, 직접 만들어 쓰는건 당연한 이치! 이미 구글을 뒤져보면 많은 대체 방법들이 나와 있습니다.

제일 간단해보이는 방법으로는 GameObject.FindObjectWithTag("Tag name") 입니다. 구글링을 하면, 가장 먼저 나오는 방법일텐데 아쉽지만 이 함수는 유니티4 버전에 들어오면서 더 이상 inactive 오브젝트를 찾아주지 않습니다.

비활성화된 오브젝트를 찾는 방법은 결국 GameObject.FindObjectsOfTypeAll(Type type) 함수를 쓰셔야합니다. 이 함수는 비활성화된 오브젝트도 포함하여 찾아주지만, 해당 타입의 오브젝트를 모두 불러오기에 성능적인 문제가 발생할 가능성이 농후하기에 사용에 주의하셔야 합니다.

유니티 함수를 사용하지 않는 방법으로는 오브젝트는 활성화 시켜준 채, 렌더러쪽만 비활성화 시킨다던가, 오브젝트를 비활성화 시키기전에 미리 오브젝트를 변수에 담아 두거나, 처음부터 오브젝트를 참조하게 만들어주는 방식의 방법이 있습니다.

2014. 10. 5.

유니티 Raycast() 란 무엇인가?

Raycast는 직역하면, 광선을 쏜다라는 뜻입니다. Raycast가 필요한 상황을 3D 탱크 게임을 예로 들어 설명하겠습니다. 

3D 탱크 게임을 만들기 위해 맵을 만들고, 중간 중간 벽들을 세워 놓았습니다. 그리고, 플레이어 탱크와 적 탱크를 생성하였습니다. 이제 적 탱크의 인공지능을 만들어야 합니다. 우리가 원하는건 적 탱크가 플레이어 탱크가 일정 범위 이내로 들어왔을때 포탄을 쏘는 것입니다. 그런데, 플레이어 탱크와 적 탱크 사이에 벽이 가로막고 있어 포탄이 막혀버리는 상황이라면, 적 탱크는 플레이어 탱크가 벽에서 빠져나올 때까지 조준만 한 채 포탄을 쏘지 않고 기다려야 합니다.

이런 상황에서 필요한 것이 Raycast입니다. 미리 광선을 쏴 지정된 방향과 거리 이내에 부딪히는 오브젝트가 있는지를 판단해 boolean 값으로 알려줍니다.
위의 그림을 보면, 플레이어가 총을 쏘는데 Raycast를 통해 미리 총을 쏘면, 어디에 맞는지를 알 수 가 있습니다. Raycast를 쓸 때, RaycastHit 매개변수를 넣으면 Raycast에 맞은 오브젝트의 정보도 가져오는데, 이것을 활용하여 맞는 물체가 적일때만 포탄이나 총을 쏘게 하는 식으로 구현을 합니다.
void Update ()
{
  if (detectPlayerTank) {
    turret.LookAt (targetTank);
    if (Physics.Raycast (firePoint.transform.position, firePoint.transform.forward, out hit, 30.0F)) {      
      if (hit.collider.tag == "Player Tank") {
        fireBullet ();
      }
    }
  }
}
위의 코드는 적군 탱크 입장에서 플레이어 탱크가 일정 범위 내에 들어왔을 때, detectPlayerTank 값을 true로 바꾼 후, 진행되는 Update() 구현부입니다.

Update에서는 적 탱크의 포탑이 플레이어 탱크를 바라보게 한 후, Raycast를 쏴 받아온 RaycastHit 오브젝트의 태그를 비교해 그 값이 "Player Tank"일 때만 포탄을 쏘도록 구현된 내용입니다.

유니티에서 Auto Destroy Particle System 제작하기

유니티의 파티클은 화려하고 사용하기 쉬운 훌륭한 시스템이지만, 기본적으로 파티클이 끝나도 오브젝트가 여전히 게임상에 남아있어, 파티클이 끝나면 오브젝트를 지워주는 작업을 해줘야합니다.
화려한 파티클! 하지만, Destroy를 직접 해주지 않으면, 파티클 LifeTime이 끝나도 오브젝트가 계속 남아있다
유니티에서 제공하는 IsAlive 함수는, 파티클이 끝났는지 여부를 알려줍니다. 이것을 이용해 ParticleSystemAutoDestroy라는 스크립트를 만들고, 어떤 파티클을 만들던 이 스크립트만 추가해주면, 자동으로 파티클이 끝났을 때, 파티클 오브젝트도 사라지도록 만들어봅시다.
using UnityEngine;
using System.Collections;

public class ParticleSystemAutoDestroy : MonoBehaviour
{
  private ParticleSystem ps;

  void Start ()
  {
    ps = GetComponent ();
  }
 
  void Update ()
  {
    if (ps) {
      if (!ps.IsAlive ()) {
        Destroy (gameObject);
      }
    } 
  }
}
이제 어떤 파티클을 만들건 이 스크립트만 추가해주면, 파티클 LifeTime이 끝남과 동시에 파티클 오브젝트도 사라지게 됩니다.