일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- todolist
- 수학
- 골드메탈
- 프로그래머스
- 기초
- 2024년
- 2022년
- 개인 프로젝트
- 개인 프로젝트 - 런앤건
- 4월
- 3월
- 코딩 테스트
- 유니티
- 10월
- C/C++
- 코딩 기초 트레이닝
- 7월
- 1월
- 자료 구조
- 다이나믹 프로그래밍
- 유니티 심화과정
- 게임 엔진 공부
- c++
- 단계별로 풀어보기
- 2023년
- 5월
- 백준
- 입문
- 2월
- 2025년
- Today
- Total
기록 보관소
[Unity/유니티] 기초-뱀서라이크: 편리한 오디오 시스템 구축[15] 본문
개요
유니티 독학을 위해 아래 링크의 골드메탈님의 영상들을 보고 직접 따라 해보면서 진행 상황을 쓰고 배웠던 점을 요약한다.
https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2
📚유니티 기초 강좌
유니티 게임 개발을 배우고 싶은 분들을 위한 기초 강좌
www.youtube.com
뱀서라이크: 편리한 오디오 시스템 구축[15]
1. 유니티의 오디오
- 이 파트는 제작에 앞서 골드메탈님이 유니티의 오디오 시스템에 대해서 간략히 설명해주시는 파트다.
- AudioSource 컴포넌트 : 에셋인 AudioClip을 재생시켜주는 컴포넌트
- Play On Awake : 활성화 되어 있으면 최초 1회 자동 재생
- AudioListener 컴포넌트 : 장면에서 재생 중인 오디오를 듣는 컴포넌트
2. 오디오 매니저
//AudioManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioManager : MonoBehaviour {
public static AudioManager instance;
[Header("#BGM")]
public AudioClip bgmClip;
public float bgmVolume;
AudioSource bgmPlayer;
[Header("#SFX")]
public AudioClip[] sfxClips;
public float sfxVolume;
public int channels;
AudioSource[] sfxPlayers;
int channelIndex;
void Awake() {
instance = this;
Init();
}
void Init() {
//배경음 플레이어 초기화
GameObject bgmObject = new GameObject("BgmPlayer");
bgmObject.transform.parent = transform;
bgmPlayer = bgmObject.AddComponent<AudioSource>();
bgmPlayer.playOnAwake = false;
bgmPlayer.loop = true;
bgmPlayer.volume = bgmVolume;
bgmPlayer.clip = bgmClip;
//효과음 플레이어 초기화
GameObject sfxObject = new GameObject("SfxPlayer");
sfxObject.transform.parent = transform;
sfxPlayers = new AudioSource[channels];
for (int index = 0; index < sfxPlayers.Length; index++) {
sfxPlayers[index] = sfxObject.AddComponent<AudioSource>();
sfxPlayers[index].playOnAwake = false;
sfxPlayers[index].volume = sfxVolume;
}
}
}
3. 효과음 시스템
//AudioManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioManager : MonoBehaviour {
public static AudioManager instance;
[Header("#BGM")]
public AudioClip bgmClip;
public float bgmVolume;
AudioSource bgmPlayer;
[Header("#SFX")]
public AudioClip[] sfxClips;
public float sfxVolume;
public int channels;
AudioSource[] sfxPlayers;
int channelIndex;
public enum Sfx { Dead, Hit, LevelUp=3, Lose, Melee, Range=7, Select, Win }
void Awake() {
instance = this;
Init();
}
void Init() {
//배경음 플레이어 초기화
GameObject bgmObject = new GameObject("BgmPlayer");
bgmObject.transform.parent = transform;
bgmPlayer = bgmObject.AddComponent<AudioSource>();
bgmPlayer.playOnAwake = false;
bgmPlayer.loop = true;
bgmPlayer.volume = bgmVolume;
bgmPlayer.clip = bgmClip;
//효과음 플레이어 초기화
GameObject sfxObject = new GameObject("SfxPlayer");
sfxObject.transform.parent = transform;
sfxPlayers = new AudioSource[channels];
for (int index = 0; index < sfxPlayers.Length; index++) {
sfxPlayers[index] = sfxObject.AddComponent<AudioSource>();
sfxPlayers[index].playOnAwake = false;
sfxPlayers[index].volume = sfxVolume;
}
}
public void PlaySfx(Sfx sfx) {
for (int index = 0; index < sfxPlayers.Length; index++) {
int loopIndex = (index + channelIndex) % sfxPlayers.Length;
if (sfxPlayers[loopIndex].isPlaying)
continue;
int ranIndex = 0;
if (sfx == Sfx.Hit || sfx == Sfx.Melee) {
ranIndex = Random.Range(0, 2);
}
channelIndex = loopIndex;
sfxPlayers[loopIndex].clip = sfxClips[(int)sfx + ranIndex];
sfxPlayers[loopIndex].Play();
break;
}
}
}
- 위 AudioManager를 통해서 각 상황에 맞게 소리가 재생되도록 아래 스크립트들을 모두 수정.
//GameManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; //장면 관리(Scene Manager 같은)를 사용하기 위한 네임스페이스.
public class GameManager : MonoBehaviour {
public static GameManager instance;
[Header("# Game Control")]
public bool isLive; //시간 정지 여부 확인 변수
public float gameTime; //게임 시간 변수
public float maxGameTime = 2 * 10f; //최대 게임 시간 변수(20초).
[Header("# Player Info")]
public int playerId;
public float health;
public float maxHealth = 100;
public int level;
public int kill;
public int exp;
public int[] nextExp = { 3, 5, 10, 100, 150, 210, 280, 360, 450, 600 };
[Header("# Game Object")]
public PoolManager pool;
public Player player;
public LevelUp uiLevelUp;
public Result uiResult;
public GameObject enemyCleaner;
void Awake() {
instance = this;
}
public void GameStart(int id) {
playerId = id;
health = maxHealth;
player.gameObject.SetActive(true);
uiLevelUp.Select(playerId % 2);
Resume();
AudioManager.instance.PlaySfx(AudioManager.Sfx.Select);
}
public void GameOver() {
StartCoroutine(GameOverRoutine());
}
IEnumerator GameOverRoutine() {
isLive = false;
yield return new WaitForSeconds(0.5f);
uiResult.gameObject.SetActive(true);
uiResult.Lose();
Stop();
AudioManager.instance.PlaySfx(AudioManager.Sfx.Lose);
}
public void GameVictory() {
StartCoroutine(GameVictoryRoutine());
}
IEnumerator GameVictoryRoutine() {
isLive = false;
enemyCleaner.SetActive(true);
yield return new WaitForSeconds(0.5f);
uiResult.gameObject.SetActive(true);
uiResult.Win();
Stop();
AudioManager.instance.PlaySfx(AudioManager.Sfx.Win);
}
public void GameRetry() {
SceneManager.LoadScene(0); //LoadScene() : 이름 혹은 인덱스로 장면을 새롭게 부르는 함수
}
void Update() {
if (!isLive)
return;
gameTime += Time.deltaTime;
if (gameTime > maxGameTime) {
gameTime = maxGameTime;
GameVictory();
}
}
public void GetExp() {
if (!isLive) //EnemyCleaner로 경험치를 못얻게 하기 위함
return;
exp++;
if (exp == nextExp[Mathf.Min(level, nextExp.Length - 1)]) {
level++;
exp = 0;
uiLevelUp.Show();
}
}
public void Stop() {
isLive = false;
Time.timeScale = 0;
}
public void Resume() {
isLive = true;
Time.timeScale = 1; //값이 1보다 크면 그만큼 시간이 빠르게 흐름. 모바일 게임에서 시간 가속하는 것이 이것..
}
}
//AchiveManager Script
using System; //Enum 사용을 위한 namespace
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AchiveManager : MonoBehaviour {
public GameObject[] lockCharacter;
public GameObject[] unlockCharacter;
public GameObject uiNotice;
enum Achive { UnlockPotato, UnlockBean }
Achive[] achives;
WaitForSecondsRealtime wait;
void Awake() {
achives = (Achive[])Enum.GetValues(typeof(Achive));
wait = new WaitForSecondsRealtime(5);
if (!PlayerPrefs.HasKey("MyData")) {
Init();
}
}
void Init() {
PlayerPrefs.SetInt("MyData", 1);
foreach (Achive Achive in achives) {
PlayerPrefs.SetInt(Achive.ToString(), 0);
}
}
void Start() {
UnlockCharacter();
}
void UnlockCharacter() {
for (int index = 0; index < lockCharacter.Length; index++) {
string AchiveName = achives[index].ToString();
bool isUnlock = PlayerPrefs.GetInt(AchiveName) == 1;
lockCharacter[index].SetActive(!isUnlock);
unlockCharacter[index].SetActive(isUnlock);
}
}
void LateUpdate() {
foreach (Achive achive in achives) {
CheckAchive(achive);
}
}
void CheckAchive(Achive achive) {
bool isAchive = false;
switch (achive) {
case Achive.UnlockPotato:
isAchive = GameManager.instance.kill >= 10;
break;
case Achive.UnlockBean:
isAchive = GameManager.instance.gameTime == GameManager.instance.maxGameTime;
break;
}
if (isAchive && PlayerPrefs.GetInt(achive.ToString()) == 0) {
PlayerPrefs.SetInt(achive.ToString(), 1);
for (int index = 0; index < uiNotice.transform.childCount; index++) {
bool isActive = index == (int)achive;
uiNotice.transform.GetChild(index).gameObject.SetActive(isActive);
}
StartCoroutine(NoticeRoutine());
}
}
IEnumerator NoticeRoutine() {
uiNotice.SetActive(true);
AudioManager.instance.PlaySfx(AudioManager.Sfx.LevelUp);
yield return wait;
uiNotice.SetActive(false);
}
}
//LevelUp Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelUp : MonoBehaviour {
RectTransform rect;
Item[] items;
void Awake() {
rect = GetComponent<RectTransform>();
items = GetComponentsInChildren<Item>(true);
}
public void Show() {
Next();
rect.localScale = Vector3.one;
GameManager.instance.Stop();
AudioManager.instance.PlaySfx(AudioManager.Sfx.LevelUp);
}
public void Hide() {
rect.localScale = Vector3.zero;
GameManager.instance.Resume();
AudioManager.instance.PlaySfx(AudioManager.Sfx.Select);
}
public void Select(int index) {
items[index].OnClick();
}
void Next() {
// 1. 모든 아이템 비활성화
foreach(Item item in items) {
item.gameObject.SetActive(false);
}
// 2. 그 중에서 랜덤 3개 아이템 활성화
int[] ran = new int[3];
while (true) {
ran[0] = Random.Range(0, items.Length);
ran[1] = Random.Range(0, items.Length);
ran[2] = Random.Range(0, items.Length);
if (ran[0] != ran[1] && ran[1] != ran[2] && ran[0] != ran[2])
break;
}
for (int index = 0; index < ran.Length; index++) {
Item ranItem = items[ran[index]];
// 3. 만렙 아이템의 경우, 소비 아이템으로 대체
if (ranItem.level == ranItem.data.damages.Length) {
items[4].gameObject.SetActive(true);
}
else {
ranItem.gameObject.SetActive(true);
}
}
}
}
//Weapon Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour {
public int id;
public int prefabId;
public float damage;
public int count;
public float speed;
float timer;
Player player;
void Awake() {
player = GameManager.instance.player;
}
void Update() {
if (!GameManager.instance.isLive)
return;
switch (id) {
case 0:
transform.Rotate(Vector3.back * speed * Time.deltaTime);
break;
default:
timer += Time.deltaTime;
if (timer > speed) {
timer = 0f;
Fire();
}
break;
}
// .. Test Code ..
if (Input.GetButtonDown("Jump")) {
LevelUp(10, 1);
}
}
public void LevelUp(float damage, int count) {
this.damage = damage * Character.Damage;
this.count += count;
if (id == 0)
Batch();
player.BroadcastMessage("ApplyGear", SendMessageOptions.DontRequireReceiver);
}
public void Init(ItemData data) {
// Basic Set
name = "Weapon " + data.itemId;
transform.parent = player.transform;
transform.localPosition = Vector3.zero;
// Property Set
id = data.itemId;
damage = data.baseDamage * Character.Damage;
count = data.baseCount + Character.Count;
for (int index = 0; index < GameManager.instance.pool.prefabs.Length; index++) {
if (data.projectile == GameManager.instance.pool.prefabs[index]) { //프리펩이 같은건지 확인
prefabId = index;
break;
}
}
switch (id) {
case 0:
speed = 150 * Character.WeaponSpeed;
Batch();
break;
default:
speed = 0.5f * Character.WeaponRate;
break;
}
// Hand Set
Hand hand = player.hands[(int)data.itemType];
hand.spriter.sprite = data.hand;
hand.gameObject.SetActive(true);
//플레이어가 가지고 있는 모든 Gear에 대해 ApplyGear를 실행하게 함. 나중에 추가된 무기에도 영향을 주기 위함.
player.BroadcastMessage("ApplyGear", SendMessageOptions.DontRequireReceiver);
}
void Batch() { //생성된 무기를 배치하는 함수
for (int index = 0; index < count; index++) {
Transform bullet;
if (index < transform.childCount) {
bullet = transform.GetChild(index); //기존 오브젝트가 있으면 먼저 활용
}
else {
bullet = GameManager.instance.pool.Get(prefabId).transform; //모자라면 풀링에서 가져옴
bullet.parent = transform;
}
bullet.localPosition = Vector3.zero; //무기 위치 초기화
bullet.localRotation = Quaternion.identity; //무기 회전값 초기화
Vector3 rotVec = Vector3.forward * 360 * index / count; //개수에 따라 360도 나누기
bullet.Rotate(rotVec);
bullet.Translate(bullet.up * 1.5f, Space.World); //무기 위쪽으로 이동
bullet.GetComponent<Bullet>().Init(damage, -1, Vector3.zero); //-1 is Infinity Per. 무한 관통.
}
}
void Fire() {
if (!player.scanner.nearestTarget)
return;
Vector3 targetPos = player.scanner.nearestTarget.position;
Vector3 dir = targetPos - transform.position;
dir = dir.normalized;
Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
bullet.position = transform.position;
bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir); //FromToRotation(지정된 축을 중심으로 목표를 향해 회전하는 함수
bullet.GetComponent<Bullet>().Init(damage, count, dir);
AudioManager.instance.PlaySfx(AudioManager.Sfx.Range);
}
}
//Enemy Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour {
public RuntimeAnimatorController[] animCon;
public float health;
public float maxHealth;
public float speed;
public Rigidbody2D target; //쫓아갈 타겟(플레이어)
bool isLive;
Rigidbody2D rigid;
Collider2D coll;
Animator anim;
SpriteRenderer spriter;
WaitForFixedUpdate wait;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
coll = GetComponent<Collider2D>();
anim = GetComponent<Animator>();
spriter = GetComponent<SpriteRenderer>();
wait = new WaitForFixedUpdate();
}
void FixedUpdate() {
if (!GameManager.instance.isLive)
return;
if (!isLive || anim.GetCurrentAnimatorStateInfo(0).IsName("Hit")) //GetCurrentAnimationStateInfo : 현재 상태 정보를 가져오는 함수
return;
Vector2 dirVec = target.position - rigid.position; // 방향 = 위치 차이의 정규화(Normalized). 위치 차이 = 타겟 위치 - 나의 위치.
Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime;
rigid.MovePosition(rigid.position + nextVec); //플레이어의 키 입력값을 더한 이동 = 몬스터의 방향 값을 더한 이동
rigid.velocity = Vector2.zero; //물리 속도가 이동에 영향을 주지 않도록 속도 제거
}
void LateUpdate() {
if (!GameManager.instance.isLive)
return;
if (!isLive)
return;
spriter.flipX = target.position.x < rigid.position.x;
}
void OnEnable() {
target = GameManager.instance.player.GetComponent<Rigidbody2D>(); //플레이어 할당
isLive = true;
coll.enabled = true;
rigid.simulated = true;
spriter.sortingOrder = 2;
anim.SetBool("Dead", false);
health = maxHealth;
}
//데이터를 가져오기 위한 초기화 함수
public void Init(SpawnData data) {
anim.runtimeAnimatorController = animCon[data.spriteType];
speed = data.speed;
maxHealth = data.health;
health = data.health;
}
void OnTriggerEnter2D(Collider2D collision) {
if (!collision.CompareTag("Bullet") || !isLive)
return;
health -= collision.GetComponent<Bullet>().damage; //총알 데미지만큼 Enemy 체력 감소
StartCoroutine(KnockBack());
if(health > 0) {
anim.SetTrigger("Hit");
AudioManager.instance.PlaySfx(AudioManager.Sfx.Hit);
}
else {
isLive = false;
coll.enabled = false;
rigid.simulated = false; //rigidbody의 물리적 비활성화는 simulated를 false로 설정.
spriter.sortingOrder = 1; //SpriteRenderer의 Sorting Order를 감소시켜 다른 몬스터를 가리지 않게 함.
anim.SetBool("Dead", true);
GameManager.instance.kill++;
GameManager.instance.GetExp();
if (GameManager.instance.isLive) //게임 승리시 사운드 테러 방지
AudioManager.instance.PlaySfx(AudioManager.Sfx.Dead);
}
}
IEnumerator KnockBack() {
yield return wait; //다음 하나의 물리 프레임 딜레이
Vector3 playerPos = GameManager.instance.player.transform.position;
Vector3 dirVec = transform.position - playerPos;
rigid.AddForce(dirVec.normalized * 3, ForceMode2D.Impulse);
}
void Dead() {
gameObject.SetActive(false);
}
}
4. 배경음 시스템
- 개발에 사용된 에셋에서는 BGM 파일이 따로 없다. 아래 골드메탈님의 영상을 통해서 개인적으로 찾은 뒤, 적용하면 된다.
https://www.youtube.com/watch?v=UGJ6T1g1BZk
- 사실, 영상 설명에 따로 사용하신 배경음악 링크를 게제해 주셔서 나는 이걸 써서 진행했다.
- 영상 마지막 테스트를 보니, 아마 Level 1을 쓰셨을듯 하다.
https://opengameart.org/content/5-chiptunes-action
5 Chiptunes (Action)
5 tracks, all seamlessly looping! Title Screen Level 1 Level 2 Level 3 Ending If you also need sound effects for your game, there's the gigantic 1000 Retro Sound Effects pack available from itch.io: https://subspaceaudio.itch.io/1000-retro-sound-effects
opengameart.org
- Audio HighPass Filter 컴포넌트 : 오디오의 주파수가 높은 음역대만 통과시키고, 낮은 음역대는 막아버리는 오디오 필터 컴포넌트. Listener Effect 계열.
- 레벨업 후 선택창에서 이 컴포넌트를 잠시 활성화하여 BGM이 잘 들리지 않도록 만든다. 정확하게 말하자면 일부 음역대만 들리게 해서 다른 소리가 묻히지 않도록 만드는 것이다.
//AudioManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioManager : MonoBehaviour {
public static AudioManager instance;
[Header("#BGM")]
public AudioClip bgmClip;
public float bgmVolume;
AudioSource bgmPlayer;
AudioHighPassFilter bgmEffect;
[Header("#SFX")]
public AudioClip[] sfxClips;
public float sfxVolume;
public int channels;
AudioSource[] sfxPlayers;
int channelIndex;
public enum Sfx { Dead, Hit, LevelUp=3, Lose, Melee, Range=7, Select, Win }
void Awake() {
instance = this;
Init();
}
void Init() {
//배경음 플레이어 초기화
GameObject bgmObject = new GameObject("BgmPlayer");
bgmObject.transform.parent = transform;
bgmPlayer = bgmObject.AddComponent<AudioSource>();
bgmPlayer.playOnAwake = false;
bgmPlayer.loop = true;
bgmPlayer.volume = bgmVolume;
bgmPlayer.clip = bgmClip;
bgmEffect = Camera.main.GetComponent<AudioHighPassFilter>();
//효과음 플레이어 초기화
GameObject sfxObject = new GameObject("SfxPlayer");
sfxObject.transform.parent = transform;
sfxPlayers = new AudioSource[channels];
for (int index = 0; index < sfxPlayers.Length; index++) {
sfxPlayers[index] = sfxObject.AddComponent<AudioSource>();
sfxPlayers[index].playOnAwake = false;
sfxPlayers[index].bypassListenerEffects = true; //Audio HighPass Filter에 막히지 않도록 처리
sfxPlayers[index].volume = sfxVolume;
}
}
public void PlayBgm(bool isPlay) {
if (isPlay) {
bgmPlayer.Play();
}
else {
bgmPlayer.Stop();
}
}
public void EffectBgm(bool isPlay) {
bgmEffect.enabled = isPlay;
}
public void PlaySfx(Sfx sfx) {
for (int index = 0; index < sfxPlayers.Length; index++) {
int loopIndex = (index + channelIndex) % sfxPlayers.Length;
if (sfxPlayers[loopIndex].isPlaying)
continue;
int ranIndex = 0;
if (sfx == Sfx.Hit || sfx == Sfx.Melee) {
ranIndex = Random.Range(0, 2);
}
channelIndex = loopIndex;
sfxPlayers[loopIndex].clip = sfxClips[(int)sfx + ranIndex];
sfxPlayers[loopIndex].Play();
break;
}
}
}
- 게임 시작시 BGM이 실행되고, 게임 종료시 중지될 수 있도록 GameManager를 수정하고, 레벨업시 잠시 BGM이 잘 들리지 않도록 LevelUp 스크립트 수정.
//GameManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; //장면 관리(Scene Manager 같은)를 사용하기 위한 네임스페이스.
public class GameManager : MonoBehaviour {
public static GameManager instance;
[Header("# Game Control")]
public bool isLive; //시간 정지 여부 확인 변수
public float gameTime; //게임 시간 변수
public float maxGameTime = 2 * 10f; //최대 게임 시간 변수(20초).
[Header("# Player Info")]
public int playerId;
public float health;
public float maxHealth = 100;
public int level;
public int kill;
public int exp;
public int[] nextExp = { 3, 5, 10, 100, 150, 210, 280, 360, 450, 600 };
[Header("# Game Object")]
public PoolManager pool;
public Player player;
public LevelUp uiLevelUp;
public Result uiResult;
public GameObject enemyCleaner;
void Awake() {
instance = this;
}
public void GameStart(int id) {
playerId = id;
health = maxHealth;
player.gameObject.SetActive(true);
uiLevelUp.Select(playerId % 2);
Resume();
AudioManager.instance.PlayBgm(true);
AudioManager.instance.PlaySfx(AudioManager.Sfx.Select);
}
public void GameOver() {
StartCoroutine(GameOverRoutine());
}
IEnumerator GameOverRoutine() {
isLive = false;
yield return new WaitForSeconds(0.5f);
uiResult.gameObject.SetActive(true);
uiResult.Lose();
Stop();
AudioManager.instance.PlayBgm(false);
AudioManager.instance.PlaySfx(AudioManager.Sfx.Lose);
}
public void GameVictory() {
StartCoroutine(GameVictoryRoutine());
}
IEnumerator GameVictoryRoutine() {
isLive = false;
enemyCleaner.SetActive(true);
yield return new WaitForSeconds(0.5f);
uiResult.gameObject.SetActive(true);
uiResult.Win();
Stop();
AudioManager.instance.PlayBgm(false);
AudioManager.instance.PlaySfx(AudioManager.Sfx.Win);
}
public void GameRetry() {
SceneManager.LoadScene(0); //LoadScene() : 이름 혹은 인덱스로 장면을 새롭게 부르는 함수
}
void Update() {
if (!isLive)
return;
gameTime += Time.deltaTime;
if (gameTime > maxGameTime) {
gameTime = maxGameTime;
GameVictory();
}
}
public void GetExp() {
if (!isLive) //EnemyCleaner로 경험치를 못얻게 하기 위함
return;
exp++;
if (exp == nextExp[Mathf.Min(level, nextExp.Length - 1)]) {
level++;
exp = 0;
uiLevelUp.Show();
}
}
public void Stop() {
isLive = false;
Time.timeScale = 0;
}
public void Resume() {
isLive = true;
Time.timeScale = 1; //값이 1보다 크면 그만큼 시간이 빠르게 흐름. 모바일 게임에서 시간 가속하는 것이 이것..
}
}
//LevelUp Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelUp : MonoBehaviour {
RectTransform rect;
Item[] items;
void Awake() {
rect = GetComponent<RectTransform>();
items = GetComponentsInChildren<Item>(true);
}
public void Show() {
Next();
rect.localScale = Vector3.one;
GameManager.instance.Stop();
AudioManager.instance.PlaySfx(AudioManager.Sfx.LevelUp);
AudioManager.instance.EffectBgm(true);
}
public void Hide() {
rect.localScale = Vector3.zero;
GameManager.instance.Resume();
AudioManager.instance.PlaySfx(AudioManager.Sfx.Select);
AudioManager.instance.EffectBgm(false);
}
public void Select(int index) {
items[index].OnClick();
}
void Next() {
// 1. 모든 아이템 비활성화
foreach(Item item in items) {
item.gameObject.SetActive(false);
}
// 2. 그 중에서 랜덤 3개 아이템 활성화
int[] ran = new int[3];
while (true) {
ran[0] = Random.Range(0, items.Length);
ran[1] = Random.Range(0, items.Length);
ran[2] = Random.Range(0, items.Length);
if (ran[0] != ran[1] && ran[1] != ran[2] && ran[0] != ran[2])
break;
}
for (int index = 0; index < ran.Length; index++) {
Item ranItem = items[ran[index]];
// 3. 만렙 아이템의 경우, 소비 아이템으로 대체
if (ranItem.level == ranItem.data.damages.Length) {
items[4].gameObject.SetActive(true);
}
else {
ranItem.gameObject.SetActive(true);
}
}
}
}
'유니티 프로젝트 > 뱀서라이크' 카테고리의 다른 글
[Unity/유니티] 기초-뱀서라이크: 모바일 빌드하기[17] (0) | 2023.09.02 |
---|---|
[Unity/유니티] 기초-뱀서라이크: 로직 보완하기[16] (0) | 2023.08.30 |
[Unity/유니티] 기초-뱀서라이크: 캐릭터 해금 시스템[14+] (0) | 2023.07.25 |
[Unity/유니티] 기초-뱀서라이크: 플레이 캐릭터 선택[14] (0) | 2023.07.21 |
[Unity/유니티] 기초-뱀서라이크: 게임 시작과 종료[13] (0) | 2023.07.18 |