Unity3D/TowerDefence 2014.10.11 18:36

마지막 시간이네요.

오늘 할건 지금까지 한거에 비하면 정말 간단한것입니다.

점수 보여주기와, GameOver, 그리고 Game 재시작.

크게 이 세가지입니다.


1. Score

타워 디펜스 게임에서는 점수가 크게 의미는 없을것 같습니다. 퍼즐게임이나 기록갱신 게임들에서는 얼마나 많은점수를 내느냐가 가장 중요한점이지만, 디펜스게임의 경우는 스테이지를 몇까지 클리어 하는지가 더 중요하니까요...

하지만, 점수가 없으면 뭔가 좀 허전해서...넣어보겠습니다.(타워디펜스도 기획에따라 점수가 정말 중요해질수도 있으니까요..)

우선 저희는 유닛을 죽이고 얻는 골드를 점수로 간주하도록 하겠습니다. 유닛마다 점수를 따로 설정하고, 타워를 짓거나 업그레이드 할때도 점수를 추가해줄수 있겠지만, 가장 간단한 방법으로 얻어지는 골드를 점수로 환산하도록 하겠습니다.


먼저 게임 점수를 저장할 gameScore변수를 만듭니다.

GameManager.cs

외부에서 게임점수를 추가할수 있도록 addScore()라는 함수도 만들어줍니다.

GameManager.cs

게임이 시작될때는 점수를 0점으로 초기화 시켜줍니다.

GameManager.cs

이제 화면에 점수를 표시해주기위해서, OnGUI() 안에 Gold를 표기한 다음줄에 표시하도록 코드를 수정해줍니다.

GameManager.cs

GameManager.cs 에서 해야할일은 끝났고, 이젠 유닛을 죽일때 GameManager addScore를 호출해주기만 하면됩니다.

처음에 말한것처럼, 유닛을 죽이고 골드를 획득할때(addGold) 획득한골드만큼 점수를 올려줄 것입니다. UnitBase에서 addGold해주는 부분을 찾아서 바로 밑에 addScore도 해주면 됩니다.

UnitBase.cs


이제 화면을 보면 아래처럼 Gold표시 아래에 Score도 표시됩니다.


2. GameOver

이번엔 GameOver를 만들어보겠습니다.
우리게임은 유닛이 목표점까지 못오도록 막는게 목적입니다. 하지만 유닛을 막지 못하고, 목표점까지 유닛이 침범한다면, 베이스캠프(?) 의 체력을 하나씩 줄어들고, 체력이 0이되면 게임은 끝납니다.

먼저 뭐부터 해야할까요?

베이스캠프(?) 의 최대 체력과 현재 체력 값을 만들어줍니다. 인스펙터에서 손쉽게 수정가능하도록 public 으로 선언하겠습니다.
그리고 현재 게임오버 상태인지 아닌지를 체크하는 isGameOver 값도 하나 추가 하겠습니다.

GameManager.cs

다음으론 유닛이 도착했을때, 현재 체력을 하나씩 깎고, 체력이 0인지 체크하는 reachUnit() 함수를 추가합니다.

GameManager.cs

기존에는 유닛이 목적지에 도착하면 자기 자신을 Destroy시키고 GameManagerremoveUnit() 를 호출해줬는데, 이제는 현재 체력을 하나씩 깍고, 체력이 0이 되면 게임 오버상태로 변경하기 위해서 reachUnit() 이란 함수를 새로 만들었습니다.

isGameOverisGameStart만 으로 게임진행상태를 판단하기 어려울 경우를 위해서 추가하였습니다.
GameOver 상태에선 새로운 타워를 짓거나, 타워를 제거하는일을 할수는 없습니다. 하지만, wave를 시작하기전에 미리 몇몇 타워를 짓고 싶다면 isGameStart 라는 변수 하나만으론 판단하기 힘듭니다.


