Unity3D/TowerDefence 2014.10.05 23:21
앞으로 2회 남았네요...

처음 계획한것과 약간 달라져서.. 좀 더 세분화 된것도 있고, 약식으로 넘어가버린것들도 있네요.
앞으로 2회분의 내용은.

-오늘 WaveData 작성관 5Wave까지 구현..(나머지는 직접 만들어보시면 됩니다.)

-게임 종료와 재시작.

이렇게 2회로 끝내겠습니다.

UI도 넣어서 좀더 게임처럼 보이고 싶었는데, 적당한 UI를 구하질 못했네요.
혹시라도 이글을 보시는 디자이너분중에.. UI작업을 도와주실분있으시면 언제라도 연락 바랍니다.

요즘 업무가 많아지고, 이것저것 신경쓸일이 많아져서, 우선은 다음회까지로 종료하겠습니다.
종료후에 지금까지 했던걸 좀더 다듬어서 재판??으로 만들고, 시간되는데로 좀더 보강하도록 하겠습니다.

//===============================================================================

오늘 작업할 내용은 WaveData 입니다.

WaveData는 한웨이브(스테이지) 에 나올 적들에 대한 데이터 입니다.

지금까지는 수동으로 버튼을 눌러서 적 유닛을 추가해줬지만,

WaveData를 통해서 나오는 적들의 속도, 체력, 나오는 간격...등등을 정해줍니다.

유닛리소스 종류도 정해주면 좋겠지만..지금껏 우리가 작업한 리소스는 유닛이 1종밖엔 없으니 할수 없겠네요.


1. WaveData.cs

웨이브 데이터는 한 웨이브당 출현할 유닛들에 대한 정보를 담고 있습니다.

웨이브에 출현할 유닛에 필요한 정보는

유닛의 HP
유닛 스피드
유닛을 제거할때 얻는 골드량
한번생성될때 몇마리의 유닛이 생성될지
유닛이 생성되고 다음생성때까지의 딜레이
유닛을 총 몇번 생성할것인지,
다음 웨이브까지의 딜레이


정도입니다.

WaveData.cs 파일을 만들고,

다음과 같이 코딩합니다.
(완료된 cs파일들은 마지막에 한번에 모두 공유하도록 하겠습니다.)

WaveData.cs

모든 public 변수들은 위에 설명한 정보들을 저장한것이고,
name 이란 변수와, generateCount 란 변수만 위에서 설명이 없는 변수인데 잠시후에 설명하겠습니다.

그림에 표시된 부분을 보면,메타 테그가 붙어있는데, 지난 타워 업데이트 시간에 UpdateData 클래스에도 동일한 메타테그를 붙였습니다.
기억나실지 모르겠지만, 저 태가가 붙어있으면 인스팩터상에서 해당 클래스의 변수들을 볼수 있게 시리얼라이즈화 시켜줍니다.

다음으로..name 이란 string변수는 크게 쓸모는 없는 변수 이지만, 
인스팩터에 현재 클래스가 표시될때 name에 정해진 이름으로 표시됩니다.

(게임상 크게 필요없지만 팁정도로 생각해주세요.)
우리는 이 name레벨 1~5까지 이름을 붙여 놓겠습니다.


2. Wave 동작 구현

먼저 우리가 이 WaveData를 어떻게 활용할껀지에 대해서 설명을 좀 하는편이 좋을것 같네요.

1. 우선 게임이 시작되면, GameManager 에서 waveDataIndex 를 '0'으로 셋팅합니다.

2. waveDataIndex번째 WaveData를 가져와서 현재 WaveDataList에 추가하고, 다음 wave까지의 딜레이를 셋팅해줍니다.

2-1. GameManagerUpdate때마다 WaveDataList에 추가되어있는 WaveData의 정보를 업데이트 해주고, WaveData는 각각 정보를 업데이트 하다가 유닛을 생성해야할 시간이 되면 GameManager에게 유닛을 추가하라고 요청합니다.

