//구글콘솔 광고 추가가

어제 써둔 pseudo 3d game 유니티 공부에 대해 저장해 두려고 글을 쓴다. 

일단 내가 만든 방법은 2d 배경 1장에 도로의 가운데 라인과, 장애물이 플레이어의 반대 방향(위에서 아래 방향)으로 이동하게 해서 플레이어는 가만히 있지만 이동하는 느낌을 만들어 봤다. 장애물 만드는 방법과 도로 라인 만드는 방법을 다르게 해 봤다. 사실 두 가지 다 각자 테스트 해보다가 각자 만들어졌지만 원리는 비슷할 것이다. 

 

 


1. 우선 게임에 필요한 스프라이트들을 구글에서 찾아서 들고온다.

 

나의 경우는 아래 스프라이트 사이트에서 많이 이용한다.

https://www.spriters-resource.com/

 

The Spriters Resource

This page does not work well in portrait mode on mobile. Please rotate your device.

www.spriters-resource.com

생각보다 많은 다양한 게임들의 스프라이트들을 다운로드할 수 있다. 만약 배경이 있다면 aseprite 프로그램을 사용해서 제거해 준다. 물론 코드로 뒷배경을 삭제시켜 주는 방법도 있다. 하지만 나는 이 프로그램을 돈 주고 샀기 때문에 뽕을 뽑아야 한다는 마음으로 작은 작업이라도 사용하는 편이다.

 

ai를 사용해서 나만의 배경을 만들어 볼까도 했지만 2시간 동안 하늘의 사이즈를 줄여주지 않는 ai에게 굴복하고 그만뒀다.(회원가입도 했는데... 하늘을 줄여주는 게 그렇게 싫었던 걸까. 그렇게 소량으로만 줄여주고 싶었던 것일까. 한글도 영어도 전부 먹히지 않았던 너란 ai. 배경 오브젝트들이 점점 한자들이 나오길래 중국인가 싶었지만 난 중국어는 못하는 걸.)

언덕도, 산도, 나무도, 하늘도 더이상 나의 계획대로 변경되지 않았던 나름 예뻤던 내가 요청한 배경. 도로 안에 가운데 라인도 빼달라고 빼달라고 그렇게 말했는데 완강했던 너의 모습. 잊지 못할 거야.

 

2. 유니티에서 몇 가지 작업을 해준다.

일단 우선적으로 2d project를 만들어 준다. 그리고 Obstacle Manager(장애물 관리 매니저) 오브젝트와, RoadLineManager(도로 라인 관리 매니저)를 만들어 준다. 프리팹들은 장애물 프리팹과, 라인 프리팹만 있으면 된다. UI Canvas를 만들어 주고 필요하다면 플레이어가 이동할 수 있는 버튼을 추가로 만들어 주면 된다. 나의 경우 버튼을 눌러 좌, 우로 이동할 수 있게 작업했다. 속도를 늘리는 버튼과 속도를 줄이는 버튼 또한 만들어 두었다.

 

 

 

3. 이제 스크립트를 짜보자.

 

우선 도로 라인이 내려오게 만들어 주는 RoadLineManager를 만들어 보자. 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class RoadLineManager : MonoBehaviour
{
    [Header("Line Spawn Settings")]
    public GameObject roadLinePrefab;
    public int poolSize = 10;
    public float spawnInterval = 1f;
    
    [Header("Line Position Settings")]
    public float startYPosition = 5.7f;
    public float startXPosition = 0.15f;
    public float endYPosition = -1f;
    public float fixedLineSpacing = 1.8f;
    
    [Header("Line Scale Settings")]
    public float startScaleX = 0.05f;
    public float startScaleY = 0.02f;
    public float endScaleX = 0.4f;
    public float endScaleY = 0.5f;
    
    [Header("Movement Settings")]
    public float initialMoveSpeed = 5f;
    public float currentMoveSpeed;
    public float maxMoveSpeed = 15f;
    public float accelerationRate = 2f;
    
    [Header("Line Settings")]
    public int maxVisibleLines = 6;

    private List<GameObject> linePool;
    private float[] linePositions;
    //private float[] spacings = new float[] { 1.0f, 1.2f, 1.5f, 1.8f, 2.2f, 2.7f };
    private float[] spacings = new float[] { 0.8f, 1.2f, 1.8f, 2.5f, 3.3f, 4.2f };
    private void Start()
    {
        linePool = new List<GameObject>();
        linePositions = new float[maxVisibleLines];
        InitializePool();
        currentMoveSpeed = initialMoveSpeed;
        UpdateLinePositions();
        SpawnInitialLines();
    }
    
    private void InitializePool()
    {
        for (int i = 0; i < poolSize; i++)
        {
            GameObject line = Instantiate(roadLinePrefab);
            line.SetActive(false);
            linePool.Add(line);
        }
    }

    private void UpdateLinePositions()
    {
        linePositions[0] = startYPosition;
        
        for (int i = 1; i < maxVisibleLines; i++)
        {
            float spacing = fixedLineSpacing * spacings[i-1];
            linePositions[i] = linePositions[i-1] - spacing;
        }
    }
    
    private void SpawnInitialLines()
    {
        for (int i = 0; i < maxVisibleLines; i++)
        {
            SpawnLineAtPosition(linePositions[i]);
        }
    }

    private void SpawnLineAtPosition(float yPosition)
    {
        GameObject line = GetInactiveLine();
        if (line != null)
        {
            float t = (startYPosition - yPosition) / (startYPosition - endYPosition);
            t = Mathf.Clamp01(t);
            
            float scaleX = Mathf.Lerp(startScaleX, endScaleX, t);
            float scaleY = Mathf.Lerp(startScaleY, endScaleY, t);
            
            line.transform.position = new Vector3(startXPosition, yPosition, 0);
            line.transform.localScale = new Vector3(scaleX, scaleY, 1);
            line.SetActive(true);
        }
    }
    
    private void Update()
    {
        UpdateLines();
    }

    private void UpdateLines()
    {
        int visibleCount = 0;
        float highestY = float.MinValue;

        foreach (GameObject line in linePool)
        {

            if (line.activeInHierarchy)
            {
                Vector3 pos = line.transform.position;
                float t = (startYPosition - pos.y) / (startYPosition - endYPosition);
                t = Mathf.Clamp01(t);

                float scaleX = Mathf.Lerp(startScaleX, endScaleX, t);
                float scaleY = Mathf.Lerp(startScaleY, endScaleY, t);

                // 스케일이 클수록 더 빠르게 이동하도록 수정
                float speedMultiplier = scaleY / startScaleY;
                pos.y -= (currentMoveSpeed * speedMultiplier) * Time.deltaTime;

                line.transform.localScale = new Vector3(scaleX, scaleY, 1);
                line.transform.position = pos;

                if (pos.y < endYPosition)
                {
                    line.SetActive(false);
                }
                else
                {
                    highestY = Mathf.Max(highestY, pos.y);
                    visibleCount++;
                }
            }
        }

        if (visibleCount < maxVisibleLines)
        {
            if (highestY == float.MinValue)
            {
                SpawnLineAtPosition(startYPosition);
            }
            else
            {
                int currentIndex = visibleCount - 1;
                float spacing = fixedLineSpacing * spacings[currentIndex];
                float newYPos = highestY + spacing;
                
                if (newYPos <= startYPosition)
                {
                    SpawnLineAtPosition(newYPos);
                }
            }
        }
    }
    
    private GameObject GetInactiveLine()
    {
        return linePool.Find(line => !line.activeInHierarchy);
    }

    public void IncreaseSpeed()
    {
        currentMoveSpeed = Mathf.Min(currentMoveSpeed + accelerationRate, maxMoveSpeed);
    }
    
    public void DecreaseSpeed()
    {
        currentMoveSpeed = Mathf.Max(currentMoveSpeed - accelerationRate, initialMoveSpeed);
    }
    
    public void ResetSpeed()
    {
        currentMoveSpeed = initialMoveSpeed;
    }
}

 

