오늘은 스파르타 코딩 클럽 unity 게임 개발 과정 3일차!
오늘도 즐거운 스파르타 코딩!! 부트캠프의 날이다! 벌써 3일차! 와우 시간이 이렇게 빨리 간다... ㄹㅇㅋㅋ
성실한 사람을 연기하면 성실한 사람이 될까 싶어서 오늘도 좀 더 일찍 학습장으로 들어섰다.
오늘 내가 할일을 생각해 보았다.
- 미니 게임 마무리 하기
- 타이틀 만들기
결론 부터 말하자면 오늘 타이틀을 만들지 못했다.
카드게임이 주인 게임이지만 미니게임은 나의 욕심으로 시작된 것이니 다른이에게 최소한의 도움을 받고 싶었지만
내가 잘 모르는 영역도 있고 (사운드 부분 추후 배워놔야 겠다) 코드를 길게 짜기 싫어서 미니게임부분에 꼼수를 사용했다. 이것은 나중에 설명하겠다.
오늘 내가 한일은..
- 미니 게임에서 죽음을 구현하기
- 미니 게임에서 죽으면 베스트 스코어 표시
- 미니 게임에서 화살 날라오게 하기
- 미니 게임에서 화살의 원근감을 나타나게 하기
- 미니 게임에서 화살에 맞으면 대미지입으며 뒤로 물러나게 하기
- 미니 게임에서 현재 시간을 표시하기
- 미니 게임에서 현재 체력을 표시하기
- 미니 게임에서 체력이 0이 되면 게임이 끝나게 하고 Re버튼을 보여주게 하기
- 미니 게임에서 Re 버튼을 누르면 스타트신으로 다시 돌아오게 하기
- 미니 게임에서 시간의 증가에 따라 화살의 속도를 올리기
- 등등..
거의 다.. 아니 전부 미니게임에 관련된 것이다.
이정도면 나의 집착이다. ㅋㅋ
미니 게임에서 사용된 코드를 소개하겠다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class MiniGameManager : MonoBehaviour
{
public GameObject arrow;
public GameObject run;
public GameObject retryBtn;
public Text healthText;
public Text timeTxt;
public Text scoreTxt;
public float currentTime = 0.0f;
public arrow Arrow;
public static MiniGameManager I;
public float health;
public float maxHealth = 100;
public Animator anim;
public float level = 1f;
public float[] nextTime = { 5f, 10f, 15f, 20f, 25f, 30f, 35f, 40f, 60f, 100f, 150f, 210f, 280f, 360f, 450f, 600f };
private void Awake()
{
scoreTxt.text = "BestScore " + PlayerPrefs.GetFloat("bestScore").ToString("N2");
anim = GetComponent<Animator>();
Time.timeScale = 1.0f;
I = this;
health = maxHealth;
InvokeRepeating("makeArrow", 0, 0.1f);
}
void Update()
{
currentTime += Time.deltaTime;
timeTxt.text = currentTime.ToString("N2");
if (health > 0)
{
healthText.text = health.ToString();
}
else
{
gameOver();
healthText.text = "YOU DIED!!!";
}
}
void makeArrow()
{
Instantiate(arrow);
}
public void gameOver()
{
retryBtn.SetActive(true);
if (PlayerPrefs.HasKey("bestScore") == false)
{
PlayerPrefs.SetFloat("bestScore", currentTime);
scoreTxt.text = "BestScore " + PlayerPrefs.GetFloat("bestScore").ToString("N2");
}
else
{
if (PlayerPrefs.GetFloat("bestScore") < currentTime)
{
PlayerPrefs.SetFloat("bestScore", currentTime);
scoreTxt.text = "BestScore " + PlayerPrefs.GetFloat("bestScore").ToString("N2");
}
}
}
}
------------ ----------------------- ----------------------- ----------------------- -----------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class minigame : MonoBehaviour
{
public AudioSource audioSource;
public AudioClip hitSound;
public AudioClip DeadSound;
public Vector2 inputVec;
Rigidbody2D rigid;
public float speed;
float stendingPos;
public Animator anim;
void Start()
{
stendingPos = Mathf.Abs(transform.position.y);
rigid = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void Update()
{
inputVec.x = Input.GetAxis("Horizontal");
inputVec.y = Input.GetAxis("Vertical");
}
//미니게임 플레이어를 이동시킨다.
private void FixedUpdate()
{
float scale = Mathf.Abs(transform.position.y) / stendingPos;
transform.localScale = new Vector3(scale, scale, 1);
Vector2 nextVec = inputVec.normalized * speed * Time.fixedDeltaTime;
rigid.MovePosition(rigid.position + inputVec);
}
void OnCollisionEnter2D(Collision2D coll)
{
if (coll.gameObject.tag == "arrow")
{
knockBoackh();
transform.position = new Vector3(transform.position.x - 5f, transform.position.y, 0); //화살에 맞으면 뒤로 이동 시킴
MiniGameManager.I.health -= 6;
audioSource.PlayOneShot(hitSound);//히트 사운드 재생
Debug.Log(MiniGameManager.I.health);
if(MiniGameManager.I.health <= 0)
{
Dead();
}
}
}
public void Dead()
{
anim.SetBool("isDie", true);
audioSource.PlayOneShot(DeadSound);
Invoke("timeStop", 0.5f);
}
public void knockBoackh()
{
anim.SetTrigger("hit");
audioSource.PlayOneShot(DeadSound);
}
void timeStop()
{
Time.timeScale = 0.0f;
}
}
----------------------- ----------------------- ----------------------- -----------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class miniRetryBtn : MonoBehaviour
{
public AudioClip click;
public AudioSource audioSource;
public void GameStart()
{
audioSource.PlayOneShot(click);
SceneManager.LoadScene("StartScene");
}
}
------------- ------------- ------------- ------------- ------------- ------------- ------------- -------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class arrow : MonoBehaviour
{
float arrowPos;
float arrowScale_x;
float arrowScale_y;
float level;
int levelCount = 0;
void Start()
{
level = MiniGameManager.I.level;
arrowPos = Mathf.Abs(transform.position.y);
arrowScale_x = transform.localScale.x;
arrowScale_y = transform.localScale.y;
float x = 70f;
float y = Random.Range(60f, 110f);
transform.position = new Vector3(x,-y, 0);
}
private void FixedUpdate()
{
//화살의 x값에 따라 삭제함
if (transform.position.x < -70)
{
Destroy(gameObject);
}
float scale = Mathf.Abs(transform.position.y) / arrowPos; // 현재 포지션과 원래 기준이 되는 포지션을 나눈다.
transform.localScale = new Vector3(scale * arrowScale_x, scale * arrowScale_y, 1); // 스케일을 이용해 크기 조절 (나눈 값을 원래 스케일과 곱한다.)
LevelUp();
}
//시간에 따라 화살속도 레벨을 올린다.
void LevelUp()
{
if (levelCount > MiniGameManager.I.nextTime.Length - 1) levelCount = MiniGameManager.I.nextTime.Length - 1; // 졸라 초고수가 나올수도 있으니 이렇게 버그안나게 수정
//currentTime이 MiniGameManager.I.nextTime[levelCount]보다 높으면 레벨이 상승한다. 그다음 레벨카운트가 상승 레벨카운트가 상승하면 목표시간이 바뀐다.
if (MiniGameManager.I.currentTime > MiniGameManager.I.nextTime[levelCount])
{
level += (float)levelCount;
transform.position -= new Vector3(level, 0, 0);
levelCount++;
Debug.Log(level);
}
else transform.position -= new Vector3(level, 0, 0);
}
void OnCollisionEnter2D(Collision2D coll)
{
if (coll.gameObject.tag == "minirun")
{
Destroy(gameObject);
}
}
}
미니게임을 위한 스크립트의 코드를 모두 올렸보았다.
설명할때 헷갈리지 않게 스크립트를 표시하겠다.
완벽한 코드는 아니지만 흥미로운 코드가 있다.
- 미니 게임에서 화살의 원근감을 나타나게 하기
arrow .cs
float arrowPos;
float arrowScale_x;
float arrowScale_y;
먼저 화살의 포지션과 스케일에 사용할 변수를 선언한다.
arrowPos = Mathf.Abs(transform.position.y);
arrowScale_x = transform.localScale.x;
arrowScale_y = transform.localScale.y;
스타트에 포지션과 스케일을 초기화 시킨다. 이것은 스타트에서 시작된 것이라 랜덤으로 생성되는 화살의 포지션이 아닌 프리펩에서 작성된 포지션이다.
float scale = Mathf.Abs(transform.position.y) / arrowPos; // 현재 포지션과 원래 기준이 되는 포지션을 나눈다.
transform.localScale = new Vector3(scale * arrowScale_x, scale * arrowScale_y, 1); // 스케일을 이용해 크기 조절 (나눈 값을 원래 스케일과 곱한다.)
FixedUpdate()에 초기화한 시킨 포지션과 스케일을 대입해서 y축에 따라 스케일을 변화시켜 원근감을 만들어낸다.
이렇게 하면 랜덤으로 소환되는 화살의 y의 위치를 처음 프리펩에서 작성된 y축 포지션 값으로 나눠 스케일에 필요한 값을 구한다.
그리고 벡터값엔 구한 스케일에 기존의 스케일 x값과 y값을 곱하면 드디어 원근감을 만들어 낼 수 있다.
이부분은 앞서 작성한 원근감을 만들어보자 ( 캐릭터 움직임에 원근감을 넣어보자! (tistory.com)) 에서 언급한적 있는데 좀 더 보완한 코드다.
물론 더 좋은 코드가 있겠지만 내가 스스로 생각해낸 코드라서 애정이 가는 느낌이다.
혹시 고수가 내 글을 본다면 이 코드에 대해 좀 더 보완된 코드를 알려줬으면 하는 바람이 있다.
또 다음으론..난이도를 올리는 것이다.
내가 바보라서 그런지 몰라도 미니게임매니저에서 InvokeRepeating("makeArrow", 0, 0.1f); 이코드를 이용해
화살의 갯수를 늘리는 방향으로 가고 싶었지만 어떻게 해야.. 할지 몰라서 (if문이 안먹혔다.. ) 다른 방법으로 난이도를 올리는 꼼수를 생각했다 그것이 바로..
- 미니 게임에서 시간의 증가에 따라 화살의 속도를 올리기
MiniGameManager.cs
public float currentTime = 0.0f;
public float level = 1f;
public float[] nextTime = { 5f, 10f, 15f, 20f, 25f, 30f, 35f, 40f, 60f, 100f, 150f, 210f, 280f, 360f, 450f, 600f };
void Update()
{
currentTime += Time.deltaTime;
.....
이 부분은 미니게임매니저에서 작성한 부분이다.
float 리스트를 만들어 그곳에 난이도를 높힐 구간을 정한다.
개인적으로 내가 만든 게임을 플레이 한 결과 30초 이상 플레이하기 힘들 정도의 난이도라 600초까지 갈 수 있을까 싶다.
arrow .cs
float level;
int levelCount = 0;
난이도에 필요한 변수를 준비한다.
level = MiniGameManager.I.level;
변수를 초기화시키고
void LevelUp()
{
if (levelCount > MiniGameManager.I.nextTime.Length - 1) levelCount = MiniGameManager.I.nextTime.Length - 1; // 졸라 초고수가 나올수도 있으니 이렇게 버그안나게 수정
//currentTime이 MiniGameManager.I.nextTime[levelCount]보다 높으면 레벨이 상승한다. 그다음 레벨카운트가 상승 레벨카운트가 상승하면 목표시간이 바뀐다.
if (MiniGameManager.I.currentTime > MiniGameManager.I.nextTime[levelCount])
{
level += (float)levelCount;
transform.position -= new Vector3(level, 0, 0);
levelCount++;
Debug.Log(level);
}
else transform.position -= new Vector3(level, 0, 0);
}
currentTime 이 nextTime에달성하면 level이 올라간다. 올라간 level은 화살의 이동속도 x값에 들어가 속도를 높힌다.
그리고 levelCount가 올라가서 nextTime의 다음 인덱스로 if의 조건이 바뀌게 되는것이다.
그렇게 되면 플레이어는 더욱 빠른속도를 다음 목표시간만큼 견뎌야 하고 또 목표시간만큼 견디면 더 더욱 빠른 속도의 화살을 느낄 수 있게 되는것이다. ㅋㅋ
물론 목표 시간이 안되었다면 그냥 그대로 놔둔다.
다음은 내가 사용한 꼼수인데
- 미니 게임에서 Re 버튼을 누르면 스타트신으로 다시 돌아오게 하기
원래 정석대로라면 re 버튼을 누르면 플레이어 위치를 원상태로 복귀 화살을 모두 지우고 리 버튼도 안보이게 하고 점수도 원상태로 복귀하고 뭐 대충 여러가지 더 써야하는데
솔직히..
어쩌피 그냥 새로 시작하면 되는거 아닌가?? 짧은 시간안에 많은것을 구현할려면 시간은 금이다.
그러니 꼼수를 사용하기로 했다. ㅋㅋㅋㅋ
내가 꼼수로 사용한 코드는.. 바로
miniRetryBtn .cs
SceneManager.LoadScene("StartScene");
이 코드다.
그냥 다시 스타트 신으로 가는것. 스타트 신이 바로 내가 미니게임을 만든 장소기 때문이다.
그래서 Re버튼을 누르면 저 코드가 발생되어 바로 스타트신으로 다시 가서 새로운 미니 게임을 사용할 수 있다. ㅋㅋㅋㅋ 개꿀우우우우 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ! ㄹㅇㅋㅋ
근데 혹시 원래 그렇게 사용한거면 와우 난 똑똑했다! 라고 생각하겠다.ㅎㅎㅎ
마지막으로 나와 우리팀원이 제작중인 게임의 모습을 보여주겟다. !
오늘도 유익한 시간이였다..
내일은 정말로 더이상 기능을 추가하지 않고 있던 기능을 다듬고 타이틀을 만들어 대문에 붙일 것이다.
오늘은 무탈하게 버그도 없었어서 기분이 좋았다
내일도 오늘 같기를 !