2-2. WaveData는 자기자신을 업데이트 며 총 생성할 유닛을 모두 생성하면 자신을 GameManager에게 자신을 제외하도록 요청합니다.

3. GameManager에서 다음 웨이브 시작시간이 되면, waveDataIndex를 증가 시키고, 2번작업으로 돌아갑니다.


위에 설명한것처럼 GameManager.cs 에 추가 작업을 하겠습니다.

GameManager.cs



그림처럼 GameManager.cs파일에 waveData에 대한 코딩을 추가해줍니다.
isGameStart 는 "게임시작" 버튼을 눌렀을때 true로 바뀌고, false일동안에는 시간체크를 하지않아서 다음 웨이브로 넘어가지 않습니다.

waveIndex는 현재 스테이지 레벨입니다.
waveDataArray는 스테이지 웨이브 데이터로, 각 스테이지의 난이도를 배열로 가지고 있습니다. 위에서 시리얼라이즈 화 시켰기때문에 인스팩터에서 게임을 하면서 난이도를 조절할수 있습니다.

currentWaveDataList는 현재 적용되고 있는 waveData들로, list로 만들어진 이유는, 난이도에 따라 동시에 여러 WaveData가 존재할수 있기때문입니다.
nextWaveDelay와, nextWaveDelayCount는 다음 웨이브데이터 추가를 위한 카운트 값입니다.


그리고, 다음과같이 startGame(), checkNextWave(), updateWaveDataList() 함수를 만들어줍니다.

GameManager.cs

startGame()은 현재는 isStarttrue로 변경하고, 인덱스만 초기화 합니다.

checkNextWave()는 시간을 체크해서 다음 웨이브 시작시간이 되면 웨이브 리스트에 다음웨이브데이터를 복사해서 넣어주고, 다음웨이브 시간 설정, 웨이브 인덱스를 하나 증가시킵니다.
웨이브 데이터를 복사해서 넣는 이유는 복사해서 넣지 않았을경우 게임을 재시작하면 게임도중 변형된 데이터(이미 사용된 데이터들) 이 들어가기때문입니다.

그리고 밑줄친 부분을 보면, 마지막 웨이브가 될경우 다음 웨이브 시간을 음수로 만들어버려서 더이상 갱신되지 않도록 처리해두었습니다.

updateWaveDataList()는 현재 등록되어있는 WaveData들을 갱신시켜줍니다.

OnGUI()에서 addUnit 버튼의 라벨과 기능을 startGame으로 변경합니다.

GameManager.cs


이제 업데이트때 checkNextWave()updateWaveDataList()를 호출해줍니다.

GameManager.cs

여기까지 하면 위에서 설명한 1번, 2번, 2-1번(일부), 3번 내용을 모두 구현한겁니다.

waveDataIndex 를 0으로 초기화 하고,(1번), 다음웨이브데이터를 넣어주고, 다음웨이브 시간을 설정해주고,(2번), updateWaveDataList() 에선 추가되어있는 모든 웨이브데이터를 업데이트 해주고,(2-1번) 다시 checkNextWave() 에선 다음웨이브 시간이 되면 새로운 웨이브 데이터를 추가해줍니다.(3번)

이젠 WaveData에 자신의 정보를 업데이트 시켜줄 update()함수와, 자신을 복제할 clone()함수를 만들겠습니다.

WaveData.cs


clone() 함수는 상당히 단순합니다. 자신과 같은 데이터 형을 새로 생성해주고, public 으로 선언된 변수에 자신의 변수와 동일하게 셋팅해주면 됩니다.

update()함수는 현재 WaveDataMonoBehaviour 를 상속받은게 아니기 떄문에, 자동으로 호출되지는 않고, 다른 클레스(GameManager.cs)에서 호출을 해줘야합니다.
update()의 마지막에 표시된 부분을 보면, 바로 위에서 repeatTime 을 하나씩 감소 시키며 더이상 반복할일이 없을때, GameManagerremoveWaveData를 호출해서 자기 자신을 제외시켜 버립니다.(3번)
그리고 윗쪽 표시를 보면, 생성시간이 되었을때, onceGenerateUnitCount만큼 유닛을 추가하려고 하는데,(지금은 주석으로만 달려있습니다.)