이 코드를 짤 때 몇 가지 골머리 써야 됐었던 부분이 있었다.

그중 가장 큰 게 라인이 프리팹으로 활성화가 될 때 아래로 내려오는 느낌으로 원근감 있게 내려오려면 처음 적용해 줬던 간격을 계속 유지하면 안 되는 문제가 있었다. 이 부분을 수정하기 위해 나에겐 여러 번의 시도가 필요했다. 그래서 UpdateLines() 함수는 정말 쉴 새 없이 수정됐었다. 그리고 마침내 해결된 방법이 바로 위에 적어둔 스케일의 사이즈에 따라 빠르게 이동시키는 것. 이 부분이 사실 그렇게 어려운 부분은 아니었다. 처음부터 수치를 디버깅해서 확인해 봤으면 문제가 되지 않았을 것이다. 변경시켜 주던 수치값이 제대로 늘어나지 않는 오류를 모르고 도대체 왜 내가 원했던 데로 나오지 않는 거지 하다가 시간을 두배로 썼다. 잊지 말자 디버깅.

 

버튼에 적용시켰던 speed up 버튼과 speed down 버튼에 사용할 함수도 여기 있는 IncreaseSpeed() 함수와 DecreaseSpeed() 함수를 연결해 주면 된다. 필요시 나머지 함수들도 연결시켜줘도 기능은 할 것이다. 

 

나의 경우는 변수들을 많이 만들어 최대한 유니티에서 배경을 바꿨을 때도 사용할 수 있게 작업했다. 지금 보이는 것과 같이 저 배경은 free이기 때문에 가져온 스프라이트지 그렇게 내 마음에 들지 않기 때문일지도 모른다. 도로의 시작과 끝을 잡아줄 위치 변수들과, 라인의 스케일을 관리해 줄 변수들, 라인과 라인 사이의 간격을 정할 변수로 되어있다. 그리고 화면에 나올 라인의 개수 변수가 있는데 이건 간격과 도로의 총길이에 따라 나오지 않을 수도 있다. 프리팹은 오브젝트 풀링을 사용해서 최적화해 두었다. 

 

 

 

장애물 매니저는 내일 다시 정리해 두겠다. 여기서는 역시나 오브젝트풀로 장애물을 관리하고, 도로의 좌, 우에 따라붙어서 원근감 있게 스케일이 커지도록 작업했다.

 

++ 나도 모르게 필요없는 파일인줄 알고 프로젝트 삭제시켜서 장애물 매니저는 그렇게 날라갔다...다시 작업해서 올리도록 하겠다.

728x90
반응형

며칠 전 공부했던 곳에서 봤던 단어 pseudo! 그때도 낯설었던 단어 슈도라는 단어를 이렇게 다시 만날 줄이야.

그때 공부했을 땐 슈도 코드에 대해서였지만 지금은 2d로 3d게임 환경을 만드는 것이다. 오래전 우리가 게임했을 때 대체로 많이 보였던 기법인데 대표적인 게임이 "Out Run" 게임이 있다. 이외에도 레이싱 게임에서는 종종 많이 발견됐었던 것 같다. 그때는 그게 3D인지 2D인지 알게 뭐였는가. 그저 게임하는 게 즐거웠을 뿐이었지. 

그렇게 즐거웠던 게임이 이제 단순히 "참 즐거웠던 게임이었지."란 과거형의 기억이 아닌 순간이 왔다. 새로 작업해 보기로 한 프로젝트 팀에서 딱 "Out Run"게임처럼 구현해 주길 바라는 말을 듣게 되었다.

 

사실 처음엔 뭐 얼마나 어렵겠어라는 생각이 전부였다. 못할 건 없지란 생각으로 일주일 동안 시도해 볼 시간을 얻어 Test를 해보기 전까지 정말 딱 그 생각이었다. 생각보다 구글에 "How do i make OutRun"라고 치는 순간 기가 막힌 정보들도 찾을 수 있어서 쉽게 접근할 수 있겠다 생각했었다. 

 

https://jakesgordon.com/writing/javascript-racer/

 

How to build a racing game

Personal Website for Jake Gordon

jakesgordon.com

https://jakesgordon.com/writing/javascript-racer-v1-straight/

 

How to build a racing game - straight roads

Personal Website for Jake Gordon

jakesgordon.com

 

정말 멋진 내용이다. 도로 기하학에서 약간 주춤거렸지만 생각보다 꽤 술술 재미있게 읽혔다. 

여기와 함께 Pseudo 3d game 부분도 필수로 읽어야 되는 내용이였다.

http://www.extentofthejam.com/pseudo/

 

Lou's Pseudo 3d Page

Lou's Pseudo 3d Page Pseudo 3d en español aqui! Gracias a Luis Peña! (C) 2013 Louis Gorenfeld, updated May 3, 2013 NEW: Important details on the segmented road system and some additional links NEW: An (optional) explanation of finding field-of-view for t

www.extentofthejam.com

 

읽으면서 드는 생각은 "생각보다 어렵겠는데"였다. 

 

위에 글을 읽어보고 하루 후에 유니티를 켜봤다가 그새를 잊은 건지 허튼짓을 했었던 순간이 있었다. 바로 2d 스프라이트들을 아래쪽에 쭉 깔아 두고 플레이어한테 다가오게 하면 안 되나 했다가 그렇게 되면 3d가 되는 거란 걸 인식하고 세상 똥멍청이인가 싶었었다.

