목표

UI이벤트에 스프라이트애니메이션 넣기
UI이벤트가 시간차로 나오게 연출하기
웹에 빌드 게시

 

게임이 완성되서 기분좋긴한데... 뭔가 뭔가다. 2%부족한느낌이다. 누군가는 에 또야?!?라고 하겠지만,

아트전공자라면 공감할 것이다

요리가 완벽한데 아무래도 중간에 소금을 덜친게 찝찝해 간을 더 맞춰야할것같다는 기분 말이다.

 

뭔가.. 잔뜩 코드만 따라하느라 내게임이라는 요소를 들넣어줬다고 느꼈다. 컨셉은 확고하지만!

그래서 배우는데 너무오래걸리는 연출보다는 애니를 조금넣어서 킹받게하려고 스프라이트넣는법을 배웠다

쫌만 젊었더라도...늦게일어나도 너무 피곤함....3.3......자고싶음...

지금도이런데 더늙으면아무것도 공부못하겠지.. 그리되기전에 빨리 배워놓자ㅠㅠㅠ


UI이벤트에 스프라이트애니메이션 넣기

이건 상대적으로 간단히 만들었으므로 요점도 간단히 하겟다

트럭이미지를 넣고싶어서 테스트로 만들어줫다

1 먼저 스프라이트를 제작한다

첫번째는 애니테스트용으로 썼고 두번째는 좀 다듬은 버전이다.

 

스프라이트는 뽑을때 한장에 뭉쳐서넣어주면 유니티가 알아서 잘라주더라고

아무튼난 클튜를사용하니 포토샵처럼 애드온은못쓰고 걍 한장에 뭉쳐줬다(애드온그리우면 또 쓸듯..)

2 프로젝트에 끌어오면 하위에 두개 생겨있을텐데 다잡고 위로 끌면

3 애니메이션파일을 저장하라는 창이 뜬다 적당한이름으로 저장해주자

4 나는 확실한정리를위해 적당한이름의폴더도 만들어서 넣어줬다.

5 이상하게 잘려있다싶으면 가져온 스프라이트 폴더에서 에디터 열어서 박스가 제대로 나눠졌나 보고 수정하면된다

보통 가져올때부터 유니티가 알아서 잘 잘라준다. 내가보면 걍 그런것같다.

 

6 가져왔으면 프로젝트에 애니 클립이랑 컨트롤러가 생겨있다 

 

7 다음 애니컨트롤러를 더블클릭해서 들어가면 왼쪽에 스프라이트 호출구조가 있을거고

  저걸 클릭하면 오른쪽에 인스펙터가뜨는데 저기서 스프라이트의 절대속도를 바꿔줄수있다

  나는지금 프레임이 둘밖에 없기때문에 처음에 1로 되어있을때 테스트해보면 너무빨라서 프레임이 안보인다

  이걸 좀 느리게 바꿔준다.

8 하이어라키에서 레이어순서도 제일 앞으로 나오게 해준다

 

 

결과

 

 

 

UI이벤트가 시간차로 나오게 연출하기

채찍피티에게 물어봤더니 처음에는

UI와 스프라이트를 하나의 오브젝트 태그(DeathUI)로 만들어서

비활성화후 스크립트로 추가하는 방식을 제안했다. 하지만 내 상황은 다르다.

이 방법대로면 나는 DeathUi라는 새오브젝트 안에 GUI와 스프라이트를 같이 넣어야할것이다

하지만 나는 이미 restart버튼을 플레이어 안에 만들어버려서
이를 묶어주면 다시 하이어라키가 꼬여버릴것 같아 더 복잡해질것 같았다.

그래서 묶지않고 스크립트만으로 해결하는법을 물어봤다.

 

우리짚티친구는 게임매니저안에서 해결하는방법으로 코루틴과 yield return을 추천했다.

 

결과

트럭스프라이트는 성공했다만, 이번에는 UI버튼이 제대로 안 뜬다.

 

 

 

왜지 싶었는데 애초에 내가 ui빌더로 버튼을 만들어줬기때문에

버튼자체가 게임매니저에 오브젝트로 들어갈 수 없었다.

 

그리고 온콜리전에서 플레이어가 죽었을 때

인스턴스밑에 해당버튼을 디스플레이로 추가해줬기 때문에 이게 원인인가싶었다

 

그래서 우리ai친구에게 다시 문제의 의도를 바꿔서 물어봤다.

 

 

 

...의심했던부분이 문제였던 게 맞았나 보다.

 그런데 이상하다. 저 style함수를 플레이어에서 게임매니저로 옮기라해서 옮겼더니

오류가 나는것임. using지시문이 없다해서 UIelements넣어줬는데도 크래시 남.

 

그래서 다시다시 질문.

아아아아아

오...그리고 중요한 사실을 깨달았다.

저때만해도 using지시문에 집중해서 버튼을
게임오브젝트로 할당한게 문제였을거라고는 생각하지 못했다.

이런 거

스테틱, 플로트,프라이빗은 공개되지않고 퍼블릭만 공개다 라는 것에만 집중하느라

퍼블릭 뒤에 있는 오브젝트도 종류가 다른 게 있을거라고는 미처 몰랐던 것이다ㅋㅋㅋㅋ

