2023. 12. 7. 21:06ㆍSKKU DT
유니티 네트워크
- System.IO 입출력 namespace 사용하기
- JSON 파일 사용하기
- XML 파일 사용하기
- CSV 파일 사용하기
- 다른 형식의 파일 읽기
- ScriptableObject 파일 읽기
- REST API -사이트 접근
- 에셋 관리 시스템
System.IO 네임스페이스 -텍스트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class Datapath : MonoBehaviour
{
string path = "c:/tmp/example.txt";
//string filePath = Application.dataPath + "/MyFolder/MyFile.txt"; //상대 경로
void Start()
{
string readText = File.ReadAllText(path);
Debug.Log(readText);
File.WriteAllText(path, "Hello.unity!");
}
}
파일 경로에 txt 파일이 있어야 한다. 스크립트를 유니티 안에서 실행하면 파일에 "Hello.unity!"가 적혀있는 것을 볼 수 있다.
상대경로 사용
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class Datapath : MonoBehaviour
{
//string path = "c:/tmp/example.txt";
string filePath = Application.dataPath + "/MyFolder/MyFile.txt"; //상대 경로
void Start()
{
string readText = File.ReadAllText(filePath);
Debug.Log(readText);
File.WriteAllText(filePath, "Hello.unity!");
}
}
상대 경로를 사용하면 내가 작업하고 있는 프로젝트의 "Assets/MyFolder/MyFile.txt"에 텍스트 파일을 만들어 놓으면 텍스트 파일에 글이 작성된다. 만약 "/MyFolder/MyFile.txt" 경로 자체가 없다면 "MyFolder" 폴더가 생성되기는 한다.
텍스트 내용을 바꾸면 텍스트 전체가 바뀐다.
System.IO 네임스페이스 -이미지 파일 불러오기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
public class LoadImageFromFile : MonoBehaviour
{
public RawImage displayImage;
void Start()
{
StartCoroutine(LoadImage("c:/tmp/low_city.png"));
}
IEnumerator LoadImage(string filePath)
{
UnityWebRequest request = UnityWebRequestTexture.GetTexture(filePath);
yield return request.SendWebRequest(); //언제까지 기다릴 것인가 -답을 받았으면 return
if(request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
{
Debug.LogError("Error: " + request.error);
}
else
{
Texture2D texture = DownloadHandlerTexture.GetContent(request);
displayImage.texture = texture;
}
}
}
위 스크립트를 GameObject에 넣고 해당 GameObject의 컴포넌트 중 비어있는 Display Image란에 [UI] - [RawImage]를 만들어서 끌어다 놓으면 유니티를 실행하면 이미지가 경로에 있는 이미지로 바뀌게 된다.
프로젝트 실행을 중지하면 이미지도 원래대로 돌아간다.
JSON 파일에 접근하기
객체형과 배열형이 있다.
메모장을 열어서 다음과 같이 쓸 수 있다. 콜론 앞은 자료형의 느낌, 콜론 뒤는 이름.
txt 유형을 json으로 바꾼 후 스크립트에서 명시해 놓은 경로에 저장한다.
{ "name": "john", "age": 30, "gender": "male", "height": 180}
스크립트를 작성해서 GameObject에 넣고 실행하면 Console창에 표시가 된다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
public class JsonLoader : MonoBehaviour
{
//JSON 데이터에 해당하는 C# 클래스, 파싱 부분. 많은 데이터 중에서 필요한 데이터만 가져와야 한다.
[System.Serializable]
public class Person
{
public string name;
public int age;
public string gender;
public int height;
}
void Start()
{
//JSON 파일의 경로
string path = "c:/tmp/object.json";
//JSON 파일을 읽고 문자열로 변환
string jsonString = File.ReadAllText(path);
//JSON 문자열을 Person 객체로 변환
Person person = JsonUtility.FromJson<Person>(jsonString);
//데이터 출력 (예시)
Debug.Log("Name: " + person.name);
Debug.Log("Age: " + person.age);
Debug.Log("Gender: " + person.gender);
Debug.Log("Height: " + person.height);
}
}
JSON 파일 읽기 -Array
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class JsonArrayLoader : MonoBehaviour
{
//JSON 배열을 위한 레퍼 클래스
[System.Serializable]
public class StringArray
{
public string[] array;
}
void Start()
{
string path = "c:/tmp/array.json";
string jsonString = File.ReadAllText(path);
//JSON 문자열은 StringArray 객체로 변환
//JSONUtility는 배열을 직접 파싱하지 않으므로 레퍼 클래스를 사용한다.
StringArray fruits = JsonUtility.FromJson<StringArray>("{\"array\":" + jsonString + "}");
//데이터 출력 (예시)
foreach (string fruit in fruits.array)
{
Debug.Log(fruit);
}
}
JSON에는 대괄호를 이용해서 배열을 입력해 놓으면 스크립트가 실행될 때 Console창에 배열이 나타난다.
["apple", "banana", "cherry", "orange"]
**JSON 검사기
문법을 틀리면 오류를 찾기 힘들기 때문에 JSON 검사기를 사용할 수도 있다.
XML 파일 읽기
XML을 파싱하기 위해서는 System.Xml을 사용한다.
메모장으로 Xml 파일을 하나 만들고,
<Person>
<Name>John Doe</Name>
<Age>30</Age>
<Gender>Male</Gender>
</Person>
스크립트를 만들어서 GameObject에 넣으면, 결과 값이 출력된다. Person 클래스를 따로 C# 스크립로 만들어도 작동한다.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;
public class XmlLoader : MonoBehaviour
{
[Serializable]
public class Person
{
public string Name;
public int Age;
public string Gender;
}
void Start()
{
//XML 파일의 경로
string path = "c:/tmp/JohnDoe.xml";
//XML 파일을 읽고 문자열로 변환
string xmlString = File.ReadAllText(path);
//XmlSerializer 객체를 생성하고, Person 클래스 타입을 지정
XmlSerializer serializer = new XmlSerializer(typeof(Person));
//문자열을 StringReader 객체로 변환
using (StringReader reader = new StringReader(xmlString))
{
//XML 데이터를 Person 객체로 변환
Person person = (Person)serializer.Deserialize(reader);
//데이터 출력 (예시)
Debug.Log("Name: " + person.Name);
Debug.Log("Age: " + person.Age);
Debug.Log("Gender: " + person.Gender);
}
}
}
CSV 파일 읽기
첫 행은 데이터가 들어가지 않는다. csv 파일도 메모장으로 작성할 수 있다.
메모장에 다음과 같이 입력한 후 csv 파일 형식으로 저장한다.
ID, Name, Age
1, Jone, 30
2, Jane, 25
3, Bob, 22
아래와 같은 코드를 작성한 후 유니티를 보면, CSV 파일을 컴포넌트에 넣을 수 있게 되어있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CSVLoader : MonoBehaviour
{
public TextAsset csvFile; //Unity Editor에서 할당
void Start()
{
ReadCSV(csvFile);
}
void ReadCSV(TextAsset csv)
{
string[] rows = csv.text.Split(new char[] { '\n' }, System.StringSplitOptions.RemoveEmptyEntries);
for (int i = 1; i < rows.Length; i++)
{
string[] rowValues = rows[i].Split(',');
if (rowValues.Length != 3) continue;
int id = int.Parse(rowValues[0]);
string name = rowValues[1];
int age = int.Parse(rowValues[2]);
Debug.Log($"ID: {id}, Name: {name}, Age: {age}");
}
}
}
CSV 파일의 기준 행 다음부터 읽어진다. 즉, for문에서 i = 1 부터 시작된다는 말이다.
다른 파일 불러오기 (YAML, INI, Binary File)
바이너리 스크립트 생성,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System;
[Serializable]
public class PlayerData
{
public string name;
public int health;
public float[] position;
public PlayerData(string name, int health, float[] poisition)
{
this.name = name;
this.health = health;
this.position = position;
}
}
DisplayPlayerData 스크립트 생성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class DisplayPlayerData : MonoBehaviour
{
public TMP_InputField playerDataText;
private PlayerData playerData;
void Start()
{
//플레이어 데이터 예시 생성
float[] position = { 1.0f, 2.0f, 3.0f };
playerData = new PlayerData("PlayerName", 100, position);
//플레이어 데이터 표시
DisplayData();
}
void DisplayData()
{
if (playerDataText != null && playerData != null)
{
playerDataText.text = $"Name: {playerData.name}\n" + $"Health: {playerData.health}\n" + $"Position: ({ playerData.position[0]}, { playerData.position[1]}, { playerData.position[2]})";
}
}
}
PlayerData 스크립트는 게임 오브젝트에 넣고, 빈 컴포넌트에는 InputField UI를 만들어서 넣는다. 그러면 InputField에 정보가 나온다.
**한글 폰트 적용2
.ttf 파일 형식의 폰트를 다운받은 후, [Window] - [TextMeshPro] - [Font Asset Creator]에서 원하는 폰트를 넣고 설정을 맞춘 후 [Generate Font Atlas]를 누르면 만들어진다.
다른 형식의 파일 읽기 ScriptableObject
상태나 데이터를 저장하는 데에 사용된다. MonoBehaviour와는 달리, ScriptableObject는 게임 오브젝트에 붙지 않고 대신 에셋으로 저장된다. 메모리 효율성, 재사용성, 모듈화의 장점이 있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "NewGameData", menuName = "Game Data", order = 51)]
public class GameData : ScriptableObject
{
public int level;
public string playerName;
public float health;
}
위의 스크립트를 프로젝트에 저장하면, 프로젝트에서 생성할 때 [Creat] - [Game Data] 라는 메뉴를 선택할 수 있다.
GameControl이라는 스크립트도 새로 생성한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameControl : MonoBehaviour
{
public GameData gameData;
void Start()
{
if(gameData != null)
{
Debug.Log("Player Name: " + gameData.playerName);
Debug.Log("Level: " + gameData.level);
Debug.Log("Health: " + gameData.health);
}
}
}
게임 오브젝트에 GameControl 스크립트를 넣고, 빈 컴포넌트에 새로 만들었던 Game Data를 넣으면 Console 창에 내용이 출력된다.
API(Application Programming Interface)
Rest API
요청이 들어올 때만 비동기적으로 응답한다.
응답 형식(Response Formats): 클라이언트에게 데이터를 전송할 때 특정 형식을 사용한다. 일반적으로 JSON, XML.
던전앤파이터 API 이용하기
위의 사이트에 들어가서 로그인한다.
API Key를 받는다.
스크립트를 생성한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using TMPro;
[System.Serializable]
public class ServerResponse
{
public ServerInfo[] rows;
}
[System.Serializable]
public class ServerInfo
{
public string serverName;
}
public class DnFNetworkTest : MonoBehaviour
{
public TMP_Text[] serverTexts = new TMP_Text[8];
void Start()
{
StartCoroutine(UnityWebRequestGet());
}
IEnumerator UnityWebRequestGet()
{
string url = "https://api.neople.co.kr/df/servers?apikey=본인의 apikey";
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
if(www.error == null)
{
string jsonResponse = www.downloadHandler.text;
ServerResponse serverResponse = JsonUtility.FromJson<ServerResponse>(jsonResponse);
for (int i = 0; i < serverTexts.Length && i < serverResponse.rows.Length; i++)
{
serverTexts[i].text = serverResponse.rows[i].serverName;
}
}
else
{
Debug.Log("Error: " + www.error);
}
}
}
8개의 텍스트를 만든 후 스크립트 컴포넌트와 연결하고 실행하면 던전앤파이터의 8개의 서버 이름이 출력되는 것을 볼 수 있다.
https://api.neople.co.kr/df/servers/<serverId>/characters?characterName=<characterName>&jobId=<jobId>&jobGrowId=<jobGrowId>&isAllJobGrow=<isAllJobGrow>&limit=<limit>&wordType=<wordType>&apikey=본인 API 주소
위의 API 주소에서 <serverId>에는 영문 서버 이름을, <charaterName>에는 인코딩한 캐릭터 이름을 넣고 필요 없는 부분은 지운다.
Debug.Log(jsonResponse); 코드를 추가하면 어떤 정보들이 불러와지는지 볼 수 있다.
기존 스크립트에서 serverName을 characterName으로 바꾸면 유니티에 캐릭터 이름이 불러와진다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using TMPro;
[System.Serializable]
public class ServerResponse
{
public ServerInfo[] rows;
}
[System.Serializable]
public class ServerInfo
{
public string characterName;
}
public class DnFNetworkTest : MonoBehaviour
{
public TMP_Text[] serverTexts = new TMP_Text[8];
void Start()
{
StartCoroutine(UnityWebRequestGet());
}
IEnumerator UnityWebRequestGet()
{
string url = "https://api.neople.co.kr/df/servers/anton/characters?characterName=%ea%b3%a0%ec%9a%b4%eb%a7%90_25696&apikey=본인 API키";
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.SendWebRequest();
if (www.error == null)
{
string jsonResponse = www.downloadHandler.text;
ServerResponse serverResponse = JsonUtility.FromJson<ServerResponse>(jsonResponse);
Debug.Log(jsonResponse);
for (int i = 0; i < serverTexts.Length && i < serverResponse.rows.Length; i++)
{
serverTexts[i].text = serverResponse.rows[i].characterName;
}
}
else
{
Debug.Log("Error: " + www.error);
}
}
}
날씨 데이터 받아오기
https://openweathermap.org/current
도시 이름을 불러오는 코드는 다음과 같다.
https://api.openweathermap.org/data/2.5/weather?q={CITY_NAME}&appid={api_key}
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using TMPro;
using System;
public class WeatherFetcher : MonoBehaviour
{
public TMP_Text[] weatherText;
private string apiKey = "본인의 api key";
private string city = "Seoul";
void Start()
{
StartCoroutine(GetWeatherData());
}
IEnumerator GetWeatherData()
{
string url = $"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={apiKey}&units=metric";
using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
{
yield return webRequest.SendWebRequest();
if (webRequest.result != UnityWebRequest.Result.Success)
{
Debug.LogError("Error: " + webRequest.error);
weatherText[0].text = "Error fetching weather data";
}
else
{
OpenWeatherResponse weatherData = JsonUtility.FromJson<OpenWeatherResponse>(webRequest.downloadHandler.text);
DisplayWeatherInfo(weatherData);
}
}
}
void DisplayWeatherInfo(OpenWeatherResponse weatherData)
{
if (weatherData != null)
{
weatherText[0].text = $"City: {weatherData.name}\n";
weatherText[1].text = $"Coordinates: {weatherData.coord.lat}, {weatherData.coord.lon}\n";
weatherText[2].text = $"Temperature: {weatherData.main.temp}°C\n";
weatherText[3].text = $"Feels Like: {weatherData.main.feels_like}°C\n";
weatherText[4].text = $"Min Temp: {weatherData.main.temp_min}°C, Max Temp: {weatherData.main.temp_max}°C\n";
weatherText[5].text = $"Pressure: {weatherData.main.pressure} hPa\n";
weatherText[6].text = $"Humidity: {weatherData.main.humidity}%\n";
weatherText[7].text = $"Visibility: {weatherData.visibility} meters\n" +
$"Wind: {weatherData.wind.speed} m/s, {weatherData.wind.deg} degrees\n" +
$"Cloudiness: {weatherData.clouds.all}%\n" +
$"Sunrise: {UnixTimeStampToDateTime(weatherData.sys.sunrise)}\n" +
$"Sunset: {UnixTimeStampToDateTime(weatherData.sys.sunset)}";
}
}
DateTime UnixTimeStampToDateTime(long unixTimeStamp)
{
// Unix timestamp is seconds past epoch
System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
return dtDateTime;
}
}
[System.Serializable]
public class OpenWeatherResponse
{
public Coordinate coord;
public WeatherInfo[] weather;
public string baseStation;
public MainWeather main;
public int visibility;
public Wind wind;
public Clouds clouds;
public int dt;
public Sys sys;
public int timezone;
public int id;
public string name;
public int cod;
}
[System.Serializable]
public class Coordinate
{
public float lon;
public float lat;
}
[System.Serializable]
public class WeatherInfo
{
public int id;
public string main;
public string description;
public string icon;
}
[System.Serializable]
public class MainWeather
{
public float temp;
public float feels_like;
public float temp_min;
public float temp_max;
public int pressure;
public int humidity;
}
[System.Serializable]
public class Wind
{
public float speed;
public int deg;
}
[System.Serializable]
public class Clouds
{
public int all;
}
[System.Serializable]
public class Sys
{
public int type;
public int id;
public string country;
public long sunrise;
public long sunset;
}
날씨 데이터가 원하는 text 자리에 들어가서 표시되었다.
공공정보 포탈 이용하기
https://www.data.go.kr/index.do
위의 홈페이지에서 공공 API를 사용할 수 있다.
에셋 번들
Resources.Load 함수를 이용하면 에셋을 불러올 수 있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AssetBundle : MonoBehaviour
{
void Start()
{
GameObject obj = Resources.Load<GameObject>("Prefabs/Cube");
Instantiate(obj);
}
}
에셋 번들 이름을 같게 하면 나중에 같은 이름을 가진 에셋들을 따로 압축할 수 있다.
[Package Manager]에서 Addressables를 다운 받으면 Addressable Asset 시스템의 주요 이점을 사용할 수 있다.
- 성능 최적화 -메모리 관리 용이, 필요할 때만 에셋 로드
- 지연 로딩 -객체의 초기화를 그 객체가 실제로 필요할 때까지 지연시킨다. 어플 시작 시간을 단축 시킴. 큰 파일을 다룰 때 사용
- 동적 로딩 -게임이 실행 중일 때 필요한 에셋을 로드/언로드하여 전체 게임 패키지의 크기를 줄이고 메모리 사용을 최적화 한다.
- 컨텐츠 업데이트 -게임을 다시 배포하지 않아도 새로운 컨텐츠 추가나 기존 컨텐츠 업데이트가 가능하다.
- 클라우드 통합 -에셋을 클라우드에 저장하고 게임에서 직접 다운로드하여 사용할 수 있다.
- 패치 시스템 -소프트웨어 패치를 더 작은 사이즈로 분할하여 사용자의 데이터 사용량을 줄이고 다운로드 시간을 줄인다.
Addressables를 다운로드하여 Inspector 창에서 해당 기능을 체크할 수 있게 되었다.
체크하면 그룹으로 나눌 수 있다.
유니티에서 JSON 파일을 이용하여 Save/Load 시스템 만들기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
using System.IO;
[System.Serializable]
public class WeaponData
{
public string Id;
public string Name;
public string Information;
}
public class JsonReadWriteSystem : MonoBehaviour
{
public TMP_InputField idInputField;
public TMP_InputField nameInputField;
public TMP_InputField infoInputField;
public void SaveToJason()
{
WeaponData data = new WeaponData();
data.Id = idInputField.text;
data.Name = nameInputField.text;
data.Information = infoInputField.text;
string json = JsonUtility.ToJson(data, true);
File.WriteAllText(Application.dataPath + "/WeaponDataFile.json", json);
}
public void LoadFromJson()
{
string json = File.ReadAllText(Application.dataPath + "/WeaponDataFile.json");
WeaponData data = JsonUtility.FromJson<WeaponData>(json);
idInputField.text = data.Id;
nameInputField.text = data.Name;
infoInputField.text = data.Information;
}
}
위의 스크립트를 생성해서 게임 오브젝트에 넣는다.
InputField 3개를 만들고 프로젝트를 실행한 후 InputField에 문자열 넣고 Save 버튼 누르면 파일이 생성되어 내용이 저장된다. Load 버튼 누르면 InputField에 저장되어 있던 값이 불러와진다.
원래 저장되어 있던 값에 다시 Save를 하면 새로운 데이터가 저장된다.
'SKKU DT' 카테고리의 다른 글
[SKKU DT] 30일차 -유니티 네트워크 -파이어베이스(Firebase) Authentication 로그인, 익명 로그인 (1) | 2023.12.11 |
---|---|
[SKKU DT] 29일차 -유니티(Unity)와 아두이노(Arduino) 연결하기 (2) | 2023.12.08 |
[SKKU DT] 27일차 -유니티 미니 프로젝트(로봇 팔 시뮬레이션)(2) (1) | 2023.12.05 |
[SKKU DT] 26일차 -유니티 미니 프로젝트(로봇 팔 시뮬레이션) (1) | 2023.12.05 |
[SKKU DT] 25일차 -유니티 C# 튜플, 인터페이스, 추상클래스, 프로퍼티, 레코드, 무명 형식, 배열 (1) | 2023.12.01 |