누군가는 또 이 방법을 사용해서 작업을 했던 흔적을 발견했다. 완전히 똥멍청이 생각은 아니였던 걸로!

 

뭐 그래도 뭐든 시도는 좋은 거니까 라는 마인드로 다시 시도해 봤다. 그러다 세그먼트들 작업을 할 때 생각했다. 왜 2D환경에서 3d를 구현하고 싶은 걸까. 옛날이라면 어쩔 수 없었던 선택일지 몰라도 요즘 같은 현대 사회에 왜 3d로 만들면 한방에 해결이 되는 이 상황을 난 지금 왜 이렇게 집중하고 있을까였다.ㅎㅎ 이렇게 오늘도 난 성장했다.

 

그럼에도 불구하고 뭔가 슬슬 화면에 나오기 시작했을 때쯤 쾌감은 짜릿했다.

하지만 내 코드에서는 몇 가지 문제점들이 보였다. 속도가 붙을수록 세그먼트들의 간격이 생기는 상황. 뭔가 잘못 만든 게 분명했다. 작업하면서 스케일값도 중요했다. 일주일 동안 이 작업만 할 수 있는 스케줄은 아니었기에 다른 쉽게 가는 방법이 있을까 하고 생각하다가 도로에 있는 차선만 이동하는 것도 나쁘지 않겠다는 생각을 했다. 결과적으로는 나쁘지 않은 결괏값을 얻을 수 있었다. 위의 방식대로 하던 중 문제가 있었던 부분은 연속으로 나오는 부분에서 간격이 벌어지는 문제였다면 라인은 연속해서 나오지 않기에 그대로 작업을 해보았다. 

작업할 때 가장 중점적으로 생각한 부분이 멀리 있는 공간에서 화면 가까이로 오는 부분에서 느껴져야 할 원근감과 스케일 부분, 그리고 왼쪽 도로와 오른쪽 도로에서 오는 장애물의 경우 차선을 따라 각각의 끝에 자연스럽게 유지돼서 붙어 내려와야 하는 부분. 그리고 애증의 간격 부분. 

간격이 생각보다 어려웠던 게 차량 이동을 생각할 때 도로 위의 라인이 멀리서 가까이로 올수록 간격이 넓어져야 하는데 간격을 정해두고 스케일만 키웠던 게 문제였다. 그래서 접근한 부분이 스케일 값이 커질수록 position y의 값을 더 작아지게 해서 간격을 늘려볼까도 생각을 했는데 이렇게 구현해 보니 무슨 문제인지 전혀 원하는 방향대로 나오지 않길래 디버그를 해봤더니 알았다. 내가 원한 만큼의 수치가 계산에서 이루어지고 있지 않았다. 수치를 조절하는 부분을 다시 수정하고 봤더니 이젠 프리팹으로 만들어 둔 오브젝트가 시작 부분에서 종료되는 부분까지 나오는 거리가 문제가 돼서 활성화가 안 되는 버그를 보고 눈을 감았다. 이것저것 머릿속에서 한참 생각해 보다가(프리팹 순서를 정해서 활성화해 볼까, 리스크에 위치값을 저장시켜서 그 부분에 가면 위치를 두 배씩 늘려볼까, 등등 지금 생각해 보면 별의별 말도 안 되는 생각들이 있었다.) 속도 값을 늘려볼까 싶었는데 이게 생각보다 괜찮은 결괏값을 만들어 줬다. 그렇게 장애물과, 라인의 움직임을 구현시켜 두었더니 한결 마음이 편해졌다. 결과적으로 프리뷰느낌으로 보기에 나쁘지 않을 정도의 상태를 만들어 두고 생각해 봤는데 이 방법이 최선의 선택은 아닌 것 같다. 저 위의 내용을 토대로 다시 한번 작업해 봐야겠다.

 

사실 이제 더 이상 이런 부분의 코드 작업을 안 할 줄 알았는데 막상 해보니까 너무 재미있었다. 아무래도 이쪽 길로 가지 않는다 해도 취미로라도 계속해서 관심을 유지해야겠다.

 

 


밑에는 도로 기하학 찾아봤다가 흥미로우면서 경악했던 구글 화면.

728x90
반응형
테스트 케이스 추가
Parameters Return
queue1(int[ ]) queue2(int[ ])
[2, 3, 2] [1, 1, 1] 1
[1, 1, 1, 1, 1, 1, 1, 1, 1, 10] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] -1

 

두 큐 합 같게 만들기 문제

 

내 코드
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution {
    public int solution(int[] queue1, int[] queue2) {
        int answer = 0;
        Queue<int> q1 = new Queue<int>();
        Queue<int> q2 = new Queue<int>();
        long sum1 = 0;
        long sum2 = 0;

        foreach(int n in queue1)
        {
            q1.Enqueue(n);
            sum1 += n;
        }
        foreach(int n in queue2)
        {
            q2.Enqueue(n);
            sum2 += n;
        }
        //평균값 구할때 배열.Sum()으로 계산하니까 런타임 걸렸어. 알아둬라
        //long average = (queue1.Sum() + queue2.Sum()) /2;        
        long average = (sum1 + sum2) / 2;
        
        if((sum1 + sum2) % 2 !=0) return -1;
        
        int maxLength = (queue1.Length + queue2.Length)*2;
        while(answer < maxLength)
        {
            if(average == sum1) return answer;
            
            if(average > sum1)
            {
                int dequeueNum = q2.Dequeue();
                q1.Enqueue(dequeueNum);
                sum1 += dequeueNum;
                answer ++;
            }
            else
            {
                int dequeueNum = q1.Dequeue();
                q2.Enqueue(dequeueNum);
                sum1 -= dequeueNum;
                answer ++;
            }
        }
        return -1;
    }
}

 

728x90
반응형
성격 유형 검사하기 문제

 

내 코드
using System;
using System.Text;
using System.Collections.Generic;

