본문 바로가기

Development/Tetris

Python - 테트리스(Tetris) 만들기 (12) - Main Screen

시작하기 전에...

테트리스(Tetris) 만들기 (11) 에서 Diagram을 그렸던 것을 다시 확인해 봅시다.

Main Screen에 접속해서, Setting 값 설정, Tetris Application실행, 종료 등을 담은 Digram이다.

 

보시면 Main Application에서 실행할 수 있는 기능이 3가지가 있습니다.

 

1. Setting Screen

2. Tetris Application

3. Exit

 

이렇게 3가지의 Status가 존재하게 됩니다. Setting screen은 같은 Main Application에 있지만, 세부적으로 들어가면 Main screen과는 다른 화면이 나오므로 화면 기능으로 봅시다.

 

이번 시간에는 Main Screen을 그려보도록 하겠습니다.

 

Main Screen 그리기

Main Screen은 어떻게 생겼을까요? 위의 Diagram을 보면 어느정도의 윤곽이 드러납니다.

대충 요런 그림이지 않을까...

 

이 그림만으로는 사실 어떻게 그려야 할 지 제대로 된 기준을 잡기 어렵습니다. 정렬의 규칙을 정해줘야 합니다.

정렬의 규칙을 담은 그림을 또 그려보았습니다.

이렇게 되면 h, w가 나중에 변화되더라도 문제가 생기지 않는다.

이것대로 Main Application을 구현해보도록 하겠습니다. 그러기 위해서는 먼저 글씨를 pygame을 통해 그릴 수 있어야 합니다.

 


글씨를 Display에 그려보기

글씨를 display에 그려보는 방법을 알아보기 위해, pygame에서 font를 검색해 보았습니다.

먼저 font.init()이 눈에 띕니다. 

font를 사용하려면 font.init()이 필요하다고 한다. 그리고 font.init()은 pygame.init()에 있다고 한다.

 

좀 더 검색하니 Font를 생성할 수 있는 좋은 method를 찾았습니다. 바로 Font Sysfont입니다.

system의 font를 활용해서 Font Object를 생성

 

File로부터 Font를 생성

System Font는 제 컴퓨터에 있는 Font를 활용해서 Font Object를 생성하고, Font는 file로 지정해서 Font Object를 생성하는 것입니다. 저는 특별한 Font를 사용할 계획이 없으므로, Font Style을 None으로 설정할 수 있는 pg.font.Font()를 사용해 보겠습니다

 

그리고 실제 text를 rendering 해야 하므로 관련 method도 찾아줍니다. 그림의 맨 밑에 render method를 찾을 수 있습니다.

draw text on a new Surface

 

 pygame.font.Font.render에 들어가면 다음과 같은 정보를 확인할 수 있습니다.

Pygame에서 text를 rendering하는 방법을 설명하는 아주 귀중한 자료

pygame에서 text를 rendering하는 방식을 아주 자세하게 알려주고 있습니다. 어떤 방식인지 해석해 보겠습니다.

 

render() method는 지정된 텍스트가 렌더링된 새 Surface를 생성합니다.
pygame.font는 기존 Surface에 text를 직접 그리는 방법을 제공하지 않습니다. 대신 Font.render()를 사용하여 텍스트의 이미지(Surface)를 생성한 다음에 다른 이미지에 붙여야(blit) 합니다.

 

새로 생성한 Text Surface를 기존 Surface에 blit을 해야 한다는 아주 귀중한 정보를 얻었습니다.

이 정보를 토대로 Main Screen에 글씨를 rendering 해보겠습니다.

 

class MainDisplay:
    def __init__(self, width, height):
        self.width, self.height = width, height
        self.display_surface = None
        self.title_font = pg.font.Font(None, 48)
        self.contents_font = pg.font.Font(None, 24)

    def init(self):
        self.display_surface = pg.display.set_mode((self.width, self.height))

    def _get_text_surface(self):
        text_surface = self.title_font.render("test context", antialias=True, color=pg.Color(127, 127, 127))
        return text_surface

    def update(self):
        self.display_surface.fill((0, 0, 0))
        text_surface = self._get_text_surface()
        self.display_surface.blit(text_surface, dest=(0, 0))
        pg.display.flip()

이렇게 작성하고 실행하면...?

 

에러가 뜹니다...

 

실행아 안된 모습을 볼 수 있습니다. 에러 메세지는 render() takes no keyword arguments라고 합니다.

이는 function(antialias=True, color=pg.Color()) 이런 식으로 적지 말고, function(True, pg.Color()) 처럼 value만 적어서 사용하라는 뜻입니다.

* 해당 에러에 대한 설명이 궁금하신 분들은 링크를 클릭해보세요

 

에러를 해결하고 다시 실행해 보겠습니다.

출력이 잘 된다.

 

이제는 출력이 잘 된 모습을 확인했으니, 화면 설계대로 다시 작성해 보도록 하겠습니다. 우선 화면 설계대로 작성하려면 Center Align을 해야 합니다.

x로 Center Align을 하려면, Dest값을 정확하게 알아야 하는데, 계산을 위해 필요한 값이 2개가 있습니다. 바로 w값과 font size x 값입니다.

Dest_x를 구하기 위해 필요한 값을 구하는 식을 그림으로 표현

 

이 값은 한번 지정되면 변하지 않을 값입니다. 그러니 init에서 실행하도록 합시다.

