2023/11/02 TIL
오늘은 어제 다루었던 PlayerPrefs에 대해 집중적으로 공부하고자 한다.
https://docs.unity3d.com/kr/530/ScriptReference/PlayerPrefs.html
UnityEngine.PlayerPrefs - Unity 스크립팅 API
Stores and accesses player preferences between game sessions.
docs.unity3d.com
PlayerPrefs에 대한 설명은 위 사이트를 참고했다.
저장되는 위치에 대한 설명이 있는데...
- Windows, PlayerPrefs are stored in the registry under HKCU\Software\[company name]\[product name] key, where company and product names are the names set up in Project Settings.
- On Windows Store Apps, Player Prefs can be found in %userprofile%\AppData\Local\Packages\[ProductPackageId]>\LocalState\playerprefs.dat
잘 모르겠으니 넘어가자
PlayerPrefs에 사용되는 정적함수는 다음과 같다.
DeleteAll | Removes all keys and values from the preferences. Use with caution. |
DeleteKey | Removes key and its corresponding value from the preferences. |
GetFloat | Returns the value corresponding to key in the preference file if it exists. |
GetInt | Returns the value corresponding to key in the preference file if it exists. |
GetString | Returns the value corresponding to key in the preference file if it exists. |
HasKey | Returns true if key exists in the preferences. |
Save | Writes all modified preferences to disk. |
SetFloat | Sets the value of the preference identified by key. |
SetInt | Sets the value of the preference identified by key. |
SetString | Sets the value of the preference identified by key. |
먼저 "Key"가 필요하다. PlayerPrefs는 Key Value로 데이터를 저장한다.
SetFloat, SetInt, SetString을 이용하면 Key에 데이터를 저장 할 수 있다.
또, GetFloat, GetInt, GetString을 이용하면 같은 Key에 저장된 값을 꺼내올 수 있다.
예시를 들어보면
public class PlayerPrefsEx : MonoBehaviour
{
string value = "예시";
public void PLAY()
{
PlayerPrefs.SetString("AnyKey", value);
Debug.Log(PlayerPrefs.GetString("AnyKey"));
}
}
라는 코드를 작성했을때,
요렇게 정상적으로 잘 작동하는 것을 볼 수 있다.
HasKey는 특정 Key가 있는지 없는지 판단해주는 함수로
특정 Key가 있다면 true, 없다면 false로 나온다.
예시를 들어보자.
public class PlayerPrefsEx : MonoBehaviour
{
string value = "예시";
void Start()
{
PlayerPrefs.SetString("AnyKey", value);
}
public void PLAY()
{
bool example = PlayerPrefs.HasKey("AnyKey");
Debug.Log(example);
}
}
Play라는 함수를 실행시켰을 때, HasKey는 "AnyKey"가 있는지 확인하게 되고
boolean값을 example에 저장한다.
우리는 Start()에서 "AnyKey"를 SetString했으므로 true라는 결과값이 나올 것이다.
훌륭하다.
PlayerPref는 데이터를 메모리상에 저장하고 그 후에 하드 드라이브에 저장한다.
만약 갑자기 블루스크린 등 저장을 못하는 경우가 생기면 메모리에 저장된 데이터들은 휘발된다.
그런일을 방지하기 위해 Save를 사용한다.
Save는 데이터를 하드드라이브에 저장시켜주는 함수이다.
따로 예시는 들지 않겠다.
이번엔 enum에 대해서 공부해보겠다.
https://learn.unity.com/tutorial/yeolgeohyeong#6268e92dedbc2a53f9b4911d
열거형 - Unity Learn
열거형을 사용하면 관련 상수의 컬렉션을 생성할 수 있습니다. 이 영상에서는 코드에서 열거형을 선언하고 사용하는 방법을 알아봅니다. 이 튜토리얼은 스크립팅의 기초 프로젝트에 포함되어
learn.unity.com
(아래 내용은 강좌의 요약본이다.)
unity를 사용하다보면 상수의 집합으로 구성된 변수가 필요할 수 있습니다.
열거형을 이용하면 변수를 일일히 넣을 필요가 없습니다.
enum이라고 하는 열거형은 특수한 데이터 유형이며 사용 가능한 값으로 이루어진 특정 하위 집합을 가집니다.
열거형은 클래스 안이나 밖에 만들 수 있습니다.
열거형만으로 이루어진 스크립트를 만들 수도 있으며
이런경우 클래스로 선언하는게 아닌 열거형으로 선언할 수 있습니다.
그런 다음 이 열거형을 다른 스크립트에 있는 클래스에서 사용할 수 있습니다. public이니까요.
특정 클래스에서만 사용하는 경우는 그 클래스에 넣으면 됩니다.
다양한 열거형 함수를 쉼표와 중괄호로 구분하여 나열해야 합니다.
열거형에서 선언된 각 상수는 값을 가지고 있으며 이는 기본적으로 0에서 증가하는 정수입니다.
값의 유형과 값 자체 모두 필요한 경우 덮어쓸 수 있습니다.
유형을 변경하는 주된 이유는 최적화이니 대부분은 신경 쓸 필요가 없습니다.
열거형을 선언했으므로 열거형 유형의 변수를 만듭니다.
(예제에선 myDirection이라는 방향 변수를 만들었다.)
이제 이 변수를 다른 변수와 똑같이 취급할 수 있는데요, 이 변수가 가질 수 있는 값은 Direction.North, Direction.South, Direction.West, Direction.East 등이에요.
이 열거형은 함수로 파싱될 수도 있고 함수에서 반환될 수도 있습니다.
using UnityEngine;
using System.Collections;
public class EnumScript : MonoBehaviour
{
enum Direction {North, East, South, West}; //enum
void Start ()
{
Direction myDirection;
myDirection = Direction.North;
}
Direction ReverseDirection (Direction dir)
{
if(dir == Direction.North)
dir = Direction.South;
else if(dir == Direction.South)
dir = Direction.North;
else if(dir == Direction.East)
dir = Direction.West;
else if(dir == Direction.West)
dir = Direction.East;
return dir;
}
}
요약해보자면
1. 열거형(enum)을 사용하면 같은 종류에 속하는 상수를 여러개 선언할때 유리함.
2. 열거형에서 선언된 각 상수는 값을 가지고 있으며 이는 기본적으로 0에서 증가하는 정수이다.
3. 이 값들은 필요한경우 언제든 수정 가능하다.
이제 저번 시간에 만들어진 코드들을 뜯어보자.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GalleryManager : MonoBehaviour
{
// Resources폴더 속 team 이미지를 배열에 넣어주세요. (순서상관없음, 실수로 중복넣어도 가능!)
public Sprite[] firstKey; //팀원1의 이미지들
public Sprite[] secondKey; // 팀원2의 이미지들
public Sprite[] thirdKey; // 팀원3의 이미지들
public Sprite[] fourthKey; // 팀원4의 이미지들
public Sprite[] fifthKey; // 팀원5의 이미지들
public Sprite[] sixthKey; //냥이의 이미지들
//---------------------------------------------------------
readonly string[] names = { "팀원1", "팀원2", "팀원3", "팀원4", "팀원5", "냥이" };
public Sprite[] faces; // 이름 순서대로 프사를 넣어주세요.
[TextArea]
public string[] part;
[TextArea]
public string[] tmis; // 이름 순서대로 설명을 적어주세요.
public Text nameTxt;
public Image faceImg;
public Text partTxt;
public Text tmiTxt;
public GalleryPopup galleryPopup;
private Dictionary<int, ProfileSetting> profile;
//profile 변수는 int 키와 해당 키에 대응하는 ProfileSetting 객체를 저장하는 딕셔너리를 나타냅니다.
//이를 사용하여 int 값을 키로 사용하여 해당 프로필 설정(ProfileSetting)에 접근하고 관리할 수 있습니다.
void Awake()
{
Time.timeScale = 1.0f;
profile = new Dictionary<int, ProfileSetting>();
for (int i = 0; i < names.Length; i++)
{
profile.Add(i, new ProfileSetting(names[i], faces[i], part[i], tmis[i]));
} //i 값을 이용해 해당 i인물의 프로필을 관리함
}
public void PopupOpen(int num)
{
nameTxt.text = profile[num].name;
tmiTxt.text = profile[num].instruction;
partTxt.text = profile[num].part;
faceImg.sprite = faces[num];
//주어진 칸에 정해진 내용을 출력
if (num == 5) // 냥이는 ssr배경
galleryPopup.OpenSSRver();
else
galleryPopup.Open();
}
}
이번엔 갤러리 버튼 코드를 가져와보겠다.
using UnityEngine;
using UnityEngine.UI;
public class GalleryBtn : MonoBehaviour
{
public enum Achives //enum으로 Achives를 선언
{
UnlockFirst, // 0
UnlockSecond, //1
UnlockThird, //2
UnlockFourth, //3
UnlockFifth, //4
UnlockSixth //5
}
public Achives achive; //achive를 선언
Sprite[] myKey; //배열 선언
AudioSource audioSource;
GalleryManager galleryManager;
public GameObject lockCard; // Lock 이미지
Button btn;
public AudioClip flip;
public AudioClip nya;
void Awake()
{
audioSource = GetComponent<AudioSource>();
btn = GetComponent<Button>();
myKey = new Sprite[1]; //myKey 배열을 크기가 1인 새로운 배열로 다시 할당
}
void Start()
{
galleryManager = FindObjectOfType<GalleryManager>();
lockCard.SetActive(true); //잠금화면을 활성화
btn.interactable = false; //버튼을 누를 수 없게 함
Init(); //Init실행
Set(); //Set실행
}
void Init()
{
audioSource.clip = flip; //오디오 소스에 flip을 저장
switch (achive)
{
case Achives.UnlockFirst: //UnlockFirst의 경우
myKey = galleryManager.firstKey; //galleryManager의 firstKey를 myKey에 저장
break;
case Achives.UnlockSecond:
myKey = galleryManager.secondKey; //이후 반복
break;
case Achives.UnlockThird:
myKey = galleryManager.thirdKey;
break;
case Achives.UnlockFourth:
myKey = galleryManager.fourthKey;
break;
case Achives.UnlockFifth:
myKey = galleryManager.fifthKey;
break;
case Achives.UnlockSixth:
myKey = galleryManager.sixthKey;
audioSource.clip = nya;
break; //여기까지 해금 조건
default:
Debug.Log("버그입니다!");
break;
}
}
void Set()
{
for (int i = 0; i < myKey.Length; i++)
{
if (!PlayerPrefs.HasKey(myKey[i].name)) // 플레이어프리프에 하나라도 안본 내사진이있다면 버튼 안열림
return;
}//만약 모든 카드를 본 경우
lockCard.SetActive(false); //잠금 비활성화
btn.interactable = true; //버튼을 이제 누를 수 있음
}
public void Click()
{
if (audioSource != null)
audioSource.Play();
galleryManager.PopupOpen((int)achive);
}
}
PlayerPrefs.HasKey를 한번밖에 이용하지 않았는데 어떻게 작동하는건지 의문이 들었다.
그래서 코드를 만드신 팀원분께 직접 여쭤보니 InIt();에서 꼼수(?)를 쓰는 거였다.
case Achives.UnlockFirst: //UnlockFirst의 경우
myKey = galleryManager.firstKey; //galleryManager의 firstKey를 myKey에 저장
break;
이 부분에서 galleryManager.firstKey를 myKey에 저장하고 이를 switch case로 만들면 위에서 상황에 맞게 myKey를 바꿔주는 것이였다. ㄷㄷ
어쨌든 이런 방법을 쓸 수 있다는 것도 알게된 코드였다.
.
.
.
어쩌다 보니 또 오디오쪽을 건들게 되었다.
배경음악이 크기 때문에 그것도 줄일 겸, 전체 오디오조절 기능을 추가하기로 했다.
https://malbongcode.tistory.com/40
Unity - 사운드 조절하기 - Slider로 조절
사운드를 켜고 끄고 하는 것은 크게 두가지 있음 1) Sound Toggle (on/off) 2) Sound Manipulation (0~100) 1) Sound Toggle (on/off) 코드 살펴보기 스크립트 작성하고 Button 만들어서 OnClick에 넣어주면 됨 설명하자면 A
malbongcode.tistory.com
말봉님의 코드를 참고했다.
1. 먼저 soundToggle을 통해 음량 On/Off를 구현했다.
2. 슬라이더를 통해 음량 조절을 구현하기로 했다.
먼저 오디오믹서를 만들어준다.
Window -> Audio -> Audio Mixer
Mixers옆에 +버튼을 누르고 믹서를 만든 후
Groups를 통해 각각의 Audio 그룹을 만들어준다.
사진에는 SFX를 넣긴 했지만, 막상 조절하다보니 너무 투머치 할것 같아서 따로 사용은 안했다.
Asset에 만들어진 Mixer를 클릭하여 Inspector 창보기->
만든 채널그룹을 모두 아래와 같이 설정해주기 (Expose Volume... 선택해야 함)
이제 UI -> Canvas -> Slider로 slider을 만들고
Slider Min Value : -40 , Max Value : 0 으로 설정함
볼륨은 -80 ~ 20까지 존재
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Audio;
using UnityEngine.UI;
public class VolumeMixer : MonoBehaviour
{
public AudioMixer masterMixer;
public Slider audioSlider;
public Image Speaker;
public Sprite SpeakerOff;
public Sprite SpeakerOn;
float sound;
public void AudioControl()
{
// 슬라이더 값 저장
float currentVolume = audioSlider.value;
PlayerPrefs.SetFloat("Volume", currentVolume);
PlayerPrefs.Save();
// 볼륨 설정
SetVolume(currentVolume);
}
public void ToggleAudioVolume()
{
AudioListener.volume = AudioListener.volume == 0 ? 1 : 0;
if (AudioListener.volume == 0)
{
Speaker.sprite = SpeakerOff;
PlayerPrefs.SetInt("Speaker", 0);
}
else
{
Speaker.sprite = SpeakerOn;
PlayerPrefs.SetInt("Speaker", 1);
}
PlayerPrefs.Save();
}
// Start is called before the first frame update
void Start()
{
if (PlayerPrefs.HasKey("Volume"))
{
float savedVolume = PlayerPrefs.GetFloat("Volume");
audioSlider.value = savedVolume;
SetVolume(savedVolume);
}
// 스피커 설정 불러오기
if (PlayerPrefs.HasKey("Speaker"))
{
int isSpeakerOn = PlayerPrefs.GetInt("Speaker");
Speaker.sprite = (isSpeakerOn == 1) ? SpeakerOn : SpeakerOff;
}
}
private void SetVolume(float volume)
{
if (volume == -40f)
{
masterMixer.SetFloat("BGM", -80);
}
else
{
masterMixer.SetFloat("BGM", volume);
}
}
// Update is called once per frame
void Update()
{
PlayerPrefs.GetFloat("Volume", audioSlider.value);
}
}
이제 코드를 보자
시작화면에 음량조절 버튼을 달아놨었는데 다른 scene으로 이동했다 돌아오면 음량버튼이 초기화되는 문제가 있었다.
그걸 해결하기 위해서 PlayerPrefs를 사용하고자 했다.
PlayerPrefs에 slider값을 저장해 놓으면 다른 씬으로 넘어가더라도 값이 사라지지 않는다.
Toggle역시 마찬가지.
이렇게 오디오 볼륨조절을 성공했다.
갤러리라는 Scene에 들어갔다 나와도 변하지 않는걸 볼 수 있다.
한번 PlayerPrefs를 공부하고 나니 생각보다 유용한 코드인것 같다.