public class Solution {
    public string solution(string[] survey, int[] choices) {
        string answer = "";
        StringBuilder answerSB = new StringBuilder();
        Dictionary<char,int> surveyDic = new Dictionary<char,int>()
        {
            { 'R', 0 },
            { 'T', 0 },
            { 'C', 0 },
            { 'F', 0 },
            { 'J', 0 },
            { 'M', 0 },
            { 'A', 0 },
            { 'N', 0 },
        };
     
        char[] c = new char[2];
        for (int i = 0; i < survey.Length; i++)
        {
            c = survey[i].ToCharArray();

            if (choices[i] > 4)
            {
                surveyDic[c[1]] += choices[i] - 4;
            }
            else if (choices[i] < 4)
            {
                surveyDic[c[0]] += 4 - choices[i];
            }
        }
        answerSB.Append(surveyDic['R'] >= surveyDic['T'] ? "R" : "T");
        answerSB.Append(surveyDic['C'] >= surveyDic['F'] ? "C" : "F");
        answerSB.Append(surveyDic['J'] >= surveyDic['M'] ? "J" : "M");
        answerSB.Append(surveyDic['A'] >= surveyDic['N'] ? "A" : "N");
        answer = answerSB.ToString();
        return answer;
    }
}
728x90
반응형

벌써 11월이 되었다. 이제 올해가 가기 전까지 단 두 달만을 남겨둔 상황이다.
나는 과연 올해에 어떤 삶을 살았는가.
최선을 다해 바쁘게 살았을 까.
결과적으로 나는 그렇지 못했던 것 같다.
바쁘게 살지 않았다 해서 여유를 가지고 나를 위해 살았나 생각해 봐도 나는 이도저도 선택하지 못한 한 해였던 것 같다.

차라리 완벽하게 편안한 쉼을 선택한 것도 아니라는 부분이 아쉽다. 왜 그랬을 까.

나이가 먹을수록 1년의 단위가 참 빠르게 지나가는 것 같다.
체감 상 거의 버스 정류장 지나가듯 한 달, 두 달 그렇게 지나가는 기분이 든다.
그 사이에 내가 하고 싶은 것들에 대해 잠시라도 눈을 떼면 이미 손에서는 멀어져 원래 내려야 할 정류장이 아닌 그다음 정류장으로 가고 있는 상황이다. 처음엔 그 상황이 당황스러웠는데, 이제는 더 늦기 전에 그다음 정류장에서라도 내려야 된다는 것을 알게 된 것 같다.

친구 관계도 그렇다. 나도 모르게 멀어져 버린 내 친구들이 올해도 존재한다. 어렸을 때는 친구를 사귀는 게 참 쉬웠던 것 같은데 이제는 친구를 잃는 것 역시 참 쉬워진 것 같다.

모두가 같은 생각을 할 수 없듯이 모두가 다른 마음을 가지고 세상을 바라보기에, 그 사이에서 오는 아쉬움을 알아차리지 못했던 것이 어쩌면 이유가 되는 것 같다. 
어렸을 땐 같은 시간 속에서 다른 생각과 다른 행동을 할 수 있다는 현실이 새롭고 흥미롭게 다가왔는데 어느 정도 성장한 지금의 상태로 보면 흥미롭게만 생각할 수 없는 것 같다. 아마도 나 또한 변해버린 시각으로 세상을 보는 것일 뿐이겠지.

 

나는 생각하는 것을 좋아한다. 과거에도 그랬고 지금도 그렇고 앞으로도 그럴 것이다.

생각이 쌓이고 그 쌓인 생각 위로 또 다른 생각이 쌓이는 것이 나는 참 좋았다. 더 깊은 생각을 할 수 있다는 것이 아마도 나이가 들면서 얻을 수 있는 큰 장점이라 생각했었다. 간과했었다. 생각에는 좋은 감정들만 있는 것이 아니라는 사실을.

어렸을 때는 기쁨의 기준치가 낮았던 것 같다. 풀밭을 걸어가도 신이 났고, 모든 게 궁금했고, 그래서 모든 시간들이 추억이 되었다.

하지만 커가면서 기쁨이라는 건 생각보다 쉽게 만날 수 있는 존재가 아니었다. 오히려 기쁨보단 슬픔이라는 감정에 대해 더 알아가는 시간이 되었던 것 같다. 그래서 더 모든 것에 무뎌진 감정을 갖게 되었다. 맞다. 나는 어쩌면 지금까지는 알지 못했던 슬픔에 대해 배워가고 있는 것 같다. 감정은 다양하기에 이 시간이 지나면 나는 또 다른 감정에 대해 배우고 있을 것이다. 그렇게 나는 인간이라는 기준치에 맞는 감정들을 가지게 될 테니 지금의 이 생각도, 익숙해지자.

아! 그래도 자격증을 두 개나 땄다. 새로운 환경에서 경험도 해보았다. 잘 되지 않은 결과라 할지라도 꽤 여러 곳에 발을 넣어보기도 했다. 그럼에도 올해의 나에게 부족하다 생각하는 계기가 되었던 이유는 아마도 세상 모두가 나보다 더 바쁘게 열심히 살고 있다는 것을 알았을 때인 것 같다. 하지만 이미 지나간 시간들은 돌아오지 않는다. 나는 오늘, 내일을 더 열심히 생각하고 살아가기로 했다. 나에게 주어진 시간들이 어느 정도 일지 몰라도 열심히 살고 싶다. 오늘 같이 또 후회되는 시간을 갖지 않기 위해서라도. 내년엔 더욱 발전한 나를 만나길 바라고 바란다. 힘내보자!

 

 

 

 

그대가 자신의 별을 따라가는 한, 영광스러운 항구에 실패 없이 도달할 수 있으리라. - 단테의 신곡.

 

728x90
반응형

Google AdMob을 사용해 내가 만들고 있는 유니티 앱에 광고를 넣을 수 있다.
크게 어렵지 않아서 따라 해 보면 바로 광고가 나오는 것을 확인할 수 있을 것이다.
 

우선 첫 번째로 구글 애드몹을 알아야 한다.
1. 구글 애드몹에 가입을 한다.
2. 구글 애드몹에 자신의 어플을 등록시켜 준다.
3. 광고단위를 클릭하고 자신에게 필요한 광고 단위를 선택해 생성해 준다. 나의 경우 전면 광고를 선택해 주었다.
광고 단위 추가 버튼을 통해서 필요한 광고를 추가해 줄 수 있다.

 

자신의 앱 ID는 알아둬야 하니 복사 필수

 
>> 앱 ID는 두 군데서 확인 가능하다. 1번째로 앱 >> 모든 앱 >> 앱 ID.

 
>> 두 번째로 앱설정에서 앱 ID 확인. 편한 곳에서 확인하고 알아두도록 하자.

 

자신의 광고 단위 ID도 알아둬야 됨.

 
여기서 저 광고 단위 ID가 중요하다. 일단 적어두거나 다른 곳에 복사해 두자.  나중에 스크립트에서 사용할 것이다.
이제 구글 애드몹에서 해줄 내용은 끝이 났다.
 

두 번째로 유니티 플러그인을 설치해 주자.
1. 유니티 플러그인으로 Google Mobile Ads Unity Plugin을 받아 설치해 주자.
(아래 링크 참고, 블로그에 적어 둘 당시 이게 최신 같아 보였다. 더 최신 버전이 있다면 선택해서 설치하자.)

