Unity3D/TowerDefence 2014/09/16 02:23

이번엔 지난시간에 이야기했던대로,

타워의 범위 안에 유닛이 있는지 검색하고, 자동으로 공격하는 스크립트를 만들도록 하겠습니다.


오늘 할일은 거의 스크립트 작업뿐이라 바로 완성된 스크립트부터 알려드리고 설명하도록 하겠습니다.


TowerBase.cs


using UnityEngine;
using System.Collections;

public class TowerBase : MonoBehaviour {
	public UnitBase target;
	private string spriteAtkNameFormat = "tower_a_{0:d2}";
	private string spriteNormalNameFormat = "tower_n_{0:d2}";
	public float range = 100;
	public float shotDelay = 3.0f;
	private float reloadTime = 0;
	private tk2dSprite spr;
	// Use this for initialization
	void Start () {
		spr = this.GetComponentInChildren<tk2dSprite>();
		reloadTime = shotDelay;
	}
	
	// Update is called once per frame
	void Update () {
		checkRangeTarget();
		lookAtTarget();
		autoShot();
		attackSprAnim();
		updateSprite();
	}

	void checkRangeTarget()
	{
		if(target != null)
		{
			if(range < Vector2.Distance((Vector2)target.transform.localPosition, (Vector2)this.transform.localPosition))target = null;
		}
		if(target == null)
		{
			foreach(UnitBase ub in GameManager.instance.unitList)
			{
				if(range > Vector2.Distance((Vector2)ub.transform.localPosition, (Vector2)this.transform.localPosition))
				{
					target = ub;
					return;
				}
			}
		}
		
	}

	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;
		spriteN = 18 -Mathf.Abs(angle - 18);
		int spriteDir = angle<18?1:-1;
		spr.scale = new Vector3(spriteDir, 1,1);
		
	}

	void autoShot()
	{
		reloadTime -= Time.deltaTime;
		if(target == null || reloadTime>0)return;
		attackTarget();
		
	}
	bool isAtkSpr = false;
	bool isAttacking = false;
	float attackTimeCount = 0f;
	float attackDuration = .1f;
	float attackDelay = .13f;
	int attackRepeat = 3;
	int attackedCount = 0;

	void attackTarget()
	{
		if(target == null || isAttacking)return;
		attackTimeCount = 0;
		attackedCount = 0;
		isAtkSpr = true;
		isAttacking = true;
		reloadTime = shotDelay;
	}

	void attackSprAnim()
	{
		if(!isAttacking)return;
		attackTimeCount += Time.deltaTime;
		if(isAtkSpr)
		{
			if(attackDuration<attackTimeCount)isAtkSpr = false;
		}
		else if(attackDelay<attackTimeCount)
		{
			attackedCount++;
			if(attackedCount<attackRepeat)
			{
				isAtkSpr = true;
				attackTimeCount -= attackDelay;
			}
			else isAttacking = false;
		}
	}

	private int spriteN = 0;
	void updateSprite()
	{
		string spriteName = string.Format(isAtkSpr?spriteAtkNameFormat:spriteNormalNameFormat, spriteN);
		spr.spriteId = spr.GetSpriteIdByName(spriteName);
	}

}


변수들 

위쪽에 추가된 변수들은 먼저 보면,


private string spriteAtkNameFormat = "tower_a_{0:d2}";
공격중인 타워 이미지입니다. 기본이미지와 동일한 각도에 불빛만 추가된 이미지입니다. 아래에서 노멀이미지와 섞어서 공격애니메이션을 만들겠습니다.

public float range = 100;
타워의 공격범위 입니다. public 으로 선언되어있으니 나중에 인스팩터에서 원하는 수치로 수정가능합니다.

public float shotDelay = 3.0f;
한번공격후 다음공격까지의 딜레이 시간입니다. 나중에 터렛 업그레이드에 공격속도증가 같은게 들어갈때 수정하면 좋겠네요.

private float reloadTime = 0;
재장전 시간입니다. 위의 shotDelay로 셋팅되고, 점점 줄어들다가 0 이되면 재 발사 가능상태가 됩니다.


함수 

우선 기본적인 Start와, Update에 추가되어있는것만 보겠습니다.

