Unity3D/TowerDefence 2014.09.13 11:24

드디어 타워짓기 입니다.

사실..길찾기 쪽이 너무 분량이 커서..좀 설명을 대충한거 같은데,

이제 타워쪽은 쉽고 재밌는부분이라 좀 설명을 자세히 적어볼까합니다.


1. 리소스 만들기

이전에 유닛을 만들었을때처럼 타워도 리소스를 만들어야 합니다.

tower.zip

첨부한 리소스를 열어보시면, 필드러너에서 뽑아온 타워 스프라이트가 있습니다.

원래는 타워종류 * 업그레이드 단계 * 공격모션 * 방향 별로 리소스가 있지만...
기본 타워의 노멀, 공경 스프라이트와 방향만가지고 만들도록 하겠습니다.

노멀 상태의 타워 스프라이트를 보면, 총 19 개의 이미지가 있습니다.
보시면 아시겠지만 오른쪽 회전 반향의 이미지들(17장)과 상, 하 이미지(2장)만 있습니다.

전체 이미지가 전부 있다면36장장이 되겠죠.

이 이미지들을 배치해보면 그림과 같습니다.


밝은쪽은 이미 있는 19장의 이미지이고, 왼쪽의 어두운 부분은 없는 이미지이죠.
왼쪽 어두운 이미지들은 사실 오른쪽이미지를 좌우 반전만 시킨 이미지들입니다.좌우대칭이되는상황이면 굳이 필요없는 이미지를 추가해서 리소스를 늘일 필요가 없기때문에 한쪽이미지로만 사용하는것입니다.
(사실 유닛을 만들때도, 좌우 대칭이기에 한쪽만 있으면 충분했지만, 아틀라스 공간도 남고 코딩으로 처리하기도 귀찮아서 반대쪽이미지도 만들어버렸습니다.)


이제, 유닛만들었을때와 마찬가지로 타워의 스프라이트 컬렉션은 만들겠습니다.

Create->tk2d->SpriteCollection을 누르고, 이름을 tower_sc 로 변경합니다.


만들어진 "tower_sc"를 선택하고, openEditor를 눌러서 뜬 에디터 창에 tower 이미지 리소스들을 드래그 해서 등록합니다.
타워의 원본 이미지가 너무 커서 그냥 쓰기엔 유닛이랑 차이가 많이 납니다.
창의 왼쪽에서 스프라이트들을 모두 선택하고,(shift를 누르고 선택하시면 편합니다.) 오른쪽의 scale 항목을 ".3"으로 변경하시고 꼭 apply버튼을 눌러주세요.

이후 commit버튼을 눌러 완료합니다.
(가장 좋은방법은 원본이미지를 1/3 로 스케일을 줄여서 작업하는방법입니다.)

이제 하이라키에 "gameField" 를 선택하고 tk2dSprite를 만들어줍니다.


만일,tower스프라이트가 아닌 엉뚱한 스프라이트가 추가 되었다면 인스팩터 창의 "Tk2dSprite"항목에서 Collection 과, Sprite를 다음과 같이 맞춰주면 됩니다.

이제 한번 플레이버튼을 눌러서 마린들과 크기를 한번 비교해보겠습니다.


마린과의 크기도 적당하고 맵상에 배치해도 딱 좋을것 같네요.

2. 타워 타겟팅

리소스는 준비되었으니, 유닛의 위치에 따라 타워의 총구를 돌려서 유닛을 타겟팅하겠습니다.

우선 방금 추가한 타워Sprite를 "tower"라는 이름으로 변경하고, TowerBase.cs 파일을 만들어서 그림과 같이 tower 오브젝트에 추가해줍니다.

TowerBase.cs



using UnityEngine;
using System.Collections;

public class TowerBase : MonoBehaviour {
	public UnitBase target;
	private string spriteNormalNameFormat = "tower_n_{0:d2}";
	private tk2dSprite spr;
	// Use this for initialization
	void Start () {
		spr = this.GetComponentInChildren<tk2dSprite>();
	}
	