https://github.com/googleads/googleads-mobile-unity/releases/tag/v9.3.0

Release Google Mobile Ads Unity Plugin v9.3.0 · googleads/googleads-mobile-unity

Plugin : Updated the iOS GMA SDK dependency version to 11.11.0. Built and tested with: Google Mobile Ads Android SDK 23.4.0 Google Mobile Ads iOS SDK 11.10.0 Google User Messaging Platform Andro...

github.com

 
설치하고 난 뒤에 유니티에서 열어주자. 다 설치가 되면 Assets >> GoogleMobile Ads >>Settings을 누르고  Inspector 창에서 아까 첫 번째로 기억해 뒀던 자신의 Google Mobile Ads App ID를 적어주자.

 


이제부터가 중요하다.

 

코드를 써보자. 물론 우리에게는 다행히 도움의 손길이 있다.

https://developers.google.com/admob/unity/interstitial?hl=ko

전면 광고  |  Unity  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 전면 광고 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 전면 광고는 호스트 앱의 인터페이스를 가

developers.google.com

광고들 마다 어떻게 사용해야 하는지 친절하게 설명해 준다. 역시나 필요한 걸 찾아서 사용하자.
https://developers.google.com/admob/unity/rewarded?hl=ko

보상형 광고  |  Unity  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 보상형 광고 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 보상형 광고는 사용자가 상호작용하는 대

developers.google.com

 

나의 경우 전면 광고였으니 위에 나와있는 도움의 손길을 토대로 그대로 따라 해 주자.

나의 경우 광고매니저 오브젝트를 만들어 스크립트를 하나 추가해주었다. 그대로 써주자.

using GoogleMobileAds;
using GoogleMobileAds.Api;
using System;
using UnityEngine;


// https://developers.google.com/admob/unity/interstitial?hl=ko 참고

public class GoogleMobileAdsScript : MonoBehaviour
{
    public GoogleMobileAdsScript instance;

    private InterstitialAd _interstitialAd;

    // These ad units are configured to always serve test ads. 
    //자신의 광고단위ID 넣어주기
#if UNITY_ANDROID
    //private string _adUnitId = "";
    private string _adUnitId = "";
#elif UNITY_IPHONE
   // private string _adUnitId = "";
#else
  private string _adUnitId = "unused";
#endif


    public void Awake()
    {
        // Initialize the Google Mobile Ads SDK.
        MobileAds.Initialize((InitializationStatus initStatus) =>
        {
            // This callback is called once the MobileAds SDK is initialized.
        });
    }


    public void Start()
    {
        LoadInterstitialAd();
    }
    
    
    public void LoadInterstitialAd()
    {
        // 이전에 로드된 광고 체크, 있으면 제거하고 해제
        if(_interstitialAd != null)
        {
            _interstitialAd.Destroy();
            _interstitialAd = null;
        }

        // 새로 광고를 로드하기위한 요청 생성
        var adRequest = new AdRequest();

        // 광고단위 ID _adUnitId와 adRequest 객체를 전달 받아 광고를 로드.
        InterstitialAd.Load(_adUnitId, adRequest,
            (InterstitialAd ad, LoadAdError error) =>
            {
                if (error != null || ad == null)
                {
                    Debug.LogError("interstitial ad failed to load an ad" + "with error : " + error);
                    return;
                }

                Debug.Log("Interstitial ad loaded with response : " + ad.GetResponseInfo());

                _interstitialAd = ad;
                
                // 성공한 경우 로드된 광고에 대한 이벤트 핸들러를 등록.
                RegisterEventHandlers(_interstitialAd);
            });
    }

    // Shows the interstitial ad.
    public void ShowInterstitialAd() //광고 표시
    {
        if (_interstitialAd != null && _interstitialAd.CanShowAd())
        {
            Debug.Log("Showing interstitial ad.");
            _interstitialAd.Show();
        }
        else
        {
            LoadInterstitialAd(); //광고 재로드
            Debug.LogError("Interstitial ad is not ready yet.");
        }
    }

    // 전면광고에 발생하는 이벤트에 대한 핸들러 등록
    private void RegisterEventHandlers(InterstitialAd interstitialAd)
    {
        // 전면광고 지급 관련 이벤트
        interstitialAd.OnAdPaid += (AdValue adValue) =>
        {
            Debug.Log(String.Format("Interstitial ad paid {0} {1}.",
                adValue.Value,
                adValue.CurrencyCode));
        };
        // 
        interstitialAd.OnAdImpressionRecorded += () =>
        {
            Debug.Log("Interstitial ad recorded an impression.");
        };
        // 전면광고가 클릭 되었을 때 이벤트
        interstitialAd.OnAdClicked += () =>
        {
            Debug.Log("Interstitial ad was clicked.");
        };
        // 전면광고가 열렸을 때 호출
        interstitialAd.OnAdFullScreenContentOpened += () =>
        {
            Debug.Log("Interstitial ad full screen content opened.");
        };
        // 전면광고가 닫혔을 때 호출
        interstitialAd.OnAdFullScreenContentClosed += () =>
        {
            Debug.Log("close Scene");
        };
        // 전면광고가 못 열였을 때 호출
        interstitialAd.OnAdFullScreenContentFailed += (AdError error) =>
        {
            Debug.LogError("Interstitial ad failed to open full screen content " +
                           "with error : " + error);
        };
    }

    private void RegisterReloadHandler(InterstitialAd interstitialAd)
    {
        // Raised when the ad closed full screen content.
        interstitialAd.OnAdFullScreenContentClosed += () => 
        {
            Debug.Log("Interstitial Ad full screen content closed.");

            // Reload the ad so that we can show another as soon as possible.
            LoadInterstitialAd();
        };
        // Raised when the ad failed to open full screen content.
        interstitialAd.OnAdFullScreenContentFailed += (AdError error) =>
        {
            Debug.LogError("Interstitial ad failed to open full screen content " +
                           "with error : " + error);

            // Reload the ad so that we can show another as soon as possible.
            LoadInterstitialAd();
        };
    }

}

 
이제 다 끝났다. 유니티에서 광고를 사용하고 싶은 부분에서 ShowInterstitialAd 함수를 불러오면 된다.
 

그럼 얘가 나올 것이다.
 
 
 
 
 
 
이렇게 다 하고 광고 나오는 건 확인했는데 유니티 Gradle 문제가 생겨서 빌드가 안된다. 하... 속이 상한다.
뭐가 또 문제인 걸까!

728x90
반응형