현재는 유닛을 추가할때, 별도로 hp나, 속도를 설정할만한 함수가 없어서 주석으로 표시 해두었습니다.
이제부터 추가하도록 하겠습니다.

먼저 UnitBase.cs를 열고, setUnit() 이란 함수를 만듭니다.

UnitBase.cs

이 함수는 유닛의 hp, speed, 획득 골드를 설정해줍니다.

GameManager.csaddUnit()함수도 다음과 같이 public 으로 변경하고, 파라미터를 받아올수 있게 수정합니다.

GameManager.cs

유닛을 추가한뒤엔 hp와, speed, 획득 Gold를 유닛에게 셋팅해주면 됩니다.

이제 다시 WaveData.cs 로 가서 update함수에 주석으로 되어있던 addUnit을 구현해보겠습니다.

WaveData.cs

사진처럼 GameManageraddUnit을 각각 파라미터를 넣고 호출해주면 끝납니다.

마지막으로 GameManager 의 인스펙터에 WaveData를 5개 정도 만들고 간단히 설정해서 테스트 해보겠습니다.
인스펙터의 WaveDataArray 항목을 열어서 size에 5 를 입력하고 엔터를 처줍니다.

생성된 5개의 WaveData에 다음처럼 값을 넣어줍니다.(값은 제가 임의로 넣은것이기에, 테스트 할때는 마음껏 변경해보시기 바랍니다.)

이제 플레이버튼을 눌러서 "start Game" 버튼을 눌러보면 마린들이 적당한 간격을 두고 나오는것을 볼수 있습니다.

하지만 잠시뒤엔 다음과 같은 에러문구 볼수 있습니다.

이 에러가 생기는 원인은 바로 GameManager의 removeWaveData() updateWaveDataList() 함수가 원입니다.
updateWaveDataList() 에서 foreach 로 
currentWaveDataList를 검색하는 중에 , removeWaveData가 동작되어 currentWaveDataList의 인덱스가 엉키면서 생기는문제입니다.
당장 보기엔 별문제 없어보이지만, Error는 Error 이니까 해결하고 가도록 하겠습니다.

이를 해결하기 위해서 바로 currentWaveDataList에서 WaveData를 삭제 하지 않고 다른방법을 사용하겠습니다.
currentWaveDataList에서 바로 삭제하지 않고 , 삭제할 WaveData를 따로 저장했다가 
currentWaveDataList 업데이트가 끝나면 지울 리스트를 한번에 삭제하도록 합니다.

먼저 지울 리스트를 생성하고, removeWaveData 함수에서 currentWaveDataList에서 삭제 대신에 지울 리스트에 추가합니다.

GameManager.cs

그리고 updateWaveDataList() 에서 모든 업데이트가 끝난뒤 아래처럼 제거 대상 데이터들을 삭제해줍니다.

GameManager.cs

이제 다시 플레이버튼을 누르고 테스트 해보면 더이상 에러는 보이지 않습니다.

3. 추가 정보 및 스킵 버튼

지금 상태로 게임을 해보면 다음 다음웨이브까지 얼마나 남았는지 알수가 없고, 미리 유닛을 모두 제거한 상태라면, 빨리 다음웨이브를 진행하고 싶어집니다.

그래서 이번엔 간단하게 다음웨이브까지 남은 시간을 표시해주고, skip버튼을 만들어보겠습니다.

먼저 다음 웨이브까지 시간을 표시하겠습니다.

GameManager.cs