(여기는 개초보가 개발을 이해하려고 삽질하는 글입니다. 왜몰라싶어도 이해하소서.)

 

그리고 역시 내가만들어준 UI툴킷의 정보가 달랐던게 이유가 맞긴 했다.

버튼 만든방식이 달라서 쓰는 함수도 다르다는 뜻

  • UI툴킷 버튼 - VisualElement 전용
  • 캔버스 버튼(전에 시작화면으로 만든 것) - SetActive 전용

아아 둘중 하나로 통일했더라면...

 

아무튼 나는 툴킷을 썼기때문에!!!! 바꿔주자!!! 코드를!!!

나는 플레이어스크립트에있던 버튼을 스코어관련 코드와 분리해서 게임매니저에 넣어줬다.

좀 복잡하지만...이렇게할수밖에..ㅠㅠ

1 버튼관련 지시문과 함수는 다 게임매니저로 옮겨오고

 

2 플레이어에선 score 관련 코드 지시문과 정의하는 함수들만 남겨놨다.

 

 

 

 

3 그리고 ui레이아웃이 들어있는 gui를 게임매니저에 넣어줬는지 반드시!!! 확인한다!!!

 

 

결과

 

 

 

웹에 빌드 게시

이거 정말..다음날에 하려고했는데... 생활상의문제로 집가구들 배치 바꾼다고 무리해서 좀 쉬었다.

덕분에 당초에 생각했던일정보다 훨씬 늦었다. 한 이틀인가 삼일 늦은거같다.

그럼에도 쉬는게 중요하다생각해서(겸사겸사 게임에 부족한 요소도 채워넣고) 여유롭게 뒀다.

덕분에 화면에 트럭이 생겼고 시간차 스크립트도 알게 됐지 않은가. 급할수록 여유를 가지자.

빌드 올리는게 다음주가 아닌게 어딘가.!!

 

1 ctrl+shift+b 또는 파일>빌드프로필로 빌드에 드간다

2 걍 복붙인데 먼저 씬리스트에서 내 프로젝트씬을 넣어줘야한다 기본씬으로되있는데 저건 내 씬이 아니다

체크되어있는걸 딜리트해서 삭제해주자

 

3 그리고 지금 열려있는씬을 추가버튼을 눌러 넣어주자

 

4 유니티 설치할때 웹빌드 서포트를 깔지 않았으면 모듈없다하는데 

눌러서 깔고 유니티를 다시 시작해주자. 왜냐면 웹용으로 올릴거기 때문이다.

모바일키셋팅을 해줬다면 모바일용을 따로 빌드해서 올릴수도 있다.

 

5 그후 플랫폼 전환.

 

 

6 이후 전환될때까지 기다렸다가, 빌드가 준비되었으면 밑에 버튼이 생긴다.

눌러서 저장할 폴더를 만들고 빌드를 생성하자.

이 과정에서 컴파일링 에러가 생길수도 있다.

그럴때는 당황하지말고 스크립트 어디에 문제가 있는지 알려주는

빨간오류메시지가 뜨니 잘 읽어보고 오브젝트나 스크립트들을 살펴보고 수정하자.

 

나의 경우는 예전에 만들어준 세이브매니저가 더이상 사용되지 않아서 스크립트 연결을 끊었는데,

오브젝트하고는 연결되지 않았지만 플레이어에 넣은 public savemanager부분이 남은게 문제가 되어서 수정해줬다.

 

참 그리고 메인화면에 있는 시작버튼이 해상도가 달라지면 크기가 튀어서 그것도 수정했다.

canvas하위에서 버튼(textmeshpro)로 만든건 캔버스에서 대중적인 크기 기준으로 수정해줘야한다

text도 디자인 바꾸면서 고정값->오토사이즈 달라진 해상도에 알아서 맞추게 세팅해줬다.

 

결과

 

 

이제 진짜 최종의최종의최최종빌드를 세팅하자.

빌드가 완료되었다:D

 

7 받은 빌드를 zip으로 압축하고 유니티플레이에 끌어다놓아서 게시한다.

이것저것 제목,설명, 썸네일, 더 할거면 플레이영상도 찍어올릴수있다.

 

Unity Play

The place for aspiring game creators to share their latest Unity creation. Gain inspiration and find thousands of FPS, Karting, 2D Platformer and other creations with Unity. No experience needed, just jump in for the chance to be featured!

play.unity.com

 

여기서 또 하나 느꼈다... 키 조작감이 유니티랑 웹이랑 *어마어마하게* 다르구나!!

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ유니티에서 날라다니던 캐릭터가 어째서 웹에서는 굼벵이처럼 기어다니는지...

시작하자마자 제대로 움직이지도 못하고 죽는 캐릭터...

웹 반응속도가 미친듯이 느린걸 보고 난 당황할수밖에 없었다...

거의 끌려다니는 수준

그러나 음...왜인지 금방 납득했다.. gif를 개시할때도 마찬가지지 않았는가.

웹에 올리기만 하면 굼떠지는 그 실행속도를 기억해봐라!!!

알파테스트의중요성 새삼 깨닫고 가입니더.

아무튼 아까의 최종의최종의최최종빌드에

또 최종최종최최종최종쓰를 붙여 드디어 완성된, 하찮은 빌드를 드디어 개시했다.