다음으론 게임시작시, 베이스캠프의 체력을 초기화 시켜주고, isGameOverfalse로 변경시켜줍니다.

GameManager.cs


이제 게임이 시작되면, 게임상태는 게임은 스타트 되었고 게임오버는 되지 않은 상태가 됩니다.
그리고 게임점수는 0점 베이스캠프의 체력은 최대 채력(10) 이 되게 될겁니다.

이번엔 다시 UnitBase.cs 로 넘어가서, 유닛이 목적지 까지 도착할때 GameManagerreachUnit()함수를 호출하도록 하겠습니다.

UnitBase.cs

위의 그림처럼 Update함수안에 목적지에 도착했는지 판단하는 부분이 있는데, 기존의 removeUnit()을 호출하는것을 reachUnit()을 호출하도록 변경해줍니다.


이상태로 게임을 한번 진행해서, 정상적으로 게임오버가 되는지 돌려보겠습니다.
정상적으로 게임오버가 된다면, 10마리 이상의 유닛을 놓치면, GAMEOVER란 메시지가 표시되고 더이상 게임이 진행되면 안됩니다.




실제로 돌려보면.....
GAMEOVER 로그가 여러번 찍히고, Life는 0 이 되어도 계속 내려갑니다.

이문제는 게임이 끝났지만, 유닛과, 타워가 계속 동작하는게 문제네요.

UnitBase.csTowerBase.cs 파일에 GameOver인지를 검사하는 코드를 넣어보도록 하겠습니다.

UnitBase.cs

Update중에 GameManagerisGameOver  상태이면 스프라이트 애니메이션을 멈추고 더이상 아무것도 하지 않습니다.
만일 spr.Stop()구문을 빼버리면 유닛이 이동하진 않지만 제자리 걸음을 하고 있을껍니다.

TowerBase.cs


TowerBase 에서도, GameOver상태이면 바로 리턴시켜 버립니다. 이러면 유닛을 바라보거나, 공격하는 행동은 더이상 하지 않습니다.


그리고 한가지 놓친곳이 있는데, GameManager 에서 다음 Wave를 체크하는 부분과, 현재 WaveData들을 업데이트 해주는부분도, GameOver상태일때는 동작해선 안됩니다.

아래 그림처럼 각각 함수 윗부분에 게임중이 아닐경우엔 return 시켜 더이상 동작하지 않도록 합니다.

GameManager.cs


이제 게임을 테스트 해보면 게임오버까지 정상적으로 동작되는것을 볼수 있습니다.


3. 게임재시작.

게임이 끝난뒤 게임을 다시 시작할수 있게 만들어주면 됩니다.
게임의 점수나, 획득 골드를 처음상태로 바꿔주고, 게임화면에 나와있는 유닛들과, 타워들을 제거해준뒤 다시 게임을 시작하면됩니다.

화면의 유닛과, 타워를 모두 제거하는 함수를 만듭니다.

GameManager.cs


다음으론 게임초기화 함수를 만들겠습니다.

GameManager.cs

gameInit()은 방금 만든 clearTowers, clearUnits 함수와 현재 wave데이터를 초기화 하고, 기타 그외의 점수, 골드 등의 게임데이터들을 초기화 해줍니다.
그리고 기존의 startGame()isGameStart를 제외한 다른 초기화코드를 제거했는데,
이는 게임이 종료되었을때, "확인"버튼을 누르면 게임이 초기화 되고, 이상태에서 미리 타워들을 배치할수 있도록 하기위해서 gameInit()때만 초기화를 해줍니다.
(startGame()에서도 초기화 시키게되면 gold점수등이 잘못초기화 될수도 있습니다.)

마지막으로 게임오버시에 화면중앙에 "GameOver" 라는 버튼을 만들고, 버튼을 누르면 초기화 시키도록 OnGUI에 코드를 추가해줍니다.

GameManager.cs