처음 if문을 보면, isGameStart false인경우(아직 게임이 시작되지 않음)는 이전처럼 StartGame버튼을 노출합니다.
isGameStart true 가 될경우(이미 게임이 시작된경우)에는 nextWave라는 skip버튼을 노출합니다.
skip버튼에는 nextWaveDelaycount값을 동일하게 만들어주면,  checkNextWave() 에서 자연스럽게 다음 웨이브를 시작하게 됩니다.

아랫쪽 표시한부분을 보면, 아주 간단하게 다음 웨이브의 이름과, 남은시간을 표시해주게 되어있습니다.
크게 복잡한 내용은 없고 string.Format에서 "F2"라는 옵션은 "고정 소수점" 을 표시하는 옵션으로 뒤에 2 가 붙었으니 항상 소수점 2째 자리까지 표시해줍니다.

게임을 시작해보면 다음과 같이 정보가 보입니다.


4. 소스 파일

오늘 작업한 소스파일들을 올려드립니다.
잘안되거나 이해안가는 부분이 있으면 참고 하시기 바랍니다.


WaveData.cs


using System;
using UnityEngine;

[System.Serializable]
public class WaveData
{
	public string name = "level_";
	
	public float unitHP = 0;
	public float unitSpeed = 0;
	public int unitGold = 0;
	public int onceGenerateUnitCount = 1;
	public float generateDelay = 1.0f;
	private float generateCount = 0;
	
	public int repeatTime = 10;
	public float nextWaveDelay = 30.0f;
	public WaveData clone()
	{
		WaveData retWd = new WaveData();
		retWd.unitHP = unitHP;
		retWd.unitSpeed = unitSpeed;
		retWd.unitGold = unitGold;
		retWd.onceGenerateUnitCount = onceGenerateUnitCount;
		retWd.generateDelay = generateDelay;
		retWd.generateCount = generateDelay;
		retWd.repeatTime = repeatTime;
		retWd.nextWaveDelay = nextWaveDelay;
		return retWd;
	}
	public void update()
	{
		generateCount += Time.deltaTime;
		if(generateDelay <= generateCount)
		{
			generateCount -= generateDelay;
			for(int i = 0; i < onceGenerateUnitCount; i++)
			{
				GameManager.instance.addUnit(unitHP, unitSpeed, unitGold);
			}
			repeatTime--;
			if(repeatTime<=0)GameManager.instance.removeWaveData(this);
		}
	}
}


GameManager.cs


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using common;

public class GameManager : MonoBehaviour {
	private static GameManager _instance = null;
	public static GameManager instance
	{
		get{
			if(_instance == null)Debug.LogError("GameManager is NULL");
			return _instance;
		}
	}

	void Awake()
	{
		_instance = this;
		init();
	}
	//=========================================
	public float cellSize = 40.0f;
	public int[,] wallMap = 
	{{1, 1, 1, 1, 1, 11, 11, 11, 1, 1, 1, 1, 1},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{1, 1, 1, 1, 1, 111, 111, 111, 1, 1, 1, 1, 1}};
	[HideInInspector]
	public List<UnitBase> unitList = new List<UnitBase>();
	[HideInInspector]
	public Dictionary<Point, TowerBase> towerDic = new Dictionary<Point, TowerBase>();
	public BgCellDisplayer bgGrid;
	public Transform unit_field;
	public UnitBase unit_marine;
	public TowerBase tower;
	public TowerUIMenu towerMenu;
	
	public int startedGold = 100;
	public int currentGold = 0;

	public Camera mainCam;

	private bool isGameStart = false;
	public int waveIndex = 0;
	//-----------waveData
	public WaveData[] waveDataArray;
	private List<WaveData> currentWaveDataList = new List<WaveData>();
	private List<WaveData> removeWaveDataList = new List<WaveData>();
	private float nextWaveDelay = 0f;
	private float nextWaveDelayCount = 0;
	
	public void addWaveData(WaveData wd)
	{
		currentWaveDataList.Add(wd);
	}
	public void removeWaveData(WaveData wd)
	{
		removeWaveDataList.Add (wd);
	}


