Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 기초
- 백준
- C/C++
- 6월
- 수학
- 개인 프로젝트 - 런앤건
- 코딩 기초 트레이닝
- 3월
- 입문
- 4월
- 유니티
- 다이나믹 프로그래밍
- 개인 프로젝트
- 코딩 테스트
- c++
- 게임 엔진 공부
- 2024년
- todolist
- 2022년
- 5월
- 단계별로 풀어보기
- 자료 구조
- 골드메탈
- 10월
- 2023년
- 프로그래머스
- 2월
- 1월
- 2025년
- 유니티 심화과정
Archives
- Today
- Total
기록 보관소
[Unity/유니티] 기초-2D 종스크롤 슈팅: 텍스트 파일을 이용한 커스텀 배치 구현[B35] 본문
유니티 프로젝트/2D 종스크롤 슈팅
[Unity/유니티] 기초-2D 종스크롤 슈팅: 텍스트 파일을 이용한 커스텀 배치 구현[B35]
JongHoon 2022. 3. 8. 23:06개요
유니티 입문과 독학을 위해서 아래 링크의 골드메탈님의 영상들을 보며 진행 상황 사진 또는 캡처를 올리고 배웠던 점을 요약해서 적는다.
현재는 영상들을 보고 따라하고 배우는 것에 집중할 것이며, 영상을 모두 보고 따라한 후에는 개인 프로젝트를 설계하고 직접 만드는 것이 목표다.
https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2
유니티 강좌 기초 채널 Basic
유니티 개발을 처음 시작하시는 입문자 분들을 위한 기초 채널. [ 프로젝트 ] B00 ~ B12 (BE1) : 유니티 필수 기초 B13 ~ B19 (BE2) : 2D 플랫포머 B20 ~ B26 (BE3) : 2D 탑다운 대화형 RPG B27 ~ B37 (BE4) : 2D 종스크롤
www.youtube.com
2D 종스크롤 슈팅: 텍스트 파일을 이용한 커스텀 배치 구현[B35]
1. 구조체

//Spawn 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawn {
public float delay;
public string type;
public int point;
}
- 적 비행기를 소환하는 시스템을 새로 만들기위해서 구조체로 사용할 Spawn 스크립트 파일을 만들어둔다. 구조체로 사용할 것이므로 Monobehavior의 상속도 제거한다.
2. 텍스트 데이터


