라이더 설치
https://www.jetbrains.com/ko-kr/rider/
자료구조
기본 자료형
https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/builtin-types/built-in-types
배열
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrayExample : MonoBehaviour
{
// 플레이어 점수를 저장하는 배열
private int[] playerScores = new int[5];
// 아이템 이름을 저장하는 배열
private string[] itemNames = { "검", "방패", "포션", "활", "마법서" };
// 적 프리팹을 저장하는 배열
public GameObject[] enemyPrefabs;
// 맵의 타일 타입을 저장하는 2D 배열
private int[,] mapTiles = new int [10, 10];
// 1차원 배열도 같은 메모리 공간을 가짐
private int[] mapTiles2 = new int[100];
}
반복문
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrayExample : MonoBehaviour
{
void Start()
{
for (var i = 0; i < playerScores.Length; i++)
{
playerScores[i] = i;
}
for (int i = 0; i < playerScores.Length; i++)
{
Debug.Log(playerScores[i]);
}
// 2차원 배열
for (int i = 0; i < mapTiles.GetLength(0); i++)
{
for (int j = 0; j < mapTiles.GetLength(1); j++)
{
}
}
}
}
Max, Average
Linq를 이용하여 자료구조를 편리하게 사용해보자
ex. 최고 점수 : Max, 평균 점수 : Average
void PlayerScoresExample()
{
// 플레이어 점수 할당
for (int i = 1; i < playerScores.Length; i++)
{
playerScores[i] = Random.Range(100, 1000);
}
int maxValue = 0;
for (var i = 0; i < playerScores.Length; i++)
{
if (playerScores[i] > maxValue)
{
maxValue = playerScores[i];
}
}
// 최고 점수 찾기
int highestScore = playerScores.Max();
Debug.Log($"최고점수: {highestScore}");
// 평균 점수 계산
int totalValue = 0;
float averageValue = 0;
for (var i = 0; i < playerScores.Length; i++)
{
totalValue += playerScores[i];
}
averageValue = totalValue /= playerScores.Length;
Debug.Log($"평균 점수1: {averageValue:F2}");
// 평균 점수 계산
double averageScore = playerScores.Average();
Debug.Log($"평균 점수: {averageScore:F2}");
}
}
실행해보면 값이 다름
이유는 averageValue가 float 값을 가지지 않았기 때문
따라서 아래처럼 로직을 수정하면 제대로 나옴
averageValue = totalValue / (float)playerScores.Length;
Contains
해당 메서드는 오래걸림
private string[] itemNames = { "검", "방패", "포션", "활", "마법서" };
void ItemInventoryExample()
{
// 랜덤 아이템 선택
int randomIndex = Random.Range(0, itemNames.Length);
string selectedItem = itemNames[randomIndex];
Debug.Log($"선택된 아이템: {selectedItem}");
// 특정 아이템 검색
string searchItem = "포션";
bool hasPotion = itemNames.Contains(searchItem);
Debug.Log($"포션 보유 여부: {hasPotion}");
}
오래 걸리는 이유 : 작동 방식이 아래와 같음
하나하나 다 비교하기 때문
private bool Contains(string itemName)
{
// Array의 Contains를 구현하는 방법
for (var i = 0; i < itemNames.Length; i++)
{
if (itemNames[i] == itemName)
{
return true;
}
}
return false;
}
적 랜덤 위치 생성
void EnemySpawnExample()
{
if (enemyPrefabs != null && enemyPrefabs.Length > 0)
{
// 랜덤 위치에 랜덤 적 생성
Vector3 spawnPosition = new Vector3(Random.Range(-10f, 10f), 0, Random.Range(-10f, 10f));
int randomEnemyIndex = Random.Range(0, enemyPrefabs.Length);
Instantiate(enemyPrefabs[randomEnemyIndex], spawnPosition, Quaternion.identity);
Debug.Log($"적 생성됨: {enemyPrefabs[randomEnemyIndex].name}");
}
}
설명
int randomEnemyIndex = Random.Range(0, enemyPrefabs.Length);
- 배열의 길이를 기반으로 랜덤 인덱스를 생성.
- Random.Range(0, enemyPrefabs.Length)는 [0, 배열 길이) 범위의 정수 중 하나를 반환.
- 이 값을 사용해 적 프리팹 배열에서 적 하나를 선택.
- Instantiate: Unity에서 게임 오브젝트(여기서는 적 프리팹)를 생성하는 함수.
- enemyPrefabs[randomEnemyIndex]: 선택된 적 프리팹.
- spawnPosition: 앞서 랜덤하게 생성된 위치.
- Quaternion.identity: 회전을 초기화(적이 정방향으로 생성됨).
간단한 맵 생성
void MapGenerationExample()
{
for (int x = 0; x < mapTiles.GetLength(0); x++)
{
for (int y = 0; y < mapTiles.GetLength(1); y++)
{
mapTiles[x, y] = Random.value > 0.8f ? 1 : 0;
}
}
// 맵 출력
string mapString = "생성된 맵:\n";
for (int x = 0; x < mapTiles.GetLength(0); x++)
{
for (int y = 0; y < mapTiles.GetLength(1); y++)
{
mapString += mapTiles[x, y] == 1 ? "■" : "□";
}
mapString += "\n";
}
Debug.Log(mapString);
}
콘솔에 찍는 대신 큐브를 생성해보자
public GameObject Cube;
public GameObject Sphere;
// 맵의 타일 타입 저장하는 2D 배열
private GameObject[,] CubeTiles = new GameObject[10, 10];
void MapGenerationExample()
{
for (int x = 0; x < mapTiles.GetLength(0); x++)
{
for (int y = 0; y < mapTiles.GetLength(1); y++)
{
mapTiles[x, y] = Random.value > 0.8f ? 1 : 0;
}
}
// 맵 출력
// string mapString = "생성된 맵:\n";
for (int x = 0; x < mapTiles.GetLength(0); x++)
{
for (int y = 0; y < mapTiles.GetLength(1); y++)
{
// 방법 1. 삼항 연산자를 사용하고 그 객체를 관리하기 위해 배열에 넣기
//CubeTiles[x,y] = mapTiles[x, y] == 1 ? Instantiate(Cube) : null;
// 방법 2. 조건문을 통해 그 객체를 관리하기 위해 배열에 넣기
if (mapTiles[x, y] == 1)
{
CubeTiles[x, y] = Instantiate(Cube, new Vector3(x - 5, y - 5, 0), Quaternion.identity);
}
else
{
CubeTiles[x, y] = Instantiate(Sphere, new Vector3(x - 5, y - 5, 0), Quaternion.identity);
}
}
}
}
결과물
Instantiate
Unity에서는 게임 오브젝트(캐릭터, 벽, 몬스터 등)를 만들기 위해 Instantiate라는 함수 사용
Instantiate를 사용하면 "복사"를 통해 게임 오브젝트를 씬(scene)에 추가 가능
Instantiate(Cube, new Vector3(x - 5, y - 5, 0), Quaternion.identity);
// Instantiate("Prefab", "위치", "회전")
- Cube
- 복사하고 싶은 원본 프리팹(prefab)
- Unity 프로젝트에 Cube라는 게임 오브젝트가 있어야 함
- new Vector3(x - 5, y - 5, 0)
- 새로 생성된 오브젝트를 배치할 위치를 결정
- Vector3는 Unity에서 위치를 나타내는 값으로, x, y, z 좌표 설정
- 여기서는:
- x - 5: 맵의 중앙을 기준으로 왼쪽(-)이나 오른쪽(+)으로 이동
- y - 5: 맵의 중앙을 기준으로 아래(-)나 위(+)로 이동
- z = 0: 2D 게임이기 때문에 높이(z축)는 0으로 고정
- Quaternion.identity
- 새로 생성된 오브젝트의 회전 값 설정
- Quaternion.identity는 "회전 없음"
- => 오브젝트가 원래 방향 그대로 생성됨
오늘 전체 코드
using System.Linq;
using UnityEngine;
public class ArrayExamples : MonoBehaviour
{
#region Values
// 데이터 관리를 용이하게 하기 위해
// 상수화 시킨 숫자를 쓴다.
private const int ARRAY_SIZE = 10;
// 플레이어 점수를 저장하는 배열
private int[] playerScores = new int[ARRAY_SIZE];
// 아이템 이름을 저장하는 배열
private string[] itemNames = { "검", "방패", "포션", "활", "마법서" };
// 적 프리팹을 저장하는 배열
public GameObject[] enemyPrefabs;
// 맵의 타일 타입을 저장하는 2D 배열
private int[,] mapTiles = new int[10, 10];
public GameObject Cube;
public GameObject Sphere;
private GameObject[,] CubeTiles = new GameObject[10, 10];
#endregion
void Start()
{
// PlayerScoresExample();
// ItemInventoryExample();
// EnemySpawnExample();
MapGenerationExample();
}
void PlayerScoresExample()
{
// 플레이어 점수 할당
for (int i = 0; i < playerScores.Length; i++)
{
// 100 ~ (1000 - 1) 사이의 랜덤한 값을 할당한다.
// Random에는 System의 Random이 있고
// Unity의 Random이 있는데
// 우리는 유니티 Random을 활용한다.
playerScores[i] = Random.Range(100, 1000);
}
#region MaxValue찾기
// 방법 1
int maxValue = 0;
for (var i = 0; i < playerScores.Length; i++)
{
// maxValue보다 playerScores[i]의 값이 크다면
if (playerScores[i] > maxValue)
{
// 제일 큰 값은 playerScores[i]으로 갱신된다.
maxValue = playerScores[i];
}
}
Debug.Log($"최고 점수1: {maxValue}");
// 방법 2
// 위의 MaxValue 찾기를 Linq를 이용해 C#에 미리 구현된 기능을 사용해서 간단히 표현할수 있다.
// 최고 점수 찾기
int highestScore = playerScores.Max();
Debug.Log($"최고 점수2: {highestScore}");
#endregion
#region Average 계산하기
// 방법 1
int totalValue = 0;
float averageValue = 0;
// 반복문을 돌면서 playerScores를 모두 더한다.
for (var i = 0; i < playerScores.Length; i++)
{
totalValue += playerScores[i];
}
// 정수 / 정수는 정수밖에 안나오므로 정수 / 실수를 해서 실수계산이 이뤄지도록 한다.
averageValue = totalValue / (float)playerScores.Length;
Debug.Log($"평균 점수1: {averageValue:F2}");
// 방법 2
// 평균 점수 계산
double averageScore = playerScores.Average();
Debug.Log($"평균 점수2: {averageScore:F2}");
#endregion
}
void ItemInventoryExample()
{
// 랜덤 아이템 선택
int randomIndex = Random.Range(0, itemNames.Length);
string selectedItem = itemNames[randomIndex];
Debug.Log($"선택된 아이템: {selectedItem}");
string itemName = "포션";
// array에서 순차적으로 요소 값을 찾을 때 linq의 Contains를 사용하는데
// Contatins를 직접 구현한 내용인다.
// Contains 직접 구현
bool hasPotion1 = Contains(itemName);
Debug.Log($"포션 보유 여부: {hasPotion1}");
// 특정 아이템 검색
string searchItem = "방패";
bool hasPotion2 = itemNames.Contains(searchItem);
Debug.Log($"포션 보유 여부: {hasPotion2}");
}
// 접근 제어자 (public, protected, private, internal) / 반환타입 함수 / 이름 / 매개 변수
// 배열의 요소들에 순차적으로 접근해서 매개변수의 값과 일치하는 요소가 있는지 체크하여 반환한다.
private bool Contains(string itemName)
{
// Array의 Contains을 구현하는 방법
for (var i = 0; i < itemNames.Length; i++)
{
if (itemNames[i] == itemName)
{
return true;
}
}
return false;
}
// 랜덤한 포지션에 enemyPrefabs의 요소중에 랜덤한 프리팹을 골라 생성하는 코드
void EnemySpawnExample()
{
if (enemyPrefabs != null && enemyPrefabs.Length > 0)
{
// 랜덤 위치에 랜덤 적 생성
Vector3 spawnPosition = new Vector3(Random.Range(-10f, 10f), 0, Random.Range(-10f, 10f));
int randomEnemyIndex = Random.Range(0, enemyPrefabs.Length);
Instantiate(enemyPrefabs[randomEnemyIndex], spawnPosition, Quaternion.identity);
Debug.Log($"적 생성됨: {enemyPrefabs[randomEnemyIndex].name}");
}
else
{
Debug.LogWarning("적 프리팹이 할당되지 않았습니다.");
}
}
// 2차원 배열을 사용하여 타일을 표현하고 타일의 요소에 따라서 박스와 스피어로 표현한 함수
void MapGenerationExample()
{
// 간단한 맵 생성 (0: 빈 공간, 1: 벽)
for (int x = 0; x < mapTiles.GetLength(0); x++)
{
for (int y = 0; y < mapTiles.GetLength(1); y++)
{
// Random의 value는 0 ~ 1사이의 값이 반환되는데
// 삼항 연산자를 활용하여 20%의 확률로 1이 80%의 확률로 0이 tiles에 저장되도록 한다.
mapTiles[x, y] = Random.value > 0.8f ? 1 : 0;
}
}
// 맵 출력
// string mapString = "생성된 맵:\n";
for (int x = 0; x < mapTiles.GetLength(0); x++)
{
for (int y = 0; y < mapTiles.GetLength(1); y++)
{
// 방법 1. 삼항 연산자를 사용하고 그 객체를 관리하기 위해 배열에 넣기
//CubeTiles[x,y] = mapTiles[x, y] == 1 ? Instantiate(Cube) : null;
// 방법 2. 조건문을 통해 그 객체를 관리하기 위해 배열에 넣기
if (mapTiles[x, y] == 1)
{
CubeTiles[x, y] = Instantiate(Cube, new Vector3(x - 5, y - 5, 0), Quaternion.identity);
}
else
{
CubeTiles[x, y] = Instantiate(Sphere, new Vector3(x - 5, y - 5, 0), Quaternion.identity);
}
}
}
}
}
기타
Refactor - Extract Method
함수로 만들고 싶은 부분을 잡고 Extract Method 실행하면
함수 분리 완!
#region
이런 식으로 영역 분리할 수 있음(scope 지정 후 우클로도 가능)
사담
인수인계에 이슈가 있었는지 진도량이 체크가 안되어서 좀 혼란스러운 하루였다ㅎ
시스템도 이제 멘토분들을 적극 활용하는 것으로 바뀌는 것 같은데
뭐 하다보면 적응 되겠지 싶다
일단 기초반은 중간에 나올 수 있대서 신청해봤다ㅎ
기본적인 unity c# 함수를 수업시간에 듣는게 아닌 것 같아서...흠
진행 방식은 아직 잘 모르겠다 '개발자스럽게'한다는데 그럼 스터디를 같이하는게 무슨 의미인지 잘 모르겠는데;
질문이 생기면 질문 창구가 있다 그 정도인가?
이것도 해보면 알겠지...ㅋㅋㅋ
자료구조는 회사 다닐 때 그래도 공부했어서 이슈는 없었는데 새로 보는 유니티 함수들이 이슈였다...
'공부 > [TIL] Game Bootcamp' 카테고리의 다른 글
[멋쟁이사자처럼 부트캠프 TIL] 유니티 게임 개발 3기 : 자료구조(Stack) (2) | 2024.12.04 |
---|---|
[멋쟁이사자처럼 부트캠프 TIL] 유니티 게임 개발 3기 : 자료구조(LinkedList) (1) | 2024.12.03 |
[멋쟁이사자처럼 부트캠프 TIL] 유니티 게임 개발 3기 : Flappy Bird 만들기 (2) | 2024.11.29 |
[멋쟁이사자처럼 부트캠프 TIL] 유니티 게임 개발 3기 : Animation, Light, UI - Text 등 (0) | 2024.11.28 |
[멋쟁이사자처럼 부트캠프 TIL] 유니티 게임 개발 3기 : Animator, Rigidbody 등 (1) | 2024.11.27 |