#pragma strict
var tex : Texture2D[] =new Texture2D[10];
var score:int;
function Awake(){
score = 25634;
}
function Update () {
score+=1*Time.time;
}
function OnGUI(){
//simplemode = 세가지 제공.. 그중 아래는 사간 화면 꽉 제운것... true = 알파 브랜딩 오케이
noOkDraw(score,Screen.width/30*0.75,Screen.width/30,10);
}
function noOkDraw(nocheck:int,noSizeXpos:float,noSize:float,noSizeYpos:float){
var a0 = nocheck/10000; // 첫번째 숫자
var a1 = nocheck%10000; // 나머지로 다음 숫자들을 찾은 다음
var a2 = a1/1000; // 두번째 숫자
var a3 = a1%1000; // 나머지로 다음 숫자들을 찾은 다음
var a4 = a3/100; // 세번째 숫자
var a5 = a3%100; // 나머지로 다음 숫자들을 찾은 다음
var a6 = a5/10; // 네 번째 숫자
var a7 = a5%10; // 나머지로 다음 숫자들을 찾은 다음
var a8 = a7/1; // 다섯번째 숫자
var a9 = a7%1; // 나머지로 다음 숫자들을 찾은 다음
GUI.DrawTexture(new Rect(noSizeXpos*2,noSize*noSizeYpos, noSize, noSize),tex[a0], ScaleMode.StretchToFill, true, 0.0F);
GUI.DrawTexture(new Rect(noSizeXpos*3,noSize*noSizeYpos, noSize, noSize),tex[a2], ScaleMode.StretchToFill, true, 0.0F);
GUI.DrawTexture(new Rect(noSizeXpos*4,noSize*noSizeYpos, noSize, noSize),tex[a4], ScaleMode.StretchToFill, true, 0.0F);
GUI.DrawTexture(new Rect(noSizeXpos*5,noSize*noSizeYpos, noSize, noSize),tex[a6], ScaleMode.StretchToFill, true, 0.0F);
GUI.DrawTexture(new Rect(noSizeXpos*6,noSize*noSizeYpos, noSize, noSize),tex[a8], ScaleMode.StretchToFill, true, 0.0F);
}
2013년 7월 28일 일요일
2013년 7월 25일 목요일
데이터 저장
- 게임 데이터를 게임이 종료된 후에도 사용할 수 있도록 저장하고 싶은 경우
- 기본 타입(type)의 데이터 말고 좀 더 복잡한 데이터를 저장하고 싶은 경우
개요
유니티에서 데이터를 저장하는 게 생각만큼 쉽지 않는 건 사실입니다. 전체 씬, 플레이어 위치 정보 등을 저장하려는 경우에그 작업량이 만만치 않습니다. 이번 튜토리얼 그리고 후에 있을 튜토리얼을 통해 유니티에서 데이터를 저장하는 좀 더 쉬운 방법을 보여드리겠습니다. 이번 튜토리얼에서는 데이터를 저장하는 몇 가지 다른 방법들과 이렇게 저장된 데이터들을 RPC 연결 / 서버에 전달해서, 게임에 사용하는 방법을 보여드리겠습니다.
가장 기본적인 방법
기본적인 방법부터 시작해 보겠습니다. 단순한 데이터형(type)에 대해서는 여러분께서 잘 알고 계실 PlayerPrefs으로 저장하는 방법을 확인 하는 차원에서 한번 알아보도록 하겠습니다. PlayerPrefs는 기본형의 데이터를 문자열키 (string key) 값과 함께 저장할 수 있도록 해줍니다. 단순한 데이터를 저장하고 불러와 사용하기에 좋은 방법입니다.
게임 내의 값들
씬 전환 시에 데이터를 저장하고 이를 공유하고 싶은 경우에 좋은 방법 중 하나는 정적 클래스 (static class)를 만들어 이 클래스에 데이터를 저장하는 방법입니다.
그리고 이 값을 읽고 싶을 때는 아래와 같이 사용하면 됩니다.
이와 같은 클래스를 사용하는 방법의 단점은 인스펙터(Inspector)에서 값을 조절할 수 없다는 점 입니다. (싱글톤으로 매니져 클래스를 구성하면 이 점은 보완할 수 있습니다..)
- 인스펙터와 코드 모두에서 값을 조절하고 싶은 경우에는 DontDestroyOnLoadobject 함수를 이용하는 방법을 고려할 수 있습니다. (아래 코드 참고)
그리고 아래와 같은 방법으로 값에 접근할 수 있습니다.
좀 더 복잡한 데이터 저장하기
위의 예제에서 PlayerPrefs를 사용해서 high score를 저장하는 방법을 살펴봤습니다. 하지만 위에서 살펴본 방법으로는 많은 데이터를 저장하는 데에는 한계가 있습니다. 따라서 아래와 같이 high score를 저장할 수 있는 테이블을 정의해서 저장하는 방법을 고려해볼 때가 왔습니다.
위의 예제를 살펴보면, List를 이용해서 ScoreEntry 클래스 정보를 담아 테이블처럼 사용합니다. 하지만 이 정보를 어떻게 저장해야 할까요? – 바로 BinaryFormatter를 사용하면 됩니다! BinaryFormatter를 사용해서 “파라미터를 갖지 않는 생성자”를 갖는 클래스라면 어떤 클래스도 바이트 배열로 변환하고, 변환된 배열을 다시 문자열 값으로 변환할 수 있습니다. (문자열 값으로 변환하면, PlayerPrefs에 저장할 수 있겠죠?) 아래 보이는 네임 스페이스들을 추가해줘야 한다는 점을 잊지 마시구요.
유니티 오브젝트 저장하기
유니티 오브젝트들을 저장하는 방법은 생각보다 까다로울 수 있습니다. 그래서 대부분 유니티 오브젝트가 갖는 값을 유추해 볼 수 있는 클래스를 새로 생성하고 이 클래스의 값을 저장합니다. 예를 들면, RPC 호출을 통해 여러 개의 AnimationStates를 전달하고 싶은 경우에, AnimationStates를 대신할 수 있는 클래스를 생성해서 이 값을 전달하게 됩니다. 다른 유니티 오브젝트의 값을 전달하려는 경우에도 상황은 마찬가지겠지요.
RPC를 통해 전달하기
아래 코드를 보면, 어떤 클래스라도 문자열로 변환할 수 있도록 하는 방법을 보여줍니다. 이를 이용하면 RPC를 통해 모든 파라미터 값을 보낼 수 있겠죠?
문서화 되지는 않았지만, RPC 역시 byte[]을 보낼 수 있습니다. 이 값은 BinaryFormatter를 이용해서 우리가 원하는 값으로 바로 변환할 수 있도록 도와줍니다. 문자열로부터 변환하는 시간을 줄여주기 때문에 더 좋다고 할 수 있습니다.
이제 highscores 값을 다른 플레이어에 전달할 수 있도록 하는 능력이 갑자기 생겼네요.
웹으로 전달하기
WWWForm 을 이용해서 웹 서버에 데이터를 전달하는 방법은 아주 쉽습니다. 하지만 위에서 보았듯이 Convert.ToBase64String()를 이용해서 바이너리 데이터를 문자열로 변환하는 방법이 제일 쉽지 않을까 생각되네요. (물론 여러분이 사용하시는 서버에 따라 달라질 수 있습니다)
파일로 저장하기
파일로 저장하는 방법도 물론 쉽습니다. 유니티가 제공하는 Application.persistentDataPath를 이용해서 파일을 저장할 위치를 정하고, MemoryStream 대신 FileStream 을 사용하면 됩니다.
결론
이번 튜토리얼을 통해서, 데이터를 저장하고 게임 중에 이 값들을 이용하는 몇몇 새로운 방법들을 익히셨으리라 생각합니다.
출처 http://unitygems.com/saving-data-1-remember-me/
위의 자료 번역했습니다. 처음 번역한거라..많이 부족하지만 읽어 주셔서 감사하고, 원문 확인 하셔도 좋을 것 같습니다.
2013년 7월 13일 토요일
GameControll
#pragma strict
public enum GameState { playing,bonus, gameover }; // 게임 상태 현재는 세가지..
public var platformPrefab : Transform; // 프레팹을 담을 변수
public static var gameState:GameState; // 게임 상태 저장 변수
private var playerTrans:Transform ; // 참조할 플레이어를 담을 변수
private var platformsSpawnedUpTo :float = 0; // 얼만큼 위에서 나타날 것인가
private var platforms: ArrayList ; // 담을 배열
private var nextPlatformCheck:float = 0.0f; // 다음번엔 어느 정도 위치에 나타나나?
function Awake () {
playerTrans = GameObject.FindGameObjectWithTag("Player").transform; // 플레이어를 참조하고
platforms = new ArrayList(); // 새로운 배열을 만든다.
SpawnPlatforms(25.0); 플랫폼 발생 함수... 위에 25정도 위에 나타나게 한다.
StartGame(); // 게임을 진행하는 함수.. 게임스테이트를 변하게 한다.
}
function Start ()
{
Time.timeScale = 1.0f; // 플레이..
gameState = GameState.playing; // 게임중으로 바꿈..
}
function StartGame()
{
Time.timeScale = 1.0f;
gameState = GameState.playing;
}
function GameOver()
{
Time.timeScale = 0.0f; //멈춤
gameState = GameState.gameover; // 상태변수
GameGUI.SP.CheckHighscore();
}
function Update () {
//Do we need to spawn new platforms yet? (we do this every X meters we climb)
var playerHeight:float = playerTrans.position.y;
if (playerHeight > nextPlatformCheck)
{
PlatformMaintenaince(); //Spawn new platforms
}
//Update camera position if the player has climbed and if the player is too low: Set gameover.
var currentCameraHeight:float = transform.position.y;
var newHeight:float = Mathf.Lerp(currentCameraHeight, playerHeight, Time.deltaTime * 10);
if (playerTrans.position.y > currentCameraHeight)
{
transform.position = new Vector3(transform.position.x, newHeight, transform.position.z);
}else{
//Player is lower..maybe below the cameras view?
if (playerHeight < (currentCameraHeight - 10))
{
GameOver();
}
}
//Have we reached a new score yet?
if (playerHeight > GameGUI.score)
{
GameGUI.score = playerHeight;
}
}
function PlatformMaintenaince()
{
nextPlatformCheck = playerTrans.position.y + 10;
//Delete all platforms below us (save performance)
for(var i:int = platforms.Count-1;i>=0;i--)
{
var plat:Transform = platforms[i];
if (plat.position.y < (transform.position.y - 10))
{
Destroy(plat.gameObject);
platforms.RemoveAt(i);
}
}
//Spawn new platforms, 25 units in advance
SpawnPlatforms(nextPlatformCheck + 25);
}
function SpawnPlatforms(upTo:float ) // 일종의 간격
{
var spawnHeight:float = platformsSpawnedUpTo; // 세로간격
while (spawnHeight <= upTo) // 보다 작으면 반복
{
var x:float = Random.Range(-10.0f, 10.0f); // x 위치 좌우 위치...나중에 레이로 쏴서 체크
var pos :Vector3 = new Vector3(x, spawnHeight, 12.0f); // x 좌표를 기반으로 간격
var plat:Transform = Instantiate(platformPrefab, pos, Quaternion.identity); // 프레펩을 pos 위치에 붙인다. 회전 없이
platforms.Add(plat); // 배열에 집어 넣는다.
spawnHeight += Random.Range(1.6f, 3.5f); // 세로 간격
}
platformsSpawnedUpTo = upTo;
}
public enum GameState { playing,bonus, gameover }; // 게임 상태 현재는 세가지..
public var platformPrefab : Transform; // 프레팹을 담을 변수
public static var gameState:GameState; // 게임 상태 저장 변수
private var playerTrans:Transform ; // 참조할 플레이어를 담을 변수
private var platformsSpawnedUpTo :float = 0; // 얼만큼 위에서 나타날 것인가
private var platforms: ArrayList ; // 담을 배열
private var nextPlatformCheck:float = 0.0f; // 다음번엔 어느 정도 위치에 나타나나?
function Awake () {
playerTrans = GameObject.FindGameObjectWithTag("Player").transform; // 플레이어를 참조하고
platforms = new ArrayList(); // 새로운 배열을 만든다.
SpawnPlatforms(25.0); 플랫폼 발생 함수... 위에 25정도 위에 나타나게 한다.
StartGame(); // 게임을 진행하는 함수.. 게임스테이트를 변하게 한다.
}
function Start ()
{
Time.timeScale = 1.0f; // 플레이..
gameState = GameState.playing; // 게임중으로 바꿈..
}
function StartGame()
{
Time.timeScale = 1.0f;
gameState = GameState.playing;
}
function GameOver()
{
Time.timeScale = 0.0f; //멈춤
gameState = GameState.gameover; // 상태변수
GameGUI.SP.CheckHighscore();
}
function Update () {
//Do we need to spawn new platforms yet? (we do this every X meters we climb)
var playerHeight:float = playerTrans.position.y;
if (playerHeight > nextPlatformCheck)
{
PlatformMaintenaince(); //Spawn new platforms
}
//Update camera position if the player has climbed and if the player is too low: Set gameover.
var currentCameraHeight:float = transform.position.y;
var newHeight:float = Mathf.Lerp(currentCameraHeight, playerHeight, Time.deltaTime * 10);
if (playerTrans.position.y > currentCameraHeight)
{
transform.position = new Vector3(transform.position.x, newHeight, transform.position.z);
}else{
//Player is lower..maybe below the cameras view?
if (playerHeight < (currentCameraHeight - 10))
{
GameOver();
}
}
//Have we reached a new score yet?
if (playerHeight > GameGUI.score)
{
GameGUI.score = playerHeight;
}
}
function PlatformMaintenaince()
{
nextPlatformCheck = playerTrans.position.y + 10;
//Delete all platforms below us (save performance)
for(var i:int = platforms.Count-1;i>=0;i--)
{
var plat:Transform = platforms[i];
if (plat.position.y < (transform.position.y - 10))
{
Destroy(plat.gameObject);
platforms.RemoveAt(i);
}
}
//Spawn new platforms, 25 units in advance
SpawnPlatforms(nextPlatformCheck + 25);
}
function SpawnPlatforms(upTo:float ) // 일종의 간격
{
var spawnHeight:float = platformsSpawnedUpTo; // 세로간격
while (spawnHeight <= upTo) // 보다 작으면 반복
{
var x:float = Random.Range(-10.0f, 10.0f); // x 위치 좌우 위치...나중에 레이로 쏴서 체크
var pos :Vector3 = new Vector3(x, spawnHeight, 12.0f); // x 좌표를 기반으로 간격
var plat:Transform = Instantiate(platformPrefab, pos, Quaternion.identity); // 프레펩을 pos 위치에 붙인다. 회전 없이
platforms.Add(plat); // 배열에 집어 넣는다.
spawnHeight += Random.Range(1.6f, 3.5f); // 세로 간격
}
platformsSpawnedUpTo = upTo;
}
javascript class 사용법
class itemClass {
var walkSpeed:float = 3.0;
var runSpeed:float = 10.0;
var inAirControlAcceleration:float = 1.0;
var gravity:float = 60.0;
var maxFallSpeed:float = 20.0;
}
var itemacess : itemClass;
itemacess.walkSpeed=15.0;
많은 변수들을 관리하기 어려울 때...
클래스를 만들어서 관리하면 편합니다.
var walkSpeed:float = 3.0;
var runSpeed:float = 10.0;
var inAirControlAcceleration:float = 1.0;
var gravity:float = 60.0;
var maxFallSpeed:float = 20.0;
}
var itemacess : itemClass;
itemacess.walkSpeed=15.0;
많은 변수들을 관리하기 어려울 때...
클래스를 만들어서 관리하면 편합니다.
피드 구독하기:
글 (Atom)