	private void init()
	{
		unitList.Clear();
		towerDic.Clear();
		initPathFinder();

		currentGold = startedGold;
	}

	public void addUnit(float hp, float speed, int gold)
	{
		UnitBase unit = Instantiate(unit_marine) as UnitBase;
		unitList.Add(unit);
		unit.setUnit(hp, speed, gold);
		unit.transform.parent = unit_field;
		Point startPoint = getStartPoint();
		unit.transform.localPosition = new Vector3(startPoint.x * cellSize + cellSize/2.0f, -startPoint.y * cellSize - cellSize/2.0f);
		unit.setStartPoint(startPoint);
		
	}

	void addTower(Point p)
	{
		
		TowerBase tw = Instantiate(tower) as TowerBase;
		tw.transform.parent = unit_field;
		tw.transform.localPosition = new Vector3(p.x * cellSize + cellSize/2.0f, -p.y * cellSize - cellSize);
		towerDic.Add(p, tw);
		
		useGold(tw.buildPrice);
	}
	public void addGold(int g)
	{
		currentGold += g;
	}
	public void useGold(int g)
	{
		currentGold -= g;
	}
	public bool checkGold(int g)
	{
		return (currentGold>=g);
	}
	public void removeUnit(UnitBase ub)
	{
		unitList.Remove(ub);
	}
	void researchPathUnits()
	{
		foreach(UnitBase ub in unitList)
		{
			if(ub!=null)
			{
				ub.getPath();
			}
		}
	}
	Point getStartPoint()
	{
		List<Point> startPointList = getStartPointList();
		if(startPointList.Count == 0)
		{
			Debug.LogError("Not Found Start Position");
			return null;
		}
		int ranIdx = Random.Range(0,startPointList.Count);
		return startPointList[ranIdx];
	}
	List<Point> getStartPointList()
	{
		//check startPoints
		List<Point> startPointList = new List<Point>();
		int _w = wallMap.GetLength(0);
		int _h = wallMap.GetLength(1);
		int x,y;
		for (x = 0; x < _w; x++)
		{
			for(y = 0; y < _h; y++)
			{
				if(wallMap[x,y] >= 10 && wallMap[x,y] <= 100)
				{
					startPointList.Add(new Point(x,y));
				}
			}
		}
		return startPointList;
	}
	public void initPathFinder()
	{
		PathFinder.instance.setMapData(wallMap);
	}
	void OnGUI()
	{
		if(!isGameStart)
		{
			if(GUI.Button( new Rect( 10, 10, 100, 40), "Start Game"))
			{
				startGame();
			}
		}
		else 
		{
			if(GUI.Button( new Rect( 10, 10, 100, 40), "nextWave!!"))
			{
				nextWaveDelay = nextWaveDelayCount;
			}
		}
		GUI.Label(new Rect( (Screen.width - 200)/2.0f, 10, 200, 50), "GOLD : "+currentGold);
		if(waveIndex < waveDataArray.Length)
			GUI.Label(new Rect( (Screen.width - 200), 10, 200, 50), 
			          string.Format("NextWave [{0}] : {1:F2}",waveDataArray[waveIndex].name,(nextWaveDelay-nextWaveDelayCount)));
	}
	void Update()
	{
		checkNextWave();
		//updateWaveDatas
		updateWaveDataList();

		if(Input.GetMouseButtonDown(0) && !towerMenu.isShow) 
		{
			Vector2 pos = Input.mousePosition;
			Vector3 mouseP = mainCam.ScreenToWorldPoint(pos) - unit_field.TransformPoint(Vector3.zero);
			Point myPos = new Point((int)(mouseP.x/cellSize), -(int)(mouseP.y/cellSize));
			if(towerDic.ContainsKey(myPos))
			{
				showMenu(towerDic[myPos]);
			}
			else 
			{
				buildTower(myPos);
			}
		}
	}
	void startGame()
	{
		waveIndex = 0;
		isGameStart = true;
	}