뿌뿌뿌뿌이

이게 빌드군아. 살짝 감격했을지도..?

내가 이걸 2주안에 해냈다고? 해냈다고??

 

오음아예 어예 어우예 내가해냄 우아예 시부럴~~~~

 

 

내가뭐랬어 되면한다고 어? 되면해~~ 할수있는거여~~

 

 

내가 만든 게임은 여기서 플레이해볼수있다.

 

play.unity.com game

 

play.unity.com

 

 

목표

플레이어데이터를 json으로 저장하기
저장버튼에 json세이브데이터 연동하기
정지된 게임화면에 시작버튼 넣기

 

 

이것도 게임에 꼭 넣고싶은기능 -데이터 저장 불러오기 기능-이라 따로 공부했다.

이번에도 자문은 짚티와 함께.

(이분도 만능이 아니라서, 할루시네이션고쳐달라고 복수오답중 선택해주기를 요구해서 힘들었다. 둘다 틀렸어!!둘다!!!)

유니티에서 Json 저장하는 순서

① 저장할 데이터 구조 만들기 (class)
② 데이터를 Json 문자열로 변환
③ PC/모바일에 파일로 저장
④ 필요할 때 다시 불러오기

 

플레이어데이터를 json으로 저장하기

저장할 데이터 구조만들기

PlayerData 스크립트 생성하기 ->json으로 변환하려면 이 Serializable구조가 필요함

[System.Serializable]
public class PlayerData : MonoBehaviour
{
    public int level;
    public float score; 
    public float hp;
    public int gold;
    public float posX;
    public float posY;

    public PlayerData(float score, int level, int hp, int gold, float x, float y)
    {
        this.score = score;
        this.hp = hp;
        this.gold = gold;
        this.posX = x;
        this.posY = y;
        this.level = level;
    }

나는 스코어점수만 저장할거라서 다른건 다 막아뒀다

스크립트생성법 등 기초는 이전에 올려둔 리스트 참조.


여기서 주의할점!!!!!!

초기에 플레이어스크립트의 score속성에 int(상수)를줬냐 float(소수)를줬냐 봅시다.

이미 플레이어의 스코어값이 float로 정의되있는데

PlayerData의 스코어값이 int로 되있으면 어떻게 될까?

 

 

이게 다르면 스크립트에 통일성이 없어서 새 public 이름을 지어달라고 하거나 int(float)가 없다고 뜬다

통일하자

그렇지않으면 만들어놓고 안나오게 하는 바보짓을 할 수도

이미 했으니 적고있는거다..아따시빠가


저장/불러오기 관리자 만들기

이 구조에서 플레이어 정보를 저장하는 SaveManager스크립트를 새로 만들자.

using System.IO;
using UnityEngine;

public class SaveManager : MonoBehaviour
{
    //참조 구문
    string savePath;

    void Awake()
    {
        savePath = Application.persistentDataPath + "/save.json";
    }

    public void SavePlayer(PlayerData data) //저장하기
    {
        string json = JsonUtility.ToJson(data, true);
        File.WriteAllText(savePath, json);

        Debug.Log("저장 완료: " + savePath);
    }

    public PlayerData LoadPlayer() //불러오기
    {
        if (!File.Exists(savePath))
        {
            Debug.Log("세이브 파일 없음");
            return null;
        }

        string json = File.ReadAllText(savePath);
        PlayerData data = JsonUtility.FromJson<PlayerData>(json);

        Debug.Log("불러오기 완료");
        return data;
    }
}

 

 

플레이어데이터 저장하기

이전에 만들었던 플레이어 스크립트에 이 score정보를 PlayerData에 저장하게 하는 코드를 넣자

    public int level = 1;
    public float score = 0; //score는 이미 위에서 만들었음
    public float hp = 100f; 
    public int gold = 0;
Public class 안에:

    void SaveGame()
    {
        PlayerData data = new PlayerData(
        score,
        level,
        hp,
        gold,
        transform.position.x,
        transform.position.y
        );

        saveManager.SavePlayer(data);
    }

 

업데이트에 테스트용으로 넣을 구문도 일시적으로 입력해주자

