중간에 연휴도 있었고 게임 분석하느라 생각 외로 시간이 너무 빨리 지나갔다.
생각한 결과를 만들진 못했지만 포스팅해 보겠다.
게임을 만들기 위한 작업을 전체를 포스팅하는 게 아니고 처음 접하거나 인상적인 내용만 포스팅하겠다.
목차
- 나만의 몬스터 만들기
- 몬스터 생성하기
- 플레이어의 위치에서 가장 가까운 몬스터에게 미사일 발사
- 몬스터가 벽(플레이어의 hp)을 공격하기
1. 나만의 몬스터 만들기
게임을 만들기 위해 무료 에셋을 사용할 수 있지만 나만의 몬스터를 만들기 위해 piskel 사이트를 이용했다.
생각보다 오래 걸렸는데 역시 본인은 미술 감각이 저질이다...
본인 기준으로 잘 만들었다 생각되는 두 갠데 애석하게도 게임에 적용할 수 없을 것 같다. ㅠㅠ
일단은 이런 몬스터 및 플레이어만 적용할 수 있어서 적용해 봤다.
2. 몬스터 생성하기
이전에 포스팅한 게임과 비슷하게 개발했고 일단 돌아가게 만들기 위해 코드는 대충 만들었다.
본인이 원하는 방법은 라운드 별로 생성할 몬스터의 개수가 정해져 있고 몬스터를 생성할 때 일정 딜레이가 있는 것이다.
public GameObject EnemyPrefab = null;
private int round = 0;
private float roundCooltime = 10.0f;
private float nextRoundCooltime = 0.0f;
private int[] enemyCntByRound = null;
private float enemyGenCooltime = 1f;
void Start () {
enemyCntByRound = new int[]{ 5, 7, 10, 15, 25, 45 };
}
void Update() {
float t = Time.time;
if (enemyCntByRound.Length <= round) {
return;
}
if (nextRoundCooltime <= t) {
nextRoundCooltime = t + roundCooltime;
int enemyCnt = enemyCntByRound[round++];
int i = 0;
while (i < enemyCnt) {
Invoke("genEnemy", enemyGenCooltime * i);
i++;
}
}
}
void genEnemy() {
Instantiate(EnemyPrefab, getInitPosition(), Quaternion.identity);
}
Vector3 getInitPosition() {
return new Vector3(Random.Range (-2.25f, 2.25f), 5.5f, 50);
}
코드를 설명하면 Start 함수에서 라운드 별 몬스터의 개수 정보를 생성하고 Update 함수에서 호출 시점의 시간과 nextRoundCooltime 변수의 값을 비교하여 호출 시점의 시간과 같거나 크면, 호출 시점의 시간과 roundCooltime 변수를 더한 값을 nextRoundCooltime에 저장하고 라운드 별 생성할 몬스터의 개수를 갖고 오며 라운드를 증가시킨다.
그 후 라운드 별 몬스터의 개수만큼 반복하면서 Invoke 함수를 호출하고 몬스터를 생성할 때 지연을 시킬 수 있도록 enemyGenCooltime * i 만큼 지연 시간을 추가한다.
3. 플레이어의 위치에서 가장 가까운 몬스터에게 미사일 발사
개발할 때 이 내용이 제일 어려웠다. 엄청 삽질하다 유니티의 기능을 검색해서 개발했더니 매우 편리했다.
GameObject[] enemyList = GameObject.FindGameObjectsWithTag("Enemy");
int i = 0;
int len = enemyList.Length;
if (len == 0) {
return;
}
float min = 9999.0f;
int enemyIndex = 0;
while (i < len) {
GameObject enemy = enemyList[i];
float dist = Vector3.Distance(enemy.transform.position, prefab.transform.position);
if (min > dist) {
min = dist;
enemyIndex = i;
}
++i;
}
몬스터 Prefab에 Enemy 태그를 추가하여 태그로 생성된 모든 몬스터 오브젝트를 가져온다.
몬스터 오브젝트들을 순회하면서 플레이어와 몬스터의 거리 차이를 Vector3.Distance 함수를 사용하여 구한 후 제일 가까운 몬스터를 찾도록 만들었다.
찾은 몬스터를 스킬의 타깃으로 전달해야 하는데
GameObject skillObj = Instantiate(bullet, prefab.transform.position, Quaternion.identity);
SkillInterface skill = skillObj.GetComponent<SkillInterface>();
skill.setTargetEnemy(enemyList[enemyIndex]);
스킬을 만든 후 setTargetEnemy 함수를 호출하여 전달하도록 만들었다.
이제 생성된 스킬이 몬스터에게 이동하면 끝나는데
void Update () {
if (target == null) {
return;
}
float seed = getSpeed ();
Vector3 targetPosition = target.transform.position - prefab.transform.position;
prefab.transform.Translate(Time.deltaTime * seed * targetPosition.normalized);
}
public int getDamage() {
return 10;
}
public float getSpeed() {
return 5.0f;
}
public void setTargetEnemy(GameObject t) {
target = t;
}
몬스터의 위치에서 스킬의 현재 위치를 뺀 후 스킬을 이동시키는데 normalized를 사용하여 이동 방향을 정한다.
4. 몬 스터가 벽(플레이어의 hp)을 공격하기
기능은 구현했지만 UI를 구현하지 못했다.
Rigidbody와 Box Collider 컴포넌트를 사용하여 몬스터와 벽이 부딪칠 때 이벤트를 받을 수 있도록 만든다.
void Update () {
if (isMove) {
gameObject.transform.Translate(Time.deltaTime * moveSeed * -Vector3.up);
}
if (isAttack) {
float t = Time.time;
if (nextAttackCooltime < t) {
nextAttackCooltime = t + attackCoolTime;
GameObject wallObj = GameObject.FindGameObjectWithTag("Wall");
WallControl wall = wallObj.GetComponent<WallControl>();
wall.attack(attackPower);
}
}
}
void OnTriggerEnter(Collider other) {
if (other.tag == "Wall") {
this.isMove = false;
this.isAttack = true;
}
}
벽과 부딪칠 때 몬스터의 움직임을 멈추고 공격할 수 있도록 OnTriggerEnter 함수에서 상태를 변경한다.
Update 함수에서 isAttack 값이 true면 Wall 태그가 있는 컴포넌트를 찾은 후 attack 함수에 몬스터의 데미지를 파라메터로 넣으며 호출한다.
public Slider hpbar = null;
private float HP = 3000.0f;
private float hp = 3000.0f;
public void attack(float damage) {
if (hp <= 0) {
return;
}
hp -= damage;
Debug.Log(hp / HP);
hpbar.value = hp / HP;
if (hp <= 0) {
//[TODO] Game over
}
}
UI 슬라이더 컴포넌트를 사용하여 hp를 표현하고 몬스터의 데미지만큼 벽의 체력을 깎고 슬라이더 컴포넌트의 값을 변경한다.
포스트는 이걸로 마치고 작업할 때 몬스터가 겹치는 경우 스킬이 겹친 몬스터들에게 충돌이 적용됐는데 Rigidbody 컴포넌트의 Is kinematic 옵션을 비활성화해야 한다.
이번 주에 작업할 목록
- 라운드 UI 표시
- LEVEL UP 시 사용할 팝업
- 팝업에서 스킬을 upgrade 할 수 있도록 기능 추가
엄청 이상하게 만드는 것 같지만 뭔가 재밌고 아이디어가 뿜뿜 한다 ㅎㅎ 어려운 건 당연한 거고... ㅠㅠ