	// Update is called once per frame
	void Update () {
		lookAtTarget();
		updateSprite();
	}
	private int spritN = 0;
	void updateSprite()
	{
		string spriteName = string.Format(spriteNormalNameFormat, spritN);
		spr.spriteId = spr.GetSpriteIdByName(spriteName);
	}
	void lookAtTarget()
	{
		if(target == null)return;
		float anglePI = Mathf.Atan2(this.transform.localPosition.y - target.transform.localPosition.y, target.transform.localPosition.x - this.transform.localPosition.x) + Mathf.PI/2.0f;
		int angle = (int)((anglePI/Mathf.PI * 18.0f) + 36)%36;
		spritN = 18 -Mathf.Abs(angle - 18);
		int spriteDir = angle<18?1:-1;
		spr.scale = new Vector3(spriteDir, 1,1);
		
	}
}


이렇게 코딩을 해주면, tower 인스펙터에 "target"이라는 항목이 생성됩니다.

"target"은 UnitBase 데이터형을 변수로 받고 있는데, 우선 테스트를 위해서 프로젝트 폴더의 "unit_marine" prefab을 하이라키의 gameField 아래로 드래그 해서 추가합니다.
(추가경로를 꼭 확인하세요. 방향을 체크할때 상대좌표를 사용하기때문에 경로가 다르면 엉뚱한곳을 보고 있을수 있습니다.)

추가된 unit_marine 을 tower의 target 에 드래그 해줍니다.


이제 플레이를 버튼을 누른뒤 scene 화면에서 tower나 추가된 marine의 좌표를 이동해보면, 항상 marine을 향해 총구를 겨냥하고 있게 됩니다.
(현재는 테스트 코드임으로 다른 유닛들이 추가되더라도 인스팩터에서 셋팅된 유닛만을 바라보게 됩니다.)




스크립트 코드를 보면서 하나씩 설명을 하도록 하겠습니다.

변수들

public UnitBase target;
타워가 바라보게될 타겟입니다.현재는 강제로 하나의 유닛을 잡고 있지만 다음엔 범위에서 벗어나면 새로운 타겟을 잡도록 변경할겁니다.

private string spriteNormalNameFormat = "tower_n_{0:d2}";
스프라이트 이름형식입니다. String.Format 함수를 통해서 스프라이트 이름을 만들어줍니다.
string.Format 함수를 처음보시는분들을 위해 설명드리면, {0} 이란곳에 2번째 파라미터를 집어 넣게 되고, 옵션으로 d2(2자리 정수값) 으로 들어갑니다.
예를들어서 2번째 파라미터로 1 이 들어갈경우 
string.Format("tower_n_{0:d2}", 1) 은  "tower_n_01" 이출력되고, string.Format("tower_n_{0:d2}", 19) 는 "tower_n_19" 가 출력됩니다.


함수들

void lookAtTarget()
자신과 타겟의위치를 비교하여, 알맞은 각도를 구하고, 알맞은 sprite의 번호를 구합니다.


만일 타겟이 없는경우 아무것도 하지 않습니다.
타겟이 있을경우 자신의 위치와, 타겟의 위치를 가지고 각도를 구합니다.
Mathf.Atan2(y,x)는 y변화량과, x 변화량으로 각도를 구해줍니다. 이때 나오는각도는 -Mathf.PI에서 Mathf.PI까지 (-3.141592....부터 3.141592..)까지입니다. 
라디안 값으로 구해지게 되는데, 아마 고등학교 수학시간에 나오는 범위인것같습니다.
간단히 이야기하면.. Mathf.PI(원주율) == 180도 라고 생각하시면 됩니다.
라디안 값을 평소에 자주쓰는 도단위 로 치환하기 위해선 180/Mathf.PI 를 곱해주면 됩니다.
이렇게 Atan2로 두 위치의 각도를 구하게 되면 타워기준으로 9시 방향이 0도가 되고 시계방향으로 값이 증가하여 180도, 시계반대방향으로 증가 하며 -180도가 됩니다.
그런데 우리가 가지고 있는 타워의 총구는 12시 방향이 0도로 되어있습니다. 그래서 시계방향으로 90도를 더 더해줍니다.( + Mathf.PI/2.0f )

