diff --git a/.vsconfig b/.vsconfig new file mode 100644 index 0000000..d70cd98 --- /dev/null +++ b/.vsconfig @@ -0,0 +1,6 @@ +{ + "version": "1.0", + "components": [ + "Microsoft.VisualStudio.Workload.ManagedGame" + ] +} diff --git a/Assets/Undead Survivor/Script/AcheiveManager.cs b/Assets/Undead Survivor/Script/AcheiveManager.cs index 219e856..4387a44 100644 --- a/Assets/Undead Survivor/Script/AcheiveManager.cs +++ b/Assets/Undead Survivor/Script/AcheiveManager.cs @@ -7,8 +7,10 @@ public class AcheiveManager : MonoBehaviour { + // 잠금, 해금 버튼들을 담을 변수 추가 및 초기화 public GameObject[] lockCharacter; public GameObject[] unlockCharacter; + // 알림 오브젝트를 저장할 변수 선언 및 초기화 public GameObject uiNotice; enum Acheive {UnlockPotato, UnlockBean} //업적 데이터와 같은 열거형 enum 작성 Acheive[] acheives; //업적 데이터들을 저장해둘 배열 선언 및 초기화 @@ -20,7 +22,8 @@ void Awake() ///앞에 Acheive[]를 추가해서 배열이라고 명시하기 acheives = (Acheive[])Enum.GetValues(typeof(Acheive)); //Enum.GetValues : 주어진 열거형의 데이터를 모두 가져오는 함수 - + // Enum.GetValues 앞에 타입을 명시적으로 지정하여 타입 맞추기 + wait = new WaitForSecondsRealtime(5); //5초간 해금 알림 UI 띄워주기 위한 변수 //HasKey 함수로 데이터 유무 체크 후 초기화 실행 @@ -29,18 +32,22 @@ void Awake() } } + // 저장 데이터 초기화 함수 작성 void Init() { + // PlayerPrefs : 간단한 저장 기능을 제공하는 유니티 제공 클래스 PlayerPrefs.SetInt("MyData",1); //데이터 저장을 시작했다는 의미의 int형 데이터 //SetInt 함수를 사용하여 key와 연결된 int형 데이터를 저장 + // foreach를 활용하여 순차적으로 데이터 저장 //아직 달성하지 않은 데이터의 key를 0으로 저장 //0 : false, 1 : true foreach (Acheive acheive in acheives) { PlayerPrefs.SetInt(acheive.ToString(),0); } //이렇게 일일히 할 필요 없이 위에 처럼 하면 됨 + // 업적 데이터와 동일한 이름의 key로 0을 저장 //PlayerPrefs.SetInt("UnlockPotato",0); //PlayerPrefs.SetInt("UnlockBean",0); } @@ -50,9 +57,10 @@ void Start() UnlockCharacter(); } - //캐릭터 해금을 위한 함수 + // 캐릭터 버튼 해금을 위한 함수 새롭게 작성 void UnlockCharacter() { + // 잠금 버튼 배열을 순회하면서 인덱스에 해당하는 업적 이름 가져오기 for (int index = 0; index < lockCharacter.Length; index++){ string acheiveName = acheives[index].ToString(); bool isUnlock = PlayerPrefs.GetInt(acheiveName) == 1; //isUnlock 변수를 true로 설정 @@ -66,16 +74,19 @@ void UnlockCharacter() void LateUpdate() { + // 모든 업적 확인을 위한 반복문을 LateUpdate에 작성 foreach (Acheive acheive in acheives){ CheckAcheive(acheive); } } + // 업적 달성을 위한 함수 새롭게 작성 void CheckAcheive(Acheive acheive) { bool isAcheive = false; - switch(acheive){ + // switch~case문으로 각 업적 달성 조건을 작성 + switch (acheive){ case Acheive.UnlockPotato: //킬수가 10마리 이상이면 언락 if(GameManager.instance.isLive){ //단, 마무리시에 일괄 죽임으로는 안되도록 isAcheive = GameManager.instance.kill >= 10;} @@ -97,6 +108,7 @@ void CheckAcheive(Acheive acheive) } } + // 알림 창을 활성화했다가 일정 시간 이후 비활성화하는 코루틴 생성 IEnumerator NoticeRoutine() { uiNotice.SetActive(true); diff --git a/Assets/Undead Survivor/Script/AudioManager.cs b/Assets/Undead Survivor/Script/AudioManager.cs index 20cff64..6cfe5f1 100644 --- a/Assets/Undead Survivor/Script/AudioManager.cs +++ b/Assets/Undead Survivor/Script/AudioManager.cs @@ -6,6 +6,7 @@ public class AudioManager : MonoBehaviour { + // 정적 메모리에 담기 위한 instance 변수 선언 public static AudioManager instance; [Header("#BGM")] @@ -15,6 +16,7 @@ public class AudioManager : MonoBehaviour AudioSource bgmPlayer; AudioHighPassFilter bgmEffect; //배경음을 잠깐 멈추기 위한 변수 설정 + // 코드 복사하여 효과음 코드로 편집 [Header("#SFX")] //효과음과 관련된 클립, 볼륨, 오디오소스 변수 선언 public AudioClip[] sfxClips; @@ -24,6 +26,8 @@ public class AudioManager : MonoBehaviour AudioSource[] sfxPlayers; int channelIndex; + // 효과음과 1:1 대응하는 열거형 데이터 선언 + // 열거형 데이터는 대응하는 숫자를 지정할 수 있어요. public enum Sfx { Dead, Hit, LevelUp = 3, Lose, Melee, Range = 7, Select, Win} void Awake() @@ -42,6 +46,7 @@ void Init() bgmPlayer.loop = true; bgmPlayer.volume = bgmVolume; bgmPlayer.clip = bgmClip; + // 메인 카메라 접근은 Camera 클래스를 사용하면 간편해요. bgmEffect = Camera.main.GetComponent(); //효과음 플레이어 초기화 @@ -54,11 +59,13 @@ void Init() { sfxPlayers[index] = sfxObject.AddComponent(); sfxPlayers[index].playOnAwake = false; + // 효과음 초기화 하는 부분에 byPassListenerEffects를 true로 변경 sfxPlayers[index].bypassListenerEffects = true; //효과음은 배경음처럼 꺼지지 않도록 추가 설정 sfxPlayers[index].volume = sfxVolume; } } + // 배경음을 재생하는 함수 작성 public void PlayBgm(bool isPlay) { if (isPlay){ @@ -69,11 +76,14 @@ public void PlayBgm(bool isPlay) } } + // 필터를 켜고 끄는 함수도 작성 public void EffectBgm(bool isPlay) { //레벨업 후 무기 선택시에 잠깐 배경음 줄이기 bgmEffect.enabled = isPlay; } + + // 효과음 재생 함수 작성 public void PlaySfx(Sfx sfx) { for (int index = 0; index < sfxPlayers.Length; index++) @@ -82,6 +92,7 @@ public void PlaySfx(Sfx sfx) int loopIndex = (index + channelIndex) % sfxPlayers.Length; if (sfxPlayers[loopIndex].isPlaying) + // continue : 반복문 도중 다음 루프로 건너뛰는 키워드 continue; //반복문 도중 다음 루프로 건너뛰기 //효과음이 2개 이상인 것은 랜덤 인덱스를 더하기 @@ -93,6 +104,7 @@ public void PlaySfx(Sfx sfx) channelIndex = loopIndex; sfxPlayers[0].clip = sfxClips[(int)sfx + ranIndex]; + // 오디오소스의 클립을 변경하고 Play 함수 호출 sfxPlayers[0].Play(); break; //효과음 재생이 된 경우에는 break로 루프 종료 } diff --git a/Assets/Undead Survivor/Script/Bullet.cs b/Assets/Undead Survivor/Script/Bullet.cs index 25b861c..18b31c0 100644 --- a/Assets/Undead Survivor/Script/Bullet.cs +++ b/Assets/Undead Survivor/Script/Bullet.cs @@ -8,9 +8,11 @@ public class Bullet : MonoBehaviour { + // 데미지와 관통 변수 선언 public float damage; public int per; + // 총알 스크립트에서 리지드바디2D 변수 선언 및 초기화 Rigidbody2D rigid; void Awake() @@ -18,31 +20,43 @@ void Awake() rigid = GetComponent(); } + // 변수 초기화 함수 작성 + // 초기화 함수에 속도 관련 매개변수 추가 public void Init(float damage, int per, Vector3 dir) //안에 있는거 받는 값 즉 parameter { this.damage = damage; //this : 해당 클래스의 변수로 접근 // this.damage = Bullet 함수 내에 damage, 그냥 damage = Init함수에 받아오는 매개변수 damage this.per = per; - if(per >= 0) { //관통이 무한이 아니면 원거리 + // 관통이 -1(무한)보다 큰 것에 대해서는 속도 적용 + //if (per > -1) + if (per >= 0) { //관통이 무한이 아니면 원거리 + //rigid.velocity = dir; + // 속력을 곱해주어 총알이 날아가는 속도 증가시키기 rigid.velocity = dir * 15f; //속도를 제어 } } void OnTriggerEnter2D(Collider2D collision) { + // 관통 로직 이전에 if문으로 조건 추가 + // || (OR) : 혹은, 좌측 우측 둘 중 하나만 true면 결과는 true if(!collision.CompareTag("Enemy") || per == -100) // || 는 or 이다 return; - - per --; + // 관통 값이 하나씩 줄어들면서 -1이 되면 비활성화 + per--; + + // 관통 이후의 로직을 감싸는 if 조건을 안전하게 변경 if (per < 0) { + // 비활성화 이전에 미리 물리 속도 초기화 rigid.velocity = Vector2.zero; gameObject.SetActive(false); } } //총알이 맵 밖으로 나가는 경우 없어지도록 처리 + // OnTriggerExit2D 이벤트와 Area를 활용하여 쉽게 비활성화 void OnTriggerExit2D(Collider2D collision) { //OnTriggerExit2D 이벤트와 Area를 활용하여 쉽게 비활성화 diff --git a/Assets/Undead Survivor/Script/Character.cs b/Assets/Undead Survivor/Script/Character.cs index 1856155..b408d3d 100644 --- a/Assets/Undead Survivor/Script/Character.cs +++ b/Assets/Undead Survivor/Script/Character.cs @@ -4,33 +4,34 @@ public class Character : MonoBehaviour { + //함수가 아닌 속성을 작성 public static float Speed { - //함수가 아닌 속성을 작성 - get { return GameManager.instance.playerId == 0? 1.1f : 1f;} + // 삼항연산자를 활용하여 캐릭터에 따라 특성치 적용 + get { return GameManager.instance.playerId == 0 ? 1.1f : 1f;} } public static float WeaponSpeed { - //함수가 아닌 속성을 작성 - get { return GameManager.instance.playerId == 1? 1.1f : 1f;} + // 캐릭터 특성치에 맞는 각종 속성들을 작성 + get { return GameManager.instance.playerId == 1 ? 1.1f : 1f;} } public static float WeaponRate { - //함수가 아닌 속성을 작성 - get { return GameManager.instance.playerId == 1? 0.9f : 1f;} + // 캐릭터 특성치에 맞는 각종 속성들을 작성 + get { return GameManager.instance.playerId == 1 ? 0.9f : 1f;} } public static float Damage { - //함수가 아닌 속성을 작성 - get { return GameManager.instance.playerId == 2? 1.2f : 1f;} + // 캐릭터 특성치에 맞는 각종 속성들을 작성 + get { return GameManager.instance.playerId == 2 ? 1.2f : 1f;} } public static int Count { - //함수가 아닌 속성을 작성 - get { return GameManager.instance.playerId == 3? 1 : 0;} + // 캐릭터 특성치에 맞는 각종 속성들을 작성 + get { return GameManager.instance.playerId == 3 ? 1 : 0;} } } diff --git a/Assets/Undead Survivor/Script/Enemy.cs b/Assets/Undead Survivor/Script/Enemy.cs index 9aea50d..febf795 100644 --- a/Assets/Undead Survivor/Script/Enemy.cs +++ b/Assets/Undead Survivor/Script/Enemy.cs @@ -5,18 +5,30 @@ public class Enemy : MonoBehaviour { + // 속도, 목표, 생존여부를 위한 변수 선언 public float speed; + // 체력 관련 변수도 함께 선언 public float health; public float maxHealth; + // 애니메이션 Sprite를 바꾸는 데이터 + // Animator의 데이터는 AnimatorController + // RuntimeAnimatorController 변수 선언 public RuntimeAnimatorController[] animCon; public Rigidbody2D target; + // 아직 테스트 상태이므로 미리 isLive = true 적용 + //bool isLive = true; + // OnEnable에서 생존여부와 체력 초기화 bool isLive; + // Rigidbody 2D와 Sprite Renderer를 위한 변수 선언 Rigidbody2D rigid; + // Collider2D 변수를 생성 및 초기화 Collider2D coll; + // 애니메이터 변수 선언 및 초기화하고 이후 로직 작성하기 Animator anim; SpriteRenderer spriter; + // WaitForFixedUpdate 변수 선언 및 초기화 WaitForFixedUpdate wait; //다음 fixedUpdate가 될때까지 기다리는 변수 void Awake() @@ -28,18 +40,27 @@ void Awake() wait = new WaitForFixedUpdate(); } + // 물리적인 이동이기 때문에 FixedUpdate를 사용 void FixedUpdate() { if(!GameManager.instance.isLive) return; - + + // 몬스터가 살아있는 동안에만 움직이도록 조건 추가 + //if (!isLive) + // GetCurrentAnimatorStateInfo : 현재 상태 정보를 가져오는 함수 + // IsName : 해당 상태의 이름이 지정된 것과 같은지 확인하는 함수 if (!isLive || anim.GetCurrentAnimatorStateInfo(0).IsName("Hit")) return; //만약 몬스터가 죽은 상태이거나 맞는 상태이면(넉백위해서) 작동하지 않음 + // 위치 차이 = 타겟 위치 - 나의 위치 Vector2 dirVec = target.position - rigid.position; + // 방향 = 위치 차이의 정규화 (Normalized) + // 프레임의 영향으로 결과가 달라지지 않도록 FixedDeltaTime 사용 Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime; //다음에 가야할 위치의 양 - rigid.MovePosition(rigid.position + nextVec); //플레이어의 키입력 값을 더한 이동 = 몬스터의 방향 값을 더한 이동 + rigid.MovePosition(rigid.position + nextVec); + // 물리 속도가 이동에 영향을 주지 않도록 속도 제거 rigid.velocity = Vector2.zero; //몬스터와 플레이어가 부딪힐 때 발생하는 속도를 (0,0)으로 고정 } @@ -47,27 +68,46 @@ void LateUpdate() { if(!GameManager.instance.isLive) return; - - if(!isLive) + + // 몬스터가 살아있는 동안에만 움직이도록 조건 추가 + if (!isLive) return; + // 목표의 X축 값과 자신의 X축 값을 비교하여 작으면 true가 되도록 설정 spriter.flipX = target.position.x < rigid.position.x; } + // Enemy는 Scene에 새롭게 등장하면서 활성화된다 + // 활성화되면서 자동으로 실행되는 함수 + // OnEnable : 스크립트가 활성화 될 때, 호출되는 이벤트 함수 void OnEnable() { + // OnEnable에서 target 변수에 GameManager를 활용하여 Player 할당 target = GameManager.instance.player.GetComponent(); + // OnEnable에서 생존여부와 체력 초기화 isLive = true; + + // 재활용을 위해 OnEnable 함수에서 되돌리기 + // 컴포넌트의 비활성화는 enabled = false //재활용 하기 위해 dead상태에서 다시 원상복구하기 coll.enabled = true; + // 리지드바디의 물리적 비활성화는 .simulated = false rigid.simulated = true; + // 스프라이트 렌더러의 Sorting Order 감소 spriter.sortingOrder = 2; + // SetBool 함수를 통해 죽는 애니메이션 상태로 전환 anim.SetBool("Dead", false); + + // 데미지를 받아 죽으면 health가 0이 되지만 Pooling에 의해 리젠되면 health도 원래대로 maxHealth가 되어야 한다 health = maxHealth; } + // Spawner에서 지정해준 데이터들을 함수로 받아야 한다 + // 초기 속성을 적용하는 함수 추가 + // 매개변수로 소환데이터 하나 지정 public void Init(SpawnData data) //매개변수로 소환데이터 하나 지정 { + // 매개변수의 속성을 몬스터 속성 변경에 활용하기 anim.runtimeAnimatorController = animCon[data.spriteType]; speed = data.speed; maxHealth = data.health; @@ -76,29 +116,50 @@ public void Init(SpawnData data) //매개변수로 소환데이터 하나 지정 //무기와 적이 다았을때 이벤트 시스템 void OnTriggerEnter2D(Collider2D collision) - { //collision = 지금 충돌한 상대 + { + // OnTriggerEnter2D 매개변수의 태그를 조건으로 활용 + //if (!collision.CompareTag("Bullet")) + // 사망 로직이 연달아 실행되는 것을 방지하기 위해 조건 추가 + //collision = 지금 충돌한 상대 if (!collision.CompareTag("Bullet") || !isLive) //지금 충돌한게 "Bullet"이 맞습니까라고 확인 & 사망 로직이 연달아 실행되는 것을 방지하기 위해 조건 추가 return; - + + // Bullet 컴포넌트로 접근하여 데미지를 가져와 피격 계산하기 health -= collision.GetComponent().damage; //맞은 무기의 데미지만큼 체력에서 깎기 + // GetCurrentAnimatorStateInfo : 현재 상태 정보를 가져오는 함수 //코루틴은 StartCoroutine으로 호출 StartCoroutine(KnockBack()); //StartCoroutine("KnockBack") 도 가능 + // 남은 체력을 조건으로 피격과 사망으로 로직을 나누기 if (health > 0) { + // .. Live, Hit Action + + // 몬스터 애니메이터의 피격 상태는 Hit Trigger로 제어되고 있음 + // 피격 부분에 애니메이터의 SetTrigger 함수를 호출하여 상태 변경 anim.SetTrigger("Hit"); //효과음을 재생할 부분마다 재생함수 호출 AudioManager.instance.PlaySfx(AudioManager.Sfx.Hit); } else{ + // .. Die + // 여러 로직을 제어하는 isLive 변수를 false로 변경 isLive = false; + // 컴포넌트의 비활성화는 enabled = false coll.enabled = false; //컴포넌트 비활성화 + // 리지드바디의 물리적 비활성화는 .simulated = false rigid.simulated = false; //rigidbody 물리적 비활성화 spriter.sortingOrder = 1; //스프라이트 랜더러의 sorting order(보이는 순서) 감소 anim.SetBool("Dead", true); //setBool 함수를 통해 죽는 애니메이션으로 전환 + + //Dead(); + + // 몬스터 사망 시 킬수 증가와 함께 경험치 함수 호출 GameManager.instance.kill++; GameManager.instance.GetExp(); + // 언데드 사망 사운드는 게임 종료 시에는 나지 않도록 조건 추가 + // if문 안쪽 로직이 한 줄이라면 중괄호 생략 가능 if (GameManager.instance.isLive){ //효과음을 재생할 부분마다 재생함수 호출 AudioManager.instance.PlaySfx(AudioManager.Sfx.Dead);} @@ -109,17 +170,27 @@ void OnTriggerEnter2D(Collider2D collision) //코루틴 Coroutine : 생명 주기와 비동기처럼 실행되는 함수 //IEnumerator : 코루틴만의 반환형 인터페이스 + // I라고 붙은 것들은 인터페이스라고 부른다 IEnumerator KnockBack() { //yield : 코루틴의 반환 키워드 + // IEnumerator를 가진 코루틴에서만 yield 사용 가능 + // yield return을 통해 다양한 쉬는 시간을 지정 + // 1프레임 쉬기 + //yield return null; //yield return new WaitForSeconds(2f); //2초 쉬기 + // new를 계속 쓰면 최적화 부분에서 안좋은 영향을 줄 수 있다 yield return wait; //다음 하나의 물리 프레임 딜레이 Vector3 playerPos = GameManager.instance.player.transform.position; + // 플레이어 기준의 반대 방향 : 현재 위치 - 플레이어 위치 Vector3 dirVec = transform.position - playerPos; + // 리지드바디2D의 AddForce 함수로 힘 가하기 rigid.AddForce(dirVec.normalized * 3, ForceMode2D.Impulse); //순간적인 힘이므로 ForceMode2D.Impulse속성 추가 // 넉백 받는 힘 곱해서 추가 } + + // 사망할 땐 SetActive 함수를 통한 오브젝트 비활성화 void Dead() { gameObject.SetActive(false); diff --git a/Assets/Undead Survivor/Script/Follow.cs b/Assets/Undead Survivor/Script/Follow.cs index e813c56..d9b9809 100644 --- a/Assets/Undead Survivor/Script/Follow.cs +++ b/Assets/Undead Survivor/Script/Follow.cs @@ -4,6 +4,9 @@ public class Follow : MonoBehaviour { + // UI에는 Transform과 RectTransform이 있다 + // RectTransform은 Transform과 달리 선언 및 초기화를 해줘야 한다 + // RectTransform 변수 선언 및 초기화 RectTransform rect; void Awake() diff --git a/Assets/Undead Survivor/Script/GameManager.cs b/Assets/Undead Survivor/Script/GameManager.cs index 6df7ba0..b00c1a3 100644 --- a/Assets/Undead Survivor/Script/GameManager.cs +++ b/Assets/Undead Survivor/Script/GameManager.cs @@ -5,6 +5,7 @@ using UnityEditor; //using UnityEditor.SearchService; using UnityEngine; +// 장면 관리를 사용하기 위해 SceneManagement 네임스페이스 추가 using UnityEngine.SceneManagement; public class GameManager : MonoBehaviour @@ -14,7 +15,12 @@ public class GameManager : MonoBehaviour //정적변수는 즉시 클래스에서 부를 수 있다는 편리함이 있다. //Static으로 만든 변수는 하이라키에 보이지 않는다. //GameManager를 메모리에 올려서 관리한다. + // static : 정적으로 사용하겠다는 키워드로, 바로 메모리에 얹어버린다 + // static으로 선언된 변수는 인스펙터에 나타나지 않는다 + // 정적 변수는 즉시 클래스에서 부를 수 있는 편리함이 있다 public static GameManager instance; + + // Header : 인스펙터의 속성들을 이쁘게 구분시켜주는 타이틀 [Header("# Game Control")] public bool isLive; //시간이 정지해 있는지를 판단하기 위해서 변수 선언 @@ -23,52 +29,77 @@ public class GameManager : MonoBehaviour public float maxGameTime = 2 * 10f; [Header("# Player Info")] + // 게임매니저에서 캐릭터 ID를 저장할 변수 선언 public int playerId; + // 게임 매니저에서 체력, 최대 체력 변수 선언 + // 게임매니저의 생명력 관련 변수는 float로 변경 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}; - //각 레벨의 필요 경험치를 보관할 배열 변수 선언 및 초기화 + //각 레벨의 필요 경험치를 보관할 배열 변수 선언 및 초기화 + // 테스트 + public int[] nextExp = {3, 5, 10, 100, 150, 210, 280, 360, 450, 600}; + //public int[] nextExp = { 10, 30, 60, 100, 150, 210, 280, 360, 450, 600 }; [Header("#Game Object")] + // 다양한 곳에서 쉽게 접근할 수 있도록 GameManager에 PoolManager 추가 public PoolManager pool; + // 플레이어 타입의 공개 변수 선언 public Player player; + // 게임매니저에 레벨업 변수 선언 및 초기화 public LevelUp uiLevelUp; + // 게임결과 UI 오브젝트를 저장할 변수 선언 및 초기화 + //public GameObject uiResult; + // 게임매니저의 기존 변수의 타입을 스크립트로 변경 public Result uiResult; + // 게임매니저에서 조이스틱 오브젝트 변수 추가하고 초기화 public Transform uiJoy; + // 게임 승리할 때 적을 정리하는 클리너 변수 선언 및 초기화 public GameObject enemyCleaner; //초기화를 스크립트 내에서 해줘야햠 void Awake() { + // Awake 생명주기에서 인스턴스 변수를 자기자신 this로 초기화 instance = this; //정적인 변수 자기자신을 집어넣어야 함 - Application.targetFrameRate = 60; //게임매니저에서 targetFrameRate속성을 직접 설정, 지정해주지 않으면 기본 30 + Application.targetFrameRate = 60; } + //void Start() + // 게임매니저의 기존 Start 함수를 GameStart로 변경 + //public void GameStart() + // 게임 시작 함수에는 int 매개변수 추가 public void GameStart(int id) //int 변수를 추가 { playerId = id; - + // 시작할 때 현재 체력과 최대 체력이 같도록 로직 추가 health = maxHealth; + // 게임 시작할 때 플레이어 활성화 후 기본 무기 지급 //플레이어 활성화 player.gameObject.SetActive(true); + // 임시 스크립트 (첫 번째 캐릭터 선택) + //uiLevelUp.Select(0); //기본 무기 지급을 위한 함수 호출에서 인자 값을 캐릭터 ID로 변경 uiLevelUp.Select(playerId % 2); + // 게임 시작 함수 내에 시간 재개 함수 호출 Resume(); + // 게임 시작 부분과 종료 부분에 해당 함수 호출 //배경음 시작을 게임 시작부분에 호출 AudioManager.instance.PlayBgm(true); //효과음을 재생할 부분마다 재생함수 호출 AudioManager.instance.PlaySfx(AudioManager.Sfx.Select); } + // 게임오버 담당 함수 작성 public void GameOver() { StartCoroutine(GameOverRoutine()); @@ -79,19 +110,26 @@ IEnumerator GameOverRoutine() { //작동을 멈추기 isLive = false; + //0.5초를 기다리고 yield return new WaitForSeconds(0.5f); + + // 게임결과 UI 오브젝트를 게임오버 코루틴에서 활성화 + //uiResult.SetActive(true); + // 게임매니저의 기존 변수의 타입을 스크립트로 변경 uiResult.gameObject.SetActive(true); uiResult.Lose(); //완전히 다 멈추기 Stop(); + // 게임 시작 부분과 종료 부분에 해당 함수 호출 //배경음 종료를 게임 종료부분에 호출 AudioManager.instance.PlayBgm(false); //효과음을 재생할 부분마다 재생함수 호출 AudioManager.instance.PlaySfx(AudioManager.Sfx.Lose); } + // 게임 승리 로직은 게임오버 로직을 복사해서 편집 public void GameVictory() { StartCoroutine(GameVictoryRoutine()); @@ -102,9 +140,12 @@ IEnumerator GameVictoryRoutine() { //작동을 멈추기 isLive = false; + // 게임 승리 코루틴의 전반부에 적 클리너를 활성화 enemyCleaner.SetActive(true); + //0.5초를 기다리고 yield return new WaitForSeconds(1.5f); + uiResult.gameObject.SetActive(true); uiResult.Win(); //완전히 다 멈추기 @@ -116,12 +157,16 @@ IEnumerator GameVictoryRoutine() AudioManager.instance.PlaySfx(AudioManager.Sfx.Win); } + // 게임매니저에 게임재시작 함수 작성 public void GameRetry() { + // 장면 관리를 사용하기 위해 SceneManagement 네임스페이스 추가 + // LoadScene : 이름 혹은 인덱스로 장면을 새롭게 부르는 함수 + // File > Build Settings으로 가면 Scene의 인덱스를 볼 수 있다 SceneManager.LoadScene(0); - //LoadScene : 이름 혹은 인덱스로 장면을 다시 새롭게 부르는 함수 } + // 종료 버튼의 기능을 담당하는 함수를 게임매니저에 추가 public void GameQuit() { Application.Quit(); //게임을 종료하는 함수 실행 @@ -130,46 +175,58 @@ public void GameQuit() void Update() { - if(!isLive) + // 각 스크립트의 Update 계열 로직에 조건 추가하기 + if (!isLive) return; //isLive가 아닌데 Update 시에 시간이 추가되지 않도록 조건 추가 - + + // Update에서 deltaTime 더하기 gameTime += Time.deltaTime; //0.2초에 적 한마리씩 생성 if (gameTime > maxGameTime){ gameTime = maxGameTime; + // 게임 시간이 최대 시간을 넘기는 때에 게임승리 함수 호출 GameVictory(); } } + // 경험치 증가 함수 새로 작성 public void GetExp() { - if(!isLive) //EnemyCleaner 발동시 경험치 얻지 못하게 처리 + // 경험치 얻는 함수에도 isLive 필터 추가 + if (!isLive) //EnemyCleaner 발동시 경험치 얻지 못하게 처리 return; exp++; //if 조건으로 최대 필요 경험치에 도달하면 최대필요 경험치로 계속해서 레벨 업하도록 작성 //레벨업 시 필요 경험치의 최대치가 100이면 그 다음 레벨업 시에도 똑같이 100으로 진행된다. + //if (exp == nextExp[level]) + // Min 함수를 사용하여 최고 경험치를 그대로 사용하도록 변경 if (exp == nextExp[Mathf.Min(level, nextExp.Length-1)]){ level ++; exp = 0; + // 게임매니저의 레벨 업 로직에 창을 보여주는 함수 호출 uiLevelUp.Show(); } } + // 시간 정지, 작동하는 함수 두 개 작성 public void Stop() { isLive = false; - Time.timeScale = 0; //timeScale : 유니티의 시간 속도(배율) 0배가 됨 + Time.timeScale = 0; //timeScale : 유니티의 시간 속도(배율)가 0배가 됨 + // 정지될 때는 크기를 0으로, 재개할 때는 크기를 1로 설정하도록 작성 uiJoy.localScale = Vector3.zero; // 멈췄을 때 조이스틱 안보이게 } public void Resume() { isLive = true; + // 1 이상의 수를 넣으면 그만큼 빨라진다 Time.timeScale = 1; //시간 속도 다시 1배 + // 정지될 때는 크기를 0으로, 재개할 때는 크기를 1로 설정하도록 작성 uiJoy.localScale = Vector3.one; // 다시 시작했을 때 조이스틱 보이게 } } diff --git a/Assets/Undead Survivor/Script/Gear.cs b/Assets/Undead Survivor/Script/Gear.cs index b302278..4ff8951 100644 --- a/Assets/Undead Survivor/Script/Gear.cs +++ b/Assets/Undead Survivor/Script/Gear.cs @@ -9,6 +9,7 @@ public class Gear : MonoBehaviour public ItemData.ItemType type; public float rate; + // Weapon과 동일하게 초기화 함수 작성 //초기화 함수 public void Init(ItemData data) { @@ -24,10 +25,11 @@ public void Init(ItemData data) ApplyGear(); } + // Weapon과 동일하게 레벨업 함수 작성 public void LevelUp(float rate) { this.rate = rate; - //레벨업 할 때 로직적용 함수를 호출 + // 장비가 새롭게 추가되거나 레벨업 할 때 로직적용 함수를 호출 ApplyGear(); } @@ -46,12 +48,14 @@ void ApplyGear() } } + // 장갑의 기능인 연사력을 올리는 함수 작성 void RateUp() //공속을 올리는 함수 { //플레이어로 올라가서 모든 weapon을 가져오기 Weapon[] weapons = transform.parent.GetComponentsInChildren(); - - foreach(Weapon weapon in weapons) { + + // foreach문으로 하나씩 순회하면서 타입에 따라 속도 올리기 + foreach (Weapon weapon in weapons) { switch(weapon.id) { //근접무기 case 0: @@ -67,6 +71,7 @@ void RateUp() //공속을 올리는 함수 } } + // 신발의 기능인 이동 속도를 올리는 함수 작성 void SpeedUp() //이속을 올리는 함수 { float speed = 3 * Character.Speed; diff --git a/Assets/Undead Survivor/Script/HUD.cs b/Assets/Undead Survivor/Script/HUD.cs index 0d93d8d..ee072bd 100644 --- a/Assets/Undead Survivor/Script/HUD.cs +++ b/Assets/Undead Survivor/Script/HUD.cs @@ -7,8 +7,11 @@ public class HUD : MonoBehaviour { public enum InfoType{Exp, Level, Kill, Time, Health} //다루게 될 데이터를 미리 열거형 enum으로 선언 + // 선언한 열거형을 타입으로 변수 추가 public InfoType type; + // UI 컴포넌트를 사용할 때는 UnityEngine.UI 네임스페이스 사용 + // Text와 Slider 변수 선언 및 초기화 Text myText; Slider mySlider; @@ -18,6 +21,7 @@ void Awake() mySlider = GetComponent(); } + // LateUpdate에서 switch~case문으로 로직 나누기 void LateUpdate() { switch(type){ @@ -28,10 +32,13 @@ void LateUpdate() mySlider.value = curExp / maxExp; break; case InfoType.Level: - myText.text = string.Format("Lv.{0:F0}",GameManager.instance.level); //Format : 각 숫가 인자값을 지정된 형태의 문자열로 만들어주는 함수 + // Format(Format을 쓸 타입, 해당 Format에 적용되는 데이터) + // 인자 값의 문자열이 들어갈 자리를 {순번} 형태로 작성 + // F0, F1, F2... : 소수점 자리를 지정 //Format(바꿔서 쓰는 값, 바꾸는 값) F0, F1, F2, ..., : 소수점 자리를 지정 //{인덱스번호 : 숫자에 대한 형태} + myText.text = string.Format("Lv.{0:F0}",GameManager.instance.level); break; case InfoType.Kill: myText.text = string.Format("{0:F0}",GameManager.instance.kill); @@ -40,13 +47,17 @@ void LateUpdate() //흐르는 시간이 아닌 남은 시간부터 구하기 float remainTime = GameManager.instance.maxGameTime - GameManager.instance.gameTime; //남은 시간을 분과 초로 분리 + // 수학 관련 함수들은 모두 Mathf 안에 있다 int min = Mathf.FloorToInt(remainTime / 60); //60으로 나누어 분을 구하되 FloorToInt로 소수점 버리기 + // A % B : A를 B로 나누고 남은 나머지 int sec = Mathf.FloorToInt(remainTime % 60); //60으로 나누고 남은 나머지 - + + // D0, D1, D2... : 자리 수를 지정 myText.text = string.Format("{0:D2}:{1:D2}",min,sec); //D0, D1, D2, ..., : 자리 수를 지정 -> 00:00형식으로 보이게 지정 break; case InfoType.Health: + // 체력 로직은 경험치 코드를 재활용하여 작성 float curHealth = GameManager.instance.health; float maxHealth = GameManager.instance.maxHealth; mySlider.value = curHealth / maxHealth; diff --git a/Assets/Undead Survivor/Script/Hand.cs b/Assets/Undead Survivor/Script/Hand.cs index ad18233..9580086 100644 --- a/Assets/Undead Survivor/Script/Hand.cs +++ b/Assets/Undead Survivor/Script/Hand.cs @@ -8,10 +8,14 @@ public class Hand : MonoBehaviour public bool isLeft; public SpriteRenderer spriter; + // 플레이어의 스프라이트렌더러 변수 선언 및 초기화 SpriteRenderer player; + + // 오른손의 각 위치를 Vector3 형태로 저장 Vector3 rightPos = new Vector3(0.35f, -0.15f, 0); Vector3 rightPosReverse = new Vector3(-0.15f, -0.15f, 0); - Quaternion leftRot = Quaternion.Euler(0, 0, -35); //왼손의 각회전을 쿼터니언 형태로 저장 + // 왼손의 각 회전을 Quaternion 형태로 저장 + Quaternion leftRot = Quaternion.Euler(0, 0, -35); Quaternion leftRotReverse = Quaternion.Euler(0, 0, -135); void Awake() { @@ -20,16 +24,25 @@ void Awake() void LateUpdate() { + // 플레이어의 반전 상태를 지역변수로 저장 bool isReverse = player.flipX; - if (isLeft){ //근접 무기 - transform.localRotation = isReverse ? leftRotReverse : leftRot; + //근접 무기 + if (isLeft){ + // 왼손 회전에는 localRotation 사용 + transform.localRotation = isReverse ? leftRotReverse : leftRot; spriter.flipY = isReverse; //왼쪽 스프라이트는 Y축 반전 + // 왼손, 오른손의 sortingOrder를 바꿔주기 spriter.sortingOrder = isReverse ? 4 : 6; //반전되었을때는 4번째 순서로 아니면 6번째 } - else { //원거리 무기 + //원거리 무기 + else + { + // 오른손 이동에는 localPosition 사용 transform.localPosition = isReverse ? rightPosReverse : rightPos; + // 오른손 스프라이트는 X축 반전 spriter.flipX = isReverse; //오른쪽 스프라이트는 X축 반전 + // 왼손, 오른손의 sortingOrder를 바꿔주기 spriter.sortingOrder = isReverse ? 6 : 4; } } diff --git a/Assets/Undead Survivor/Script/Item.cs b/Assets/Undead Survivor/Script/Item.cs index 2fc3ee5..30d7c0b 100644 --- a/Assets/Undead Survivor/Script/Item.cs +++ b/Assets/Undead Survivor/Script/Item.cs @@ -3,17 +3,21 @@ using Unity.VisualScripting; using UnityEngine; using UnityEngine.Android; +// 이미지 사용하기 위해 아래 꼭 입력 using UnityEngine.UI; //이걸 받아오지 않으면 Image 사용 불가 public class Item : MonoBehaviour { + // 아이템 관리에 필요한 변수들 선언 public ItemData data; //설정한 아이템 데이터 가져오기 public int level; public Weapon weapon; + // 버튼 스크립트에서 새롭게 작성한 장비 타입의 변수 선언 public Gear gear; Image icon; Text textLevel; + // 아이템 스크립트에 이름과 설명 텍스트 변수 추가 및 초기화 Text textName; Text textDesc; @@ -33,11 +37,15 @@ void Awake() textName.text = data.itemName; } + // 레벨 텍스트 로직은 OnEnable로 이동 //사라졌다가 나타났다가 할 것이므로 활성화되었을 때 자동으로 실행되는 이벤트 함수를 활용 void OnEnable(){ + // LateUpdate에서 레벨 텍스트 갱신 textLevel.text = "Lv." + (level + 1); + + // 아이템 타입에 따라 switch case문으로 로직 분리 //아이템 타입별로 설명 유무가 다르므로 switch로 케이스 나누기 - switch(data.itemType){ + switch (data.itemType){ case ItemData.ItemType.Melee: case ItemData.ItemType.Range: textDesc.text = string.Format(data.itemDesc, data.damages[level] * 100, data.counts[level]); @@ -55,40 +63,47 @@ void OnEnable(){ } //void LateUpdate() //{ - //// textLevel.text = "LV." + (level + 1); //이렇게 하면 레벨 1부터 시작 - //textLevel.text = "LV." + level; //이렇게 하면 레벨 0부터 시작 - //무기가 몇 레벨인지 측정 + // LateUpdate에서 레벨 텍스트 갱신 + //// textLevel.text = "LV." + (level + 1); //이렇게 하면 레벨 1부터 시작 + //textLevel.text = "LV." + level; //이렇게 하면 레벨 0부터 시작 + //무기가 몇 레벨인지 측정 //} + // 버튼 클릭 이벤트와 연결할 함수 추가 public void OnClick() { + // 아이템 타입을 통해 switch case문 작성해두기 switch (data.itemType){ + //여러개의 case를 붙여서 로직을 실행하게 할 수 있음 case ItemData.ItemType.Melee: case ItemData.ItemType.Range: - //여러개의 case를 붙여서 로직을 실행하게 할 수 있음 - //같은 로직이므로 같이 묶어서 코드 실행 - if (level == 0) { - GameObject newWeapon = new GameObject(); //새로운 게임오브젝트를 코드로 생성 - weapon = newWeapon.AddComponent(); - //AddComponent : 게임 오브젝트에 T 컴포넌트를 추가하는 모습 - //AddComponenet 함수 반환 값을 미리 선언한 변수에 저장 - weapon.Init(data); //weapon의 데이터를 초기화 - } - else { - float nextDamage = data.baseDamage; - int nextCount = 0; + //같은 로직이므로 같이 묶어서 코드 실행 + if (level == 0) { + GameObject newWeapon = new GameObject(); //새로운 게임오브젝트를 코드로 생성 + //AddComponent : 게임 오브젝트에 T 컴포넌트를 추가하는 모습 + //AddComponenet 함수 반환 값을 미리 선언한 변수에 저장 + weapon = newWeapon.AddComponent(); + // Ctrl + 마우스 클릭으로 함수를 클릭하면 해당 함수 내용으로 바로 이동 + // Weapon 초기화 함수에 스크립터블 오브젝트를 매개변수로 받아 활용 + weapon.Init(data); //weapon의 데이터를 초기화 + } + else { + float nextDamage = data.baseDamage; + int nextCount = 0; - //처음 이후의 레벨업은 데미지와 횟수를 계산 - nextDamage += data.baseDamage * data.damages[level]; - nextCount += data.counts[level]; + //처음 이후의 레벨업은 데미지와 횟수를 계산 + nextDamage += data.baseDamage * data.damages[level]; + nextCount += data.counts[level]; - //weapon에 작성된 레벨업 함수를 활용하여 레벨업 적용 - weapon.LevelUp(nextDamage, nextCount); - } - level++; - break; + //weapon에 작성된 레벨업 함수를 활용하여 레벨업 적용 + weapon.LevelUp(nextDamage, nextCount); + } + // 레벨 값을 올리는 로직을 무기, 장비 case 안쪽으로 이동 + level++; + break; case ItemData.ItemType.Glove: case ItemData.ItemType.Shoe: + // 무기 로직과 마찬가지로 최초 레벨업은 게임오브젝트 생성 로직을 작성 if (level == 0) { GameObject newGear = new GameObject(); gear = newGear.AddComponent(); //AddComponenet 함수 반환 값을 미리 선언한 변수에 저장 @@ -98,13 +113,21 @@ public void OnClick() float nextRate = data.damages[level]; gear.LevelUp(nextRate); } - level++; - break; + // 레벨 값을 올리는 로직을 무기, 장비 case 안쪽으로 이동 + level++; + break; case ItemData.ItemType.Heal: + // 치료 기능의 음료수 로직은 case 문에서 바로 작성 GameManager.instance.health = GameManager.instance.maxHealth; break; } + // 이후 level 값 하나 더하기 + // 레벨 값을 올리는 로직을 무기, 장비 case 안쪽으로 이동 + //level++; + + // 스크립터블 오브젝트에 작성한 레벨 데이터 개수를 넘기지 않게 로직 추가 + // Canvas > LevelUp > Item 0 ~ 4 > Inspector > Button > Interactable의 체크 표시를 없앨 것이다 //버튼이 최대레벨로 도달하면 버튼 클릭이 불가능하도록 설정 if (level == data.damages.Length){ GetComponent