- 이 텍스트 파일을 읽어서 리스트로 만든 다음에 적 기체 생성에 사용할 것이다.
//GameManager 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.IO; //파일 읽기용 라이브러리. C#에서 지원
public class GameManager : MonoBehaviour {
string[] enemyObjs;
public Transform[] spawnPoints;
public GameObject player;
public Text scoreText;
public Image[] lifeImage;
public Image[] boomImage;
public GameObject gameOverSet;
public ObjectManager objectManager;
public float nextSpawnDelay;
public float curSpawnDelay;
public List<Spawn> spawnList;
public int spawnIndex;
public bool spawnEnd;
void Awake() {
enemyObjs = new string[] { "EnemyS", "EnemyM", "EnemyL" };
spawnList = new List<Spawn>();
}
void ReadSpawnFile() {
//변수 초기화
spawnList.Clear();
spawnIndex = 0;
spawnEnd = false;
//텍스트 파일 읽기
TextAsset textFile = Resources.Load("Stage 0") as TextAsset; //텍스트 파일 Stage 0 불러오기
StringReader stringReader = new StringReader(textFile.text);
while (stringReader != null) {
string line = stringReader.ReadLine();
Debug.Log(line);
if (line == null)
break;
//스폰 데이터 생성
Spawn spawnData = new Spawn();
spawnData.delay = float.Parse(line.Split(',')[0]);
spawnData.type = line.Split(',')[1];
spawnData.point = int.Parse(line.Split(',')[2]);
spawnList.Add(spawnData);
}
//텍스트 파일 닫기
stringReader.Close();
//첫번째 스폰 딜레이 적용
nextSpawnDelay = spawnList[0].delay;
}
void Update() {
curSpawnDelay += Time.deltaTime;
if (curSpawnDelay > nextSpawnDelay) {
SpawnEnemy();
nextSpawnDelay = Random.Range(0.5f, 3f);
curSpawnDelay = 0;
}
//UI 점수 업데이트
Player playerLogic = player.GetComponent<Player>();
scoreText.text = string.Format("{0:n0}", playerLogic.score);
}
void SpawnEnemy() {
int ranEnemy = Random.Range(0, 3);
int ranPoint = Random.Range(0, 9);
GameObject enemy = objectManager.MakeObj(enemyObjs[ranEnemy]);
enemy.transform.position = spawnPoints[ranPoint].position;
Rigidbody2D rigid = enemy.GetComponent<Rigidbody2D>();
Enemy enemyLogic = enemy.GetComponent<Enemy>();
enemyLogic.player = player;
enemyLogic.objectManager = objectManager;
if (ranPoint == 5 || ranPoint == 6) { //오른쪽 포인트
enemy.transform.Rotate(Vector3.back * 90); //적 기체 스프라이트 돌리기
rigid.velocity = new Vector2(enemyLogic.speed * (-1), -1);
}
else if (ranPoint == 7 || ranPoint == 8) { //왼쪽 포인트
enemy.transform.Rotate(Vector3.back * (-90)); //적 기체 스프라이트 돌리기
rigid.velocity = new Vector2(enemyLogic.speed, -1);
}
else { //중앙 앞쪽 포인트
rigid.velocity = new Vector2(0, enemyLogic.speed * (-1));
}
}
public void UpdateLifeIcon(int life) {
//UI 라이프 모두 투명화
for (int index = 0; index < 3; index++) {
lifeImage[index].color = new Color(1, 1, 1, 0);
}
//UI 남은 라이프 수만큼 활성화
for (int index = 0; index < life; index++) {
lifeImage[index].color = new Color(1, 1, 1, 1);
}
}
public void UpdateBoomIcon(int boom) {
//UI 붐 모두 투명화
for (int index = 0; index < 3; index++) {
boomImage[index].color = new Color(1, 1, 1, 0);
}
//UI 남은 붐 수만큼 활성화
for (int index = 0; index < boom; index++) {
boomImage[index].color = new Color(1, 1, 1, 1);
}
}
public void RespawnPlayer() {
Invoke("RespawnPlayerExe", 2f);
}
void RespawnPlayerExe() {
player.transform.position = Vector3.down * 3.5f; //플레이어 위치 초기화
player.SetActive(true);
Player playerLogic = player.GetComponent<Player>();
playerLogic.isHit = false;
}
public void GameOver() {
gameOverSet.SetActive(true);
}
public void GameRetry() {
SceneManager.LoadScene(0);
}
}