class MainDisplay:
    BACKGROUND_COLOR = (255, 255, 255)
    TITLE_COLOR = (0, 0, 0)
    CONTENTS_COLOR = (127, 127, 127)
    TITLE = "Tetris"
    START = "start"
    SETTING = "setting"
    QUIT = "quit"

    def __init__(self, width, height):
        self.width, self.height = width, height
        self.display_surface = None
        self.title_font = pg.font.Font(None, 48)
        self.contents_font = pg.font.Font(None, 24)

        ## text setting
        self.text_title_surface = self.title_font.render(self.TITLE, True, self.TITLE_COLOR)
        text_title_w, _ = self.title_font.size(self.TITLE)
        self.text_title_x, self.text_title_y = (width - text_title_w)/2, 0.15 * height

        self.text_start_surface = self.contents_font.render(self.START, True, self.CONTENTS_COLOR)
        text_start_w, _ = self.contents_font.size(self.START)
        self.text_start_x, self.text_start_y = (width - text_start_w)/2, 0.5 * height

        self.text_setting_surface = self.contents_font.render(self.SETTING, True, self.CONTENTS_COLOR)
        text_setting_w, _ = self.contents_font.size(self.SETTING)
        self.text_setting_x, self.text_setting_y = (width - text_setting_w)/2, 0.65 * height

        self.text_quit_surface = self.contents_font.render(self.QUIT, True, self.CONTENTS_COLOR)
        text_quit_w, _ = self.contents_font.size(self.QUIT)
        self.text_quit_x, self.text_quit_y = (width - text_quit_w)/2, 0.8 * height
        
    def init(self):
        self.display_surface = pg.display.set_mode((self.width, self.height))

    def update(self):
        self.display_surface.fill(self.BACKGROUND_COLOR)
        self.display_surface.blit(self.text_title_surface, dest=(self.text_title_x, self.text_title_y))
        self.display_surface.blit(self.text_start_surface, dest=(self.text_start_x, self.text_start_y))
        self.display_surface.blit(self.text_setting_surface, dest=(self.text_setting_x, self.text_setting_y))
        self.display_surface.blit(self.text_quit_surface, dest=(self.text_quit_x, self.text_quit_y))
        pg.display.flip()

 

 

예시 그림만큼 이쁘게 나오지는 않았지만 비슷하게 나왔다.

 

설명하려 했던 글씨 크기보다는 좀 작게 출력되었습니다. 지금 상태의 코드에서 font의 크기만 조정해 보도록 합시다.

    def __init__(self, width, height):
        self.width, self.height = width, height
        self.display_surface = None
        self.title_font = pg.font.Font(None, 56)
        self.contents_font = pg.font.Font(None, 32)

 

Font Size만 바꿔도 적용이 잘 된다.

아무래도 예시 그림에서의 여백 0.25가 너무 큰 듯 합니다. 위의 공백을 0.2로 올리고, 아래 공백 0.25도 0.2로 맞춰 주도록 합시다. 그리고 선택 메뉴의 Font도 뭔가 마음에 드는 것으로 사용하고 싶은 마음이 생겼습니다. font는 "calibri"로 바꿔주도록 합시다.

원래 그림상의 여백도 0.2, 0.2가 맞는 듯 한데 왜 그랬을까요...

 

class MainDisplay:
    BACKGROUND_COLOR = (255, 255, 255)
    TITLE_COLOR = (0, 0, 0)
    CONTENTS_COLOR = (127, 127, 127)
    TITLE = "Tetris"
    START = "Start"
    SETTING = "Setting"
    QUIT = "Quit"

    def __init__(self, width, height):
        self.width, self.height = width, height
        self.display_surface = None
        self.title_font = pg.font.SysFont("calibri", 56, bold=True)
        self.contents_font = pg.font.SysFont("calibri", 32)

        ## text setting
        self.text_title_surface = self.title_font.render(self.TITLE, True, self.TITLE_COLOR)
        text_title_w, _ = self.title_font.size(self.TITLE)
        self.text_title_x, self.text_title_y = (width - text_title_w)/2, 0.2 * height

        self.text_start_surface = self.contents_font.render(self.START, True, self.CONTENTS_COLOR)
        text_start_w, _ = self.contents_font.size(self.START)
        self.text_start_x, self.text_start_y = (width - text_start_w)/2, 0.5 * height

        self.text_setting_surface = self.contents_font.render(self.SETTING, True, self.CONTENTS_COLOR)
        text_setting_w, _ = self.contents_font.size(self.SETTING)
        self.text_setting_x, self.text_setting_y = (width - text_setting_w)/2, 0.65 * height

        self.text_quit_surface = self.contents_font.render(self.QUIT, True, self.CONTENTS_COLOR)
        text_quit_w, _ = self.contents_font.size(self.QUIT)
        self.text_quit_x, self.text_quit_y = (width - text_quit_w)/2, 0.8 * height

 

 

마음에 드는 수준의 예상한 대로의 화면이 출력되었다.

 

이번 시간에는 여기까지 입니다. 다음에는 Main Screen에 Event를 넣고 Setting Screen을 띄워 보도록 하겠습니다.

 

 

 


* reference

https://docs.python.org/3/library/stdtypes.html?highlight=dict%20get#dict.get 

https://www.pygame.org/docs/ref/font.html