	void checkNextWave()
	{
		if(!isGameStart)return;
		if(nextWaveDelay<0)return;
		nextWaveDelayCount += Time.deltaTime;
		if(nextWaveDelay<nextWaveDelayCount)
		{
			nextWaveDelayCount = 0;
			if(waveIndex >= waveDataArray.Length)
			{
				nextWaveDelay = -1;
			}
			else
			{
				addWaveData(waveDataArray[waveIndex].clone());
				nextWaveDelay = waveDataArray[waveIndex].nextWaveDelay;
				waveIndex++;
			}
		}
	}

	void updateWaveDataList()
	{
		foreach( WaveData wd in currentWaveDataList)
		{
			wd.update();
		}
		foreach( WaveData wd in removeWaveDataList)
		{
			if(currentWaveDataList.Contains(wd))currentWaveDataList.Remove(wd);
		}
		removeWaveDataList.Clear();
	}

	bool checkReachAble()
	{
		PathFinder.instance.setCheckMode(true);
		foreach(Point sp in getStartPointList())
		{
			if(PathFinder.instance.getPath(sp,100+wallMap[sp.x,sp.y]) == null)
			{
				Debug.Log("StartPoint Path NULL");
				return false;
			}
		}
		foreach(UnitBase unit in unitList)
		{
			if(!unit.getPath())
			{
				Debug.Log("Unit Path NULL");
				return false;
			}
		}
		PathFinder.instance.setPath();
		return true;
	}
	void buildTower(Point p)
	{
		if(p.x<0||p.y<0 || p.x >= wallMap.GetLength(0) || p.y >= wallMap.GetLength(1))return;
		int prevIndex = wallMap[p.x, p.y];
		
		if(wallMap[p.x, p.y] == 0)wallMap[p.x, p.y] = 2;
		else return;
			
		if(checkGold(tower.buildPrice) && checkReachAble())
		{
			bgGrid.refreshDisplay();
			researchPathUnits();
			addTower(p);
		}
		else 
		{
			wallMap[p.x, p.y] = prevIndex;
		}
		PathFinder.instance.setCheckMode(false);
	}
	public void sellTower(TowerBase tw)
	{
		if(!towerDic.ContainsValue(tw))return;
		addGold(tw.totalPrice/2);
		removeTower(tw);
	}
	public void removeTower(TowerBase tw)
	{
		if(!towerDic.ContainsValue(tw))return;
		foreach(Point keyP in towerDic.Keys)
		{
			if(towerDic[keyP] == tw)
			{
				if(keyP.x<0||keyP.y<0 || keyP.x >= wallMap.GetLength(0) || keyP.y >= wallMap.GetLength(1))return;
				wallMap[keyP.x, keyP.y] = 0;
				towerDic.Remove(keyP);
				break;
			}
		}
		Destroy(tw.gameObject);
		bgGrid.refreshDisplay();
		checkReachAble();
		PathFinder.instance.setPath();
		PathFinder.instance.setCheckMode(false);
	}
	void showMenu(TowerBase tw)
	{
		towerMenu.showMenu(tw);
	}
}



UnitBase.cs


using UnityEngine;
using System.Collections;
using common;

public class UnitBase : MonoBehaviour {
	public float maxHp = 100;
	public float curHp = 100;
	public int gainGold = 5;

	public float moveSpeed = 1.0f;
	public tk2dSpriteAnimator spr;