- TextAsset : 텍스트 파일 에셋 클래스
- Resources.Load(string path) : Resources 폴더 내 파일명 path를 불러오는 함수.
- 위 코드에서는 Resources.Load("Stage 0")에 as TextAsset이 붙어서 Stage 0라는 텍스트 파일만을 불러온다. 만약 Stage 0 이름의 파일은 있지만, 텍스트 파일이 아니면 null을 반환한다.
- StringReader : 파일 내의 문자열 데이터 읽기 클래스
- .ReadLine() : 텍스트 데이터를 한 줄씩 반환. (자동 줄 바꿈)
- 열었던 파일은 작업이 끝나면 .Close() 함수로 닫아준다.
3. 데이터 적용
//GameManager 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.IO; //파일 읽기용 라이브러리. C#에서 지원
public class GameManager : MonoBehaviour {
string[] enemyObjs;
public Transform[] spawnPoints;
public GameObject player;
public Text scoreText;
public Image[] lifeImage;
public Image[] boomImage;
public GameObject gameOverSet;
public ObjectManager objectManager;
public float nextSpawnDelay;
public float curSpawnDelay;
public List<Spawn> spawnList;
public int spawnIndex;
public bool spawnEnd;
void Awake() {
enemyObjs = new string[] { "EnemyS", "EnemyM", "EnemyL" };
spawnList = new List<Spawn>();
ReadSpawnFile();
}
void ReadSpawnFile() {
//변수 초기화
spawnList.Clear();
spawnIndex = 0;
spawnEnd = false;
//텍스트 파일 읽기
TextAsset textFile = Resources.Load("Stage 0") as TextAsset; //텍스트 파일 Stage 0 불러오기
StringReader stringReader = new StringReader(textFile.text);
while (stringReader != null) {
string line = stringReader.ReadLine();
if (line == null)
break;
//스폰 데이터 생성
Spawn spawnData = new Spawn();
spawnData.delay = float.Parse(line.Split(',')[0]);
spawnData.type = line.Split(',')[1];
spawnData.point = int.Parse(line.Split(',')[2]);
spawnList.Add(spawnData);
}
//텍스트 파일 닫기
stringReader.Close();
//첫번째 스폰 딜레이 적용
nextSpawnDelay = spawnList[0].delay;
}
void Update() {
curSpawnDelay += Time.deltaTime;
if (curSpawnDelay > nextSpawnDelay && !spawnEnd) {
SpawnEnemy();
curSpawnDelay = 0;
}
//UI 점수 업데이트
Player playerLogic = player.GetComponent<Player>();
scoreText.text = string.Format("{0:n0}", playerLogic.score);
}
void SpawnEnemy() {
int enemyIndex = 0;
switch(spawnList[spawnIndex].type) {
case "S":
enemyIndex = 0;
break;
case "M":
enemyIndex = 1;
break;
case "L":
enemyIndex = 2;
break;
}
int enemyPoint = spawnList[spawnIndex].point;
GameObject enemy = objectManager.MakeObj(enemyObjs[enemyIndex]);
enemy.transform.position = spawnPoints[enemyPoint].position;
Rigidbody2D rigid = enemy.GetComponent<Rigidbody2D>();
Enemy enemyLogic = enemy.GetComponent<Enemy>();
enemyLogic.player = player;
enemyLogic.objectManager = objectManager;
if (enemyPoint == 5 || enemyPoint == 6) { //오른쪽 포인트
enemy.transform.Rotate(Vector3.back * 90); //적 기체 스프라이트 돌리기
rigid.velocity = new Vector2(enemyLogic.speed * (-1), -1);
}
else if (enemyPoint == 7 || enemyPoint == 8) { //왼쪽 포인트
enemy.transform.Rotate(Vector3.back * (-90)); //적 기체 스프라이트 돌리기
rigid.velocity = new Vector2(enemyLogic.speed, -1);
}
else { //중앙 앞쪽 포인트
rigid.velocity = new Vector2(0, enemyLogic.speed * (-1));
}
//스폰 인덱스 증가
spawnIndex++;
if (spawnIndex == spawnList.Count) {
spawnEnd = true;
return;
}
//다음 리스폰 딜레이 갱신
nextSpawnDelay = spawnList[spawnIndex].delay;
}
public void UpdateLifeIcon(int life) {
//UI 라이프 모두 투명화
for (int index = 0; index < 3; index++) {
lifeImage[index].color = new Color(1, 1, 1, 0);
}
//UI 남은 라이프 수만큼 활성화
for (int index = 0; index < life; index++) {
lifeImage[index].color = new Color(1, 1, 1, 1);
}
}
public void UpdateBoomIcon(int boom) {
//UI 붐 모두 투명화
for (int index = 0; index < 3; index++) {
boomImage[index].color = new Color(1, 1, 1, 0);
}
//UI 남은 붐 수만큼 활성화
for (int index = 0; index < boom; index++) {
boomImage[index].color = new Color(1, 1, 1, 1);
}
}
public void RespawnPlayer() {
Invoke("RespawnPlayerExe", 2f);
}
void RespawnPlayerExe() {
player.transform.position = Vector3.down * 3.5f; //플레이어 위치 초기화
player.SetActive(true);
Player playerLogic = player.GetComponent<Player>();
playerLogic.isHit = false;
}
public void GameOver() {
gameOverSet.SetActive(true);
}
public void GameRetry() {
SceneManager.LoadScene(0);
}
}





- 이제 적 기체들은 랜덤 생성이 아닌, 텍스트 파일을 이용해 정해진 패턴대로 생성된다.
'유니티 프로젝트 > 2D 종스크롤 슈팅' 카테고리의 다른 글
[Unity/유니티] 기초-2D 종스크롤 슈팅: 탄막을 뿜어대는 보스 만들기[B37] (0) | 2022.03.11 |
---|---|
[Unity/유니티] 기초-2D 종스크롤 슈팅: 따라다니는 보조 무기 만들기[B36] (0) | 2022.03.09 |
[Unity/유니티] 기초-2D 종스크롤 슈팅: 최적화의 기본, 오브젝트 풀링[B34] (0) | 2022.03.06 |
[Unity/유니티] 기초-2D 종스크롤 슈팅: 원근감있는 무한 배경만들기[B33] (0) | 2022.03.05 |
[Unity/유니티] 기초-2D 종스크롤 슈팅: 아이템과 필살기 구현하기[B32] (0) | 2022.03.04 |