룰루루루루루룰루 블로그에 짧은 후기를 남기고 한동안 가을만 느끼며 살던 나에게 기적 같은 일이 일어났다.
찍었던 문제가 다 맞은 것인지 푼 문제가 다 맞은 것인지 합격을 했다.
istqb 불합격 ㄴㄴ!! 유후!

사실 정말 시험이 어려웠어서 기대가 하나도 되지 않는 상황이었다. 집에 돌아오는 길 내내 복기하면서 왔을 정도로 다음 시험을 준비해야 되겠구나 싶었는데 이렇게 합격이 뜨다니! ㅎㅎㅎㅎㅎㅎ
최근 들어서 지금 이 순간이 제일 기분이 좋다.
목요일 밤 9시에 결과가 발표 난다는 것을 알고 있었지만 속상할 것 같아서 텐트 밖은 유럽을 보며 애써 잊었다.
그러다 자기 전에 내일 친구를 만나러 가기 전 결과를 보면 친구한테도 별로 좋지 않은 영향을 줄 것 같다는 생각을 하고 슬그머니 sten을 들어가 봤다.
지난번보다 많이 틀렸으려나 하고 들어간 화면에서 “합격” 문구를 본 순간. 난 내 눈을 의심했다. 뭔가 잘못된 것인가 하는 마음으로 로그아웃하고 다시 들어가 봤다. 새로고침도 하고 결과확인도 다시 클릭해 보고 다해봐도 합격이었다. “합격.” 모두가 자고 있는 이 시간에 난 쾌재를 불렀다. 에헤라디야 둥가둥가 룰룰루 합격이라는 두 글자에 이렇게 기쁠 수 있을까.



일단 istqb ctfl v4.0 시험 합격의 기쁨은 여기까지 하고 간단하게 나의 합격 방법을 적어둔다. 누군가에게 도움이 되길 바라며.

0. 시험 시간 진짜 촉박하다. 40문제를 1시간 안에 풀어야 된다 생각하지 말고 50분 안에 푼다 생각하자. 20문제를 풀고 있는데 25분이 지나가고 있으면 속도를 내야 한다. 시험 보는 날 시간이 여유롭다면 일찍 와서 공부하자. 그때가 제일 공부가 잘 된다. 시험장에는 일찍 들어갈 수 있다. 긴장되니까 화장실은 다녀오고 들어가자. 컴싸도 상관없지만 다한증이라도 있다면 볼펜을 추천한다. 안 번진다. 난 그냥 두 개 다 들고 갔다.
 

1. 나는 샘플문제를 각각 5번 정도 풀어봤다. 아이패드를 사용하는 나로서는 레이어를 나눌 수 있어서 풀고 채점하고 복기하고 다른 날 다시 풀고 채점하고 복기하고를 반복했다. (처음엔 A, B, C, D 순서대로 풀고 두 번째부터는 순서대로 하지 말고 계속 섞어가면서 풀었다.)
 

2. 나의 경우 시험 보러 가기 3일 전 인공눈물을 잘못 사용해서 눈병에 걸렸다. 눈알이 검은 눈동자 빼고 전부 부풀어 올라서 눈이 터지려고 하는 상황에 더 이상 눈을 뜨고 공부를 할 수 없어 다시 유튜브 “김기태 님의 sw강의”를 밤새 들었다. 근데 이게 공부했던 내용을 꽤 정리해 주는 계기가 되었다. 전부 외웠다고 생각해도 놓칠만한 게 분명 생긴다. 나는 강의를 시험 보러 가기 이틀 전 정도에 다시 들어보는 걸 추천한다. + 유튜브 “라메드랩스“ - 단어 풀이해 주는 강의도 꼭 들어보기. 강추. 강추.
 

3. 실라버스 한국어 해석이 그렇게 잘 되어 있지 않다. 영어 버전도 다운로드하여서 번역해서 같이 읽어보는 것을 추천한다. 나의 경우엔 테스트 피라미드 부분을 영어 번역본을 통해 다시 읽어봐서 이해가 됐다. 다른 부분들도 꽤 도움이 되니 화면을 이중으로 켜두고 같이 읽어보는 것을 추천한다. 나는 캡쳐해서 내 한국어 실라버스 옆에 붙여두고 공부할 때 같이 읽었다.
 

4. 외워야 되는 건 그냥 암기가 답이다. 자기 전에 외우고 일어나서 암기했던 부분을 다시 적어보면서 공부를 시작했다. 외워서 맞출 수 있는 문제를 틀리기엔 시험이 너무 어렵다. 그렇게 틀리기엔 아까워서 두고두고 후회할 것이다. 외워서 맞출 수 있는 문제는 꼭 다 외워가자. (테스트 활동과 업무, 테스트 웨어는 무조건 다 외우는 게 낫다. 그 외에 테스트 레벨, 테스팅 사분면, 동적테스트와 정적테스트 차이, 테스팅 지원도구 등등)
 

5. 아이패드나 갤럭시탭이나 용지로 직접 공부하는 것이 아니라면 레이어를 적극 활용해 보자. 한번 정리해서 공부하고 난 뒤 다시 새로운 레이어에서 아무 필기가 안되어 있는 상태로 다시 공부를 하면 내가 놓치고 못 본 부분도 다시 볼 수 있다. 두 개 정도 레이어를 나눠서 공부했다면 두 개의 화면을 같이 켜두고 안 보고 넘긴 부분이 있는지 다시 체크해서 읽어보는 것을 추천한다. (나의 경우는 총 3개의 레이어를 사용했다. 1 레이어 - 번역본 + 용어 설명 + 키워드, 2 레이어 - 꼭 외워야 되는 내용, 3 레이어 - 샘플 문제에서 틀린 부분 정리해서 다시 키워드 정리)
시험은 정말 실라버스의 모든 내용에서 나오는 것 같다. 한 문장이라도 대충 보고 넘어간 그 부분에서 문제가 나온다. 나도 알고 싶지 않았다가 두 번째 시험 보고 나서 깨달았다.
 

6. 문배를 풀어보는 것을 추천한다. 문배의 문제도 문제지만 해설에서 도움을 꽤 받았다. (특히 4장 테스팅문제 푸는 부분) 실라버스를 어느 정도 공부를 했다면 문배를 풀어보자. 문배에서 내용이 너무 예전 것이라 헷갈릴 수 있지만 공부를 했다면 시험범위가 아닌 부분을 바로 캐치할 수 있다. 넘길 부분들은 넘기고 그 외의 부분들을 풀어보면서 해설과 실라버스 내용을 같이 읽어보는 것을 추천한다. 실라버스만 공부해서는 사실 쉽지 않은 것 같다.
 