이제 구해진 라디안 값을 10도 단위로 잘라줘야 합니다.
위에서 잠깐 이야기한대로 라디안 값에 180/Mathf.PI를 곱해주면 되는데, 우리가 필요한건 10도 로 나눈값이기에 18/Mathf.PI만 곱하겠습니다.
우리가 원하는 값은 0~35까지의 각도를 알고 싶은데, 지금은 음수 값이 섞여있어서, 원하는데로 0~35까지의 값만 나오도록 간단한 연산을 추가해줍니다.
(어떤 값에 36을 더해주고, % 36으로 36으로 나눈 나머지를 구하면 0~35까지의 값이 나오게 됩니다. 단, 어떤 값이 -36보단 커야합니다.)

여기까지 해주면 angle값은 0~35의 정수로 나오게 됩니다.
타워의 Sprite가 각도별고 0~35개 있다면 바로 계산이 끝나게 되지만, 리소스를 주리기 위해서 반대쪽방향은 Sprite가 없는상황입니다.
그래서 대칭되는 값을 찾아줘야 하는데, 0,1,2,3,4....17,18,17,16,15.... 이런식으로 18을 기준으로 다시 숫자가 줄어들면 될것 같습니다.

중학교 수학시간에 많이 했던 그래프로 그려보면...

이런식이 되면 딱 좋을것 같습니다.

공식으로 써보면...

y = 18 -|(x - 18)| 

이면 될것같네요.

이걸 유니티 코드로 바꾼게,
spriteN = 18 -Mathf.Abs(angle - 18);
입니다.

이제 spriteN은 0부터 증가하다가 18이상이되면 하나씩 감소하게 될껍니다.

마지막으로 angle이 18 이상일경우 xScale을 -1 로 바꿔서 좌우 대칭으로만 바꿔주면 됩니다.


void updateSprite()
각도에 따라서 sprite를 교체시켜줍니다.


string.Format과 위에서 구한 spriteN값으로 원하는 각도의 sprite이름을 만들어줍니다.
tk2d의 경우 스프라이트 네임으로 바로 스프라이트 교체가 안되서, GetSpriteIdBuName으로 아이디값을 알아낸뒤, spriteId를 셋팅해서 스프라이트를 교체시킵니다.


여기까지 타워가 유닛을 바라보게 만들어주었습니다.

그런데, 조금 마음에 안드는 부분이..
타워 주위를 원을그리며 유닛을 움직여보면 타워가 덜컹거리면서 움직여보입니다.
원인은 타워스프라이트의 중심점이 맞지 않아서 입니다.
오늘은 중심점만 맞추고, 끝내도록 하겠습니다.


3. 중심점 맞추기.

프로젝트 텝에 tower_sc를 선택하고, openEditor를 클릭해서 편집창을 엽니다.

에디터 창에서 오른쪽에 Sprites를 모두 선택후, Anchor 를 Custom으로 변경후 Apply버튼을 누릅니다.(공격 모션도 어차피 맞춰야하기때문에 이번에 한번에 다 하겠습니다.)

apply버튼을 누르면 모든 sprite들이 anchor가 Custom으로 변경되어있습니다.

이제부터 약간의 노가다 작업이 필요합니다.


화면 아랫쪽에 "Anchor"버튼을 누르면 다음과 같이 중심점을 변경할수 있는 화면이 나옵니다.

중심점을 마우스로 이동시켜 동그라미로 표시된 부분(이미지에 동그란 기준점이 보입니다.)으로 모든 스프라이트의 위치를 하나씩 이동시켜주고, commit 버튼을 눌러줍니다.


다시 플레이버튼을 누르고 회전하는 모습을 보면 이전처럼 덜컹거리지 않고 아주 자연스럽게 잘 돌아가보입니다.



이번 강좌는 여기서 마치도록 하겠습니다.

다음번엔 범위안에 유닛이 있는지 체크하고, 유닛을 자동으로 공격하는 방법까지 만들어보도록 하겠습니다.




신고
posted by andwhy

티스토리 툴바