-
게임개발 6일차 (간단한 슈팅게임 4)게임 개발 2025. 5. 28. 15:09
오늘은 메뉴, 게임오버 Scene을 만들고 버튼들을 만들어 장면들을 옮겨다니도록 만들었다. 그리고 에셋스토어에서 무료 효과음을 받아서 효과음들을 집어넣었다. 어제 오늘 했던 것들 간단하게 코드와 방법들을 리뷰해보려고 한다.
먼저 적의 코드다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy : MonoBehaviour
{
public float speed = 2;
// 피격 시 반짝이는 머티리얼과 기본 머티리얼// 머티리얼 = "게임 오브젝트의 피부"
// 오브젝트가 무슨 색인지, 반짝이는지, 투명한지, 이미지를 입혔는지 등을 설정하는 "껍질"
public Material flashMaterial;
public Material defaultMaterial;
// 추적대상 (플레이어)
public GameObject target;
// 피격 및 사망 사운드
public AudioClip hitSound;
public AudioClip deadSound;
// 적 상태 (스폰 중, 이동 중, 죽는 중)
enum State
{
spawning,
Moving,
Dying
}
State state;
void Start()
{
}
// 적 생성 함수 (외부에서 호출)
public void Spawn(GameObject target) // 추적대상을 인자로 받음
{
this.target = target;
state = State.spawning;
// 체력 초기화 및 애니메이션
GetComponent<Character>().initialize();
GetComponent<Animator>().SetTrigger("Spawn"); // 스폰애니메이션
// 충돌 꺼두기 (스폰 애니메이션 중 무적)
GetComponent<Collider2D>().enabled = false;
// 일정 시간 후 이동 시작
Invoke("StartMoving", 1f);
}
// 이동 시작 (스폰 애니메이션 후 호출)
void StartMoving()
{
GetComponent<Collider2D>().enabled = true; // 무적 해제
state = State.Moving; // 이동상태로 변경
}
// 물리 프레임마다 호출 (이동 처리)
private void FixedUpdate()
{
if (state == State.Moving) // 이동상태라면
{
Vector2 direction = target.transform.position - transform.position; // 타겟의벡터와 내 벡터를 뺀 상대?벡터
transform.Translate(direction.normalized * speed * Time.fixedDeltaTime); // 타겟 방향으로 이동
// 이동 방향에 따라 스프라이트 좌우 반전
if (direction.x < 0)
{
GetComponent<SpriteRenderer>().flipX = true;
}
else
{
GetComponent<SpriteRenderer>().flipX = false;
}
}
}
// 충돌 처리
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Bullet") // 총알과 충돌하면
{
// 총알 데미지 가져오기
float d = collision.gameObject.GetComponent<Bullet>().damage;
// 피격 처리
if (GetComponent<Character>().Hit(d)) // 살아있는 경우
{
GetComponent<AudioSource>().PlayOneShot(hitSound); // 맞는 사운드
Flash(); // 반짝 함수
}
else // 체력 0 → 사망
{
GetComponent<AudioSource>().PlayOneShot(deadSound); //죽는 사운드
Die(); // 죽음 함수
}
}
}
// 반짝이 머티리얼 적용
void Flash()
{
GetComponent<SpriteRenderer>().material = flashMaterial; // 순간 반짝임
Invoke("AfterFlash", 0.2f);
}
// 머티리얼 복원
void AfterFlash()
{
GetComponent<SpriteRenderer>().material = defaultMaterial; // 일정 시간 후 원래 머티리얼로 복원
}
// 사망 처리
void Die()
{
state = State.Dying; // 죽음 상태
GetComponent<Animator>().SetTrigger("Die"); // 죽는 애니메이션 재생
Invoke("AfterDying", 0.8f); // 애니메이션 후 비활성화
}
// 오브젝트 비활성화 (실제로 제거하지 않고 끔)
void AfterDying()
{
gameObject.SetActive(false);
}
}
다음은 hp관련한 Character 스크립트다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Character : MonoBehaviour
{
public float MaxHP=3; // 최대 HP
float HP; // 현재 HP
public GameObject HPBar; // HP바
float HPMaxWidth; // HP바의 최대길이
void Start()
{
HP = MaxHP; // 현재 HP를 최대 HP로 설정
if (HPBar != null) // 적은 HPBar가 없기 때문에 조건 설정
{
HPMaxWidth = HPBar.GetComponent<RectTransform>().sizeDelta.x;// HPBar의 현재 가로 길이(x)를 저장함 (기본 최대 길이로 사용하려고)
}
}
public void initialize()
{
HP = MaxHP; // 적이 비활성화되었다가 다시 활성화되었을 때 체력을 최대로 회복시켜줌
}
public bool Hit(float damage) // 살아있으면 true
{
HP -= damage;
if (HP < 0)
{
HP = 0;
}
if (HPBar != null) // 플레이어만
{
HPBar.GetComponent<RectTransform>().sizeDelta = new Vector2(HP / MaxHP * HPMaxWidth,
HPBar.GetComponent<RectTransform>().sizeDelta.y); // Y는 그대로 두고 X를 떨어진 HP만큼 줄어들게 만듬 (이를 위해서 미리 HP바를 좌측을 기준으로 정렬해놓음)
}
return HP > 0; // 살아있으면 true
}
}
다음은 적의 스폰과 생존시간을 책임지는 GamaManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro; // 생존 시간용 함수 위한 선언
public class GameManager : MonoBehaviour
{
public GameObject player;
public float spawnTerm = 2; // 스폰주기
public float minSpawnTerm = 1; // 최소 스폰주기
public float fasterEverySpawn = 0.4f; // 스폰주기 감소 변수
float timeAfterLastSpawn; // 스폰후 지난 시간
public TextMeshProUGUI scoreText; // 생존시간 텍스트
float score; // 생존 시간
void Start()
{
timeAfterLastSpawn = 0;
score = 0;
}
void Update()
{
timeAfterLastSpawn += Time.deltaTime;
score += Time.deltaTime;
if (timeAfterLastSpawn > spawnTerm) // 마지막스폰시간이 스폰주기에 닿으면
{
timeAfterLastSpawn -= spawnTerm; // 마지막스폰시간에서 스폰주기를 빼주고
SpawnEnemy(); // 적을 스폰하고
spawnTerm -= fasterEverySpawn; // 스폰주기를 줄임
if (spawnTerm < minSpawnTerm) // 스폰주기는 최소스폰주기보다 줄지 않음
{
spawnTerm = minSpawnTerm;
}
}
scoreText.text = ((int)score).ToString(); // 생존시간을 텍스트로 만들어줌
}
void SpawnEnemy() // 적 스폰
{
float x = Random.Range(-16f, 15f); // 스폰 범위
float y = Random.Range(-15f, 10f);
GameObject obj = GetComponent<ObjectPool>().Get(); // 오브젝트 풀에서 가져옴
obj.transform.position = new Vector3(x, y, 0); // 스폰범위를 정해줌
obj.GetComponent<Enemy>().Spawn(player); // player를 타겟으로 스폰시킴
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.SceneManagement; // 장면전환용 선언
public class Player : MonoBehaviour
{
public float speed=10;
Vector3 move;
public GameObject bulletPrefab;
public Material flashMaterial;
public Material defaultMaterial;
public AudioClip shotSound;
public AudioClip hitSound;
public AudioClip deadSound;
void Start()
{
}
void Update()
{
move = Vector3.zero;
if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.A))
{
move += Vector3.left;
}
if (Input.GetKey(KeyCode.RightArrow) || Input.GetKey(KeyCode.D))
{
move += Vector3.right;
}
if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.W))
{
move += Vector3.up;
}
if (Input.GetKey(KeyCode.DownArrow) || Input.GetKey(KeyCode.S))
{
move += Vector3.down;
}
move = move.normalized;
if (move.x < 0)
{
GetComponent<SpriteRenderer>().flipX = true;
}
else
{
GetComponent<SpriteRenderer>().flipX = false;
}
if (move.magnitude > 0)
{
GetComponent<Animator>().SetTrigger("Move");
}
else
{
GetComponent<Animator>().SetTrigger("Stop");
}
if (Input.GetMouseButtonDown(0))
{
Shoot();
}
}
private void FixedUpdate()
{
transform.Translate(move * speed * Time.fixedDeltaTime);
}
void Shoot()
{
GetComponent<AudioSource>().PlayOneShot(shotSound);
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
worldPosition.z = 0;
worldPosition -= (transform.position + new Vector3(0, -0.5f, 0));
GameObject newBullet = GetComponent<ObjectPool>().Get();
if(newBullet != null)
{
newBullet.transform.position = transform.position + new Vector3(0, -0.5f, 0);
newBullet.GetComponent<Bullet>().Direction = worldPosition;
}
}
private void OnCollisionEnter2D(Collision2D collision) // 충돌 처리
{
if (collision.gameObject.tag == "Enemy") // 적과 충돌 했을 때
{
if (GetComponent<Character>().Hit(1)) //생존 (1데미지를 받았을 때 true라면)
{
Flash(); // 반짝
}
else //죽음
{
Die(); // 사망
}
}
}
void Flash()
{
GetComponent<AudioSource>().PlayOneShot(hitSound);
GetComponent<SpriteRenderer>().material = flashMaterial; // 하얗게됨
Invoke("AfterFlash", 0.2f);
}
void AfterFlash()
{
GetComponent<SpriteRenderer>().material = defaultMaterial; // 원상복귀
}
void Die()
{
GetComponent<AudioSource>().PlayOneShot(deadSound);
GetComponent<Animator>().SetTrigger("Die");
Invoke("AfterDying", 0.8f); //죽는 애니메이션 보여주는 시간
}
void AfterDying()
{
SceneManager.LoadScene("GameOverScene"); // 게임오버장면으로 넘김
}
}
다음은 메인메뉴의 MainMenuManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; // 장면을 넘기기 위한 선언
public class MenuManager : MonoBehaviour
{
void Start()
{
}
public void OnPressGameStart() // 게임스타트 버튼 누르면
{
SceneManager.LoadScene("GameScene"); // 게임장면으로 전환
}
public void OnPressExit() // 종료버튼 누르면
{
Application.Quit(); // 종료
}
}
게임오버를 처리해줄 게임오버 매니저다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameOverManager : MonoBehaviour
{
void Start()
{
}
public void OnPressTryAgain()
{
SceneManager.LoadScene("GameScene");
}
public void OnPressMainMenu()
{
SceneManager.LoadScene("MainMenuScene");
}
}
선생님의 가르침을 끝났지만 아직 더 개선할 여지가 남아있어서 내일 추가해보려고 한다.
먼저 내가 죽었을 때 죽은 상태에서 못움직이게 바꾸고 싶다.
적이 나를 못밀게 바꾸고 싶다.
적이 나에게 충돌하고 나서 계속 비빌 때 1초간격으로 데미지를 계속 입게하고 싶다. (지금은 계속붙어있으면 무적)
적이 죽고나서 죽음 애니메이션이 진행중일 때 총알을 맞을 때마다 죽음사운드가 반복되는 현상을 제거해보고 싶다.
흥미진진한 배경음을 넣어보고 싶다.
게임오버 장면에서 점수를 보여주고 싶다.
버튼에 마우스를 올렸을 때 올렸다는 표시가 나게 바꿔보고 싶다.
쿨타임이 있는 우클릭 필살기를 만들어보고 싶다.
다양한 적을 만들어보고 싶다.
맵을 한개 더 만들어보고 싶다.
정말 할게 많다!!!!
'게임 개발' 카테고리의 다른 글
게임개발 8일차 (간단한 슈팅게임 6) (0) 2025.06.02 게임개발 7일차 (간단한 슈팅게임 5) (0) 2025.05.29 게임개발 5일차 (간단한 슈팅게임 3) (0) 2025.05.27 게임개발 4일차 (간단한 슈팅게임 2) (1) 2025.05.26 게임개발 3일차 (간단한 슈팅게임 1) (0) 2025.05.25