이제 게임오버가 되면 다음과 같이 화면 중앙에 GameOver 버튼이 보이고, 버튼을 누르면 기존의 유닛,타워들이 사라지고, 초기화 되는것을 볼수 있습니다.



이번 강의는 여기까지입니다.

그동안 힘등 강의 따라와주셔서 정말 감사합니다.

마지막으로 게임코드 공유 하고 끝내도록 하겠습니다.


4. 게임코드


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 int maxLife = 10;
	public int currentLife = 10;
	public bool isGameOver = false;

	public Camera mainCam;

	private int gameScore = 0;
	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 addScore(int s)
	{
		gameScore += s;
	}
	public void useGold(int g)
	{
		currentGold -= g;
	}
	public bool checkGold(int g)
	{
		return (currentGold>=g);
	}
	public void reachUnit(UnitBase ub)
	{
		currentLife --;
		removeUnit(ub);
		if(currentLife<=0)
		{
			Debug.Log("GAMEOVER");
			isGameStart = false;
			isGameOver = true;
		}
	}
	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(isGameOver)
		{
			if(GUI.Button( new Rect( (Screen.width - 200)/2.0f, (Screen.height - 40)/2.0f, 100, 40), "GameOver"))
			{
				
				gameInit();
			}
		}
		else 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 - 100)/2.0f, 10, 100, 50), "GOLD : "+currentGold+" \n"+"Score : "+gameScore);
		if(waveIndex < waveDataArray.Length)
			GUI.Label(new Rect( (Screen.width - 200), 50, 200, 50), 
			          string.Format("NextWave [{0}] : {1:F2}",waveDataArray[waveIndex].name,(nextWaveDelay-nextWaveDelayCount)));
		GUI.Label(new Rect( (Screen.width - 100), 10, 100, 50), string.Format("Life {0} / {1}",currentLife, maxLife));
	}
	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 gameInit()
	{
		gameScore = 0;
		waveIndex = 0;
		currentGold = startedGold;
		isGameOver = false;
		currentLife = maxLife;
		nextWaveDelay = 0f;
		nextWaveDelayCount = 0;

		clearTowers();
		clearUnits();
		currentWaveDataList.Clear();

	}
	void startGame()
	{
		isGameStart = true;
	}
	private void clearTowers()
	{
		foreach(Point keyP in towerDic.Keys)
		{
			if(keyP.x<0||keyP.y<0 || keyP.x >= wallMap.GetLength(0) || keyP.y >= wallMap.GetLength(1))return;
			wallMap[keyP.x, keyP.y] = 0;
			Destroy(towerDic[keyP].gameObject);
		}
		towerDic.Clear();
		bgGrid.refreshDisplay();
		PathFinder.instance.setPath();
		PathFinder.instance.setCheckMode(false);
	}
	private void clearUnits()
	{
		foreach(UnitBase unit in unitList)
		{
			Destroy( unit.gameObject);
		}
		unitList.Clear();
	}
	void checkNextWave()
	{
		if(!isGameStart||isGameOver)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()
	{
		if(!isGameStart||isGameOver)return;
		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;
		if(GameManager.instance.isGameOver){spr.Stop();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);
				GameManager.instance.reachUnit(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.addScore(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;
	}
}


TowerBase.cs


using UnityEngine;
using System.Collections;

public class TowerBase : MonoBehaviour {
	public UnitBase target;
	public float attack = 10.0f;
	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;

	public int totalPrice = 0;
	public int buildPrice = 50;
	private int atkLv = 1;
	private int spdLv = 1;
	private int rngLv = 1;

	public UpgradeInfo[] attackUpgradeInfoArr = new UpgradeInfo[]
	{
		new UpgradeInfo(1, 0, 20), new UpgradeInfo(2, 10, 25), new UpgradeInfo(3, 20, 30), new UpgradeInfo(4, 30, 35), new UpgradeInfo(5, 40, 40)
	};
	public UpgradeInfo[] speedUpgradeInfoArr = new UpgradeInfo[]
	{
		new UpgradeInfo(1, 0, 2.5f), new UpgradeInfo(2, 10, 2.1f), new UpgradeInfo(3, 20, 1.7f), new UpgradeInfo(4, 30, 1.3f), new UpgradeInfo(5, 40, .9f)
	};
	public UpgradeInfo[] rangeUpgradeInfoArr = new UpgradeInfo[]
	{
		new UpgradeInfo(1, 0, 100), new UpgradeInfo(2, 10, 130), new UpgradeInfo(3, 20, 160), new UpgradeInfo(4, 30, 190), new UpgradeInfo(5, 40, 220)
	};
	private UpgradeInfo getInfo(int lv, UpgradeInfo[] infoArr)
	{
		foreach(UpgradeInfo ui in infoArr)
		{
			if(ui.level == lv)return ui;
		}
		return null;
	}
	public int getLvupAtkPrice() { return getLvupAtkPrice(atkLv + 1); }
	public int getLvupAtkPrice(int lv)
	{
		UpgradeInfo info = getInfo(lv, attackUpgradeInfoArr);
		if(info== null)return -1;
		return info.price;
	}
	public int getLvupSpdPrice() { return getLvupSpdPrice(spdLv + 1); }
	public int getLvupSpdPrice(int lv)
	{
		UpgradeInfo info = getInfo(lv, speedUpgradeInfoArr);
		if(info== null)return -1;
		return info.price;
	}
	public int getLvupRngPrice() { return getLvupRngPrice(rngLv + 1); }
	public int getLvupRngPrice(int lv)
	{
		UpgradeInfo info = getInfo(lv, rangeUpgradeInfoArr);
		if(info== null)return -1;
		return info.price;
	}
	public void upgradeAtk(){upgradeAtk(atkLv+1);}
	public void upgradeAtk(int lv)
	{
		UpgradeInfo info = getInfo(lv, attackUpgradeInfoArr);
		if(info== null)return;
		if(!GameManager.instance.checkGold(info.price))return;
		GameManager.instance.useGold(info.price);
		totalPrice += info.price;
		atkLv = info.level;
		attack = info.value;
	}
	public void upgradeSpd(){upgradeSpd(spdLv+1);}
	public void upgradeSpd(int lv)
	{
		UpgradeInfo info = getInfo(lv, speedUpgradeInfoArr);
		if(info== null)return;
		if(!GameManager.instance.checkGold(info.price))return;
		GameManager.instance.useGold(info.price);
		totalPrice += info.price;
		spdLv = info.level;
		shotDelay = info.value;
	}
	public void upgradeRng(){upgradeRng(rngLv+1);}
	public void upgradeRng(int lv)
	{
		UpgradeInfo info = getInfo(lv, rangeUpgradeInfoArr);
		if(info== null)return;
		if(!GameManager.instance.checkGold(info.price))return;
		GameManager.instance.useGold(info.price);
		totalPrice += info.price;
		rngLv = info.level;
		range = info.value;
	}
	// Use this for initialization
	void Start () {
		spr = this.GetComponentInChildren<tk2dSprite>();
		init();
	}

	void init()
	{
		totalPrice = buildPrice;
		reloadTime = shotDelay;
		upgradeAtk(1);
		upgradeSpd(1);
		upgradeRng(1);
	}

	// Update is called once per frame
	void Update () {
		if(GameManager.instance.isGameOver)return;
		spr.SortingOrder = -(int)this.transform.localPosition.y;
		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;

		target.attackMe(attack);
	}

	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);
	}

}
[System.Serializable]
public class UpgradeInfo
{
	public int level = 0;
	public int price = 0;
	public float value = 0;
	
	public UpgradeInfo(int _level, int _price, float _value)
	{
		level = _level;
		price = _price;
		value = _value;
	}
}






신고
posted by andwhy