	private enum CHAR_ANI {UP, DOWN, LEFT, RIGHT, DESTROY};
	private string[] charAniStr = new string[]{"walk_up","walk_down","walk_left","walk_right","destroy"};
	// Use this for initialization
	void Start () {
		curHp = maxHp;
	}
	private bool isMoveAble = false;
	// Update is called once per frame
	void Update () {
		if(!isMoveAble)return;
		float cellSize = GameManager.instance.cellSize;
		float _speed = cellSize * Time.deltaTime * moveSpeed;
		float rx = (nextPoint.x * cellSize + cellSize/2.0f) - this.transform.localPosition.x;
		float ry = (-nextPoint.y * cellSize - cellSize/2.0f) - this.transform.localPosition.y;
		float dx = _speed * makeNomal(rx);
		float dy = _speed * makeNomal(ry);
		bool isCloseX = false;
		bool isCloseY = false;
		if(Mathf.Abs(dx)>Mathf.Abs(rx)||dx==0){dx = rx;isCloseX = true;}
		if(Mathf.Abs(dy)>Mathf.Abs(ry)||dy==0){dy = ry;isCloseY = true;}
		this.transform.localPosition += new Vector3(dx , dy , 0);
		spr.Sprite.SortingOrder = -(int)this.transform.localPosition.y;
		if(isCloseX && isCloseY)
		{
			if(pathArr.Length <= pathIndex + 1)
			{
				isMoveAble = false;
				GameManager.instance.removeUnit(this);
				Destroy(this.gameObject);
				return;
			}
			setNextPoint();
		}
	}

	public void setUnit(float hp, float speed, int gold) {
		curHp = maxHp = hp;
		moveSpeed = speed;
		gainGold = gold;
	}

	int makeNomal(float f)
	{
		float k = 0.1f;
		if(f>k)return 1;
		else if(f<-k)return -1;
		else return 0;
	}
	private Point[] pathArr;
	private Point startPoint;
	private Point nextPoint;
	private int pathIndex =0;
	public void setStartPoint(Point p)
	{
		startPoint = p;
		getPath();
		nextPoint = pathArr[pathIndex];
		showCharDir();
		isMoveAble = true;
	}
	public bool getPath()
	{
		float cellSize = GameManager.instance.cellSize;
		startPoint = new Point((int)(this.transform.localPosition.x/cellSize),-(int)(this.transform.localPosition.y/cellSize) );
		int wallMapIndex = GameManager.instance.wallMap[startPoint.x,startPoint.y];
		if(wallMapIndex > 0 && wallMapIndex < 10)return true;

		Point[] pArr = PathFinder.instance.getPath(startPoint, 111);
		if(pArr == null){Debug.Log("NULL path");return false;}
		pathArr = pArr;

		if(nextPoint != null && pathArr.Length > 1 && nextPoint.isEqual(pathArr[1]))pathIndex = 1;
		else pathIndex = 0;
		nextPoint = pathArr[pathIndex];
		showCharDir();
		return true;
	}
	public void attackMe(float dmg)
	{
		curHp -= dmg;
		if(curHp < 0)
		{
			isMoveAble = false;
			spr.Play ("destroy");
			GameManager.instance.addGold(gainGold);
			GameManager.instance.removeUnit(this);
			spr.AnimationCompleted = unitDestoryAniComplete;
		}
	}
	private void unitDestoryAniComplete(tk2dSpriteAnimator sprite, tk2dSpriteAnimationClip clip)
	{
		Destroy(this.gameObject);
	}
	private void setNextPoint()
	{
		startPoint = nextPoint;
		pathIndex++;
		nextPoint = pathArr[pathIndex];
		showCharDir();
	}
	private void showCharDir()
	{
		
		float cellSize = GameManager.instance.cellSize;
		float nx = (nextPoint.x * cellSize + cellSize/2.0f);
		float ny = (-nextPoint.y * cellSize - cellSize/2.0f);
		if(this.transform.localPosition.x<nx)
			spr.Play (charAniStr[(int)CHAR_ANI.RIGHT]);
		else if(this.transform.localPosition.x>nx)
			spr.Play (charAniStr[(int)CHAR_ANI.LEFT]);
		else if(this.transform.localPosition.y<ny)
			spr.Play (charAniStr[(int)CHAR_ANI.UP]);
		else if(this.transform.localPosition.y>ny)
			spr.Play (charAniStr[(int)CHAR_ANI.DOWN]);
		spr.ClipFps *= moveSpeed;
	}
}






신고
posted by andwhy