        if (Input.GetKeyDown(KeyCode.S)) //S키누르면저장됨
        {
            SaveGame();
        }

->이건 테스트해보면 오류떠서 물어보니 유니티 인풋시스템이 바뀌어서 저 명령을 못쓴다고 함.
버튼으로 테스트하는게 좋을것같아서 버튼방식으로 하기로 했다.

만약 input말고 지금쓰는걸로 바꾸면 

void Update()
{
    if (Keyboard.current.sKey.wasPressedThisFrame)
    {
        Save();
    }
}

이렇게 될것임..

 

 

이제 유니티에서 빈오브젝트를 생성하고

만들어준 세이브매니저 스크립트를 넣고

플레이어 공개탭에 넣어주면 이제 저장 기능이 활성화됨.

 

저장버튼에 json세이브데이터 연동하기

버튼 만들고 세이브데이터 연결하기

위에 S눌러 SaveGame으로 저장하는스크립트를 없앳기때문에 

작동하지않는 void SaveGame부분을 public void로 바꾸어서 활성화시켜주자

 

적당한 거리에 버튼 이름, 텍스트 복사해서 생성해준다음

 

 

그후 저번에만들어줬던 리스타트버튼 위에 세이브버튼을 하나더만들어서 호출시켜줬다

 

 

개초보 유니티로 2d게임만들기 06 파티클과 UI버튼

목표충돌할때 플레이어에 폭발파티클 넣기 UI툴킷으로 재시작버튼 추가하기장애물에도 충돌효과 주기 파티클 추가플레이어 죽을때 효과 죽었을때 재시작하는 판떼기 만들어보자이것으로 간단

sunplant88.tistory.com

스크립트포함 UI버튼만들기는 지난 포스트를 참조

 

 

결과

 

이제 테스트하면서 저장버튼을 누르면

C:\Users\사용자명\AppData\LocalLow\DefaultCompany\프로젝트명 <이 로컬경로에 저장이 된다

...........만?

문제가 생겼다

"You are trying to create a MonoBehaviour using the 'new' keyword.  This is not allowed."

요 오류가 뜬다

플레이어 스크립트를 보자.

거기에 세이브게임관련 코드가 있을것이다.

 

