Playerprefs는 레지스트리에 저장된다. regedit 프로그램을 통해 레지스트리에 접근할 수 있다. 빌드 경로와 에디터에서 사용하는 경로가 다르다.빌드 : HKEY_CURRENT_USER/Software/[회사명]/[제품 이름]에디터 : HKEY_CURRENT_USER/Software/Unity/UnityEditor/[회사명]/[제품 명] 여기서 회사명과 제품 명은PlayerSettings에 있다. 해당 경로의 레지스트리는 지워도 유니티를 통해 다시 생성할 수 있으니 안심하고 삭제해도 된다.에디터에서는 정상적이지만 빌드된거에선 PlayerPrefs가 다르게 동작한다면 해당 레지스트리를 삭제하면 된다.
GSTU(Google Sheet To Unity)라는 에셋을 통해 실시간으로 시트의 데이터를 불러올 수 있다.에셋은 무료이니 Sheet를 Json으로 변환한 뒤 가져와서 파싱할 번거로움을 덜어줄 수 있다. 세팅먼저 에셋을 다운로드 해준다.https://assetstore.unity.com/packages/tools/utilities/google-sheets-to-unity-73410 Google Sheets To Unity | 유틸리티 도구 | Unity Asset StoreUse the Google Sheets To Unity from Greener Games on your next project. Find this utility tool & more on the Unity Asset Store.as..
반응형
GSTU(Google Sheet To Unity)라는 에셋을 통해 실시간으로 시트의 데이터를 불러올 수 있다.
에셋은 무료이니 Sheet를 Json으로 변환한 뒤 가져와서 파싱할 번거로움을 덜어줄 수 있다.
나는 이 블로그의 코드를 제네릭 클래스로 커스텀해서 다양한 시트의 데이터를 받아올 수 있게 했다.
using GoogleSheetsToUnity;
using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.Events;
public abstract class DataReaderBase<T> : ScriptableObject
{
[Header("시트의 주소")][SerializeField] public string associatedSheet = "";
[Header("스프레드 시트의 시트 이름")][SerializeField] public string associatedWorksheet = "";
[Header("읽기 시작할 행 번호")][SerializeField] public int START_ROW_LENGTH = 2;
[Header("읽을 마지막 행 번호")][SerializeField] public int END_ROW_LENGTH = -1;
[Header("스프레드시트에서 읽혀져 직렬화 된 오브젝트")] public List<T> DataList = new List<T>();
public abstract void UpdateStats(List<GSTU_Cell> list);
}
#if UNITY_EDITOR
[CustomEditor(typeof(DataReaderBase<>), true)]
public class DataReaderEditor : Editor
{
protected object targetDataReader;
void OnEnable()
{
targetDataReader = target;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Label("\n\n스프레드 시트 읽어오기");
if (GUILayout.Button("데이터 읽기(API 호출)"))
{
var list = targetDataReader.GetType().GetField("DataList").GetValue(targetDataReader) as IList;
list.Clear();
UpdateStats(UpdateMethodOne);
}
}
void UpdateStats(UnityAction<GstuSpreadSheet> callback, bool mergedCells = false)
{
string associatedSheet = (string)targetDataReader.GetType().GetField("associatedSheet").GetValue(targetDataReader);
string associatedWorksheet = (string)targetDataReader.GetType().GetField("associatedWorksheet").GetValue(targetDataReader);
SpreadsheetManager.Read(new GSTU_Search(associatedSheet, associatedWorksheet), callback, mergedCells);
}
void UpdateMethodOne(GstuSpreadSheet ss)
{
var start = (int)targetDataReader.GetType().GetField("START_ROW_LENGTH").GetValue(targetDataReader);
var end = (int)targetDataReader.GetType().GetField("END_ROW_LENGTH").GetValue(targetDataReader);
var list = (targetDataReader.GetType().GetField("DataList").GetValue(targetDataReader) as IList);
list.Clear();
for (int i = start; i <= end; ++i)
{
var rowCells = ss.rows[i];
object[] parameters = new object[] { rowCells };
targetDataReader.GetType().GetMethod("UpdateStats").Invoke(targetDataReader, parameters);
}
EditorUtility.SetDirty(target);
}
}
#endif
해당 클래스를 상속받아 쓰면 된다.
using GoogleSheetsToUnity;
using System;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Reader", menuName = "ScriptableObject/Item/DataReader")]
public class ItemDataReader : DataReaderBase<ItemData>
{
public override void UpdateStats(List<GSTU_Cell> list)
{
int id = 0;
string name = "";
ItemExtraStatType ability1Type = ItemExtraStatType.None;
float ability1Value = 0;
ItemExtraStatType ability2Type = ItemExtraStatType.None;
float ability2Value = 0;
int itemEffectId = 0;
int synergyId = 0;
ItemGradeType itemGradeType = ItemGradeType.Common;
string iconURL = "";
for (int i = 0; i < list.Count; i++)
{
switch (list[i].columnId)
{
case "ID":
{
id = int.Parse(list[i].value);
break;
}
case "Grade":
{
itemGradeType = Enum.Parse<ItemGradeType>(list[i].value);
break;
}
case "Name":
{
name = list[i].value;
break;
}
case "Ability1":
{
ability1Type = Enum.Parse<ItemExtraStatType>(list[i].value);
break;
}
case "Value1":
{
ability1Value = float.Parse(list[i].value);
break;
}
case "Ability2":
{
ability2Type = Enum.Parse<ItemExtraStatType>(list[i].value);
break;
}
case "Value2":
{
ability2Value = float.Parse(list[i].value);
break;
}
case "ItemEffectID":
{
itemEffectId = int.Parse(list[i].value);
break;
}
case "SynergyID":
{
synergyId = int.Parse(list[i].value);
break;
}
case "IconURL":
{
iconURL = list[i].value;
break;
}
}
}
DataList.Add(new ItemData(id, itemGradeType, name, ability1Type, ability1Value, ability2Type, ability2Value, itemEffectId, synergyId, iconURL));
}
}
상속받아 쓴 ItemDataReader 클래스이다.
...
[Serializable]
public class ItemStat
{
public ItemExtraStatType ItemExtraStatType;
public ItemTriggerType ItemTriggerType;
public float Value;
}
[Serializable]
public class ItemData
{
...
public int Id;
public string ItemName;
public ItemStat Stat1;
public ItemStat Stat2;
public string IconURL;
public int ItemEffectId;
public int SynergyId;
public ItemGradeType ItemGrade;
public ItemEffectData EffectData;
public string SynergyText;
public string EffectText;
...
public ItemData(int id, ItemGradeType itemGradeType, string _name, ItemExtraStatType ability_1Type, float ability_1Value, ItemExtraStatType ability_2Type, float ability_2Value, int itemEffectId, int synergyId, string iconURL)
{
Id = id;
ItemGrade = itemGradeType;
ItemName = _name;
Stat1 = new ItemStat() { ItemExtraStatType = ability_1Type, ItemTriggerType = ItemTriggerType.Always, Value = ability_1Value };
Stat2 = new ItemStat() { ItemExtraStatType = ability_2Type, ItemTriggerType = ItemTriggerType.Always, Value = ability_2Value };
ItemEffectId = itemEffectId;
SynergyId = synergyId;
IconURL = iconURL;
EffectData = default(ItemEffectData);
}
}
시트의 값을 저장할 ItemData이다.
실제 시트의 데이터이다.
보면 ItemData의 멤버 변수와 동일한 식별자를 가진다.
enum이라면 문자 그대로 대소문자 똑같이 써줘야 한다.
ItemDataReader를 SO로 만들었을 때 사진처럼 4개의 입력과 받아오기 버튼이 있을것이다.
게임에서 캐릭터가 땅에 서 있는지 판정하는 건 아주 기본적이지만, 생각보다 정확하게 구현하기가 까다롭다.특히 박스 콜라이더(BoxCollider)를 쓸 때, 한 지점만 검사하면 경사로나 돌출부 때문에 의도치 않은 결과가 나올 수 있기 때문에 문제가 생각보다 빈번하게 발생한다.이번 글에서는 박스 콜라이더 바닥 여러 위치에서 Raycast를 쏴서 안정적으로 바닥을 감지하는 방법을 소개한다.왜 여러 위치에서 체크해야 할까?박스 콜라이더는 사각형 모양이라, 바닥에 완벽하게 평평하게 닿지 않는 경우가 많다.한 지점만 검사하면 캐릭터가 바닥에 붙어있어도 감지 못할 수 있고, 경사진 지형에서 문제를 일으킨다.그래서 콜라이더 바닥 주변의 8개 위치(네 구석과 네 중간 지점)에서 짧은 Raycast를 쏴서 하나라도 닿으..
반응형
게임에서 캐릭터가 땅에 서 있는지 판정하는 건 아주 기본적이지만, 생각보다 정확하게 구현하기가 까다롭다. 특히 박스 콜라이더(BoxCollider)를 쓸 때, 한 지점만 검사하면 경사로나 돌출부 때문에 의도치 않은 결과가 나올 수 있기 때문에 문제가 생각보다 빈번하게 발생한다.
이번 글에서는 박스 콜라이더 바닥 여러 위치에서 Raycast를 쏴서 안정적으로 바닥을 감지하는 방법을 소개한다.
왜 여러 위치에서 체크해야 할까?
박스 콜라이더는 사각형 모양이라, 바닥에 완벽하게 평평하게 닿지 않는 경우가 많다. 한 지점만 검사하면 캐릭터가 바닥에 붙어있어도 감지 못할 수 있고, 경사진 지형에서 문제를 일으킨다.
그래서 콜라이더 바닥 주변의 8개 위치(네 구석과 네 중간 지점)에서 짧은 Raycast를 쏴서 하나라도 닿으면 바닥에 닿았다고 판단한다.
코드 예제
internal bool CanJump()
{
var bounds = _boxCollider.bounds;
var min = bounds.min;
var max = bounds.max;
var center = bounds.center;
int groundLayerMask = LayerMask.GetMask("Ground");
List<Vector3> checkPoints = new List<Vector3>
{
new Vector3(min.x, min.y + 0.05f, min.z),
new Vector3(center.x, min.y + 0.05f, min.z),
new Vector3(max.x, min.y + 0.05f, min.z),
new Vector3(min.x, min.y + 0.05f, center.z),
new Vector3(max.x, min.y + 0.05f, center.z),
new Vector3(min.x, min.y + 0.05f, max.z),
new Vector3(center.x, min.y + 0.05f, max.z),
new Vector3(max.x, min.y + 0.05f, max.z)
};
foreach (var point in checkPoints)
{
if (Physics.Raycast(point, Vector3.down, 0.05f, groundLayerMask))
return true;
}
return false;
}
이번 포스트에서는 2D 애니메이션에 대해서 알아보겠다. Project창에서 우클릭 → Create 하면 Animation관련 데이터를 생성할 수 있다. 먼저 Animation을 생성한다는건 Animation Clip 을 생성한다는것이다.Animation Clip우선 Animation Clip을 자세히 보기 위해선 Animation 창을 열어야 한다.Window → Animation → Animation에서 열 수 있다. 그 이후 Animator Controller 컴포넌트가 장착된 게임 오브젝트를 클릭하면Animation창에서 해당 Anim Controller에 들어가있는애니메이션을 미리보기할 수 있는 기능들이 모여있다.보고 싶은 애니메이션을 설정할 수 있다. 오른쪽에 있는 것들은 특정 프레임에 이벤트를..
반응형
이번 포스트에서는 2D 애니메이션에 대해서 알아보겠다.
Project창에서 우클릭 → Create 하면 Animation관련 데이터를 생성할 수 있다.
먼저 Animation을 생성한다는건 Animation Clip 을 생성한다는것이다.
Animation Clip
우선 Animation Clip을 자세히 보기 위해선 Animation 창을 열어야 한다.
Window → Animation → Animation에서 열 수 있다.
그 이후 Animator Controller 컴포넌트가 장착된 게임 오브젝트를 클릭하면
Animation창에서 해당 Anim Controller에 들어가있는
애니메이션을 미리보기할 수 있는 기능들이 모여있다.
보고 싶은 애니메이션을 설정할 수 있다. 오른쪽에 있는 것들은 특정 프레임에 이벤트를 삽입하는 기능이다. (이벤트는 스크립트의 특정 함수를 호출하는 등 다양한 이벤트가 있다.)
현재 Clip에서 사용되는 게임 오브젝트의 속성들이다. 이곳에서 게임 오브젝트에 달려있는 웬만한 컴포넌트들을 조작할 수 있다.
실제 애니메이션이 실행될 때 프레임별로 기능들을 보여주는 곳이다. 사진처럼 Sprite들이 보통 있고 Position이나 Scale 등을 수정하는 경우도 많다.
DopeSheet는 현재 사진처럼 각 프레임별로 어떤 동작을 하는지 볼 수 있고 Curves는 프레임별로 이전, 다음 프레임과의 동작 연결시키는 시간을 조절할 수 있다. (만약 Scale이나 Position을 Clip에서 사용하며 프레임에 2개이상 있을 때 나올것이다. 첫 프레임에서 Scale.X를 3으로 했다가 두 번째 프레임에서 5로 설정한다 쳤을 때 다음 프레임으로 전환될 때 3에서 5로 즉시 전환될 지 3... 4... 5... 이런식으로 전환될지를 설정할 수 있다.)
Animator
Animator Controller를 두번 클릭하면 해당 Animator에 관련해서 창이 열린다.
Layer같은 경우 여러 애니메이션을 동시에 실행시키고 싶을 때 사용한다.
예를 들어 하체는 달리기, 상체는 활 쏘기, 휘두르기 등을 분리시켜 동시에 실행시킬 수 있다.
왼쪽에는 Animation을 전환할 때 사용될 조건문에 들어갈 변수들이다.
흔히 아는 int, float, bool을 주로 사용한다.
오른쪽 창에 뭔가 많은데 주황색, 회색 박스들이 Animation Clip 또는 Clip 전환용 State이다.
주황색 박스는 기본 State로 Animator가 실행될 때 가장 처음 연결될 State이다.
각 박스마다 화살표가 연결되어있는데 이는 State 전환 방향을 의미하며 현재 State에서 연결된 State로 전환될 수 있음을 뜻한다.
해당 State에 Parameter를 넣어서 조건문을 달아줄 수도 있다.
화살표를 클릭하면 나오는 Inspector이다.
디버깅 용으로만 사용되는 Solo와 Mute이다.
Solo: 해당 트랜지션이나 상태만 단독으로 재생하도록 설정한다.
Mute: 해당 트랜지션이나 상태를 무시하고 재생되지 않게 함
애니메이션끼리 전환될 때 설정하는 부분이다.
Has Exit Time: 전환되기 전 실행되고 있는 애니메이션을 다 끝낸 다음에 넘어갈지를 결정한다.
Exit Time: 애니메이션 전환 시간(0초면 애니메이션 종료, Has Exit time이 설정된 경우에만 켜짐)
유니티로 모바일 게임 개발을 하기 위해 안드로이드 스튜디오의 가상 디바이스를 쓰려고 하는데눈씻고 찾아봐도 Run Device에 나오지 않는다. 그래서 문제점은 sdk라고 가정하고 안드로이드 스튜디오의 sdk를 직접 설정해주었더니 해결되었다.sdk 설치 및 설정우측 위 톱니바퀴 → SDK Manager → Languages & Frameworks → Android SDK 에서 설치 및 설정할 수 있다.SDK Platforms에서 원하는 SDK를 설치한 뒤 SDK Tools에서 아래의 것들을 체크해준다.Android SDK Platform ToolsGoogle USB DriverAndroid SDK Command-line Tools (latest)나머지는 아마 기본으로 설정되어있을것이다. 이후에 유니티로 ..
반응형
유니티로 모바일 게임 개발을 하기 위해 안드로이드 스튜디오의 가상 디바이스를 쓰려고 하는데
눈씻고 찾아봐도 Run Device에 나오지 않는다.
그래서 문제점은 sdk라고 가정하고 안드로이드 스튜디오의 sdk를 직접 설정해주었더니 해결되었다.
sdk 설치 및 설정
우측 위 톱니바퀴 → SDK Manager → Languages & Frameworks → Android SDK 에서 설치 및 설정할 수 있다.
SDK Platforms에서 원하는 SDK를 설치한 뒤 SDK Tools에서 아래의 것들을 체크해준다.
Android SDK Platform Tools
Google USB Driver
Android SDK Command-line Tools (latest)
나머지는 아마 기본으로 설정되어있을것이다.
이후에 유니티로 돌아와 안드로이드 스튜디오의 SDK로 설정을 해준다.
Preferences → External Tools → SDK 설정을 직접 다운 받은 sdk로 설정
UGUI앞에 gameobejct 렌더링하는 방법은 쉽게 찾아볼 수 있지만Ui Document위에 렌더링 하는 방법은 해외를 포함해 자료가 별로 없습니다 여러번 찾아본 결과 UGUI랑 크게 다를건 없다는걸 알았습니다 방식은 raw image와 render texture를 사용하는겁니다. 카메라의 culling mask는 게임 오브젝트 카메라만 신경쓰면 됩니다.저같은 경우 게임 오브젝트의 레이어와 Default 두개만 했습니다. 저 가운데에 있는 탄지로가 게임 오브젝트이고 나머지가 ui document입니다.
반응형
UGUI앞에 gameobejct 렌더링하는 방법은 쉽게 찾아볼 수 있지만
Ui Document위에 렌더링 하는 방법은 해외를 포함해 자료가 별로 없습니다
여러번 찾아본 결과 UGUI랑 크게 다를건 없다는걸 알았습니다
방식은 raw image와 render texture를 사용하는겁니다.
ui document panel settings 및 카메라 render texture 설정