Start();
 재장전시간만 shotDelay로 초기화 시켜줍니다. 맵에 추가된뒤 바로공격하지 않고, shotDelay만큼 재장전시간이 지난후에 공격을 시작하게 됩니다.

Update();
기존보다 좀더 많은 함수들을 호축해줍니다.
순서대로 설명하면,
 범위안에 타겟이 있는지 검사 -> 타겟을 조준 -> 자동공격 -> 공격애니메이션 -> 스프라이트 갱신
이런 순서입니다.

추가된 함수들을 아래에서 하나씩 보도록 하겠습니다.



현재 설정된 타겟과, 타워사이의 거리가 공격범위 보다 많으면 타겟을 해제합니다.
그리고 GameManager의 unitList에서 범위내에 있는유닛이 있으면 그 유닛을 타겟으로 지정합니다.

transform.localPosition은 Vector3 형이지만, Vector3로 Distance를 구할경우 z 값에도 영향을 받기때문에 강제로 Vector2 로 캐스팅해서 계산합니다.
그리고, 두번째 if 에서 else를 쓰지 않은 이유는 첫번째검사에서 타겟이 해제될경우 바로 다른 타겟을 찾을수 있도록 하기위해서 else를 사용하지 않습니다.


autoShot은 reloadTime을 계속 줄여서 0 이하의 값이 되면 attackTarget()을 실행해줍니다.


타워가 한번 공격에 딱 한번씩 총알을 쏘고 끝낼수도 있지만, 좀더 퀄리티를 높이기 위해 3연발 기관총을 쏘는 타워로 만들어보겠습니다.

(공격딜레이나, 리핏 값을 수정해서 다양하게 사용가능합니다.)



변수들을 먼저 살펴보면,

isAtkSpr 가 '참' 일경우 공격하는 sprite를 표시하고 '거짓'일경우 일반스프라이트를 표시해줄기위한 bool값입니다.

isAttacking 은 현재 공격애니메이션인지를 판단할수있는 bool값입니다.

attackTimeCount는 공격애니메이션을 위해 공격시간을 카운트 하는 변수입니다.

attackDuration은 공격하는 스프라이트 를 표시해주는 시간으로, 0.1초간 공격하는 스프라이트를 보여주도록 설정했습니다.

attackDelay는 연발로 총을쏠때 다음발이 발사 될때까지의 딜레이입니다. 여기선 0.13 초로 되어있어서 공격후 0.03초뒤에 다음공격을 하게됩니다.

attackRepeat 은 연발 공격 횟수로 현재 3회로 되어있습니다.

attackedCount 는 한공격에 이미 몇발 공격했는지 count하는 변수입니다. 위의 attackRepeat횟수보다 많으면 공격을 중지합니다.


attackTarget() 함수는

위의 autoShot()이 재장전시간이 끝나면 호출하는 함수로 공격애니메이션에 필요한 값들을 초기화 하고 재장전시간을 셋팅합니다.


attackSprAnim() 함수를 보면
현재 공격화면인경우(isAtkSpr 가 'true') 공격애니메이션 시간이 공격화면 표시시간보다 길어지면 보통화면으로 바꾸고, 
보통화면일 경우,
연발 발사 딜레이시간보다 커지게 되면 연발 누적회수를 증가시키고,
연발 공격회수보다 작으면 다시 공격화면으로 스프라이트를 변경해줍니다.

공격횟수만큼 도달한다면 공격 애니메이션을 중단시킵니다.




마지막으로
updateSprite()는, 

지난시관과 크게 바뀐점은 없지만 위에 표시한대로 스프라이트 이름을 만들때,
isAtkSpr가 '참' 일경우 공격스프라이트를,
'거짓' 일경우 일반 스프라이트를 만들어주게 됩니다.


이제 하이라키 텝 안에, gameField에서 tower를 적당히 옮겨서 화면 중앙에 배치시키고,(임시로 붙였던 "unit_marine" 오브젝트는 삭제합니다.)
플레이 버튼을 누르고 "AddUnit"버튼으로 유닛을 추가해보면, 



화면처럼 마린을 겨냥해서 총을 쏴대는 타워를 볼수 있습니다.

다음 시간에는 맵상에 타워를 짓고, 마린들을 정말로 죽여보겠습니다.




posted by andwhy
<PREV NEXT> 1 2 3 4 5 ... 64

티스토리 툴바