    public void SaveGame()
    {
        PlayerData data = new PlayerData(
        score
        );

그리고 플레이어데이터를 보자.

new를 쓰기 위해서는 PlayerData가 모노비헤이비어면 안된다

monobehaviour는 태생적(?)으로 new를 쓸 수 없다고 함.

씬에 붙거나, AddComponent(게임화면으로 뭔가하는것들)로 붙이는 것만 가능함.

플레이어데이터는 그냥 저장용 상자라서 아무것도 붙지 않음.

✔ : MonoBehaviour 완전히 제거
✔ 씬에 붙이지 않음
✔ 그냥 C# 데이터 클래스

뒤에 있는 모노비헤이비어를 떼고 빈껍데기로 만들어주자.

유니티에서 모노어쩌구가 있는것과 없는것은, 뭘 하는 놈과 그냥 존재하는 놈인 것 같음.

사실 난 아직도 이해가 안되서 세이브매니저는 플레이데이터와의 차이가 뭐냐고 더 물어봤다ㅋㅋㅋㅋ

대답.

요약

👉 SaveManager는 “행동 주체”, PlayerData는 “데이터 묶음”이기 때문이야.

왜 SaveManager는 MonoBehaviour인가?

  • 씬에 존재해야 함
  • 버튼 클릭, UI 이벤트, 게임 흐름에 반응해야 함
  • Awake(), Start() 같은 유니티 생명주기 사용
  • Application.persistentDataPath 같은 유니티 API 사용
👉 즉, 게임 세계와 상호작용하는 관리자

왜 PlayerData는 MonoBehaviour가 아닌가?

  • 씬에 존재할 필요 ❌
  • 행동 없음 ❌
  • 업데이트 없음 ❌
  • 순수하게 값만 들고 있음
  • new로 만들어서 Json으로 저장/복사/전송해야 함
👉 즉, 파일로 저장되는 데이터 덩어리

비유로 한 번에 이해하기

  • SaveManager = 📦 우체국 직원 (언제, 어디에, 어떻게 보낼지 결정)
  • PlayerData = ✉️ 편지 내용 (보내는 정보 그 자체)
직원이 편지가 될 수 없고
편지가 우체국 직원일 필요도 없음 😄

 

 

json파일을 열어보자

스코어 점수가 달라질때마다 저장하면 바뀌어있을것이다.

결과

 


알면 좋은거

만약 세이브데이터의 위치를 바꾸고 싶다면 savemanager의 savepath를 보면 된다

파일로 저장하려면 savepath에 "/원하는폴더/파일명.json"

폴더가 들어가야한다면 생성한후에 위 '디렉토리'코드를 사용해서 폴더의 경로를 넣어주자

사실 나는 이거 D드라이브에 넣을까하고 경로 바꿨었는데 실패해서 이유를 물어보니

 

  • 드라이브 자체(D:\ 같은 경로)로 바꾸는 건 권장도 안 되고, 플랫폼에 따라 실패함
    → 권한·보안·배포 환경 때문에 오류 나기 쉬움
  • 반드시 Application.persistentDataPath 아래에서만 저장해야 안전함
    → 같은 드라이브(C:) 안의 다른 폴더는 OK, 드라이브 변경은 ❌

라고 한다. 그렇구나..... 네.. 프로젝트 깔린데다 할게요...

 


정지된 게임화면에 시작버튼 넣기

버튼 생성하기(ui빌더(X) 텍스트매쉬프로 사용)

정지된 화면에 버튼 넣기... 이거때문에 새벽에 공부하다가 지쳐서 기록은 다음날에 한다.

하이어라키에서 ui>캔버스>패널(TitlePanel)>버튼(StartButton) 순으로 생성후 하위로 넣어준다

캔버스의 sort order(화면 레이어)를 높은숫자로해서 뿌연캔버스가 전체화면을 덮도록 한다

(이런다고 정지되는게 아니고 일케 렌더링만해주고 스크립트는 따로 짜야됨)

 

 

화면을 정지상태로 만들어주는 스크립트 넣기

게임매니저에서 게임시작전에는 화면이 정지해있도록 하는 스크립트를 넣어준다

맨처음 시작할때는 timeScale이 정지상태(0)고 titlepanel 오버레이가 전체화면을 덮음

스타트게임(버튼클릭하면 여기서 반응하게해줄것임) 호출하면 timeScale이 작동상태(1)고 오버레이가 없어지게 함.

두번째로 IsGamePaused를 넣어서 Input System을 멈추게 한다.

 

왜 IsGamePaused까지 넣어줘야하는지 이해가 안되서 물어보니,

1. Time.timeScale은 XY좌표의 물리이동을 시키고
2. InputSystem은 마우스 입력을 시키기 때문에

아예 플레이어를 정지시키려면 이동과 마우스 회전 둘다 해준다고 한다.

 

나루호도~ 끄덕끄덕.

 

그리고

 

플레이어스크립트의 업데이트에서 인풋대신 무조건 IsGamePaused가 관리하게 해준다

이렇게하면 플레이어는 무조건 정지상태가되고 버튼을클릭해줘야만 움직이게됨.

 

버튼에 시작 연동하기

이제 스타트버튼을 나름대로 배치하고 디자인도바꾸고 한 뒤

on click() 에 버튼에 화면을 연동시키자 +를눌러 추가한뒤에

runtime on 되있는지보고 빈칸에 스크립트(x) 오브젝트(o)를 넣어준다

그러면 위에 GameManager.StartGame 입력되면서 버튼이 작동하게된다

난 이거 스크립트입력하는줄 알고 잘못넣었다가 계속 빠꾸먹고 짚티한테 두번정도 물어보고나서야 알았음.

그리고 선택창에 대놓고 '오브젝트'라고 되있어서 허무했다.

버튼 온클릭을 작동할때 해당 오브젝트안의 스타트게임 구문을 출력하라는 구조임.

 

결과

감자버튼 만들어줬음

 

 

드디어 인생 첫 게임다운 게?임이 만들어졌다(나름 감격..!!!)
다음에는 이것을 빌드하여 웹에 퍼블리싱하는것까지 하고 마치도록 하겠다. 

 

지금 유니티6 일부사용자들의 문제가 뭐냐면 에셋사용중에 이 오류보고서가 뜨는 문제다.

유니티6 에서 어젠가 그저께 포럼에도 올라온 따끈따끈한 오류보고이다(...)

내 사유는 패키지매니저에 받아놓은 에셋이 업데이트된 최신버전이라도

문제없는줄알고 받아놓았다가 설치하면 패키지 오류를 보이는 것(어쩌면 내 경우는 흔한 것일지도..)

이거때문인지 몰라도 사람들이 에셋을 받다가 경고를받으면 계속 패키지에 문제가있다고 떠서

유니티자체를 다시 설치해야하는둥 실시간으로 문제를 겪고있다고 한다

오류 디테일을 눌러서 공식포럼으로 들어가보니 공식에서도 긴급사항이라고 해서 검토중인것같다.

 

일단 내 경우를 겪는분이라면 참고하시라고 나도 기록을 남겨본다

왠지 버전차이로 인한 오류라는 생각은 드는데 말이다 그것과별개로 기분은 너무 안좋다...

에셋패키지매니저는 인스톨만있고 삭제나 다운그레이드하는법은 없어서 초보자는 난감할것같다

이 에셋패키지는 패키지매니저에 종속되어있다고 하면서 서비스버전을 설치해달라는 메시지

가 떠서 설치해봤더니

 

이상태로 다시 보면 모든 서비스코드?에 오류가 뜬다

아니 그럼 왜 버전6.0.61 이상에 호환된다는 문구를 넣은 거야..

6.3 최신버전을 쓰는 사람들은 이 부분을 특별히 주의해야겠다.

업뎃했다해놓고는 실제로 안맞는 에셋이 많을수도 있다.

 

나는 이때 당황해서 어 어쩌지?만 반복하면서 패키지 업데이트만 하다가 유니티를 껐는데..

생각해보면 패키지에 자꾸 문제가있다고 뜨는걸보니 백업폴더를 바꿔주면 되지않을까란 생각이 들었다

그래서 이렇게 해결했다. 일단 간단하다.

 

이럴때 쓰기위해 미리 유니티 프로젝트폴더를 로컬에 백업해놓자(라이브러리 빼고)

침착하게 유니티닫고 백업해둔파일에서 요... 패키지 복사해서

오류난 프로젝트>package를 덮어씌워주자

 

다시 열면 알아서 리스트를 리프레시 정렬되면서 오류뜬거 다 지워져있다.

 

백업 안되있으면.... ....그건 잘 모르겠다. 재설치?

나의 경우는 백업파일로 살렸는데, 원인이 뭐냐에 따라 다시 설치해서 되는분도 있고 아닌분도 있다는걸 보면... 케바케인 듯.

 


현재 커뮤니티이용자들의 말을 빌리면,

6~.3f에서 6~.5f로 업데이트한 사용자들도 발견하는 문제라고 한다. 나는 6~.3f 인데?

아무튼 맥에서는 패키지를 다운로드할 수 없거나 찾을 수 없다는 메시지가 뜨기도 하고,

그렇지 않으면 '컴파일오류는 없어서 단순경고로 여기고 무시하는 수준에서 사용'중인 이용자들도 있는 모양이다.

 

 

목표

배경음악과 사운드이펙트 넣기
배경에 파티클 넣기
게임에 모바일기기 호환시키기


서론이라고 쓰고 아무말

해보면서 느낀건데, 아트할때처럼 코딩할때도 진짜 커피한잔빨고 해야할거같다.
안 그러면 코드나 명령우선순위 헷갈릴때 정신이 아득해지는것을 버티기 힘들더라.....
아아 드립커피의 창시자여 도대체 몇년뒤를 예상하신겁니까..

 

여기까지 구 아트종사자의 아무말대잔치였다(나요즘 커피끊고있는데 너무힘들어서 좀씩 마심ㅠㅠ)


 

심화: 게임오버되면 장애물이 화면밖으로 나가게 하기 >너무쉬움주의<

이건 그냥 게임컨셉이랑 안맞아서 안넣을건데, 유니티가 알려주니까 걍 기록함.

접때 만들어준 화면 테두리를 충돌시점에서 없애서,

장애물이 화면 바깥으로 빠져나가게끔 만들어주는 것임.



1. 플레이어 스크립트에 유니티 공개탭으로 만들어줄 Gameobject인 보더페어런츠(아무이름)를 작성

2. 보더페어런츠(아무이름)에 셋액티브(있음-true / 없음-false)

  Destroy(Gameobject);>플레이어가 죽을때라서 거기서 테두리를 없애면 된다.

공개탭에 오브젝트 넣고 테스트

 

결과

게임오버 순간 화면밖으로 빠지는 감자들

 

 

사운드 넣기

1 에셋스토어>마이에셋>패키지매니저 열고 스토어에서 다운받은거 받아주고 프로젝트에 넣기하면 넣어진다

2 프로젝트창의 에셋폴더를 보자

로컬 폴더에서 에셋으로 드래그해도 된다

3 하이어라키 오디오소스에 넣어주고 루프 키고 볼륨 좀 낮춰서 테스트해본다

 

4 플레이어 터질때 나올 사운드도 에셋스토어나 로컬로 찾아

5 폭발이펙트 프리펩을 찾아

6 인스펙터에서 컴포넌트로 오디오소스 추가

   그럼 폭발이펙트가 들어있는 프리펩이 나타날때 사운드가 나올것이다

7 같은 방법으로 플레이어 이미지에도 사운드소스 여러개 붙여본다

   priority(우선권) 이 사운드의 존재감을 얼마나 줄건지(오른쪽일수록 존재감이 옅다)

   pitch 왜곡할 높낮이 조절

 

 

배경에 파티클 넣기

이것도 위에서 파티클시스템 찾고 이름해준후 인스펙터>모양>박스모양>스케일 화면에 맞게 설정

 

지속시간

시작시 수명 (동시에 뜨는파티클수가 많게하려면 늘림)

시작시 속도

시작시 크기 (그냥 크기)

색상

요정도하면 대충 실루엣은 만들수잇음 

별빛같은 파티클은 요정도에서 끝내도 됨

나는 먼지가 날리도록 만들어주고 싶어서 추가로 설정해줌

 

그다음 먼지처럼 입자가 무작위방향으로 흩뜨려지게

1 벨로시티 오버 라이프타임(수명주기동안의 속도) 체크

2 선형(linear)에서 두상수 사이값 클릭해서 두칸 생성

3 XY 각각 -1~1 까지 설정

 

결과

먼지 휘날리는거처럼 사방팔방에 생성됨

 

 

 


추가제작

이제 슬슬 때깔을 바꿔줄 타이밍인것같으니 바로 배경제작해버리기

 

배경만바꿔줬을뿐인데 듀토리얼때랑 전혀다른게임이 된것같은기분이다ㅋㅋㅋ

ui나 이펙트 오브젝트 반경 등 바꾼배경에 맞게 바꿔주기

나만의 비주얼로 바꾸는것이기에 좀더 시간을들여 찬찬이 디테일을 바꿔주자

 

 

참 때깔 바꿀때 배경에 border로 만들어놨던것도 잊지말고 투명화시켜주자

 


 

 

모바일기기에 게임입력감지 호환시키기

1 플레이어 스크립트에 공개변수 추가

 

2 위에 using UnityEngine.InputSystem; 있는지 확인

 

3 플레이어 오브젝트 인스펙터창에 공개탭 2개 추가되있는거 확인

move Forward

액션타입 / Button   

바인딩경로 / Pointer > Press

look Position

액션타입 / Value

바인딩경로 / Pointer > Position

참고: 포인터 입력은 모바일에서 마우스 또는 터치 모두 작동합니다.

 

4 플레이어스크립트의 스타트에서 두가지 모두 활성상태로 만들기 (추가)

        moveFoward.Enable();
        LookPosition.Enable();

 

5 플레이어스크립트의 업데이트에서 마우스 입력이 되는부분을 LookPorition으로 바꿔주기 (수정)

 Vector3 mousePos = Camera.main.ScreenToWorldPoint(LookPosition.ReadValue<Vector2>());

 

6 프레임별로 클릭감지되게 해놓은경우도 moveFoward로 바꿔주기 (수정)

            if (moveFoward.WasPressedThisFrame())
            {
                PaprikaAngry.SetActive(true);
            }
            else if (moveFoward.WasReleasedThisFrame())
            {
                PaprikaAngry.SetActive(false);
            }

 

이제 거의 다 끝낫다

 

시뮬레이터에서 모바일 테스트하기

이러고 이제 유니티에서 시뮬레이터를 열어보자

위에서 열어도되고 게임뷰어 왼쪽에 Game열어서 밑에 시뮬레이터로 열어도 된다

 

그럼 시뮬레이터가 첨에 세로로긴화면으로 뜰텐데 창을 양쪽으로 드래그해주면

모바일기기 변경 / 화면회전 / 플레이최적화 여부 / 컨트롤패널 

 

일단 화면이 안맞으니까 회전해주겟음

 

 

기기화면에서 게임 안전지대가 어디까진지 보여주는선 표시

여기서 왼쪽의 모바일기기를 클릭해 ios/aos별로 화면어디까지 조작할수있는지를 확인하자.

 

또 모바일기기 변경을 눌러 모바일상태에서 게임화면이 어떻게 보이는지를 보자

 이런느낌으로 기기마다 해상도가 달라서 양쪽이 미묘하게 잘려보일수있다

기기 바꿔가면서 에디터에서 적당한 전체화면너비와 충돌 보더 위치를 수정해주자.

 

이런 느낌으로다가 말이다

 

 

유니티에디터에서 테스트 재생할때 게임플레이에 초점을 맞출지 여부도 선택

플레이포커스: 재생시에 선택한 게임뷰에서 초점맞춰 테스트한다

언포커스: 포커스를 강제로 맞추거나 전체 화면으로 최대화하지 않고 게임 화면을 본다

맥시마이즈: 시뮬레이터창을 에디터 전체로 채워서 본다

 

일단 여기서는 플레이포커스를 해주고 재생을 누르자.

지금 에디터에서는 마우스클릭이 안되고 시뮬레이터에서만 클릭될것이다.

이걸 돌리면서 화면이 맞나안맞나보고 에디터에서 수정하고를 반복해 결과물을 얻자

 

 

결과

시뮬레이터 열어놓고 에디터에서 외안되?!하며 광클하지 말자. 방금 내가 그랬기 때문이다.ㅋㅋㅋ

 

컨트롤패널에서 디바이스세팅과 어플리케이션세팅을 해준다고하는데 이건 잘모르므로 패스

 

 

 

 

배경에 카메라연출 설정하기

빠띠끌 수정하려다가 재밌는컨셉이 생각나서 강좌는 제껴두고 독자적인작업을 하나 해주기로햇다.

게임에다 박스로 옮겨지는 감자 사이에서 터지지않기위해 피하는 파프리카라는 스토리를 넣어주기로 했다

그러기 위해서 필요한게 거칠게 흔들리는 상자 내부라는 설정인데

그래서 카메라 연출과 어울리는 배경을 만들어 주기로 했다

개초보쌉초보는 우리 챗짚티와 함께~

생각한 연출은...

1. 평소에 카메라 작게 흔들

2. 감자 맞으면 → 게임 오버 → 카메라 크게 흔들

그 대충 컨셉을 정해놓고 물어보면서 진행했다.

 

유니티에서 필요한것

1 GameManager오브젝트

2 GameManager스크립트 >게임매니저에 넣어줄것

3 CameraShake스크립트 >메인카메라에 넣어줄것

4 배경 이미지보다 카메라를 조금 작게 설정하기(6이라면 5.7정도)

 

여기서 게임매니저에 대한 간단 설명

게임매니저란?
각종 게임이벤트(게임오버, 시작, 점수 등) 처리하는 컨트롤타워

  • Player, Obstacle, UI가
  • 각자 제멋대로 처리하지 않고
  • 👉 “야, 큰일 났어”라고 GameManager에게 보고
  • GameManager가
    카메라 흔들고 / 시간 멈추고 / UI 띄움

나쁜 구조 (초보자가 흔히 하는 실수)
Player → 카메라 흔듦
Player → UI 띄움
Potato → 게임오버 처리
UI → 시간 멈춤

좋은 구조 (GameManager 중심)

Player / Potato
      ↓
   GameManager
      ↓
카메라 / UI / 시간 / 사운드

(근데 저번시간에 전부 플레이어에 붙여놨기때문에 이 게임매니저는 카메라만 처리할것임.)

 

그리고

 

void Start()
{
    Shake(0.5f, 0.1f);
}

카메라 테스트용으로 흔들려면 이 테스트코드를 camerashake스크립트에 넣어본다.

>'덜컹' 하고 흔들리는 효과가 보인다.

 

 

 

1. CameraShake 코드 (카메라 일정하게 흔들림)

using UnityEngine;
using System.Collections;

public class CameraShake : MonoBehaviour
{
    public float idleAmount = 0.01f;
    public float idleSpeed = 2f;

    Vector3 originalPos;
    Vector3 eventOffset;

    void Awake()
    {
        originalPos = transform.localPosition;
    }

    // Update is called once per frame
    void Update()
    {
        float x = Mathf.Sin(Time.time * idleSpeed) * idleAmount;
        float y = Mathf.Cos(Time.time * idleSpeed) * idleAmount;

        transform.localPosition = originalPos + new Vector3(x, y, 0f) + eventOffset;
    }
    public void Shake(float duration, float magnitude)
    {
        StopAllCoroutines();
        StartCoroutine(EventShake(duration, magnitude));
    }

    IEnumerator EventShake(float duration, float magnitude)
    {
        float t = 0f;

        while (t < duration)
        {
            eventOffset = new Vector3(
                Random.Range(-1f, 1f) * magnitude,
                Random.Range(-1f, 1f) * magnitude,
                0f
            );

            t += Time.deltaTime;
            yield return null;
        }

        eventOffset = Vector3.zero;
    }
}

 

여기에 위에서 테스트했던 큰 진동이 일정주기마다 일어나게 하려면?

퍼블릭 클래스 안에

    [Header("기본 진동")]
    public float idleAmount = 0.01f;
    public float idleSpeed = 2f;

    [Header("이벤트 흔들림 주기")]
    public float eventInterval = 3f;   // 몇 초마다 한 번
    public float eventDuration = 0.5f;
    public float eventMagnitude = 0.1f;

    Vector3 originalPos;
    Vector3 eventOffset;
    float timer;
추가

그리고 void update 안에

        // 일정 시간마다 이벤트 흔들림 실행
        timer += Time.deltaTime;
        if (timer >= eventInterval)
        {
            timer = 0f;
            Shake(eventDuration, eventMagnitude);
        }

추가

업데이트에서 타이머를 재다가 시간이 되면 shake를 한번씩 호출하는 구조로 짜면 된다고 한다.

 

메인카메라에서 이제 진동과 흔들림을 설정할수있다

 

결과

 

 


?충돌시 나오는 카메라스크립트가 계속 실패하다

처음에 짚티가 장애물오브젝트에 태그가 필요한 코드를 플레이어스크립트로 추천해줬음

장애물에 처음부터 태그없이 작업중이었는데

말한대로 해보니 태그가 있어봐야 카메라작동도안하고 쓸모가 없음

그래서 짚티가 준 해결방안을 모두 확인한후 그래도 문제가 있음을 어필

태그없이 초보자도 무조건쓸수있는 스크립트를 주문함.

 

처음에 짚티가 추천한 방법

이렇게하라고해서 게임오버구문에 멈춤관련 코드를 넣었는데 삑사리남.

 

2. PlayerController 코드

 void OnCollisionEnter2D(Collision2D collision)
{
    Destroy(gameObject);
    Instantiate(explosionEffect, transform.position, transform.rotation);
    RestartButton.style.display = DisplayStyle.Flex;

    if (collision.gameObject.CompareTag("Obstacle"))
    {
        GameManager.Instance.GameOver();
    }
}

 

이 버전은 오브젝트태그로 게임매니저에 호출해주는 것 같다

 

3. GameManager 코드

using UnityEngine;

public class GameManager : MonoBehaviour
{

    public static GameManager Instance;

    private CameraShake cameraShake;

    void Awake()
    {
        // 전역 접근용
        Instance = this;

        // 메인 카메라에서 CameraShake 가져오기
        cameraShake = Camera.main.GetComponent<CameraShake>();
    }

    public void GameOver()
    {

        // 카메라 흔들기
        cameraShake.Shake(0.5f, 0.1f);
        Invoke(nameof(StopGame), 0.6f); // 흔들린 후 멈춤
    }

    void StopGame()
    {
        Time.timeScale = 0f;
    }

    void Update()
    {       
    }
}

요 빨간색코드가 들어가면 테스트 작동 안했음.

 

 

다음 방법으로 해결하다

수정판(게임매니저로 직접 호출)

2. PlayerController 코드

   void OnCollisionEnter2D(Collision2D collision)
   {
       GameManager.Instance.GameOver();

       Destroy(gameObject);
       Instantiate(explosionEffect, transform.position, transform.rotation);
       RestartButton.style.display = DisplayStyle.Flex;

   }

게임오브젝트가 사라질 때 넣었던 구문에 추가

 

 

3. GameManager 코드

using UnityEngine;

public class GameManager : MonoBehaviour
{

    public static GameManager Instance;

    private CameraShake cameraShake;

    void Awake()
    {
        // 전역 접근용
        Instance = this;

        // 메인 카메라에서 CameraShake 가져오기
        cameraShake = Camera.main.GetComponent<CameraShake>();
    }

    public void GameOver()
    {
        // 카메라 흔들기
        cameraShake.Shake(0.5f, 0.1f);
    }

    void Update()
    {
        
    }
}

빨간색 다 빼고 무조건 카메라만 흔들어주는 매니저로 만들어줌

휴 드디어ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ돼따!!!ㅠㅠㅠㅠㅠㅠㅠ

 

결과

최종 구조

  • 기본 카메라셰이크는 메인카메라에서 호출
  • 플레이어 충돌하는 셰이크는 게임매니저에서 호출
  • 플레이어는 카메라가 동작하는것을 모름
  • 플레이어는 지가 죽을때만 매니저가 뭘 선언한다는것만 앎

이렇게 호출담당 스크립트랑 그대로 받기만 하는 스크립트를 분리 해줬음

 

 

+ Recent posts