7. 샘플 문제 안에서 틀린 문제들은 자신이 문제 안에서 나온 단어들을 제대로 이해하였는지 확인하는 것이 중요하다. 구글을 통해 이해가 안 된 단어들을 자세하게 알아보는 게 이해하는데 도움이 된다. + 답만 찾지 말고 답이 아닌 애들은 왜 틀렸는지, 뭘 설명하는지 적어두고 풀이할 때 맞게 썼는지 확인해 보면 1문제로 여러 공부를 할 수 있다. 이것도 강추.
 



사실 나는 정말 실라버스를 거의 다 외우고 갔다고 생각했는데 문제를 받고 머리가 새하얘졌다. 모두 시간도 적당히 봐서 넘길 문제는 넘기고 풀 수 있는 문제 먼저 푸는 것을 추천한다. 혹시나 시간을 들여서 이미 풀려고 했다면 아닌 답들이라도 확실하게 지워두고 넘어가자. 나중엔 시간이 촉박해서 다시 풀고 싶어도 못 푸는 상황이 올 수 있다. 그럴 땐 어쩔 수 없이 찍는다 하더라도 적어도 맞출 확률은 높여둬야 하니까!

모두 화이팅! 이제 취업의 문을 두드리러 가봐야지!

++ 그리고 지난번 시험이 끝나고 궁금했던 나가는 순서에 대한 의문점이 풀렸다. 감독관님께서 싸인을 해주시고 나가도 된다는 말을 해주신다. 시험이 완전히 끝나기 전에 끝마친다면 감독관님이 싸인을 해주시면서 이야기해 주신다. 나도 두 번째 시험에서는 나가도 된다는 허락을 받고 먼저 일어났다. 시험 시간이 완전히 끝나면 나가는 게 보류되는 것 같았다. 내가 나가려고 일어났을 때 다시 한번 다른 감독관님께서 오셔서 허락받았는지 확인하셨었다.

728x90
반응형

나는 꿈들을 많이 꾼다. 영화같이 긴 내용의 꿈들도 꾸기 때문에 꿈을 꾼 날에는 기분이 극명하게 갈린다.
흥미진진한 내용의 꿈들을 꾸면 꿈에서 깨기 싫어진다. 도망을 치거나 무언가 사건의 목격자가 된 꿈들에서는 깨고는 싶지만 결말까지 보고 싶은 아이러니한 기분을 느끼게 된다. 달콤한 상상의 꿈들에선 꾸고 난 후 나의 일상에 영향을 미칠 정도로 기분 좋은 스타트가 되지만 불쾌할 정도로 찝찝한 꿈들에서는 하루 종일 꿈에 대해 되새겨 보다가 그날 하루가 끝나기도 한다.
 
내 꿈에서는 시점들이 계속해서 변화된다. 지난번 어떤 꿈에서는 피해자, 가해자, 목격자의 모두의 시점을 볼 수 있었다.
역시나 오늘의 꿈에서도 나의 시점은 계속해서 변했다. 
 
오늘 꾼 꿈은 약 40분 정도의 짧은 시간 동안 꾼 꿈이었다. 꽤 오랫동안 기억에 남길래 적어본다.


 
< 꿈속의 시점 변화 > 
1. 의사들.
2. 실험체로 추정되는 두 분류로 나뉜 사람들.
3. 특이한 형태의 괴물들.
4.곳곳에 설치된 CCTV
 
< 꿈속에서의 등장인물 형태 >
1. 의사 - 공통 오브젝트 : 의사 가운. 가운 외에는 캐주얼하게 입거나, 와이셔츠를 입고 넥타이를 매고 있는 사람도 있었다.
2. 사람들 - 병원 환자 복을 입고 있기도 했고, 일반 복을 입고 있는 사람들도 있었다. 환자복이 70%, 일반복이 30%
3. 괴물 - 검은 봉지를 얼굴에 쓰고 있어서 눈이 보이지 않았다. 얼굴, 몸 전부 살이 있을 곳엔 검은 물감이 묻어 있다. 입고 있는 것도 밭에서 사용한 비닐 멀칭처럼 모래가 묻은 낡고 찢어진 검은 비닐이었다. 공사장 같은 데서 보이는 기다란 검은 비닐들로 온몸이 감아져 있었다.
4. 스프레이 - 우리가 흔히 알고 있는 세모난 삼각원뿔형태의 스프레이. 플라스틱으로 되어 있으며 투명한 것도 있고 뿌연 파란색도 있지만 내부의 액체는 보인다.
5. 주사기 - 얇은 주사기. 의사들은 주사기를 들고 있을 때 모두 흰색 손장갑을 꼈다.
 
< 환경 >
1. 밤이였다. 
2. 건물 위층에서 아래를 보면 나무들이 빼곡하게 많아 온통 검은 숲들로 둘러쌓져 있다. 
3. 달의 빛이 은은하게 건물을 비추고 있다.
4. 건물의 내부에 어느 벽들은 힘을 주면 슬라임처럼 벽 안으로 들어갈 수 있는 구조로 되어있다.
5. 건물 내부 : 통유리로 되어 있는 창문이 깨져있으며 꽤 오랫동안 방치되어 이끼가 끼어있는 층들이 있는가 하면 어제까지도 사용했던 것 같이 모든 오브젝트들이 깔끔하게 들어가 있는 층들도 있다. 
6. 의사들이 시체를 옮기고 있던 곳엔 화장터처럼 네모난 직육면체 공간이 길게 뚫려 있는 벽이 있었다.
7. 복도 곳곳에 나무로 만들어진 파티션들이 벽 쪽에 세워져 있었다.
8. 이불이 있는 방의 이불들과 위에 달려있는 스프링 클러는 이전에도 사용했던 것 같이 사용 흔적이 남아있었다.
9. 의사들이 연구하는 연구실에는 파란색 불이 희미하게 있고 중간중간 책상에 LED 조명이 켜져 있었다.
10. 형광물질이 사람의 몸에 닿을 때는 형광색 물감이 물풍선에서 터지듯 묻은 것처럼 보였다.  
 
커다란 건물 두개가 쌍둥이처럼 붙어있었으며 가운데 연결된 통로로 넘어갈 수 있게 생긴 구조였다. 통유리의 창문을 통해 반대쪽 건물에서의 움직임을 전체적으로 볼 수 있었다.
나는 실험체에 포함되는 인물 1이었는데, 시간 안에 살아남은 후, 다시 불특정한 쉬는 시간을 가지고 반복하는 형태였던 것 같다. 
 
