일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- COSMO
- 모작
- 포토샵
- 채색
- 개발
- menu
- 드로잉
- Aseprite
- layer
- 도트공부
- 서포터즈
- 픽셀아트
- 픽셀 아트
- pixel art
- 스마일게이트
- photoshop
- 자원순환보증금관리센터
- 연습
- 장학팀
- 드로잉 연습
- 에이세프라이트
- TOOL
- 인디게임 개발
- 노하우
- 멋쟁이사자처럼
- 애니메이션
- Pixelart
- 도트
- 기초
- 반환원정대
- Today
- Total
소소한 나의 하루들
2d 종스크롤 슈팅(9) - 텍스트 파일을 이용한 커스텀 배치 구현 본문
출처: https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2&feature=shared
이전에는 랜덤한 위치에서 랜덤한 타입의 적 비행기가 소환되도록 했었다. 이번에는 원하는대로 적 비행기를 소환하는 시스템을 만들어본다. 적 비행기를 소환하기 위해서 필요한 요소가 3가지 있다.
1. 소환하는 시간
2. 적 비행기의 타입
3. 어디에 소환할 것인가
우리는 적이 생성되기위한 스폰지점을 만들어놓았다. 이 스폰지점을 정리해보도록 한다.
#1. 구조체
Spawn 스크립트를 생성한다. 참고
public class Spawn
{
public float delay;
public string type;
public int point;
}
Spawn 스크립트는 구조체로 사용할 것이기 때문에 Monebehavior 상속을 제거하고 오로지 변수만 사용하도록 한다. 지속시간 float형 delay, 타입 string형 type, 소환할 위치(포인트)는 GameManager에서 배열로 관리를 하고 있다. 그래서 인덱스가 있어야하므로 int형 point를 만든다.
이렇게 적 비행기를 소환할 때 필요한 구조체가 완성됐다.
#2. 텍스트 데이터
이제 '언제, 어디에서 누구를 소환할 것인지'를 작성할 것인데, 이것은 c# 스크립트가 아니라 일반 텍스트 파일을 사용하겠다.
윈도우 메모장을 실행한다.
메모장에 구조체 변수 순서에 맞도록 구분자를 넣어서 값을 입력한다. 원하는 적 출현을 위해 다중 데이터를 작성한다.
그리고 저장한다. (Stage 0)
이제 이 텍스트 파일을 에셋에 놓아야하는데 Assets 아래에 Resources라는 폴더를 생성한다.
Resources : 런타임에서 불러오는 에셋이 저장된 폴더
import하거나 드래그해서 Resources 폴더 안에 방금 만들었던 텍스트 파일을 넣는다. 이 텍스트 파일을 읽어서 그것을 리스트로 만든 다음에 GameManager의 SpawnEnemy()에서 소환하면 된다.
다음으로 해야할 것은 '파일을 읽는 것'이다.
public List<Spawn> spawnList;
public int spawnIndex;
public bool spawnEnd;
private void Awake()
{
spawnList = new List<Spawn>();
playerLogic = player.GetComponent<Player>();
enemyObjs = new string[] { "EnemyL", "EnemyM", "EnemyS"};
}
void ReadSpawnFile()
{
//변수 초기화
spawnList.Clear();
spawnIndex = 0;
spawnEnd = false;
}
GameManager에서 파일을 읽을 ReadSpawnFile() 함수를 생성한다. 이 함수에 필요한 변수는 3가지이다.
1. 우리가 방금 만들었던 Spawn이라는 구조체, 이건 클래스(멤버 변수, 멤버 함수(메소드)의 집합체)라고 볼 수 있다. Spawn에 대한 구조체가 담겨있는 리스트 spawnList
2. 리스트에서 구조체를 가져오기 위해 필요한 인덱스 spawnIndex
3. 플래스 변수 spawnEnd
리스트를 비울 때는 리스트명.Clear()
TextAsset textFile = Resources.Load("Stage 0") as TextAsset;
적 출현 변수를 초기화하고, 그 다음 리스폰 파일을 읽는다. 파일을 읽을 때 (c#에서 지원하는) using System.IO 라이브러리를 선언한다. 그리고 텍스트 파일을 읽기 위해 TextAsset 클래스 변수를 생성한다.
Resources.Load("파일명") : Resources 폴더 내 파일 불러오기
*인수로 string으로 텍스트파일 이름을 작성하면 된다. 그리고 이것을 검증하기 위해 as 키워드를 사용한다.
as TextAsset : 타입을 검증하는 연산자 '만약 TextAsset' 타입이 아니라면 그냥 null 처리가 된다.'
StringReader stringReader = new StringReader(textFile.text);
string line = stringReader.ReadLine();
이렇게 텍스트 파일을 불러왔고, 이제 문제열을 읽기위해 StringReader 클래스 변수를 선언한다.
StringReader : 파일 내의 문자열 데이터 읽기 클래스 (System.IO에 있는 클래스)
그리고 불러온 텍스트 파일의 텍스트를 불러온다. 이제 한 줄씩 읽으면 된다.
ReadLine() : 텍스트 데이터를 한 줄씩 반환 (자동 줄 바꿈)
이제 텍스트 파일의 1,S,1이 문자열로 line에 들어가게 된다.
Spawn spawnData = new Spawn();
spawnData.delay = float.Parse(line.Split(',')[0]);
spawnData.type = line.Split(',')[1];
spawnData.point = int.Parse(line.Split(',')[2]);
앞에서 선언한 리스트 spawnList에 저장하면 된다. 이때 구분자는 Split()으로 문자열을 나눠준다.
Split() : 지정한 구분 문자로 문자열을 나누는 함수
쉼표(,)를 기준으로 line을 잘라주면 문자열의 배열이 나온다. {"1", "S", "1"} 그리고 각 변수의 타입도 고려해야하는데 delay는 float형, type은 string형, point는 int형이다.
그래서 형 변환을 해주기 위해 자료형.Parse()를 해준다. 참고
이제 구조체 변수가 다 채워졌으면, 구조체를 spawnList 리스트에 저장해주면 된다.
//GameManager
void ReadSpawnFile()
{
//변수 초기화
spawnList.Clear();
spawnIndex = 0;
spawnEnd = false;
//리스폰 파일 읽기
TextAsset textFile = Resources.Load("Stage 0") as TextAsset;
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;
}
텍스트 파일에서 맨 마지막 12번째 2,M,3을 읽고나면 다음 읽을 텍스트가 없다. 그래서 while문으로 조건을 달아줘야한다. while문으로 텍스트 데이터 끝에 다다를 때까지 반복한다.
stringReader를 통해서 파일을 열었다. 파일을 열었으면, 꼭 닫아줘야한다. stringReader.Close()
StringReader로 열어둔 파일은 작업이 끝난 후 꼭 닫기
//GameManager
private void Awake()
{
spawnList = new List<Spawn>();
playerLogic = player.GetComponent<Player>();
enemyObjs = new string[] { "EnemyL", "EnemyM", "EnemyS"};
ReadSpawnFile();
}
그리고 미리 첫번째 출현 시간을 적용시킨다. 마지막으로 Awake()에서 ReadSpawnFile()을 호출하여 초기화시킨다.
이렇게 텍스트 파일을 한줄씩 읽어서 리스트화시키는 것은 완료되었으니 이제는 데이터에 적용시킨다.
아직은 실행시키면 이전 Update문에 작성한 로직대로 실행될 것이다. (현재 데이터를 반영하지 않은 상태)
#3. 데이터 적용
void SpawnEnemy()
{
int enemyIndex = 0;
switch(spawnList[spawnIndex].type)
{
case "L":
enemyIndex = 0;
break;
case "M":
enemyIndex = 1;
break;
case "S":
enemyIndex = 2;
break;
}
int enemyPoint = spawnList[spawnIndex].point;
//int enemyIndex = Random.Range(0, 3);
//int enemyPoint = Random.Range(0, 9);
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.forward * 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), -1);
}
else
{
rigid.velocity = new Vector2(0, enemyLogic.speed * -1);
}
//리스폰 인덱스 증가
spawnIndex++;
if(spawnIndex == spawnList.Count)
{
spawnEnd = true;
return;
}
//다음 리스폰 딜레이 갱신
nextSpawnDelay = spawnList[spawnIndex].delay;
}
기존 적 생성 로직(SpawnEnemy())을 구조체를 활용한 로직으로 교체한다.
enemyObjs = new string[] { "EnemyL", "EnemyM", "EnemyS"};
switch(spawnList[spawnIndex].type)
{
case "L":
enemyIndex = 0;
break;
case "M":
enemyIndex = 1;
break;
case "S":
enemyIndex = 2;
break;
}
※주의할 점
앞에서 오브젝트 풀링으로 설정한 문자열 배열 인덱스 순서를 고려하여 값을 설정해줘야한다.
적을 하나 소환하면 리스트의 인덱스를 1 올려준다. 만약 인덱스가 리스트의 마지막(리스트 숫자 세는 것은 .Count)이라면, 플래그 spawnEnd 상태를 true로 바꿔준다.
배열.Length
리스트.Count
적 생성이 완료되면 다음 생성을 위한 nextSpawnDelay 시간도 갱신해준다.
※반드시 spawnIndex가 증가한 다음에 nextSpawnDelay를 갱신해줘야한다. (다음에 만들어야할 적 비행기이기 때문에)
private void Update()
{
curSpawnDelay += Time.deltaTime;
if (curSpawnDelay > nextSpawnDelay && !spawnEnd)
{
SpawnEnemy();
curSpawnDelay = 0;
}
//UI Score UPdate
scoreText.text = string.Format("{0:n0}", playerLogic.score);
}
마지막으로 Update()에서 SpawnEnemy() 호출 조건에 플래그 변수를 추가하여 적 생성 조건을 제어한다. curSpawnDelay 초기화는 반드시 해야한다.
이렇게 직접 적 타입과 나오는 시간, 위치를 설정하면 한 위치에서 같은 적이 여러 개 등장하도록 할 수도 있다. 나오는 시간을 조절해서 적 사이의 간격도 조절할 수 있다.
: 텍스트 파일만 수정하면 원하는 적을 원하는 시간, 원하는 위치에 출현하도록 구현 가능
'개발 > 유니티' 카테고리의 다른 글
2d 종스크롤 슈팅(11) - 탄막을 뿜어대는 보스 만들기 (0) | 2024.02.26 |
---|---|
2d 종스크롤 슈팅(10) - 따라다니는 보조무기 만들기 (0) | 2024.02.25 |
2d 종스크롤 슈팅(8) - 최적화의 기본, 오브젝트 풀링 (0) | 2024.02.22 |
2d 종스크롤 슈팅(7) - 원근감있는 무한 배경 만들기 (0) | 2024.02.21 |
2d 종스크롤 슈팅(6) - 아이템과 필살기 구현하기 (0) | 2024.02.20 |