내가 있는 쪽 건물에서는 눈이 안보이는 괴물들이 스프레이를 들고 뿌리면서 사람들을 찾고 있었다. 
스프레이 속에는 형광 물질이 들어있는지 사람한테 뿌리면 형광이 반응하였다. 벽이나 물건들에 뿌렸을 때는 형광이 발현되지 않았던 걸로 보아 아무래도 체온이라거나 사람에게만 있는 반응에 반응하는 것 같았다. 형광 물질이 몸에 묻은 사람들은 스프레이 뿌리는 괴물 뒤에 있던 괴물들에게 끌려갔다. 형광물질이 몸에 묻어 있어도 괴물들의 눈을 피해 도망을 친다면, 불이 꺼질 때까지 쉴 수 있는 이불이 있는 방에서 옷을 갈아입거나 묻은 옷을 벗고 피할 수도 있었다. 하지만 워낙 건물 내부의 빛들이 없었기 때문에 형광의 색들은 눈에 너무 잘 띄었다. 사람들 또한 그들과 같이 있는 것을 극도로 꺼렸다. 이유는 당연하게도 형광물질이 닿아 몸에 묻거나 같이 있다가 표적이 되어 스프레이에 맞을 확률이 커지기 때문이었다. 극한의 이기심들이 눈앞에서 일어났지만 딱히 서로를 도와줄 수 있는 방법은 없었다.
 
일정 시간동안 괴물들을 피해 스프레이를 맞지 않고 피하면서 건물에서 불이 켜져 있는 이불들이 펼쳐진 방으로 들어가면 불이 켜져 있을 때까지 잠시 동안은 쉴 수 있었다. 물론 불이 꺼지면 다시 도망쳐야 되는 상황이었다. 
건물 내부는 거의 모든 곳이 불이 꺼져 있기 때문에 불이 켜져 있는 공간은 반대쪽 건물에서 알 수 있었다. 그래서 우리는 팀을 나눠 반대쪽 건물과, 본 건물에서 서로에게 불빛이 있는 공간의 위치를 알려주었다.
 
도망치던 사이에 반대쪽 건물에서 의사들이 사람을 죽이고 유기를 하는 모습을 보았다. 
목이 잘린 시신의 목에는 붕대로 돌돌 말아져 있었다. 의사들이 시신의 팔다리를 들어 어딘가로 이동하던 중 도망치던 또 다른 사람들과 마주쳐서 시체를 바닥에 두고 그 사람들을 쫓아가는 모습을 보았다. 
거기 있던 의사들은 괴물들이 데려간 사람들을 데리고 임상 시험을 하고 있는 중이었던 것 같다.
 
반대쪽 건물에는 두 종류의 의사들이 있었다.
임상시험을 진행하는 의사와 몰래 형광 물질에 대한 백신을 만들고 있는 의사.
 
CCTV의 시점으로 보게 된 기억은 이렇다.
< 복도의 CCTV - 소리 녹음 X >
엘리베이터의 문안으로 숨어 들어가 숨죽이고 있던 사람들에게 의사들이 무언가를 이야기하면서 다급하게 접근했다. 이후 파란 물질이 들어가 있는 주사기를 그들의 팔에 주사하는 것을 보았다.
< 연구실의 CCTV - 소리 녹음 O >
의사들끼리도 의견이 다른지 주사를 맞겠다고 싸우는 의사들이 생겨났다. 어떤 의사는 자신한테도 주사를 놔달라며 옆의 의사의 멱살을 잡으면서 소리를 지르고 있었는데 뒤에 있던 다른 의사가 와서 파란색 약물이 들어있는 주사기로 주사를 놓는 척하다가 주황색의 약물이 들어있는 주사기로 바꿔 치기 해서 주사하는 것을 보았다.
어떤 영향을 받는 건지는 알아내지 못했지만 투약할 양이 인원수대로 있었던 것은 아니었던 것 같다. 
 
++
< 불이 켜져있는 이불이 펼쳐진 방 >
여기에는 이불들이 수련회에 갔을 때처럼 바닥 전체에 침구가 깔려있다. 침대가 있거나, 바닥에 이불이 깔려 있거나, 또 이불이 작거나 크거나, 침낭이거나 했는데, 방을 찾아 간신히 살아 들어온 후에도 내부에서 다시 자리를 차지하기 위해 싸웠어야 됐다. 어떤 사람들은 자신의 가족이 5명이라 5명의 자리를 맡아 둔 거라면서 이불을 움켜쥐는 사람이 있는 가 하면, 4명 정도의 인원이 들어갈 수 있는 대형 침대에서 혼자만 쓸 것이라면서 소리 지르는 아저씨도 볼 수 있었다. 문 앞에서는 사람들이 자리를 찾기 위해 이동을 했고, 문 바로 앞에 있는 이불들에서는 쟁탈전이 펼쳐지기도 했다. 아무래도 문 앞자리인 만큼 사람들 사이에서 뺏고 뺏는 싸움이 일어났었다. 그 사이에서도 어떤 사람들은 자신들의 자리옆에 사람 한 명 더 들어올 수 있다면서 자리를 뺏기고 돌아다니고 있는 사람들을 불러 데려가기도 했다.
 
< 이불이 켜져 있는 방의 규칙 >
1. 괴물들이 방 근처를 걸어갈 때 방의 불이 꺼지면서 방에 있는 모두가 이불속으로 들어가야 됐다.
2. 만약 누군가가 이불 밖을 나와있다면 천장에 있던 스프링클러가 작동하면서 형광물질이 비처럼 나오게 된다.
3. 그 사이 괴물들은 형광물질을 맞은 사람을 데려가고 형광물질을 피해 이불속에 있던 사람들은 괴물이 지나간 후 방에서 나와 다시 도망가야 됐다.
4. 스프링 쿨러가 작동된 방은 더 이상 불이 켜지지 않게 되며 괴물에게서 안전하지 않게 된다.
5. 만약 모두가 이불속에 있어서 스프링 클러가 작동되지 않았다면 불이 다시 켜지며 안전한 공간으로 남는다. 
 


결과를 못보고 꿈에서 깼기 때문에 그 후로 어떻게 됐는지는 모르겠지만 생각보다 꿈꾸고 난 후 시간이 꽤 지나고도 기억되기에 적어둔다.
지난번엔 파만 먹는 게스트 하우스에 가서 아침밥으로 익은 파를 통째로 썰어 먹고 있었던 게 오래 기억됐었는데 이번 꿈으로 갱신한 것 같다. 아, 참고로 다른 손님들은 파로 샌드위치를 해 먹거나, 파를 갈아 우유쉐이크를 해 먹거나, 파를 먹는 척하고 그릇에 두고 신문만 읽거나 하고 있었다.  

728x90
반응형

+ Recent posts