# Table of Contents - [게임제작하며 프로젝트 기반으로 파이썬 배우기 (순한맛) | 파이썬-파이게임 제로 (순한맛)](#-) - [무료제공 중인 코딩교육용 온라인 북들 | 책 소개](#-) - [1. 파이게임 제로(pygame zero) 라이브러리 | 파이썬-파이게임 제로 (순한맛)](#1-pygame-zero-) - [2. 개발환경 구축 | 파이썬-파이게임 제로 (순한맛)](#2-) - [3. Hello World 프로그램에서 시작하자 | 파이썬-파이게임 제로 (순한맛)](#3-hello-world-) - [4. 게임 루프의 이해 | 파이썬-파이게임 제로 (순한맛)](#4-) - [5.2 화면에 플래피버드(오브젝트) 나타내기와 움직이기 | 파이썬-파이게임 제로 (순한맛)](#5-2-) - [5.3 화면에 플래피버드(오브젝트)의 자연스러운 움직임 만들기 | 파이썬-파이게임 제로 (순한맛)](#5-3-) - [5.1 화면에 배경 이미지(오브젝트) 나타내기 | 파이썬-파이게임 제로 (순한맛)](#5-1-) - [5.8 플래피버드 게임 추가기능 구현하기 (플래피버드 애니메이션) | 파이썬-파이게임 제로 (순한맛)](#5-8-) - [5.4 화면에 파이프(오브젝트) 나타내기와 움직이기 | 파이썬-파이게임 제로 (순한맛)](#5-4-) - [5.6 플래피버드 게임 추가기능 구현하기 (점수기능) | 파이썬-파이게임 제로 (순한맛)](#5-6-) - [5.7 플래피버드 게임 추가기능 구현하기 (파이프 위치 랜덤화) | 파이썬-파이게임 제로 (순한맛)](#5-7-) - [7.2 배우들의 움직임과 총알 공격 구현하기 | 파이썬-파이게임 제로 (순한맛)](#7-2-) - [8.4 객체지향 개발이론 (사용자 정의 객체 만들기, 상속) | 파이썬-파이게임 제로 (순한맛)](#8-4-) - [6.4 충돌검사시 고려할 것들 | 파이썬-파이게임 제로 (순한맛)](#6-4-) - [9.7 객체지향으로 개발하기 4 | 파이썬-파이게임 제로 (순한맛)](#9-7-4-) - [6.3 공의 반사와 블록격파 구현하기 | 파이썬-파이게임 제로 (순한맛)](#6-3-) - [6.1 게임무대에 배경과 배우 등장시키기 | 파이썬-파이게임 제로 (순한맛)](#6-1-) - [10.1 네트워크 게임방식의 이해와 라이브러리 설치 | 파이썬-파이게임 제로 (순한맛)](#10-1-) - [7.4 그 밖에 도전과제 | 파이썬-파이게임 제로 (순한맛)](#7-4-) - [5.5 플래피버드와 파이프의 충돌 구현하기 | 파이썬-파이게임 제로 (순한맛)](#5-5-) - [8.2 절차지향형으로 개발하기 2 | 파이썬-파이게임 제로 (순한맛)](#8-2-2-) - [8.1 절차지향으로 개발하기 1 | 파이썬-파이게임 제로 (순한맛)](#8-1-1-) - [6.2 배우들의 움직임 구현하기 | 파이썬-파이게임 제로 (순한맛)](#6-2-) - [8.3 객체지향 개발이론 (객제지향 디자인이란) | 파이썬-파이게임 제로 (순한맛)](#8-3-) - [부록 | 파이썬-파이게임 제로 (순한맛)](#-) - [10. 퐁(Pong)을 네트워크 게임으로 만들기 | 파이썬-파이게임 제로 (순한맛)](#10-pong-) - [6.5 (보너스) 오리지널 게임처럼 만들어 보기 | 파이썬-파이게임 제로 (순한맛)](#6-5-) - [8.8 (보너스) 파이게임제로 예제버전 2 | 파이썬-파이게임 제로 (순한맛)](#8-8-2-) - [8.7 (보너스) 파이게임제로 예제버전 1 | 파이썬-파이게임 제로 (순한맛)](#8-7-1-) - [9.3 객체지향으로 개발하기 1 | 파이썬-파이게임 제로 (순한맛)](#9-3-1-) - [8.5 객체지향으로 개발하기 1 | 파이썬-파이게임 제로 (순한맛)](#8-5-1-) - [9.1 절차지향형으로 개발하기 1 | 파이썬-파이게임 제로 (순한맛)](#9-1-1-) - [10.2 릴레이 서버 구동과 클라이언트 접속 | 파이썬-파이게임 제로 (순한맛)](#10-2-) - [9.5 객체지향으로 개발하기 2 | 파이썬-파이게임 제로 (순한맛)](#9-5-2-) - [9.4 객체지향 개발이론 (인터페이스) | 파이썬-파이게임 제로 (순한맛)](#9-4-) - [7.3 충돌처리 및 기타정보(점수 및 게임종료) 표기 | 파이썬-파이게임 제로 (순한맛)](#7-3-) - [5. 플래피 버드(Flappy bird) 게임 만들기 | 파이썬-파이게임 제로 (순한맛)](#5-flappy-bird-) - [9.6 객체지향으로 개발하기 3 | 파이썬-파이게임 제로 (순한맛)](#9-6-3-) - [6. 블록격파(Breakout) 게임만들기 | 파이썬-파이게임 제로 (순한맛)](#6-breakout-) - [뮤 에디터 단축키 모음 | 파이썬-파이게임 제로 (순한맛)](#-) - [10.4 네트워크 게임으로 만들기 2 | 파이썬-파이게임 제로 (순한맛)](#10-4-2-) - [7. 트윈비(TwinBee) 슈팅게임 만들기 | 파이썬-파이게임 제로 (순한맛)](#7-twinbee-) - [7.1 스크롤 배경객체 만들기, 배경음, 배우(적, 주인공) 등장시키기 | 파이썬-파이게임 제로 (순한맛)](#7-1-) - [8. 퐁(Pong) 게임 만들기 | 파이썬-파이게임 제로 (순한맛)](#8-pong-) - [9. 배틀 시티(Battle city) 게임 만들기 | 파이썬-파이게임 제로 (순한맛)](#9-battle-city-) - [8.6 객체지향으로 개발하기 2 | 파이썬-파이게임 제로 (순한맛)](#8-6-2-) - [9.2 절차지향형으로 개발하기 2 | 파이썬-파이게임 제로 (순한맛)](#9-2-2-) - [10.3 네트워크 게임으로 만들기 1 | 파이썬-파이게임 제로 (순한맛)](#10-3-1-) - [게임을 단 한 개의 실행파일로 패키징 하기 | 파이썬-파이게임 제로 (순한맛)](#-) - [게임제작하며 프로젝트 기반으로 파이썬 배우기 (매운맛) | 파이썬-파이게임](#-) - [엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#-7-) - [엔트리로 파이썬 기초 입문하기 | 엔트리-파이썬](#-) - [엔트리로 실전 아두이노 배우기 | 엔트리-아두이노](#-) - [3.2 입/출력 (Input/Output) | 엔트리-파이썬](#3-2-input-output-) - [4.1 순차/병렬처리 (Serial/Parallel) | 엔트리-파이썬](#4-1-serial-parallel-) - [3.3 변수 (Variable) | 엔트리-파이썬](#3-3-variable-) - [3.6 리스트 (List) | 엔트리-파이썬](#3-6-list-) - [4.3 이벤트 주도형 (Event-Driven) | 엔트리-파이썬](#4-3-event-driven-) - [3.7 무작위 수(Random) | 엔트리-파이썬](#3-7-random-) - [5.5 팔 벌려 뛰기 횟수를 세어봐요 - 변수 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-5-7-) - [4.4 객체 지향형 (Object-Oriented) | 엔트리-파이썬](#4-4-object-oriented-) - [4.2 절차 지향형 (Procedural) | 엔트리-파이썬](#4-2-procedural-) - [5.2 꽃잎 한 장으로 꽃송이를 만들어요 - 반복 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-2-7-) - [3. Hello World 프로그램에서 시작하자 | 파이썬-파이게임](#3-hello-world-) - [1. 개발환경 구축 | 파이썬-파이게임](#1-) - [2. 엔트리-파이썬의 시작 | 엔트리-파이썬](#2-) - [1. 파이게임 제로(pygame zero) 라이브러리 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#1-pygame-zero-7-) - [2. 파이게임(pygame) 라이브러리 | 파이썬-파이게임](#2-pygame-) - [4. 파이게임의 기본구조 익히기 | 파이썬-파이게임](#4-) - [3. Hello World 프로그램에서 시작하자 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#3-hello-world-7-) - [4. 엔트리-파이썬으로 프로그래밍의 핵심 개념을 익히자 | 엔트리-파이썬](#4-) - [1. 엔트리 vs 엔트리-파이썬 | 엔트리-파이썬](#1-vs-) - [5. 엔트리 초급책을 코딩하기 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-7-) - [3. 엔트리-파이썬으로 파이썬 언어의 기본문법을 익히자 | 엔트리-파이썬](#3-) - [2. 개발환경 구축 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#2-7-) - [4. 게임 루프의 이해 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#4-7-) - [5. 부록 | 엔트리-파이썬](#5-) - [부록 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#-7-) - [1. 엔트리란 | 엔트리-아두이노](#1-) - [3. 아두이노 블록코딩의 시작 | 엔트리-아두이노](#3-) - [4. 아두이노 보드 기본 | 엔트리-아두이노](#4-) - [7. 부록 | 엔트리-아두이노](#7-) - [6. 마무리 | 엔트리-아두이노](#6-) - [5. 실전 아두이노 블록코딩 | 엔트리-아두이노](#5-) - [2. 아두이노란 | 엔트리-아두이노](#2-) - [3.5 반복 (Loop) | 엔트리-파이썬](#3-5-loop-) - [3.8 함수 (Function) | 엔트리-파이썬](#3-8-function-) - [3.1 Hello World 예제코드의 이해 | 엔트리-파이썬](#3-1-hello-world-) - [3.4 조건 (Condition) | 엔트리-파이썬](#3-4-condition-) - [5.7 두더지 게임을 만들어요 - 종합 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-7-7-) - [5.6 암호를 입력해 보물상자를 열어요 - 입출력 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-6-7-) - [5.1.2 멍멍이와 야옹이가 인사해요2 - 순차 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-1-2-2-7-) - [5.1.3 멍멍이와 야옹이가 인사해요3 - 순차 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-1-3-3-7-) - [5.3 로봇 청소기가 장애물을 만난다면? - 조건 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-3-7-) - [5.4 나만의 그림판을 만들어요 - 이벤트 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-4-7-) - [5.1.1 멍멍이와 야옹이가 인사해요1 - 순차 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#5-1-1-1-7-) - [엔트리-파이썬 단축키 모음 | 엔트리-파이썬](#-) - [5.1 디지털 출력 코딩 | 엔트리-아두이노](#5-1-) - [5.2 디지털 입력 코딩 | 엔트리-아두이노](#5-2-) - [4.1 아두이노 보드의 종류 | 엔트리-아두이노](#4-1-) - [5.4 아날로그 입력 코딩 | 엔트리-아두이노](#5-4-) - [3.1 아두이노 블록코딩의 한계 | 엔트리-아두이노](#3-1-) - [3.4 Hello World 코딩 | 엔트리-아두이노](#3-4-hello-world-) - [3.6 블록코딩을 텍스트 코딩으로 변환하기 | 엔트리-아두이노](#3-6-) - [6.2 PIR 센서모듈 설정 방법 | 엔트리-아두이노](#6-2-pir-) - [6.1 저항의 종류와 색 띠 저항값 읽는 법 | 엔트리-아두이노](#6-1-) - [뮤 에디터 단축키 모음 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#-7-) - [게임을 단 한 개의 실행파일로 패키징 하기 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기](#-7-) - [4.2 아두이노 입출력 핀 구성 | 엔트리-아두이노](#4-2-) - [5.3 아날로그 출력 코딩 | 엔트리-아두이노](#5-3-) - [3.5 코드 업로드 하기 | 엔트리-아두이노](#3-5-) - [3.3 디지털 신호와 아날로그 신호 | 엔트리-아두이노](#3-3-) - [3.2 아두이노 보드와 연결하기 | 엔트리-아두이노](#3-2-) --- # 게임제작하며 프로젝트 기반으로 파이썬 배우기 (순한맛) | 파이썬-파이게임 제로 (순한맛) 파이썬 프로그래밍 언어는 텍스트 코딩용 언어 중에서 가장 쉬운 언어이면서 동시에 가장 인간 친화적인 언어라고 불린다. 그만큼 배우기가 쉬운 언어라고 알려져 있지만, 세상에 어떤 외국어(컴퓨터와 소통하기 위한 프로그래밍 언어 조차도 외국어 한 종류로 간주할 수 있음)가 아무리 쉽다해도 정말 손쉽게 배워지는게 있었던가. 적어도 필자의 기억엔 없었다. 언어를 배우는 지난하고 많은 인내가 필요한 일에 있어서 다만, 좀 덜 지치면서 배울 수 있는 어느 정도의 학습법은 존재했던 것 같다. 그런 기억을 되살려 필자는 블록코딩에서 어느 정도의 실력을 쌓았고, 본격적으로 텍스트 코딩에 입문하려는 분들을 위한 3번째 책이다. 만약, 당신이 블록코딩까지만 알고 텍스트코딩의 경험이 전혀 없다면, "[엔트리-파이썬](https://jjlee.gitbook.io/entry-python/) ", "[엔트리 기초책 7개 예제 클론하며 파이썬 배우기](https://jjlee.gitbook.io/pygame_zero-entry_basic/) " 두 권의 책을 앞서 먼저 학습하고 오면 이 책의 난이도가 적합할 것 같고, 그러나, 어떤 독자의 경우, 본인은 이미 파이썬 기본 문법정도는 어느 정도 알고 있는 수준은 된다고 생각한다면, 곧바로 이 책에서부터 학습을 시작할 수 있다. 이번 책 역시도 배우다가 금새 지치게 만드는 **기존의 언어문법 위주의 책이 아닌, "프로젝트 기반으로 배우기" 라는 이미 블록 코딩 때에도 수없이 경험해 봤고, 그렇게 성장해 왔던 우리의 기존 성장루트를 그대로 적용한 게임제작을 기반으로 파이썬 언어 배우기라는 방법을 사용하고 있다.** 본 서는 이전 서와 동일하게 파이썬 게임제작 라이브러리인 파이게임(PyGame)을 더 간략화한 파이게임 제로(PyGame Zero) 라이브러리를 활용한 쉬운 접근을 시도한다. 그러나, 라이브러리가 쉽다고 코딩까지 쉽다고는 장담하긴 어렵다. 하지만, 미리부터 걱정말자 본서의 저자가 친절한 안내자로서 여러분의 코딩배움 여정에 함께 동행할테니까 말이다. _참고로 만약 어려분이 이미 파이썬 기초문법을 넘어 심화문법에 지식도 어느 정도는 있다고 생각하다면, 이_ _**순한맛**_ _편말고, 곧바로 파이게임 라이브러리를 직접 사용하는_ [_**매운맛**_arrow-up-right](https://jjlee.gitbook.io/python-pygame) _편(아직 완간 전)을 도전해 보는 것도 가능합니다. 효과적인 학습을 위하여 여러분의 자신의 수준에 맞는 책으로 도전해 볼 것을 추천드립니다._ #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero#undefined) 변경내역: 버전 날짜 내역 v1 2025/4/18 첫 버전 공개 v1.0.1 2026/2/04 오탈자 수정 #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero#jj-comseong-gmail.com) **저자**: JJ ([comseong@gmail.com](https://jjlee.gitbook.io/python-pygame_zero#jj-comseong-gmail.com) ) #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero#undefined-1) 참고 사이트 목록 [https://pygamezero-bird.readthedocs.io/en/latest/index.htmlarrow-up-right](https://pygamezero-bird.readthedocs.io/en/latest/index.html) [https://github.com/StanislavPetrovV/Python-Arkanoid-Breakoutarrow-up-right](https://github.com/StanislavPetrovV/Python-Arkanoid-Breakout) [https://github.com/WokLibCodeClub/breakoutarrow-up-right](https://github.com/WokLibCodeClub/breakout) [https://m.blog.naver.com/elpissoft/120185941500arrow-up-right](https://m.blog.naver.com/elpissoft/120185941500) [https://aposteriori.trinket.io/game-development-with-pygame-zeroarrow-up-right](https://aposteriori.trinket.io/game-development-with-pygame-zero) #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero#https-creativecommons.org-licenses-by-nc-sa-4.0) 저작권: [https://creativecommons.org/licenses/by-nc-sa/4.0/arrow-up-right](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ko) ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2FZOoo6AnroO2vGSvpOKw7%2Fblobs%2FPNCUUPpkZQcjfctXOVU4%2Fimage.png&width=768&dpr=3&quality=100&sign=57dd370d&sv=2) [Next1\. 파이게임 제로(pygame zero) 라이브러리chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pygame_zero_lib) Last updated 6 days ago --- # 무료제공 중인 코딩교육용 온라인 북들 | 책 소개 [hashtag](https://jjlee.gitbook.io/intro#undefined) [**엔트리로 실전 아두이노 배우기 (완간)**arrow-up-right](https://jjlee.gitbook.io/entry-arduino/) -------------------------------------------------------------------------------------------------------------------------------------------- 본 서는 엔트리에 대한 기본 지식은 있으나, 아직 아두이노 C++코딩에는 입문하지 못한, **엔트리 블록코딩으로 아두이노 제어를 통해 피지컬컴퓨팅을 익히고자 하는 분/교육하고자 하는 분**들을 위해 씌여졌습니다. [hashtag](https://jjlee.gitbook.io/intro#undefined-1) [엔트리-파이썬으로 파이썬 기초 입문하기 (완간)](https://jjlee.gitbook.io/entry-python/) -------------------------------------------------------------------------------------------------------------------------------- 본 서는 엔트리의 블록기반 코딩으로 코딩(프로그래밍)을 익힌 이후 파이썬 언어를 사용하는 텍스트기반 코딩으로 넘어가가고자 하는 분들에게 중간 다리 역할을 하는 "엔트리-파이썬" 활용해 **프로그래밍 교육목적의 블록 코딩에서 실전개발을 위한 텍스트 코딩으로의 전환을 꽤하고자 하는 분/ 블록 코딩을 교육한 이후 학습자의 텍스트 코딩으로의 원활한 전환을 돕고자하는 교육자**들을 위해 씌여졌습니다. [hashtag](https://jjlee.gitbook.io/intro#id-7) [엔트리 기초책 7개 예제 클론하며 파이썬 배우기 (완간)](https://jjlee.gitbook.io/pygame_zero-entry_basic/) ----------------------------------------------------------------------------------------------------------------------------------------- [이전 서인 엔트리-파이썬](https://jjlee.gitbook.io/entry-python/) 을 통해 파이썬 생기초 문법을 뗀 사람들 위한 다음 단계의 책입니다.[네이버에서 공개한 엔트리 블록코딩 기초책(원제: 차근차근 따라하며 배우는 엔트리 프로그래밍-기초)의 7개 예제arrow-up-right](https://m.blog.naver.com/erke2000/220640141178) 를 파이썬 코딩으로 클론(clone)하면서 좀더 심화된 파이썬 텍스트 코딩과 문법을 배웁니다. 파이썬 게임제작 라이브러리인 파이게임(PyGame)을 더 간략화한 파이게임 제로(PyGame Zero) 라이브러리를 활용한 쉬운 접근을 시도합니다.[그 다음단계의 책](https://jjlee.gitbook.io/intro#id-70) 인 본격적인 파이썬 게임을 만들며 파이썬 코딩을 배우기 위한 사전 기초 지식을 습득하는 단계로 활용합니다. [hashtag](https://jjlee.gitbook.io/intro#undefined-2) [게임제작하며 프로젝트 기반으로 파이썬 배우기 - 순한맛 (완간)](https://jjlee.gitbook.io/python-pygame_zero/) ----------------------------------------------------------------------------------------------------------------------------------------------- 이전의 두 권([엔트리-파이썬](https://jjlee.gitbook.io/intro#undefined-1) , [엔트리 기초책으로 파이썬 배우기](https://jjlee.gitbook.io/intro#id-7) )의 책을 거쳐오지 않았더라도, 본인이 어느 정도 파이썬 기초 문법은 안다고 여기는 분들을 위한 다음 단계의 책입니다. 배우다가 금새 지치게 만드는 **기존의 언어문법 위주의 책이 아닌, "프로젝트 기반으로 배우기" 라는 이미 블록 코딩 때에도 수없이 경험해 봤고, 그렇게 성장해 왔던 우리의 기존 성장루트를 그대로 적용한 게임제작을 기반으로 파이썬 언어 배우기라는 방법으로** 씌여졌습니다. [이전 서](https://jjlee.gitbook.io/intro#id-7) 와 동일하게 파이썬 게임제작 라이브러리인 파이게임(PyGame)을 더 간략화한 파이게임 제로(PyGame Zero) 라이브러리를 활용한 쉬운 접근을 시도합니다. _참고로 만약 어려분이 이미 파이썬 기초문법을 넘어 심화문법에 지식도 어느 정도는 있다고 생각하다면, 이_ _**순한맛**_ _편말고, 곧바로 파이게임 라이브러리를 직접 사용하는_ [_**매운맛**_arrow-up-right](https://jjlee.gitbook.io/python-pygame) _편(아직 완간 전)을 도전해 보는 것도 가능합니다. 효과적인 학습을 위하여 여러분 자신의 수준에 맞는 책으로 도전해 볼 것을 추천드립니다._ Last updated 9 months ago --- # 1. 파이게임 제로(pygame zero) 라이브러리 | 파이썬-파이게임 제로 (순한맛) [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pygame_zero_lib#pygame) 파이게임(pygame) 라이브러리 ----------------------------------------------------------------------------------------------------- 우리 책에서 사용할 **게임개발용 라이브러리**(라이브러리가 무엇인지 이해가 필요할 경우 [이전 서](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) 를 참고)를 소개하겠다. 파이썬에서 2D 게임을 만들고자 할 때 사용되는 라이브러리로 2000년에 첫 출시된 이래로 벌써 20년 이상 서비스되고 여전히 개선되고 있는 "오픈소스 게임개발용 라이브러리"이다. 이름은 Python용 라이브러리라는 의미로 py라는 접두사를 붙힌 **pygame**라 명명했고, 해당 라이브러리의 로고는 다음과 같다. 좀 더 상세하고 전문적인 내용을 알고 싶다면 [공식 웹사이트arrow-up-right](https://pygame.org/) 에서 확인해 보는 것이 좋겠다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2FZOoo6AnroO2vGSvpOKw7%2Fblobs%2Fkp58AjWTIQEc7PB5vcpI%2Fimage.png&width=768&dpr=3&quality=100&sign=77902cfa&sv=2) 파이게임 로고 일반적으로 개임을 개발할 때 이러한 라이브러리의 지원 없이 모든 코드를 제로 베이스(Zero-based)에서 밑바닥부터 코드 한땀한땀 코딩하면서 개발하겠다고 한다면 불가능한 것은 아니지만 어마어마한 시간이 걸릴 것이다. 게임 자체가 종합예술이라 불릴만큼 많은 기술(특별히 수학적, 물리적)들의 적용이 필요하고, 이미지, 소리, 동영상 등 다양한 멀티미디어를 동시에 다뤄야 하기 때문에 코드의 양과 복잡도가 상당히 높은 편에 속한다. 그래서, 그런 세부적 기술구현의 디테일은 모르더라도 간편하게 그런 기술을 활용할 수 있게 해주는 라이브러리가 있다는 것이 우리에겐 구원의 소식이다. 혹시 여러분들 중에 이걸로 상용게임을 만들 수 있지 않을까 하는 기대를 한다면, 아쉽게도 일찌감치 접어두는 것이 좋을 것 같다. 이 라이브러리는 그런 수준이나 용도를 목적하지 않았고, 무엇보다 아직은 파이썬 언어자체가 상용게임 제작용에 사용되는 주류 언어가 아니다. 그럼에도 우리는 이것을 통해 우리 자신과 우리 주변 친구들, 지인정도가 재미있게 즐길 수 있는 가벼운 인디게임 정도는 충분히 만들 수 있을 것이다. [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pygame_zero_lib#pygame-zero) 파이게임 제로(pygame zero) 라이브러리 ------------------------------------------------------------------------------------------------------------------ 파이게임 라이브러리를 통한 개발은 파이썬 초보자 입장에서는 솔직히 쉬운 편은 아니다. 그렇기 때문에 초보자의 교육적 배려를 위해 더 장벽을 낮춰야 할 필요가 있는게 사실이었고, 저자와 같은 이런 고민이 있었던 한 개발자가 그런 목적으로 "파이게임 제로" 란 라이브러리의 이름에서 알 수 있듯이 _기존의 "__**파이게임" 라이브러리를 기반해 이를 더 간략화시켜 최소한의 코딩만으로도 게임제작이 가능하도록 난이도를 낮춘 파이게임 라이브러리**__를 만들었고, 이를_ [_오픈소스_arrow-up-right](https://github.com/lordmauve/pgzero) _로 공개하였다. 이를_ [_소개한 웹사이트_arrow-up-right](https://pygame-zero.readthedocs.io/) _에서 밝히고 있듯이 본 라이브러리는 코딩을 가르치기 위한 교육용 목적으로 활용을 기대하고 있으며, 따라서 이제 막 텍스트코딩을 시작했고, 특별히 파이썬 언어를 통해 텍스트코딩에 입문한 사용자들을 흥미있게 교육하고, 배우는데 유용할 것으로 기대할 수 있다._ ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F7bNidsbMGQZWyqXXQAgp%252Fimage.png%3Falt%3Dmedia%26token%3D3a46ff9f-328a-4dca-bd10-118f98ccffb0&width=768&dpr=3&quality=100&sign=6a2f266d&sv=2) 파이게임 제로 로고 다만, 한 가지 아쉬운 것은 본 라이브러리가 계속해서 관리되고 성장되지는 못하고 있고, 2021년 1.2.1 버전 이래로 오래동안 정체되어 있는 현실이긴 하나, 그럼에도 불구하고 적어도 우리가 목적하고 있는 파이썬 언어를 활용한 텍스트코딩을 더 잘 배우기 위한 교육용 목적에서의 충분한 수준의 개발은 이미 이루어 진 것으로 볼 수 있다. _이 책은 게임 그 자체를 잘 개발하기 위해 씌여진 책이 아니고, 파이썬 언어를 통한 텍스트코딩을 더 잘 배우기 위한 목적에서 게임개발이라는 수단을 가져온 것이라 이해하면 좋겠다. 우리는 이미 엔트리/스크래치를 통한 블록코딩으로 게임만들면서 코딩에 재미를 붙혀왔던 것처럼 말이다. 이번 우리 목표는 파이게임 제로(pygame zero) 라이브러리 + 파이썬(python) 텍스트코딩용 언어의 조합으로 게임만들면서 블록코딩을 넘어선 새로운 차원의 텍스트코딩 세계에서도 코딩의 재미를 느껴보자._ [Previous게임제작하며 프로젝트 기반으로 파이썬 배우기 (순한맛)chevron-left](https://jjlee.gitbook.io/python-pygame_zero) [Next2\. 개발환경 구축chevron-right](https://jjlee.gitbook.io/python-pygame_zero/dev_env) Last updated 10 months ago --- # 2. 개발환경 구축 | 파이썬-파이게임 제로 (순한맛) 모든 프로그래밍 언어 배우는 책에서 가장 먼저 등장하는 챕터가 아닐까 한다. 왜냐면 제목에서 유추할 수 있듯이 코딩을 시작하기 위해 사전에 기본적으로 설치될 프로그램들이 존재한다는 의미이고, 이를 설치(Install)하고 설정(Configuration)한 이후에야 본격적인 코딩이 가능하기 때문이다. 일반적으로 이 과정에는 크게, 2가지가 필요하다. 첫번째는 사용하고자 하는 프로그래밍 언어(여기서는 파이썬) 그 자체를 설치하는 일이고, 두번째는 이제 그 설치된 언어로 컴퓨터와 소통하기 위해 의미있는 문장과 글을 써내려가는 일(우리는 이 과정을 코딩(또는 프로그래밍)이라 함)을 쉽고 편하게 도와 줄 도우미 프로그램인 코드 편집기(텍스트 에디터: Text Editor) 또는 통합개발환경(IDE: Integrated Development Environment)이라는 프로그램을 설치하는 일이다. circle-info **IDE(Integrated Development Environment, 통합개발환경)**: 하나의 프로그램 안에서 소프트웨어 개발과 관련된 모든 기능을 제공하는 환경을 가진 프로그램. 소프트웨어 개발에 관련된 모든 작업이라고 하면 기본적인 코드 편집, 컴파일(Compile), 디버그(Debug), 빌드(Build), 바이너리 배포(Binary deployment), 다른 개발자와의 공동 개발작업 등을 말한다. 유명한 제품으로 마이크로소프트의 비주얼 스튜디오, 구글의 안드로이드 스튜디오, 파이썬 전용 IDE로는 PyCharm 등이 있다. 그런데, 이러한 과정은 새로운 코딩언어를 사용하게 될 때마다 매번 거쳐야 하는 일상적인 일임에도 불구하고, 초보자 입장에서는 여전히 번거롭고 까다로울 수 있는데 예를들어 설치과정 중 문제라도 생겨 해결을 위한 트러블슈팅(trobleshooting)하는 과정 등에서부터 막히면 텍스트코딩은 어렵다는 선입견을 갖을 수 있고, 다행히 저자와 같이 이런 같은 고민을 했던 또다른 해외 개발자/교육자 그룹이 있었으며, 그들은 그러한 목적에 맞게 개발환경의 추가적인 설치가 필요없이 앱을 실행시키지마자 곧바로 코딩을 시작할 수 있는 필요한 모든 것을 이미 다 에디터에 탑재시킨 [Mu 에디터(Mu editor)라는 코드 편집기의 오픈소스 프로젝트arrow-up-right](https://github.com/mu-editor/mu) 를 시작했으며, **본 서는 순한맛 편으로 코딩 외에 주변적인 일은 최대한 간소화와 생략시키고, 우리가 더 깊히 알고 이해해야 할 텍스트코딩이란 본질에만 더 집중할 수 있게** 하는게 좋을 것이다. 따라서 우리의 취지에 맞게 우리는 Mu 에디터(코드편집기)를 활용해 코딩하도록 하겠다. 그럼에도 불구하고, 만약, 별도 개발환경 구축이란 상세를 살펴보기 원하시는 분이 있다면, 본 서의 **매운맛 버전**([오리지널 파이게임 라이브러리를 활용한 파이썬 코딩 배우기 책](https://jjlee.gitbook.io/python-pygame_zero/dev_env) )에서 확인해볼 수 있다. [hashtag](https://jjlee.gitbook.io/python-pygame_zero/dev_env#mu) Mu 에디터(코드편집기) ------------------------------------------------------------------------------------ 설치를 위해 [Mu 에디터 공식 사이트arrow-up-right](https://codewith.mu/en/download) 를 방문해 본인의 컴퓨터 환경에 따라(컴퓨터의 운영체제는 무엇인지에 따라)에 맞는 현 시점의 가장 최신의 설치 프로그램을 다운로드 받아 설치하면 된다. circle-info 참고로 **필자는 오픈소스 Mu 에디터를 기존보다 더더욱 교육에 편리하게 활용하기 위해서 소스코드를 수정하여 커스텀 버전을 만들고 있는 중**이다. 예를 들어, 우리가 앞으로 첫 게임으로 만들어 볼 플래피버드 게임제작에 있어서 필요한 리소스(이미지들, 추가설치가 필요한 모듈 등)를 에디터 내에 사전에 탑재시켰다. **이는 최초 에디터 설치 이후에 별도의 리소스의 온라인 추가 설치 등의 번거로움 없이 하나의 에디터만으로도 필요한 교육의 A-Z까지(심지어 오프라인 환경일지라도) 할 수 있게 하기 위한 목적이다.** **그밖에 아이들이 열광(?)할 기능으로 자기가 만든 게임을 친구 등 다른 이에게 쉽게 배포해 즐길 수 있는 기회를 줄 수 있게하기 위한** [**자신의 게임을 단 한 개의 실행 파일(EXE 파일) 형태로 변환해 주는 오리지널 에디터에는 없는 추가기능**](https://jjlee.gitbook.io/python-pygame_zero/appendix/packaging) 을 탑재하고 있다. **해당 커스텀 에디터 버전을 시험해 보고 싶은 분은** [**이 곳**arrow-up-right](https://github.com/roboticsware/mu/releases/) **에서 다운로드 할 수 있다.** ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252Fazi5h3nVLOoXyoCDsxLF%252Fimage.png%3Falt%3Dmedia%26token%3D2939639a-43f9-4710-a070-162cab7fa038&width=768&dpr=3&quality=100&sign=f1885080&sv=2) Mu 에디터 공식 사이트 Mu 에디터는 코딩도우미 프로그램으로서 "단순 코드 편집기" 또는 "통합개발환경(IDE) 프로그램" 둘 중에 어느 쪽에 더 가까울까? 답은 때로는 IDE에 좀 더 가깝고, 때로는 코드 편집기에 더 가깝다고 할 수 있다. Mu 에디터에는 **본인이 코딩하려는 개발환경(이를 모드(mode)라 지칭)**을 선택해 손쉽게 바꿀 수 있는 기능을 제공하고 있는데 한 개의 에디터로 다양한 파이썬 코딩(일반 SW개발코딩, 게임코딩, HW제어코딩 등) 을 손쉽게 할 수 있는 초보자 입장에서는 매우 편리한 기능이다. 이러한 모드를 선택하려면 에디터 상단의 메뉴바(Menu bar)에서 **Mode(모드)** 아이콘을 누르면 아래와 같은 (개발환경)모드를 선택해 바꿀 수 있으며(이는 마치 tv리모콘으로 tv채널을 변경하듯이 손쉽게), 현재 우리는 파이게임제로를 활용한 게임코딩을 할 예정이므로, 파이게임제로 모드를 선택해 보자. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FSerNCFEkGhkY9c32AFlb%252Fimage.png%3Falt%3Dmedia%26token%3Deff1dd2b-ffaf-4177-80c8-10690fccd7f8&width=768&dpr=3&quality=100&sign=a8e6d4c&sv=2) 메뉴바의 모드선택 아이콘 ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F8cyvNXKkXqWRqn36bPfl%252Flib_sel.png%3Falt%3Dmedia%26token%3D86771be2-4639-40c1-92f0-502fe76794aa&width=768&dpr=3&quality=100&sign=2c740713&sv=2) 모드 선택화면 모드가 잘 선택되었다면, 다음과 같이 현재 선택된 코딩환경이 무엇인지를 보여주는 상태바(Status bar)를 통해 우리가 선택한 코딩모드가 잘 선택되었는지 한번더 확인할 수 있다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FoiLV4wLaaeZ0XBHyHhVV%252Fimage.png%3Falt%3Dmedia%26token%3D55212067-6ddb-4b6c-8f61-25f2d9a5bc96&width=768&dpr=3&quality=100&sign=6bdf0ba3&sv=2) ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/dev_env#mu-1) Mu 에디터 한글화 프로젝트 소개 circle-info **위에 에디터 화면은 위에서 언급한 필자가 제작 중인 커스텀 버전의 외관으로, 보이는 것처럼 현재 일부 한글화가 적용되어 있고, 전체 한글화를 목표하고 있다. 다만, 전체 한글화에는 많이 시간이 걸리므로 여러분의 참여를 기다리고 있다. 가능한한 많은 분들이 함께 번역에 참여해 빠른 시간에 마치고, 모두가 함께 그 혜택을 누리길 원하고 있다. 모두를 위해 자신의 시간을 기꺼이 할애해 자발적 참여해 주실 분들을 언제나 환영하고 기다린다🙏** 번역참여링크: [https://crowdin.com/project/mu-editor-korean-translationarrow-up-right](https://crowdin.com/project/mu-editor-korean-translation) 이로써 우리는 코딩을 시작하기 위한 사전 준비를 마쳤고, 이제 다음 시간부터는 실제 파이썬 언어 코딩을 시작해 보자. [Previous1\. 파이게임 제로(pygame zero) 라이브러리chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pygame_zero_lib) [Next3\. Hello World 프로그램에서 시작하자chevron-right](https://jjlee.gitbook.io/python-pygame_zero/hello-world) Last updated 5 months ago --- # 3. Hello World 프로그램에서 시작하자 | 파이썬-파이게임 제로 (순한맛) 코딩으로 들어가는 첫 관문이다. 항상 그렇지만, 여러분과 함께 [Hello Worldarrow-up-right](https://ko.wikipedia.org/wiki/%22Hello,_World!%22_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8) 코드부터 배워보는 것으로 시작해야 할 것이다. 그런데, 파이게임제로(pygame zero)은 프로그래밍 언어 그 자체가 아니라, 라이브러리이기 때문에 정확하게 우리가 알고 있는 그 Hello World 프로그램을 만들어 보는게 아니라, Hello World 프로그램에 빗대어 파이게임제로 라이브러리를 이용한 코딩을 할 때, 빼놓을 수 없는 가장 최소한의 기본 코드로만 구성된 가장 작은 프로그램을 만들어보는 예제를 살펴보도록 하겠다. 먼저, MU 에디터를 실행시키자. 메뉴바에 있는 **New(새 파일)** 버튼을 눌러 파일을 열고, 첫 코딩을 시작하자. 다음의 단 한줄짜리 코드를 적어보자. Copy TITLE = 'Hello World!' 코딩은 끝이다. 와우! 너무 간단하지 않은가? 그렇다. 그래서, _파이게임제로는_ [_엔트리-파이썬_](https://jjlee.gitbook.io/entry-python/) _에서 우리가 파이썬 코딩의 맛뵈기를 본 것과 같이 파이썬 코딩을 통한 게임제작을 아주 단순화시켜 놓았다 그래서 우리가 파이게임제로를 통한 게임코딩을_ _**파이썬 게임코딩의 순한맛**__이라고 부를 수 있는 것이다._ 우리는 단지 이 한 줄(라인)짜리 코드만으로 'Hello World' 라는 앱 이름을 갖는 내용없는 화면만 가진 아주 간단한 프로그램을 만들었다. 이제 코드를 파일에 저장하기 위해 메뉴바에 **Save(저장)** 버튼을 누르고 hello\_world 라는 파일이름으로 저장하자. 그리고, 메뉴바의 **Play(실행)** 버튼을 눌러 실행을 시켜서 원하는데로 잘 동작하는지 확인해 보자. 우리가 기대 했던 'Hello World' 라는 앱 이름을 갖는 내용없는 화면만 가진 앱이 잘 실행되었는가? ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FEg1mTml9lK2UujKjpIAv%252Fimage.png%3Falt%3Dmedia%26token%3Dbd87c615-87f5-4bcf-ab21-1fc6da86aa6b&width=768&dpr=3&quality=100&sign=e31c7bf6&sv=2) ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FYD4Il1E2cOdOInytaCeO%252Fimage.png%3Falt%3Dmedia%26token%3D2581ba25-9174-46bc-90a1-2d42f3ceb7c1&width=768&dpr=3&quality=100&sign=ed4b332&sv=2) Hello World 라는 타이틀을 갖는 실행화면 다시 코딩으로 돌아가기 위해 우리는 방금 실행된 앱을 종료시켜야 하는데, 해당 프로그램 창에 x버튼(프로그램 종료버튼)을 눌러 게임앱 자체의 종료는 가능하지만, **Mu 에디터에서 다시 코딩을 이어가려면 반드시 메뉴바의 Stop(정지) 버튼을 눌러 종료시켜야 한다는 것을 잊지말도록 하자**. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FfkjimAex71ByWvervxp3%252Fimage.png%3Falt%3Dmedia%26token%3D92539085-bd56-4e6a-b323-39e0971e1f12&width=768&dpr=3&quality=100&sign=45c4950a&sv=2) 단, 한 줄짜리 코드지만, 코드를 분석해 코드를 이해해 보자. **TITLE** 이란 변수에 앱의 이름을 저장해 두었다. TITLE 이란 변수명에서 직감적으로 알 수 있듯이 만들고자하는 앱의 이름(타이틀)을 지정하는 용도의 변수이구나 라고 추측할 수 있을 것이다. 그런데, 왜 변수명이 TITLE 이어야 하지? 라는 의문이 들 수 있다. 소문자로 title로 하거나, 아예 APPNAME 이라고 내가 원하는데로 변수명을 정하면 안되는 것인가? 그렇다, 애석하게도 그렇게는 되지 않는다. 진짜 그러한지 확인을 위해 실험을 원한다면, 변수명을 다른 것으로 바꾼 후, 원하는 결과가 나오는지 다시 앱을 실행시켜 보자. 원하는 결과를 얻을 수 없다는 것을 알 수 있을 것이다. 우리가 기존 서를 통해 파이썬 언어를 처음 배울 때 배웠던 [**내장함수**](https://jjlee.gitbook.io/entry-python/basic_syntax/3.2-input-output) **, 내장변수** 같은 개념들이 기억나는가? 이 변수는 이러한 개념들과 유사하다. 우리가 이전 서에서 라이브러리가 무엇인지를 배우면서, [**라이브러리**](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) **를 사용한다는 것 자체는 라이브러리의 제작자가 정해놓은 규칙을 배우고, 그 규칙에 맞춰 코딩하는 것**이라 언급했던 것을 기억한다면, _파이게임제로를 사용한다는 것은 파이게임제로 게임제작자가 정해놓은 규칙을 배워 코딩하다는 것이고, 방금 배운 TITLE이라는 변수는 이미 제작자가 정해놓은 고유명사 같은 것이고, 이 변수명만 사용해 변수에 앱의 이름을 담아야지만, 앱 실행시 앱 이름이 정상표기를 보장한다 것을 의미한다._ 이제 우리 게임앱을 더 확장시켜보자. 이제 다음의 2줄의 코딩을 더해 총 3줄(라인)짜리 코딩이 되었다. 우리는 단지 이 3줄짜리 코드만으로 앱 이름으로 'Hello World' 라는 이름을 갖는 가로세로 400 x 300 픽셀크기의 내용없는 화면만 가진 아주 간단한 프로그램을 만들었다. 이를 Play(실행) 버튼을 눌러 실행시켜보자. Copy TITLE = 'Hello World!' WIDTH = 400 HEIGHT = 300 ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FkGhATvLBZ90gmSkzxX1Y%252Fimage.png%3Falt%3Dmedia%26token%3D90a93d6e-16c9-4163-9b3f-062c06e3be46&width=768&dpr=3&quality=100&sign=d354d31a&sv=2) 가로 400픽셀, 세로 300픽셀의 크기를 갖는 앱의 실행화면 그렇다면 맨 처음 만든 1줄짜리 앱과 이 앱의 차이는 무엇인가? 그렇다 우리는 추가적으로 **WIDTH**, **HEIGHT**라는 넓이, 높이라는 의미를 같는 변수를 사용한 추가적인 코딩을 했고, 여러분들이 직관적으로 느낄 수 있듯이 기존에 TITLE 같이 이 변수명들은 파이게임제로 라이브러리가 미리 정해놓은 앱의 크기를 정하는 내장변수 같은 것이고, 이를 활용한 덕분에 이제 우리는 우리가 원하는 정확한 크기를 갖는 앱을 만들 수 있게 되었다. 여기까지 Hello World 프로그램 제작을 마치고, 다음부터는 이제 실제 게임을 하나씩 만들어 가면서 재밌게 ! 즐겁게! 파이썬 코딩을 익혀보도록 하자. [Previous2\. 개발환경 구축chevron-left](https://jjlee.gitbook.io/python-pygame_zero/dev_env) [Next4\. 게임 루프의 이해chevron-right](https://jjlee.gitbook.io/python-pygame_zero/game_loop) Last updated 1 year ago --- # 4. 게임 루프의 이해 | 파이썬-파이게임 제로 (순한맛) 본격적인 게임 제작에 앞서 우리가 만들게 될 게임 프로그램의 기본 동작구조를 이해할 필요가 있다. 엔트리 블록코딩에서 일반적 코딩이 어떠했는지를 먼저 기억해보자. 일반적으로 먼저, 화면에 등장시킬 1개 이상의 주인공 오브젝트들 불러와 화면에 위치시켰고, 이후 각각의 오브젝들이 게임에서 각자 해야할 행동들에 대해서 오브젝트별로 개별적으로 코딩을 했다. 이후 완성된 게임을 실행했을 때도, 각각의 오브젝트들이 각자 독립적으로 동시에 실행되고, 심지어 한 오브젝트 안에서 "시작하기 버튼을 클릭했을 때"를 2개 이상 배치해 마치 병렬적([자세한 내용은 이전 서 참조](https://jjlee.gitbook.io/entry-python/paradigm/4.1-serial-parallel) )으로 행동하는 것처럼 동작하였다는 것을 기억할 것이다. 파이썬 언어와 파이게임제로 라이브러리로 코딩하는 것은 기존에 블록코딩하던 것과 유사한 것도 있지만, 다른 부분도 상당히 있다. 가장 크게 다른 부분이 바로 아쉽게도 이제는 더이상 각각의 주인공 오브젝트들이 독립적이고 동시적(concurrency)으로 행동하는 것으로 간주하고 코딩할 수 없고, 모든 주인공 오브젝트들의 움직임을 한 곳(게임 루프)에서 관리하고 통제(콘트롤) 한다는 것을 전제하고 코딩해야 한다. 그것을 그림으로 표현하면 다음과 같이 끊임없이 반복되는 무한루프(특별히 게임에 사용하는 루프구조라고 하여 게임루프라 부름) 안에서 게임이 실행되게 되는 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252Fzuj7vJV97HMAhzgFUbAo%252Fimage.png%3Falt%3Dmedia%26token%3Db40a443a-7e3c-4a2d-b787-a3a353e03758&width=768&dpr=3&quality=100&sign=c36c2d06&sv=2) 출처: [https://inventwithpython.com/pygame/chapter2.htmlarrow-up-right](https://inventwithpython.com/pygame/chapter2.html) 그림에 표현된 것처럼 **게임루프에서는 크게 3가지(게임 오브젝트를 화면에 그리기, 게임 오브젝트의 움직임 처리, 사용자의 조작에 의한 키보드/마우스 등의 이벤트 처리)를 루프를 돌면서 끊임없이 처리**하게 된다. 만약 우리가 파이게임제로 라이브러리가 아닌 파이게임 라이브러리으로 코딩을 하고 있다면, 저 게임루프의 구조를 만드는 것도 우리에게 할당된 몫으로, 우리가 직접 만들면서 코딩해야 한다. 하지만, **파이게임제로는 초보자들을 배려해 그 부분을 의도적으로 숨기고, 위에서 언급한 3가지의 반복처리 사항을 사전에 콜백함수(draw, update, on\_mouse / on\_key)로 만들어 놓았고, 그 콜백함수에 맞춰 코딩하면 되도록 초보자들을 위해 코딩하기 더 쉬운 구조를 만들어 놓았다.** 다행히 우리는 이미 엔트리-파이썬에서 이미 [콜백함수](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-6) 에 맞춰 파이썬 코딩하는 것을 해보았기 때문에 이런 구조에 어렵지 않게 적응할 수 있을 것이다. 다음 시간부터는 이에 대한 이해를 바탕으로 본격적인 게임 코딩을 해보도록 하겠다. [Previous3\. Hello World 프로그램에서 시작하자chevron-left](https://jjlee.gitbook.io/python-pygame_zero/hello-world) [Next5\. 플래피 버드(Flappy bird) 게임 만들기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird) Last updated 1 year ago --- # 5.2 화면에 플래피버드(오브젝트) 나타내기와 움직이기 | 파이썬-파이게임 제로 (순한맛) 이제 우리 게임의 주인공 플래피버드 객체(object)를 화면에 그려줄 차례이다. 전혀 어렵지 않다. 아래와 같이 이전 코드에서 딱 2라인(5번, 9번 라인)의 코드만 더 추가하면 된다. 그리고, 실행해보자. 플래피버드가 게임화면에 좌측중앙 언저리즘에(정확히는 객체의 한가운데 중앙이 (x, y좌표)로 (75, 350) 위치에) 잘 나타날 것이다. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 flappy_bird = Actor('bird1', (75, 350)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FMU6X2BKQbDtSTzUqJTRm%252Fimage.png%3Falt%3Dmedia%26token%3De345fc28-8c1c-4569-9cbf-45328f8eb452&width=768&dpr=3&quality=100&sign=d5f7900b&sv=2) 🔢 이제 코드 분석시간이다. 먼저 우리는 여전히 객체지향 패러다임 안에서 코딩중임을 기억하자. 새롭게 추가한 5번 라인부터 살펴보자. 여러분들은 이 코드를 보자마자 Actor라는 함수를호출해서 리턴된 결과값을 flappy\_bird라는 변수에 저장하고 있는 것으로 보일 수 있다. 하지만, 큰 오해가 있는데 **Actor는 일단 함수호출이 아니고, 객체(object)를 생성하고 있는 중인 것이다.** 어떻게 그것을 단번에 알 수 있는지는 **파이썬 문법약속(꼭 파이썬만이 아닌 다른 프로그래밍 언어도 동일)으로 함수이름은 소문자로 시작해 만들고,** [**클래스**](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.2-dog-cat#undefined) **(class, 객체를 정의하는 문법으로 함수를 정의할때 사용했던 def 문법과 유사) 이름은 대문자로 시작해서 만든다는 관습적인 규칙**을 잘 따랐다라고 가정할 수 있고, 그것을 근거로 Actor가 대문자로 시작하고 있기 때문에 Actor라는 클래스를 통해 객체를 생성했고, 그 생성된 객체(참고로 객체지향 패러다임에서는 클래스로부터 생성된 객체를 특별히 인스턴스(instance)라는 용어를 사용해 지칭)를 flappy\_bird라는 변수에 저장하고 있다. **객체를 생성할 때도 함수호출처럼 유사하게 생성시 클래스로 넘겨야 할 인자값이 있을 수도 있고 없을 수도 있는데**, 이 경우는 2가지 인자값을 요구하고 있다. blit 함수에 사용할 때와 유사한 2가지 정보(Actor객체의 외관이 될 이미지 파일이름과 그 이미지가 화면에 어디에 그려질지에 대한 위치정보(정확하게는 해당 이미지의 중심값(center)이 화면상에 위치해야 할 위치정보)를 객체생성 위한 클래스의 인자값으로 넘기게 된다. 그렇다면, 라이브러리 저작자가 우리를 위해 미리 틀을 만들어 제공하는 Actor 클래스의 목적과 용도는 무엇인지를 어떻게 알 수 있을까? 그렇다 지난 시간에 배운 것처럼 [라이브러리 메뉴얼(문서)arrow-up-right](https://pygame-zero.readthedocs.io/en/latest/builtins.html#actors) 를 찾아보면 저자 자신이 이미 해당 클래스의 목적과 사용방법을 상세히 설명하고 있다. 그러나 Actor 클래스의 목적은 문서를 찾아보지 않더라도 _Actor라고 '배우'라는 뜻으로 의도적으로 의미부여한 이름에서 직관적으로 유추할 수 있듯이, 우리 게임이란 무대에 등장하는 움직임이 가능한 배우(객체)를 만드는데 사용한다 것인데 예를들어 우리 게임무대에는 크게 2개의 배우(플래피버드 배우와 플래피버드가 부딪히지 말고 통과해야 하는 파이프 배우)가 있을 수 있다. 지금은 그중에서 지금은 먼저 플래피버드 배우(Actor)객체를 만든 것이다._ 🔢 객체는 잘 생성되어졌고, 이제 이를 화면에 출력만 하면되는데 9번 라인의 코드처럼 Actor객체가 제공하는 함수인 draw함수를 호출해서 화면에 그려낼 수 있는데, 현재 생성된 객체를 가리키고 있는(참조하고 있는) 변수인 flappy\_bird를 통해 해당 함수를 접근할 수 있다. 플래피버드 객체를 화면에 잘 출력했다. 이제 우리에게 남은 일은 이 게임의 컨셉인 플래피버드의 기본 움직임을 구현하는것인데 기본 움직임은 다음과 같다. 첫번 째는 플래피버드가 강력한 중력(?)의 힘을 받아 가만있으면 아래방향으로 추락하는 것이고, 두번 째는 이 힘을 극복하기 위해 마우스를 클릭할 때마다 추진력을 발휘해 공중으로 얼마간 비상하게 되는 움직임이다. 🔢 먼저, 플래피버드 객체가 화면 아래로 추락하는 중력의 힘을 구현하자. 이것은 다른 말로 말하면, 객체가 아래로 추락하는 움직임이라는 애니메이션의 구현이다. 애니메이션의 기본적인 구현은 우리가 잘 알다시피 매우 짧은 시간안에 아주 조금씩 이미지(여기서는 객체)의 변화지만 빠르게 사용자의 눈에 보여주게 된다면, 마치 우리 눈에는 마치 움직이고 있는 것처럼 보이는 착시효과이다. 구체적으로 잔상 효과라는 착시인데 실제로는 이미지(객체) 사이에 짧은 중단이 있어도 연속적인 이미지가 계속해서 지속하는 것처럼 보이는 현상을 말한다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FPF0KSwEJAIbSiyzlP7gD%252Fimage.png%3Falt%3Dmedia%26token%3D616070db-a854-4896-8c8c-249595d7fd7d&width=768&dpr=3&quality=100&sign=d78bb2da&sv=2) 출처 : https://theappliancesreviews.com/why-frame-rate-is-25-30-or-29-97-fps/ 우리보고 애니메이션의 메커니즘을 다 구현하라고 하면 갑자기 멘붕(?)이 올 수 있으나, 알다시피 파이게임제로는 최대한 쉬운 코딩으로 게임구현을 목표하고 있기 때문에 그런건 이미 다 내부적으로 구현되어 있고, 우리에게 요구하는 것은 그 한장한장의 이미지(객체)들의 움직임 변화에 대한 것만 코딩해주면 된다. **기존에 우리는 라이브러리가 요구하는 draw 콜백함수를 통해 객체를 화면에 그렸듯이, 애니메이션을 위해서는 라이브러리가 요구하는 update 콜백함수를 통해 객체의 움직임을 표현하면 된다.** 그러나 여기서는 이미지(객체) 자체의 변화는 없이 단순히 중력이 작용하는 상황을 애니메이션화해 코드로 표현하면 다음과 같다. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 flappy_bird = Actor('bird1', (75, 350)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() def update(): flappy_bird.y += 1 라이브러리는 update 콜백함수를 최대 초당 60회(60 FPS(Frame Per Second)라고 부름)를 자동 콜백호출하게 된다. 호출될 때마다 해야하는 일은 플래피버드 객체가 매우 조금씩(1픽셀씩) 화면 아래 방향으로 움직이면 되는 것이다. 12번 라인에서 flappy\_bird.y += 1 이라고 코딩했는데, 생성된 객체(인스턴스)를 가르키는 flappy\_bird 변수는 위에 살펴본 바와 같이 생성된 객체의 접근을 대리하는 참조 포인트로서 이 변수를 통해 객체가 객체 이용자들에게 제공하는 함수를 접근할 수도 있고, 객체가 갖고 있는 속성(여기서는 x, y좌표로 표현되는 화면상의 위치정보) 또한 접근할 수 있다. 우리는 객체의 y좌표값를 접근해 그 값을 1픽셀(pixel)씩 증가시키고 있다. 여기서 화면 아래로 이동시키기 위해서 값을 증가시켜야 하는 이유는 우리가 이전 장에서 살펴본 [파이게임제로에서의 화면좌표계](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background#undefined) 에 의한 것이다. 실행결과를 살펴보자. 플래피버드 객체는 실행하자마자 마치 중력힘이 존재하는 것처럼 자유낙하하게 된다. 만약 더 빠른 속도로 자유낙하 시키고 싶다면, 어떤 값을 바꿔야 할까? 그렇다 일회 움직임의 픽셀값을 기존 1픽셀보다 더 크게 하면 될 것이다. 두번째로 마우스버튼을 누를 때마다 현재 위치에 위쪽으로 움직이게 하는 것을 코딩하자. 어떻게 코딩하면 좋을까? 블록코딩에서의 경험상, 또 구체적으로는 우리가 이전서에서 배웠던 [이벤트 주도형 또는 이벤트 기반형(Event-Based) 프로그래밍 패러다임arrow-up-right](https://jjlee.gitbook.io/entry-python/paradigm/4.3-event-driven) 을 적용하면 되지 않을까? 라는 생각이 들었다면, 여러분은 이제 텍스트코딩 세계의 감을 잡아가고 있는 것이다^^ Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 flappy_bird = Actor('bird1', (75, 350)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() def update(): flappy_bird.y += 1 def on_mouse_down(): flappy_bird.y -= 50 🔢 기존 코드에서 14-15번 라인에 **on\_mouse\_down** 콜백함수(마우스 클릭시마다 라이브러리에서 자동으로 호출되는) 구현을 하나를 추가했다. 콜백함수이기 때문에 함수명은 우리가 정한 이름이 아니고, 라이브러리에서 이미 정해놓은([라이브러리 문서참조arrow-up-right](https://pygame-zero.readthedocs.io/en/latest/hooks.html#on_mouse_down) ) 이름으로 우리는 정확히 그 이름으로 된 함수명을 사용하고, 라이브러리에 의해서 그 함수가 자동으로 불렸을 때 해야할 행동만 함수 안에 구현하면 된다. 불렸을 때 해야할 일은 다음과 같은데, 15번에 라인에서 구현한 것처럼 플래피버드 객체를 현재 위치에 50픽셀(pixel) 위쪽 방향으로 이동시켜 마치 공중으로 부상하는 것 같은 움직임을 만드는 것이다. 위에서 해본 자유낙하 구현과 크게 다를 건 없어서 이해하기 전혀 어렵지 않을 것이다. 다만, 기존 위치에서 -50을 한다는 것은, 역시나 [파이게임제로에서의 화면좌표계](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background#undefined) 때문이고, 화면위쪽으로의 이동은 현재 y축 위치값보다 줄어야들어야(마이너스 해야) 함을 알 수 있다. 현재까지의 코드를 실행시켜보자. 기대했던데로 자유낙하하는 플래피버드는 마우스 클릭을 통해 부양시킬 수 있다. 여기까지 잘 따라온 당신은 이처럼 파이게제로는 적은량의 코드로도 게임을 손쉽게 만들어 갈 수 있다는 것을 이해하기 시작했을 것이다. 현재까지의 코드도 괜찮긴 하지만, 코드를 실행해 보면 플래피버드의 동작에 다소 아쉬운 점이 있다. 그것은 공중부양하는 동작이 자연스럽지 않고, 뚝뚝 끊어짐과 함께 급한 순간이동처럼 느껴지는 상승동작에 관한 것이다. 이 부분을 어떻게 하면 자연스러운 동작으로 개선할 수 있을까? 다음 장에서 살펴보도록 하자. [Previous5.1 화면에 배경 이미지(오브젝트) 나타내기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background) [Next5.3 화면에 플래피버드(오브젝트)의 자연스러운 움직임 만들기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.3_flying_gravity) Last updated 7 months ago --- # 5.3 화면에 플래피버드(오브젝트)의 자연스러운 움직임 만들기 | 파이썬-파이게임 제로 (순한맛) 우리 눈에 플래피버드의 움직임이 자연스럽지 않았던 건 자연에 존재하는 실제 물리법칙 즉 중력과 유사하게 만들지 않았기 때문이다라고도 할 수도 있다. 그래서, 움직임을 실제 중력과 유사하게 구현해 보도록 하자. 우리가 아는 상식에서의 중력은 고정된 일정한 힘이며, 중력에 의해서 물체가 낙하할 때, 물체는 일정한 속도로 낙하하는게 아니라, 가속도가 붙어서 떨어질 때는 점점더 빠르게 떨어진다는 것이다. 따라서 우리도 이 두 가지(중력과 떨어지는 속도)를 두 가지 변수를 사용해 분리하자. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 flappy_bird = Actor('bird1', (75, 350)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() def update(): global drop_speed drop_speed += GRAVITY flappy_bird.y += drop_speed def on_mouse_down(): global drop_speed drop_speed = -6.5 🔢 위에서 언급한 것처럼 자연스럽게 나는 모습을 구현하기 위해서는 몇 가지 추가적인 변수를 도입할 필요가 있다. 플래피버드가 자연낙하하게 하는 중력(아래방향으로 당기는 힘)이라는 변하지 않는 고정된 힘의 크기를 5번 라인에서 GRAVITY 라는 변수명과 함께 그 크기를 0.3이라고 정했다. _여기서 중력의 힘의 크기를 일부러 변수를 담아놓아다는 것에 주목하자. 이렇게 하는 의도는 앞으로 해당 값은 변수이름을 통해서 읽어 오겠다는 것이다. 이런 식의 테크닉을 구사하는데는 두 가지 목적이 있다._ 첫째, 중력힘의 값을 변수명이 아닌 숫자값 그대로 사용했다고 하면, 당장은 몰라도 시간이 지나 나중에 나 자신 또는 내 코드를 읽게 되는 다른 동료의 코드 가독성(해독성)을 떨어뜨리게 된다. 코드를 해독하기 위해 눈으로 한 라인 한 라인 읽어나가다가 갑자기 등장한 숫자(0.3)을 보고, 그 숫자값은 도데체 어떤 값을 의미하는 숫자인지 잠시 고민하는 시간이 필요하고, 이렇게 한눈에 한번에 빠르게 읽어나가지 못하고 멈추게 하는 그 지점이 바로 가독성을 떨어뜨리는 지점인 것이다. 그런데, 우리가 이런 식으로 그 숫자값을 중력이라는 의미를 부여한 변수이름(GRAVITY)과 매칭해 놓았다면, 우리는 변수이름만 보고([이전 서에서 언급했지만, 그래서 변수이름을 의미있 잘 짓는 것이 중요!](https://jjlee.gitbook.io/entry-python/basic_syntax/3.3-variable) ) **곧바로, 그 숫자값의 의미를 파악할 수 있다. 또 의도적으로 변수이름을 대문자로 표현한 것도 특징인데, 물론 변수가 변화하는 값을 저장하는 기본적인 목적이 있으나, 이 경우는 그러한 목적보다는 특정값을 대치하는 용도로서의 용도가 더 크기 때문에 그 의미를 부여하기 위해 대문자 표기를 하였다. 참고로 이는 일종의 프로그래머들 사이의 관습적 표현(**[**Conding conventions**arrow-up-right](https://en.wikipedia.org/wiki/Coding_conventions) **)이므로 적극 활용하면 좋을 것이다.** 둘째 이렇게 할 경우, 또다른 장점은 무엇이 있을까? 나중에 어떤 이유에 의해서([이전 서](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) 에서 언급했지만, 소프트웨어 특성상 생각보다 자주 코드수정이 빈번하고) 우리 게임에 새로운 요구사항이 와서 게임의 난이도를 높이기 위해 더 센 중력의 힘을 요구한다고 하자 그리고, 우리는 이에 맞춰 전반적인 코드의 재수정이 필요하다. 우리는 이미 똑똑하게 이러한 가능성을 대비해 해당 값을 변수명을 통해 코딩해 놓았지 않는가? 따라서, 이 값(GRAVITY)의 현재 값인 0.3을 예를들어 0.5으로만 바꿈으로써 이러한 코드 수정 요구사항의 빠른 대응이 가능하게 되는 것이다. 🔢 6번 라인에 도입한 drop\_speed 변수는 말 그대로 **값이 계속 변화하는 값이 적용되는 변수로서의 용도이기 때문에 의도적으로 소문자로만 변수명을 적었다는 것을 기억하자. 이렇게 대문자/소문자의 구분표기만으로도 그 용도가 쉽게 들어나는 용이성이 있다.** drop\_speed 라는 이름을 부여한 것에서 알 수 있듯이, 낙하를 만드는 힘(중력)은 고정된 것이나 실제 낙하할 때 속도는 시간이 지남에 따라 가속도가 붙어 더 빠른 속도로 증가하게 되고, 이를 표현한 값인 것이다. 해당 변수의 목적과 용도는 이해했다. 자 그런데 이번엔 _전체 코드영역에서 이 변수의 정의(definition)가 어디에 위치하고 있느냐에 관한 것으로 왜 함수 안쪽 영역(scope)에 존재하지 않고, 함수 바깥쪽 영역에 존재해야 하는가에 대해 알아볼 필요가 있다._ **이는 변수를 만드는 규칙에 관한 것으로, 특정 함수 안에서만 사용되는 변수는 해당 함수 안쪽에서 정의하고, 여러 함수에 걸쳐 공통으로 함께 사용해야 하는 변수는 이처럼 함수들 바깥쪽 영역에 정의하게 된다.** 이처럼 drop\_speed 값의 사용처는 update 함수와 on\_mouse\_down 함수 두 곳에서 함께 사용되고 있다는 것을 알 수 있고, 이러한 이유 때문에 해당 변수는 함수들 바깥쪽 영역에 정의되었다. 이 변수를 사용하는 함수에서의 활용을 유심히 살펴 본다면, **함수 밖에 있는 변수이고 함수 안쪽에서 그 값을 수정하려면 변수명 앞에 global 이라는 키워드(keyword)를 붙혀 함수 도입부분에서 변수명을 선언해야 하는 파이썬 문법이 존재**하는 것을 발견할 수 있을 것이다. 그렇다. **파이썬 문법에서는 함수 밖에 존재하는 변수를 활용하고 있다는 의미를 명확하게 하기 위해 변수명 앞에 global(말 그대로 코드전체에서 사용가능한 변수의 의미로)를 붙히게 되어있다는 것을 기억하자. 이러한 변수들을 지칭하는 용어도 global 변수라고 불린다는 것도 함께 기억하자.** circle-info _참고로 여기서 주의할 것이 있는데_ _**파이썬이 갖는 엄밀히 문법을 따지지 않는 유연성이 장점이면서 동시에 때론 단점**__인데, 때로는 global 이라는 키워드를 명확히 쓰지 않았음에도 불구하고 실행시 에러없이 잘 실행되기때문에 이 경우, 왜 원하는데로 동작하지 않는지의 논리적 오류를 발견하기가 쉽지 않은 상황은 만날 수도 있으니 주의하도록 하자._ 🔢 _21번_ 라인에서 사용자가 마우스버튼을 눌러 플래피버드를 부상하려고 할 때, 낙하하는 속도를 의도적으로 음수값을 통해 뒤로 되돌림으로써 플래피버드가 상승하는 효과를 만들고 있다. 지금까지 플래피버드의 자연스러운 움직임을 만들기 위한 테크닉 적용에 대해 살펴보았고, 이를 통해 새로운 것들(파이썬 문법, 코딩관습 등)을 배울 수 있었다. 이제 다음장에서는 우리 게임무대의 두번째 Actor(배우)인 파이프 객체들을 생성하고 출현시켜 보자. [Previous5.2 화면에 플래피버드(오브젝트) 나타내기와 움직이기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.2_flying) [Next5.4 화면에 파이프(오브젝트) 나타내기와 움직이기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.4_pipes) Last updated 1 year ago --- # 5.1 화면에 배경 이미지(오브젝트) 나타내기 | 파이썬-파이게임 제로 (순한맛) 엔트리 블록코딩에서의 경험을 상기시켜 보자. 여러분이 게임을 만들 때 가장 먼저 했던 일은 무엇이었을까? 화면에 게임에 등장할 오브젝트를 배치하는 일부터 였을 것 같다. 텍스트코딩에서도 유사하다. 우리도 배경과 주인공(플래피 버드) 등의 오브젝트를 화면에 등장시키는 일부터 시작하길 원한다. 엔트리에서도 그랬듯이 게임에 사용할 오브젝트들이 엔트리에 기본 내장된 오브젝트들이 아니라면 별도로 게임에 사용될이미지 파일들을 불러와 엔트리에 등록한 이후에서야 코딩을 시작할 수 있었다. 동일하게 우리가 첫 번째로 해야 할 일은 우리 게임에 사용될 전체 이미지의 압축파일을 여기 [다운로드 링크arrow-up-right](https://kinolien.github.io/gitzip/?download=https://github.com/andyborrell/pygamezero_bird/tree/master/images) 에서 다운 받아 압축을 푼 후, _Mu 에디터 (이하 뮤 에디터)설치시 자동으로 생성되는 작업 디렉토리 경로(__**사용자 계정/mu\_code**__)에 디폴트(default) 이미지들의 저장소인 images 라는 폴더(디렉토리) 안으로 일괄 저장시켜 놓는 일이다._ 또는 [커스텀 뮤 에디터arrow-up-right](https://github.com/roboticsware/mu/releases) 를 사용 중이라면 뮤 에디터의 작업 디렉토리인 (사용자계정)\\mu\_code\\examples\\pygame\_zero\\images 안에서 복사해 올 수 있다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FiJAYR961eK0EPigh2VtQ%252Fimage.png%3Falt%3Dmedia%26token%3D84a98766-981a-4e8c-8304-bfaf610f38bd&width=768&dpr=3&quality=100&sign=fb0192b&sv=2) 그 폴더의 위치를 확인하고 싶으면, 뮤 에디터상의 메뉴바의 **images 라는 버튼을 눌러** 코딩에 사용되는 이미지들이 저장되는 폴더를 열어보자. 기본적으로 이미 내장된 몇 개의 샘플 이미지가 있을텐데 그 곳에 함께 저장시킬 수 있다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FEpgmX3FObWQhFVm6RcZF%252Fsel_imgs.png%3Falt%3Dmedia%26token%3D5cee11d4-3473-40df-ac85-2bae2a91ba34&width=768&dpr=3&quality=100&sign=36dc7f74&sv=2) ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252Fv7ZQrE3bh6v5tvYY2DeO%252Fimage.png%3Falt%3Dmedia%26token%3D55f1d7c2-8d66-486e-bf4e-838ecad55e01&width=768&dpr=3&quality=100&sign=87edf9b0&sv=2) images 폴더에 저장된 게임 이미지들 circle-info 참고로 **게임개발시 필요한 리소스들(이미지, 폰트, 효과음, 배경음)을 담아 놓는 하위폴더들의 이름은 이미 정해진 이름들(images, fonts, sounds, music)로만 생성해야 한다는 파이게임제로 라이브러리가 갖고 있는 기본제약사항이 준수되어야만 하고**, 그러한 이유로, 뮤 에디터에 메뉴 아이콘의 이름들이 그 하위폴더의 이름들과 일치되어 있는 것이다. 따라서, 우리는 리소스의 종류에 맞춰 각각이 아이콘을 눌러 자동으로 열리는 폴더에 해당 리소스 파일을 저장하는 방식으로 개발하는게 편리하기도 하고 실행시 리소스를 발견할 수 없다는 실행에러를 줄일 수 있는 길이다. Mu 에디터의 관련 메뉴얼 참조: [https://codewith.mu/en/howto/1.2/pgzero\_sounds\_imagesarrow-up-right](https://codewith.mu/en/howto/1.2/pgzero_sounds_images) 이제 에디터 창에 아래의 6라인짜리 우리 게임의 첫번째 코드를 입력한 후, 메뉴의 **Play(실행) 아이콘 버튼을 눌러** 실행시켜보자. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 def draw(): screen.blit('background', (0, 0)) 게임의 배경화면 이미지가 출력된 우리의 게임앱이 잘 실행되었을 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FaOE9A4tLLzSRv0yTxV0z%252Fimage.png%3Falt%3Dmedia%26token%3D79e0ea1a-9270-4f97-ac60-ca3eb7738e2d&width=768&dpr=3&quality=100&sign=aeb277c6&sv=2) 🔢 이제는 코드분석을 해볼 차례이다. 1~3번 라인의 코드는 이미 지난 과에서 사용해 본 코드이기 때문에 코드 이해의 어려움은 없을 것이다. 5~6번 라인에서 **draw** 콜백함수 안에서 다시 pygame 라이브러리의 내장함수인 **blit** 함수를 **screen.blit** 형태로 위치정보와 함께 호출하고 있다. **screen.** 이란 blit 함수는 screen 안에 있다는 계층적 위치정보를 가졌기 때문에 기존 서에 배웠던 것처럼 screen 이란 모듈이 있고, 그 안에 있는 blit 함수인가라고 생각할 수 있다. 그러나, 여기서의 screen의 본질은 사실은 모듈이면서 동시에 [이전 서(엔트리 예제로 파이썬 배우기)](https://jjlee.gitbook.io/pygame_zero-entry_basic/) 에서 언급된 적이 있는 [**객체(오브젝트)**](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.2-dog-cat#undefined-1) 이다. 즉, screen. 이란 뜻는 screen 객체가 갖고 있는(안에 있는) blit이란 함수를 호출하고 있는 것이다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background#undefined) 객체(오브젝트)의 이해 객체(오브젝트)를 활용한 텍스트코딩이 이번 책 안에서 처음 등장했기 때문에 다시금 기존 서들에서의 객체에 대한 이해의 수준에서 조금더 심화할 필요가 있겠다. _**객체는 말 그대로 실제 현실 세계(real world)의 어떤 실체가 있는 객체(object), 예를들어 자동차, 동물, 사람 등을 프로그램 세계(program world)에서 사용하게 위한 목적으로 그 특징을 모사한 것일 수도 있고, 어떤 객체는 실 세계에는 없는 오직 프로그램 세계에서만 존재하는 것, 예를들어 몬스터(monster)라는 가상의 객체, 또는 애니메이션(animation)라는 기술적 테크닉 등 일 수 있다.**_ _**객체는 그 자체로 하나의 독립된 개체이기 때문에 자신의 속성(attribute)을 표현하기 위한 변수와 자신의 할 수 있는 어떠한 동적인 액션(action) 또는 행동(behavior)을 표현하기 위함 함수(특별히 객체 안에 존재하는 함수를 메소드(method)라 호칭)가 하나로 묶여있게 된다.**_ 예를들어, 자동차를 모사한 객체를 만든다고 했을 때, 자동차가 가질 수 있는 특징으로 차체의 모양, 엔진의 종류, 바퀴의 종류 등의 **속성**을 변수로 갖을 수 있고, 자동차가 할 수 있는 달리거나(drive), 멈추거나(brake), 회전하거나(turn) 등의 **행동**을 함수로 갖을 수 있을 것이다. 위에서 screen이 파이게임제로 라이브러리 저자가 만든 라이브러리 내의 모듈이면서 동시에 객체라고 했고, **라이브러리의 저자들은 항상 자신의 라이브러리 안에 모든 것들을 사용자들이 목적에 맞게 정확하게 사용하도록 별도의 설명서를 제공하는게 일반적이고**, 따라서, [screen 객체에 대한 설명서arrow-up-right](https://pygame-zero.readthedocs.io/en/latest/builtins.html#screen) 도 저자가 올려놓은 [인터넷 저장소arrow-up-right](https://pygame-zero.readthedocs.io/) 에서 찾을 수 있다. 설명서에 의하면, screen 객체는 앱의 화면을 객체화한 것으로 그 screen 객체가 할 수 있는 행동(behavior) 중에 하나로 [blit 함수(메소드)arrow-up-right](https://pygame-zero.readthedocs.io/en/latest/builtins.html#Screen.blit) 를 통해 특정 이미지를 화면의 특정 위치에 그리는 일을 할 수 있는 것이다. 그래서, 결론적으로 우리는 배경이미지를 화면에 그리려는 우리의 목적을 달성하기 위해 screen 객체 안에 blit 함수를 이용하되, blit 함수에서 요구하는 2개의 파라미터 정보는 첫번째로 화면에 무엇을 그려야 하는지 정확히는 그려져야 할 이미지의 파일이름, 두번째로 그 이미지가 화면상에 어디에 그려져야 하는지의 위치정보(정확하게는 이미지의 좌상단(topleft)이 화면상에 어디에 위치해야 하는지의 위치정보)를 요구하고 있다. 그래서, 우리의 함수호출은 최종적으로 이런 형태 screen.blit('background', (0, 0)) 의 모습이 되게 된다. blit 함수에서 더 알아야 것들이 남아있다. 함수로 넘겨야 하는 실제값(이를 아규먼트(argument) 또는 한국어로 인자값 라는 용어로 지칭) 중에서 함수의 첫번째 인자값은 'background'로 이미지들을 저장해둔 images 폴더 안에 있는 이미지 중에 게임의 배경이미지 파일이름이란 것을 직관적으로 알 수 있으나, 두번째 인자값인 (0, 0)이란 이 위치정보는 이전 서에서 배우지 못한 데이터의 형태(type)를 띠고 있다. (x좌표, y좌표) 형태로 값을 1개가 아닌 복수개인 2개(x, y좌표값)를 한꺼번에 넘기고 있는데, 우리가 이전 서에서 배웠듯이 단 하나의 값이 아니라, 여러 개의 복수의 값을 저장할 수 있는 있는 파이썬에서 제공하는 메모리 공간은 무엇이었는지 기억하는가? 그렇다 [리스트(List)](https://jjlee.gitbook.io/entry-python/basic_syntax/3.6-list) 에 담아서 보내면 되겠다라는 생각이 들 것이다. 리스트의 문법형식이 기억나는가? 큰 대괄호 안에 값을 채우면 된다. 즉, 이 경우에 \[x좌표, y좌표\] 이렇게 값을 채워서 blit 함수로 전달하면 되지 않겠는가? 그렇다, 가능은 하다. 그런데, 이 함수를 만든 사람은 그렇게 말고, 해당 값들을 소괄호()에 담아 보내달라는 함수 이용자인 우리에게 제약을 걸어두었다. **대괄호에 담는 것과 소괄호에 담는 것에 차이는 무엇인가? 점하나도 공백하나도 다 의미가 있는가 있는 프로그래밍 세계에서 괄호종류가 다른 것은 당연히 의미가 달라진다.** 소괄호에 데이터들를 담는 것은 기존에 우리가 배우지 않았던([이전 서](https://jjlee.gitbook.io/pygame_zero-entry_basic/) 를 거쳐오지 않은 분들의 경우) 새로운 데이터 유형(타입)이다. 이런 데이터 타입을 튜플(Tuple) 이라고 부른다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background#tuple) 튜플 (Tuple) 먼저, 튜플 어떻게 사용할 수 있는 문법을 확인해보자. 정말, 리스트의 대괄호가 소괄호로 바뀐거 외엔 표면적으로 차이가 없다. > **튜플명 = (문자, 숫자 등의 여러 값. 단, 값들은 콤마(,)로 구분)** **리스트와 튜플의 근본적인 차이는 값을 추후에 바꿀 수 있느냐/없느냐의 여부이다, 수정가능/읽기전용의 차이이다.** 튜플은 후자인데, 의도적으로 읽기전용(read only)값으로 설정함으로써, 처음 초기값으로 설정한 값을 보호해서 추후에 자신이나 또는 다른 개발자가 개발해나가면서 실수로 해당 값을 변경하려는 시도를 처음부터 막으려는 의도가 있다. 항상 우리는 함수 저작자가 만들어 놓은 의도성 있는 제약을 따르도록 하자. 우리는 남이 만든 함수나 라이브러리를 가져다가 쓰는 입장에서 크게 불만(?)을 갖긴 어렵고, 함수 저작자가 제안하거나 제약하는 틀 안에서 코딩한다고 생각하면 좀 더 편한다. 그래서 간단히 우리는 blit 함수를 쓰려면, 이미지가 그려져야 할 화면상의 x, y좌표값를 튜플에 담아서 함수의 인자값(아규먼트)로 전달한다 라고 생각하면 되고, 그렇게 코딩한 것이다. [hashtag](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background#undefined-1) 파이게임제로의 화면좌표계 ---------------------------------------------------------------------------------------------------------------- 이제 마지막 의문을 해결하자. 위치정보 데이터의 타입은 튜플이란 것을 알았다. 그런데 왜 위치정보값, 즉, x, y좌표값은 0, 0 이 되어야 하는거지? 라는 의문이 필요하다. 이것을 이해하려면 **파이게임 라이브러리에서의 좌표체계를 알아야 하는데, 이는 우리가 기존에 익숙하던 엔트리/스크래치와는 다른 좌표체계를 갖고 있다. 엔트리/스크래치는 화면중앙이 중심좌표 0, 0 이고, 이것을 기반으로 위치계산하는 좌표체계이다. 그런데, 파이게임의 좌표체계는 아래의 예시처럼 화면의 중앙이 아닌 좌측상단이 0, 0이 된다.** **즉, x의 좌표값은 우측으로 갈수록 커지고, y의 좌표값은 아래방향으로 갈 수록 커지게 된다.** ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FeApTXYoAYIe24dgkJkaL%252Fimage.png%3Falt%3Dmedia%26token%3D442cf840-f9bc-444f-95ce-ffd6708127d5&width=768&dpr=3&quality=100&sign=3de04e7f&sv=2) 파이게임의 좌표체계 예시 이제 왜 배경 이미지를 화면에 꽉 채위기 위해서는 배경 이미지의 좌상단이 (0, 0) 좌표부터 그려져야 하는지 이해가 되었을 것이다. 이번 장을 마치기 전에 어떤 사람은 아직 해결되지 않은 의문이 남아있을 수 있고, 어떤 사람은 없을 수 있다. 그것은 바로 이것인데 우리는 draw 함수를 정의(definition)만 했지 코드의 어느 곳에서도 그 함수를 호출을 한 적이 없는데 어떻게 누구에 의해 저 함수가 호출이 되서 앱이 실행되어 배경이미지가 잘 그려지고 있지? 라는 것인데, 이전 서에서 [콜백함수](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-6) 의 개념을 제대로 이해하고 있는 사람이었다면 이 의문이 안 남았을 것이고, 만약 의문이 여전히 남아있다면 그것은 콜백함수 개념이 아직 확실히 잡히지 않은 것이니 이전 서로 다시 돌아가 해당파트를 다시한번 공부하고 돌아오도록 하자. 그렇다 **draw 함수는 콜백함수다. 이는 파이게임제로 라이브러리가 사전에 정해놓은 것으로 우리가 화면에 무언가 그려야 할 때는 draw 라는 미리 정해진 이름의 함수를 반드시 사용해야 한다는 것이다. 그래야만 그 함수를 우리 대신에 라이브러리가 직접 자동으로 호출해주기 때문이다.** 다음 장에서는 이어서 우리의 주인공 객체(오브젝트)인 플래피 버드를 화면에 그리고 움직여 보도록 하자. [Previous5\. 플래피 버드(Flappy bird) 게임 만들기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird) [Next5.2 화면에 플래피버드(오브젝트) 나타내기와 움직이기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.2_flying) Last updated 10 months ago --- # 5.8 플래피버드 게임 추가기능 구현하기 (플래피버드 애니메이션) | 파이썬-파이게임 제로 (순한맛) 현재 플래피 버드의 이미지는 날고 있는 중임에도 불구하고, 날개짓이 없는 정지상태로 날고 있다. 어딘가 부자연스럽다. 날개짓하는 애니메이션이 표현된다면 더 훌륭한 게임될 수 있겠다. 자, 이제 이 부분을 함께 구현해 보도록 하자. 여러분이 기존에 엔트리 블록코딩에 익숙하다면, 어떻게 해야할지 금새 감이 잡힐 것이다. 엔트리에서는 여러 오브젝트 중에 일부 오브젝트 경우 \[모양\]이라는 탭을 누르면, 애니메이션 활용이 가능한 여러 이미지로 구성되어 있음을 알 수 있고, 이 이미지들을 순차적으로 일정시간 간격으로 순환시키면서 애니메이션을 만들었던 기억이 있을 것이다. 만약, 파이게임제로에서도 이런식으로 코딩할 수 있으면 편할 것 같다. 그런데, 오리지널 파이게임제로는 아쉽게도 그런식의 코딩을 할 수 있는 준비가 되어있지 못하다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.8_animation#pgzhelper) pgzhelper 라이브러리 설치 그러나, **오픈소스 세계의 장점이 무엇인가? 아쉬우면 누구든 오픈된 기존 코드를 수정하므로 기능을 개선해 재공개할 수 있는 것인데**, 싱가폴에 한 개발자가 우리가 엔트리에 코딩에서 해봤던 방식과 유사하게 오브젝트의 애니메이션을 만들 수 있는 [파이게임제로에 기반한 파이썬 추가모듈을 개발해 오픈소스로 제공arrow-up-right](https://github.com/QuirkyCort/pgzhelper) 하고 있다. circle-info pgzhelper 는 스크래치 블록코딩 한 것을 파이게임제로 포팅(porting)하는(똑같이 기능구현하는) 과정에서 파이게임제로 자체 한계로 인해 어려움이 있었던 부분을 개선하기 위해 싱가포르의 **Cort** 라는 개발자가 최초로 만든 것으로, [로보틱스웨어arrow-up-right](https://roboticsware.uz/) 에서도 유사한 이유로 엔트리 블록코딩을 파이게임제로로 포팅을 용이하게 하기위해 이 라이브러리에 추가 기능을 더해 보완하였고, 이를 코드 공개 사이트인 깃허브([https://github.com/roboticsware/pgzhelperarrow-up-right](https://github.com/roboticsware/pgzhelper) )에서 공개하고 있다. 이번 절에는 그 모듈을 가져다가 활용하도록 하겠다. 현재 [커스텀 뮤 에디터arrow-up-right](https://github.com/roboticsware/mu/releases) 의 **1.2.6 버전 이상**을 사용해 코딩하고 있다면, 이미 해당 모듈을 에디터 안에 내장하고 있으므로, 위에 언급된 추가적인 모듈설치의 과정없이 곧바로 코딩을 시작할 수 있다! 그러나, 다른 에디터나 IDE를 사용하고 있다면 설치 방법은 크게 두 가지인데 [PIP(Python Package Index)arrow-up-right](https://pypi.org/project/pgzhelper-rw/) 를 이용한 설치방법과 깃허브 사이트에서 모듈을 메뉴얼로 직접 다운로드 하는 방법이 있다. 먼저, PIP를 이용한 설치방법의 경우는 다음의 명령어를 윈도우즈 사용자의 경우는 명령어 프롬프트창에서 입력하고, 맥 사용자의 경우는 터미널창에서 입력하면 된다. Copy pip install pgzhelper-rw 두번째로 직접 다운로드 설치방법을 위해서는 [이곳arrow-up-right](https://raw.githubusercontent.com/QuirkyCort/pgzhelper/main/pgzhelper.py) 을 누르면, 웹브라우저(필자의 경우, 크롬 웹브라우저)가 열리면서 모듈의 소스코드가 보일 것이고, \[파일\] 메뉴에서 \[페이지를 다른 이름으로 저장\] 메뉴를 택하고, 저장위치를 우리게임의 소스코드가 존재하는 뮤 에디터의 기본 폴더(본인 계정 아래에 mu\_code라는 폴더)에 저장해 넣도록 하자. 저장하는 방법과 최종 저장된 모습은 아래에서 확인할 수 있다. 우리가 개발 중인 게임 소스코드와 사용하려는 모듈이 같은 위치에 존재하게 하는게 필요한데 그래야만 그 모듈을 단순 import 문을 사용해 코딩할 수 있기 때문이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FoIdNnPwb6qGJkczCxtkj%252Fimage.png%3Falt%3Dmedia%26token%3D37198dd7-7f12-482b-9b89-6d1a8bd4a836&width=768&dpr=3&quality=100&sign=19f9368&sv=2) ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FUukyZr1Sgt0p4yAkQGtz%252Fpgzhelper.png%3Falt%3Dmedia%26token%3D544fc24b-ccf2-4410-883e-64240c5eec98&width=768&dpr=3&quality=100&sign=5055a313&sv=2) 코딩을 위한 준비는 끝났고, 이제 실제 코딩을 시작해 보자. 라이브러리든 모듈이던 만든이의 의도대로 사용자들이 이용하도록 사용자 설명서가 제공되는게 일반적이라고 언급했을 것을 기억한다면, 당연히 이 모듈도 사용법이 안내되어 있고, [이곳arrow-up-right](https://www.aposteriori.com.sg/pygame-zero-helper/) 에서 확인이 가능하다. 사용법이 주어졌으니 각자 읽어보고 알아서 코딩을 할 수도 있겠으나, 우리 책은 초보자를 배려하는 순한맛 버전이므로 함께 같이 코딩해보도록 하겠다. 먼저 설명서에 언급된 애니메이션 하는 방법만 극도로 간략화시킨 코드는 다음과 같고, 기존 코드를 잠시 임시로 다 주석처리 해놓고, 이렇게 11 라인짜리 코드만을 실행해보면 다음과 같이 애니메이션하는 플래피버드를 확인할 수 있다. Copy from pgzhelper import * flappy_bird = Actor('bird1') flappy_bird.images = ['bird0', 'bird1', 'bird2'] flappy_bird.fps = 10 def update(): flappy_bird.animate() def draw(): flappy_bird.draw() ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FqyEIZfffVQ8Z4uHl0uSL%252FKapture%25202024-01-24%2520at%252010.28.52.gif%3Falt%3Dmedia%26token%3D90dd32a4-ea65-43ba-b288-e1455e2bf8e0&width=768&dpr=3&quality=100&sign=14211afb&sv=2) 결국 우리는 위에 애니메이션 코드를 우리의 기존에 코드에 적절히 끼워넣으면 되는 것이고, 결론적으로 아래와 같이 최종 코딩을 할 수 있다. 이제 코딩된 내용을 차례차례 함께 이해해 보도록 하자. flappy\_bird.py Copy import random from pgzhelper import * TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 GAP = 140 PIPE_SPEED = -3 bird_alive = True score = 0 # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) flappy_bird.images = ['bird0', 'bird1', 'bird2'] flappy_bird.fps = 10 top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() screen.draw.text( str(score), color = 'white', midtop = (WIDTH/2, 10), fontsize = 70, shadow = (1, 1) ) def reset_pipes(): random_y = random.randint(-100, 100) top_pipe.y = random_y bottom_pipe.y = top_pipe.height + GAP + random_y top_pipe.x = WIDTH bottom_pipe.x = WIDTH def update(): global drop_speed, bird_alive, score drop_speed += GRAVITY flappy_bird.y += drop_speed if bird_alive == True: flappy_bird.animate() top_pipe.x += PIPE_SPEED bottom_pipe.x += PIPE_SPEED # 파이프의 무한순환 if top_pipe.right < 0 or bottom_pipe.right < 0: reset_pipes() if bird_alive == True: score += 1 # 파이프와의 충돌 if flappy_bird.colliderect(top_pipe) or flappy_bird.colliderect(bottom_pipe): flappy_bird.image = "birddead" bird_alive = False # 게임의 재시작 if flappy_bird.y > HEIGHT or flappy_bird.y < 0: flappy_bird.image = "bird1" bird_alive = True flappy_bird.center = (75, 350) drop_speed = 0 reset_pipes() score = 0 def on_mouse_down(): global drop_speed if bird_alive == True: drop_speed = -6.5 🔢 우리는 별도로 가져온 추가모듈(pgzhelper)을 우리 코드 안에서 불러다 쓰기 위해 2번 라인처럼 당연히 먼저 해당 모듈을 import를 해야할 것이다. import 문 사용이 이제 익숙해졌을 것으로 여겨지는데, 여기서는 그 import 문법의 새로운 형태로 from-import \* 문 형태를 처음으로 사용하고 있다. 그냥 **import 하는 것과 from-import \* 하는 둘의 차이는 무엇일까?** 원래 import 문으로 가져온 라이브러리(또는 패키지) 또는 모듈 안에 특정 클래스나 함수를 사용한다고 하면, 우리가 익히 알고 있듯이 해당함수의 계층적 위계의 위치정보를 포함한 (해당함수를 포함하는 상위의)라이브러리(또는 패키지, 모듈)명. 형태로 호출을 해야한다. 예들들어, 37번 라인의 randint 함수 호출의 예만 봐도 사용을 위해선 **random.** 이란 모듈명이 필요했고, 최종적으로 **random.randint** 형식으로 호출했다. 그러나, 만약 random 모듈을 from-import \* 문을 사용해 from random import \* 으로 import 했다면 사용이 어떻게 달라지는가? 이 경우는 이제 더이상 해당함수의 계층적 위계의 위치정보 없이, 곧바로 함수명인 randint 만으로도 호출이 가능하게 된다. 어떤가? 편리한 면이 있지 않은가? from-import \* 문의 일반적인 목적의 용도는 이렇고, **여기서 pgzhelper 모듈을 from-import \* 문으로 호출해야만 하는 이유는, 앞으로 기술하게 될 Actor 클래서의 추가적인 속성인 images, fps 등과 행동인 animate 함수(메소드)라던지 Actor 클래스 확장을 편리한 코딩으로 사용하기 위한 목적정도로 이해하면 될 것 같다.** 🔢 그 다음으로 우리는 모듈 사용법 예제에서 요구하는 것처럼 우리가 애니메이션에서 사용할 한장한장(프레임) 이미지의 단계적 이미지들의 **순차적인** 모음을 17번 라인처럼 리스트로 만들어 두어야 한다. 플래피버드가 하늘을 나는 동작은 총 3장의 이미지로 구성되어 있고, 그 이미지들은 우리가 [5.1절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background) 에서 images 폴더 안에 이미 복사해 넣어두었다는 것을 기억할 것이다. 그럼, 18번 라인의 fps라는 것은 무엇일까? 간단히 애니메이션이 진행되는 속도라고 생각하자. 구체적으로는 FPS(Frame Per Second)라고 해서, 1초 안에 몇 장의 단계적 이미지(프레임)를 보여줄 것인가에 관한 것인데 자세한 것을 알기 원하는 분은 [이 책의 매운맛 버전](https://jjlee.gitbook.io/python-pygame/boilerplate#animation) 에서 확인하는 것으로 갈음하겠다. 여기서는 단지 fps값이 크면 애니메이션이 빨리지고, 적으면 느려진다라고만 알면 될 것 같고, 몇 차례 실행해보면서 적당값 값을 찾으면 되겠다. 🔢 이제 남은 것은 update 함수 안에서 animate 함수만 호출하면 끝나지만, 고려할 게 예외상황이 하나 있는데 현재 코드 그대로 놔두면 플래피버드가 날아가다가 파이프에 닿아서 죽은 상태가 되도 플래피버드가 곧바로 다시 부활(?)해 연이어 날아가는 애니메이션을 수행하는 모습을 볼 수 있을 것이다. 당연히 이렇게 되서는 안되기 때문에, 47-48번 라인에서처럼 애니메이션은 플래피버드가 살아있을 때만 가능하다는 조건 안에 애니메이션 실행코드를 가두어 두어야 하겠다. 이로써 플래피버드 게임의 3가지의 추가 기능구현까지 다 마쳤다. 3가지 기능을 추가하면서 총 25라인의 코드가 더 증가해 총 76라인짜리 소스코드가 돼었다. 게임성을 더 높이기 위해 더 추가해 볼 기능들을 얼마든지 더 있다. 예를들어 화면 구석에 현재까지 얻은 가장 높은 점수을 보여줌으로써 게임에 대한 승부욕을 더 높일 수 있고, 파이프의 높낮이만 랜덤(무작위)할게 아니라, 파이프가 달려오는 속도도 게임이 진행됨에 따라 난이도를 높이기 위해 변동성을 줄 수 있을 것이고, 게임의 청각적인 재미를 위해 게임의 사운드(배경음악, 플래피버드가 죽었을 때 효과음, 플래피버드가 공중부양할 때의 효과음 등)를 넣어주는 것도 좋다. 플래피버드 게임은 사실상 몸풀기정도의 첫 프로젝트이니 더 욕심은 내지 않겠다. 위해 언급한 기능들은 추후 다른 게임프로젝트에서 더 다뤄보도록 하겠다. 그러나, 그럼에도 불구하고 지금 당장 스스로 [파이게임제로 라이브러리의 문서arrow-up-right](https://pygame-zero.readthedocs.io/) 를 참고해 시도해보겠다는 하는 분이 있다면 당연히 그 도전정신에 박수를 보낸다. 그런 도전이 여러분을 더 나은 개발자로 이끄는 것은 자명하기 때문이다. [Previous5.7 플래피버드 게임 추가기능 구현하기 (파이프 위치 랜덤화)chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.7_pipe_random) [Next6\. 블록격파(Breakout) 게임만들기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/breakout) Last updated 6 months ago --- # 5.4 화면에 파이프(오브젝트) 나타내기와 움직이기 | 파이썬-파이게임 제로 (순한맛) 이제 우리는 우리의 게임무대에서 필요한 파이프 객체 배우(Actor)들을 등장시킬 차례이다. 코드를 적기 전에 먼저 이 객체들에 대해 생각해보자. **실제 코딩을 시작하기 전 어떻게 코딩할지를 생각으로 먼저 틀을 잡는게 항상 선행되어야 하는 일인 것이다.** 일단 우리에겐 파이프 객체가 2개가 필요하다. 왜냐하면 화면 위쪽에 매달린 파이프와 화면 아래쪽에 붙은 파이프가 필요하고, 두 파이프 사이는 일정한 간격의 공간 존재해야만 하는데, 이 게임의 목적은 플래피버드가 파이프에 닿지 않고, 그 공간을 통과하는 게임이기 때문이다. 마지막으로 파이프 객체들의 움직임(애니메이션)에 대해 생각해보자. 화면 우측 끝에서 등장해 화면 좌측방향으로 움직여가며 화면 좌측 끝까지 이동하여 화면 밖을 빠져나가는 것과 동시에 다시 처음 등장 때처럼 화면우측 끝에서부터 재등장이 필요하다. 다만, 이때 게임의 재미를 위해서는 파이프는 항상 같은 위치에서 등장하면 안되고, 등장 때마다 무작위(랜덤)한위치가 변화를 동반해야만 할 것이다. 이것이 우리의 파이프 객체가 가져야 할 요구사항인 것이다. 자, 이것을 코드로 어떻게 표현할 수 있는지 살펴보자. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, 500)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() def update(): global drop_speed drop_speed += GRAVITY flappy_bird.y += drop_speed def on_mouse_down(): global drop_speed drop_speed = -6.5 🔢 10-11번 라인의 코드에서 화면 위쪽 파이프와 화면 아래쪽 _파이프 각각을 Actor 객체로 생성해 각각을 top\_pipe, bottom\_pipe의 변수에 담아놓았다(다른 표현으로는 변수에게 생성된 객체를 접근할 수 있는 참조점을 할당)_. 이전 장에서 했던 것처럼 객체를 생성했고, 생성된 객체를 draw 콜백함수 안에서 화면에 그린 것으로 이미 익숙한 코드이기 때문에 이해에 어려움이 없을 것이다. 코드를 실행시켜보면 한 가지 문제가 있는데, 상하 두 파이프가 붙어져 있는 모습으로 그려진다. 그러나, 우리 게임 무대에서의 파이프 배우(Actor)들의 요구사항은 두 파이프 사이는 어느정도의 공간이 존재하도록 그려져야 하는데 왜냐하면 그 사이로 플래피버드가 통과해야 하기 때문이다. 이제 그 부분을 코딩해 보자. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 GAP = 140 # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() def update(): global drop_speed drop_speed += GRAVITY flappy_bird.y += drop_speed def on_mouse_down(): global drop_speed drop_speed = -6.5 🔢 기존 코드에서의 변화는 2곳인데 먼저 7번 라인에 두 파이프 사이에 존재해야 하는 어느정도의 공간의 크기를 표현하는 의미로 GAP 라고 이름짓고(이전 장에서 언급한 의도적 대문자표기임을 기억할 것) , 그 공간의 크기는 140픽셀이라고 정의하였다. 두 번째로 12번 라인에서 아래 파이프의 위치를 위 파이프의 높이로부터 GAP 크기만큼 공간을 띄어(기존 위치에서 GAP 크기만큼 아래로 밀어내어) 위치시키는 코드로 변경했다. 여기서 top\_pipe.height 라는 값은 직관적으로 어떤 값일지 와닿긴 하나,즉, 상위 파이프 객체 이미지의 높이로 _라이브러리가 제공하는 모든 Actor객체마다 기본적으로 제공하고 있는 위치정보 속성값으로 그에 대한 상세는 다음과 같다._ ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FPoQQkAl6bTWt2P79cAZj%252Fimage.png%3Falt%3Dmedia%26token%3D12b00a46-4403-454f-bb24-3210e338995b&width=768&dpr=3&quality=100&sign=c9d75eaa&sv=2) 파이프들 사이에 공간이 확보되었으니 이제 그 다음 요구사항인 파이프들이 플래피버드가 존재하는 방향으로 전진하는 움직임(애니매이션)이 필요하고, [5.2절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.2_flying) 에서 배운 플래피버드를 애니메이션 시키는 방법과 크게 다르지 않아 코드이해에 어려움은 없을 것이다. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 GAP = 140 PIPE_SPEED = -3 # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() def update(): global drop_speed drop_speed += GRAVITY flappy_bird.y += drop_speed top_pipe.x += PIPE_SPEED bottom_pipe.x += PIPE_SPEED def on_mouse_down(): global drop_speed drop_speed = -6.5 🔢 8번 라인에서 파이프가 이동하는 속도는 변하지 않는 값이고, 코드의 가독성을 높이고, 추후 요구사항 변화의 빠른 대응을 위해 숫자값을 대치하는 기능([Macroarrow-up-right](https://ko.wikipedia.org/wiki/%EB%A7%A4%ED%81%AC%EB%A1%9C_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)) , 매크로라고 함)의 의미로 전체 대문자 변수명으로 적용하였고, 파이프들은 화면상의 좌측방향으로 이동해야 하므로 변수값은 음수가 되었다. 파이프 애니메이션을 만들기 위해 1초에 60번 호출되는(60 FPS(Frames Per Second)라고 함) update 함수 내에서 파이프들의 x좌표값을 계속 변화시킨다. 이 코드를 실행시켜보자. 의도했던데로 잘 동작할 것이다. 다만 단점이 하나 있다면, 좌측화면 끝까지 이동해버린 파이프들이 다시 재등장하는 것이 없다는 것이다. 우리가 엔트리에서 이런류의 게임을 만들어봤다면, 이 상황을 어떻게 대응해야하는지에 대한 알고리즘에 대해 익숙할 것이다. 그렇다 _파이프가 좌측 화면밖을 빠져나감과 동시에 파이프를 화면이 맨 우측의 눈에 보이지 않는 바깥쪽 넘어로 순간이동시켜버리는 것이다. 이렇게 하면 파이프는 무한하게 계속 순환(rotation)하는 구조를 만들 수 있다._ 이를 코드로 표현해 보도록 하자. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 GAP = 140 PIPE_SPEED = -3 # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() def update(): global drop_speed drop_speed += GRAVITY flappy_bird.y += drop_speed top_pipe.x += PIPE_SPEED bottom_pipe.x += PIPE_SPEED if top_pipe.right < 0 or bottom_pipe.right < 0: #파이프의 무한순환 top_pipe.x = WIDTH bottom_pipe.x = WIDTH def on_mouse_down(): global drop_speed drop_speed = -6.5 🔢 알고리즘은 예상하는 바와 같이 28번 라인처럼 드디어 조건문이 등장해 파이프의 이동 애니메이션하는 매순간마다의 파이프의 현재위치의 점검이 필요하다. 위쪽 파이프 또는 아래쪽 파이프 중 둘 중 하나의(**이 목적으로 비교문 안에 or 를 사용했음을 주목하자!**) 현재 위치가 화면의 좌측 빠져나간 상태(파이프의 right값 위치값이 0보다 작아지는 상태)인가? 그렇다면, 파이프들의 위치를 순간이동 시키는데 화면 최우측의 바로 넘어로(x값이 WIDTH값이 되는) 그 위치를 옮겨버리면 될 것이다. 이제 남은 요구사항은 게임의 재미를 위해 파이프의 재등장시 항상 같은 위치에서 등장하면 안되고, 등장 때마다 무작위(랜덤)한 위치변화를 동반해야만 할 것이다. 이 부분의 코딩은 일단은 뒤에 [추가기능 구현](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.7_pipe_random) 으로 미뤄두겠다. 이 책은 순한맛 버전이므로 아무래도 처음에는 최대한 난이도를 최대한 낮춰 배우는 것에 중점을 두는 것이 필요하기 때문이다. [Previous5.3 화면에 플래피버드(오브젝트)의 자연스러운 움직임 만들기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.3_flying_gravity) [Next5.5 플래피버드와 파이프의 충돌 구현하기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.5_collision) Last updated 7 months ago --- # 5.6 플래피버드 게임 추가기능 구현하기 (점수기능) | 파이썬-파이게임 제로 (순한맛) 지금부터 구현해 보는 기능은 더 나은 게임성을 위해 추가되는 내용으로 보너스 챕터로 생각해도 좋을 것이다. 코딩도 배우고 완성도 높은 게임을 원하는 사람에게 참고가 되길 바란다. 먼저 아래와 같이 플래피버드가 파이프 통과를 성공하면, 화면상단 중앙에 성공한 횟수를 점수화해서 표시되는 코드를 추가해 보자. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F4HRxM6YE7M3Z0ciFCpmo%252FKapture%25202024-01-13%2520at%252022.55.28.gif%3Falt%3Dmedia%26token%3D8fc8cd09-9c51-4dec-99b8-a5b590d1f0a1&width=768&dpr=3&quality=100&sign=ba202b30&sv=2) Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 GAP = 140 PIPE_SPEED = -3 bird_alive = True score = 0 # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() screen.draw.text( str(score), color = 'white', midtop = (WIDTH/2, 10), fontsize = 70, shadow = (1, 1) ) def update(): global drop_speed, bird_alive, score drop_speed += GRAVITY flappy_bird.y += drop_speed top_pipe.x += PIPE_SPEED bottom_pipe.x += PIPE_SPEED # 파이프의 무한순환 if top_pipe.right < 0 or bottom_pipe.right < 0: top_pipe.x = WIDTH bottom_pipe.x = WIDTH if bird_alive == True: score += 1 # 파이프와의 충돌 if flappy_bird.colliderect(top_pipe) or flappy_bird.colliderect(bottom_pipe): flappy_bird.image = "birddead" bird_alive = False # 게임의 재시작 if flappy_bird.y > HEIGHT or flappy_bird.y < 0: flappy_bird.image = "bird1" bird_alive = True flappy_bird.center = (75, 350) drop_speed = 0 top_pipe.x = WIDTH bottom_pipe.x = WIDTH score = 0 def on_mouse_down(): global drop_speed if bird_alive == True: drop_speed = -6.5 🔢 점수를 추가하는 것은 어떤 시점에 점수가 얻어지게 되는지에 대한 알고리즘만 잘 고민하면 구현자체는 크게 어려운 것은 없다. 플래피버드가 파이프를 잘 통과한 시점이 점수을 얻어야 하는 시점인데, 사실 엄밀하게 생각하면 애니메이션 중인 플래피 버드와 파이프 둘의 현재 위치를 동시에 파악해 플래피버드가 살아있는 상태로 플래피버드의 좌측면(flappy\_bird.left)이 파이프 객체의 우측면(pipe.right) 위치정보를 지났는가를 계속 검사해야 하는가 등으로 다소 복잡해 질 수도 있는데, 그렇게까지 엄밀하게 고려하지 않더라도 간략화시켜 생각해보면 플래피버드의 위치가 게임화면 좌측에서 크게 떨어져 있지 않다라는 것에 전제하여, 파이프의 이동하다가 게임화면 좌측에 도달해 다시 게임화면 우측의 초기위치로 순환하는 시점까지 플래피버드가 죽지않고 살아만 있다면, 결국 플래피버드가 파이프에 닿지 않고 무사통과했다라고 봐도 크게 무방하지 않다. 이런 단순한 알고리즘을 가져가면, 구현자체도 손쉬워지고 매순간 위치계산의 연산비교량이 증가해 게임의 퍼포먼스가 떨어질 수 있는 문제도 동시에 극복할 수 있게 된다. 이를 적용한 코드는 크게 3부분인데 먼저 10번 라인에서 score를 global 변수로 생성했고, 42-43번 라인에서 파이프가 화면빠져나갈 때까지 플래피버드가 죽지않았다면 점수를 증가시키고, 58번 라인에서 게임이 재시작하는 시점에서 다시 점수도 0부터 시작하도록 초기화했다. 🔢 이제 남은 코드는 점수를 화면에 그려주는 부분이다. 이것을 위해 라이브러리의 screen객체 안에 준비된 함수가 있는데 draw.text 함수이다. draw.text 함수의 자세한 사용법은 [라이브러리 문서arrow-up-right](https://pygame-zero.readthedocs.io/en/latest/ptext.html) 를 참조하고, 24-28번 라인에서 보는 것처럼 해당 함수에는 총 5개의 인자값(argument)을 넘겨 사용하는데, 첫번째는 화면에 출력할 텍스트 그 자체로 우리는 score 값을 그릴 건데 문제는 score의 데이터형태가 숫자값이라 텍스트형태(스트링)은 아니기 때문에, 데이터형태를 문자형으로 변경해주는 파이썬의 내장함수인 str 함수를 사용해 데이터타입을 변경한 후 값을 전달하고 있다. 두번째는 텍스트의 색깔, 세번째는 화면상에 그려질 위치로 화면의 상단의 중앙값(midtop)을 적용했고, 네번째는 폰트의 크기, 마지막 shdow 값은 그려질 텍스트에 그림자같은 것을 함께 그려주어 텍스트의 입체적 효과를 만드는 것으로 튜플형태로 넘긴 (1, 1)의 의미는 그림자가 원본으로부터 x, y 방향으로 얼마나 떨어져 그려져야 하는지에 대한 값을 넘긴 것이다. 여기까지 잘 구현되었다면 이번 챕터 맨 처음의 게임실행화면 예시처럼 파이프 통과시 화면상단에 점수(score)가 잘 증가하며 보여지는 것을 확인할 수 있다. 이제 다음으로 게임성을 높이기 위해 구현해 볼 것은 매번 파이프의 등장마다 위치를 랜덤화 시켜 게임의 흥미를 높이는 것이 남아있다. [Previous5.5 플래피버드와 파이프의 충돌 구현하기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.5_collision) [Next5.7 플래피버드 게임 추가기능 구현하기 (파이프 위치 랜덤화)chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.7_pipe_random) Last updated 1 year ago --- # 5.7 플래피버드 게임 추가기능 구현하기 (파이프 위치 랜덤화) | 파이썬-파이게임 제로 (순한맛) 만일 당신이 [엔트리-파이썬 책](https://jjlee.gitbook.io/entry-python/) 부터 잘 따라왔다면 랜덤화(무작위화)는 낯선 것은 아니다. 엔트리-파이썬 책에 [무작위 수 챕터](https://jjlee.gitbook.io/entry-python/basic_syntax/3.7-random) 에서 이미 다룬적이 있기 때문이다. 거기에서 엔트리-파이썬과 오리지널 파이썬에서 약간의 차이가 있다고 언급했던 부분 있는데 그것은 바로 랜덤기능을 코딩하기 위해서는 실제 파이썬의 경우, 랜덤(random)이라는 모듈을 불러오는 부분이 선행되어야 한다고 언급했었는데 그 부분부터 시작해 보도록 하자. 따라서 우리는 **오리지널 파이썬이기 때문에 랜덤을 활용하기 위해 코드의 맨 첫번째 라인에서 random 모듈을 import하는 것부터 시작하자.** 우리의 코드 안에서 파이프 위치가 랜덤화 되어야 할 때는 언제인가? 적어도 최소한 2번의 시점에서 필요한데, 첫번째로 파이프가 화면 좌측으로 빠져나가면서 우측화면부터 재등장해야하는 시점에서 필요하고, 두번째로 게임이 첨부터 재시작하는 시점에서도 파이프의 위치가 무작위로 등장하면 좋을 것이다. _**이렇게 적어도 2번 이상 같은 코드가 활용될 것으로 예상되기 때문에 이런 경우, 이 부분을 함수화하는 것을 고려하면 좋을 것이다.**_ Copy import random TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 GAP = 140 PIPE_SPEED = -3 bird_alive = True score = 0 # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() screen.draw.text( str(score), color = 'white', midtop = (WIDTH/2, 10), fontsize = 70, shadow = (1, 1) ) def reset_pipes(): random_y = random.randint(-100, 100) top_pipe.y = random_y bottom_pipe.y = top_pipe.height + GAP + random_y top_pipe.x = WIDTH bottom_pipe.x = WIDTH def update(): global drop_speed, bird_alive, score drop_speed += GRAVITY flappy_bird.y += drop_speed top_pipe.x += PIPE_SPEED bottom_pipe.x += PIPE_SPEED # 파이프의 무한순환 if top_pipe.right < 0 or bottom_pipe.right < 0: reset_pipes() if bird_alive == True: score += 1 # 파이프와의 충돌 if flappy_bird.colliderect(top_pipe) or flappy_bird.colliderect(bottom_pipe): flappy_bird.image = "birddead" bird_alive = False # 게임의 재시작 if flappy_bird.y > HEIGHT or flappy_bird.y < 0: flappy_bird.image = "bird1" bird_alive = True flappy_bird.center = (75, 350) drop_speed = 0 reset_pipes() score = 0 def on_mouse_down(): global drop_speed if bird_alive == True: drop_speed = -6.5 🔢 위에서 언급했던 것처럼 33~38번 라인에서 그 부분을 reset\_pipes 라는 이름으로 함수화 하였다. 그리고, 해당 함수를 호출은 위에서 언급한 바로 그 두 시점(게임의 재시작, 파이프의 무한순환)에서 호출하고 있다. 이제 이 함수코드를 자세히 이해해 보자. 먼저 랜덤값(무작위값)을 얻기 위해 random 모듈 안에 있는 [randintarrow-up-right](https://docs.python.org/3/library/random.html#random.randint) 함수를 호출하는데 이 함수는 이름에서 알 수 있듯이 램덤으로 생성할 정수값(integer)의 최소값과 최대값의 범위를 파라미터로 갖는다. 우리는 randint(-100, 100) 이라고 호출했는데 이 의미는 -100 ~ 100 사이의 랜덤한 정수값을 얻겠다는 것이고, 더 궁극적인 의미는 파이프들의 위치를 그 범위 사이에서 위아래로 랜덤(무작위)하게 움직이게 하려는 목적에서다. 35-36번 라인의 코드에서 확인할 수 있듯이, 기존 top\_pipe.y 와 bottom\_pipe.y 값에 파이프들의 기존 기본 위치에 랜덤값(random\_y)을 추가로 덧붙혀 줌으로써 두 파이프는 일제히 랜덤값이 반영된 최종 위치를 갖게 되는데, 결과적으로 만약 랜덤값이 음수값 일 경우는 기존 원래 위치보다 더 위쪽으로, 양수값 일 경우는 더 아래쪽으로 이동하게 된다. 이제 마지막으로 게임성을 높이기 위해 구현해 볼 것은 플래피버드가 하늘을 날 때, 날개 펄럭임의 애니메이션을 추가해 게임의 그래픽 퀄러티 수준을 높히는(?) 것이 남아있다. [Previous5.6 플래피버드 게임 추가기능 구현하기 (점수기능)chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.6_score) [Next5.8 플래피버드 게임 추가기능 구현하기 (플래피버드 애니메이션)chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.8_animation) Last updated 1 year ago --- # 7.2 배우들의 움직임과 총알 공격 구현하기 | 파이썬-파이게임 제로 (순한맛) 이번 절부터는 이전 절에서 전체적인 구조적 윤곽을 잡으면서 구현체 없이 빈 함수로 만들어 두었던 각 함수들의 구현체를 채우도록 하겠다. 제일 첫번째로 **move\_player** 함수다. 기존 게임들에서는 마우스로만 조작했던 게임이라 키보드 조작에 대해서는 처음 등장하고 있다. _파이게임제로의 편리한 점은 키보드 조작에 대해 on\_key\_down 콜백함수를 꼭 쓰지 않더라도, update 콜백함수 안에서 특정 키보드의 키들(예를들어 방향키)에 대해 사전 정의된 변수들(예를 들어,_ _**keyboard. up/down/left/right**_ _등)을 통해 키의 눌림을 검사할 수 있다는 것이다._ 각 방향키의 눌림에 따라 5픽셀 값 만큼의 이동을 만든다. 주인공 비행기는 전후좌우 사방으로 이동이 가능하나 단, 주어진 화면을 넘어서까지의 이동은 막도록 경계를 두는게 필요하다(10~17라인). 이제 이정도 수준의 코딩은 이미 이전 게임들에서 많이 해봤기 때문에 이해나 구현에는 큰 어려움이 없을 것이다. Copy def move_player(): if keyboard.right: player.x += 5 if keyboard.left: player.x -= 5 if keyboard.up: player.y -= 5 if keyboard.down: player.y += 5 if player.right > WIDTH: player.right = WIDTH if player.left < 0: player.left = 0 if player.bottom > HEIGHT: player.bottom = HEIGHT if player.top < 0: player.top = 0 그 다음, 주인공이 총알발사는 부분인 **shoot\_bullets** 함수를 구현하자. 먼저, 기억해야 할 것은 총알도 크기가 작고 사소해 보여도 총알 한개한개가 다 객체라는 것이다. 사용자가 스페이스바를 눌러 총알을 발생할 때 발생할 수 있는 모든 일련의 작업순서를 생각해보면, 먼저는 총알 발사효과음 내고(3번 라인), 총알 객체를 생성하되(4번 라인), 그 총알의 발사 최초위치는 주인공 비행기에서부터 시작되야 하고(5번 라인), 총날이 날아가야 하는 방향의 각도는 상단전방이다(6번 라인). 총알이 15픽셀씩 이동해 날아가되(10라인), 만약, 날아가던 총알이 화면상단 밖으로 넘어가면, 그 총알은 더 이상의 존재의미가 없기 때문에 계속 메모리에 자리를 차지하면서 메모리를 고갈시키지 않도록(참고로 메모리가 full로 다 차면, 프로그램이 갑자기 강제종료될 수 있음) 그런 총알객체는 메모리에서 제거시킨다(11-12번 라인). 메모리에서 제거시키다는 것이 결국 총알객체를 모아놓은 모음인 bullets 리스트 객체 안에서 해당 총알객체를 삭제한다는 것이고, 이를 위해서 리스트 내장메소드 중에 **remove** 메소드를 사용하면 편리하다(12번 라인). 1회 발사되는 총알개수를 MAX\_BULLETS 으로 왜 제약하는지에 궁금할텐데, 그 총알개수 최대값을 검사하는 코드를 제거하고 게임을 실행시켜 보면 왜 그게 필요한지 단번에 이해할게 될 것이다. 우리는 스페이스 바를 아주 잠깐 1회 누른다고 생각하지만, 사실은 update 콜백함수가 초당 60번이나 실행되는(60 FPS) 엄청나게 빠른 속도로 반복해서 불리기 때문에, 스페이스 바가 계속 눌린 상태로 인식되어 생성 총알개수가 순식간에 늘어나 수많은 총알이 한번에 날아가는 것을 볼 수 있다. 이러한 현상은 우리가 적들에게서 지나친 공격적 우위를 점하게 되고, 이는 게임의 난이도를 너무 쉽게 만들어 게임의 재미를 반감시키기 때문에, 게임성을 위해서는 1회 발사되는 총알량을 어느 정도 제한하는 것이 필요하다. 6번 라인에서 등장한 angle 속성값은 우리가 [이전 서](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/2-make_flower) 에서 이미 익혔던 내용으로, 90도 값을 설정한 것은 화면 상단방향으로 총알이 움직이게 하려는 목적이다. 만약, [이전 서](https://jjlee.gitbook.io/python-pygame_zero/) 를 거쳐오지 않은 독자가 있을 수 있어 아래에 angle 값의 의미에 대한 추가 설명 이미지를 첨부한다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FDiMlU6F3CJ2CQFoimJsZ%252Fimage.png%3Falt%3Dmedia%26token%3D8a7dc6d9-f3bb-4056-91dc-e109deb62d84&width=768&dpr=3&quality=100&sign=13d64024&sv=2) Copy def shoot_bullets(): if keyboard.space and len(bullets) < MAX_BULLETS: sounds.sfx_sounds_interaction25.play() bullet = Actor("player_bullet") bullet.pos = player.pos bullet.angle = 90 bullets.append(bullet) for bullet in bullets: bullet.move_forward(15) if bullet.y < 0: bullets.remove(bullet) 그 다음으로는 적 비행기들을 출현시켜는 함수는 **create\_enemies** 를 구현해보자. 적 비행기가 출연하고 이후 공격을 위해 총알을 쏘는 모든 일련의 작업순서를 생각해보면, 먼저는 적 비행기는 눈에 보이지 않는 화면 상단에서(6번 라인) 랜덤하게 생성해(7번 라인) 화면 아래방향으로 랜덤하게(8번 라인) 날아가도록 해야할 것이다(12번 라인). 8번 라인에서 [이전 서](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/2-make_flower) 에서 우리가 이미 익혔던 angle과 거의 유사하나 개념이 조금 다른 **direction** 이라는 속성값이 등장했는데, 엔트리 블록코딩 거쳐온 독자라면 이미 익숙할텐데 엔트리에 방향/이동방향이란 두 가지 값이 존재했었고, 두 값의 차이는 실제 오브젝트 자체를 회전시키느냐와 오브젝트 자체에 대한 회전이 아닌 오브젝트가 움직일 때의 그 움직임의 방향만 바꾸냐의 차이가 존재했다. 여기서 direction은 후자의 경우이다. 파이게임제로에서 Actor객체의 angle 속성값을 설정할 때의 각도와 direction 속성값을 설정할 때의 각도는 동일한 의미를 갖기 때문에 코드에서 random.randint(-100, -80) 의 의미는 화면 정하단방향(-90도) 주변으로 10도씩 약간의 랜덤한 움직임을 주려는 목적인 것이다. 적 비행기가 날아갈 때, 애니메이션을 가미하여(4번, 13번 라인) 게임성을 더했다는 것도 기억해두자. 파이게임제로에서 Actor객체의 애니메이션을 만드는 법에 대해선 [이전 서](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/5-count_boy) 에서 이미 학습했기 때문에 자세한 설명은 하지 않겠고, 추가 학습이 필요한 분은 이전 서의 내용을 참조하면 좋겠다. 적 비행기는 가만히 날아가는 것이 아니고, 날아가면서 총알공격을 하기 때문에 이 부분을 구현해야 하며, 쏜 총알이 날아가는 방향은 360도 전방위로 랜덤하게(19번 라인), 5픽셀 속도로 날아가게(23번 라인) 하였다. 그밖에 적 비행기든 총알이든 화면 밖을 빠져나가게 되면, 메모리에서 제거하는 코드는 여전히 항상 필요하다 하겠다(14-15번 라인, 24-25번 라인). Copy def create_enemies(): if random.randint(0, 1000) > 980: enemy = Actor("enemy1_1") enemy.images = ["enemy1_1", "enemy1_2"] enemy.fps = 5 enemy.y = -50 enemy.x = random.randint(100, WIDTH - 100) enemy.direction = random.randint(-100, -80) enemies.append(enemy) for enemy in enemies: enemy.move_in_direction(4) enemy.animate() if enemy.top > HEIGHT: enemies.remove(enemy) if random.randint(0, 1000) > 990: bullet = Actor("enemy_bullet") bullet.pos = enemy.pos bullet.angle = random.randint(0, 359) enemy_bullets.append(bullet) for bullet in enemy_bullets: bullet.move_forward(5) if bullet.x < 0 or bullet.x > WIDTH or bullet.y < 0 or bullet.y > HEIGHT: enemy_bullets.remove(bullet) 나머지 2개 함수(check\_collision, draw\_text)에 대해서는 다음 절에서 계속 설명을 이어나가도록 하겠다. 만약, 공부하다가 코드에 대한 추가적인 문의가 생긴다면, 함께 [소통할 채널arrow-up-right](https://discord.com/channels/1201625272634576936/1201632809618509904) 을 준비해 두었으니 언제든 그곳을 통해 의문점을 함께 해결해 나갈 수 있다는 것을 기억하자. [Previous7.1 스크롤 배경객체 만들기, 배경음, 배우(적, 주인공) 등장시키기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.1_scroll_backgrounds) [Next7.3 충돌처리 및 기타정보(점수 및 게임종료) 표기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.3_collision_detection) Last updated 5 months ago --- # 8.4 객체지향 개발이론 (사용자 정의 객체 만들기, 상속) | 파이썬-파이게임 제로 (순한맛) 우리는 구체적인 객체지향 설계에 앞서 객체 자체에 대해서 더 심도 있게 알아야 할 필요가 있는데 지금까지는 남들이 만들어 놓은 객체(오브젝트)를 사용하는 코딩만 해봤지만, 이제는 직접 나만의 객체(사용자 정의 객체: User Defiined Object)를 만들어서 활용하는 법을 통해 객체를 더 심도 있게 이해해 보자. 우리에게 익숙한 실물세계의 객체 하나를 예로 가져와 이를 프로그래밍 세계에 맞게 객체화 시켜보자. 실물의 자동차를 프로그래밍 세계의 자동차 객체로 변환해보자. 객체화 할 때는 먼저, 한 객체 안에 존재하는 **속성(attribute)**/**행동(behavior)**이란 두 가지의 관점으로 객체를 바라봐야 한다. 속성이란 자동차 그 자체가 가진 속성으로 예를 들어 일반의 모든 자동차가 가질 수 있는 공통적인 특징으로 차체의 모양(body), 엔진의 종류(engine), 바퀴의 종류(wheels) 등이 **속성이 될 수 있고, 이는 객체 안에서 변수값으로 표현된다.** _사실 자동차의 특징은 그 밖에도 수많은게 있으나, 모든 것을 다 표현할 필요는 없고, 우리 프로그램의 목적성취에 필요한 것들만 취하면 된다._ 이번엔 자동차가 수행할 수 있는 행동들에 대해 생각해보면, 자동차가 할 수 있는 행동으로는 달리거나(drive), 멈추거나(brake), 회전하거나(turn) 등의 **행동을 할 수 있으며 이는 객체 안에서 메소드(객체 안에 존재하는 함수를 지칭하는 이름)로 표현된다.** _마찬가지로_ _자동차가 할 수 있는 행동으로는 그 밖에도 다양하게 더 있을 수 있으나, 모든 것을 다 표현할 필요는 없고, 우리 프로그램의 목적성취에 필요한 것들만 취하면 된다._ 달리거나 멈추거나 회전하거나의 행동의 결과에 따른 현재 자동차의 상태(state)를 표현하려면 추가로 (현재)속도(speed), (현재)방향(direction)에 대한 속성값들도 추가로 필요하겠다. _이처럼 처음 자동차의 특징들을 통해 속성들을 추출했을 때는 속성으로 파악되지 않았던 것들이 행동들을 추출하는 시점에서 특정 행동의 결과로 인한 현 자동차의 상태를 파악하기 위한 목적으로 추가적인 속성들이 인식되어 추가되는 것도 자연스러운 현상이다._ 그럼, 이제 이를 코드로 표현해보자. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F9HhVZM86cABXUBcQzYm6%252Fimage.png%3Falt%3Dmedia%26token%3Dd49a5afd-abbc-4fe6-9b78-a78c0e0ff42a&width=768&dpr=3&quality=100&sign=a0b9919f&sv=2) [https://www.youtube.com/watch?v=X3cFiJnxUBYarrow-up-right](https://www.youtube.com/watch?v=X3cFiJnxUBY) ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong/oop_inheritance#define) **객체 정의(define) 문법** > **class** 클래스 이름_(상속받을 클래스 이름들)_: > > _super().\_\_init\_\_(파라미터들)_ > > **def** \_\_init\_\_(self, 파라미터들): > > 속성들 > > 메소드들 우리는 함수를 만들 때, **def** 라는 키워드를 사용해 정의(define) 것처럼, 객체를 만들 때는 **class** 키워드를 사용해 정의해야 한다. 문법 중에 녹색으로 이탤릭체로 표현된 부분의 객체의 상속(Inheritance)에 관한 문법으로 이에 대해서는 추후 다루기로 하고 당장은 무시해도 된다. Copy class Car: def __init__(self, body='general', engine='general', wheels='general'): self._speed = 0 self._direction = 'forward' self._body = body self._engine = engine self._wheels = wheels def drive(self): self._speed += 2 print("Current speed is " + str(self._speed)) def brake(self): self._speed -= 1 if self._speed < 0: self._speed = 0 print("Current speed is " + str(self._speed)) def turn(self, direction): self._direction = direction print("Changed direction to " + str(self._direction)) 객체정의 문법에 맞춰 Car 라는 객체를 정의했는데, 위에서 언급된 속성 5개는 3-7라인에 걸쳐 변수형태로 존재하는 것을 파악했을 것이고, 위에서 언급된 3가지 행동에 대해선 함수형태로 존재하는 것을 파악했을 것이다. 그런데, **\_\_init\_\_** 이라는 메소드는 기존에 못보던 형태의 낯선 함수라 무엇인지 궁금할텐데 **이는 파이썬의 일종의 내장함수로 객체의 속성값들을 초기화 하기 위한 특별한 목적으로 클래스 안에서 사용되는 것**_으로 나중에 해당 객체를 사용하는 시점에서 해당 객체생성시 파이썬 안에서 자동호출되는 콜백 메소드_이다. 그럼, 또 **self** 는 무엇인가? self의 역할은 나중에 객체가 생성된 시점에 실제 메모리 안에 **생성되어 존재하는 객체(인스턴스(Instance)) 그 자신**를 가르키게 된다. 아직은 이게 무슨 말인지 한번에 와닿지 않을 수 있는데, _복잡하게 여기지 말고 단순히 구분자의 역할로 생각해, self가 있으면 객체 자신 안의 속성값과 메소드들을 나타내고, self가 없으면 일반변수와 일반함수로 구분 용도로 여기는 것도 가능하다._ 속성 부분을 좀 더 살펴보자. 속도(speed)와 방향(direction) 속성을 나타내는 변수에 **\_(언더바(underbar) 또는 언더스코어(underscore)라 읽음)**를 추가했음을 알 수 있다. 이미 과거에 여러 차례 언급되었지만, 코딩에서 점하나 공백하나 조차도 다 의미부여가 있기 때문에, 이것도 어떤 의도가 있으리라 유추할 수 있다. 당연히 이는 의도성이 있는 것이며, 파이썬 언어에서 객체지향 구현시 관습적인 약속으로 이 속성은 특별한 객체속성들로 **외부(여기서 외부는 객체의 이용자를 지칭)에서 저 속성값의 존재를 구지 알 필요가 없는 객체 내부용도로만 사용하는 속성이며 객체 이용자가 이 값을 직접 바꾸려는 시도를 거부한다라는 의미를 표현하기 위해 언더스코어를 붙인 것**이다. (객체지향 이론에서 이를 **private** **속성**이란 용어로 지칭) \_\_init\_\_ 메소드 안에 이 속성값들을 초기화하는 부분을 좀 더 살펴보면, \_\_init\_\_ 메소드의 파마리터(body, engine, wheels) 로 얻은 값을 가지고서, 속성값들을 초기화 하고 있다. 이 말은 이 Car객체를 이용하려고 누군가 생성하는 시점에 인자값으로써 이 값들을 넘겨주고, 그 값을 내부에서 다시 이용하기 위해 **self.**를 붙혀 객체 속성값으로 설정해 놓는다는 것이다. 그외 나머지 메소드들은 코드해석에 크게 어려움은 없을 것으로 보인다. _drive를 계속 호출하면, 속도를 +2씩 증가하는 것이고, 반대로 brake를 계속 호출하면 속도를 -1씩 감소시키는 것이고, turn을 호출하면 방향을 전환하는 것이다._ ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong/oop_inheritance#undefined) 객체의 이용 객체를 만들었으니, 자 이제는 이를 이용해 보자. 자동차를 가속시켰다가 속도를 줄여 좌회전 한 후 다시 가속시키는 코드이다. 객체를 만드는게 처음이지 이용은 이미 많이 해봤기 때문에 큰 어려움은 없을 것이다. Copy car = Car() for _ in range(3): car.drive() for _ in range(2): car.brake() car.turn('left') for _ in range(2): car.drive() Copy 실행결과: Current speed is 2 Current speed is 4 Current speed is 6 Current speed is 5 Current speed is 4 Changed direction to left Current speed is 6 Current speed is 8 1번 라인에서 최초 객체를 생성할 때 아무 인자값도 넘기지 않고 생성했다. 그런데도, 실행시켜보면 아무 에러도 발생하지 않고, 실제 객체 안에 \_\_init\_\_ 메소드에 의한 속성값들의 초기화도 문제없이 진행되었다 어찌된 일인가? 그 비밀은 바로 \_\_init\_\_ 메소드를 정의할 때 파라미터들의 기본값(default값) 사전 설정해 놓는 문법이 있고, 이를 사용했기 때문이다. Copy def __init__(self, body='general', engine='general', wheels='general'): 바로 이 부분인데 3개의 파마리터값들(body, engine, wheels) 각각에 기본값으로 'general' 이라는 값을 설정해 놓았기에 사용하는 측에서 특별히 인자값을 넘기지 않을 경우, 이 값들이 자동으로 사용된다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong/oop_inheritance#undefined-1) 객체의 상속 객체의 이해를 좀 더 심화시켜보자. 이제 본격적인 객체지향이 가진 놀라운 능력들이 등장하기 시작한다. 지금까지 우리는 간단한 일반적 자동차 객체를 만들어봤다. 이제는 이 기본적인 자동차를 베이스(base) 삼아 이를 파생시킨 다음과 같이 더 다양한 종류의 자동차를 만들 수 있으면 좋지 않을까라는 생각이 들기 시작한다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FrsQx1PUslah2BKbpdt1b%252Fimage.png%3Falt%3Dmedia%26token%3Dab090ed6-cf67-40c2-9eaa-cbd43c2c8893&width=768&dpr=3&quality=100&sign=dd03a688&sv=2) [https://www.youtube.com/watch?v=X3cFiJnxUBYarrow-up-right](https://www.youtube.com/watch?v=X3cFiJnxUBY) 이게 가능하다고? 그렇다 가능하다. 객체지향은 탄생배경 자체가 기존 절차지향형의 한계를 극복한 소프트웨어 개발의 효율성과 확장성 증대에 있었으니 말이다. 이를 적용하기 위해서는 **객체간의 상속(Inheritance)에 대한 이해가 필요하다. 크게 어려운 개념은 아니고, 부모-자식간에 DNA의 유전처럼 자녀는 부모의 DNA를 물려받으면서 동시에 자신만의 독특성을 갖게 되는 것과 유사하게, 부모객체(Parent 또는 Super객체라 호칭) 를 상속한 자녀객체(Children 또는 Sub객체라 호칭)는 부모의 모든 것(속성/행동)을 다 물려받아 자기 것처럼 쓸 수 있고, 동시에 자신만의 속성/행동도 갖게 되는 것이다.** 기본 베이스 자동차에서 파생할 수 있는 여러 자동차들 중에서 우리는 가속력이 뛰어난 노란색 스포츠카와 뚜껑이 열리는 기능이 추가된 빨간색 컨버터블 자동차를 만든다고 가정해 보자. 이러한 베이스를 기반한 상속관계를 코딩으로 표현하면 어떻게 될까? ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FKiu1THftXopeONIkbqZK%252Fimage.png%3Falt%3Dmedia%26token%3D16e52b3e-7762-49c7-8010-0ff7cd2c65ca&width=768&dpr=3&quality=100&sign=111f55a8&sv=2) [https://www.youtube.com/watch?v=X3cFiJnxUBYarrow-up-right](https://www.youtube.com/watch?v=X3cFiJnxUBY) Copy class SportsCar(Car): def __init__(self, color): super().__init__('sports', 'strong', 'big') self._color = color def drive(self): self._speed += 4 print("Current speed is " + str(self._speed)) def brake(self): self._speed -= 2 if self._speed < 0: self._speed = 0 print("Current speed is " + str(self._speed)) class ConvertableCar(Car): def __init__(self, color): super().__init__('convertable') self._color = color self._opened = False def move_roof(self): self._opened = not self._opened if self._opened: print("Current roof is opned") else: print("Current roof is closed") 이를 코딩하기 위해선 위에 문법표현에서 잠시 미뤄두자고 했던 객체 생성 문법에서 상속관련 문법(녹색의 이텔릭체 부분) 이 더해진 것을 기억하자. 1번, 17번 라인에서 파생객체(상속받은 객체를 지칭하는 용어)를 만들 때, 부모(여기서는 Car)는 누구였는지 괄호 안에 표기해 만들게 된다. 그리고, 이후에 객체 속성값 초기화 메소드인 \_\_init\_\_ 안 에서 제일 먼저는 **super().\_\_init\_\_() 라는 메소드를 사용해서 부모의 것(body, engine, wheels)은 부모에게 넘기는 방식으로 부모객체의 속성값을 초기화**하고 있다. _순서적으로 부모쪽이 먼저 준비(초기화)되고, 이후에 그걸 다 물려받아 사용할(상속받은) 내쪽이 그다음에 초기화되는게 논리적으로도 맞는 것이다._ 여기서, **super() 의 용도를 짐작했겠지만, 이전의 self와 유사한 용도로 객체 안에서 사용하는 파이썬 내장함수 같은 것으로 자신의 부모클래스(Parent)의 객체를 리턴해주는 특별 메소드**인 것이다. 스포츠카는 기본적으로 베이스 자동차인 부모의 모든 것을 다 물려받을 것이고, 그밖에 자기만의 특성이라고 할만한 속성들은 무엇이 있을까? 우리가 만들려고 하는 것이 노란색 스포츠카라고 했던 점을 기억한다면 이 스포츠카는 차체색깔(color)이란 별도 속성을 가져야 할 것을 예상할 수 있다(4번 라인 참조). 또 기존에 부모와의 차이점은 무엇이 있는가? _부모와 똑같은 행동으로 달리고(drive), 회전하고(turn), 감속할 수 있는데(brake) 차이가 있다면, 차량 경주와 같은 다이나믹한 운전경험을 보장하기 위해 보다 떠빨리 달리고(drive), 더 빨리 감속할 수 있는 것(brake)에서 차이를 갖고 있다._ 따라서, **이미 부모에게 있는 행동들이라 그대로 물려받을 수 있음에도 불구하고, 이미 부모에 존재하는 메소드들인 drive, brake를 자식은 스포츠카 클래스 안에서 다시 재기능구현** 한 부분이다. 이 의미는 자식 입장에서 부모의 것이 그대로 쓰기에는 자기에 맞지 않아 부모 것이 아닌 따로 내꺼를 쓰겠다 라고 의도하는 것이다. 객체지향에선 이를 **오버라이딩**(0verriding) 이란 용어로 지칭하는데 이 단어뜻 그대로 기존 것을 덮어씌었다는 의미이다. drive와 brake 메소드 안에 구현부분을 자세히 보면 좋겠다. 7번과 11번 라인에서 self.speed라는 속성이 어디서 왔을까? \_\_init\_\_ 메소드 안에서 해당 속성값을 한번도 생성한 적이 없지만, 마지 원래 자기 것처럼 사용하고 있는데, 그렇다 _그 속성값은 본래 부모의 것이나, 부모의 모든 것을 상속받은 자녀입장에서 자신에게 없음에도 마치 자신에게 이미 존재했던 것처럼 사용하고 있는 것이다._ 스포츠카의 부모-자녀 상속관계를 잘 이해했다면, 컨버터블카의 경우는 너무 싶다. 마찬가지로 부모의 모든 것을 다 물려받았고, 자기만의 특성이라고 할만한 것은 무엇이 있을까? 우리가 빨깐색 컨버터블이라고 했기때문에 마찬가지로 차체색깔(color)을 가져야 할 것이고, 컨버터블 차라고 불리는 이유가 되는 고유한 특성은 바로 차 지붕을 열고/닫을 수 있는(move\_roof) 행동을 할 수 있다는 것이다. 이를 상세구현하면 다음과 같다. 먼저, 17번 라인에서 ConvertibleCar(Car) 문법을 통해 Car의 상속을 받고, 위에서 정리한 속성과 행동면에서 부모와는 다른 자신만의 특성들에 대해서만 추가 코딩을 진행하면 되는 것이다. 컨버터블카가 과연 부모로부터 모든 것을 다 물려받은 것이 사실인지 알 수 있는 부분은 어디일까? 다음의 파생클래스의 기능점검 테스트 코드 중에 15, 17, 18, 20 라인에서 drive, brake, turn 메소드는 컨버터블 클래스 안에서 정의한 적 없음에도 마치 자신에게 이미 존재하는 것처럼 사용되고 있다. Copy # 스포츠카 테스트 s_car = SportsCar('yellow') for _ in range(3): s_car.drive() for _ in range(2): s_car.brake() s_car.turn('left') for _ in range(2): s_car.drive() # 커버터블카 테스트 c_car = ConvertableCar('red') c_car.move_roof() for _ in range(2): c_car.drive() for _ in range(2): c_car.brake() s_car.turn('right') for _ in range(2): c_car.brake() c_car.move_roof() Copy 실행결과: Current speed is 4 Current speed is 8 Current speed is 12 Current speed is 10 Current speed is 8 Changed direction to left Current speed is 12 Current speed is 16 Current roof is opned Current speed is 2 Current speed is 4 Current speed is 3 Current speed is 2 Changed direction to right Current speed is 1 Current speed is 0 Current roof is closed 여기까지해서 우리는 객체를 어떻게 만들고, 객체의 활용성을 높이는 객체의 특성 중 가장 기본이 되는 특성인 상속 정도를 이해했다. 그럼에도 불구하고 만약 당신에게 객체지향 개발이 처음이었다면 이해가 쉽지 않았을 수 있다. 그러나 걱정할 필요가 없다. 누구에게나 익숙해지는데는 시간이 필요하기 때문이다. 사실은 객체지향의 세계는 상당히 방대하고 넓다. 그런데, 일부러 여러분들이 처음부터 방대한 지식의 양으로 지치버리면 곤란하기 때문에 당장 필요한 수준에서 이해시키는 것으로 끝내려 한다. 이후에 또다른 개념적 이해가 필요한 부분이 나오면 그 부분에서 계속 다음 지식을 이어 설명하는 방식으로 진행할 예정이다. 지금까지 우리가 해오던 방법대로 말이다. 자, 그럼 다음 절부터는 본격적으로 기존의 절차지향형으로 개발한 퐁 게임을 예제 삼아 이를 객체지향으로 구현해 보는 것을 시작해 보자. [Previous8.3 객체지향 개발이론 (객제지향 디자인이란)chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong/ood) [Next8.5 객체지향으로 개발하기 1chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong/8.5_oop_dev1) Last updated 1 year ago --- # 6.4 충돌검사시 고려할 것들 | 파이썬-파이게임 제로 (순한맛) 당신은 이전 절 끝에서 언급했던, 현재까지 구현한 게임의 그 특정(?) 버그(bug)를 발견했는가? 두 가지 정도 발견했을 것 같다. 두 가지 다 반사판의 양 옆 끝부분(edge)로 공을 받았을 때 발생하는 현상인데 첫번째는 엄밀하게 반사판이 공을 받아낸게 아님에도(공이 반사반에 닿지도 않았는데) 마치 정상 받아낸 것처럼 동작하는 현상이 하나 있다. 원인을 찾기 위해 공의 이미지의 실체에 대해 생각해보자. 그래픽에디터를 통해 직접 이미지를 제작해 본 경험이 있는 사람은 더 잘 이해할 것이다. _공 이미지는 게임상에서 우리 눈에 보이긴 원(circle)으로 보이나 실제 원은 아니고, 원래 맨 처음엔 가로세로 32x32px(픽셀) 크기의 정사각형 형태에 모든 픽셀이 꽉들어차 있고, 사람 눈에 보여야 하는 픽셀은 실제 사람 눈에 보이는 가시적인 색깔을 칠하게 되고, 사람 눈에 보이지 말아야 하는 픽셀(특별히 공의 네 귀퉁이를 둥글게 깎은 부분으로 체스판처럼 보이는 부분)은 투명처리색(transparent)으로 표시(색칠)하게된다. 따라서, 그 실체는 본래 사각형이나, 원처럼 보이게하기 위한 눈속임 정도로 생각할 수 있다._ ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FLxvNdH2vNm7By3wHlVsS%252Fimage.png%3Falt%3Dmedia%26token%3D5c4a0554-e8c3-41ca-a2ed-99d7c91a386e&width=768&dpr=3&quality=100&sign=9349b829&sv=2) 따라서 **우리 게임에 등장하는 배우(Actor)들의 원래 이미지는 사각형(rectangle)였던 것이고, 게임에서 말하는 이 이미지들간에 서로 닿았는가의 충돌(collision)이라는 의미는 아래 예시처럼 이 각각의 이미지들의 사각형 영역이 서로 닿았는가라는 것으로 판단(충돌검사)하는 것**이었기 때문에, 아래의 예시처럼 내부적으로 실제 프로그램 상에는 닿은게 맞지만, 우리 눈에는 안닿은 것처럼 보여서 오동작처럼 보였던 현상이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FoT6pVZoLa77PkvfEJcvT%252Fimage.png%3Falt%3Dmedia%26token%3Dc96c5d8a-96ab-4fa2-8b97-0aa40d7d0ce6&width=768&dpr=3&quality=100&sign=83abe59f&sv=2) 이 충돌검사하는 기존 코드를 다시한번 살펴보면 왜 코딩에 사용한 메소드(객체소속 함수)이 이름이 **colliderect (collide + rectangle)** 였는지를 이제 이해할 수 있을 것이다. Copy if ball.colliderect(bar) == True: 자 그럼 이제 이 부분을 어떻게 처리하면 오동작처럼 보이지 않고 우리 눈에 자연스럽게 보일 수 있을까? 몇 가지 아이디어가 있을 수 있을 것 같다. 제일 첫번째로 생각해 볼 수 있는게 **사각형(rect)**끼리의 충돌검사가 아닌, 만약 우리 눈에 보이는 이미지가 **원형(circle)**에 더 가까우면 아래 그림의 우측처럼 충돌검사 영역을 원형으로 만들어 검사(detection)할 수 있으면 더 좋지 않을까? 괜찮은 아이디어이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FP5bY4TF72TzofPYiGjZm%252Fimage.png%3Falt%3Dmedia%26token%3D06787198-3e91-419d-bea5-37d11db0f6bd&width=768&dpr=3&quality=100&sign=ff5160cb&sv=2) 그 다음으로 또 다른 아이디어는 뭐가 있을까? 완벽한 충돌검사를 위해 아예 사람 눈에 보이는 색깔이 칠해진 영역끼리만의 충돌 검사를 허락한다면 이것이 젤 완벽한 충돌검사일 것 같다. 그럴려면 본래 사각형에서 아래와 같이 색깔 칠해진 영역만 따로 한번 더 분리해 내야 하는 작업(그래픽툴에서는 이걸 [누끼따기arrow-up-right](https://namu.wiki/w/%EB%88%84%EB%81%BC) 라고 말함)이 추가로 필요하고, 또 이후에 실제 충돌영역 검사에서도 불규칙적인 외형끼리의 픽셀겸칩이라는 더 복잡한 연산이 필요한게 사실이다. 따라서, 이로인한 게임의 속도가 느려지는 현상이 생길 수도 있기에 이렇듯 각각의 솔루션에는 항상 트레이드오프(trade-off)가 있다. 결국 게임사용자에게 큰 불편을 주지 않는 어느정도 선해서 솔루션 선택의 타협을 해야할 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F4FPsGze6UF9xNyV9zM0e%252Fimage.png%3Falt%3Dmedia%26token%3D20d0adf6-29c4-45ad-a0ed-4ffb8b019f2d&width=768&dpr=3&quality=100&sign=a54bac50&sv=2) 이제 이 솔루션을 적용해 기존 코드를 바꿔보자. 그런데, 아쉽게도 그런식의 충돌검사 방법을 파이게임제로 라이브러리에서는 제공하지 않고 있다는 것이다. 그러나, 다행인건 지난 [5.8절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.8_animation) 에서 플래피버드 애니메이션을 위해 사용해봤던 파이게임제로를 기능확장하는 추가모듈(**pgzhelper**)에서는 제공하고 있기 때문에 이를 통해 해결할 수 있다는 것이다. 만약, 당신의 커스텀 뮤 에디터의 사용자가 아니고, 이전에 이 모듈을 설치한 적이 없다면, 최초 1회에 한 해 해당 모듈의 설치가 필요하며 설치에 관한 것은 [5.8](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.8_animation) [절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.8_animation) 에서 이미 충분히 설명하였으므로 해당 부분을 참고하여 설치하자. 다음으로 이제 위에서 언급한 첫번째 솔루션을 적용한 코드를 살펴보자. Copy from pgzhelper import * TITLE = 'Breakout' WIDTH = 800 HEIGHT = 600 GAP_FROM_SCREEN = 50 ball = Actor('ball', (WIDTH / 2, HEIGHT / 2)) ball.radius = ball.width / 2 bar = Actor('bar', (WIDTH / 2, HEIGHT - GAP_FROM_SCREEN)) # 4행*8열짜리 블록더미 만들기 blocks = [] for block_row in range(4): for block_col in range(8): block = Actor( 'block', (block_col * 100, block_row * 32 + GAP_FROM_SCREEN), anchor=('left', 'top') ) blocks.append(block) # 볼의 초기 (방향이 있는)속도값 vx = 5 vy = -5 def draw(): screen.blit('space', (0, 0)) ball.draw() bar.draw() for block in blocks: block.draw() def update(): global vx, vy # 반사판의 이동을 화면 안에 안에 가두기 if bar.left < 0: bar.left = 0 if bar.right > WIDTH: bar.right = WIDTH # vx와 vy만큼 속도로 공을 이동시키기 ball.move_ip(vx, vy) # 공이 외쪽 또는 오른쪽벽에 부딪힐 때 if ball.left < 0 or ball.right > WIDTH: vx = -vx # 속도의 x축 방향을 반대로하기 sounds.wall.play() # 공이 위쪽벽 또는 반사판에 부딪힐 때 if ball.top < 0 or ball.circle_colliderect(bar) == True: vy = -vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 공이 블록과 부딪힐 때 b_index = ball.collidelist(blocks) if b_index != -1: vy = -vy # 속도의 y축 방향을 반대로하기 sounds.block.play() blocks.pop(b_index) # 게임종료 if ball.bottom > HEIGHT: sounds.die.play() game.exit() if not blocks: sounds.win.play() vx = 0 vy = 0 def on_mouse_move(pos): x, y = pos bar.centerx = x 🔢 기존 코드에서 딱 3라인의 변화만 있다. 먼저 맨 첫 라인에서 pgzhelper 모듈을 우선적으로 import 했다. 8-10라인의 게임등장 배우객체들의 생성코드 중, _기존과 다른 우리 목표는 공을 사각형(rect)가 아닌 원형(circle)로 간주되길 원하는거라 9번라인처럼 객체에 반지름(radius)을 사전에 설정해 두는게 필요하다_. 가로세로 32픽셀 사각형 안에 꽉차는 원이기 때문에 반지름은 ball 이미지 길이의 절반(16픽셀)으로 ball 객체의 반지름(**radius**)을 설정해 놓으면 된다. 이제 실제 충돌검사하는 부분인 53번 라인에서는 **circle\_colliderect** 함수를 사용하면 된다. 이 함수로 인해 반사판은 여전히 사각형(rect)일지라도 적어도 공 객체는 원 형태로해서 상호간에 충돌검사가 일어난다. 두번째 솔루션의 경우도 적용은 쉬운데, _객체 모양을 누끼따낸 픽셀간의 충돌검사를 위한 메소드인_ _**collide\_pixel**_ _를 사용하면 되고, 이 메소드는 만약 충돌이 없을 경우엔 None 값을 리턴_하기 때문에 그것에 맞춰 다음과 같이 53번라인 코드를 수정하면 끝이다. Copy if ball.top < 0 or ball.collide_pixel(bar) != None: 이제 두번째 버그를 살펴볼 차례이다. 두번째 버그는 아래의 그림처럼 공이 반사판의 옆구리(edge)에 닿는 순간 반사판을 타고 도는 의도지 않은 기현상(?)이 발생하고 있다. 먼저, 이 버그는 도데체 왜 생겨나는가에 대해 추측해보자. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FXuhygpLbonwvcJRyaxiM%252FKapture%25202024-02-05%2520at%252020.40.40.gif%3Falt%3Dmedia%26token%3D9a42ac0c-8729-44fc-8e52-f43ffe016568&width=768&dpr=3&quality=100&sign=f9cbfe89&sv=2) 정확한 원인을 유추하기 위해 타고 돌고 있는 공의 모습을 자세히 살펴보자. 여러분의 이해를 돕기 위해 공의 움직임을 슬로우 모션으로 캡쳐해 보여주고 있다. 첫번째 힌트로 공은 반사판 안에 갇혀서 이동하고 있다. 두번째 힌트로 공은 오르락 내리락을 반복하면서 이동하고 있다. 먼저 첫번째 문제가 공이 반사판 우측끝(edge)에 닿았을 때부터 문제가 생기는데 그 순간 54번 라인의 코드에 의해 공이 반사시키려고 공의 속도(velocity)를 반대방향으로 바꾸는데 바꾸는 것까진 좋았는데 그 결과로 오히려 공이 반사판 내부쪽으로 방향이 바뀌면서 반사판 내부로 침투(?)하는 현상이 생긴 것이다. 이 침투한게 매우 치명적이다. 원래는 공은 침투해서는 안되고, 표면에서 반사되어 반사판을 곧바로 떠나가야 하는데 알다시피 update 함수는 1초 60번이나 호출이 되는데 이는 매우 짧은 순간으로, 공의 아주 조금의 위치변화가 있긴 있었으나, 그래봐야 여전히 현 위치가 계속 반사판 내부를 벗어나지 못한 관계로 매번 계속 반사판과 충돌이라고 판단해 버리게 된다. 그결과로 연이은 매 update 함수 호출마다 속도방향을 반대로 바꾸게 되기 때문에 반사판에 갇혀 있는 공이 오르락 내리락 하면서 이동하게되는 것이다. 그다음으로 이제 원인은 알았으니 어떻게 해결하면 좋을까? 여러분에겐 어떤 아이디어가 있는지 무척이나 궁금하다. 이 문제를 해결하는데는 두 가지 정도의 해법이 있을 것 같다. 두 아이디어 모두 공이 반사판 안쪽으로 침투를 막는 것에 관한 것이다. 첫번 째 아이디어는 공이 반사판 끝쪽에서의 반사의 경우는 반사시 반사판쪽 방향으로 꺽이지 않고, 즉 공은 방향의 꺾임없이 들어온 방향 그대로 같은 방향으로 다시 되돌아 나가게 하는 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FFGrPIvrGPej5OdMFHfT0%252Fimage.png%3Falt%3Dmedia%26token%3D3c86b9c4-5ad6-4e61-a02d-7cf57eea7cb9&width=768&dpr=3&quality=100&sign=34d353ae&sv=2) 두번 째 아이디어는 반사방향에 대한 것은 건드리지 않되, 반사되어 나갈 때 반사판에 걸려서 그 안으로 침투가 안되도록 하기 위해 일단 공을 수직으로 얼마간(10픽셀) 반사판으로부터 떨어뜨려놓은 후, 그다음에 기존처럼 반사하는 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F4ZQK0iqZ1Yw48wPfWPec%252Fimage.png%3Falt%3Dmedia%26token%3D8fe0475d-06e9-4e1e-91f3-c73ff29431ba&width=768&dpr=3&quality=100&sign=672615b1&sv=2) 우리는 복잡하지 않게 최대한 간단하게 구현하는데 더 우선순위가 있으므로, 둘 중에 후자가 훨씬 간단하므로 해당 솔루션을 적용한 이번 게임의 최종 코드는 다음과 같다. Copy from pgzhelper import * TITLE = 'Breakout' WIDTH = 800 HEIGHT = 600 GAP_FROM_SCREEN = 50 ball = Actor('ball', (WIDTH / 2, HEIGHT / 2)) ball.radius = ball.width / 2 bar = Actor('bar', (WIDTH / 2, HEIGHT - GAP_FROM_SCREEN)) # 4행*8열짜리 블록더미 만들기 blocks = [] for block_row in range(4): for block_col in range(8): block = Actor( 'block', (block_col * 100, block_row * 32 + GAP_FROM_SCREEN), anchor=('left', 'top') ) blocks.append(block) # 볼의 초기 (방향이 있는)속도값 vx = 5 vy = -5 def draw(): screen.blit('background', (0, 0)) ball.draw() bar.draw() for block in blocks: block.draw() def update(): global vx, vy # 반사판의 이동을 화면 안에 안에 가두기 if bar.left < 0: bar.left = 0 if bar.right > WIDTH: bar.right = WIDTH # vx와 vy만큼 속도로 공을 이동시키기 ball.move_ip(vx, vy) # 공이 외쪽 또는 오른쪽 벽에 부딪힐 때 if ball.left < 0 or ball.right > WIDTH: vx = -vx # 속도의 x축 방향을 반대로하기 sounds.wall.play() # 공이 위쪽벽에 부딪힐 때 if ball.top < 0: vy = -vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 공이 반사판에 부딪힐 때 if ball.circle_colliderect(bar) == True: ball.y -= 10 # 10픽셀 수직으로 먼저 튀어오르기 vy = -vy # 속도의 y축 방향을 반대로하기 sounds.bar.play() # 공이 블록과 부딪 때 b_index = ball.collidelist(blocks) if b_index != -1: vy = -vy # 속도의 y축 방향을 반대로하기 sounds.block.play() blocks.pop(b_index) # 게임종료 if ball.bottom > HEIGHT: sounds.die.play() pygame.exit() if not blocks: sounds.win.play() vx = 0 vy = 0 def on_mouse_move(pos): x, y = pos bar.centerx = x [Previous6.3 공의 반사와 블록격파 구현하기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.3_reflection_breakout) [Next6.5 (보너스) 오리지널 게임처럼 만들어 보기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original) Last updated 1 year ago --- # 9.7 객체지향으로 개발하기 4 | 파이썬-파이게임 제로 (순한맛) 드디어 먼 길을 거처 구현의 마지막까지 도달한 여러분을 축하한다. 이번 절에서는 지난 절에서 만들었던 코드를 일부 수정하고, 미구현 된 부분을 마져 구현하는 것으로 전체 코딩을 마무리 하려고 한다. 먼저 미구현된 부분인 총알을 맞은 탱크가 폭발해 불이나는 것을 추가하려고 한다. 우리가 이미 절차지향 개발에서 살펴봐서 알고 있듯이 "폭발" 이란 것도 사실은 하나의 Actor 객체이고, 그 폭발객체를 에니메이션 하는 것으로 구현하였다. 그렇다면, 객체지향 버전에서는 이 폭발객체를 Actor에서 객체에서 상속한 독립객체(여기서는 Explosion라 명명함)로 만들고, 이를 생성하고 활용하는 쪽은 탱크객체 자신이 될 수 있다. 그래서, 이를 다음과 같이 코딩할 수 있다. actors.py Copy class Explosion(Actor): def __init__(self, img_names, pos): super().__init__(img_names[0], pos) self.images = img_names self.fps = 8 self.duration = 15 def update(self): self.animate() self.duration -= 1 class Tank(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, walls, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self._original_pos = None self.angle = angle self.walls = walls self.bullets = [] self.explosions = [] ... 생략 def draw(self): super().draw() for bullet in self.bullets: bullet.draw() for explosion in self.explosions: explosion.draw() def collidelist_bullets(self, tanks): tank_index = -1 for bullet in self.bullets: bullet.move() # 화면경계 확인 if bullet.is_out_of_screen(): self.bullets.remove(bullet) # 벽 더미 확인 wall_index = bullet.collidelist(self.walls) if wall_index != -1: del self.walls[wall_index] self.bullets.remove(bullet) # 상대 탱크 확인 tank_index = bullet.collidelist(tanks) if tank_index != -1: self.bullets.remove(bullet) explosion = Explosion(["explosion3", "explosion4"], \ tanks[tank_index].pos) self.explosions.append(explosion) # 폭발 에니메이션 for explosion in self.explosions: explosion.update() if explosion.duration == 0: self.explosions.remove(explosion) return tank_index 🔢 폭발관련 처리를 위해 추가된 코드는 1~10, 21, 29~30, 52~59라인까지의 코드이다. 위의 내용을 실행시켜보면, 총알을 맞은 탱크는 우리가 목적한데로 폭발 에니메이션이 표현될 것이다. 이로써 우리가 만들고자 했던 배틀시티 게임의 객체지향 버전의 최종판을 완성했다. 어떤가 나만의 클래스를 정의해 나만의 객체를 만들고 이들 간의 협력을 통한 객체지향 패러다임으로 개발하는 것이 재미있는 경험이었을 것이라 믿는다. 다만, 아직 객체지향 복잡하기만 하고 큰 장점을 모르겠다라고 느낄 수 있을텐데 이번 과의 서두에서 아래와 같이 언급했지만, > _예를들어 적으로서 탱크만이 아닌 다양한 형태의 다른 적이 등장한다던지, 내가 사용하는 무기도 대포알 뿐만 아니라 다른 형태의 공격무기도 갖게 할 것인지, 지금은 장매물이 벽 더미이지만, 그 외의 다른 형태의 장애물도 생각하는지 등등에 따라 당장의 상속의 깊이에서부터 객체지향 설계의 스케일이 달라지기 때문이다._ 사실은 이 게임이 규모가 더 커지고 복잡해지면서 그 장점이 본격적으로 드러날 것이다. 물런 이런 고려까지 포함해 개발하려면 초기에 객체지향의 설계에서 더 난이도가 높아지고 추가적으로 더 알아야 할 지식들이 생기기는 하겠지만 말이다. 첫 술에 배부룰 순 없으니, 두 번째 객체지향 패러다임 개발은 이 정도 선에 만족하기로 하고, 다음 판 책 업그레이드 기회가 된다면 그 심화된 구현에 대해 부록으로 추가할 수 있는 기회를 갖으면 좋을 것 같다. 이제 마지막으로 지금까지 코딩내용의 전체 소스코들 첨부하는 것으로 이 과를 마무리 하겠다. actors.py Copy from pgzhelper import * from abc import ABC, abstractmethod class CheckOutOfScreen(ABC): def __init__(self, screen): self.s_width, self.s_height = screen @abstractmethod def is_out_of_screen(self): pass class Bullet(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, walls, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self.angle = angle self.walls = walls def is_out_of_screen(self): if self.x > self.s_width or self.x < 0 or \ self.y > self.s_height or self.y < 0: return True else: return False def move(self): if self.angle == 0: self.x += 5 elif self.angle == 90: self.y -= 5 elif self.angle == 180: self.x -= 5 elif self.angle == 270: self.y += 5 class Explosion(Actor): def __init__(self, img_names, pos): super().__init__(img_names[0], pos) self.images = img_names self.fps = 8 self.duration = 15 def update(self): self.animate() self.duration -= 1 class Tank(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, walls, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self._original_pos = None self.angle = angle self.walls = walls self.bullets = [] self.explosions = [] def is_out_of_screen(self): if self.right > self.s_width or self.left < 0 or \ self.bottom > self.s_height or self.top < 0: return True else: return False def move(self): self._original_pos = self.pos if self.angle == 180: self.x -= 2 elif self.angle == 0: self.x += 2 elif self.angle == 90: self.y -= 2 elif self.angle == 270: self.y += 2 # 화면경계 확인 if self.is_out_of_screen(): self.pos = self._original_pos # 벽더미 확인 if self.collidelist(self.walls) != -1: self.pos = self._original_pos def fire(self, image, sound_obj=None): bullet = Bullet(image, self.pos, self.angle, self.walls, (self.s_width, self.s_height)) self.bullets.append(bullet) if sound_obj: sound_obj.play() def collidelist_bullets(self, tanks): tank_index = -1 for bullet in self.bullets: bullet.move() # 화면경계 확인 if bullet.is_out_of_screen(): self.bullets.remove(bullet) # 벽더미 확인 wall_index = bullet.collidelist(self.walls) if wall_index != -1: del self.walls[wall_index] self.bullets.remove(bullet) # 상대탱크 확인 tank_index = bullet.collidelist(tanks) if tank_index != -1: self.bullets.remove(bullet) explosion = Explosion(["explosion3", "explosion4"], \ tanks[tank_index].pos) self.explosions.append(explosion) # 폭발 에니메이션 for explosion in self.explosions: explosion.update() if explosion.duration == 0: self.explosions.remove(explosion) return tank_index def draw(self): super().draw() for bullet in self.bullets: bullet.draw() for explosion in self.explosions: explosion.draw() battle\_city\_oop.py Copy import random from actors import Tank WIDTH = 800 HEIGHT = 600 winner = "" # 적 탱크 이동 지연 enemy_move_cnt = 0 ENEMY_MOVE_DELAY = 20 # 주인공 탱크 총알 재장전 지연 bullet_delay_cnt = 0 BULLET_DELAY = 50 # 50x50 크기의 벽의 더미 생성 walls = [] WALL_SIZE = 50 for x in range(int(WIDTH / WALL_SIZE)): # 탱크가 위치 할 첫 행과 마지막 행 총 2행을 비워두기 위해 -2 하여 생성 for y in range(int(HEIGHT / WALL_SIZE - 2)): if random.randint(0, 100) < 50: # 적정 수의 벽을 생성 wall = Actor("wall", anchor=("left", "top")) wall.x = x * WALL_SIZE wall.y = y * WALL_SIZE + WALL_SIZE # 맨 첫 행 비우기 위해 전체적으로 아래로 밀기 walls.append(wall) # 주인공 탱크 생성 tank = Tank("tank_blue", (400, 575), 90, walls, (WIDTH, HEIGHT)) # 적 탱크 생성 enemies = [] MAX_ENEMIES = 3 for i in range(MAX_ENEMIES): enemies.append(Tank("tank_red", (400, 25), 270, walls, (WIDTH, HEIGHT))) def draw(): screen.blit("grass", (0, 0)) # 배경이미지 그리기 tank.draw() for enemy in enemies: enemy.draw() for wall in walls: wall.draw() if winner: screen.draw.text( winner + " Win!", midbottom=(WIDTH / 2, HEIGHT / 2), fontsize=100 ) def update(): global enemy_move_cnt, bullet_delay_cnt, winner # 주인공 탱크 if winner == "": if keyboard.left: tank.angle = 180 tank.move() elif keyboard.right: tank.angle = 0 tank.move() elif keyboard.up: tank.angle = 90 tank.move() elif keyboard.down: tank.angle = 270 tank.move() if bullet_delay_cnt == 0: # 총알 재장전 가능 if keyboard.space: tank.fire("bulletblue2", sounds.sfx_exp_medium12) bullet_delay_cnt = BULLET_DELAY else: bullet_delay_cnt -= 1 enemy_idx = tank.collidelist_bullets(enemies) if enemy_idx != -1: del enemies[enemy_idx] if len(enemies) == 0: winner = "You" # 적 탱크 for enemy in enemies: choice = random.randint(0, 3) if enemy_move_cnt > 0: enemy_move_cnt -= 1 enemy.move() elif choice == 0: # 움직임 지연 초기화 enemy_move_cnt = ENEMY_MOVE_DELAY elif choice == 1: # 랜덤방향 결정 enemy.angle = random.randint(0, 3) * 90 else: enemy.fire("bulletred2") tank_idx = enemy.collidelist_bullets([tank]) if tank_idx != -1: winner = "Enemy" [Previous9.6 객체지향으로 개발하기 3chevron-left](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.6_oop_dev3) [Next10\. 퐁(Pong)을 네트워크 게임으로 만들기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong_network) Last updated 1 year ago --- # 6.3 공의 반사와 블록격파 구현하기 | 파이썬-파이게임 제로 (순한맛) 이번 절에서는 이제 공의 반사를 구현할 차례이다. 공 반사의 경우 수는 좌우벽, 상단벽, 공을 반사판을 받아낸 순간, 마지막으로 블록결파된 순간으로 블록은 부서지면서 동시에 반사가 일어난다. Copy TITLE = 'Breakout' WIDTH = 800 HEIGHT = 600 GAP_FROM_SCREEN = 50 ball = Actor('ball', (WIDTH / 2, HEIGHT / 2)) bar = Actor('bar', (WIDTH / 2, HEIGHT - GAP_FROM_SCREEN)) # 4행*8열짜리 블록더미 만들기 blocks = [] for block_row in range(4): for block_col in range(8): block = Actor( 'block', (block_col * 100, block_row * 32 + GAP_FROM_SCREEN), anchor=('left', 'top') ) blocks.append(block) # 볼의 초기 (방향이 있는)속도값 vx = 5 vy = -5 def draw(): screen.blit('space', (0, 0)) ball.draw() bar.draw() for block in blocks: block.draw() def update(): global vx, vy # 반사판의 이동을 화면 안에 안에 가두기 if bar.left < 0: bar.left = 0 if bar.right > WIDTH: bar.right = WIDTH # 각각의 x와 y 방향으로 거리 vx와 vy만큼 공을 이동시키기 ball.move_ip(vx, vy) # 공이 외쪽 또는 오른쪽 벽에 부딪힐 때, x의 방향을 반대로하기 if ball.left < 0 or ball.right > WIDTH: vx = -vx sounds.wall.play() # 공이 위쪽벽 또는 반사판에 부딪힐 때, y의 방향을 반대로하기 if ball.top < 0 or ball.colliderect(bar) == True: vy = -vy sounds.wall.play() # 공이 블록과 부딪 때 b_index = ball.collidelist(blocks) if b_index != -1: vy = -vy sounds.block.play() blocks.pop(b_index) # 게임종료 if ball.bottom > HEIGHT: sounds.die.play() game.exit() if not blocks: sounds.win.play() vx = 0 vy = 0 def on_mouse_move(pos): x, y = pos bar.centerx = x 🔢 먼저, 총 4군데, 외쪽(벽)/오른쪽(벽)/위쪽(벽)/반사판(bar)에 의한 반사에 대한 구현을 하겠다. 유사성 있는 2군데씩 짝을 지어 코딩하면 코드량이 줄어든다. 먼저 45라인의 코드에 의해 공이 좌/우벽에 닿았는지 if 조건문으로 지속적으로 검사해 반사를 시키면 되는데 반사시키는 코드는 어렵지 않은게 46번 라인 vx = -vx 의 의미는 _속도값에서의 x축 방향(direction)만 반대방향으로 바꾼 것으로, 반대방향으로 바꾸는 건 +(양수)를 -(음수)로, -(음수)를 +(양수)로 값의 부호만 바꾸면 되는 것이므로 본래값에 반대부호화 시키는 의미로 -vx 한 값을 자신에게 재대입시켰다._ 이번 게임부터는 처음으로 게임 내의 어떤 상황(공의 반사순간, 블록격파 순간, 게임의 종료)에 대해 효과음을 추가하므로써 게임성을 높여 사용자에게 게임의 흥미를 더하도록 만들자. 절차는 간단한데 먼저 기존에 게임에 사용할 이미지들을 images 라는 정해진 폴더 안에 넣어둔 것처럼, **게임에 사용할 미리 준비된 효과음들을 sounds라는 정해진 이름을 갖는 폴더 안에 저장해 놓으면 된다. 저장하기 쉬운 방법은 뮤 에디터에서 효과음(Sounds)이라는 아이콘을 눌러 해당 폴더를 열고(만약 기존에 폴더가 없을 경우 자동생성됨), 그 안에 넣어놓으면 된다.** 참고로 이 게임에 사용되는 효과음은 [이곳arrow-up-right](https://github.com/roboticsware/python-pygame_zero-samples/tree/main/breakout/sounds) 에서 다운로드 할 수 있다. 또는 당신이 커스텀 뮤 에디터를 사용 중이라면, 뮤 에디터 설치 시 해당 리소스들도 함께 설치되었을 것으므로, (계정명)\\mu\_code\\examples\\pygame\_zero 폴더 안에서 찾을 수 있다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FTJuWeo7krnA9eTUfYvFn%252Fimage.png%3Falt%3Dmedia%26token%3D162a167a-3a86-408d-8adf-e2017861c3b5&width=768&dpr=3&quality=100&sign=2bb7424e&sv=2) 효과음을 재생시키는 방법은 간단한데 47번 라인에서 보는 바와 같이 **sounds.재생할 효과음파일이름.play() 라는 문법을 통해 재생**시킬 수 있다. 상세한 사용방법에 대한 내용은 역시나 [이곳 파이게임제로 라이브러리 문서arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/builtins.html?highlight=colliderect#sounds) 에서 확인할 수 있다. 남은 것은 공이 상부벽에 낳은 경우 또는 우리가 조작하는 반사판에 닿은 경우로 이 경우도 둘이 함께 조건문으로 묶어 코딩할 수 있는데 이유는 속도값에서 y축 방향만 반대방향으로 바꾸면 공을 반사시킬 수 있기 때문이다. 공이 게임화면 위쪽에 닿았는지를 확인(ball.y < 0)에 대한 것은 어려운 것이 없고, 공이 반사판에 닿았는지(충돌했는지)의 여부는 지난 [5.5절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.5_collision) 에서 배운 Actor 객체소속 메소드인 colliderect 함수를 사용하면 되겠다. 이 경우에 _공의 입장에서 반사판과 충돌했는지 여부확인용으로 ball.colliderect(bar)로 코딩하던, 반대로 반사판 입장에서 공과 충돌했는지 여부의 확인으로 bar.colliderect(ball) 로 코딩하던 어느쪽이든 상관은 없다._ 🔢 이제 공이 블록과 충돌한 상황으로 넘어가자. 충돌을 검사해야 하는데 여러분이 떠오르는 아이디어는 어떤 것인가? 아마도 이런 아이디어가 있을 것이다. for 문을 사용해 블록더미 리스트에서 블록하나씩 꺼내서 그중 어떤 블록과 닿았는가(충돌했는가)를 colliderect 함수로 일일이 확인하면 될 것 같다. 코드로 표현하면 다음과 같을 것이다. Copy for block in blocks: if ball.colliderect(block): 그렇다 좋은 아이디어이고 잘 생각해냈다. 생각한데로 코딩을 해도 잘 동작할 것이다. 그런데, 저 두 줄짜리 코딩에 대해 파이게임에서 이미 한 개의 함수로 만들어 놓은게 있어서, 55번 라인처럼 [collidelist 함수arrow-up-right](https://www.pygame.org/docs/ref/rect.html#pygame.Rect.collidelist) 를 사용하면 한번에 해결하는 방법이 있다. _이 함수는 파라미터로 List를 입력으로 받아서 그 리스트 안 어떤 아이템에 충돌이 발견되면, 그 충돌된 것은 아이템 리스트 안에 몇번째 위치하는지의 인덱스값을 리턴값으로 알려준다. 만약에 리스트 안에 아이템들 중에 어떠한 충돌도 발견하지 못했다면, -1 의 음수값을 리턴해준다.(참고로 인덱스값은 결코 음수를 가질 수 없기 때문에 의도적으로 그 값을 사용함.)_ 따라서, 그것을 기반으로 56라인 조건문에서 인덱스값이 -1 인 아닌 경우에만(**서로 값이 같지 않은 경우라는 의미의 연산자 !=** 를 사용), 즉 충돌이 발생했을 때만 조건문 안으로 진입하여 공을 반사시키기위해 속도의 방향을 반전시키고, 충돌효과 소리를 내며, 마지막으로 블록더미 리스트(blocks) 객체의 메소드인 **pop 함수**를 사용하여 리스트에서 그 충돌한 블록을 없에고 있다. 🔢 마지막은 공의 반사는 아니고, 게임의 종료상황인데 먼저 사용자가 공을 받아내야 하는 미션을 수행하지 못해서 공이 그대로 게임화면 바닥으로 추락해버린 경우, 즉 미션실패로 게임종료된 상황에 대한 간단한 처리를 추가하겠다. 62-64라인처럼 게임종료 상황이니 그에 맞는 효과음 내고, game.exit 메소드를 호출하여 앱을 종료한다. 반대로 미션을 완전히 클리어 해서 블록더미를 완전히 격파한 경우도 승리로서의 게임종료상황 처리가 필요하다. 66-69라인의 코딩처럼 게임승리에 대한 효과음을 냄과 동시에 공을 그자리에서 멈추게 된다. 여기까지 블록격파게임의 핵심은 다 구현했다. 위에 코드를 가지고 게임을 즐겨보면 아마 미흡한 부분이나 특정(?) 버그를 하나 발견할 것이다. 다음 절에 그 부분을 다 해결해서 우리게임의 마무리를 짓도록 하자. [Previous6.2 배우들의 움직임 구현하기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.2_actors_movement) [Next6.4 충돌검사시 고려할 것들chevron-right](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.4_consider_collisions) Last updated 1 year ago --- # 6.1 게임무대에 배경과 배우 등장시키기 | 파이썬-파이게임 제로 (순한맛) 이번 절에는 코딩의 첫 시작이니 우리 게임의 무대인 게임화면에 배경입히고, 게임무대의 배우들인 블록더미, 블록더미의 블록을 격파하게 될 공, 사용자의 조작이 필요한 공을 반사시킬 반사판(영어로는 패들(paddle)이라 부르나, 우리는 bar라는 이름을 사용함)을 등장시켜보는 것까지 해보겠다. 우리가 만들게 될 게임의 최종 결과물은 다음과 같다. 결과화면을 먼저 보고나면 코드 이해가 훨씬 쉬울 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F64Nx9Z7KBpaTnPxylTtI%252Fimage.png%3Falt%3Dmedia%26token%3Db6b0cd64-bfc0-40e7-9885-b8ccffc79c82&width=768&dpr=3&quality=100&sign=8ac2edee&sv=2) 최종 게임 화면 Copy TITLE = 'Breakout' WIDTH = 800 HEIGHT = 600 GAP_FROM_SCREEN = 50 ball = Actor('ball', (WIDTH / 2, HEIGHT / 2)) bar = Actor('bar', (WIDTH / 2, HEIGHT - GAP_FROM_SCREEN)) # 4행*8열짜리 블록더미 만들기 blocks = [] for block_row in range(4): for block_col in range(8): block = Actor( 'block', (block_col * 100, block_row * 32 + GAP_FROM_SCREEN), anchor=('left', 'top') ) blocks.append(block) def draw(): screen.blit('space', (0, 0)) ball.draw() bar.draw() for block in blocks: block.draw() 🔢 21번 라인의 게임의 배경을 그리는 부분은 이미 본서의 첫 게임을 만들면서 [5.1절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background) 절 [](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background) 전체를 할애해 설명했기 때문에 독자분들이 충분히 잘 이해하고 있을 것으로 추가적인 설명이 필요 없을 것 같다. 참고로 이번 게임제작에 필요한 이미지는 [이곳arrow-up-right](https://github.com/roboticsware/python-pygame_zero-samples/tree/main/breakout/images) 에서 다운로드 할 수 있다. 또는 [커스텀 뮤 에디터arrow-up-right](https://github.com/roboticsware/mu/releases) 를 사용 중이라면 뮤 에디터의 작업 디렉토리인 (사용자계정)\\mu\_code\\examples\\pygame\_zero\\images 안에서 복사해 올 수 있다. 다만, 기억해야 할 것은 [5.1절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background) 에서 이미 언급했듯이, **우리가 코딩시 사용하는 이미지들은 항상 코딩하는 소스코드가 존재하는 폴더(디렉토리)의 하위 폴더로서 존재해야 하며 폴더명은 images 여야만 정상인식되어 사용할 수 있다 규칙을 다시한번 기억**하면 좋겠다. 🔢 6-7라인에서 먼저, 두 주연배우인 볼(ball)과 반사판(bar)의 배우(Actor)객체를 만들면서 이미지이름과 이 이미지가 화면상에 위치할 위치정보, 이렇게 두 가지의 생성인자값을 사용해 생성하였다. 이어서 공이 닿아 부서지게 될 블록더미라 불리는 블록객체들의 모음을 만들어야 하는데, 보기에는 한 덩어리처럼 보여도 공이 닿아 부셔져야 하는 블록 하나하나가 다 사실은 독립된 블록객체이다. 그 전에 먼저 위에서 살펴본 본 게임의 최종화면을 잘 관찰해보면 _블록더미는 게임화면(screen) 최상단에서 어느 정도의 빈 공간의 간격를 떼고 이후에 블록들이 하나의 더미를 이뤄 모여있는 구조와 동시에 공을 반사시키는 반사판 역시도 화면 최하단에서 윗방향으로 어느 정도 간격을 뗀 위치를 갖는 특징을 발견했을 것이다. 이 어느정도의 간격은 우리가 임의로 정한 것이고, 5번 라인에서 이를 의미하는 변수값으로_ _**GAP\_FROM\_SCREEN**_ _라고 이름짓고, 50px(픽셀)의 간격을 할당_하였다. 이제 블록더미의 구성을 자세히 살펴보자. 가로로 8개씩, 세로로 3열의 형태를 갖고 있는데, 2번 라인에서 정한 게임화면의 가로크기가 800px(픽셀)이기 때문에, 그 안에 8개의 블록을 연이어 붙혀 한 행으로 놓으려면, 예상하기로 블록 1개의 가로길이는 100px 이어야할 것으로 유추할 수 있다. 그 예상이 맞았고, 1개의 블록객체는 (블록 이미지)크기는 가로세로(100 x 32px) 크기다. 🔢 이제 이 정보를 기반으로 코딩을 하면 되는데, 먼저 _독립된 각각의 블록객체를 차곡차곡 담아서 하나의 블록더미를 만들 공간이 필요한데 이 경우, 리스트(List)라는 데이터 형태가 딱이고, 따라서 10번 라인에서 blocks라는 빈 리스트를 하나 생성_했다. 이후, 이 리스트에 각각의 생성된 독립적인 블록 하나하나들을 화면에 보여줄 순서에 맞게 차곡차곡 담으면 되는데, 이 때 화면에 보여지는 순서라는 것은 행을 먼저 쌓고 이후 열을 쌓는 순서로 하던, 반대로 열을 먼저 쌓으면서 행을 늘려가는 방식으로 쌓던 어떤 방식이든 관계는 없다 본인이 임의로 택한 방식으로 하면 되는데, 본 서에는 먼저 한 행을 다 쌓고, 이후 다음행으로 넘어가서 쌓는 방식으로 할 것이며, 이런 상황에 적합한 코딩이 11-12라인에서 볼 수 있는 2중 for 루프문(nested loop statements)이다. for 문 안에 for 문이 들어가는 구조로, 블록더미 한 행 안에서 8개의 블록을 채우는 안쪽 루프와 그 다음행으로 넘어가는 즉 행을 옮기는 바깥쪽 루프를 번갈아 돌면서 생성된(13~17번 라인) 각각의 독립된 블록객체를 리스트 안에 차곡차곡 채우는(18번 라인) 것이다. _리스트에 값을 추가하는 함수는 List 객체(List 자체가 사실은 그 본질은 객체였다 라는 것도 참고로 알아두자)가 제공하는 멤버함수인_ _**append**_ _함수를 이용할 수 있다._ 🔢 이제 반복문을 이해해보도록 하자. [기존 서](https://jjlee.gitbook.io/pygame_zero-entry_basic/) 를 거쳐오지 않은 분들을 위해 _**for-in range()**_ **구문을 한번 더 설명하면, 반복에 사용하는 이 구문은 우리가 엔트리 블록코딩에서 숱하게 사용해 봤던 "몇 회 반복하기" 코딩블럭이라고 생각하면 될 것이다.** _일단 저 구문에서 for-in과 range 함수를 분리해서 살펴보는게 필요하다._ 먼저 [range 함수arrow-up-right](https://docs.python.org/3/library/functions.html#func-range) 는 파이썬언어의 내장함수로 아주 간략화시켜서(본서는 순한맛 버전으로 일단 이정도만 알고 넘어가고) 설명하면, 다음과 같은 행동을 하는 함수이다. circle-info **range(n) 함수** 파라미터 n: 양의 정수값 리턴값: 0부터 n-1 까지의 연속적인 정수값을 같는 수의 모음을 리턴 예시로 list(range(4))를 실행시켜 어떤 결과 리턴되는지 확인해보자. Copy print(list(range(4))) output: [0, 1, 2, 3] range함수의 결과가 range객체들이기 때문에 list함수로 감싸서, 이를 list형태로 변환해 보는게 우리가 그 실체를 한눈에 이해하기 편한다. 즉, 인자로 4를 넘기면, 0부터 시작해서 3까지의 연속적인 정수값을 담겨져 있다라고 이해할 수 있다. 이제 for-in 문법구문을 이해해 보자. 여기서 "연속형 객체"라는 것은 리스트 같이 여러 개의 값을 값는 데이터타입을 말한다. circle-info **for** (연속형 객체)아이템 값의 변수명 **in** 연속형 객체**:** <반복할 내용> 다음의 코드 예시를 보면 이해가 빠를 것이다. for-in 구문 안에 내용을 총 4회 반복 수행할 것이고, 매 반복 회차마다 리스트 안에 값(이를 아이템이라 부름)을 우리가 정한 변수명으로 하나하나씩 순차적으로 자동적으로 가져오게 되면서 반복을 수행한다. Copy for item in [0, 1, 2, 3]: print(item) # 반복의 매 회차마다 [0, 1, 2, 3] 리스트 안에 아이템 값을 순서대로 가져옮 output: 0 1 2 3 **위에 예시로 사용한 for item in \[0, 1, 2, 3\]은 사실은 for item in range(4) 코드와 동일한 것으로 간주할 수 있다.** 여기까지 이해가 되었다면, 이제 11번 라인의 **for block\_row in range(4):** 구문을 이해할 수 있을 것이다. 총 4회 반복을 수행하되 매 반복회차마다 block\_row 변수값이 차례대로 0~3까지의 값이 될 것이고, 12번 라인의 **for block\_col in range(8):**은 총 8회 반복을 수행하되 매 반복회차마다 block\_col 변수값이 차례대로 0~7까지의 값이 될 것라는 것을 예상할 수 있다. 이것을 활용해서 15라인에 block\_row, block\_col변수값을 활용해 Actor 객체생성시 위치값을 산정할 수 있게 되는 것이다. 🔢 블록객체 생성시 활용하는 파라미터 중에 전에 배우지 않은 1가지가 더 남아 있는데, 16번 라인에 있는 anchor에 관한 것이다. **anchor는 우리가 엔트리 블록코딩에서 이미 사용해봤던 각 오브젝트마다 갖고 있는 중심점을 말하는 것으로, 파이게임에서는 이를 anchor라고 지칭**하고, 엔트리코딩에 코딩시 때로는 그 중심점의 이동이 필요했었던 것처럼, 텍스트코딩에서도 마찬가지 이유로 필요할 때가 있는데, 지금 이순간이 바로 그 순간이다. 엔트리 때도 그랬지만, **기본적으로 Actor 객체의 디폴트 anchor는 이미지의 한 가운데(center) 중심이다.** 우리는 이것을 아래 화면상에 보이는 좌측최상단(topleft 또는 lefttop)으로 옮기고자 하며, 이 경우 16번 라인에서 보는 것과 같이 anchor=('left', 'top') 이란 인자값을 넘기면 되는 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FAg9nF2EH9HhOxTQuGoIq%252Fimage.png%3Falt%3Dmedia%26token%3D46eb59dd-fc79-4470-9e5b-37117f21df4a&width=768&dpr=3&quality=100&sign=8d8bb1b1&sv=2) 왜 anchor를 옮겨야 하느냐라고 묻는다면, 게임화면의 좌측면에 딱 붙혀서 거기서부터 출발해 블록객체 더미를 화면에 그려나가고 싶어서이다. anchor를 옮기지 않고 화면에 블록객체 더미를 그려보면 이 말의 의미를 곧바로 알게 되는데, anchor의 디폴트값이 이미지의 중앙이므로, anchor를 옮기지 않고 그리면 아래의 그림의 상단처럼 블록의 절반이 잘려나간 채로 그려지기 때문이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F1GirtJoqfvRztwMpJuVz%252Fimage.png%3Falt%3Dmedia%26token%3D50c3266f-ea1d-48eb-b8c1-81c0a9889dc7&width=768&dpr=3&quality=100&sign=b0337bd8&sv=2) 🔢 이제 마지막으로 draw 함수 안에서 블록더미를 그리는 23-24라인의 코드를 살펴보자. 우리가 이미 배운 for-in 반복문을 사용하고 있고, blocks 라고 하는 독립된 블록객체들을 모아놓은 리스트 안에 존재하는 블록객체 하나하나씩 꺼내서 화면에 일일이 그리는 일을 리스트 안에 객체가 다 꺼내질 때까지 반복하고 있다는 것을 알 수 있다. 본 장의 1절부터 기존에 공부하지 않았던 개념이 몇 개 등장해 예상보다 설명이 많아지고, 길어진 면이 있다. 다음 2절에서는 등장인물의 움직임 알고리즘 위주로 재미있게 공부해보자. [Previous6\. 블록격파(Breakout) 게임만들기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/breakout) [Next6.2 배우들의 움직임 구현하기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.2_actors_movement) Last updated 1 year ago --- # 10.1 네트워크 게임방식의 이해와 라이브러리 설치 | 파이썬-파이게임 제로 (순한맛) 요즘 시대는 항상 인터넷에 연결된(Always-on) 휴대폰의 보편화로 기본적으로 대다수의 게임이 네트워크 상이 다른 유저와 함께 게임을 즐기는 네트워크 게임이 보편화 되었다. 우리 모두는 그러한 게임을 한번 만들어 보고 싶을 것이란 생각이 든다. 그래서, 상용 수준은 아니더라도 그런 게임제작의 원리를 이해하는데 충분한 수준의 게임을 만들어 보기로 하자. 네트워크 게임 제작을 설명하는데 시간이 충분지 않기 때문에 아예 새로운 게임제작 대한 도전으로 시간을 낭비하지 않고, 기존에 우리가 로컬(local)용으로 만들어 봤던 게임을 네트워크 게임으로 전환해 보는 방식으로 진행해 보는게 좋겠다. 네트워크 게임을 만들기 위해서는 기존에 네트워크 게임들이 어떻게 게임유저(user)간의 네트워크 상에 연결을 만들어 관리하는지를 이해할 필요가 있다. 연결을 위한 핵심 솔루션으로는 **게임사용자를 연결하는 호스팅(hosting) 솔루션은 크게 다음 아래의 그림처럼 게임의 요구 사항에 따라 릴레이(Relay) 서버 또는 전용(Dedicated) 게임 서버방식**이 있다. 둘의 구분은 크게 게임참여자의 크기에 따라 구분되는데 2~10명 내외의 참여자의 동시게임을 위한 것인지와 최소 수십명 이상이 함께 게임하려는 목적인지에 따라 구분할 수 있다. 이 중에서 _우리는 소규모 규모의 무겁지 않은 케주얼 게임에 적합한 릴레이 서버방식의 연결 솔루션을 사용해 게임을 구현하도록 하겠다._ ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252Fj15JMh5WtuwCfiJj0e6h%252Fimage.png%3Falt%3Dmedia%26token%3Db3ab2d1c-b45f-4a74-a469-23a148b4d522&width=768&dpr=3&quality=100&sign=e6c83c75&sv=2) 출처: Unity 웹사이트 **릴레이 서버는 플레이어 간의 연결과 메시지를 중계하는 역할만 담당하며, 게임 로직은 각 플레이어의 클라이언트에서 처리**하기 때문에 플레이어 간 연결 상태에 따라 게임 성능이 달라질 수 있으며, 불안정한 연결은 지연이나 끊김 현상을 유발할 수 있는 단점은 있으나 장점은 방화벽(firewall)이 있는 네트워크에서도 게임을 진행할 수 있는 장점이 있다. 파이게임제로는 아직 네트워크 게임을 만들 수 있는 라이브러리까진 제공되지 않고 있다. 그러나, 다행히도 pgzhelper의 저작자(Cort)가 또 기여를 했는데, [릴레이 서버에 기반한 네트워크 게임 개발용 라이브러리를 만들어 오픈소스arrow-up-right](https://github.com/QuirkyCort/nethelper) 로 제공하고 있다. 우리는 그 라이브러리 모듈을 가져다가 개발에 활용하도록 하겠다. 현재 [커스텀 뮤 에디터arrow-up-right](https://github.com/roboticsware/mu/releases) 의 **1.2.9 버전 이상**을 사용해 코딩하고 있다면, 이미 해당 모듈을 에디터 안에 내장하고 있으므로, 위에 언급된 추가적인 모듈설치의 과정없이 곧바로 코딩을 시작할 수 있다! 그러나, 다른 에디터나 IDE를 사용하고 있다면 모듈을 메뉴얼로 다운로드해 설치해야 하는데, 다운로드 하는 방법은 [이곳arrow-up-right](https://raw.githubusercontent.com/QuirkyCort/nethelper/refs/heads/main/nethelper.py) 을 누르면, 웹브라우저(필자의 경우, 크롬 웹브라우저)가 열리면서 모듈의 소스코드가 보일 것이고, \[파일\] 메뉴에서 \[페이지를 다른 이름으로 저장\] 메뉴를 택하고, 저장위치를 우리게임의 소스코드가 존재하는 뮤 에디터의 기본 폴더(본인 계정 아래에 mu\_code라는 폴더)에 저장해 넣도록 하자. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FoIdNnPwb6qGJkczCxtkj%252Fimage.png%3Falt%3Dmedia%26token%3D37198dd7-7f12-482b-9b89-6d1a8bd4a836&width=768&dpr=3&quality=100&sign=19f9368&sv=2) ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FykwdBzZWPbnU93h3oU8G%252Fnethelper.png%3Falt%3Dmedia%26token%3Da7fba075-52a7-4745-b227-6e7870a09adc&width=768&dpr=3&quality=100&sign=1245d561&sv=2) 코딩을 위한 준비는 끝났고, 이제 실제 코딩을 시작해 보자. 라이브러리든 모듈이던 만든이의 의도대로 사용자들이 이용하도록 사용자 설명서가 제공되는게 일반적이라고 언급했던 것을 기억한다면, 당연히 이 모듈도 [깃허브 소스코드 저장소arrow-up-right](https://github.com/QuirkyCort/nethelper) 에 사용법이 안내되어 있고, [이곳arrow-up-right](https://github.com/QuirkyCort/nethelper/wiki) 에서는 각 API(Application Programming Interface)의 상세확인이 가능하다. 사용법이 주어졌으니 각자 읽어보고 알아서 코딩을 할 수도 있겠으나, 우리 책은 초보자를 배려하는 순한맛 버전이므로 다음 절에서 같이 코딩을 시작해 보도록 하겠다. [Previous10\. 퐁(Pong)을 네트워크 게임으로 만들기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong_network) [Next10.2 릴레이 서버 구동과 클라이언트 접속chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.2_server_connection) Last updated 11 months ago --- # 7.4 그 밖에 도전과제 | 파이썬-파이게임 제로 (순한맛) 지금까지 만들어본 트윈비의 클론게임은 우리의 배움 수준을 감안해 아주 간단한 핵심기능 수준에서만 구현했다는 것을 알 것이다. 그러나, 여러분 들 중에는 충분히 능력이 뛰어나고 더 시도해보고자 하는 의지가 있는 분들이 있다는 것을 안다. 그런 분들을 위해 도전해 볼 과제들을 나열해 보고, 그 기능들의 일부들을 구현에 대해 공부해 볼 수 있는 사이트를 안내하는 것으로 이 과를 최종 마무리 하겠다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.4_additional_challenge#undefined) 도전과제들 다음 사항을 순서대로 시도해 보세요. **시작 화면 및 난이도 레벨 (level)** * 게임의 시작 인트로(Intro) 화면을 만들어, 실제 게임은 시작 화면에서 "시작" 아이콘을 클릭할 때만 시작되게 하기 * 게임 안에 여러 레벨을 만들어, 각 레벨에 따라 배경과 적의 유형을 변경시키기 **여러 생명** * 플레이어에게 3개의 생명을 주어 게임은 3개의 생명을 모두 잃을 때만 끝나게 하기 **배경의 그래픽 효과** * 떠다니는 구름을 추가 * 배경 안에 회전하는 풍차 등의 애니메이션 이미지를 추가 **파워업 아이템 추가** * 실드(Shield) 아이템: 주인공에 실드를 제공하는 아이템으로 아이템 획득하면 주인공 주변에 막이 형성됨. 단, 적의 총알을 한 번 막으면 실드가 사라짐 * 폭탄 아이템: 화면의 모든 적을 파괴하는 폭탄 아이템 * 드론: 아이템으로 주인공과 나란히 비행하며 도움을 주는 드론 아이템 (오리지널 게임 참조) **다양한 적을 추가** * 강력한 적: 파괴하는 데 여러 개의 총알이 필요한 적 * 슈터(Shooter) 적: 모든 방향으로 총알을 계속 쏘는 적 * 보스(Boss) 적: 각 레벨 마지막에 등장하는 보스 적 위에 언급된 기능의 추가를 우선적으로는 스스로 추가해 보려고 애쓰자. 그러나, 생각보다 여의지 않을 경우, [이 사이트arrow-up-right](https://trinket.io/aposteriori/courses/intermediate-game-development-with-pygame-zero) 를 참조하여 개인적인 공부를 이어나갈 수 있다. [Previous7.3 충돌처리 및 기타정보(점수 및 게임종료) 표기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.3_collision_detection) [Next8\. 퐁(Pong) 게임 만들기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong) Last updated 1 year ago --- # 5.5 플래피버드와 파이프의 충돌 구현하기 | 파이썬-파이게임 제로 (순한맛) 이번 절까지 배우면 플래피버드 게임의 핵심기능 구현까지는 완료된다. 얼마 남지않은 코드를 힘내서 함께 구현해보자. 이번 절에는 제목처럼 플래피버드가 파이프와 충돌하는 상황으로, 이 경우는 게임이 종료되는 상황이다. 다만, 부딪히는 그 즉시로 종료되면 게임에 흥미가 떨어지기 때문에, 닿는 순간 플래피버드가 죽은상태의 모습으로 그대로 땅 아래 방향으로 추락하는 에니메이션을 추가하고, 결국 추락하는 플래피버드가 게임화면 하단을 넘어서는 순간 그제서야 첨부터 게임을 다시 시작하게되면 더 자연스러울 것이다. 이 요구사항을 실제 코드로 구현하면 다음 아래의 코드와 같다. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 GAP = 140 PIPE_SPEED = -3 bird_alive = True # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() def update(): global drop_speed, bird_alive drop_speed += GRAVITY flappy_bird.y += drop_speed top_pipe.x += PIPE_SPEED bottom_pipe.x += PIPE_SPEED # 파이프의 무한순환 if top_pipe.right < 0 or bottom_pipe.right < 0: top_pipe.x = WIDTH bottom_pipe.x = WIDTH # 파이프와의 충돌 if flappy_bird.colliderect(top_pipe) or flappy_bird.colliderect(bottom_pipe): flappy_bird.image = "birddead" bird_alive = False def on_mouse_down(): global drop_speed if bird_alive == True: drop_speed = -6.5 🔢 우리가 두 객체의 충돌검사를 할 때, 엔트리에서의 코딩을 기억해보면 충돌검사를 위한 별도의 블록("~에 닿았는가?" 블록)이 존재했던 것처럼, 파이게임제로에도 이를 위해 별도로 준비된 함수(메소드)가 있는데, **Actor 객체에는 Actor 객체가 갖는 행동(Behavior)의 하나로서 아래와 같이 Actor 객체간의 충돌검사를 할 수 있는 colliderect라는 함수(메소드)가 준비되어 있는데 이를 활용하여 쉽게 코딩이 가능하다**. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F0CdpioFHjTBubbltAHiJ%252Fimage.png%3Falt%3Dmedia%26token%3D59b41d7b-10b4-4f55-ac7a-9330d8cd379d&width=768&dpr=3&quality=100&sign=b4552a0a&sv=2) 출처: [https://kidscancode.org/blog/2016/08/pygame\_shmup\_part\_5/arrow-up-right](https://kidscancode.org/blog/2016/08/pygame_shmup_part_5/) 화면의 애니메이션이 일어나는 update 함수 안에서 35번 라인에서 플래피버드가 위 파이프 또는 아래 파이프 둘 중 하나와(이 목적으로 비교문 안에 or 를 사용했음을 주목하자!) 충돌했는지를 지속적으로 체크한 후, 만약 충돌했으면 플래피버드의 이미지를 죽은 모양의 이미지(birddead)로 바꾸고, 추가적으로 bird\_alive 라는 플래피버드의 생존유무를 나타내는 global 변수(함수 밖에서 선언)에 값을 죽음을 의미하는 False로 바꾼다. bird\_alive 변수는 왜 필요하게 되었을까? 그것은 41번 라인에 추가된 조건문에서 알 수 있는데, 이미 죽은 상태의 플래피버드를 마치 살아있는 것처럼 하늘로 날아오르는 공중부양을 가능하게 해서는 안되기 때문에 현재 플래피버드가 살아 있는 상태인지 죽어있는 상태인지의 분간이 필요했다. 여기까지만의 코드로 플래피버드는 계획했던데로 파이프에 닿자마자 (외형의 모습까지도)죽은상태가 되서 추락을 시작하게 된다. 이제 남은 건 화면 바닥까지 추락했을 때, 게임을 재시작 해야 하는게 남아있다. 그런데 게임을 재시작하는 상황은 비단 파이프와의 충돌만이 있는게 아닐 수 있다. 왜냐하면 사용자가 의도적으로 플래피버드를 가만히 놔둠으로 중력에 의한 자동추락으로 게임화면 하단을 넘어서게 만들거나, 또는 마우스 버튼을 빠른 시간 안에 계속 클릭함으로써 공중부양으 심하게(?) 시켜 게임화면 상단 위쪽을 벗어나게도 만들 수 있다. 이 경우도 게임을 재시작하게 만들면 좋을 것이다. 결론적으로 이 때는 즉 플래피버드가 살아있던 죽어있던지에 상관없이 화면의 최상단이나 최하단을 넘어서는 상황에 대한 처리를 추가하면 되는 것이고, 이는 사실상 파이프의 무한순환을 구현한 코딩과 크게 다를 바 없다. 이를 코드로 구현하면 다음 아래의 코드와 같다. Copy TITLE = 'Flappy Bird' WIDTH = 400 HEIGHT = 708 GRAVITY = 0.3 drop_speed = 0 GAP = 140 PIPE_SPEED = -3 bird_alive = True # Actor 객체들 flappy_bird = Actor('bird1', (75, 350)) top_pipe = Actor('top', (350,0)) bottom_pipe = Actor('bottom', (350, top_pipe.height + GAP)) def draw(): screen.blit('background', (0, 0)) flappy_bird.draw() top_pipe.draw() bottom_pipe.draw() def update(): global drop_speed, bird_alive drop_speed += GRAVITY flappy_bird.y += drop_speed top_pipe.x += PIPE_SPEED bottom_pipe.x += PIPE_SPEED # 파이프의 무한순환 if top_pipe.right < 0 or bottom_pipe.right < 0: top_pipe.x = WIDTH bottom_pipe.x = WIDTH # 파이프와의 충돌 if flappy_bird.colliderect(top_pipe) or flappy_bird.colliderect(bottom_pipe): flappy_bird.image = "birddead" bird_alive = False # 게임의 재시작 if flappy_bird.y > HEIGHT or flappy_bird.y < 0: flappy_bird.image = "bird1" bird_alive = True flappy_bird.center = (75, 350) drop_speed = 0 top_pipe.x = WIDTH bottom_pipe.x = WIDTH def on_mouse_down(): global drop_speed if bird_alive == True: drop_speed = -6.5 🔢 40번 라인에서 조건문을 통해 플래버드가 화면 밖을 넘었는지를 지속적으로 검사하다가, 넘어서는 순간 게임을 처음 초기상태서 재시작하기 위해 필요한 값들의 초기화를 그 조건문 안에 코딩하면 된다. 초기화 과정은 먼저 41번 라인에서처럼 플래피버드 이미지를 다시 살아있는 상태의 이미지로 바꾸고, 42번 라인에서 자신의 생존유무를 나타내는 값(bird\_alive)도 살아있음(True)으로 바꾸고, 38번 라인에서 플래피버드의 누적되어 온 낙하속도(drop\_speed)도 다시 0으로 초기화 하고, 마지막으로 게임처음 시작시 플래피버드와 파이프들이 위치해야 할 처음 위치정보로 되돌린다. 이로써 플래피버드의 핵심구현 기능구현은 다 끝났다. 추가적으로 게임에 흥미를 더하기 위한 몇 가지 추가 기능요구사항(파이프 위치 램덤화하기, 파이프 무사통과시 점수부여, 게임음악 추가 등등)이 있을 수 있으나, 이처럼 단지, 50라인짜리 코딩으로도 게임하나를 만들 수 있다는 것이 파이게임제로의 가장 큰 장점이라 할 수 있겠다. 추가기능 구현에 대해서는 따로 별도의 챕터에서 기술하도록 하겠다. 지금까지 플래피버드 게임만들기 통해, 파이게임제로에 성공적으로 입문하게 된 여러분들을 다시한번 축하하고, 앞으로 이어질 몇 개의 챕터를 통해 이러한 크고작은 몇 개의 게임제작 프로젝트들을 진행해 보면서, 파이썬 언어를 통한 텍스트코딩의 참 재미를 발견할 수 있길 기대해본다. [Previous5.4 화면에 파이프(오브젝트) 나타내기와 움직이기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.4_pipes) [Next5.6 플래피버드 게임 추가기능 구현하기 (점수기능)chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.6_score) Last updated 1 year ago --- # 8.2 절차지향형으로 개발하기 2 | 파이썬-파이게임 제로 (순한맛) 이번 절에서부터 앞서 언급된 4개의 사용자 함수의 구현체를 구체적으로 채워보는 시간이다. 먼저, **reset\_ball** 함수이다. 이름에서 알 수 있듯이 볼의 초기 위치와 속력의 초기값을 정하는 목적의 함수이다. 오리지널 게임을 유심히 살펴보았다면 알 수 있는데 매 새 게임이 시작할 때마다, 화면 중앙선에서 공이 좌/우 반사판으로 랜덤하게 던져지는 것을 알 수 있다. 우리도 게임이 최종 승패점수(**FINAL\_SCORE**)에 도달하기 전까지는 그렇게 동작하도록 유사 구현을 하였다. 특별히 좌우의 랜덤을 결정하는 것은 13번 라인의 **random.choice**라는 함수인데 인자값으로 전달된 리스트 안에 존재하는 두 값(-1, 또는 1) 중에 하나를 랜덤하게 골라주는 함수에 의해 동작하게 된다. Copy def reset_ball(): global vx, vy if b1_score == FINAL_SCORE or b2_score == FINAL_SCORE: vx = 0 vy = 0 else: vx = 5 vy = 5 # 중심선에서 좌우로 랜덤하게 던지기 ball.center = (WIDTH/2, random.randint(BALL_RADIUS*2, HEIGHT - BALL_RADIUS*2) vx *= random.choice([-1, 1]) 다음으로 **ball\_move\_collide** 함수이다. 볼의 움직임을 만들고, 움직인 이후에 충돌검사를 하는 함수이다. 만약, 벽(게임화면 테두리)과 충돌지점이 좌/우냐에 따라 어떤 게임유저(**bar1** 또는 **bar2**)가 점수(**b1\_score** 또는 **b2\_score**)를 획득하는지 판별하고 승패가 났기 때문에 새 게임을 시작하는 준비를 한다. Copy def ball_move_collide(): global vx, vy, b1_score, b2_score # vx와 vy만큼 속도로 공을 이동시키기 ball.move_ip(vx, vy) # 공이 위쪽 또는 아래쪽 벽에 부딪힐 때 if ball.top < 0 or ball.bottom > HEIGHT: vy = -vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 공이 왼쪽 벽에 부딪힐 때 if ball.left < 0: b2_score += 1 reset_ball() sounds.die.play() # 공이 오른쪽 벽에 부딪힐 때 if ball.right > WIDTH: b1_score += 1 reset_ball() sounds.die.play() 다음으로 **gameover\_draw** 함수이다. 게임 최종승패에 대한 화면에 UI를 표시하는 워낙 간단한 구현체라 특별히 이해에 어려움은 없다. Copy def gameover_draw(): if b1_score == FINAL_SCORE: winner = 'Player1' else: winner = 'Player2' screen.draw.text(winner + ' Win!!', (WIDTH/3, HEIGHT/2 - 50), color='blue', fontsize=70) screen.draw.text('Press Space to play again', (WIDTH/4, HEIGHT/2 + 50), \ color='skyblue', fontsize=50) 마지막으로 **bar\_move\_collide** 함수이다. 이름처럼 반사판의 움직임을 만들고, 반사판에 충돌한 공에 대한 처리를 담당한다. 기존에 블록격파 게임과 유사하여 특별히 코드이해에 어려움은 없고, 새롭게 추가된 부분은 이전 절에 언급되었던 블록격파게임의 신규 추가기능을 제안되었던 게임 랠리가 지속되면서 속도를 점점 증가시키는 알고리즘과 반사판 영역을 두개 상하부로 나눠 상부에 공이 닿을 때와 하부에 공이 닿을 때의 동작을 달리해 게임유저의 반사판 조작에 따른 공격/방어의 자유도를 증가시켜 게임성을 향상시키는 코드를 추가하였다(25~30라인). circle-info 참고로 26-27라인 같이 여러 라인에 걸친 주석의 경우, 주석의 시작과 끝에 작은 따옴표(') 또는 큰 따옴표(")를 3개씩을 넣는다는 것도 파이썬 문법의 기본상식으로 알아두자. Copy def bar_move_collide(bar): global vx, vy if bar == bar1: if keyboard.a: if bar.y > 0: bar.y -= 7 elif keyboard.z: if bar.y + bar.height < HEIGHT: bar.y += 7 else: # bar2 if keyboard.up: if bar.y > 0: bar.y -= 7 elif keyboard.down: if bar.y + bar.height < HEIGHT: bar.y += 7 if bar.colliderect(ball): # 10픽셀 앞으로 먼저 튀어오르기 if vx < 0: # bar1 경우 ball.x += 10 else: # bar2 경우 ball.x -= 10 vx = -vx * SPEED_UP # 속도의 x축 방향을 반대로하기 ''' 공이 윗측 진입하면서 반사판 윗측에 부딪힐 때 또는 공이 아래측 진입하면서 반사판 아래측에 부딪힐 때는 진입방향 그대로 반사 ''' if (vy > 0 and ball.centery < bar.centery) or \ (vy < 0 and ball.centery > bar.centery): vy = -vy * SPEED_UP sounds.bar.play() 이상으로 4개의 사용자 함수에 대한 구현과 이해가 끝났다. 앞서도 언급했지만, 이 게임은 앞선 블록격파 게임의 특징들에 대한 사진 지식이 있었기 때문에 생각보다 이 게임의 구현이나 이해에 많은 시간이 걸리지 않았던 같다. 마지막으로 모든 전체코드를 공유하는 것으로 절차지향형으로 만들어본 퐁 게임을 마무리 짓고, 다음 절부터는 드디어 사용자 정의 객체를 만드는 방법부터 시작해 그 객체들을 활용해 동일한 게임을 객체지향 패러다임으로 만들어 보기를 시작해보자. Copy import math import random # 게임화면 TITLE = 'pong' WIDTH = 800 HEIGHT = 600 GAP_FROM_SCR = 20 # 반사판 BAR_H = 100 BAR_W = 15 # 볼 vx = 5 vy = 5 BALL_RADIUS = 10 SPEED_UP = 1.05 # 점수 FINAL_SCORE = 11 b1_score = -1 b2_score = -1 def reset_ball(): global vx, vy if b1_score == FINAL_SCORE or b2_score == FINAL_SCORE: vx = 0 vy = 0 else: vx = 5 vy = 5 # 화면 중앙 상부에 위치좌우로 랜덤하게 던지기 ball.center = (WIDTH/2, random.randint(BALL_RADIUS*2, HEIGHT - BALL_RADIUS*2)) vx *= random.choice([-1, 1]) def ball_move_collide(): global vx, vy, b1_score, b2_score # vx와 vy만큼 속도로 공을 이동시키기 ball.move_ip(vx, vy) # 공이 위쪽 또는 아래쪽 벽에 부딪힐 때 if ball.top < 0 or ball.bottom > HEIGHT: vy = -vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 공이 왼쪽 벽에 부딪힐 때 if ball.left < 0: b2_score += 1 reset_ball() sounds.die.play() # 공이 오른쪽 벽에 부딪힐 때 if ball.right > WIDTH: b1_score += 1 reset_ball() sounds.die.play() def gameover_draw(): if b1_score == FINAL_SCORE: winner = 'Player1' else: winner = 'Player2' screen.draw.text(winner + ' Win!!', (WIDTH/3, HEIGHT/2 - 50), color='blue', fontsize=70) screen.draw.text('Press Space to play again', (WIDTH/4, HEIGHT/2 + 50), \ color='skyblue', fontsize=50) def bar_move_collide(bar): global vx, vy if bar == bar1: if keyboard.a: if bar.y > 0: bar.y -= 7 elif keyboard.z: if bar.y + bar.height < HEIGHT: bar.y += 7 else: # bar2 if keyboard.up: if bar.y > 0: bar.y -= 7 elif keyboard.down: if bar.y + bar.height < HEIGHT: bar.y += 7 if bar.colliderect(ball): # 10픽셀 앞으로 먼저 튀어오르기 if vx < 0: # bar1 경우 ball.x += 10 else: # bar2 경우 ball.x -= 10 vx = -vx * SPEED_UP # 속도의 x축 방향을 반대로하기 ''' 공이 윗측 진입하면서 반사판 윗측에 부딪힐 때 또는 공이 아래측 진입하면서 반사판 아래측에 부딪힐 때는 진입방향 그대로 반사 ''' if (vy > 0 and ball.centery < bar.centery) or \ (vy < 0 and ball.centery > bar.centery): vy = -vy * SPEED_UP sounds.bar.play() ball = Rect(WIDTH/2, HEIGHT/2, \ BALL_RADIUS * math.sqrt(2), BALL_RADIUS * math.sqrt(2)) bar1 = Rect(GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, BAR_W, BAR_H) bar2 = Rect(WIDTH - BAR_W - GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, BAR_W, BAR_H) bars = [bar1, bar2] def draw(): screen.clear() # 중심선 screen.draw.line((WIDTH/2, GAP_FROM_SCR), (WIDTH/2, HEIGHT - GAP_FROM_SCR), color='grey') # 반사판 for bar in bars: screen.draw.filled_rect(bar, 'white') # 점수 screen.draw.text(str(b1_score), (WIDTH/4, GAP_FROM_SCR), color='yellow', fontsize=60) screen.draw.text(str(b2_score), ((WIDTH/4)*3, GAP_FROM_SCR), color='yellow', fontsize=60) if b1_score == FINAL_SCORE or b2_score == FINAL_SCORE: gameover_draw() else: # 공 screen.draw.filled_circle(ball.center, BALL_RADIUS, 'white') def update(): global b1_score, b2_score # 공 ball_move_collide() # 반사판 for bar in bars: bar_move_collide(bar) # 게임시작 조건 if (b1_score == FINAL_SCORE or b2_score == FINAL_SCORE) or \ (b1_score == -1 and b2_score == -1): if keyboard.space: b1_score = 0 b2_score = 0 reset_ball() else: # 공 ball_move_collide() [Previous8.1 절차지향으로 개발하기 1chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong/8.1_procedural_dev1) [Next8.3 객체지향 개발이론 (객제지향 디자인이란)chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong/ood) Last updated 11 months ago --- # 8.1 절차지향으로 개발하기 1 | 파이썬-파이게임 제로 (순한맛) 바로 아래 화면의 우리가 만들게 될 게임의 최종 결과물을 먼저 보고나면 코드 이해가 훨씬 쉬울 것이다. 우리는 이 게임을 두 가지 프로그래밍 패러다임([절자지향형](https://jjlee.gitbook.io/entry-python/paradigm/4.2-procedural) , [객체지향형](https://jjlee.gitbook.io/entry-python/paradigm/4.4-object-oriented) ) 각각을 사용한 두 가지 버전으로 만들어 볼 예정이다. 먼저 이전 절에서 예고했듯이 나만의 객체를 만들고 활용하는 것으로 배워 활용해 봄으로써 이제는 보편화되어 사용되는 객체지향형이 기존의 절자지향형과 어떠한 현격한 차이를 가지는지를 이해하고 객체지향 패러다임으로 입문하는 것이 이번 장의 과업이라고 할 수 있겠다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FWiW0s6KiHXvj7tqqlHkM%252Fpong1.png%3Falt%3Dmedia%26token%3D3bf148c2-b92d-422f-9d50-5eca267a525f&width=768&dpr=3&quality=100&sign=9a430ac7&sv=2) 우리는 이미 5-7장 통해 3개의 게임제작을 소화했기에 이미 튜토리얼 수준을 넘어섰다 볼 수 있다. 따라서, 이번 장부터는 이미 설명한 기본적인 내용에 대한 자세한 중복설명없이 빠르게 핵심적인 코딩에 대한 설명 위주로 진행하는 것이 여러분을 존중(?)하는 것이라 생각한다. 참고로 이번 게임제작에 필요한 사운드 등의 리소스들은 [이곳arrow-up-right](https://github.com/roboticsware/python-pygame_zero-samples/tree/main/breakout/images) 에서 다운로드 할 수 있으며 게임에 필요한 효과음은 이미 sounds 폴더에 저장되어 있다고 간주하고 시작하겠다. 또는 [커스텀 뮤 에디터arrow-up-right](https://github.com/roboticsware/mu/releases) 를 사용 중이라면 뮤 에디터의 작업 디렉토리인 (사용자계정)\\mu\_code\\examples\\pygame\_zero 안에서 해당 리소스들을 복사해 올 수 있다. 우선은 먼저 절차지향형으로성의 개발을 시작해 보자. 이 게임은 두 명의 유저가 공을 주고받는 탁구형식의 게임이나 구조적인 면에서는 공과 반사판을 사용한다는 점에서 이전에 만들어 본 [오리지널 블록격파 게임](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original) 의 확장판 정도로 여겨질 수 있다. 블록격파에서는 공을 반사시키는 반사판(또는 패들이라 불림)이 1개였으나, 여기서는 사용자별로 별도로 조작하는 2개의 반사판이 존재한다는 차이점 정도를 고려하면 되겠다. Copy import math import random # 게임화면 TITLE = 'pong' WIDTH = 800 HEIGHT = 600 GAP_FROM_SCR = 20 # 반사판 BAR_H = 100 BAR_W = 15 # 볼 vx = 5 vy = 5 BALL_RADIUS = 10 SPEED_UP = 1.05 # 점수 FINAL_SCORE = 11 b1_score = -1 b2_score = -1 # 배우객체들 ball = Rect(WIDTH/2, HEIGHT/2, \ BALL_RADIUS * math.sqrt(2), BALL_RADIUS * math.sqrt(2)) bar1 = Rect(GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, BAR_W, BAR_H) bar2 = Rect(WIDTH - BAR_W - GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, BAR_W, BAR_H) bars = [bar1, bar2] def reset_ball(): pass def ball_move_collide(): pass def gameover_draw(): pass def bar_move_collide(player): pass def draw(): screen.clear() # 중심선 screen.draw.line((WIDTH/2, GAP_FROM_SCR), (WIDTH/2, HEIGHT - GAP_FROM_SCR), color='grey') # 배우들 screen.draw.filled_circle(ball.center, BALL_RADIUS, 'white') for bar in bars: screen.draw.filled_rect(bar, 'white') # 점수 screen.draw.text(str(b1_score), (WIDTH/4, GAP_FROM_SCR), color='yellow', fontsize=60) screen.draw.text(str(b2_score), ((WIDTH/4)*3, GAP_FROM_SCR), color='yellow', fontsize=60) if b1_score == FINAL_SCORE or b2_score == FINAL_SCORE: gameover_draw() def update(): global b1_score, b2_score # 공 ball_move_collide() # 반사판 for bar in bars: bar_move_collide(player) # 게임시작 조건 if (b1_score == FINAL_SCORE or b2_score == FINAL_SCORE) or \ (b1_score == -1 and b2_score == -1): if keyboard.space: b1_score = 0 b2_score = 0 reset_ball() else: # 공 ball_move_collide() 🔢 그밖에 6장에서 만들어 본 블록격파 게임과 다른 점은 2인용 게임임에도 불구하고 1인용처럼 실행되자마자 곧바로 시작되면 당황스럽기 때문에, 두 게임유저가 준비되었을 때 스페이스 바를 눌러 플레이를 시작하고, 최종게임승패가 결정난 이후에도 다시 스페이스 바를 눌러 게임을 재시작 할 수 있는 기회가 제공된다. 이와 관련된 코드로는 22-23번 라인에서 점수의 초기값을 -1 로 정한 것으로 게임진행이 아직 시작 전 상태임을 구분하기 위한 목적이고, 스페이스 바를 누르면 72번 이하 라인이 적용되면서 플레이 상태로 들어간다. 🔢 위의 30번째 라인까지 코드는 게임무대의 배우객체들을 생성하는 것이다. 공(ball), 반사판(bar)와 2개를 생성한다. 각 객체를 만드는 부분은 기존의 오리지널 블록격파 게임과 크게 다르지 않다. 참고로 여기서 배우객체들은 Actor 클래스를 통해 생성한게 아니고, [Rect 클래스](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original#undefined) 를 통해 생성된 객체라는 것을 다시한번 유념하자. 현재는 절차지향형으로 개발하기로 했기때문에 필요한 기능들을 함수화(functionalize) 할 필요가 있다는 것을 배웠다. 이번 게임도 함수화를 진행해보자. **코딩에서 항상 염두해 두어야 할 점은 나와 남을 위해 가독성이 좋고, 추후 수정에 대비한 유지보수(maintenance) 측면을 늘 고려해야 한다.** 사실상 함수도 그러한 이유로 출연했다고 할 수 있다. 함수의 주목적은 코드중복을 줄이는 것으로 그러나, 이번 게임에서도 여러 객체가 공통으로 사용할 함수로서 묶을 만한게 많지 않은데, 그나마 동일한 목적과 기능의 반사판 객체가 2개가 등장하고 있어, 이 반사판들이 함께 공유할 수 있는 함수목적에 부합하는 **bar\_move\_collide** 함수를 추가하자. 그밖에 나머지 함수들(**reset\_ball**, **ball\_move\_collide**, **gameover\_draw**)은 공통사용 목적이라기보다 기능적으로 분리된 함수들이라 하겠다. 🔢 다음 절부터 위에 언급된 함수들의 구현체를 만들어보기로 하고, 지금은 그 함수들을 제외한 나머지 코드들에 대해 더 검토해보자. 지금까지 모든 과정을 함께 공부해 온 여러분의 실력상 대부분은 이전에 이미 배웠던 것들의 약간의 변형이라 찬찬히 드려다보면 대부분 이해가 될 것으로 믿는다. 여러분들은 이전에 블록격파 게임을 마무리하면서 제안했던 [추가 도전과제들](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original#undefined-5) 에 대해 기억하고 있는가? 그 도전과제들을 스스로 추가 기능구현해 봤는지 모르겠다. 이번 퐁 게임에서는 그 도전과제들에서 제안되었던 추가기능들을 포함해서 구현하도록 하겠다. 따라서, 게임의 흥미를 더하기 위해 두 게임 유저가 빠른 승부를 내지 못하고 계속 랠리(Rally)가 이어지면 점점더 공의 움직임의 속도를 빠르게 하기 위해 18라인에 **SPEED\_UP**의 값만큼 속도를 증가시키는 알고리즘이 추가될 예정이고 그 때 사용하려고 [매크로arrow-up-right](https://ko.wikipedia.org/wiki/%EB%A7%A4%ED%81%AC%EB%A1%9C_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)) (Macro)를 미리 선언해 두었다. 그럼, 이제 다음 절에서부터 본격적으로 우리의 사용자 함수의 구현체를 구체적으로 채워보도록 하겠다. [Previous8\. 퐁(Pong) 게임 만들기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong) [Next8.2 절차지향형으로 개발하기 2chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong/8.2_procedural_dev2) Last updated 11 months ago --- # 6.2 배우들의 움직임 구현하기 | 파이썬-파이게임 제로 (순한맛) 이번 절에서 우리는 우리 무대의 등장인물을 각각 움직여 보도록 하겠다. 가장 먼저는 이 게임에서 유일하게 사용자가 조작할 수 있는 반사판을 화면 하단의 좌우방향으로만 움직이되 화면의 끝을 넘어가지는 못하도록 코딩하겠다. Copy TITLE = 'Breakout' WIDTH = 800 HEIGHT = 600 GAP_FROM_SCREEN = 50 ball = Actor('ball', (WIDTH / 2, HEIGHT / 2)) bar = Actor('bar', (WIDTH / 2, HEIGHT - GAP_FROM_SCREEN)) # 4행*8열짜리 블록더미 만들기 blocks = [] for block_row in range(4): for block_col in range(8): block = Actor( 'block', (block_col * 100, block_row * 32 + GAP_FROM_SCREEN), anchor=('left', 'top') ) blocks.append(block) def draw(): screen.blit('space', (0, 0)) ball.draw() bar.draw() for block in blocks: block.draw() def update(): # 반사판의 이동을 화면 안에 안에 가두기 if bar.left < 0: bar.left = 0 if bar.right > WIDTH: bar.right = WIDTH def on_mouse_move(pos): x, y = pos bar.centerx = x 🔢 34-36 라인에서 마우스가 움직이는 이벤트가 있을 때마다 콜백호출 되는 **on\_mouse\_move** 콜백함수를 사용하도록 하자. 해당 함수에 대한 자세한 사용법은 [파이게임제로 라이브러리 문서arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/hooks.html#on_mouse_down) 를 살펴보면 될 것이다. 간단히 설명하면 _on\_mouse\_move 함수의 첫번째 인자인 pos 는 움직이는 마우스 포인터의 현재 위치(x, y좌표값)를 저장하고 있는 것_으로, 35번 라인에서 pos안에 두 값_(x, y좌표값)_을 각각의 x, y 변수값의 분리해낸 후, 우리는 이것의 x좌표값을 bar.centerx 위치(bar객체 중심위치의 x좌표)와 동조화시키도록 하면서, 마우스를 움직일 때마다 bar 객체가 계속 따라서 움직이게 할 수 있는 것이다. 이후 움직이는 bar 객체가 게임화면을 넘어서까지 이동할 수 없도록 update 콜백함수 내의 29-32번 라인처럼 만약 화면좌측 끝을 넘어간다면(bar.left < 0 라면) 그때는 실제로 음수값을 갖게하면 안되기 때문에 최대치인 0값을 그대로 유지하게 하고, 마찬가지로 화면우측 끝을 넘어간다면(bar.right > WIDTH 라면) 그 이상의 값을 갖지 못하도록 bar.right의 최대치인 WIDTH를 유지하도록 하면된다. 이러한 방식으로 그 움직임의 최대치 경계를 지어주면 된다. Copy TITLE = 'Breakout' WIDTH = 800 HEIGHT = 600 GAP_FROM_SCREEN = 50 ball = Actor('ball', (WIDTH / 2, HEIGHT / 2)) bar = Actor('bar', (WIDTH / 2, HEIGHT - GAP_FROM_SCREEN)) # 4행*8열짜리 블록더미 만들기 blocks = [] for block_row in range(4): for block_col in range(8): block = Actor( 'block', (block_col * 100, block_row * 32 + GAP_FROM_SCREEN), anchor=('left', 'top') ) blocks.append(block) # 볼의 초기 (방향이 있는)속도값 vx = 5 vy = -5 def draw(): screen.blit('space', (0, 0)) ball.draw() bar.draw() for block in blocks: block.draw() def update(): global vx, vy # 반사판의 이동을 화면 안에 안에 가두기 if bar.left < 0: bar.left = 0 if bar.right > WIDTH: bar.right = WIDTH # 각각의 x와 y 방향으로 거리 vx와 vy만큼 공을 이동시키기 ball.move_ip(vx, vy) def on_mouse_move(pos): x, y = pos bar.centerx = x 🔢 두번째로 공을 움직임을 주기 위해 추가된 코드들을 살펴보도록 하겠다. _공을 이동시키려면 기본적으로 파이게임 좌표계 안에서 어떤_ _**방향(direction)**__으로 얼마만한_ _**속력(speed)**__으로 이동하겠는가라는 기준을 정해야 할 것이다. 이 두 가지 속도와 방향이 합쳐진 물리량을_ _**속도(velocity)**__라고 부른다._ 지금은 물리시간은 아니니 이 개념의 명확한(?) 이해가 필요하면 [나무위키arrow-up-right](https://namu.wiki/w/%EC%86%8D%EB%8F%84) 등을 찾아보자^^; 다만, 어렵게 생각할 필요는 없고, 기존의 파이게임좌표계를 기준하면 되고, x축에서 좌측방향(음수) 또는 우측방향(양수)으로 한번 이동할 때는 얼마크기만큼 이동하겠느냐의 개념이고, 마찬가지로 y축에서 상측방향(음수) 또는 하측방향(양수)으로 한번 이동할 때 얼마크기만큼 이동하겠느냐 값이고, 각각의 두축의 속도의 초기값 갖고 시작해야 하기 때문에 21-22라인에 그 내용을 글로벌 변수로 코딩했다. **글로벌 변수로 사용했다는 의미는 이 변수값이 함수 여러곳에 사용될 수 있거나 또는 함수 안에 이 변수값을 바꿀 수 있고, 그 바뀌어진 값이 다음번 함수 진입시마다 계속 유지된 상태여야 한다는 목적을 실현시키기 위한 의도적인 코딩이다.** 이번의 경우는 후자의 목적실현에서 글로벌 변수로 코딩했다. 그리고, 이를 update 함수 안에 적용하여 초당 60회 호출이 되는([5.2절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.2_flying) 에서 이미 언급) update 함수 호출 때마다 객체를 계속 이동시키기 위해 41번 라인에 ball 객체가 갖고 있는 [**move\_ip 함수(메소드)**arrow-up-right](https://www.pygame.org/docs/ref/rect.html#pygame.Rect.move_ip) 를 활용했다. 이 함수의 인자로 우리가 설정한 두축의 초기 속도값을 전달하면, 호출될 때마다 해당 객체의 현재 위치에서 그 속도의 다음위치를 이동시켜주게 된다. _만약 move\_ip 함수}를 사용하기 싫으면 그 대신에 다음과 같이 코딩해도 똑같은 의미이고, 결국 ball 객체의 x, y 위치값을 속도 변화량인 vx, vy 만큼 매번 누적변화를 만든다는 의미이다._ Copy ball.x += vx ball.y += vy 우리는 지난 절에서 많은 것을 배우느라 머리 속에 넣을 것이 많아 약간 피곤(?)해졌을 수 있어서, 여기서 간단히 이정도로 이번 절을 마무리하겠다. 다음 절에서는 더 재미있는 공을 반사시키고, 블록을 격파하는 알고리즘에 대해 배워볼 것이다. [Previous6.1 게임무대에 배경과 배우 등장시키기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.1_bg_actors) [Next6.3 공의 반사와 블록격파 구현하기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.3_reflection_breakout) Last updated 6 months ago --- # 8.3 객체지향 개발이론 (객제지향 디자인이란) | 파이썬-파이게임 제로 (순한맛) 이제 우리는 드디어 배틀시티 게임의 [객체지향형](https://jjlee.gitbook.io/entry-python/paradigm/4.4-object-oriented) 버전을 만들기를 도전해 볼 차례이다. 그러나 배틀시티 게임의 객체지향형 버전을 만들어 보기 전에 [절차지향형](https://jjlee.gitbook.io/entry-python/paradigm/4.2-procedural) 과 객체지향형 패러다임의 차이를 그림으로 표현한 것을 통해 우리가 최종 목표하는 둘의 근본적 구조적 차이를 먼저 이해해 보도록 하자. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252Fu9SvHj4C2jnIpkjgvLp1%252Fimage.png%3Falt%3Dmedia%26token%3Df4ec7648-e15c-45d2-89f5-f7b0c17457f6&width=768&dpr=3&quality=100&sign=5a7a6628&sv=2) 이미지 출처: [https://www.altexsoft.com/blog/pros-and-cons-of-java-programming/arrow-up-right](https://www.altexsoft.com/blog/pros-and-cons-of-java-programming/) ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252Fal7bCXmRUqPGG1lli3io%252Fimage.png%3Falt%3Dmedia%26token%3D65915e1d-4eb0-40a6-979c-e4b5acdc6aa4&width=768&dpr=3&quality=100&sign=5a1e2642&sv=2) 이미지 출처: [https://en.wikipedia.org/wiki/Coupling\_(computer\_programming)arrow-up-right](https://en.wikipedia.org/wiki/Coupling_(computer_programming)) 절차지향형 패러다임은 함수간의 상호 다대다(many-to-many) 관계로 상호간에 강하게 묶여(이를 [겹합도(couping)arrow-up-right](https://en.wikipedia.org/wiki/Coupling_(computer_programming)) 가 강하다/높다라고 표현함, 상단의 결합도 관련한 이미지 참조) 서로서로 제안없이 호출하고 의존관계(dependancy)를 맺는 방식의 구조를 갖게 된다. 이게 작은 크기의 프로그램에서는 어느 정도 감당할 수 있지만, 소프트웨어는 한번 만들고 끝인게 아닌 계속적인 진화를 거듭하는 존재로서 시간이 지나 점점더 새로운 기능이 추가되고 기존 기능의 기능개선의 수정과 보완이 이뤄지면서 크기와 양적인 면에서 복잡도(complexity)가 가파르게 증가하여 감당하기 어려운 수준까지 이르게 되기 쉽상이다. 결합도가 높으면 무엇이 문제인가? 기능의 개선이나 기능추가를 하려면 기존 코드수정이 반드시 필요한데, 만약 상호간의 결합도가 높다면, 어느 한부분을 고치면 그게 상당히 많은 다른 코드영역에 영향을 미치게 되고, 그로 인해 다른쪽 코드영역의 재수정이 필요하고, 그 재수정은 또 다른 코드영역에 영향을 미치면서 [나비효과arrow-up-right](https://en.wikipedia.org/wiki/Butterfly_effect) 처럼 퍼져나가기 때문이다. 높은 복잡도는 이미 잠재적인 소프트웨어의 결함(버그, bug)이 내재되기 쉽고, 코드의 유지보수(maintenance)를 어렵게 만든다. 결국 이처럼 명확한 한계를 갖고 있는게 방법을 계속 고집할 순 없고, 애초부터 이런 한계를 뛰어넘을 수 있는 새로운 패러다임의 프로그래밍 디자인이 필요하게 되었던 것이다. 기존의 함수 상호간의 수많은 직접 참조는 엄청난 복잡성이 생기는 근본 원인이므로 전체 소프트웨어 구성에 있어서 각자의 역할과 책임단위로 각각 독립된 코드 덩어리로 분리할 수 있는 객체라는 개념을 도입해 상대에 대한 직접 호출없이 해당 상대 객체의 역할과 책임이 필요한 시점에 그 수행을 요청하는 방식으로 각각의 독립된 객체 상호간의 협력구조로 전체 프로그램을 실행한다. 이러한 방식은 상호간의 결합도를 매우 낮출 수 있어(상단의 결합도 관련한 이미지 참조) 각각 객체의 코드 수정은 객체 내부에서만 영향을 미칠 뿐이지 자신의 넘어 다른 객체에게까지 미치는 영향은 최소화된다. 이러한 객체지향형 패러다임을 더 확장시켜 나아가면, 그 객체들을 모아 하나의 독립된 부품([componentarrow-up-right](https://en.wikipedia.org/wiki/Software_component) ) 형태로 구성할 수 있고, 그 독립된 부품들을 조립해서 하나의 거대한 최종 목적의 소프트웨어 제품을 만드는 방식까지 나아갈 수 있다. 이러한 개발방식을 컴포넌트 기반 개발(CBD([Componenet-Based Developmentarrow-up-right](https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8_%EA%B8%B0%EB%B0%98_%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EA%B3%B5%ED%95%99) )) 이라고 불린다. 이 방식의 장점은 잘만들어 놓은 독립된 부품은 다른 소프트웨어 개발에도 해당 부품의 재사용이 가능한 코드 재사용성([reusabilityarrow-up-right](https://en.wikipedia.org/wiki/Reusability) )을 높일 수 있단 점이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FAGqmT7aWRYaGKmcKCBie%252Fimage.png%3Falt%3Dmedia%26token%3D88022025-d53d-46b1-9335-4eb382aefd37&width=768&dpr=3&quality=100&sign=599549&sv=2) CBD개발에 따른 소프트웨어의 구조( 이미지 출처: [https://ars.els-cdn.com/content/image/3-s2.0-B978155860900650004X-f01-08-9781558609006.gifarrow-up-right](https://ars.els-cdn.com/content/image/3-s2.0-B978155860900650004X-f01-08-9781558609006.gif) ) _객체지향 개발이 위에서 언급된 것처럼 장점만 갖고 있는가? 세상에 어떤 것도 그러한 것은 없을 것 같은데 역시 객체지향도 단점들이 있는데, 무엇보다 코딩을 시작하기에 앞서 각각의 객체자체를 잘 설계(디자인)하는데에 많은 시간과 에너지가 들어간다는 것이다. 설계를 위해 사전에 알아야 할 개념들이 많고, 그러한 개념들을 능숙하게 사용하면서 설계를 잘하는 수준까지 올라가는데도 경험과 시행착오를 통해 상당한 시간과 노력이 소요된다는 점이다._ 그럼에도 불구하고 우리는 수준 높은(?) 개발자가 되기 위해선 피해갈 수 없고 반드시 익혀야 하기에 익숙해지기 전까지 처음에는 다소 어렵게 느껴지더라도 포기하지 말고 인내하며 끝까지 도전할 수 있기를 기대해 본다. 객체지향 설계([OOD: Object-Oriented Designarrow-up-right](https://simple.wikipedia.org/wiki/Object-oriented_design) )를 시작하기에 앞서, 기존 절차지향과는 다른 사고의 전환이 필요한데 우리가 만들고자 하는 프로그램에 구성과 구조적인 디자인을 할 때, 이제 우리는 객체(오브젝트) 중심으로 사고하는게 필요하다. 그 말은 우리 프로그램에서 객체가 되어야 할 것들을 식별하고, 그들간의 협력구조를 어떻게 만들까를 고민하는 과정이다. 그런데, 다행히도 우리는 엔트리 블록코딩부터 알게 모르게 자연스럽게 그런한 방식의 개발에 이미 익숙해져 있다. 특별히 우리가 만들고 있는 게임프로그램에 있어서 객체들의 식별은 용이한데 게임 무대에 등장하는 등장인물(배우)들 중심으로 객체지향을 구성하면 되기 때문이다. 객체가 식별된 이후에는 각 객체들이 가져야 할 역할(Role)과 책임(Responsibility), 그리고, 각 객체들이 협력을 위해 주고받아야 할 메시지(Message)에 대해서 고민을 시작해야 한다. 참고로 책임은 객체의 외부 곧, 객체의 이용자들에게 제공해 줄 수 있는 서비스의 목록(참고로 객체지향에서는 이를 공용 인터페이스(public interface)이라 지칭)이다. 먼저 기억해야 할 것은 _함수화 과정에서도 마찬가지였는데 코드의 어느 부분을 어디까지 함수화 하느냐에 대한 것에 명확한 답이 있는게 아니듯이 OOD를 할 때도 이렇게 하는게 맞다라는 100% 정답이 없다. 설계를 할 때 이런 기준을 갖고 하는 것을 원칙으로 삼아라 라고 하는 원칙적(예를들어,_ [_SOLID 원칙_arrow-up-right](https://ko.wikipedia.org/wiki/SOLID_(%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5_%EC%84%A4%EA%B3%84)) _) 기준제시가 있을 뿐이다._ 사실 설계에 대한 부분을 제대로 공부하고자 한다면, 대학에서서 최소한 한 학기 분량의 내용이라 이 책의 수준에서 그렇게까지 진도가 나가는 것은 너무 과하다. 따라서, 난이도 조절상 모든 설계원칙을 사전에 세세하게 공부하고 이를 일일이 적용하고 설명하기보다 여러분의 이해에 큰 무리가 되지 않는 어느정도 타협하는 선에서객체지향 설계를 적용할 예정이고, 더 심도있는 공부와 함께 그 공부한 내용의 설계를 다양하게 바꿔 적용해 보는 것은 여러분 각자의 몫으로 남겨 놓아야 할 수 밖에 없음을 먼저 양해바란다. [Previous8.2 절차지향형으로 개발하기 2chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong/8.2_procedural_dev2) [Next8.4 객체지향 개발이론 (사용자 정의 객체 만들기, 상속)chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong/oop_inheritance) Last updated 1 year ago --- # 부록 | 파이썬-파이게임 제로 (순한맛) [게임을 단 한 개의 실행파일로 패키징 하기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/appendix/packaging) [뮤 에디터 단축키 모음chevron-right](https://jjlee.gitbook.io/python-pygame_zero/appendix/shortcuts) [Previous10.4 네트워크 게임으로 만들기 2chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.4_dev_net_2) [Next게임을 단 한 개의 실행파일로 패키징 하기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/appendix/packaging) --- # 10. 퐁(Pong)을 네트워크 게임으로 만들기 | 파이썬-파이게임 제로 (순한맛) 이번 장에서는 8장에서 만들어본 퐁(Pong) 게임을 네트워크 게임으로 만들어, 기존 컴퓨터하고 대전하거나 또는 한 컴퓨터 앞에서 둘이 같이 2인용 게임을 하는 로컬용 게임이 아닌 네트워크 상에 원격의 다른 컴퓨터에 앉아 있는 실제 사람 유저(user)와 함께 대전을 할 수 있는 네트워크용 게임으로 변신시켜보자. [Previous9.7 객체지향으로 개발하기 4chevron-left](https://jjlee.gitbook.io/python-pygame_zero/battle_city/8.9_oop_dev4) [Next10.1 네트워크 게임방식의 이해와 라이브러리 설치chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.1_nethelper_install) Last updated 12 months ago --- # 6.5 (보너스) 오리지널 게임처럼 만들어 보기 | 파이썬-파이게임 제로 (순한맛) 이번 절은 여러분의 추가적인 공부를 위한 보너스이다. 기존 게임을 오리지널 게임처럼 이미지적 요소없이 그래픽적 요소들을 오직 코딩으로만 표현한 방식으로 바꾸는데 있다. 눈치챘겠지만, UI적 요소의 변경이 대부분이라 게임 운영 알고리즘 자체는 크게 바꿀게 없고, 기존 게임무대 배경은 없에고, 이후 단지 게임의 배우였던 공, 반사판, 블록더미를 코딩으로 직접 만들고 그리는 부분만 바꾸면 되는 과정이다. 그래서, 각각을 하나씩 어떻게 코딩으로 그릴 수 있는지 살펴보도록 하겠다. 먼저 코딩의 이해를 돕기 위해 우리가 만들게 될 게임의 최종결과를 미리 살펴보자. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FeI2KEuqVAttXK49rs43m%252Fimage.png%3Falt%3Dmedia%26token%3Dbb491168-2f8d-4226-9f6a-60ea2c9e628b&width=768&dpr=3&quality=100&sign=58f06d34&sv=2) #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original#undefined) 반사판 그리기 제일 그리기 쉬운 것부터 살펴보자. 반사판은 그냥 직사각형(Rectangle) 하나이다. 파이게임에서 직사각형을 만들고 그 내부를 특정(여기서는 빨강)색으로 채워그리는 코드는 다음과 같다. circle-info 사각형 Rect 클래스(class) Rect(화면상의 위치할 x좌표, 화면상의 위치할 y좌표, 가로길이, 세로길이) 또는 Rect((화면상의 위치할 x좌표, 화면상의 위치할 y좌표), (가로길이, 세로길이)) Copy TITLE = 'Breakout' WIDTH = 800 HEIGHT = 600 RED = (200, 0, 0) GAP_FROM_SCREEN = 50 bar = Rect(WIDTH / 2, HEIGHT - GAP_FROM_SCREEN, 100, 15) def draw(): screen.draw.filled_rect(bar, RED) 여기서 Rect(사각형) 클래스로 생성한 Rect객체는 파이게임제로에서는 내장객체처럼 사용할 수 있기 때문에 어떤 추가적인 모듈, 라이브러리의 import 없이도 사용했다는 것을 알 수 있다. 생성한 사각형(rect) 객체의 내부를 특정 색상으로 채우는 **filled\_rect** 메소드는 파라미터로 내부를 칠할 색상을 지정하고 있다. **색상값은 빛의 3원색(빨강, 초록, 파랑)의 세기값(최소 0 ~ 최대 255)의 조합으로 표현하며, 연이은 3원색의 각각의 광원값을 (Red 광원값, Green 광원값, Blue 광원값) 형태의** [**튜플**](https://jjlee.gitbook.io/python-pygame/boilerplate#tuple) **데이터형태로 제공해야 한다.** 색생표현법에 관한 내용은 [이전 서](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat#rgb-10) 에서 자세히 설명하였으므로 해당 부분을 참고하거나, 워낙 인터넷에 정리된 자료가 많은데 [이 블로그arrow-up-right](https://phominator.tistory.com/42) 를 참조할 수 있다. #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original#undefined-1) 공 그리기 우리는 [지난 절](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.4_consider_collisions) 을 통해 공의 본질이 기본적으로 (정)사각형 곧 Rect 라는 것을 알았고, 따라서 공 역시도 위에서 만들어본 반사판처럼 Rect 객체로 생성해야 한다. 이것을 이해한다면, 공을 만드는데 어려움은 전혀 없다. 다음의 코드를 통해 만들 수 있다. Copy HEIGHT = 600 WHITE = (200, 200, 200) BALL_RADIUS = 10 ball = Rect(WIDTH / 2, HEIGHT / 2, BALL_RADIUS * 2, BALL_RADIUS * 2) def draw(): screen.draw.filled_circle(ball.center, BALL_RADIUS, WHITE) 다행이도 Rect 형태의 공일지라도 우리 눈에는 원처럼 보일 수 있게 그려주는 함수가 준비되어 있다. **screnn.draw.filled\_circle** 메소드에 원의 중심좌표(공의 rect객체가 만들어지는 순간 center값은 자동으로 생성됨)와 반지름, 그리고, 원을 채울 색상값을 넘겨주면 된다. 다만, 공을 Rect로 만들 경우, 한 가지 추가로 고민할게 있는데, 공과 반사판 충돌검사는 천상 Rect간 출동로만 확인할 방법이 없게 된다는 것이고, 이 경우, 지난 시간에 언급했던 자세히 보면 공과 반사판이 정확히 닿지 않았는데도 충돌검사상 닿은 것으로 판단해 공을 반사시키는 것에 대한 버그를 기억할 것이다. 이 부분을 어떻게 해결하면 좋을까? pgzhelper 모듈을 떠올릴텐데, Rect된 공은 이미지가 아니라서, Actor로 만들 수도 없어서 해당 모듈의 도움도 요원한 상태이다. 이런 경우, _공의 충돌검사의 기준이 될 Rect와 우리 눈에 보이는 공의 원형외관의 관계설정을 통해 해결의 아이디어를 만들 수 있다. 우리는 기존에는 공의 밖을 둘러싸는 Rect에 대한 생각만 했지, Rect가 공의 안쪽에 있을 수도 있다는 것이다._ ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FLxvNdH2vNm7By3wHlVsS%252Fimage.png%3Falt%3Dmedia%26token%3D5c4a0554-e8c3-41ca-a2ed-99d7c91a386e&width=300&dpr=3&quality=100&sign=9349b829&sv=2)![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FlasyccwLHY0zU7UR0502%252Fimage.png%3Falt%3Dmedia%26token%3D3c948aaa-ae37-42f7-80c2-d84d502cdf5d&width=300&dpr=3&quality=100&sign=5cc9556c&sv=2) Rect를 공의 안쪽에 위치시켜 반사판과 충돌검사를 하게 되면 최소한 우리 눈에 공이 닿지도 않았는데, 반사되는 것으로 보이는 버그는 수정할 수 있게된다. 해당 내용을 반영하여 코드를 수정하는 다음과 같다. 참고로 _sqrt 는 우리가 수학에서 배운 루트(root)의 의미이고, 이를 코딩으로 표현하려면 math 라이브러리를 import해 sqrt 함수를 사용해 계산해야 한다._ Copy import math HEIGHT = 600 WHITE = (200, 200, 200) BALL_RADIUS = 10 ball = Rect(WIDTH / 2, HEIGHT / 2, BALL_RADIUS * math.sqrt(2), BALL_RADIUS * math.sqrt(2)) def draw(): screen.draw.filled_circle(ball.center, BALL_RADIUS, WHITE) #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original#undefined-2) 블록더미 그리기 블록더미 코드도 어려울 것은 없다. 간단히 기존코드에서 블록 한장한장을 객체로 생성할 때, 이 블록을 Rect로 생성하면 되는 것이다. 이를 코드로 구현하면 다음과 같다. Copy GAP_FROM_SCREEN = 50 GOLD = (205, 145, 0) blocks = [] for block_row in range(4): for block_col in range(8): block = Rect( block_col * 100 + 2, block_row * 25 + GAP_FROM_SCREEN, 96, # 블록의 가로길이 23 # 블록의 세로길이 ) blocks.append(block) def draw(): for block in blocks: screen.draw.filled_rect(block, GOLD) 여기서 블록의 가로길이는 기존에 이미지로 그릴 때와는 달리 100픽셀이 아니고, 96픽셀인 이유는 이웃하는 블록간의 서로 빈틈없이 블록을 연이어 그려 붙이면 블록더미 안의 블록 하나하나를 구분할 수 없게 보이기 때문에(맨 위의 게임의 최종결과 화면 참조), 각각의 블록 좌우로 2픽셀씩 아무것도 없는 빈 여유공간을 주기위해서 이다. 이 부분은 블록을 그려야 하는 위치선정시에도 고려해야 하는데, 따라서, block\_col \* 100 + 2 라고 추가로 2를 더한 것은 그 2픽셀의 빈공간을 넘어서 그 다음 블록을 그려야하기 때문이다. 이 부분을 잘 이해했다면, 블록의 세로길이가 25픽셀 아니고, 23픽셀인 이유도 마찬가지이고, 블록간의 세로방향의 빈공간은 2픽셀의 여유를 갖는다. #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original#undefined-3) 배경화면 대신 화면 지우기 기존에 이미지형 무대 배경화면은 오리지널 게임에서는 없기 때문에 해당 코드는 제거하고, 대신에 등장 배우들을 그리기 전에 이전 그려진 것을 깨끗이 지우고 그 위에 그려야만 이전 잔상과 겹치지 않게 때문에 무대배경은 화면을 지우는 다음 코드로 대치한다. Copy screen.clear() #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original#undefined-4) 코드를 다 모아 종합하기 이제 위에서 각각 그렸던 게임배우들을 다 종합한 전체 코드를 살펴보자. 서두에 언급했던 것처럼 게임 운영 알고리즘 자체는 바뀐게 없고, 단지 무대배경을 없에고, 게임의 배우들였던 공, 반사판, 블록더미를 코딩으로 직접 만들고 그리는 부분만 바뀌어져 있다. Copy import math TITLE = 'Breakout' WIDTH = 800 HEIGHT = 600 WHITE = (200, 200, 200) RED = (200, 0, 0) GOLD = (205, 145, 0) GAP_FROM_SCREEN = 50 bar = Rect(WIDTH / 2, HEIGHT - GAP_FROM_SCREEN, 100, 15) BALL_RADIUS = 10 ball = Rect(WIDTH / 2, HEIGHT / 2, BALL_RADIUS * math.sqrt(2), BALL_RADIUS * math.sqrt(2)) # 4행*8열짜리 블록더미 만들기 blocks = [] for block_row in range(4): for block_col in range(8): block = Rect( block_col * 100 + 2, block_row * 25 + GAP_FROM_SCREEN, 96, # 블록의 가로길이 23 # 블록의 세로길이 ) blocks.append(block) # 볼의 초기 (방향이 있는)속도값 vx = 5 vy = -5 def draw(): screen.clear() screen.draw.filled_rect(bar, RED) screen.draw.filled_circle(ball.center, BALL_RADIUS, WHITE) for block in blocks: screen.draw.filled_rect(block, GOLD) def update(): global vx, vy # 반사판의 이동을 화면 안에 안에 가두기 if bar.left < 0: bar.left = 0 if bar.right > WIDTH: bar.right = WIDTH # vx와 vy만큼 속도로 공을 이동시키기 ball.move_ip(vx, vy) # 공이 외쪽 또는 오른쪽벽에 부딪힐 때 if ball.left < 0 or ball.right > WIDTH: vx = -vx # 속도의 x축 방향을 반대로하기 sounds.wall.play() # 공이 위쪽벽에 부딪힐 때 if ball.top < 0: vy = -vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 공이 반사판에 부딪힐 때 if ball.colliderect(bar) == True: ball.y -= 10 # 10픽셀 수직으로 먼저 튀어오르기 vy = -vy # 속도의 y축 방향을 반대로하기 sounds.bar.play() # 공이 블록과 부딪힐 때 b_index = ball.collidelist(blocks) if b_index != -1: vy = -vy # 속도의 y축 방향을 반대로하기 sounds.block.play() blocks.pop(b_index) # 게임종료 if ball.bottom > HEIGHT: sounds.die.play() exit() if not blocks: sounds.win.play() vx = 0 vy = 0 def on_mouse_move(pos): x, y = pos bar.centerx = x #### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original#undefined-5) 추가 도전 과제 이번 블록격파 게임은 여기까지 제작해 보는 것으로 종료하겠다. 그러나, 게임성을 더 높이기 위해 더 추가해 볼 기능들을 얼마든지 더 있다. 예를들어 게임의 시작과 끝까지 공의 속력변화가 없어서 게임이 쉽다고 느껴지고 도전정신이 살아나지 않는 것을 방지하기 위해 격파된 블록의 수가 많아지면 많아질수록 또는 반사판으로 공을 받아낸 횟수가 많아지면 많아질수록 특정시점을 기준으로 공의 속력을 조금씩 높여가는 방법이 있겠다. 그밖이 단순이 공을 받아내는데 것으로 만으로는 사용자의 자유도가 적은 편이라 흥미가 떨어질 수 있으니, 사용자가 공을 반사판의 좌우측 특정 부분으로 의도적으로 받아내느냐에 따라 공의 반사각도 변화를 받게하는 방법을 추가하는 것도 게임성을 높일 수 있는 좋은 아이디어가 되겠다. 추가 기능구현에 대한 과제는 여러분에게 맡겨놓겠고, 여기까지 공부하느라 여러분들도 수고가 많았다. _앞으로 몇 개의 게임을 더 만들어보면서 제작과정에서 만나는 문제상황을 돌파할 알고리즘을 답을 보기 전에 먼저 스스로 고민해 보고, 그 알고리즘을 텍스트코딩 기법 안에 어떻게 해결해 나가는 것까지 내것으로 소화해 나간다면_ 어느새 **텍스트코딩이란게 내가 살아가면서 만나게 되는 다양한 세상의 문제해결을 위해 두려움 없이 꺼내어 쓸 수 있는 내 소유의 도구 또는 스킬셋(skillset) 중에 하나로 자리잡게 될 것이다.** 그날이 올 것을 목표하고 기대하며 다음게임으로 넘어가 보자! [Previous6.4 충돌검사시 고려할 것들chevron-left](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.4_consider_collisions) [Next7\. 트윈비(TwinBee) 슈팅게임 만들기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/twinbee) Last updated 6 days ago --- # 8.8 (보너스) 파이게임제로 예제버전 2 | 파이썬-파이게임 제로 (순한맛) 패들(Paddle)과 테니스공(TennisBall) 객체 먼저 살펴보자. 두 객체 모두 이전에 만들어 반사판(Bar), 공(Ball) 객체과 크게 다르지 않아 이해에 큰 어려움은 없을 것 같다. 필자의 것과 크게 다른 부분은 패들객체와 볼객체에서 각자에게서 충돌에 대한 처리를 하는 부분이 없고, 메인객체인 게임객체(Game)에서 그 부분을 처리하게 된다. Copy class Paddle(Rect): """ Paddle represents one player on the screen. It is drawn like a long rectangle and positioned either left or right on the screen. Two helper methods move the paddle up or down. """ def __init__(self, start_x, start_y): super().__init__(start_x, start_y, PADDLE_WIDTH, PADDLE_HEIGHT) def up(self): if self.y - 5 > 40: self.y -= 5 def down(self): if self.y + self.height + 5 < HEIGHT - 40: self.y += 5 def draw(self): screen.draw.filled_rect(self, MAIN_COLOR) class TennisBall(): """ Represents a tennis ball on the screen """ def __init__(self, start_pos, dt): """ Initialize the tennis ball position and set the movement rate """ self.x, self.y = start_pos self.dx = self.dy = dt @property def pos(self): return (self.x, self.y) def move(self): self.x += self.dx self.y += self.dy def draw(self): screen.draw.filled_circle(self.pos, TENNIS_BALL_RADIUS, MAIN_COLOR) 그 밖에 차이점으로는 테니스공 객체는 Rect를 상속받지 않았고, 그렇기 때문에 객체 스스로 자신의 위치정보를 관리해야 할 필요성이 있으며, 그를 위해 38~40라인에서 보는 바와 같이 **pos** 라는 자신의 현재 위치를 나타내는 속성값을 별도로 갖고 있는 것을 알 수 있다. 또한 _**property**__라는 데코레이터(__**decorator**__) 를 사용한 파이썬 문법은 처음 등장했기 때문에 낫설기도 할텐데, 간단히 설명하면_ _**특정 데코레이터를 함수명 위에다 @와 함께 붙히면**_ _**기존 함수를 수정하지 않고 그 기능을 확장하는 방법을 제공하는 문법**__으로_ _여기서는 pos가 더이상 객체의 메소드 함수가 아니라 객체의 속성(property)처럼 간주되길 원한다는 목적_에서 붙혔다고 간단히 생각하자. 다시말해 해당 코드(38~40라인)는 아래 5번 라인과 100% 동일하다. Copy class TennisBall(): def __init__(self, start_pos, dt): self.x, self.y = start_pos self.dx = self.dy = dt self.pos = (self.x, self.y) 그럼, 저렇게 코딩하면 될 것을 왜 구지 데코레이터를 붙혀서 따로 분리해서 코딩했는가 라고 했을 때, 게임을 만든 저자가 **pos** 라는 속성값이 중요하기 때문에 강조해서 눈의 띄게 하려는 의미정도로 이해하면 될 것 같다. 이제 다음으로 게임객체를 살펴볼텐데, 거기서 이 property 데코레이터가 한번 더 사용되고, 그제서야 이 데코레이터의 진정한 목적을 알 수 있을 것이다. 그럼, 본격적으로 가장 핵심이면서 덩치도 크고 무거운 게임객체를 살펴보도록 하자. 게임객체는 자신에게 필요한 구성품과 같은 각각의 객체를 직접 생성하고 소멸시키는 것까지 제어한다. 13~14라인에 패들객체를 직접 생성하여 내부 속성값으로 할당하고 있다. 그리고, 연이은 16번 라인의 **set\_ball** 멤버함수를 통해 테니스공 객체를 생성하고 있다. 여기서 set ball 함수의 인자값으로 **ball\_pos** 속성값을 넘기고 있는 것을 알 수 있다. 18~23번 라인의 ball\_pos함수에 위에서 언급했던 그 데코레이터가 다시 등장하고 있는데, property 데코레이터를 사용해 본질상 함수인 것을 변수(속성값)처럼 활용할 수 있게 변경했다. 그래서, ball\_pos가 더이상 함수가 아닌, **self.ball\_pos** 로 호출하면서 마치 변수처럼 사용하고 있다. _ball\_pos 함수를 살펴보면 먼저와 테니스공의 pos와 같이 단순히 현재 위치값을 반환하는게 아니라, 반환 전에 공의 위치를 현재 패들의 위치에 따른 패들 중앙에 딱 달라붙은 공의 위치 값으로 위치를 최종 가공한 후에 반환하고 있는 것을 알 수 있다. 그렇다. 사실 property 데코레이터의 본래 활용목적은 이렇게 속성값이 반환시 어떤 추가적인 가공이 필요할 때 사용하기 위한게 주 목적인 것이다._ 이제 다른 나머지의 멤버함수에 구현체 대해서는 차근히 하나하나 살펴보면서 알고리즘적인 부분을 이해하면 될 것 같고, 마지막으로 51, 61라인에 사용된 collidepoint 함수에 대해 추가설명하면, 이름에서 알 수 있듯이 충돌검사를 하되, 기존엔 Rect 기반, 즉 사격형 기반으로 충돌검사를 했다면, Point 기반 즉, 픽셀단위 기반으로 충돌을 체크한다는 것이다. 픽셀단위로 확인한다니 더 정밀하게 충돌검사 할 수 있게단 기대가 된다. 그러나, 세상이치가 모든게 다 좋을 수 만은 없고, 약점이 있듯이, 이 경우도 그런데 더 세밀한 검사를 위해 더 많은 연산으로 컴퓨터의 자원을 많이 사용하게 되고, 최악의 경우 게임 전체 실행속도가 늦어지는 단점이 발생할 수 있음을 염두해 둬야 한다. 그래서, 우리는 항상 어느정도의 타협점(tradeoff)에서 코딩을 하게 되는 것이다. 참고로 이 함수는 파이게임제로의 모태가 되는 [파이게임 라이브러리 Rect 객체의 멤버함수arrow-up-right](https://www.pygame.org/docs/ref/rect.html#pygame.Rect.collidepoint) 이다. Copy class Game(): def __init__(self, player): self.active_player = player self.score_left = 0 self.score_right = 0 self.in_progress = False self.computer_acting = False # position paddles in the middle of the screen middle = HEIGHT/2 - PADDLE_HEIGHT/2 self.left_paddle = Paddle(20, middle) self.right_paddle = Paddle(WIDTH-40, middle) self.set_ball(self.ball_pos) @property def ball_pos(self): if self.active_player == LEFT_PLAYER: return (20 + PADDLE_WIDTH + 10, self.left_paddle.centery) else: return (WIDTH - 35 - PADDLE_WIDTH, self.right_paddle.centery) def set_ball(self, pos): # a ball is set on the paddle of last player that got a point dt = 5 if self.active_player == LEFT_PLAYER else -5 self.tennis_ball = TennisBall(pos, dt) def position_ball(self): # used when the player moves tha paddle and # game is not in progress self.tennis_ball.x, self.tennis_ball.y = self.ball_pos def score_for_left(self): self.in_progress = False self.active_player = LEFT_PLAYER self.score_left += 1 self.set_ball(self.ball_pos) def score_for_right(self): self.in_progress = False self.active_player = RIGHT_PLAYER self.score_right += 1 self.set_ball(self.ball_pos) def proceed(self): self.tennis_ball.move() # bounce from the walls if self.tennis_ball.y <= 40: self.tennis_ball.dy = -self.tennis_ball.dy if self.tennis_ball.y >= HEIGHT - 40: self.tennis_ball.dy = -self.tennis_ball.dy # bounce from the paddles if self.left_paddle.collidepoint(self.tennis_ball.pos): self.tennis_ball.dx = -self.tennis_ball.dx if self.right_paddle.collidepoint(self.tennis_ball.pos): self.tennis_ball.dx = -self.tennis_ball.dx # if we didn't bounce, then that is a score if self.tennis_ball.x <= 0: self.score_for_right() if self.tennis_ball.x >= WIDTH: self.score_for_left() if self.score_left == 11 or self.score_right == 11: self.in_progress = False # computer movement def computer_launch(self): self.in_progress = True self.computer_acting = False def computer_stop_acting(self): self.computer_acting = False def computer_move(self): # move towards the center of the screen when the ball is # travelling toward the enemy if self.tennis_ball.dx > 0: target_y = HEIGHT / 2 else: # when the ball is on other side of screen, just move # in general direction if self.tennis_ball.x > WIDTH / 2: delta = int(WIDTH * 0.25) if self.tennis_ball.dy < 0: target_y = self.tennis_ball.y - delta else: target_y = self.tennis_ball.y + delta else: # the ball is on our side, move with it rnd = random.randint(40, 200) if self.tennis_ball.dy < 0: target_y = self.tennis_ball.y - rnd else: target_y = self.tennis_ball.y + rnd target_y = max(40, min(target_y, HEIGHT - 80)) animate( self.left_paddle, y=target_y, duration=.50, on_finished=self.computer_stop_acting ) def computer_move_randomly(self): # move the paddle randomly during one second before launching the ball target_y = random.randint(40, HEIGHT - PADDLE_HEIGHT - 80) distance = abs(self.left_paddle.y - target_y) duration = max(0.1, distance / 200.0) self.computer_total_duration += duration if self.computer_total_duration + duration < 1.0: on_finished = self.computer_move_randomly else: on_finished = self.computer_launch animate( self.left_paddle, y=target_y, duration=duration, on_finished=on_finished ) def computer_act(self): if self.in_progress: # predict where the ball will move and move towards it self.computer_move() elif self.active_player == LEFT_PLAYER: # move randomly for a bit, then shoot the ball out if not self.computer_acting: self.computer_acting = True self.computer_total_duration = 0.0 self.computer_move_randomly() def draw(self): # slightly gray background screen.fill((64, 64, 64)) # show the score for the left player screen.draw.text( 'Computer: {}'.format(self.score_left), color=MAIN_COLOR, center=(WIDTH/4 - 20, 20), fontsize=48 ) # show the score for the right player screen.draw.text( 'Player: {}'.format(self.score_right), color=MAIN_COLOR, center=(WIDTH/2 + WIDTH/4 - 20, 20), fontsize=48 ) # a dividing line screen.draw.line( (WIDTH/2, 40), (WIDTH/2, HEIGHT-40), color=MAIN_COLOR) if self.score_left == 11: screen.draw.text( 'COMPUTER WINS!!!', color=MAIN_COLOR, center=(WIDTH/2, HEIGHT/2), fontsize=96 ) elif self.score_right == 11: screen.draw.text( 'PLAYER WINS!!!', color=MAIN_COLOR, center=(WIDTH/2, HEIGHT/2), fontsize=96 ) else: self.left_paddle.draw() self.right_paddle.draw() self.tennis_ball.draw() 여기까지해서 퐁 게임에 대한 것은 최종 마치도록 하겠다. 이번 보너스 절의 경우, 여러분 수준에서 내용이해에 조금 어려웠을 수 있겠단 생각이 든다. 그러나 어디까지나 말그대로 보너스이다. 모두가 다 이해하길 바라는 것이 아니고 여러분들 중에 누군가에게는 다소 수준보다 높은 지식이 도움이 될 것이라 추가된 것이고, 여러분들 중에 당장 이해를 다 못했을지라도 걱정하지 말라. 그것은 자연스러운 현상이고, 후추에 좀 더 여러 방면에서 지식이 더 자라났을 때, 다시 드려다보기를 몇 차례 더 하다보면 완전히 다 자기 것으로 소화될 날이 올 것이 틀림없다. 그럼, 여기서 마무리 하고, 다음 과에서 게속 객체지향 공부를 이어나가도록 하겠다. [Previous8.7 (보너스) 파이게임제로 예제버전 1chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong/8.7_bonus_dev1) [Next9\. 배틀 시티(Battle city) 게임 만들기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/battle_city) Last updated 1 year ago --- # 8.7 (보너스) 파이게임제로 예제버전 1 | 파이썬-파이게임 제로 (순한맛) ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong/8.7_bonus_dev1#undefined) 게임의 디자인 구조 이해하기 이번 보너스 절은 같은 퐁 게임이지만, [파이게임제로 라이브러리의 저자가 직접 만들어 라이브러리의 활용예제로서 함께 배포하는 코드arrow-up-right](https://github.com/lordmauve/pgzero/tree/main/examples/pong) 를 예제코드로 사용해 함께 코드분석해보며 이런 방식으로도 객체지향 코드를 만들 수 있구나를 공부하는 방식으로 진행해 보겠다. 참고로 해당 예제코드는 이번 과 도입부에서 실제 게임기의 예제 동영상에서 봤던 것처럼 두 명의 유저가 대결방식으로 진행하는 방식의 2인용 게임이 아닌, 컴퓨터가 가상의 상대가 되어 컴퓨터와 대전을 벌이는 1인용 게임을 만들 것이다. 참고로 먼저도 언급된 내용이지만, 일반적으로 파일 한 개로만 모든 소스코드를 만드는 방식은 선호되지는 않으나, 예상하기로는 저자와 같은 의도(최소한 간소화시킨 코드로부터 배우기)로 전체 소스코드를 pong.py 라는 파일 1개로 코딩했다는 것을 기억하자. 뿐만 아니라 게임의 추가적 간소화를 위해 심지어 사운드 효과도 제외하는 등 게임에 어떤 추가적인 미디어을 전혀 사용하지 않고 게임상의 그래픽적 요소들에 대해서도 오직 코드로만 구현했다. 바로 아래 화면의 우리가 만들게 될 게임의 최종 결과물을 먼저 보고나면 코드 이해가 훨씬 쉬울 것이다. 다음으로 실제 코드를 하나씩 살펴보자. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FFX09NldLclHqNNWAIYxB%252Fpong.png%3Falt%3Dmedia%26token%3D61bcf2d3-3ba2-41c3-9fa2-d47b1bcabc20&width=768&dpr=3&quality=100&sign=cb3af67f&sv=2) 전체적인 윤곽을 빠르게 이해하기 위해 게임의 메인파트(draw, update 함수 등)를 먼저 살펴보도록 하겠다. Copy # computer-controlled opponent or another player controlling a second # paddle on the opposing side. Players use the paddles to hit a ball # back and forth. The aim is for each player to reach eleven points # before the opponent; points are earned when one fails to return # the ball to the other. import pgzrun import random WIDTH = 800 HEIGHT = 600 TITLE = 'pong' # a color used to draw things MAIN_COLOR = 'yellow' # width and height of a player paddle PADDLE_WIDTH = 15 PADDLE_HEIGHT = 100 # radius of the tennis ball TENNIS_BALL_RADIUS = 10 LEFT_PLAYER = "left" RIGHT_PLAYER = "right" player = LEFT_PLAYER # first player is chosen game = Game(player) def draw(): game.draw() def update(): if keyboard.up: game.right_paddle.up() elif keyboard.down: game.right_paddle.down() # move the computer controlled paddle game.computer_act() # set the position of the ball to be in the middle of the paddle if not game.in_progress: game.position_ball() if game.in_progress: game.proceed() def on_key_down(key): # pressing SPACE launches the ball if key == keys.SPACE: if game.score_left == 11 or game.score_right == 11: game.score_left = game.score_right = 0 if not game.in_progress and game.active_player == RIGHT_PLAYER: game.in_progress = True 이 게임 저작자가 만든 코드의 가장 큰 차이점 점은 맨 첫 두 라인은 Game이라는 게임 전체를 총괄하는 슈퍼객체를 두고 있으며, 결국 게임은 이 메인객체 하나로 전체 게임을 구성하고 구성품과 같은 개별 객체(공, 패들(우리는 기존에 반사판이라 칭함))를 관리하는 방식을 취했다. 여러분들은 게임을 만드는 객체지향 코딩을 이런 식으로도 가능하다는 하나의 기법 정도로 이해하면 좋겠다. 그밖에 이 게임의 저자는 점수를 따로 객체로 만들지 않았다는 것이 다르다고 할 수 있다. 게임의 메인파트(draw, update 함수 등)가 게임객체(Game)의 멤버변수/함수의 조작으로만 구성되게 최대한 간략화 시켰기 때문에 크게 코드이해의 어려움은 없다. 좌측 패들은 컴퓨터에 의한 자동조정이고, 우측 패들은 사용자에 의한 조작이다. 여기서 공 객체는 자동으로 중앙선에서 좌우로 뿌려지는 방식이 아닌, 탁구게임과 유사하게 각각의 사용자가 상대를 향해 서브를 넣는 것처럼, 내가 가진 공을 상대를 향해 쏘기(launch) 위해 스페이스 바를 누르는 것이 필요하다. 최종 게임의 승패는 어느 한쪽의 점수가 11점에 도달되었을 경우에 결정되게 된다. 이제 다음 장에서부터 게임객체를 구성하는 3개의 개별 객체(공, 패들, 게임)의 내부를 한개씩 차례대로 살펴보도록 하겠다. [Previous8.6 객체지향으로 개발하기 2chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong/8.6_oop_dev2) [Next8.8 (보너스) 파이게임제로 예제버전 2chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong/8.8_bonus_dev2) Last updated 1 year ago --- # 9.3 객체지향으로 개발하기 1 | 파이썬-파이게임 제로 (순한맛) 배틀시티 게임의 객체지향형 버전에선 이전에 비해 객체지향 디자인(OOD: Objected Oriented Design)적인 면을 더 고려해 보는 훈련을 해보면 좋겠다. 객체지향적으로 만든다는 것은 구조적으로 어떠한 형태를 띄어야 하는 것일까? 파이게임제로 라이브러리를 베이스로 사용하고 있기 때문에 필수 콜백함수들(draw, update 함수 등)을 통한 [게임루프의 구조](https://jjlee.gitbook.io/python-pygame_zero/game_loop) 는 그대로 가져갈 수 밖에 없다는 걸 전제해야 한다는 것을 이해하고 있을 것이다. 다만, 그 게임루프에서 개임의 주인공 객체간의 협력으로 게임이 잘 동작하게 만드는 것이 우리의 최종 목표라 하겠다. 저 그럼 배틀시티 게임에 필요한 객체식별부터 시작해 보자. 우리 게임무대에 등장인물들은 배우(Actor)객체 중심으로 찾아보면 쉬운데, "주인공 탱크", "적 탱크", "벽 더미", "대포알", "폭발장면"이 있다. 크게 구분해 보면, "주인공", "적", "장애물", "무기" 라는 역할을 한다고 말할 수 있다. 우리의 고민은 여기서 다시 시작된다. _이 게임을 예제수준에서 먼저 절차지향형으로 개발해 본 딱 그정도만 기능구현하고 더이상 추가구현 없는 전혀 건드리지 않을(?) 게임으로 생각하는지, 아니면 개인적인 의욕이 있어 이 게임을 더 개선해서 더 발전된 형태의 버전업이 되는 게임, 예를들어 적으로서 탱크만이 아닌 다양한 형태의 다른 적이 등장한다던지, 내가 사용하는 무기도 대포알 뿐만 아니라 다른 형태의 공격무기도 갖게 할 것인지, 지금은 장매물이 벽 더미이지만, 그 외의 다른 형태의 장애물도 생각하는지 등등에 따라 당장의 상속의 깊이에서부터 객체지향 설계의 스케일이 달라지기 때문이다._ 스케일을 너무 크게 잡고 설계 하는 부분에 대해서는 애시당초 이 책이 고려한 학습 난이도에 비해 높고, 분량이 많아져서 기회가 되면 이 책의 다음 레벨의 책에서 더 진도를 나가기로 하고, 이 책에서는 적절한 수준으로 타협하여 설계하도록 하겠다. 그럼, 먼저 주인공과 적의 역할만 놓고 생각해보자. 주인공과 적의 목표는 이동하면서 서로 상대방을 포쏘기 공격해 맞추는 것이다. 이 때 둘의 사용하는 무기(대포알)가 동일하고 이동하는 방법 역시 전후좌우의 직진이동만 할 수 있어 둘 다 똑같은 제약을 갖는다. 그렇다. 탱크라는 한 공통 객체를 만들어 그 객체로부터 재활용해 주인공 탱크/적 탱크 각각을 생성할 수 있는 것이다. 그럼, 이를 또 한편으로 탱크객체가 해야할 **책임(외부의 객체의 사용자에게 제공하게 될 정보 또는 요청에 의한 행동)**에 대해서 고민해보자. 그런데 **책임들을 정하기에 앞서 먼저는 객체들 간에 협력에 대해 생각하는게 우선인데, 협력에 대해 고민하면 자연스럽게 객체의 책임을 이끌어 내기 때문이다.** 즉, _어떤 객체가 탱크객체에게 어떤 행동의 요구를 할 것인가에 대한 것을 고민해보자. 가장 본질적인 요구는 각 객체을 생성(creation)하는 것에서부터 출발할 수 있다._ _각 객체를 첨부터 무조건 다 만들어 놓고 시작하는게 아니라, 특정 객체가 특정 객체의 생성을 특정 시점에 요구할 수 있는 것이다._ 예를들어 스페이스 바를 눌러 대포를 쏘는 시점에 "총알" 라는 객체가 생성되고 날아가기 시작하면 되는 것처럼 말이다. 게임이 맨 처음 시작했을 때 화면에 맨 처음 등장하는 객체들은 무엇인가? "주인공 탱크", "적 탱크", "벽 더미" 이다. 그리고, 객체는 아닌 단순 이미지인 배경 이미지가 존재해야 할 것이다. 그럼, 이들 객체의 생성은 누가할 것인가? 엔트리 블록코딩에서는 엔트리(시스템) 자체가 내부적으로 자동으로 해주었다면, 이젠 그것도 우리가 직접 코딩으로 해야하는 것이다. 객체들이 화면에 그려지기 위해서는 draw 콜백함수 안에서 각 객체의 draw 메소드를 호출해야 할 것이고, 그 말은 draw 콜백함수 이전에 최소한 생성이 되어 있어야 하는 것을 전제한다. 다음의 battle\_city\_oop.py 라고 명명한 파일에서 방금 언급된 내용의 기본적인 코딩을 해보도록 하겠다. battle\_city\_oop.py Copy import random from actors import Tank WIDTH = 800 HEIGHT = 600 # 50x50 크기의 벽의 더미 생성 walls = [] WALL_SIZE = 50 for x in range(int(WIDTH / WALL_SIZE)): # 탱크가 위치 할 첫 행과 마지막 행 총 2행을 비워두기 위해 -2 하여 생성 for y in range(int(HEIGHT / WALL_SIZE - 2)): if random.randint(0, 100) < 50: # 적정 수의 벽을 생성 wall = Actor("wall", anchor=("left", "top")) wall.x = x * WALL_SIZE wall.y = y * WALL_SIZE + WALL_SIZE # 맨 첫 행 비우기 위해 전체적으로 아래로 밀기 walls.append(wall) # 주인공 탱크 생성 tank = Tank("tank_blue", (400, 575)) # 적 탱크 생성 enemies = [] MAX_ENEMIES = 3 for i in range(MAX_ENEMIES): enemies.append(Tank("tank_red", (400, 25)) def draw(): screen.blit("grass", (0, 0)) # 배경이미지 그리기 tank.draw() for enemy in enemies: enemy.draw() for wall in walls: wall.draw() 🔢 벽 더미 생성코드는 기존의 절차지향 버전에서의 것과 동일하여 설명이 필요없다. 19-26라인까지 주인공 탱크와 3대의 적 탱크를 생성하고 있는데, 특이한 것은 기존에는 배우객체를 생성할 때 Actor 클래스를 통해 생성했었는데, 이제는 우리가 직접 만든 사용자 객체인 MyTank, EnemyTank라는 클래스를 사용해 생성하고 있다는 것이다. 그 사용자 객체들은 battle\_city\_oop.py 파일 안에 함께 코딩할 수도 있지만, **한 파일 안에 너무 많은 코드를 적는 것은 지양되고, 의미있게 여러 파일로 나눠 모듈화(modulization) 하는 것이 더 큰 프로젝트를 관리하는데 효과적이며 일반적이다.** 따라서, _우리의 사용자 객체들은 아래처럼 actors.py 라는 파일 안에 존재하며, 이를 battle\_city\_oop.py 안에서 불러 사용하기 위해 2번 라인에서 from-import 문을 사용해 actors 모듈(이를 통해 우리는 파일명이 곧 모듈명이 된다는 것을 알 수 있음) 안에 클래스들을 import 하고 있다._ actors.py Copy from pgzhelper import * class Bullet(Actor): def __init__(): super().__init__() pass class Explosion(Actor): def __init__(): super().__init__() pass class Tank(Actor): def __init__(): super().__init__() pass def move(): pass def fire(): pass 🔢 이해를 돕기 위해 상세구현 없는 식별된 객체들의 아주 기본적인 뼈대로부터 시작하는게 좋겠다. 먼저 탱크 객체부터 차근히 고민해보자. 탱크 객체는 Actor객체를 상속했기 때문에 기존의 Actor처럼 간주될 수 있기 때문에 기존의 일반적인 배우 객체의 생성 때처럼 객체의 외관 이미지와 최초 화면상의 위치정보의 두 파라미터 값으로 생성할 수 있으나, 잘 고민해보면 게임 시작시 적 객체와 주인공 객체가 서로 마주보는 상태로 게임이 시작해야 하므로, 객체생성시 초기 파마미터로 아예 angle 속성값도 함께 넘겨 초기화하면 좋겠다. 참고로 angle 속성값은 원래 오리지널 Actor 객체의 속성엔 존재하지 않지만 actors.py 상단에서 pgzhelper 모듈을 import 했기 때문에 Actor객체 안에 원래 존해하는 추가 속성처럼 인식이 가능하게 되었다는 것을 잘 알고 있을 것으로 안다. 총 3개의 파라미터에서 img\_name, pos 값은 부모객체인 오리지널 Actor객체의 초기화에 사용되도록 값을 넘기고, angle값은 파생객체의 자신의 속성값으로 설정한다. 그럼 지금까지 언급된 내용을 코드로 표현해 보면 다음과 같다. Copy class Tank(Actor): def __init__(self, img_name, pos, angle): super().__init__(self, img_name, pos) self.angle = angle 🔢 그 다음으로는 탱크 이동에 관한 move 메소드의 구현을 고민해 보자. 부모는 자식들이 모두 공유하는 공통된 기능에 대한 것을 구현하는 것이 의미 있는데 그래야만 부모를 상속한 자녀가 부모 것을 그대로 쓰면 되기 때문이다. 현재까지 찾아진 탱크의 공통동작이라고 할 수 있는 것은 근본적인 탱크 이동에 대한 것으로 이 move 메소드가 호출될 때 마다 현재 탱크가 바라보고 있는 방향을 기준으로 정해진 크기만큼 직진이동 한다는 것이다. 이 부분까지 포함해 다시 전체적인 코드로 표현하면 다음과 같다. Copy class Tank(Actor): def __init__(self, img_name, pos, angle): super().__init__(self, img_name, pos) self.angle = angle def move(self): if self.angle == 180: self.x -= 2 elif self.angle == 0: self.x += 2 elif self.angle == 90: self.y -= 2 elif self.angle == 270: self.y += 2 탱크의 이동에 대해 우리가 간과한(?)이 하나 있는데, 탱크이동에 제약이 있다는 것이다. 이 제약은 기존 절차지향으로 개발할 때도 다 인지가 되었던 것으로 탱크가 벽을 통과해 이동할 수 없고, 보이는 전체 게임화면을 넘어 이동해서도 안된다. _두 제약은 이동제약이란 측면에선 유사하지만 실제 구현관점에선 화면경계 제약은 단순 범위제약이고, 벽 관련 제약은 두 객체(탱크와 벽)의 충돌확인 후 이동의 제약이라 서로 내용이 다르다._ 여기서 전자의 경우는 꼭 탱크객체에게만 유효할까? 다른 객체에는 필요없을까? 를 생각해보면, '총알' 객체에게도 필요한 부분이다. 왜냐하면, 총알객체가 화면경계 밖을 넘어서자마자 객체를 삭제(메모리에서 제거)해야 하기 때문이다. **이렇게 모든 객체에게는 필요하지 않지만, 일부 객체에겐 필요한 기능을 구현하기 위해 객체지향 프로그래밍에서 반드시 알아야하는 또다른 개념이 하나 등장하는데 추상 클래스(abstract class) 또는 인터페이스(interface)라는 개념**을 배워야 할 시점이 왔다. 이 부분은 이론적 이해가 필요해 내용이 길어질 수 있으므로 다음 장에서 좀 더 상세히 설하도록 하겠다. [Previous9.2 절차지향형으로 개발하기 2chevron-left](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.2_procedural_dev2) [Next9.4 객체지향 개발이론 (인터페이스)chevron-right](https://jjlee.gitbook.io/python-pygame_zero/battle_city/oop_interface) Last updated 6 months ago --- # 8.5 객체지향으로 개발하기 1 | 파이썬-파이게임 제로 (순한맛) 퐁 게임의 객체지향형 버전을 만든다는 것은 구조적으로 어떠한 형태를 띄어야 하는 것일까? 파이게임제로 라이브러리를 베이스로 사용하고 있기 때문에 필수 콜백함수들(draw, update 함수 등)을 통한 [게임루프의 구조](https://jjlee.gitbook.io/python-pygame_zero/game_loop) 는 그대로 가져갈 수 밖에 없다는 걸 전제해야 한다. 그러나, 기존의 절차지향 버전에서 존재했던 (글로벌)함수들 기반의 절자지향형 코드들은 이제 객체형태로 변형되어 그 객체간의 협력만으로 게임이 잘 동작하게 만드는 것이 목표라 하겠다. 저 그럼 퐁 게임에 필요한 객체식별부터 시작해 보자. 우리 게임무대에 등장인물들은 기존의 절차지향에서 사용했던 **Rect**기반 객체 중심으로 찾아보면 쉬운데, "반사판 1", "반사판 2", "공", "점수", 이렇게 4개면 충분하다. 사실 객체지향 패러다임으로 시도해보는 첫 게임으로서 구지 복잡한 게임을 예제삼을 필요없이 이정도 간단한 난이도 부터 시작하는게 적당하다. 그럼, 먼저 반사판들의 역할만 놓고 생각해보자. 두 반사판의 목표는 상하 이동하면서 볼을 놓히지 않게 잘 반사해 내는 것이다. 두 반사판 모두 똑같은 목표와 똑같은 제약을 갖는다. 단지, 각각의 반사판을 조작하기 위한 키보드의 키가 다를 뿐이다. 그렇다. 이 지점이 객체지향의 코드재사용성의 장점이 드러날 수 있는 부분이다. Bar객체를 클래스 형태로 1개로 정의하고, 이 클래스 Bar로부터 두 개의 객체(반사판1, 반사판2)를 만들어 사용하면 된다. 게임이 맨 처음 시작했을 때 화면에 등장하는 객체들은 무엇인가? 위에 언급된 4개의 객체("반사판1", "반사판2", "공", "점수") 전부 다 일 것이다. 그럼, 이들 객체의 생성은 누가할 것인가? 엔트리 블록코딩에서는 엔트리(시스템) 자체가 내부적으로 자동으로 해주었다면, 이젠 그것도 우리가 직접 코딩으로 해야하는 것이다. 객체들이 화면에 그려지기 위해서는 draw 콜백함수 안에서 각 객체의 draw 메소드를 호출해야 할 것이고, 그 말은 draw 콜백함수 이전에 최소한 생성이 되어 있어야 하는 것을 전제한다. 그럼, 방금 언급된 내용의 기본적인 코딩을 해보려고 하는데, 처음부터 코드가 너무 복잡해 보여 이해를 어렵게 만들지 않기 위해 당장은 객체에 대한 정의를 제외하고(있다고 가정하고), 그를 활용하는 코드만을 확인해 보겠다. pong\_oop.py Copy import random import math from pong_actors import Score, Ball, Bar # 게임화면 TITLE = 'pong' WIDTH = 800 HEIGHT = 600 # 반사판 BAR_H = 100 BAR_W = 15 GAP_FROM_SCR = 20 # 볼 BALL_RADIUS = 10 VELOCITY = 3 SPEED_UP = 1.05 # 점수 FINAL_SCORE = 11 # 주인공 객체들 생성 score = Score(FINAL_SCORE) ball = Ball((WIDTH/2, HEIGHT/2), VELOCITY, score) bar1 = Bar(GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, ball) bar2 = Bar(WIDTH - BAR_W - GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, ball) bars = [bar1, bar2] def draw(): screen.clear() screen.draw.line((WIDTH/2, GAP_FROM_SCR), (WIDTH/2, HEIGHT - GAP_FROM_SCR), \ color='grey') # 중심선 if not score.is_game_over(): ball.draw() bar1.draw() bar2.draw() score.draw() def update(): # 반사판 if keyboard.a: bar1.up() if keyboard.z: bar1.down() if keyboard.up: bar2.up() if keyboard.down: bar2.down() for bar in bars: bar.collide_ball() # 게임시작 조건 if score.is_game_over(): if keyboard.space: score.reset() ball.reset() else: # 공 ball.move() ball.collide_wall() 🔢 25-28라인까지 점수, 공, 2개의 반사판을 생성하고 있는데, 기존 절차지향버전에서 배우객체를 생성할 때 **Rect** 객체를 통해 생성했었는데, 이제는 우리가 직접 만든 사용자 객체인 Score, Ball, Bar라는 클래스를 사용해 생성하고 있다는 것이다. 일반적으로 **한 파일 안에 너무 많은 코드를 적는 것은 지양되고, 의미있게 여러 파일로 나눠 모듈화(modulization) 하는 것이 더 큰 프로젝트를 관리하는데 효과적이다.** _따라서,_ 사_용자 객체들을 정의하는 클래스 정의문은 아래처럼 예들들어, pong\_actors.py 라는 파일 안에 별도로 분리시키고, 이를 pong\_oop.py 안에서 import해 사용하는 방식을 취하는게 좋으나, 여러분의 수준에서 당장은 여러 파일을 오가면서 코드를 확인하고 디버깅하는게 어려울 수 있다 판단하여 객체지향 코딩을 배우는 것에 가장 초점을 맞추기 위해 한 파일(pong\_oop.py) 안에 모든 코드를 포함하는 식으로 코딩할 예정이다._ pong\_actors.py Copy class Score(): def __init__(): pass def _gameover_draw(): pass def is_game_over(): pass def reset(): pass def draw(): pass class Ball(Rect): def __init__(): super().__init__() pass def collide_wall(): pass def reset(): pass def draw(): pass class Bar(Rect): def __init__(): super().__init__() pass def up(): pass def down(): pass def collide_ball(): pass 🔢 이해를 돕기 위해 상세구현 없는 식별된 객체들의 아주 기본적인 뼈대로부터 시작하는게 좋겠다. 먼저 점수(Score)객체이다. 화면에 각 게임유저의 현재점수와 승패의 UI를 나타내기 위한 목적으로 공이나 반사판처럼 지속적인 움직임을 갖는 속성이 필요한 것 아니고, 고정위치에서 표현되기 때문에 Rect 라는 부모객체를 상속하지 않은 것을 알 수 있다. 그렇게 때문에 상속된 객체는 항상 호출이 필요한 **super().\_\_init()\_\_** 부모객체를 초기화하기 위한 초기화 함수도 호출하지 않는다. 생성시 파라미터값으로 받아드리는 것은 최종 승패를 가리는 점수인 **final\_score** 값만 필요하다. 6번 라인에서 그 값을 객체의 내부의 속성값(멤버변수라 지칭)인 self.**\_final\_score** 로 재할당하는 것을 알 수 있다. 해당 멤버변수값 앞에 \_(언더바 또는 언더스코어)를 붙힌 이유는 앞 절에서 설명되었던 바와 같이 객체 내부적으로만 사용하기 위한 목적이다. 참고로 이를 객체지향 패러다임에서는 **비공개 멤버변수**라는 용어로 지칭한다. **비공개(private) 메소드 또는 변수란?** circle-info 객체간의 상호작용을 목적으로 외부에 공개하는 멤버변수 및 함수와는 달리 이렇게 객체 내부적으로만 사용하려는 목적의 멤버변수 및 멤버함수를 지칭한다. _**참고로 객체의 클래스를 정의하면서, 아래의 2~4라인처럼 객체의 이름 하단에 그 객체에 목적과 사용에 대해 간단히 주석으로 적어두는 것은 파이썬 코딩에 있어서 하나의 관습이기도 하면서 동시에 가독성을 높히는 좋은 습관이 될 수 있다.**_ Copy class Score(): ''' 화면에 점수와 승패의 UI 표현 ''' def __init__(self, final_score): self._final_score = final_score self._b1_score = -1 self._b2_score = -1 그 다음으로 최종승패의 UI를 그리는 멤버함수 **\_gameover\_draw** 를 추가했는데 _멤버함수의 이름에 \_(언더바)가 있는 이유는 위에서 언급된 목적과 다르지 않을 것으라 유추할 수 있고, 예상한대로 해당 함수는 객체 외부에서 접근할 수 없고 내부적으로만 사용하는 목적이라는 것을 나타내기 위한 관습적인 코딩의 약속이다._ 참고로 이를 객체지향 패러다임에서는 위에서 언급되었던 것처럼 **비공개 멤버함수**라는 용어로 지칭한다. 추가로 현재시점의 최종 게임승패를 분별하는 **is\_game\_over** 멤버함수, 최종 승패 이전의 매 경기마다의 승자에게 점수를 주는 **add** 멤버함수, 게임을 완전 새로 시작하기 위해 점수를 다시 원점으로 돌리는 **reset** 함수가 추가되었다. 그리고, 마지막으로 **draw** 멤버함수의 목적은 위에서 언급했듯이 실제 화면에 점수 UI 를 그리는 용도이다. Copy class Score(): ''' 화면에 점수와 승패의 UI 표현 ''' def __init__(self, final_score): self._final_score = final_score self._b1_score = -1 self._b2_score = -1 def _gameover_draw(self): if self._b1_score == self._final_score: winner = 'Player1' elif self._b2_score == self._final_score: winner = 'Player2' if winner: screen.draw.text(winner + ' Win!!', (WIDTH/3, HEIGHT/2 - 50), color='blue', fontsize=70) screen.draw.text('Press Space to play again', (WIDTH/4, HEIGHT/2 + 50), color='skyblue', fontsize=50) def is_game_over(self): if (self._b1_score == self._final_score or self._b2_score == self._final_score) \ or (self._b1_score == -1 and self._b2_score == -1): return True else: return False def add(self, who): if who == 'b1': self._b1_score += 1 elif who == 'b2': self._b2_score += 1 def reset(self): self._b1_score = 0 self._b2_score = 0 def draw(self): screen.draw.text(str(self._b1_score), (WIDTH/4, GAP_FROM_SCR), color='yellow', fontsize=60) screen.draw.text(str(self._b2_score), ((WIDTH/4)*3, GAP_FROM_SCR), color='yellow', fontsize=60) if self.is_game_over(): self._gameover_draw() 한 절에서 너무 많은 양을 기술하여 내용소화에 부담을 느끼지 않도록 남은 두 객체에 대해서는 다음 절에서 계속 이어 설명하겠다. [Previous8.4 객체지향 개발이론 (사용자 정의 객체 만들기, 상속)chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong/oop_inheritance) [Next8.6 객체지향으로 개발하기 2chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong/8.6_oop_dev2) Last updated 5 months ago --- # 9.1 절차지향형으로 개발하기 1 | 파이썬-파이게임 제로 (순한맛) 바로 아래 화면의 우리가 만들게 될 게임의 최종 결과물을 먼저 보고나면 코드 이해가 훨씬 쉬울 것이다. 다만, 이번 과의 맨 서두에서 언급했듯이 난이도 조절을 위해 오리지널 게임의 기능동작 전체 다를 구현하지 않을 예정이고, 대신 이 게임을 두 가지 프로그래밍 패러다임([절자지향형](https://jjlee.gitbook.io/entry-python/paradigm/4.2-procedural) , [객체지향형](https://jjlee.gitbook.io/entry-python/paradigm/4.4-object-oriented) ) 각각을 사용한 두 가지 버전으로 만들어 볼 예정이다. 먼저 이전 절에서 예고했듯이 다시한번 나만의 객체를 만들고 활용하는 것으로 배워 봄으로써 이제는 보편화되어 사용되는 객체지향형이 기존의 절자지향형과 어떠한 현격한 차이를 가지는지를 한번 더 명확하게 이해하는 것이 이번 장의 과업이라고 할 수 있겠다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FfC5bCenAepGirHjFdCLs%252Fimage.png%3Falt%3Dmedia%26token%3D2ab88b68-16e5-4bb6-bd39-c1695a64f2f0&width=768&dpr=3&quality=100&sign=c105f8df&sv=2) 우리는 이미 5-8장 통해 4개의 게임제작을 소화했기에 이미 튜토리얼 수준을 넘어섰다 볼 수 있다. 따라서, 이번 장에서도 이미 설명한 기본적인 내용에 대한 자세한 중복설명없이 빠르게 핵심적인 코딩에 대한 설명 위주로 진행하도록 하겠다. 참고로 이번 게임제작에 필요한 이미지, 사운드 등의 리소스들은 [이곳arrow-up-right](https://github.com/roboticsware/python-pygame_zero-samples/tree/main/breakout/images) 에서 다운로드 할 수 있으며 게임에 필요한 이미지 파일들은 코드에서 사용한 이름 그대로 imgaes 폴더에 이미 저장되어 있고, 효과음은 sounds 폴더에 저장되어 있다고 간주하고 시작하겠다. 또는 [커스텀 뮤 에디터arrow-up-right](https://github.com/roboticsware/mu/releases) 를 사용 중이라면 뮤 에디터의 작업 디렉토리인 (사용자계정)\\mu\_code\\examples\\pygame\_zero 안에서 복사해 올 수 있다. 우선은 먼저 절차지향형으로성의 개발을 시작해 보자. 배틀시티게임은 이전에 만들어 본 블록격파 게임과 트윈비 게임을 섞어놓은 게임과 같다. Copy from pgzhelper import * import random WIDTH = 800 HEIGHT = 600 bullets = [] bullet_delay_cnt = 0 BULLET_DELAY = 50 enemy_bullets = [] explosions = [] winner = '' tank = Actor("tank_blue", (400, 575)) tank.angle = 90 ENEMY_MOVE_DELAY = 20 MAX_ENEMIES = 3 enemies = [] for i in range(MAX_ENEMIES): enemy = Actor("tank_red") enemy.angle = 270 enemy.x = (i + 1) * WIDTH / (MAX_ENEMIES + 1) enemy.y = 25 enemy.move_cnt = 0 enemies.append(enemy) # 50x50 크기의 벽의 더미 생성 walls = [] WALL_SIZE = 50 for x in range(int(WIDTH / WALL_SIZE)): for y in range(int(HEIGHT / WALL_SIZE - 2)): # 맨 상/하단을 비워두기 # 랜덤하게 벽없는 빈 공간 생성 if random.randint(0, 100) < 50: wall = Actor("wall", anchor=("left", "top")) wall.x = x * WALL_SIZE wall.y = y * WALL_SIZE + WALL_SIZE # 맨 상단 비우기 walls.append(wall) def move_player(player): pass def fire_bullets(player, bullets): pass def collide_bullets(bullets): pass def draw(): screen.blit('grass', (0, 0)) tank.draw() for enemy in enemies: enemy.draw() for wall in walls: wall.draw() for bullet in bullets: bullet.draw() for bullet in enemy_bullets: bullet.draw() for explosion in explosions: explosion.draw() if winner: screen.draw.text(winner + " Win!", \ midbottom=(WIDTH / 2, HEIGHT / 2), fontsize=100) def update(): global bullet_delay_cnt, enemy_move_cnt # 주인공 탱크 if winner == '': move_player(tank) if bullet_delay_cnt == 0: # 지연시간 종료 후 재발사 가능 if keyboard.space: sounds.sfx_exp_medium12.play() fire_bullets(tank, bullets) bullet_delay_cnt = BULLET_DELAY else: bullet_delay_cnt -= 1 collide_bullets(bullets) # 적 탱크 for enemy in enemies: choice = random.randint(0, 2) if enemy.move_cnt > 0: # 탱크 움직이기 enemy.move_cnt -= 1 move_player(enemy) elif choice == 0: # 움직임 지연시간 초기화 enemy.move_cnt = ENEMY_MOVE_DELAY elif choice == 1: # 탱크 방향전환 enemy.angle = random.randint(0, 3) * 90 else: # 대포알 발사 fire_bullets(enemy, enemy_bullets) collide_bullets(enemy_bullets) 🔢 위의 38번째 라인까지 코드는 게임무대의 배우객체들을 생성하는 것이다. 장애물 벽더미(walls), 주인공 탱크(tank)와 3대의 적 탱크(enemies)를 생성한다. 벽더미를 만드는 부분은 기존의 블록격파 게임에서 블록생성 부분과 크게 다르지 않다. 다만, 벽더미의 생성 알고리즘이 일부 다를 뿐이다. 벽더미의 구성을 자세히 살펴보자. 벽 한개의 크기는 가로세로 50픽셀(WALL\_SIZE)의 정사각형이다. 이 벽이 가로세로 800x600픽셀 크기의 화면에 몇 개가 들어갈지는 WIDTH / WALL\_SIZE 와 HEIGHT / WALL\_SIZE 계산으로 쉽게 알 수 있다. 궁금한 점은 왜 그 계산식을 **int()**라는 내장함수에 넣어 사용했는가인데, 그 이유는 파이썬에서의 나눗셈 연산은 결과를 항상 실수형(float)으로 값을 되돌려 주기 때문이다. 우리는 그 연산결과를 다시 **range()** 내장함수에서 사용할 건데, 이 함수의 인자값(argument)은 또 항상 정수형(integer)을 넘겨주어야 한다. _결국 int() 함수의 목적은 실수값을 정수값으로 변환해 주는 함수인 것이다._ 32번 라인에서 전체 세로열에서 2를 뺀 것은 위의 게임결과 화면에서 보면 알 수 있듯이, 화면 전체에 전부 벽을 놓을 게 아니라 적 탱크(enemy)가 위치할 화면 맨 윗 열(row)와 내 탱크(tank)가 위치할 화면 맨 아래 하단의 한 열(row)을 비워두기 위한 목적이다. 그리고, 34번 라인은 이 게임이 벽을 다 부시는 블록격파(?) 게임은 아니고, 상대 탱크를 포로 맞춰 없에는게 과업인 게임에서 일부러 화면전체에 벽을 가득채우지 않고, 군데군데 랜덤하게 비워두기 위해서 넣은 코드이다. 🔢 현재는 절차지향형으로 개발하기로 했기때문에 필요한 기능들을 함수화(functionalize) 할 필요가 있다는 것을 배웠다. 이번 게임도 함수화를 진행해보자. **코딩에서 항상 염두해 두어야 할 점은 나와 남을 위해 가독성이 좋고, 추후 수정에 대비한 유지보수(maintenance) 측면을 늘 고려해야 한다.** 사실상 함수도 그러한 이유로 출연했다고 할 수 있다. 함수의 주목적은 코드중복을 줄이는 것으로 이전 트위비 게임에서는 안타깝게도 공통 기능적으로 함수로 묶을 만한게 마땅지 않았지만, 이번 배틀시티 게임에서는 그 목적에 부합하는 함수 3개(**move\_player**, **fire\_bullets**, **collide\_bullets**)를 만들 수 있다. 다음 절부터 위에 언급된 3개 함수의 구현체를 만들어보기로 하고, 지금은 그 함수들을 제외한 나머지 코드들에 대해 더 검토해보자. 지금까지 모든 과정을 함께 공부해 온 여러분의 실력상 대부분은 이전에 이미 배웠던 것들의 약간의 변형이라 찬찬히 드려다보면 대부분 이해가 될 것으로 믿는다. 약간 낯선 부분이 있다면, **bullet\_delay\_cnt**, **enemy\_move\_cnt** 두 변수에 관한 것일 것 같다. 두 변수값은 다시 **BULLET\_DELAY**, **ENEMY\_MOVE\_DELAY** 이란 [매크로arrow-up-right](https://ko.wikipedia.org/wiki/%EB%A7%A4%ED%81%AC%EB%A1%9C_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)) (Macro)값으로 채우고 있다(79, 91라인). 변수명이나 매크로명에서 유추할 수 있듯이 뭔가 의도적으로 코드실행의 지연(delay)을 주기 위한 것으로 유추할 수 있다. 왜 지연이 필요한가? 그렇다. 근본적인 이유는 트윈비에서도 유사한 상황이 있었는데 바로 update 콜백함수의 호출속도 (60FPS) 때문이다. 따라서, **bullet\_delay\_cnt** 는 대포알의 너무 빨리도 많이도 나가지 않게 하기 위한 목적이고, **enemy\_move\_cnt** 는 적 탱크들은 사람이 조작하는게 아니라 컴퓨터에 의해 자동조작 운행이 될 예정인데 이 때 너무 빠른 속도로 운행되지 않게 하기 위한 목적이다. 결국, 대표알이든 탱크 움직임이든 게임성을 저해하지 않게 적당해야지 대전형 게임에서 어느 한쪽이 지나친 우위를 점하면 게임성이 떨어져 재미가 없어진다는 건 누구나 아는 상식이다. 마지막으로 컴퓨터에 의한 적 탱크의 자동조정의 아이디어는 3가지 행동(움직하기, 방향전환, 포쏘기)을 무작위화(램덤화)하는 것이고, 이 때 움직이는 시간에 대해서도 랜덤화함으로써 좀 더 변칙적인 행동을 유도한다. 그럼, 이제 다음 절에서부터 우리의 3가지 사용자 함수의 구현체를 구체적으로 채워보도록 하겠다. [Previous9\. 배틀 시티(Battle city) 게임 만들기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/battle_city) [Next9.2 절차지향형으로 개발하기 2chevron-right](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.2_procedural_dev2) Last updated 6 months ago --- # 10.2 릴레이 서버 구동과 클라이언트 접속 | 파이썬-파이게임 제로 (순한맛) 퐁 게임의 OOP(Object Oriented Programming) 버전을 베이스로 해서 네트워크 게임으로 변환되면서 추가되고, 확장되는 부분만 추가로 설명해 나가는 방식을 취하겠다. 그러나 여러분들이 초보자임을 감안해야 하는데 네트워크에 대한 아주 기본적인 지식과 추가로 nethelper의 기본 사용법은 사전에 익혀야 하기 때문에 그 부분에 대한 설명을 먼저 하고 넘어가도록하겠다. 먼저 네트워크(Network) 관련 기본 용어정리를 하자. 우리는 네트워크 중에서도 컴퓨터 간의 유/무선(Wired/Wireless) 네트워크 만들어 필요한 정보를 상호교환하기 위한 목적인 컴퓨터 네트워크(Computer Network)로 한정한다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.2_server_connection#lan-vs-internet) 랜(LAN) vs 인터넷(Internet) 각 용어의 구분은 네트워크의 규모의 차이에 의한 것으로 가장 기본 단위인 LAN(Local Area Network)은 제한된 지역 내에서 컴퓨터들을 연결하는 네트워크를 의미합니다. 여기서의 제한된 지역이란 보통 한 집, 한 교실, 한 사무실 또는 한 건물 정도의 규모라 생각하면 된다. 주변에서 손쉽게 볼 수 있는 것은 가정이나 사무실에서 무선 와이파이(WiFi) 라우터(Router)를 이용해 통신장치가 내장된 전자기기(컴퓨터 포함)들 사이에 통신망을 구성한 것이다. 인터넷은 이러한 네트워크의 기본단위인 LAN간의 연결을 점점더 확대하여 그 네트워크 규모를 전 세계단위로 확대한 것이다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.2_server_connection#ip-protocol-ip-address) IP 프로토콜(Protocol)과 IP 주소(Address) 그렇다면, 네트워크 상에 묶여있는 기기 간에 데이터를 보내기 위해 기본적으로 무엇이 필요할까? 데이터를 송수신하기 위해서는 데이터를 어떤 형태로 가공한 후 어떠한 방식으로 주고 받을지에 대한 각 기기 간에 사전에 상호 약속을 전제해야 한다. 이러한 상호간의 통신을 위한 규약에 대해 **프로토콜(Protocol)** 이란 용어로 부른다. **IP(Internet Protocol, 인터넷 프로토콜)**라 불리는 통신규약은 이름 그대로 인터넷을 구성하기 위한 통신규약으로, 인터넷을 구성하는 기본 단위인 LAN에서부터 이 통신규약을 지켜서 통신해야 하는 것이다. _이 통신규약 안에서는 네트워크 안에 존재하는 통신하고자 하는 상대를 인식하고 구분하기 위한 인식체계를 갖고 있는데 그것은 우리가 실생활에서도 널리 쓰는 방식인 주소(Address)를 기반이다._ 집 주소, 이메일 주소를 알면 그 상대를 특정해서 우리가 필요한 데이터(메시지 또는 편지 등)를 전송할 수 있는 것처럼 말이다. 따라서, IP 주소는 IP 프로토콜 통신규약으로 통신하게 위해 인터넷 상에 위치한 모든 통신기기들에게 부여되는 임시적인 주소인 것이다. 여기서 임시적으로 부여된다는 의미는 모든 기기에 그 주소가 사전에 영구적으로 할당되는게 아니라, 통신망에 연결해 통신이란 활동을 할 때만 임시적으로 사용할 수 있도록 인터넷 통신망에 연결되는 순간에 일시적으로 부여된다. 여러분 컴퓨터가 현재 네트워크에 연결되어 있는가? 그렇다면 할당받은 IP 주소를 반드시 갖고 있을 것이고, 내가 할당받은 그 IP 주소는 어떻게 확인할 수 있을까? 다음의 방법을 통해 확인할 수 있다. **Windows** **명령 프롬프트(Command Prompt) 이용:** * 윈도우 키 + R을 눌러 실행 창을 엽니다. * `cmd`를 입력하고 Enter 키를 누릅니다. * 명령 프롬프트 창이 나타나면 `ipconfig`를 입력하고 Enter 키를 누릅니다. * IPv4 주소가 컴퓨터의 IP 주소입니다. **macOS** **터미널 이용:** * Finder > 응용 프로그램 > 유틸리티 > 터미널을 실행합니다. * `ifconfig`를 입력하고 Enter 키를 누릅니다. * en0 또는 en1(Wi-Fi의 경우) 섹션에서 inet 뒤에 오는 주소가 IP 주소입니다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.2_server_connection#client-server) 클라이언트(Client)와 서버(Server) 두 용어의 영어 뜻 그대로 클라이언트(Client)는 고객이란 뜻으로 서비스를 요청하는 자이고, 서버(Server)는 고객의 요청에 응대해 서비스를 제공하는 자이다. 그런데, _네트워크에서의 클라이언트이든 서버는 컴퓨터 단위의 개념이라기 보다, 컴퓨터에 돌아가는 프로그램으로 보는게 이해가 쉽다._ 우리는 앞서 릴레이(중계) 서버에 대해 살펴봤다. 서버를 프로그램이라고 했는데, 즉, 릴레이 서버란 프로그램을 실행시켜 놓으면, 그 서버 프로그램은 이제 메시지의 중계자 역할로서 온라인 게임 참여자들의 클라이언트 프로그램 간에 상호 데이터(메시지)를 이쪽으로 저쪽으로 넘겨 전달해주는 서비스를 제공하는 행위인 서빙(Serving)을 시작하게 된다. 우리가 네트워크 상에 연결된 불특정 다수와 같이 온라인 게임을 하기 위해 이 릴레이 서버 컴퓨터는 물리적으로 어디에 위치시키면 가장 좋을까? 여러분이 실제 상업적인 게임 서비스 제공한다라면 당연히 네트워크 상에 모든 사용자에게 가장 접속이 용이한 곳이면서 동시에 모든 게임 참여자들에게 빠른 네트워크 속도를 제공할 수 있는 장소를 고려할 것이다. 그런 곳이 어딜까? 그렇다. 여러분들이 한번쯤을 들어봤을 용어인데 그런 장소를 데이터 센터(Data center) 또는 [클라우드(Cloud)arrow-up-right](https://en.wikipedia.org/wiki/Cloud_computing) 라고 부른다. 물론 그런 물리적 장소를 임대하는 것은 비싼 비용이 들어간다. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.2_server_connection#undefined) 릴레이 서버 구동과 클라이언트 접속 우리는 이제 막 네트워크 게임 만들기를 배우는 수준에서 저런 장소가 당장 필요할까? 당연히 필요가 없다. 그러나, 혹시나 나중에 상당히 원거리에 떨어져 사는 친구와 여러분이 만든 온라인 게임을 함께 하고 싶을 때가 올 수도 있는데, 그때는 한번 클라우드 상에 가상 컴퓨터를 임대하는 것도 고려해보도록 하자. 그러나 당장은 이 릴레이 서버 프로그램을 단지, 여러분 본인 컴퓨터에서 실행시켜 당장은 본인 컴퓨터가 릴레이 서버역할을 하게 하자. 릴레이 서버 프로그램은 nethelper 라이브러리 내에서 제공되는데 여러분 각자의 컴퓨터에서 다음과 같이 명령 프롬프트 창 또는 터미널에서 프로그램을 실행할 수 있다. Copy python nethelper -g <그룹명> 여기서 그룹명은 온라인게임에 참여자들이 모이게 되는 공간인 그룹(group)의 이름 나타내는 것으로 미리 그룹의 이름을 정해놔야 그 그룹명 식별자를 통해 게임참여자들이 해당 공간에 합류할 수 있게 된다. 우리는 온라인으로 같이 퐁(pong) 게임을 할 예정이므로, 방 이름을 **pong\_game** 이라 하겠다. 그 그룹명과 함께 실행하면 다음과 같은 실행결과를 볼 수 있다. 서버가 잘 시작되었다면 클라이언트들의 접속을 대기하고 있단 의미의 **listening** 이란 메시지를 볼 수 있을 것이다. 서버는 온라인 게임이 진행되는 동안은 계속 실행된 상태(alive) 이여야 하므로, 실행된 창 그 상태를 건드리지 말고 그대로 유지하도록 하자. Copy python nethelper.py -g pong_game Starting Server Interface: ALL Port: 65042 Groups: pong_game Server listening 다음으로 클라이언트들 즉, 릴레이 서버에 만들어진 그룹방에 접속해 실제 게임을 하게 되는 네크워크 게임 프로그램을 만들 건데, 정확히는 릴레이 서버의 pong\_game이라 불리는 그룹방에 접속하는 부분을 먼저 코딩해 보자. pong\_net.py Copy from nethelper import NetNode SERVER_IP = 'localhost' # 릴레이서버 IP주소 is_host = False # 그룹의 host역할 여부 peer_id = '' # 통신 상대방의 id MAX_PEERS = 2 # 최대 게임가능 인원 num_peers = 0 # 현재 게임참여 인원 my_bar = '' net = NetNode() def connect_server(): global is_host, peer_id, my_bar # 맨 처음 연결된 사람이 host (Player1) 역할 if net.connect(SERVER_IP, "Player1", "pong_game", wait=True): is_host = True peer_id = "Player2" my_bar = bar1 print("Connected as host (Player 1)") else: # 이미 호스트가 존재하면, peer (Player2) 역할 if net.connect(SERVER_IP, "Player2", "pong_game", wait=True): is_host = False peer_id = "Player1" my_bar = bar2 print("Connected as peer (Player 2)") else: print("Failed to connect. The group is full.") exit() # 게임시작 전 릴레이 서버와 연결설정 connect_server() nethelper 라이브러리에서 **NetNode** 라는 네크워크 게임유저로서 한 일원(node)이 되는 객체를 import 한 후, 10번라인에서 그 객체를 생성하고 있다. NetNode 객체에는 **connect** 라는 멤버함수가 있는데, 4개의 파마리터값을 인자로 갖는다. 첫번째로 릴레이 서버의 IP주소, 두번째로 본인의 그룹방에서의 id, 세번째가 그룹방 이름, 네번째는 서버와의 연결성립이 완료될 때까지의 대기유무를 나타낸다. 각각의 인자값(argument)들을 자세히 살펴보자. 첫번째, 릴레이 서버주소가 '**localhost**' 이다. 원래 IP주소는 4개의 숫자가 점으로 구분된 형태라는 점을 알고 있을텐데, _예외적으로 본인 컴퓨터 자기자신을 나타내는 IP 주소로 고유하게 약속된 값인_ _**127.0.0.1**_ _이란 주소를 대신해 간편하게 나타내는 동일표현으로 localhost 를 사용_할 수 있다. 그말은 만약 'localhost' 란 값을 쓰기 싫으면, 대신해 '127.0.0.1' 란 값을 지정해도 된다는 뜻이다. **참고로 당장은 내 컴퓨터 내에서 릴레이 서버를 서비스 하지만, 추후 우리가 실제 이 네트워크 게임을 네트워크 상에 원격 사용자들에게 서비스하게 된다면, 그때는 릴레이 서버 주소를 릴레이 서버가 운용되게 되는 인터넷 상의 고유한 IP 주소를 입력해야 함을 기억해 두자.** 두번째, 그룹방에 접속시 본인 id를 **Player1** 으로 하겠다는 것인데, 그 말은 곧 그룹방에 젤 먼저 들어가는 사람은 게임전체를 총괄하는 주인(host)을 담당하게 된다는 것이다(23번 라인). _릴레이 서버방식의 게임상 그룹방에 한사람은 반드시 호스트의 역할을 해야는게 룰_인데, 우리 게임에선 젤 먼저 그룹방에 접속하는 사람이 호스트가 되기로 한다. 이 게임은 단 2인만 즐기는 일대일 대전형 게임이고, 본인이 호스트가 되었다면 본인의 상대가 되는 게임유저의 id는 자연스럽게 **Player2** 가 될 것이다(18번 라인). 19번 라인에 **my\_bar**라는 글로벌 변수는 나중에 내가 조작하는 반사판이 좌/우측 중에 어떤 것인지를 구분하기 위한 용도로 우리 게임에서는 호스트는 항상 좌측면의 반사판(bar1)을 조작하게 되며, 상대가 되는 게임유저는 우측(bar2)를 조작하도록 한다. _참고로 해당 코드를 실행시킨다면 당장은 코드실행시 해당 변수(bar1 또는 bar2)가 없다는 에러가 발생하므로 임시로 주석처리를 하여 실행시키도록 하자._ 순서상 두번째로 그룹방에 접속하는 사람은 곧바로 21번 라인으로 진입하게 되는데 그 이유는 connect 멤버함수는 호출시 인자값으로 넘긴 원하는 id값을 이미 그룹 내 다른 유저가 사용 중이라면, 곧바로 False값을 리턴하게 되기 때문이다. 따라서, 두번째 접속자는 호스트의 역할은 아닌 호스트로부터의 제어명령과 게임상태정보 받아 처리하면서 게임에 참여하는 일반 유저가 된다. 참고로 우리의 **퐁 네트워크 게임 프로그램은 호스트용/일반유저용을 구분하여 각각 별도로 개발하지 않고, 한 프로그램에서 두 가지 역할을 다 소화가능하도록 만들 예정이다. 따라서, 위에 릴레이 서버와의 네트워크 연결을 위한 것도 두 역할(호스트/일반유저)을 다 소화**할 것이고, 이 pong\_net.py 라는 프로그램을 시험해보기 위해서는 명령 프롬프트(또는 터미널) 창을 새로 2개를 띄워놓고, 각각의 창에서 이 pong\_net.py 라는 프로그램을 각각 실행시켜 보기 바란다. 2개의 창 어느쪽에서든 먼저 실행시킨 쪽이(접속시킨 쪽이) pong\_game 그룹방에서의 호스트(Player1)가 되는 것을 알 수 있다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FO6bbCFOLXeDafEhKLOMl%252Fimage.png%3Falt%3Dmedia%26token%3Dceb6e92e-d7de-4972-a2ad-88873c47c96f&width=768&dpr=3&quality=100&sign=df80ffc4&sv=2) 참고로 그룹에 두 명의 게임유저가 다 모여야 게임시작조건이 되고, 먼저 그룹에 입장했고 상대가 입장하기를 기다리는 부분에 UI적인 처리가 필요하며, 다음의 코드를 통해서 처리할 수 있다. 여기서 **num\_peers** 라고 하는 글로벌 변수값에는 현재 그룹 참여자의 총수를 실시간으로 나타낸다. Copy def draw(): ... if num_peers < MAX_PEERS: screen.draw.text('Waiting for a peer connection...', (WIDTH/6, HEIGHT/2), color='blue', fontsize=50) 지금까지 네트워크 게임시작을 위한 릴레이 서버를 통한 게임유저들 간의 기본적인 연결이 끝나고, 다음 절에서부터 본격적으로 네트워크 게임진행을 위해 필요한 코드를 추가해 보도록 하자. [Previous10.1 네트워크 게임방식의 이해와 라이브러리 설치chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.1_nethelper_install) [Next10.3 네트워크 게임으로 만들기 1chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.3_dev_net_1) Last updated 6 months ago --- # 9.5 객체지향으로 개발하기 2 | 파이썬-파이게임 제로 (순한맛) 우리는 지난 [5절](https://jjlee.gitbook.io/python-pygame_zero/battle_city/oop_interface) 에서 모든 객체에게 필요한 것은 아닌, 탱크와 총알 객체에게 필요한 화면경계 제약을 객체지향 관점에서 어떻게 구현하면 좋을지를 고민하면서 끝이 났던 것을 기억할 것이다. 이를 구현하기 위해서는 인터페이스라는 개념이 필요했는데 지난 장에서 이 부분을 잘 이해하였으니, 이제 이를 실제적으로 적용해보자. 아래와 같이 **CheckOutOfScreen** 이란 추상클래스를 정의하고, 그안에 실제 화면 경계를 벗어났는지를 확인하는 is\_out\_of\_screen 추상메소드를 추가하여 이 인터페이스를 상속하는 쪽에서 이 메소드를 직접 구현하도록 하였다. 왜냐면 화면경계를 벗어났는지 여부의 기준이 객체 각자마다 다를 수 있기 때문이다. Copy from abc import ABC, abstractmethod class CheckOutOfScreen(ABC): def __init__(self, screen): self.s_width, self.s_height = screen @abstractmethod def is_out_of_screen(self): pass circle-info 참고로 경계를 확인해야하는 화면의 크기는 미리 정해놓지 말고, 객체를 상속하는 쪽에서 각자 경계를 확인 할 자신이 원하는 화면의 크기를 알려주도록 한다. 5번 라인에 self.s\_width, self.s\_height = screen 라는 표현을 처음 보았을 수도 있는데, 이 코드 의미는 screen 라는 변수가 하나이지만, 그 안에 값은 2개 이상일 수 있는데 예를들어, 리스트나 투플일 수 있다는 것이다. 그래서, 이 코드는 그 리스트나 투플 안에 각각의 값을 앞에서부터 하나씩 꺼내어 새로운 변수(self.s\_width, self.s\_height)로 각각 옮겨 담는 것으로 이해하면 될 것이다. 파이썬에서는 이러한 과정을 **unpack** 이란 용어로 사용한다. 예를들어, 각 객체에서 구현한 다음의 구현내역을 살펴보면 다음과 같은데, 총알(대포알)객체의 경우, 총알의 중심좌표(self.x, self.y)를 기준으로 화면 경계를 넘었나/안넘었나를 따진다면(7~12라인), 탱크객체의 경우, 탱크객체 이미지의 좌우상하(self.left, self.right, self.top, self.bottom)을 기준으로 경계를 따지고 있어서(22~27라인), 객체마다 그 경계를 삼는 기준이 다를 수 있다. Copy class Bullet(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self.angle = angle def is_out_of_screen(self): if self.x > self.s_width or self.x < 0 or \ self.y > self.s_height or self.y < 0: return True else: return False class Tank(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self._original_pos = 0 self.angle = angle def is_out_of_screen(self): if self.right > self.s_width or self.left < 0 or \ self.bottom > self.s_height or self.top < 0: return True else: return False def move(self): self._original_pos = self.pos if self.angle == 180: self.x -= 2 elif self.angle == 0: self.x += 2 elif self.angle == 90: self.y -= 2 elif self.angle == 270: self.y += 2 # 화면경계 확인 if self.is_out_of_screen(): self.pos = self._original_pos circle-info 참고로 17번 라인의 부모객체의 초기화는 코드가 전에는 **super().\_\_init\_\_** 메소드 였으나, 이제는 특정 객체의 이름과 함께 호출해야 하는데, 이유는 2개 이상의 부모객체로부터 상속을 받았기 때문에, 어느 부모객체를 초기화 하려는지를 이름을 통해 명시를 해줘야 하기 때문이다. 🔢 탱크객체의 **move** 메소드에도 변화가 있는 것을 감지할 수 있을 것이다. 30번 라인에서 현재 위치를 기억해두고, 40~42라인을 통해 이동한 지점에 화면경계를 넘어서는지를 객체의 자신의 **is\_out\_of\_screen** 메소드로 확인하고, 만약, 넘어서게 되면 그쪽으로 이동할 순 없고 다시 제자리로 원위치하는 부분을 추가되어 화면경계 넘어서의 이동을 제약하는 목적을 달성하고 있다. 이미 지난 시간 탱크의 이동제약에 관해 고려에서 언급된 부분인데 벽을 뚫고 이동하지 하지 못하는 벽 관련 제약으로 이 부분은 두 객체(탱크와 벽)의 충돌확인 후 이동의 제약이고, 해당 내용은 **move** 메소드에 추가되어야 할 것이다. 이 내용까지 포함해 지금까지 구현내용을 한번꺼번에 보여주면 다음과 같다. actors.py Copy from pgzhelper import * from abc import ABC, abstractmethod class CheckOutOfScreen(ABC): def __init__(self, screen): self.s_width, self.s_height = screen @abstractmethod def is_out_of_screen(self): pass class Bullet(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self.angle = angle def is_out_of_screen(self): if self.x > self.s_width or self.x < 0 or \ self.y > self.s_height or self.y < 0: return True else: return False class Tank(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, walls, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self._original_pos = 0 self.angle = angle self._walls = walls def is_out_of_screen(self): if self.right > self.s_width or self.left < 0 or \ self.bottom > self.s_height or self.top < 0: return True else: return False def move(self): self._original_pos = self.pos if self.angle == 180: self.x -= 2 elif self.angle == 0: self.x += 2 elif self.angle == 90: self.y -= 2 elif self.angle == 270: self.y += 2 # 화면경계 확인 if self.is_out_of_screen(): self.pos = self._original_pos # 벽더미 확인 if self.collidelist(self._walls) != -1: self.pos = self._original_pos 🔢 59~60 라인에서 탱크가 벽더미와 충돌이 있는지의 여부는 **collidelist** 메소드의 활용을 통해 충돌여부 확인이 가능하고, 다만, 충돌의 대상이 되는 벽더미들 객체들의 인스턴스 정보(구체적으로는 생성된 객체의 메모리 상에 위치정보)는 29, 34 라인에서처럼 탱크 객체를 생성할 때 파라미터로 넘겨받아서 활용할 수 있다. 탱크객체들의 이동관련 기능구현이 완료되었으므로, 이를 실제로 사용하는 쪽(battle\_city\_oop.py 내의 update 함수)에서의 코드까지 포함해서 살펴보면 우리의 구현코드가 어떻게 동작하는지 더 입체적으로 보일 것이다. battle\_city\_oop.py Copy import random from actors import Tank WIDTH = 800 HEIGHT = 600 enemy_move_cnt = 0 ENEMY_MOVE_DELAY = 20 # 50x50 크기의 벽의 더미 생성 walls = [] WALL_SIZE = 50 for x in range(int(WIDTH / WALL_SIZE)): # 탱크가 위치 할 첫 행과 마지막 행 총 2행을 비워두기 위해 -2 하여 생성 for y in range(int(HEIGHT / WALL_SIZE - 2)): if random.randint(0, 100) < 50: # 적정 수의 벽을 생성 wall = Actor("wall", anchor=("left", "top")) wall.x = x * WALL_SIZE wall.y = y * WALL_SIZE + WALL_SIZE # 맨 첫 행 비우기 위해 전체적으로 아래로 밀기 walls.append(wall) # 주인공 탱크 생성 tank = Tank("tank_blue", (400, 575), 90, walls, (WIDTH, HEIGHT)) # 적 탱크 생성 enemies = [] MAX_ENEMIES = 3 for i in range(MAX_ENEMIES): enemies.append(Tank("tank_red", (400, 25), 270, walls, (WIDTH, HEIGHT))) def draw(): screen.blit("grass", (0, 0)) # 배경이미지 그리기 tank.draw() for enemy in enemies: enemy.draw() for wall in walls: wall.draw() def update(): global enemy_move_cnt # 주인공 탱크 if keyboard.left: tank.angle = 180 tank.move() elif keyboard.right: tank.angle = 0 tank.move() elif keyboard.up: tank.angle = 90 tank.move() elif keyboard.down: tank.angle = 270 tank.move() # 적 탱크 for enemy in enemies: choice = random.randint(0, 3) if enemy_move_cnt > 0: enemy_move_cnt -= 1 enemy.move() elif choice == 0: # 움직임 지연 초기화 enemy_move_cnt = ENEMY_MOVE_DELAY elif choice == 1: # 랜덤방향 결정 enemy.angle = random.randint(0, 3) 코드에서 유심히 볼 것은 새롭게 등장한 **update** 함수인데 주인공 탱크를 게임유저의 키보드 조작으로 조정하고, 적 탱크를 자동조정하기 위한 코드 부분은 절차지향 때의 알고리즘하고 동일하여 이해에 큰 어려움은 없을 것이다. 기존 절차지향과의 차이점은 기존의 글로벌 함수호출로 기능동작이 이뤄졌던 부분이 이제는 다 객체 호출로 대치되고 있어 우리가 목표하는 바를 이뤄가고 있다는 것이다. 그럼, 이제 다음으로 탱크들의 총알발사에 대한 것으로 다음 절에서 이를 디자인해보고 구현해보도록 하자. [Previous9.4 객체지향 개발이론 (인터페이스)chevron-left](https://jjlee.gitbook.io/python-pygame_zero/battle_city/oop_interface) [Next9.6 객체지향으로 개발하기 3chevron-right](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.6_oop_dev3) Last updated 6 months ago --- # 9.4 객체지향 개발이론 (인터페이스) | 파이썬-파이게임 제로 (순한맛) 우리는 지난번 객체지향 개발이론에서 상속이라는 개념을 익혔다. 이번 절에서는 개념적으로는 상속의 한 형태이나 목적이 조금 다른 인터페이스(Interface)라는 개념을 배울 필요가 있다. 상속이 매우 유용한 개념임에도 불구하고 모든 문제를 상속으로만 풀 수 없는 한계를 가지고 있는데 다음과 같은 예의 상황이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FZY93qRpKTqdbEJzuWpQk%252FPasted%2520image%252020241219092928.png%3Falt%3Dmedia%26token%3Dcca87546-01f0-4f35-a9bf-6f93529f67e7&width=768&dpr=3&quality=100&sign=886f1962&sv=2) 그림에서 보는 것과 같이 우리가 살아가는 생태계에는 펭귄처럼 조류라고 해서 모두 날 수 있는 건 아니고, 날 수 있지만 조류에 속하지 않는 박쥐가 있고, 포유류이지만 걸어다니지 않고 어류처럼 헤엄쳐 다니는 고래처럼 여러 분류에 걸쳐지는 하이브리드 생명체들이 존재한다. 이처럼 일부 객체에게서만 필요한 특정 기능(수영할 수 있는(Swimmable), 날 수 있는(Flyable) 기능)이 존재할 수 있는데, 이를 위해 상위 부모객체(Bird, Mammal, Fish)에서 구현을 해놓으면 그 기능이 실제가 필요가 없는 모든 자식들에게도 존재하게 되고 결국 모든 자식 객체의 크기도 일괄 커지게 되어 불필요 메모리만 차지하게 되는 단점이 존재한다. 따라서, 이러한 기능들에 대해서는 독립적인 추상(abstract) 객체를 만들고, 해당 기능이 필요한 각각의 객체들이 이 추상객체를 상속 후 이를 직접 구현을 강제(?)하도록 하게 하는 방법을 사용하는데, 이러한 추상객체(abstract object)를 인터페이스라고 부른다. 여기서, _추상적이라는 의미는 구체적(concrete)의 반대적 의미로, 실제 구체화된 구현(implementation) 내용은 없다는 의미_로 따라서, **인터페이스는 메서드의 시그니처(이름, 매개변수, 반환 타입)만을 정의(define)하고 구현은 포함하지 않는 추상적인 타입**이다. 객체는 하나 이상의 인터페이스를 상속할 수 있으며, 상속한 클래스는 인터페이스에 정의된 모든 메서드를 반드시 구현해야 한다(구현 안하고 실행시키면 에러가 발생)는 의미가 구현을 강제한다는 의미이다. 위에 그림의 일부를 인터페이스를 포함해 코드로 표현해 보면 다음과 같다. Copy from abc import ABC, abstractmethod class Animal(ABC): @abstractmethod def has_organ(self): pass class Bird(Animal, ABC): def has_organ(self): print("I have wings") @abstractmethod def has_feather(self): pass class Flyable(ABC): @abstractmethod def fly(self): pass class Eagle(Bird, Flyable): def has_feather(self): print("I have 718 feathers.") def fly(self): print("I can fly.") class Penguin(Bird): def has_feather(self): print("I have short and waterproof feathers.") 참고로 설명을 위한 예졔 코드라 단순화 시켜 보여주기 위해 \_\_init\_\_ 함수들은 제외했음을 인지하기 바란다. Copy # 사용예시 eagle = Eagle() eagle.has_organ() eagle.fly() eagle.has_feather() penguin = Penguin() penguin.has_feather() 실행결과: I have wings I can fly. I have 718 feathers. I have short and waterproof feathers. ### [hashtag](https://jjlee.gitbook.io/python-pygame_zero/battle_city/oop_interface#define) **추상객체 정의(define) 문법** > from abc import ABC, abstractmethod > > **class** 추상클래스 이름(ABC): > > _**def**_ _\_\_init\_\_(self, 파라미터들):_ > > _속성들_ > > **@abstractmethod** > > **def** 추상메소드 이름(self): > > pass 추상객체들을 만드는 문법은 처음 등장했으므로 유심히 살펴보도록 하자. 먼저는 **abc**라는 내장 모듈을 import 하고 **ABC**([Abstract Base Classarrow-up-right](https://docs.python.org/3.13/library/abc.html) ) 라는 추상클래스를 만들기 위한 베이스 클래스를 상속받고, 추상클래스 안에 존재하게 될 추상메소드 위에는 **@abstractmethod** 라는 데코레이터(decorator)를 붙혀서 만들고, _추상이기 때문에 당연히 그 함수의 상세 구현내용 없이 pass 라는 아무 것도 실행할 것이 없다는 구문을 하나 넣어놓고 끝낸다._ _데코레이터 문법도 새롭게 등장했는데 기존 함수를 수정하지 않고 그 기능을 확장하는 방법을 제공하는 문법으로_ 여기서는 간단히 이 함수는 일반 메소드가 아니라 추상 메소드로 인지하라는 의미로 붙혔다고 간단히 생각하자. Flyable 인터페이스를 상속받은 Eagle 클래스를 살펴보자. Flyable 인터페이스를 상속 받았기 때문에 Flyable 인터페이스의 추상메소드였던 fly 함수를 반드시 구현하게(이전 시간에 배운 함수 오버라이딩 구현방식) 되어있다. 그럼, 상속만 받고 구현을 안하면 어떻게 될까? 직접 해보면 아는데 아래와 같이, 추상메소드인 fly 함수를 구현하지 않았다 라고 에러를 내고 종료하게 된다. 그도 그걸 것이 사용하는 측 입장에서 실제 구현이 없는 것을 어떤 기능을 하도록 기대하며 사용할 수 는 없지 않은가? Copy File "test.py", line 64, in eagle = Eagle() TypeError: Can't instantiate abstract class Eagle with abstract methods fly 다시 정리하면, 여기서의 _**인터페이스(추상객체) 상속의 개념은 우리가 부모의 모든 기능을 물려받는 목적에서의 상속개념이라기 보다, 이를 상속한 객체의 경우, 그 기능을 반드시 갖고 있다는 것을 보장하게 되는 약속 개념과 그 인터페이스의 구현을 강제화 함으로써 객체지향설계를 한 사람의 규칙을 따르게 하려는 목적에 있다.**_ 결국 그 규칙을 따르게 되면 어떠한 이점을 갖게 되는가가 궁금할 수 있는데, 그 부분은 객체지향의 또 다른 커다란 주제인 **다형성(Polymorphism)**이란 개념과 밀접한 관련이 있고, 워낙 내용적으로 설명할 내용이 많아 이번에 다루지 않고, 추후에 이 개념을 알고 사용해야 하는 시점에서 다시한번 설명할 기회를 갖도록 하겠다. 자, 그럼 이 인터페이스 개념을 활용해 이번 과의 목적인 배틀 시티게임의 객체지향형으로의 개발을 계속 이어가도록 하자. [Previous9.3 객체지향으로 개발하기 1chevron-left](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.3_oop_dev1) [Next9.5 객체지향으로 개발하기 2chevron-right](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.5_oop_dev2) Last updated 6 months ago --- # 7.3 충돌처리 및 기타정보(점수 및 게임종료) 표기 | 파이썬-파이게임 제로 (순한맛) 이제 남은 두 개의 함수의 구현체를 만들도록 하겠다. 제일 먼저, 충돌검사(**check\_collision**) 함수에 대한 부분이다. 여기서 말하는 충돌이란, 주인공이 쏜 총알에 적 비행기가 맞았을 때, 또는 반대로 적 비행기의 총알에 주인공이 맞았을 때, 마지막으로 적 비행기와 주인공이 직접 충돌했을 때 이렇게 총 3가지가 있을 수 있겠다. 각각의 충돌에 대한 코드를 살펴보자. 적의 총알은 단 1개가 아니므로 총알객체의 모음인 리스트 형태로 존재하고, 따라서, 리스트 안에 하나하나의 총알들과 개별 충돌검사를 for 구문을 통해 하게되고, 충돌이 발견될 경우, 총알 자체도 없에고(8번 라인에서 메모리에서 해제), 적 비행기도 없에면(15번 라인) 되는데 단순하게 그냥 사라지면 흥미가 떨어지니 게임성을 위해 폭발장면을 애니메이션화 하였다. 참고로 적 비행기를 없에기 위한 방법으로 **del** 명령어 문법이 새롭게 등장했는데, 이는 객체를 제거(메모리에서 해제)하는 한 방법 중에 하나이다. 우리는 enemies 리스트에 enemy 객체들이 담겨있다는 것을 알고, enemy\_index를 통해 그 안에 지우고자 하는 객체의 위치를 특정해 지우는 것이다. 그밖에 리스트 안에 특정 값을 제거하는 방법으로 **pop**과 **remove** 내장메소드를 사용해봤었는데, 이 메소드들을 사용해서 지울 수 있지만, 편리한 새로운 문법을 배우는 기회로 삼으면 되겠다. 폭발 장면 2장의 이미지를 객체화한 후 에니매이션 처리를 하였는데 주의깊게 봐야할 부분은 폭발장면 애니메이션을 시간지연을 주면서 충분히 보여주는 처리가 필요하였다(13번, 21~23번 라인). 이 부분이 왜 필요한지는 이 부분의 코드를 제거하고 실행해보면 금방 이해가 되는데, [이전 절](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.2_bullet_attack) 에서 update 콜백함수의 빠른 호출에 의한 동일한 사유로 애니메이션을 충분히 보여주기도 전에 폭발객체 자체를 삭제해버리는 것을 방지하기 위한 목적이다. 그밖에 score와 game\_over의 두 글로벌 변수에 대해서는 적 비행기를 제거해 점수가 증가되는 상황(16번 라인)과 주인공이 죽어 게임이 종료되는 상황(27번 라인)에 대해 변수값 변경을 적용하고 있다. Copy def check_collision(): global score, game_over # 적이 총알에 맞았을 때 for bullet in bullets: enemy_index = bullet.collidelist(enemies) if enemy_index != -1: bullets.remove(bullet) explosion = Actor("explosion1") explosion.pos = enemies[enemy_index].pos explosion.images = ["explosion1", "explosion2"] explosion.fps = 8 explosion.duration = 15 explosions.append(explosion) del enemies[enemy_index] score += 1 # 적 비행기 폭발 애니메이션 for explosion in explosions: explosion.animate() explosion.duration -= 1 if explosion.duration == 0: explosions.remove(explosion) # 적 총알에 주인공이 맞거나, 적과 직접 충돌했을 때 if player.collidelist(enemy_bullets) != -1 or player.collidelist(enemies) != -1: game_over = True 이제 마지막으로 남은 화면에 정보표시용 draw\_text 함수를 구현하는 것으로 모든 사용자 함수의 구현은 끝이나게 된다. draw\_text 함수는 간단한데, 아래와 같이 점수와 게임종료 상황에 대해 단순 화면 표시이다. Copy def draw_text(): screen.draw.text("Score " + str(score), (50, 0), color="black", fontsize=30) if game_over: screen.draw.text( "Game over", midbottom=(WIDTH / 2, HEIGHT / 2), color="blue", fontsize=100 ) 정말 마지막으로 처리해야 할 것이 하나 남았는데 그것은 게임이 종료되었음에도 불구하고 계속 게임을 할 수 있는 상황을 막아놓는 것이다. 크게 어려운 것은 아니고, 아래와 같이 update 콜백함수에서 게임종료가 아닐 때만, 주인공의 조작이 가능하게 조건문을 넣으면 되는 것이다(8번 라인). Copy def update(): for background in backgrounds: background.y += 3 if background.top > HEIGHT: background.y = (HEIGHT / 2) - HEIGHT create_enemies() if game_over == False: move_player() shoot_bullets() check_collision() 이것으로 이번 트윈비 게임의 모든 구현이 완료되었다. 마지막으로 전체 소스코드를 첨부하는 것으로 이 절을 마무리 하겠다. Copy from pgzhelper import * import random WIDTH = 800 HEIGHT = 600 backgrounds = [] background1 = Actor("background1", (WIDTH / 2, HEIGHT / 2)) backgrounds.append(background1) background2 = Actor("background2", (WIDTH / 2, (HEIGHT / 2) - HEIGHT)) backgrounds.append(background2) player = Actor("player", (400, 500)) MAX_BULLETS = 3 bullets = [] enemies = [] enemy_bullets = [] explosions = [] score = 0 game_over = False music.play('main_theme') def move_player(): if keyboard.right: player.x += 5 if keyboard.left: player.x -= 5 if keyboard.up: player.y -= 5 if keyboard.down: player.y += 5 if player.right > WIDTH: player.right = WIDTH if player.left < 0: player.left = 0 if player.bottom > HEIGHT: player.bottom = HEIGHT if player.top < 0: player.top = 0 def shoot_bullets(): if keyboard.space and len(bullets) < MAX_BULLETS: sounds.sfx_sounds_interaction25.play() bullet_delay = 5 bullet = Actor("player_bullet") bullet.pos = player.pos bullet.angle = 90 bullets.append(bullet) for bullet in bullets: bullet.move_forward(15) if bullet.y < 0: bullets.remove(bullet) def create_enemies(): if random.randint(0, 1000) > 980: enemy = Actor("enemy1_1") enemy.images = ["enemy1_1", "enemy1_2"] enemy.fps = 5 enemy.y = -50 enemy.x = random.randint(100, WIDTH - 100) enemy.direction = random.randint(-100, -80) enemies.append(enemy) for enemy in enemies: enemy.move_in_direction(4) enemy.animate() if enemy.top > HEIGHT: enemies.remove(enemy) if random.randint(0, 1000) > 990: bullet = Actor("enemy_bullet") bullet.pos = enemy.pos bullet.angle = random.randint(0, 359) enemy_bullets.append(bullet) for bullet in enemy_bullets: bullet.move_forward(5) if bullet.x < 0 or bullet.x > WIDTH or bullet.y < 0 or bullet.y > HEIGHT: enemy_bullets.remove(bullet) def check_collision(): global score, game_over for bullet in bullets: enemy_index = bullet.collidelist(enemies) if enemy_index != -1: bullets.remove(bullet) explosion = Actor("explosion1") explosion.pos = enemies[enemy_index].pos explosion.images = ["explosion1", "explosion2"] explosion.fps = 8 explosion.duration = 15 explosions.append(explosion) del enemies[enemy_index] score += 1 for explosion in explosions: explosion.animate() explosion.duration -= 1 if explosion.duration == 0: explosions.remove(explosion) if player.collidelist(enemy_bullets) != -1 or player.collidelist(enemies) != -1: game_over = True def draw_text(): screen.draw.text("Score " + str(score), (50, 0), color="black", fontsize=30) if game_over: screen.draw.text( "Game over", midbottom=(WIDTH / 2, HEIGHT / 2), color="blue", fontsize=100 ) def draw(): for background in backgrounds: background.draw() player.draw() for enemy in enemies: enemy.draw() for bullet in bullets: bullet.draw() for explosion in explosions: explosion.draw() for bullet in enemy_bullets: bullet.draw() draw_text() def update(): for background in backgrounds: background.y += 3 if background.top > HEIGHT: background.y = (HEIGHT / 2) - HEIGHT create_enemies() if game_over == False: move_player() shoot_bullets() check_collision() [Previous7.2 배우들의 움직임과 총알 공격 구현하기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.2_bullet_attack) [Next7.4 그 밖에 도전과제chevron-right](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.4_additional_challenge) Last updated 6 months ago --- # 5. 플래피 버드(Flappy bird) 게임 만들기 | 파이썬-파이게임 제로 (순한맛) 우리가 처음으로 게임제작에 도전할 게임은 바로 플래피 버드(Flappy bird)란 게임으로 이 게임은 2012년 베트남 1인 인디게임 개발자가 만든 게임으로 전 세계적 글로벌 히트를 만들었던 과거 한 때는 중독성(?)으로 꽤나 유명한 게임이다. 특별히 개도국인 베트남에서 젊은 한 청년이 이룬 대단한 성공신화로 또 글로벌 히트가 시작되어 당시 일 5만달러 이상의 수익이 발생하고 있던 차에 돌연 3개월만에 해당 게임으로 돈버는 일을 자진 중단한 것까지 [게임에 얽힌 스토리텔링적 요소arrow-up-right](https://www.thisisgame.com/webzine/series/nboard/212/?n=116507&series=26) 도 상당하기 때문에 이런 내용까지 함께 알고 게임을 만든다면 만드는 재미가 더할 것이다. 아마 이미 여러분들 중에 일부는 이 게임을 똑같이 재현해 보는 클론(clone)을 엔트리/스크래치 블록코딩으로 만들어 보았을 수도 있으리라 생각한다. 그러나 플래피 버드(Flappy bird) 게임 자체를 만들어 보는게 이번에 처음 안 사람도 괜찮다 그런 분들은 다음의 동영상 시청을 통해 해당 게임의 플레이 방법에 대해 사전숙지하게 되면 이번 과의 전체적인 이해에 도움을 얻을 수 있을 것이다. [Previous4\. 게임 루프의 이해chevron-left](https://jjlee.gitbook.io/python-pygame_zero/game_loop) [Next5.1 화면에 배경 이미지(오브젝트) 나타내기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 9.6 객체지향으로 개발하기 3 | 파이썬-파이게임 제로 (순한맛) 우리가 객체지향에서 객체를 누가 생성하는지에 대한 생성에 관한 것이 중요하고, 때론 특히나 게임개발에서는 소멸시점에 대해서도 함께 고려할 필요가 있는데 총알객체라고 하는 부분이 이에 해당할 수 있다. 총알객체는 탱크(주인공 탱크, 적 탱크)가 생성하는 것은 당연한데 주인공 탱크는 게임유저의 조작, 즉, 스페이스바를 누를 때 생성되야 하고, 적 탱크는 컴퓨터에 의해 자동으로 랜덤발사가 된다. 그밖에 _총알의 소멸시점은 총 3가지로 고려될 수 있는데, 먼저, 총알이 날아가다가 벽과 충돌할 때, 상대 탱크와 충돌할 때, 마지막으로 중간에 아무 충돌없이 화면 밖으로 나갔을 때이다._ 먼저, 기존 총알 객체에는 총알 날아가는 것에 대한 구현이 빠져있는데, **move** 라는 메소드를 추가하되, 총알이 동서남북 어디로 날지는 **angle** 속성값으로 결정되게 된다. 탱크 객체는 객체 이용자에게 총알을 쏘는 서비스를 제공해야 하고, 이를 f**ire** 라는 메소드를 만들어 제공하기로 한다. 그리고, 총알 객체가 만들어지는 시점도 바로 이 함수가 호출되는 시점이라 할 수 있다. actors.py Copy class Bullet(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self.angle = angle def is_out_of_screen(self): if self.x > self.s_width or self.x < 0 or \ self.y > self.s_height or self.y < 0: return True else: return False def move(self): if self.angle == 0: self.x += 5 elif self.angle == 90: self.y -= 5 elif self.angle == 180: self.x -= 5 elif self.angle == 270: self.y += 5 # 화면경계 확인 if self.is_out_of_screen(): self.pos = self._original_pos # 벽더미 확인 if self.collidelist(self._walls) != -1: self.pos = self._original_pos class Tank(Actor, CheckOutOfScreen): def __init__(self, img_name, pos, angle, walls, screen): Actor.__init__(self, img_name, pos) CheckOutOfScreen.__init__(self, screen) self._original_pos = 0 self.angle = angle self._walls = walls self._bullets = [] def is_out_of_screen(self): if self.right > self.s_width or self.left < 0 or \ self.bottom > self.s_height or self.top < 0: return True else: return False def move(self): self._original_pos = self.pos if self.angle == 180: self.x -= 2 elif self.angle == 0: self.x += 2 elif self.angle == 90: self.y -= 2 elif self.angle == 270: self.y += 2 # 화면경계 확인 if self.is_out_of_screen(): self.pos = self._original_pos # 벽더미 확인 if self.collidelist(self._walls) != -1: self.pos = self._original_pos def fire(self, image, sound_obj=None): bullet = Bullet(image, self.pos, self.angle, (self.s_width, self.s_height)) self._bullets.append(bullet) if sound_obj: sound_obj.play() def draw(self): super().draw() for bullet in self._bullets: bullet.draw() 🔢 52라인에서 총알객체를 생성하면서 총알객체의 첫 위치는 탱크 그 자신의 중심점에 존재행야 하기 때문에 **self.pos** 라는 좌표값를 인자값로 넘겼다는 것을 기억하자. 그리고, 화면경계에 대한 튜플값 **(self.s\_width, self.s\_height)** 은 이전 챕터 CheckOutOfScreen 부모객체(인터페이스)의 초기화 때 이미 설정해놨기 때문에 부모에 존재하는 그 속성값을 자식은 마치 본인의 속성값처럼 사용할 수 있는 것이다. battle\_city\_oop.py Copy # 주인공 탱크 총알 재장전 지연 bullet_delay_cnt = 0 BULLET_DELAY = 50 def update(): global enemy_move_cnt, bullet_delay_cnt # 주인공 탱크 if keyboard.left: tank.angle = 180 tank.move() elif keyboard.right: tank.angle = 0 tank.move() elif keyboard.up: tank.angle = 90 tank.move() elif keyboard.down: tank.angle = 270 tank.move() if bullet_delay_cnt == 0: # 총알 재장전 가능 if keyboard.space: tank.fire("bulletblue2", sounds.sfx_exp_medium12) bullet_delay_cnt = BULLET_DELAY else: bullet_delay_cnt -= 1 # 적 탱크 for enemy in enemies: choice = random.randint(0, 3) if enemy_move_cnt > 0: enemy_move_cnt -= 1 enemy.move() elif choice == 0: # 움직임 지연 초기화 enemy_move_cnt = ENEMY_MOVE_DELAY elif choice == 1: # 랜덤방향 결정 enemy.angle = random.randint(0, 3) * 90 else: enemy.fire("bulletred2") 🔢 **battle\_city\_oop.py** 코드 내 **update** 함수에서 주인공 탱크의 총알 재장전 속도를 지연시키기 위한 코드 부분은 절차지향 때의 알고리즘하고 동일하여 이해에 큰 어려움은 없을 것이다. 절차지향에서는 총알발사 소리에 대한 재생을 직접했다면, 객체지향에서는 소리재생 부분도 객체에게 위임하고 있다. 지금까지 코딩된 부분을 실행시켜보면, 적 탱크들이 자동으로 총알발사가 일어나고, 주인공 탱크도 스페이스바가 눌려진 시점에 발포소리와 함께 총알생성이 잘 이뤄질 것이다. 다만, 생성된 총알이 실제 움직여 날아가지 않을 텐데, 그 이유는 우리가 아직 실제 총알 움직임을 만들고 충돌검사를 하는 부분의 코드를 추가하지 않았기 때문이고, 이제 그 부분의 코드를 함께 추가해 보도록 하자. actors.py Copy class Tank(Actor, CheckOutOfScreen): ... 생략 def collidelist_bullets(self, tanks):battle_city_oop.py tank_index = -1 for bullet in self.bullets: bullet.move() # 화면경계 확인 if bullet.is_out_of_screen(): self.bullets.remove(bullet) # 벽 더미 확인 wall_index = bullet.collidelist(self.walls) if wall_index != -1: del self.walls[wall_index] self.bullets.remove(bullet) # 상대 탱크 확인 tank_index = bullet.collidelist(tanks) if tank_index != -1: self.bullets.remove(bullet) return tank_index battle\_city\_oop.py Copy def update(): global enemy_move_cnt, bullet_delay_cnt # 주인공 탱크 ... 생략 enemy_idx = tank.collidelist_bullets(enemies) if enemy_idx != -1: del enemies[enemy_idx] if len(enemies) == 0: winner = "You" # 적 탱크 for enemy in enemies: ... 생략 tank_idx = enemy.collidelist_bullets([tank]) if tank_idx != -1: winner = "Enemy" 🔢 우리는 탱크 부모 객체에 **collidelist\_bullets** 라는 멤버함수를 구현했다. 이 함수의 역할은 총알이 있을 경우, 총알의 움직임을 만들고(8번 라인), 그 총알이 날아가다가 벽 더미, 또는 상대 탱크와 충돌이 생기거나 충돌없이 화면 밖을 나가면 총알객체를 제거한다.(12, 18, 23라인). 그리고, 리턴값으로는 탱크에 총알이 명중했냐의 유무가 중요하기 때문에 그에 따른 결과를 리턴한다. 반면에 battle\_city\_oop.py 안 update 함수 안에서는 이 멤버함수를 반복적으로 호출하므로써 멤버함수의 리턴값을 통해 총알의 명중유무를 판별 후 예를 들어, 적 탱크가 명중하면 명중된 탱크를 제거 하는 등(9번 라인) 이 결과를 게임에 반영하는 코드가 추가되었다. 여기서 **winner** 라는 변수는 절차지향형 버전에서도 사용했던 동일한 내용으로 게임의 승자판별을 목적하는 값으로 winner 라는 변수에 담긴 값에 따라 추후 화면중앙에서 게임결과가 큰 텍스트 형태로 표시된다. 드디어, 우리 게임의 거의 대부분을 완성했고 마지막 남은 부분은 명중된 탱크의 폭발장면의 에니메이션을 추가하는 부분이 남아있다. 남은 부분은 최종 완성은 다음 절로 넘겨 진행하도록 하겠다. [Previous9.5 객체지향으로 개발하기 2chevron-left](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.5_oop_dev2) [Next9.7 객체지향으로 개발하기 4chevron-right](https://jjlee.gitbook.io/python-pygame_zero/battle_city/8.9_oop_dev4) Last updated 6 months ago --- # 6. 블록격파(Breakout) 게임만들기 | 파이썬-파이게임 제로 (순한맛) 블록격파 게임은 모르는 사람이 없을 정도로 1972년에 창업한 세계 최초의 비디오게임 회사인 [**아타리(Atari)**arrow-up-right](https://namu.wiki/w/%EC%95%84%ED%83%80%EB%A6%AC) 에서 1976년에 제작한 아주 고전적인 게임이다. 시간을 거치면서 다양한 변형 버전이 출시되었으나, 우리는 발매당시 최초 버전의 블록격파 게임(또는 벽돌깨기 게임이라고도 불렸으며, 영어의 원제는 [**Breakout**arrow-up-right](https://namu.wiki/w/%EB%B8%8C%EB%A0%88%EC%9D%B4%ED%81%AC%EC%95%84%EC%9B%83) 임) 고전게임의 기본적인 게임룰을 따라하는 것을 목표할 것이다. 즉, 게임화면 하단을 제외하고는 화면의 모든 닿는 것을 반사시키는 공이 계속 반사되면서 모든 블록(또는 벽돌)을 격파하는게(부시는게) 목표인 게임의 클론(clone)을 같이 만들어 보겠다. 블록격파(벽돌깨기) 게임을 만드는 방식을 크게 2가지 중 하나를 택할 수 있는데, 이 게임무대의 배우들(블록, 공, 반사판)을 전부 직접 코딩으로 그려서 사용할 것인가와 아니면 기존처럼 전부 준비된 이미지를 사용해서 만들 것인가를 선택할 수 있다. 전자는 사실은 이 게임의 원조게임에서 사용했던 방식이고, 후자는 요즘 트랜드의 그래픽을 중시하는 게임이라 할 수 있는데 그렇다면, 두 게임제작 방법 중에 단순히 어떤 것이 더 코딩이 적고 코드를 이해하기 쉬운가를 따지면, 후자쪽이다. 그러나 만약 우리는 1인 개발자라서 게임에 필요한 이미지들을 본인이 직접 그래픽편집툴을 사용해 다 제작해야 한다면 코딩은 적어진다고 해도, 이 작업에서 시간과 노력이 많이 들어갈 수도 있어서 전체적으로 개발자 입장에서 어떤게 더 편하냐의 의미는 달라질 수도 있다. 이번 6장에서 우리는 이렇게 하겠다. 우선 더 쉬운 방법인 준비된 이미지들을 활용한 게임을 먼저 만들고, 이후로 추가 옵션으로 이미지들은 전혀 사용하지 않고, 게임의 등장인물들은 모두 코딩으로 직접 그려서 만드는 원조 게임의 클론(clone)을 추가적으로 만들어 보기로 하겠다. [Previous5.8 플래피버드 게임 추가기능 구현하기 (플래피버드 애니메이션)chevron-left](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.8_animation) [Next6.1 게임무대에 배경과 배우 등장시키기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.1_bg_actors) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 뮤 에디터 단축키 모음 | 파이썬-파이게임 제로 (순한맛) 기타 단축키 내역은 [여기arrow-up-right](https://codewith.mu/en/tutorials/1.2/shortcuts) 서 확인 [hashtag](https://jjlee.gitbook.io/python-pygame_zero/appendix/shortcuts#undefined) 윈도우즈용 ---------------------------------------------------------------------------------------------- 기능 단축키 사용가능 버전 들여쓰기 / 내어쓰기 단계 조정 Tab / Shift + Tab 선택 라인을 주석화 하기 Ctrl + k 현재 라인을 지우기 Ctrl + l (단어)검색하기 Ctrl + f 전방방향으로 이어서 검색 F3 후방방향으로 이어서 검색 Shift + F3 코딩화면 확대 / 축소 Ctrl + Shift + = , Ctrl + - 코드의 실행과 멈춤 F5 코드 검사 F2 코딩모드 변경 Ctrl + Shift + m 설정화면 열기 Ctrl + Shift + d 코딩화면 화면캡처(스크린 샷) Ctrl + Alt + s 파일을 새 이름으로 저장 Ctrl + Shift + s 현재 탭(윈도우) 닫기 Ctrl + w 커스텀 1.2.5 이상 [hashtag](https://jjlee.gitbook.io/python-pygame_zero/appendix/shortcuts#undefined-1) 맥용 --------------------------------------------------------------------------------------------- 윈도우즈용 단축키에서 Ctrl키 대신 Command키를 사용하면 대부분 동일하게 동작 [Previous게임을 단 한 개의 실행파일로 패키징 하기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/appendix/packaging) Last updated 1 year ago --- # 10.4 네트워크 게임으로 만들기 2 | 파이썬-파이게임 제로 (순한맛) 이전 절에 예고된대로 먼저, 게임의 시작처리와 공의 움직임 처리에 대해서 아보도록 하자. 게임의 시작은 기존 처럼 누구든 먼저 스페이스 바를 누르면서 시작할 수 있다. 다만, 네트워크 게임의 특성상 그룹방 안에 게임 시작 가능 총 인원이 다 입장되어 있는지의 추가 확인이 필요하고(37라인) 확인된 이후에 게임시작이 가능한다. 게임의 시작하게 되면 원격에 떨어져 있는 상대도 곧바로 게임을 시작할 수 있도록 상대에게 **game\_start** 메시지를 보낼 필요가 있다(40라인). Copy class Ball(Rect): ... def collide_wall(self, sound_only=False): # 위쪽 또는 아래쪽 벽 if self.top < 0 or self.bottom > HEIGHT: if not sound_only: self.vy = -self.vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 왼쪽 벽 if self.left < 0: if not sound_only: self.reset() sounds.die.play() return 'b2' # 오른쪽 벽 if self.right > WIDTH: if not sound_only: self.reset() sounds.die.play() return 'b1' return '' ... def update(): global num_peers ... # 게임시작 조건 if score.is_game_over(): # 게임 시작은 누구나 먼저 시작가능 if num_peers == MAX_PEERS and keyboard.space: score.reset() ball.reset() net.send_msg(peer_id, 'game_start', True) else: # 공 if is_host: ball.move() net.send_msg('Player2', 'ball_pos', {'center': ball.center, 'vel': (ball.vx, ball.vy)}) who_win = ball.collide_wall() if who_win: # 점수 score.add(who_win) net.send_msg('Player2', 'score', who_win) ball.reset() else: # Plyer2 ball.move() ball.collide_wall(sound_only=True) ... 다음으로는 41라인 이후의 볼의 움직임에 관한 것으로, 이전에 살펴본 반사판의 움직임 처리와 많이 유사하다. 공의 움직임에 처리에 대한 책임은 호스트가 갖고 있으며 공의 현재 위치정보를 **ball\_pos** 메시지를 통해 지속적으로 상대에게 보내상대도 자신의 화면에서 공의 위치를 실시간 업데이트 할 기회를 주고 있다(44라인). 공의 위치정보는 공의 중심좌표(ball.center)와 이동속도(ball.vx, ball.vy)에 대해 (튜플)값을 **{'center':ball.center, 'vel':(ball.vx, ball.vy)}** [딕셔너리](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.4_dev_net_2#tuple) 라는 데이터 형태로 제공하고 있음을 주의할 필요가 있다. 딕셔너리라고 하는 데이터 타입은 이 책에서 처음으로 등장했기 때문에, 해당 데이터 타입 등장한 목적과 사용 문법을 익힐 필요가 있다. [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.4_dev_net_2#tuple) 딕셔너리, 사전 (Dictionary) ------------------------------------------------------------------------------------------------------------------- 먼저, 딕셔너리는 그 이름 그대로, 사전이란 뜻인데, 사전은 특정 단어와 그 단어의 뜻을 쌍(pair)으로 모아놓은 집합과 같다 할 수 있다. 파이썬의 딕셔너리라는 데이터 타입도 그러하다. 리스트가 단순한 모든 종류의 데이터들의 모음(객체)이라면, _딕셔너리는 그_ _**각각의 데이터(값(Value)라고 호칭)**__에_ _**그 데이터를 지칭하는 특정이름(키(Key)라고 호칭)**__까지 함께 쌍으로 묶은 데이터들의 모음(객체)_이라고 할 수 있다. 리스트에서는 리스트 안에 값을 참조하기 위해 인덱스를 사용했다면, 딕셔너리에서는 인덱스 없이 대신 자신을 지칭하는 사전에 설정한 그 이름(key)으로 값을 참조할 수 있게 된다. 다음의 사용 문법을 보면 더 쉽게 이해할 수 있다. > **딕셔너리 이름 = {****키: 값** **의 쌍의 형태로 구성된 여러 값. 단, 키와 값의 쌍들 사이는 콤마(,)로 구분}** 딕셔너리 예시코드 Copy person = {'name': 'Bob', 'occupation': 'Engineer'} print(person['name']) # 출력: name (키를 사용하여 값 접근) person['age'] = 25 # 새로운 키-값 쌍 추가 person['occupation'] = 'Software Engineer' # 기존 키의 값 변경 del person['age'] # 특정 키-값 쌍 삭제 print(person) # 출력: {'name': 'Bob', 'occupation': 'Software Engineer'} 이제 공이 벽에 부딪히는 상황에 대한 처리로 기존에 **collide\_wall** 멤버함수에 일부 변형이 적용되었는데, 공이 화면 좌우벽에 부딪힌 상황 즉, 승패가 난 상황에 대해 네트워크를 통한 원격 점수 업데이트(48라인)가 필요하기 때문에 그 부분에 대한 처리가 적용되었다. collide\_wall 를 자세히 살펴보면, 기존에는 승점 발생시 객체 자신이 점수를 직접 업데이트 했었는데 이제는 승점을 획득한 편이 누구인지에 대한 값(b1 또는 b2)만을 리턴할 뿐(16, 23라인), 승점의 업데이트는 리턴값을 받은 쪽에서 업데이트 하도록 위임한다(45~47라인). 그리고, collide\_wall 멤버함수에도 **sound\_only** 라는 파마미터가 추가되었는데, 이전 반사판에서의 경우와 동일하게 호스트가 아닌 일반유저의 경우 공의 반사에 대해서 단지 효과음 처리만 하려는 의도이다. 다음으로는 살펴보는 것을 미뤄둔 수신 메시지의 처리에 대한 부분을 살펴보도록 하자. 수신 메시지의 통합적인 처리를 위해 handle\_recv\_messages 함수에 안에서 처리한다. Copy def handle_recv_messages(): # 전체 메시지 수신 net.process_recv() # 특정 메시지 구분 game_start = net.get_msg(peer_id, 'game_start') bar_pos = net.get_msg(peer_id, 'bar_pos', clear=False) ball_pos = net.get_msg("Player1", 'ball_pos', clear=False) who_win = net.get_msg("Player1", 'score') # 게임 시작 if game_start: score.reset() ball.reset() # 공 if ball_pos: ball.pos_update(ball_pos['center'], ball_pos['vel']) # 바 if bar_pos: if is_host: bar2.pos_update(bar_pos) else: # Plyer2 bar1.pos_update(bar_pos) # 점수 if who_win: score.add(who_win) def update(): global num_peers # 네트워크 수신 메시지 처리 handle_recv_messages() ... 우리가 송신 메시지 처리에서 상대에게 보내려는 메시지들을 버퍼라는 임시공간에 모았다가 주기적으로 한꺼번에 보내는 식으로 적용했던 것을 기억할 것이다. 수신하는 쪽에 처리에서도 이와 유사하게 메시지 수신 버퍼라는게 존재하여 네트워크를 통해서 계속 유입되는 메시지들을 수신 버퍼라는 공간에 계속 모아두었다가 주기적으로 한꺼번에 처리하는 방식을 사용하며, 따라서, 그러한 식의 처리를 위해서 NetNode 객체 안에 **process\_recv** 멤버함수를 주기적으로 호출할 필요가 있다(3라인). 먼저 우리는 상대방에서 수신된 메시지들을 각각을 구분하는게 필요하고, 이후 그 구분된 메시지에 맞게 처리를 하면된다. 메시지의 구분은 NetNode 객체의 **get\_msg** 멤버함수를 통해 할 수 있다. 이 함수는 3가지 파라미터 값을 갖는다. 첫번째로 이 (정보성)메시지의 수신자의 그룹방에서의 id값으로 여기서는 **peer\_id**를 인자로 넘겼는데, 마찬가지로 이전 장의 connect\_server 함수에서 이 값이 사전 설정되어 있다. 즉, 호스트가 된 게임유저는 상대인 일반유저에게/일반유저인 게임유저는 호스트에게 메시지를 보내게 되는 것이다. 두번째 값은 메시지 수신자가 자신에게 도착하면 여러 메시지들 중에서 특정 메시지를 구분할 수 있게 하려는 목적의 메시지 식별자(일종의 id)이다. 세번째 값으로 **clear**라는 값이 있는데, 이 값은 필요하면 사용할 수 있고 필요 없다면 구지 값을 넘길 필요는 없다. 이 파마미터의 목적은 수신 버퍼에서 메시지를 읽어드린 후 그 메시지를 수신 버퍼에서 삭제할 것인지의 여부를 결정한다. 이런 사유가 왜 필요할까를 고민해보자. 해당 값은 네트워크라는 특성에서 오는 예외적인 필요가 있어서 존재하는데 네트워크는 그 특성상 네트워크 상에 오가는 데이터들 사이에 [지연(delay)arrow-up-right](https://en.wikipedia.org/wiki/Network_delay) 이 발생할 수 있음을 염두해야 한다. 예를 들어, 상대방의 실시간 위치표시 등과 같이 정보성 메시지가 늦게 도착할 경우를 생각해보자. 이 때, 내 화면에서 상대방을 멈춰있는 것으로 보이거나, 움직임에 끊김현상이 발생하여 매우 부자연스러게 보일 수 있다. 이런 경우를 대비해 clear값을 False로 설정하면, 해당 메시지 id에 해당하는 새로운 메시지가 도착하기 전까지는 기존 메시지를 삭제하지 않고 유지하는 방법을 통해, 화면 UI적인 멈춤현상을 억제하는 것이다. 여기서는 공의 움직임(ball\_pos), 바의 움직임(bar\_pos)의 두 개의 실시간 움직임처리에 대해서 clear=Flase를 설정했다. 추가로 clear 값에 더 자세히 알고싶을 경우, [라이브러리 저자가 직접 설명한 내용arrow-up-right](https://github.com/QuirkyCort/nethelper/tree/main/examples#remember_commandspy) 을 참고할 수 있다. 이제 남은 것은 각 수신 메시지의 상세처리 부분인 11~29라인은 직관적으로 이해가 가능한 내용이라 추가적인 부연설명을 하지 않아도 여러분들이 충분히 이해할 수 있을 것으로 기대한다. 이로써 pong게임을 네트워크 게임으로 만드는 모든 과정은 끝이 났다. 이처럼 네트워크 게임은 게임자체에 대한 내용과 더불어 네트워크라는 추가적인 코딩요소가 더해지기 때문에 다소 난이도가 있는 편이다. 그러나, 이제 네트워크라고 하는 부분은 우리의 일상에서 땔 수 없는 부분이고, 따라서, 모든 프로그래밍 요소에서 항상 등장하기 때문에 피할 수 있는 것이 아니고, 반드시 지식적 이해가 있어야 하는 부분으로 받아드리면 좋겠다. 이제 마지막으로 해당 게임의 전체 소스코드를 살펴보는 것으로 마무리 하겠다. pong\_net.py Copy import random import math from nethelper import NetNode # 게임화면 TITLE = 'pong' WIDTH = 800 HEIGHT = 600 # 반사판 BAR_H = 100 BAR_W = 15 GAP_FROM_SCR = 20 # 볼 BALL_RADIUS = 10 VELOCITY = 5 SPEED_UP = 1.08 # 점수 FINAL_SCORE = 11 # 네트워크 SERVER_IP = 'localhost' # 릴레이서버 IP주소 is_host = False # 그룹의 host역할 여부 peer_id = '' # 통신 상대방의 id MAX_PEERS = 2 # 최대 게임가능 인원 num_peers = 0 # 현재 게임참여 인원 my_bar = '' class Score(): ''' 화면에 점수와 승패의 UI 표현 ''' def __init__(self, final_score): self._final_score = final_score self._b1_score = -1 self._b2_score = -1 def _gameover_draw(self): winner = '' if self._b1_score == self._final_score: winner = 'Player1' elif self._b2_score == self._final_score: winner = 'Player2' if winner: screen.draw.text(winner + ' Win!!', (WIDTH/3, HEIGHT/2 - 50), color='blue', fontsize=70) screen.draw.text('Press Space to play again', (WIDTH/4, HEIGHT/2 + 50), color='skyblue', fontsize=50) def is_game_over(self): if (self._b1_score == self._final_score or self._b2_score == self._final_score) \ or (self._b1_score == -1 and self._b2_score == -1): return True else: return False def add(self, who): if who == 'b1': self._b1_score += 1 elif who == 'b2': self._b2_score += 1 def reset(self): self._b1_score = 0 self._b2_score = 0 def reinit(self): self._b1_score = -1 self._b2_score = -1 def draw(self): screen.draw.text(str(self._b1_score), (WIDTH/4, GAP_FROM_SCR), color='yellow', fontsize=60) screen.draw.text(str(self._b2_score), ((WIDTH/4)*3, GAP_FROM_SCR), color='yellow', fontsize=60) if self.is_game_over(): self._gameover_draw() class Ball(Rect): ''' 공을 동작(위치, 움직임)과 충돌검사 ''' def __init__(self, start_pos, velocity, score): super().__init__(0, 0, BALL_RADIUS * math.sqrt(2), BALL_RADIUS * math.sqrt(2)) self.center = start_pos self._velocity = velocity self.vx = self.vy = self._velocity self._score = score def pos_update(self, center, vel): self.centerx, self.centery = center self.vx, self.vy = vel def reset(self): if self._score.is_game_over(): self.vx = self.vy = 0 else: self.vx = self.vy = self._velocity self.vx *= random.choice([-1, 1]) # 중심선에서 좌우로 랜덤하게 던지기 self.center = (WIDTH/2 , random.randint(BALL_RADIUS*2, \ HEIGHT - BALL_RADIUS*2)) def move(self): # vx와 vy만큼 속도로 공을 이동시키기 self.move_ip(self.vx, self.vy) def collide_wall(self, sound_only=False): # 위쪽 또는 아래쪽 벽 if self.top < 0 or self.bottom > HEIGHT: if not sound_only: self.vy = -self.vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 왼쪽 벽 if self.left < 0: if not sound_only: self.reset() sounds.die.play() return 'b2' # 오른쪽 벽 if self.right > WIDTH: if not sound_only: self.reset() sounds.die.play() return 'b1' return '' def draw(self): screen.draw.filled_circle(self.center, BALL_RADIUS, 'white') class Bar(Rect): def __init__(self, x, y, ball): super().__init__(x, y, BAR_W, BAR_H) self.ball = ball def up(self): if self.y > 0: self.y -= 7 def down(self): if self.y + self.height < HEIGHT: self.y += 7 def pos_update(self, center): self.centerx, self.centery = center def collide_ball(self, sound_only=False): if self.colliderect(self.ball): if not sound_only: # # 10픽셀 앞으로 먼저 튀어오르기 if self.ball.vx < 0: # bar1 이면 self.ball.x += 10 else: # bar2 이면 self.ball.x -= 10 self.ball.vx = -self.ball.vx * SPEED_UP # 속도의 x축 방향을 반대로하기 ''' 공이 윗측 진입하면서 반사판 윗측에 부딪힐 때 또는 공이 아래측 진입하면서 반사판 아래측에 부딪힐 때는 진입방향 그대로 반사 ''' if (self.ball.vy > 0 and self.ball.centery < self.centery) or \ (self.ball.vy < 0 and self.ball.centery > self.centery): self.ball.vy = -self.ball.vy * SPEED_UP sounds.bar.play() def draw(self): screen.draw.filled_rect(self, 'white') # 주인공 객체들 생성 score = Score(FINAL_SCORE) ball = Ball((WIDTH/2, HEIGHT/2), VELOCITY, score) bar1 = Bar(GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, ball) bar2 = Bar(WIDTH - BAR_W - GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, ball) bars = [bar1, bar2] net = NetNode() def connect_server(): global is_host, peer_id, my_bar # 맨 처음 연결된 사람이 host (Player1) 역할 if net.connect(SERVER_IP, "Player1", "pong_game", wait=True): is_host = True peer_id = "Player2" my_bar = bar1 print("Connected as host (Player 1)") else: # 이미 호스트가 존재하면, peer (Player2) 역할 if net.connect(SERVER_IP, "Player2", "pong_game", wait=True): is_host = False peer_id = "Player1" my_bar = bar2 print("Connected as peer (Player 2)") else: print("Failed to connect. The group is full.") exit() # 게임시작 전 릴레이서버와 연결설정 connect_server() def draw(): screen.clear() screen.draw.line((WIDTH/2, GAP_FROM_SCR), (WIDTH/2, HEIGHT - GAP_FROM_SCR), \ color='grey') if not score.is_game_over(): ball.draw() bar1.draw() bar2.draw() score.draw() if num_peers < MAX_PEERS: screen.draw.text('Waiting for a peer connection...', (WIDTH/6, HEIGHT/2), color='blue', fontsize=50) def handle_recv_messages(): # 전체 메시지 수신 net.process_recv() # 특정 메시지 구분 game_start = net.get_msg(peer_id, 'game_start') bar_pos = net.get_msg(peer_id, 'bar_pos', clear=False) ball_pos = net.get_msg("Player1", 'ball_pos', clear=False) who_win = net.get_msg("Player1", 'score') # 게임 시작 if game_start: score.reset() ball.reset() # 공 if ball_pos: ball.pos_update(ball_pos['center'], ball_pos['vel']) # 바 if bar_pos: if is_host: bar2.pos_update(bar_pos) else: # Plyer2 bar1.pos_update(bar_pos) # 점수 if who_win: score.add(who_win) def update(): global num_peers # 네트워크 수신 메시지 처리 handle_recv_messages() # 반사판 if keyboard.a or keyboard.up: my_bar.up() if keyboard.z or keyboard.down: my_bar.down() net.send_msg(peer_id, 'bar_pos', my_bar.center) for bar in bars: if is_host: bar.collide_ball() else: # Plyer2 bar.collide_ball(sound_only=True) # 게임시작 조건 if score.is_game_over(): # 게임 시작은 누구나 먼저 시작가능 if num_peers == MAX_PEERS and keyboard.space: score.reset() ball.reset() net.send_msg(peer_id, 'game_start', True) else: # 공 if is_host: ball.move() net.send_msg('Player2', 'ball_pos', {'center':ball.center, 'vel':(ball.vx, ball.vy)}) who_win = ball.collide_wall() if who_win: # 점수 score.add(who_win) net.send_msg('Player2', 'score', who_win) ball.reset() else: # Plyer2 ball.move() ball.collide_wall(sound_only=True) # 상대 연결 끓김 num_peers = len(net.get_peers()) if num_peers < MAX_PEERS: if is_host: score.reinit() ball.reset() else: # Plyer2 print("The host is disconnected.") exit() # 호스트가 종료되면 방 폭파 # 메시지 전송 net.process_send() 참고로 해당 게임을 이제 로컬호스트 내에서 플레이 아는게 아닌, 실제 원격에 떨어진 유저와 게임을 하길 원할 것이다. 이 경우, 릴레이 서버 프로그램을 로컬 네트워크 (LAN) 내에 또는 전 세계 모든 원격 사용자가 접속할 수 있는 [클라우드(cloud)arrow-up-right](https://en.wikipedia.org/wiki/Cloud_computing) 에 설치할 필요가 있을 수 있고, 또 해당 서버 프로그램이 설치되는 운영체제(OS: Operating System)에 따라 [방화벽(firewall)arrow-up-right](https://en.wikipedia.org/wiki/Firewall_(computing)) 설정을 조절해 실행되고 있는 릴레이 서버 프로그램으로의 원할한 원격접속이 이뤄지도록 하는 네트워크 설정을 조절할 필요한다. 이에 대한 자세한 설명은 [라이브러리의 서버설정 메뉴얼arrow-up-right](https://github.com/QuirkyCort/nethelper?tab=readme-ov-file#computer-within-lan) 을 통해 상세 확인할 수 있다. 만약, 앞서 언급한 설치와 설정을 모두 완료해 릴레이 서버 프로그램이 네트워크 상에 정상 실행되고 있다면, 우리의 게임 프로그램이 이 서버에 접속해야 하는데, 이 때는 먼저 [10.2절](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.2_server_connection) 에서 언급했던 것처럼 단 한 줄의 수정이 필요한데, 릴레이 서버가 운용 중인 컴퓨터의 인터넷 상의 고유한 IP 주소를 SERVER\_IP 값에 설정해 주어야 한다. [hashtag](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.4_dev_net_2#undefined) 맺음말 ----------------------------------------------------------------------------------------------------- 이로써 귀하는 이 책의 모든 여정을 맞췄다. 먼저, 이 여정을 마친 여러분의 수고에 진심으로 박수를 보내며, 이 책은 [이전 서](https://jjlee.gitbook.io/pygame_zero-entry_basic/) 에 비해서는 확실히 난이도가 있었지만, 그런 다소 고통스런(?) 과정을 통과하면서 여러분은 분명히 전보다 많이 성장했음을 믿어 의심지 않는다. 다행히도 여러분의 여정은 혼자가 아니라는 것을 기억하고, 함께 할 수 있는 누군가가 있다는 것으로 힘을 낼 수 있을 것이다. 저자는 앞으로의 여러분의 여정에도 함께 동반자가 되어 게쏙 여러분을 지원하고 도울 것을 약속드리며, 다음의 또다른 책에서 여러분을 다시 만나길 고대하겠다. [Previous10.3 네트워크 게임으로 만들기 1chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.3_dev_net_1) [Next부록chevron-right](https://jjlee.gitbook.io/python-pygame_zero/appendix) Last updated 9 months ago --- # 7. 트윈비(TwinBee) 슈팅게임 만들기 | 파이썬-파이게임 제로 (순한맛) 이 게임은 1985년에 만들어진 트윈비(TwinBee)라는 고전 슈팅게임이다. 이 게임의 실제 동작화면을 보면 다음과 같다. 완전 대중적으로 유명한 게임은 아닐지 몰라도 수직스크롤(vertical-scrolling) 방식의 슈팅게임으로서 상당히 성공한 게임으로 알려져 있다. 우리가 이 게임을 완전히 클론해 거의 유사하게 만들어 보려면, 코딩량의 상당히 증가하여 자칫 여러분의 코딩재미를 반감(?)할 수 있단 염려가 있어, 여러 스타일의 게임을 만들어 보면서 코딩을 재미있게 배워보려는 본래 목적에서 벗어나지 않게 적당한 수준에서 수위를 조절할 예정이다. 그러나, 또 걔 중에는 의지가 있고, 욕심이 있는 독자도 있을 것을 감안하여 추가적인 코딩에 대해서는 여러분에게 과제로 남기며, 또 그 부분을 스스로 독학해 볼 수 있는 참고 웹사이트를 공유하는 것으로 최종 마무리 하려고 한다. 자, 그럼 함께 게임 제작을 시작해 보자. [Previous6.5 (보너스) 오리지널 게임처럼 만들어 보기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/breakout/6.5_make_original) [Next7.1 스크롤 배경객체 만들기, 배경음, 배우(적, 주인공) 등장시키기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.1_scroll_backgrounds) Last updated 6 days ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 7.1 스크롤 배경객체 만들기, 배경음, 배우(적, 주인공) 등장시키기 | 파이썬-파이게임 제로 (순한맛) 바로 아래 화면의 우리가 만들게 될 게임의 최종 결과물을 먼저 보고나면 코드 이해가 훨씬 쉬울 것이다. 다만, 이번 과의 맨 서두에서 언급했듯이 난이도 조절을 위해 기능동작 전체 다를 구현하지 않을 예정이고, 아래의 최종결과물 전체 다를 구현해보고자 하는 분들을 위해선 따로 공부할 수 있는 참조링크를 전달할 예정이다. 본격적인 게임제작에 앞서 이번 게임제작에 필요한 이미지, 사운드 등의 리소스들은 [이곳arrow-up-right](https://github.com/roboticsware/python-pygame_zero-samples/tree/main/breakout/images) 에서 다운로드 할 수 있으며 게임에 필요한 이미지 파일들은 코드에서 사용한 이름 그대로 imgaes 폴더에 이미 저장되어 있고, 효과음은 sounds에 배경음은 music 폴더에 저장되어 있다고 간주하고 시작하겠다. 또는 [커스텀 뮤 에디터arrow-up-right](https://github.com/roboticsware/mu/releases) 를 사용 중이라면 뮤 에디터의 작업 디렉토리인 (사용자계정)\\mu\_code\\examples\\pygame\_zero 안에서 복사해 올 수 있다. 이번 게임의 무대가 기존과 다른 점은 가만히 멈춰 있는 배경이 아니라, 움직임이 있는 배경(객체)라는 점이다. 만약, 귀하가 엔트리 블록코딩에 익숙하다면 배경이 객체가 될 수 있고, 객체처럼 다뤄 코딩할 수 있는 것이 전혀 낯설지 않을 것이다. 이전 예제들에서 배경을 단순 이미지처럼 사용할 때의 코딩에 대해선 다음과 같이 사용할 수 있다는 것을 우리가 익히 알고 있다. 그러나, 지금부터 우리가 하려는 것은 배경을 객체로 만들어, 무한반복 수직 스크롤하는 배경을 구현하려고 한다. 일단 스크롤 화면을 만들려면 물리적으로 나눠져 있지만 마치 하나의 파노라마 이미지로 볼 수 있는 최소한 2장 이상의 이미지가 필요하다. 여기서는 background 1, background2 로 이름 붙혀진 2장의 이미지 각각을 배우객체로 만들고, 첫번째 배경객체의 Anchor(중심좌표)는 전체화면의 가로, 세로의 각각 절반(WIDTH / 2, HEIGHT / 2) 이여야만 화면크기 안에 딱 맞게 그릴 수 있다. 그 배경객체와 상단에 이어져 존재하는 연이은 배경객체의 중심좌표는 최초 우리 눈에 보이지 않지만, 화면상단쪽 넘어에 위치시키기 위해 화면의 세로길이만큼 뺀((HEIGHT / 2) - HEIGHT) 위치의 Anchor 값(값으로는 -300)을 적용하게 된다. 해당 설명을 이해를 돕기 위해 그림으로 표현하면 다음과 같다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252Fu3CJoxoADmpE2VipUgC4%252Fimage.png%3Falt%3Dmedia%26token%3D0b4033e6-2720-4824-bf3c-6022dbe55f2a&width=768&dpr=3&quality=100&sign=2cb957c6&sv=2) 배경을 객체화 하고, **draw** 콜백함수에서 화면에 그리기까지 했고, 이후 이 화면에 스크롤되어 무한반복으로 로테이션(rotation) 시키기 위한 코드, 즉 객체에 움직임을 만드는 코드이기 때문에 **update** 콜백함수에서 코딩되어야 한다. 화면이 이동속도는 3픽셀씩 아래로 이동시키고, 각 배경객체는 이동하다가 현재 보이는 화면영역 밖을 빠져나가게 되면, 화면상단에 보이지 않는 영역으로 즉시 이동시키는 방법으로 무한 로테이션을 시키도록 하면 된다. _이처럼 알고리즘 자체는 이미 블록코딩에서 많이 사용해 봤던 것과 전혀 다르지 않다._ 지금까지 언급된 것을 이를 실제 코드로 표현하면 다음과 같다. 이제 이번 절의 나머지는 게임무대의 주인공들(적 비행기, 내 비행기)을 등장시킬 차례이고, 또 궁극적으로 이들을 제어하는데 **필요한 기능 코드들에 대해 미리 고려해 보고, 이 기능들을 의미있는 코드덩어리인 함수로 만드는 함수화(functionalize) 작업을 잘 시키는게 좋은 개발자의 자질**인 것이다. 이를 고민할 때, 다음과 같이 필요한 함수들의 이름짓기(naming)을 포함한 목록 미리 작성해 나열해 봄으로써 전체적인 구조적 윤곽을 잡은데 도움을 받을 수 있다. **함수화 하는 과정에서 우리가 젤 먼저 고려해야 할 우선 순위는 함수가 탄생하게 된 제일 큰 목적이 그러하듯이 중복되는 코드를 줄이기 위한 목적의 함수에 대해서 젤 우선순위로 생각할 수 있다.** _그런데, 우리가 만들고자 하는 최종 프로그램이 어떠한 것이냐에 따라 생각보다 중복코드가 없는 경우도 있을 수 있다._ 이번 게임에 경우가 그러한 편인데, 예를들어 1인용 슈팅게임이다 보니, 키보드 조작을 통한 움직임의 경우, 주인공 비행기만 필요하지 다른 여러 객체에서 공통사용은 없다. 또 총알을 쏘는 방식도 주인공이 쏘는 총알의 종류와 쏘는 방법과 적 비행기가 쏘는 총알의 종류와 방법에 있어 공통된 부분의 거의 없어 공통함수로 만들어 쓰는 유익도 없다. 따라서, 아래의 코드에서 확인해보면 _우리 프로그램에서의 함수의 사용목적의 중복코드를 줄이기 위한 것이라기보다 코드 가독성을 높이기 위한 의미단위로 함수화_ 했다는 것을 알 수 있다. 총 5개 (**move\_player**, **shoot\_bullets**, **create\_enemies**, **check\_collision**) 의 빈 함수로 만들면서, 각 함수 안에 **pass** 라고 임시로 남겼는데, 이 의미는 당장은 실행 것이 아무 것도 없다는 것으로, 세부 구현을 이후로 미루기 위한 임시적인 표현구문이다. 여기서 왜 함수를 5개로 구분해 만들었느냐, 4 또는 6개는 안되느냐 등의 질문이 생길 수 있는데, 초보자 입장에서는 미리 어떤 함수가 필요하겠고, 몇 부분의 함수로 나눌지가 처음부터 잘 떠오르지 않는게 정상이고, 사실상 정확하게 이렇게 나눠져야 하는 100% 정답도 없는 것이 코딩이란 영역이다. **코딩은 본질상 해법이 여러 개일 수 있는데 그 중에서 가장 효율적인 방법을 찾아내는 과정이기 때문이다.** 따라서, 이런 부분이 원활하게 되기 위해선 경험이 많이 쌓여야 함을 전제할 필요가 있다. 처음부터 너무 부담을 갖지 않는게 중요하고, 스스로 고민을 많이 해보고, 또 함수를 한번에 완벽하게 만들 수 있는게 아니고, 이렇게 저렇게 만들어 보고 반복적으로 고치보면서 점진적으로 발전해나가는게 자연스러운 과정이다. (코딩에서는 이러한 과정에 [**리펙토링**arrow-up-right](https://ko.wikipedia.org/wiki/%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81) 이란 용어를 사용한다.) 이처럼 **게임 프로그램의 경우는 사실상 우리가** [**이전 서**](https://jjlee.gitbook.io/entry-python/) **에서 배웠던 함수를 통한** [**절차지향형 프로그래밍 페러다임**](https://jjlee.gitbook.io/entry-python/paradigm/4.2-procedural) **보다는** [**객체지향형 프로그래밍 패러다임**](https://jjlee.gitbook.io/entry-python/paradigm/4.4-object-oriented) **에 더 적합하고**, 그러나 우리는 지금까지 남이 만들어 놓은 클래스들을 통한 준비된 객체활용만 해봤지, 내 필요에 의한 나만의 객체를 만들어 활용하는 방법에 대해서는 아직 배우지 못했다. 사실은 필자가 여러분들이 사용자 정의 함수(User defined function)에 생성과 사용에 어느 정도 익숙해지길 기다린 면이 있고, 다음의 8과 예제부터는 우리도 우리만의 클래스와 객체를 통해 코딩하는 법을 배우도록 하겠다. 마지막으로 21번 라인의 게임 내내 배경음을 재생시키는 방법에 대해 언급하고 이번 장을 마무리 하겠다. 효과음과 배경음의 차이는 효과음은 짧은 단회성이고, 배경음은 길고 무한반복적인 특징이 있다. 효과음을 재생시키는 방법은 익히 알고 있고, 배경음 사실은 효과음처럼 재생할 수 있고, 그 음을 무한반복을 시키고자 한다면, 인자값으로 -1을 넘기면 된다. 그러나, 배경음에 대해 [라이브러리 차원에서 전용 APIarrow-up-right](https://pygame-zero.readthedocs.io/en/stable/builtins.html#music) 를 제공하고 있기 때문에 이를 사용하는게 젤 목적에 부합한다고 할 수 있겠다. 배경음의 음악 파일종류는 mp3와 ogg 파일을 사용할 수 있고, 재생을 위한 함수는 라이브러리가 제공하는 **music.play** 를 사용하면 된다. [Previous7\. 트윈비(TwinBee) 슈팅게임 만들기chevron-left](https://jjlee.gitbook.io/python-pygame_zero/twinbee) [Next7.2 배우들의 움직임과 총알 공격 구현하기chevron-right](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.2_bullet_attack) Last updated 3 days ago sun-brightdesktopmoon Copy def draw(): screen.blit('background', (0, 0)) Copy WIDTH = 800 HEIGHT = 600 backgrounds = [] background1 = Actor("background1", (400, 300)) backgrounds.append(background1) background2 = Actor("background2", (400, -300)) backgrounds.append(background2) def draw(): for background in backgrounds: background.draw() def update(): for background in backgrounds: background.y += 3 if background.top > HEIGHT: background.y = -300 Copy WIDTH = 800 HEIGHT = 600 backgrounds = [] background1 = Actor("background1", (400, 300)) backgrounds.append(background1) background2 = Actor("background2", (400, -300)) backgrounds.append(background2) player = Actor("player", (400, 500)) MAX_BULLETS = 3 bullets = [] enemies = [] enemy_bullets = [] explosions = [] score = 0 game_over = False music.play(main_theme) def move_player(): pass def shoot_bullets(): pass def create_enemies(): pass def check_collision(): pass def draw_text(): pass def draw(): for background in backgrounds: # 배경 그리기 background.draw() player.draw() # 주인공 비행기 그리기 for enemy in enemies: # 적 비행기 그리기 enemy.draw() for bullet in bullets: # 주인공 총알 그리기 bullet.draw() for explosion in explosions: # 적 비행기 폭발 그리기 explosion.draw() for bullet in enemy_bullets: # 적 총알 그리기 bullet.draw() draw_text() # 점수와 게임종료 텍스트 그리기 def update(): for background in backgrounds: background.y += 3 if background.top > HEIGHT: background.y = -300 move_player() # 주인공 비행기의 키보드 조작 shoot_bullets() # 적을 향해 총쏘기 create_enemies() # 총알 쏘며 움직이는 적 비행기 생성 check_collision() # 충돌 관련한 모든 처리 Copy sounds.main_theme.play(-1) sun-brightdesktopmoon --- # 8. 퐁(Pong) 게임 만들기 | 파이썬-파이게임 제로 (순한맛) 이번 장에서 우리가 같이 만들어 보게 될 게임은 1972년에 창업한 세계 최초의 비디오게임 회사이면서 동시에 게임이란 산업을 최초의 만든 회사라고 일컬어 지는 역사적인 회사, [**아타리(Atari)**arrow-up-right](https://namu.wiki/w/%EC%95%84%ED%83%80%EB%A6%AC) 에서 1972년 창업당시에 출시한 아주 고전적인 게임이면서 동시에 상업적으로 성공한 최초의 아케이드 비디오 게임이 되겠다. **아케이드 비디오 게임**이라고 하는 건 요즘은 PC 방으로 대체되어 보기 힘들지만, 과거에 동네마다 오락실이라고 해서, 동전넣고 게임 한판씩 하던 시절에 그 비디오 게임기에 탑재되는 게임을 말한다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FEaIB0P1KjWNApSO0gVbZ%252Fimage.png%3Falt%3Dmedia%26token%3D7963bce4-0a60-40d5-bbe6-d1d32016460d&width=768&dpr=3&quality=100&sign=5bf196e7&sv=2) 출처: 위키피디아 우리는 발매당시 최초의 버전의 [**퐁(Pong)** arrow-up-right](https://namu.wiki/w/%ED%90%81) 게임(아래 유투브 실행동영상 참고) 의 클론(clone)을 같이 만들어 보겠다. 참고로 게임이름을 퐁이라 명명한 이유는 핑퐁(Ping-pong)이라 불리는 탁구를 모사한 게임이기 때문이다. 여담이지만, [6장](https://jjlee.gitbook.io/python-pygame_zero/breakout) 에서 만들어본 블록격파(Breakout) 이란 게임은 퐁 게임이 2인으로만 게임가능한 단점을 극복하고자 1인용으로 만든 유사게임이라고 한다. [Previous7.4 그 밖에 도전과제chevron-left](https://jjlee.gitbook.io/python-pygame_zero/twinbee/7.4_additional_challenge) [Next8.1 절차지향으로 개발하기 1chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong/8.1_procedural_dev1) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 9. 배틀 시티(Battle city) 게임 만들기 | 파이썬-파이게임 제로 (순한맛) 이번 장에서는 어린 시절(?) 오락실에서 많이 즐겼던 배틀 시티(Battale city) 게임의 클론(Clone)을 함께 만들어보자. 아직 배틀시티 게임이 어떤 게임을 잘 모르는 젊은(?) 분들을 위해 유투브에 올라온 해당 게임의 친절한 리뷰를 살펴보자. 복잡한 게임은 아니고, 그냥 단순히 컴퓨터와 포를 쏘며 대전하는 대전류의 게임정도로 이해하면 되겠다. 다만, 본 과정은 순한맛 버전으로 원본 게임을 완전히 똑같이 만들기에는 품이 많이 들어 코딩량이 상당히 많아지기 때문에 가능한한 적은 코드량으로 핵심기능을 모사하는 정도만 해볼 예정이긴 하다. 그럼, 본격적으로 다음 절부터 게임만들기를 시작해보자! [Previous8.8 (보너스) 파이게임제로 예제버전 2chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong/8.8_bonus_dev2) [Next9.1 절차지향형으로 개발하기 1chevron-right](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.1_procedural_dev1) Last updated 6 days ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 8.6 객체지향으로 개발하기 2 | 파이썬-파이게임 제로 (순한맛) 이제 나머지 공(Ball) 객체와 반사판(Bar) 객체를 살펴보자. 먼저, Ball 객체이다. Rect 객체를 상속했는데, Rect의 실체는 파이게임제로 라이브러리의 모체인 [파이게임 안에 존재하는 객체arrow-up-right](https://www.pygame.org/docs/ref/rect.html) 이다. 주 용도는 우리가 Actor객체에서 다음과 같은 내장된 위치정보를 활용할 수 있음을 배웠는데, 사실은 이 위치정보의 본체는 Rect객체에 있는 것이고, Actor객체가 이 Rect객체를 상속받아 사용하고 있기 때문에 Actor객체도 저 위치속성값들이 사실은 원래 부모의 속성값들임에도 불구하고 마치 본래부터 자기에게 있었던 것마냥 사용하고 있는 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FPoQQkAl6bTWt2P79cAZj%252Fimage.png%3Falt%3Dmedia%26token%3D12b00a46-4403-454f-bb24-3210e338995b&width=768&dpr=3&quality=100&sign=c9d75eaa&sv=2) 🔢 7번라인에서 Rect 부모객체의 생성시 넘겨야 할 4개의 초기값(x좌표, y좌표, 넓이, 높이)을 넘기면서, 부모객체를 초기화하고 있다. 여기서, (x, y)좌표를 (0, 0)으로 넘겼는데, 사실은 어떤 값을 넘기더라도 크게 의미가 없는 것은 그다음 8번 라인에서 곧바로 이 객체의 중심좌표값을 start\_pos 파라미터값으로 대치하기 때문이다. 공 객체를 생성할 때 넘겨져야 할 파라미터값들 중 하나로 점수(**score)**객체가 넘겨져야 하는데, 이는 공 객체 안에서 점수객체와 의존성이 생기는 것으로 공 객체가 점수객체에게 어떤 행동을 요청하거나 또는 그가 외부에서 값을 바꿀 수 있도록 허락한 속성값들을 직접 바꾸는 등의 조작을 할 수 있음을 의미한다. 여기서는 전자의 경우로 **collide\_wall** 함수에서 공이 좌우(화면)벽과의 충돌하는 상황은 곧 매 경기의 승패가 나는 상황이고, 이때 승리쪽에 점수값을 add **공개 메소드**를 호출하여 증가시키게 된다. circle-info **공개 함수 또는 공개 메소드(public method)란?** 어떤 객체의 멤버함수 중 다른 객체와의 상호작용을 목적으로 다른 객체가 호출하여 특정 행동을 유도할 수 있도록 외부에 공개된 멤버함수를 지칭하는 말. 그 반대말은 비공개(private) 메소드라고 한다. 🔢 26번 라인의 **move** 함수 안에 사용한 **move\_ip** 함수 호출방법에 대해 유심히 살펴보면 **self** 를 통해 호출하고 있는데, 이 역시도 상속에 의한 결과로 move\_ip 함수가 Ball 객체 안에 정의된 멤버함수가 아님에도 불구하고, 마치 자신에게 존재하는 것처럼 호출하고 있다. 즉, move\_ip 함수는 결국 부모인 Rect객체 안에 존재하고 있으며, 자녀인 공 객체가 자신의 일부처럼 사용할 수 있는 것이다. 다음으로 반사판 객체를 살펴보자. 반사판 객체도 Rect객체를 부모로부터 상속받았다. 멤버함수들의 구성을 천천히 살펴보면 객체를 상하로 움직이는 행동을 요청할 수 있는 함수들을 별도로 두고 있으며, 이 행동시 제약으로는 함수 내부적으로 반사판의 화면 밖을 넘어가지 않도록 하는 것을 보장하며 움직이고 있다. 역시나 이 객체도 공(Ball) 객체라는 외부 객체를 **ball** 이라는 파마리터값으로 전달받으면서, 공 객체와 의존성을 갖게 되는데 이의 목적도 이전의 공 객체에서 점수객체에 의존하는 것과 유사한데 반사판이 공과의 충돌검사하면서 공이 반사되는 움직임을 만들기 위해 공의 진행방향을 반대쪽 방향으로 전환시키는 처리를 수행하기 위한 목적이다. 이로써 우리가 만들고자 했던 퐁 게임의 객체지향 버전의 최종판을 완성했다. 어떤가 남이 만들어준 객체만을 활용하는게 아니라 나만의 사용자 객체를 만들고 이들 간의 협력을 통한 객체지향 패러다임으로 개발하는 것이 신선한 경험이었을 것이라 믿는다. 다만, 아직 객체지향 복잡하기만 하고 큰 장점을 모르겠다라고 느낄 수 있을텐데 사실은 이 게임이 워낙 단순한 구조를 갖고 있어서 객체지향의 큰 장점이 드러날 순 없었다. 그럼에도 불구하고, 엔트리 블록코딩에서 객체개념으로 생각하고 코딩했을 때의 편한함(?)의 기억이 조금 복기되는 느낌을 갖게 되었다면, 그것만으로도 객체지향 패러다임에 성공적으로 입문했다고 할 수 있다. 아직 첫 술에 배부르긴 이루다. 다음 게임부터는 계속해서 객체지향으로 게임제작을 시도함으로써 점점더 객체지향 개념에 익숙해져 갈 것이다. 이제 마지막으로 지금까지 코딩내용의 전체 소스코들 첨부하는 것으로 이 과를 마무리 하겠다. 이미 게임초기에 언급했던 내용이지만, 모든 소스코드가 파일 1개로 만들어져 있음을 기억하자. [Previous8.5 객체지향으로 개발하기 1chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong/8.5_oop_dev1) [Next8.7 (보너스) 파이게임제로 예제버전 1chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong/8.7_bonus_dev1) Last updated 6 months ago sun-brightdesktopmoon Copy class Ball(Rect): ''' 공을 동작(위치, 움직임)과 충돌검사 ''' def __init__(self, start_pos, velocity, score): super().__init__(0, 0, BALL_RADIUS * math.sqrt(2), BALL_RADIUS * math.sqrt(2)) self.center = start_pos self._velocity = velocity self.vx = self.vy = self._velocity self._score = score def reset(self): if self._score.is_game_over(): self.vx = self.vy = 0 else: self.vx = self.vy = self._velocity self.vx *= random.choice([-1, 1]) # 중심선에서 좌우로 랜덤하게 던지기 self.center = (WIDTH/2 , random.randint(BALL_RADIUS*2, \ HEIGHT - BALL_RADIUS*2)) def move(self): # vx와 vy만큼 속도로 공을 이동시키기 self.move_ip(self.vx, self.vy) def collide_wall(self): # 위쪽 또는 아래쪽 벽 if self.top < 0 or self.bottom > HEIGHT: self.vy = -self.vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 왼쪽 벽 if self.left < 0: self._score.add('b2') += 1 sounds.die.play() self.reset() # 오른쪽 벽 if self.right > WIDTH: self._score.add('b1') += 1 sounds.die.play() self.reset() def draw(self): screen.draw.filled_circle(self.center, BALL_RADIUS, 'white') Copy class Bar(Rect): def __init__(self, x, y, ball): super().__init__(x, y, BAR_W, BAR_H) self.ball = ball def up(self): if self.y > 0: self.y -= 7 def down(self): if self.y + self.height < HEIGHT: self.y += 7 def collide_ball(self): if self.colliderect(self.ball): # 10픽셀 앞으로 먼저 튀어오르기 if self.ball.vx < 0: # bar1 이면 self.ball.x += 10 else: # bar2 이면 self.ball.x -= 10 self.ball.vx = -self.ball.vx * SPEED_UP # 속도의 x축 방향을 반대로하기 ''' 공이 윗측 진입하면서 반사판 윗측에 부딪힐 때 또는 공이 아래측 진입하면서 반사판 아래측에 부딪힐 때는 진입방향 그대로 반사 ''' if (self.ball.vy > 0 and self.ball.centery < self.centery) or \ (self.ball.vy < 0 and self.ball.centery > self.centery): self.ball.vy = -self.ball.vy * SPEED_UP sounds.bar.play() def draw(self): screen.draw.filled_rect(self, 'white') pong\_oop.py Copy import random import math # 게임화면 TITLE = 'pong' WIDTH = 800 HEIGHT = 600 # 반사판 BAR_H = 100 BAR_W = 15 GAP_FROM_SCR = 20 # 볼 BALL_RADIUS = 10 VELOCITY = 5 SPEED_UP = 1.08 # 점수 FINAL_SCORE = 11 class Score(): ''' 화면에 점수와 승패의 UI 표현 ''' def __init__(self, final_score): self._final_score = final_score self._b1_score = -1 self._b2_score = -1 def _gameover_draw(self): if self._b1_score == self._final_score: winner = 'Player1' elif self._b2_score == self._final_score: winner = 'Player2' if winner: screen.draw.text(winner + ' Win!!', (WIDTH/3, HEIGHT/2 - 50), color='blue', fontsize=70) screen.draw.text('Press Space to play again', (WIDTH/4, HEIGHT/2 + 50), color='skyblue', fontsize=50) def is_game_over(self): if (self._b1_score == self._final_score or self._b2_score == self._final_score) \ or (self._b1_score == -1 and self._b2_score == -1): return True else: return False def add(self, who): if who == 'b1': self._b1_score += 1 elif who == 'b2': self._b2_score += 1 def reset(self): self._b1_score = 0 self._b2_score = 0 def draw(self): screen.draw.text(str(self._b1_score), (WIDTH/4, GAP_FROM_SCR), color='yellow', fontsize=60) screen.draw.text(str(self._b2_score), ((WIDTH/4)*3, GAP_FROM_SCR), color='yellow', fontsize=60) if self.is_game_over(): self._gameover_draw() class Ball(Rect): ''' 공을 동작(위치, 움직임)과 충돌검사 ''' def __init__(self, start_pos, velocity, score): super().__init__(0, 0, BALL_RADIUS * math.sqrt(2), BALL_RADIUS * math.sqrt(2)) self.center = start_pos self._velocity = velocity self.vx = self.vy = self._velocity self._score = score def reset(self): if self._score.is_game_over(): self.vx = self.vy = 0 else: self.vx = self.vy = self._velocity self.vx *= random.choice([-1, 1]) # 중심선에서 좌우로 랜덤하게 던지기 self.center = (WIDTH/2 , random.randint(BALL_RADIUS*2, \ HEIGHT - BALL_RADIUS*2)) def move(self): # vx와 vy만큼 속도로 공을 이동시키기 self.move_ip(self.vx, self.vy) def collide_wall(self): # 위쪽 또는 아래쪽 벽 if self.top < 0 or self.bottom > HEIGHT: self.vy = -self.vy # 속도의 y축 방향을 반대로하기 sounds.wall.play() # 왼쪽 벽 if self.left < 0: self._score.add('b2') sounds.die.play() self.reset() # 오른쪽 벽 if self.right > WIDTH: self._score.add('b1') sounds.die.play() self.reset() def draw(self): screen.draw.filled_circle(self.center, BALL_RADIUS, 'white') class Bar(Rect): def __init__(self, x, y, ball): super().__init__(x, y, BAR_W, BAR_H) self.ball = ball def up(self): if self.y > 0: self.y -= 7 def down(self): if self.y + self.height < HEIGHT: self.y += 7 def collide_ball(self): if self.colliderect(self.ball): # 10픽셀 앞으로 먼저 튀어오르기 if self.ball.vx < 0: # bar1 이면 self.ball.x += 10 else: # bar2 이면 self.ball.x -= 10 self.ball.vx = -self.ball.vx * SPEED_UP # 속도의 x축 방향을 반대로하기 ''' 공이 윗측 진입하면서 반사판 윗측에 부딪힐 때 또는 공이 아래측 진입하면서 반사판 아래측에 부딪힐 때는 진입방향 그대로 반사 ''' if (self.ball.vy > 0 and self.ball.centery < self.centery) or \ (self.ball.vy < 0 and self.ball.centery > self.centery): self.ball.vy = -self.ball.vy * SPEED_UP sounds.bar.play() def draw(self): screen.draw.filled_rect(self, 'white') # 주인공 객체들 생성 score = Score(FINAL_SCORE) ball = Ball((WIDTH/2, HEIGHT/2), VELOCITY, score) bar1 = Bar(GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, ball) bar2 = Bar(WIDTH - BAR_W - GAP_FROM_SCR, HEIGHT/2 - BAR_H/2, ball) bars = [bar1, bar2] def draw(): screen.clear() screen.draw.line((WIDTH/2, GAP_FROM_SCR), (WIDTH/2, HEIGHT - GAP_FROM_SCR), \ color='grey') if not score.is_game_over(): ball.draw() bar1.draw() bar2.draw() score.draw() def update(): # 반사판 if keyboard.a: bar1.up() if keyboard.z: bar1.down() if keyboard.up: bar2.up() if keyboard.down: bar2.down() for bar in bars: bar.collide_ball() # 게임시작 조건 if score.is_game_over(): if keyboard.space: score.reset() ball.reset() else: # 공 ball.move() ball.collide_wall() sun-brightdesktopmoon --- # 9.2 절차지향형으로 개발하기 2 | 파이썬-파이게임 제로 (순한맛) 이번 절에서부터 우리의 3가지 사용자 함수의 구현체를 구체적으로 채워보는 시간이다. 먼저, **move\_player** 함수이다. 이미 우리에겐 너무 익숙한 함수이다. 다만, 주인공 탱크의 사용자 키보드 조작이 있는 경우와 적 탱의 컴퓨터에 의한 자동조작의 경우를 파라미터값(player)으로 구분해 적용하고 있다. 탱크의 이동에 있어서 제약이 따르는데 화면 밖으로 나갈 수 없고(35~39번 라인), 벽을 뚫고 지나갈 수 없다(30~32번 라인). 그러한 시도의 경우에는 현재 제자리(origin\_x, origin\_y)에 그대로 머물로록 처리하였다. Copy def move_player(player): # 탱크의 최초 위치를 저장해 두기 original_x = player.x original_y = player.y if player == tank: # 주인공 탱크 if keyboard.right: player.angle = 0 player.x += 2 elif keyboard.left: player.angle = 180 player.x -= 2 elif keyboard.up: player.angle = 90 player.y -= 2 elif keyboard.down: player.angle = 270 player.y += 2 else: # 적 탱크 if player.angle == 0: player.x += 2 elif player.angle == 90: player.y -= 2 elif player.angle == 180: player.x -= 2 elif player.angle == 270: player.y += 2 # 벽을 뚫고 나가지 못하고 자기 자리에 머물기 if player.collidelist(walls) != -1: player.x = original_x player.y = original_y # 게임화면 안에 가두기 if player.left < 0 or player.right > WIDTH \ or player.top < 0 or player.bottom > HEIGHT: player.x = original_x player.y = original_y 다음으로 **fire\_bullets** 함수이다. 역시나 워낙 간단한 구현체이고 이미 트윈비에서 만들어 본 것과 유사하므로 애해의 어려움은 없을 것이다. 다만, 차이는 함수에 파라미터값들이 존재한다는 것인데, 주인공 탱크와 적 탱크의 총알(대포알) 이미지 종류를 구분해 총알을 생성할 수 있다. 마지막으로 **collide\_bullets** 함수이다. 이름처럼 대포알이 날아가면서 다른 오브젝트(벽 또는 상대 탱크)들과의 충돌이 발생할 시의 처리를 담당한다. 하지만, 전혀 아무 것과 충돌없이 그냥 화면 밖을 빠져나갈 수도 있는데, 이 때는 단순히 그 대포알을 없에면 된다(21-23번 라인). 그밖에 주의할 점은 벽이과 상대탱크와 충돌시, 벽과 상대탱크는 당연히 없에지만 그와 부딪힌 대포알 자체도 또한 함께 없에야 한다는 것이다(18, 29번 라인). 우리가 최종 게임의 승패시점을 어디서 판단할까 했을 때, 충돌처리 함수 안에서 판별하는 것이 위치상 나쁘지 않다. 적 탱크가 하나도 남지 않은 시점(37-38번 라인), 주인공 탱크가 대포알에 맞은 시점(40-41번 라인)이 바로 그 시점이 될 수 있다. 3개의 사용자 함수에 대한 구현과 이해가 끝났다. 앞서도 언급했지만, 이 게임은 앞선 두 게임(블록격파, 트위비)의 특징들에 대한 사진 지식이 있었기 때문에 생각보다 이 게임의 구현이나 이해에 많은 시간이 걸리지 않았던 같다. 마지막으로 모든 전체코드를 공유하는 것으로 절차지향형으로 만들어본 배틀시티 게임을 마무리 짓고, 다음 절부터는 드디어 사용자 정의 객체를 만드는 방법부터 시작해 그 객체들을 활용해 동일한 게임을 객체지향 패러다임으로 만들어 보기를 시작해보자. [Previous9.1 절차지향형으로 개발하기 1chevron-left](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.1_procedural_dev1) [Next9.3 객체지향으로 개발하기 1chevron-right](https://jjlee.gitbook.io/python-pygame_zero/battle_city/9.3_oop_dev1) Last updated 1 year ago sun-brightdesktopmoon Copy def fire_bullets(player, bullets): if player == tank: bullet = Actor("bulletblue2") else: bullet = Actor("bulletred2") bullet.angle = player.angle bullet.pos = player.pos bullets.append(bullet) Copy def collide_bullets(bullets): global winner for bullet in bullets: if bullet.angle == 0: bullet.x += 5 elif bullet.angle == 90: bullet.y -= 5 elif bullet.angle == 180: bullet.x -= 5 elif bullet.angle == 270: bullet.y += 5 # 벽과 충돌처리 wall_index = bullet.collidelist(walls) if wall_index != -1: del walls[wall_index] bullets.remove(bullet) # 화면이탈 처리 if bullet.x < 0 or bullet.x > WIDTH \ or bullet.y < 0 or bullet.y > HEIGHT: bullets.remove(bullet) # 탱크와의 충돌처리 if bullets != enemy_bullets: # 적 탱크 enemy_index = bullet.collidelist(enemies) if enemy_index != -1: bullets.remove(bullet) explosion = Actor("explosion3") explosion.pos = enemies[enemy_index].pos explosion.images = ["explosion3", "explosion4"] explosion.fps = 8 explosion.duration = 15 explosions.append(explosion) del enemies[enemy_index] if len(enemies) == 0: winner = "You" else: # 주인공 탱크 if bullet.colliderect(tank): winner = "Enemy" # 탱크폭발 애니메이션 for explosion in explosions: explosion.animate() explosion.duration -= 1 if explosion.duration == 0: explosions.remove(explosion) Copy from pgzhelper import * import random WIDTH = 800 HEIGHT = 600 bullets = [] bullet_delay_cnt = 0 BULLET_DELAY = 50 enemy_bullets = [] explosions = [] winner = '' tank = Actor("tank_blue", (400, 575)) tank.angle = 90 ENEMY_MOVE_DELAY = 20 MAX_ENEMIES = 3 enemies = [] for i in range(MAX_ENEMIES): enemy = Actor("tank_red") enemy.angle = 270 enemy.x = (i + 1) * WIDTH / (MAX_ENEMIES + 1) enemy.y = 25 enemy.move_cnt = 0 enemies.append(enemy) # 50x50 크기의 벽의 더미 생성 walls = [] WALL_SIZE = 50 for x in range(int(WIDTH / WALL_SIZE)): for y in range(int(HEIGHT / WALL_SIZE - 2)): # 맨 상/하단을 비워두기 # 랜덤하게 벽없는 빈 공간 생성 if random.randint(0, 100) < 50: wall = Actor("wall", anchor=("left", "top")) wall.x = x * WALL_SIZE wall.y = y * WALL_SIZE + WALL_SIZE # 맨 상단 비우기 walls.append(wall) def move_player(player): # 탱크의 최초 위치를 저장해 두기 original_x = player.x original_y = player.y if player == tank: # 주인공 탱크 if keyboard.right: player.angle = 0 player.x += 2 elif keyboard.left: player.angle = 180 player.x -= 2 elif keyboard.up: player.angle = 90 player.y -= 2 elif keyboard.down: player.angle = 270 player.y += 2 else: # 적 탱크 if player.angle == 0: player.x += 2 elif player.angle == 90: player.y -= 2 elif player.angle == 180: player.x -= 2 elif player.angle == 270: player.y += 2 # 벽을 뚫고 나가지 못하고 자기 자리에 머물기 if player.collidelist(walls) != -1: player.x = original_x player.y = original_y # 게임화면 안에 가두기 if player.left < 0 or player.right > WIDTH \ or player.top < 0 or player.bottom > HEIGHT: player.x = original_x player.y = original_y def fire_bullets(player, bullets): if player == tank: bullet = Actor("bulletblue2") else: bullet = Actor("bulletred2") bullet.angle = player.angle bullet.pos = player.pos bullets.append(bullet) def collide_bullets(bullets): global winner for bullet in bullets: if bullet.angle == 0: bullet.x += 5 elif bullet.angle == 90: bullet.y -= 5 elif bullet.angle == 180: bullet.x -= 5 elif bullet.angle == 270: bullet.y += 5 # 벽과 충돌처리 wall_index = bullet.collidelist(walls) if wall_index != -1: del walls[wall_index] bullets.remove(bullet) # 화면이탈 처리 if bullet.x < 0 or bullet.x > WIDTH \ or bullet.y < 0 or bullet.y > HEIGHT: bullets.remove(bullet) # 탱크와의 충돌처리 if bullets != enemy_bullets: # 적 탱크 enemy_index = bullet.collidelist(enemies) if enemy_index != -1: bullets.remove(bullet) explosion = Actor("explosion3") explosion.pos = enemies[enemy_index].pos explosion.images = ["explosion3", "explosion4"] explosion.fps = 8 explosion.duration = 15 explosions.append(explosion) del enemies[enemy_index] if len(enemies) == 0: winner = "You" else: # 주인공 탱크 if bullet.colliderect(tank): winner = "Enemy" # 탱크폭발 애니메이션 for explosion in explosions: explosion.animate() explosion.duration -= 1 if explosion.duration == 0: explosions.remove(explosion) def draw(): screen.blit('grass', (0, 0)) tank.draw() for enemy in enemies: enemy.draw() for wall in walls: wall.draw() for bullet in bullets: bullet.draw() for bullet in enemy_bullets: bullet.draw() for explosion in explosions: explosion.draw() if winner: screen.draw.text(winner + " Win!", \ midbottom=(WIDTH / 2, HEIGHT / 2), fontsize=100) def update(): global bullet_delay_cnt, enemy_move_cnt # 주인공 탱크 if winner == '': move_player(tank) if bullet_delay_cnt == 0: # 지연시간 종료 후 재발사 가능 if keyboard.space: sounds.sfx_exp_medium12.play() fire_bullets(tank, bullets) bullet_delay_cnt = BULLET_DELAY else: bullet_delay_cnt -= 1 collide_bullets(bullets) # 적 탱크 for enemy in enemies: choice = random.randint(0, 2) if enemy.move_cnt > 0: # 탱크 움직이기 enemy.move_cnt -= 1 move_player(enemy) elif choice == 0: # 움직임 지연시간 초기화 enemy.move_cnt = ENEMY_MOVE_DELAY elif choice == 1: # 탱크 방향전환 enemy.angle = random.randint(0, 3) * 90 else: # 대포알 발사 fire_bullets(enemy, enemy_bullets) collide_bullets(enemy_bullets) sun-brightdesktopmoon --- # 10.3 네트워크 게임으로 만들기 1 | 파이썬-파이게임 제로 (순한맛) 단 한대의 컴퓨터에서만 동작하는 게임(이를 지칭하는 용어로 standalone 게임이라 함)을 네트워크 게임으로 변모시키기 위해서는 기존 퐁 게임을 구성하는 객체들(점수, 반사판, 공)의 코드에 일부 수정이 필요하다. 우리가 처음부터 네트워크형 게임으로 만들 것을 작정하고 객체설계를 한 것이 아니기 때문에 네트워크 게임을 위해 미흡한 부분이 있고 이번 절에서는 이를 보완하도록 하자. 먼저, 점수(Score) 객체이다. 참고로 각 객체의 코드 전체가 아닌 추가/수정된 부분만 따로 떼내 살펴보도록 하겠다. **reinit** 멤버 함수가 추가로 필요한데 이 함수의 목적은 게임을 아예 최초 시작단계로 돌아가기 위한 목적이다. 이런 상황은 네트워크 게임에서만 발생할 수 있는 상황인데 언제 필요할 수 있을까? 그것은 한창 게임 진행 중에 갑자기 네트워크 불완전성으로 인한 또는 의도적인 상대방의 불특정 게임중단 상황을 대응하기 위함이다. Copy class Score(): ... def reinit(self): self._b1_score = -1 self._b2_score = -1 ... def update(): global num_peers ... # 상대 연결 끓김 num_peers = len(net.get_peers()) if num_peers < MAX_PEERS: if is_host: score.reinit() ball.reset() else: # Plyer2 print("The host is disconnected.") exit() # 호스트가 종료되면 방 폭파 ... 릴레이 서버방식의 게임의 특징은 우리가 앞서 살펴봤듯이 그룹방에서 호스트가 중요하고, 호스트는 게임의 전체적인 상태의 관리를 통해 게임을 유지시키는 것을 담당하는 주인장 같은 역할이다. 이 호스트가 어떤 상황에서든 게임 도중에 그룹방에서 나가게 되면 당연히 더이상 게임은 진행될 수 없는 상황으로, 어떻게 처리할지는 여러 시나리오를 만들 수 있는데, 제일 간단히 처리하는 방법은 우리가 알고 있는 소위 방폭파(?)라는 방법으로 그룹안에 있는 모든 클라이언트들이 그룹방에서 내쫒기는 것으로, 즉 모든 클라이언트들의 기존 서버와의 네트워크 연결을 강제종료 시키는 것이다(18~20라인). 이와는 반대로 호스트가 아닌 일반 유저가 갑자기 그룹방에서 나간 상황에 대해서는 어떻게 처리해야 할까? 이 때가 바로 renint 함수를 호출해서 게임을 완전초기화해서 호스트만 혼자 그룹방에서 게임상대자의 접속을 기다리는 최초 상태로 돌리는 지점인 것이다(15~17라인). 그렇다면, 게임중간에 어떤 클라이언트가 그룹에서 빠져나갔는지를 감지할 수 있을까? 그 해답은 update 함수의 5번 라인에 NetNode 객체의 **get\_peers** 멤버함수로 이 함수는 현재 그룹안에 있는 클라이언트들의 id목록을 리턴해주며 update 함수 안에 위치시켜 그 변동성을 실시간 확인함으로써 게임중간에 어떤 클라이언트가 그룹에서 빠져나갔는지 알 수 있다. 다음으로는 각 클라이언트들의 반사판의 조작과 움직임 처리하는 루틴을 살펴보자. 반사판의 조작은 호스트/일반 유저이든 상관없이 둘 다 본인이 선호하는 방식으로 키보드의 '**a**' 또는 '**윗방향키**' 로 윗방향으로 움직이고, 키보드의 '**z**' 또는 '**아래방향키**' 로 아래방향으로 움직일 수 있다(24~27라인). 참고로 **my\_bar** 변수값은 이전 장의 connect\_server 함수에서 살펴보았듯이 그룹방에 입장한 순서에 따라, bar1 또는 bar2값이 사전에 할당되어 있음을 알 수 있다. 여기까지 **UI적으로 본인화면에 움직임을 만든 후에는 상대방에게도 나의 움직임에 대한 변경폭을 네트워크를 통해 알려주어야만 서로 상대방 화면에 상의 움직임이 실시간으로 보일 것이다.** **그러한 목적의 멤버함수가 바로 그 다음 9번 라인에서 사용한 send\_msg 함수이다.** 이 함수는 3가지 파라미터 값을 갖는다. 첫번째로 이 (정보성)메시지의 수신자의 그룹방에서의 id값으로 여기서는 **peer\_id**를 인자로 넘겼는데, 마찬가지로 이전 장의 connect\_server 함수에서 이 값이 사전 설정되어 있다. 즉, 호스트가 된 게임유저는 상대인 일반유저에게/일반유저인 게임유저는 호스트에게 메시지를 보내게 되는 것이다. 두번째 값은 메시지 수신자가 자신에게 도착하면 여러 메시지들 중에서 특정 메시지를 구분할 수 있게 하려는 목적의 메시지 식별자(일종의 id)이다. 마지막으로는 실제 메시지의 내용으로 여기서는 반사판 객체의 중심좌표(**my\_bar.center**)를 보내고 수신측에서는 이 값을 화면에 UI적으로 반영시킴으로서 상대방 반사판의 움직임을 표현한다. 그러나, **send\_msg 함수는 즉시 상대에게 메시지를 전송까지 하는 건 아니고, 해당 메시지를 메시지 송신 버퍼(buffer)라고 보낼 메시지들을 임시로 모아두는 기억장소에 저장해 놓는 것까지의 역할을 하고, 실제 메시지 전송은 38라인에 있는 NetNode 객체의 process\_send 멤버함수에 의해서 송신 버퍼에 임시저장되어 모아진 메시지들을 일괄 전송하는 일을 주기적으로 하게 된다. 따라서, 이 함수를 upate 함수의 맨 마지막에 항상 위치시켜야 한다는 것을 잊지말자.** 지금까지 메시지 송신과정의 전반을 이해했고, 메시지를 전송하는 부분이 있다면, 당연히 해당 메시지 수신하여 처리하는 루틴이 있을 것이고, 이 부분에 대해서는 다음 절에서 자세히 살펴보도록 하자. 마지막으로 반사판 조작해서 새롭게 추가된 부분을 더 살펴보면(31~34라인), 게임 호스트와 일반유저의 처리를 구분했고, 호스트의 경우, 단독게임(standalone) 때와 다를바가 없고 일반유저의 경우는 collide\_ball 함수를 호출하면서 전에는 본적이 없는 **sound\_only** 라는 새로운 인자값을 넘기고 있다. 그 이름에서 알 수 있듯이 공의 충돌감지에 대한 처리시 반사에 대한 직접 처리는 하지 않고, 효과음만 발생시키려는 목적이다(5번 라인). 왜 이런 처리가 필요할까? 그것은 _네트워크 게임의 특성상 원격에 게임에 참여하는 모든 게임참여자의 실시간 일치(sync) 를 위해 각 클라이언트가 독립적으로 처리해야 하는 부분이 있고, 호스트가 대표해서 처리해야하는 루틴이 분리되게 된다. 따라서, 이 부분을 잘 고려해 역할분담해 코딩하는 것이 필요하다. 그러나, 이러한 역할분담에 대해서는 사전에 미리 한번에 미리 알 수 있기보다는 게임을 만들어가는 과정에서 실제 네트워크 환경에서 테스트를 해보며 발견되는 경우가 많다._ 그럼 다음 절에 이이서 게임 제작의 남은 부분인 게임의 시작처리와 공의 움직임 처리, 송신 메시지의 수신 처리에 대해서 알아보도록 하자. [Previous10.2 릴레이 서버 구동과 클라이언트 접속chevron-left](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.2_server_connection) [Next10.4 네트워크 게임으로 만들기 2chevron-right](https://jjlee.gitbook.io/python-pygame_zero/pong_network/10.4_dev_net_2) Last updated 6 months ago sun-brightdesktopmoon Copy class Bar(Rect): ... def collide_ball(self, sound_only=False): if self.colliderect(self.ball): if not sound_only: # # 10픽셀 앞으로 먼저 튀어오르기 if self.ball.vx < 0: # bar1 이면 self.ball.x += 10 else: # bar2 이면 self.ball.x -= 10 self.ball.vx = -self.ball.vx * SPEED_UP # 속도의 x축 방향을 반대로하기 ''' 공이 윗측 진입하면서 반사판 윗측에 부딪힐 때 또는 공이 아래측 진입하면서 반사판 아래측에 부딪힐 때는 진입방향 그대로 반사 ''' if (self.ball.vy > 0 and self.ball.centery < self.centery) or \ (self.ball.vy < 0 and self.ball.centery > self.centery): self.ball.vy = -self.ball.vy * SPEED_UP sounds.bar.play() ... def update(): ... # 반사판 if keyboard.a or keyboard.up: my_bar.up() if keyboard.z or keyboard.down: my_bar.down() net.send_msg(peer_id, 'bar_pos', my_bar.center) for bar in bars: if is_host: bar.collide_ball() else: # 클라이언트 bar.collide_ball(sound_only=True) ... # 메시지 전송 net.process_send() sun-brightdesktopmoon --- # 게임을 단 한 개의 실행파일로 패키징 하기 | 파이썬-파이게임 제로 (순한맛) **먼저 이 기능은 일반 공식 Mu(이하 뮤) 에디터에서는 제공되지 않으며, 저자가 개발하고 있는** [**커스텀 버전의 뮤 에디터**arrow-up-right](https://github.com/roboticsware/mu/releases) **에서만 가능함을 먼저 알린다.** 사용방법은 간단하다. 먼저, _내가 만든 게임이 다른 사람에게 배포해도 될 정도로 충분히 완성이 되었다고 생각하면, 에디터에 내장된 해당 기능을 이용해 게임제작시 필요했던 소스파일, 추가 모듈, 리소스들(이미지, 사운드, 폰트 등)을 다 모아 게임을 단 한개의 실행파일 형태(윈도우즈 플랫폼의 경우, exe파일)로 자동으로 만들 수 있다. 이런 형태가 되어야만 내 게임을 다른 이들에게 배포하기도 쉽고, 게임 사용자들 입장에서도 단 한개의 실행파일을 더블클릭 함으로써 게임을 즐길 수 있어 간편하다._ 예를들어 [4장](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird) 에서 함께 만들어본 플래피버드 게임의 패키징을 시도해 보자. 먼저, 현재 패키징 하려는 게임 소스파일의 위치가 어떻게 되어있는지를 살펴보길 바란다. 에디터 설치 시 에디터가 파이게임제로의 기본폴더로 설정한 "계정명/mu\_code" 에 위치할 수 있고 물론 이 경우에도 그대로 패키징을 시도할 수는 있다. 그러나, **게임에는 전혀 관련없는 파일들이 함께 패키징돼 불필요한 용량만 커지는거 외에 아무 득이 없기 때문에 가능한한 패키징시 정확하게 게임에 필요한 파일과 리소스들만을 패키징 하는게 젤 용량의 낭비가 없는 방법일 것이다.** 이미 처_음 게임개발 시작시부터 관련 파일들을 별도의 폴더로 정리해서 개발을 시작했으면 그대로 패키징을 시도하면 될 것이고, 아닌 경우는 이제서야 따로 모아 정리해도 된다._ 다음과 예시는 플래피버드 게임에 사용되는 소스파일과 리소스들만 별도 폴더 안에 정리한 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252F11KUWNvFAd5EZl6X5dn4%252Fimage.png%3Falt%3Dmedia%26token%3D93c92cd6-ed18-4626-97af-efd9dc11f62c&width=768&dpr=3&quality=100&sign=ddb9f7a5&sv=2) 폴더로 정리될시 주의할 점이 하나 있는데, [4.1절](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background) 참고사항에서 언급된 리소스들(**이미지, 폰트, 효과음, 배경음**)을 담아 놓는 하위폴더들의 이름은 이미 정해진 이름들(**앞에서부터 순서대로 각각 images, fonts, sounds, music)**로만 생성해야 한다는 파이게임제로 라이브러리가 갖고 있는 기본제약사항을 준수해서 생성되어야만 한다는 것이다. 이제 준비는 끝났고, 이후 방법은 아주 단순한데 아래 이미지처럼 _패키징 할 게임의 소스파일탭에 포커스가 되어있는 상태(만약, 여러탭을 열어놓았을 경우)에서_ _"__**패키징**__" 이란 메뉴아이콘을 누르기만 하면 된다._ ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FzhZ0v3CoM8z9sZenef7H%252Fimage.png%3Falt%3Dmedia%26token%3D61704bb6-8f32-4168-aed0-d15126b3528d&width=768&dpr=3&quality=100&sign=9950a690&sv=2) 버튼을 누름과 동시에 패키징 작업이 진행될 것이고, 다음의 이미지처럼 최종적으로 "... **completed successfully**" 라고 패키징이 성공적으로 끝났다면, 소스파일이 저장되어 있던 곳에 **dist 라는 폴더**가 생성되어 있는 것을 확인할 수 있고, 그 안에 단 1개의 실행파일로 패키징된 플래피버드 게임을 확인할 수 있을 것이다. ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FldH2VFSWZIlM8FoSe24Z%252Fimage.png%3Falt%3Dmedia%26token%3D453b50d6-71c0-40b9-a6f9-b72d54f2a88b&width=768&dpr=3&quality=100&sign=4076eee7&sv=2) ![](https://jjlee.gitbook.io/python-pygame_zero/~gitbook/image?url=https%3A%2F%2F949761892-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FZOoo6AnroO2vGSvpOKw7%252Fuploads%252FJkt9pfJ6N2nP5kfo8I8L%252Fimage.png%3Falt%3Dmedia%26token%3Dbd88dfc4-b9ee-4590-a22d-46bd8a50cf09&width=768&dpr=3&quality=100&sign=faebb2bd&sv=2) [Previous부록chevron-left](https://jjlee.gitbook.io/python-pygame_zero/appendix) [Next뮤 에디터 단축키 모음chevron-right](https://jjlee.gitbook.io/python-pygame_zero/appendix/shortcuts) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 게임제작하며 프로젝트 기반으로 파이썬 배우기 (매운맛) | 파이썬-파이게임 파이썬 프로그래밍 언어는 텍스트 코딩용 언어 중에서 가장 쉬운 언어이면서 동시에 가장 인간 친화적인 언어라고 불린다. 그만큼 배우기가 쉬운 언어라고 알려져 있지만, 세상에 어떤 외국어(컴퓨터와 소통하기 위한 프로그래밍 언어 조차도 외국어 한 종류로 간주할 수 있음)가 아무리 쉽다해도 정말 손쉽게 배워지는게 있었던가. 적어도 필자의 기억엔 없었다. 언어를 배우는 지난하고 많은 인내가 필요한 일에 있어서 다만, 좀 덜 지치면서 배울 수 있는 어느 정도의 학습법은 존재했던 것 같다. 그런 기억을 되살려 필자는 이제 막 텍스트 코딩세계에 입문한 이들이 맨 처음 접하게 되고 배우다가 금새 지치게 만드는 **기존의 언어문법 위주의 책이 아닌, "프로젝트 기반으로 배우기" 라는 이미 블록 코딩 때에도 수없이 경험해 봤고, 그렇게 성장해 왔던 우리의 기존 성장루트를 적용해 게임제작을 기반으로 파이썬 언어 배우기라는 방법으로 기술된 책**에 도전하게 되었다. 그럼, 이제 필자와 함께 게임제작하며 배우는 파이썬 세계로 딥 다이브(Deep dive) 해보겠는가? _참고로 만약 여러분이 파이썬 문법에 여전히 많이 익숙하지 않다고 생각한다면, 이번_ _**매운맛**_ _편말고, 더 난이도를 낮춘_ [_**순한맛**_arrow-up-right](https://jjlee.gitbook.io/python-pygame_zero) _편을 먼저 도전해 보는 것이 재미와 흥미를 잃지 않고 공부할 수 있는 방법일 것이다. 효과적인 학습을 위해서는 늘 여러분 자신의 수준에 맞는 책으로 시작하는 것을 추천드린다._ #### [hashtag](https://jjlee.gitbook.io/python-pygame#undefined) 변경내역: 버전 날짜 내역 v1 2024/12/xx 첫 버전 공개 #### [hashtag](https://jjlee.gitbook.io/python-pygame#jj-comseong-gmail.com) **저자**: JJ ([comseong@gmail.com](https://jjlee.gitbook.io/python-pygame#jj-comseong-gmail.com) ) #### [hashtag](https://jjlee.gitbook.io/python-pygame#id-1-https-ai-creator.tistory.com-522) 참고 블로그1: [https://ai-creator.tistory.com/522arrow-up-right](https://ai-creator.tistory.com/522) #### [hashtag](https://jjlee.gitbook.io/python-pygame#id-2) 참고 서적2: #### [hashtag](https://jjlee.gitbook.io/python-pygame#https-creativecommons.org-licenses-by-nc-sa-4.0) 저작권: [https://creativecommons.org/licenses/by-nc-sa/4.0/arrow-up-right](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ko) ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2FXqfaxSWS1mduUmYVCPQP%2Fblobs%2FB4Mw6M98Csz9Cau4Jpyp%2Fimage.png&width=768&dpr=3&quality=100&sign=b73eeaf4&sv=2) [Next1\. 개발환경 구축chevron-right](https://jjlee.gitbook.io/python-pygame/dev_env) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 파이썬 프로그래밍 언어는 텍스트 코딩용 언어 중에서 가장 쉬운 언어이면서 동시에 가장 인간 친화적인 언어라고 불린다. 그만큼 배우기가 쉬운 언어라고 알려져 있지만, 세상에 어떤 외국어(컴퓨터와 소통하기 위한 프로그래밍 언어 조차도 외국어 한 종류로 간주할 수 있음)가 아무리 쉽다해도 정말 손쉽게 배워지는게 있었던가. 적어도 필자의 기억엔 없었다. 언어를 배우는 지난하고 많은 인내가 필요한 일에 있어서 다만, 좀 덜 지치면서 배울 수 있는 어느 정도의 학습법은 존재했던 것 같다. 그런 기억을 되살려 필자는 블록코딩에서 어느 정도의 실력을 쌓았고, 이제 막 텍스트 코딩세계에 입문하고자 하는 이들이 맨 처음 접하게 되고 배우다가 금새 지치게 만드는 **기존의 언어문법 위주의 책이 아닌, "프로젝트 기반으로 배우기" 라는 이미 블록 코딩 때에도 수없이 경험해 봤고, 그렇게 성장해 왔던 우리가 익히 아는 성장루트를 적용해 게임제작을 기반으로 파이썬 언어 배우기라는 방법으로 기술된 책**에 도전하게 되었다. 그럼, 이제 필자와 함께 게임제작하며 배우는 파이썬 세계로 딥 다이브(Deep dive) 해보겠는가? _참고로 본서는 이제 막 텍스트 코딩세계에 입문하고자 하는 이들을 위한 100% 텍스트코딩에 기반한 첫번째 도서입니다._ [_이전 서인 엔트리-파이썬이라는 책_](https://jjlee.gitbook.io/entry-python/) _을 통해 블록코딩-텍스트코딩의 브릿지 통해 생기초 문법을 뗀 사람들 위한 다음 단계의 책입니다._[_네이버에서 공개한 엔트리 블록코딩 기초책(원제: 차근차근 따라하며 배우는 엔트리 프로그래밍-기초)의 7개 예제_arrow-up-right](https://m.blog.naver.com/erke2000/220640141178) _를 파이썬 코딩으로 클론(clone)하면서 좀더 심화된 파이썬 텍스트코딩의 문법을 배웁니다._ [_그 다음 서_](https://jjlee.gitbook.io/pygame_zero-entry_basic#id-70) _인 파이게임제로 책에서는 본격적인 파이썬 게임을 만들며 파이썬 코딩을 배우게 됩니다. 본 서는 그 단계에 진입하기 위한 사전 기초 지식을 습득하는 단계에서 활용가능 합니다._ #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/#undefined) 변경내역: 버전 날짜 내역 v1 2024/07/25 첫 버전 공개 v1.2 2024/10/21 5.1.1장 내용보강 및 6번째 예제 수정 #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/#jj-comseong-mail.com) **저자**: JJ ([comseong@envelope](mailto:comseong@gmail.com) [mail.comenvelope](mailto:comseong@gmail.com) ) #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/#id-1) 참고 자료1: [차근차근 따라하며 배우는 엔트리 프로그래밍 - 기초편arrow-up-right](https://m.blog.naver.com/erke2000/220640141178) #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/#https-creativecommons.org-licenses-by-nc-sa-4.0) 저작권: [https://creativecommons.org/licenses/by-nc-sa/4.0/arrow-up-right](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ko) ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FKPZGq9BGuCGnPcF4DV7Z%2Fimage.png&width=768&dpr=3&quality=100&sign=b22860f1&sv=2) [Next1\. 파이게임 제로(pygame zero) 라이브러리chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/pygame_zero_lib) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 엔트리로 파이썬 기초 입문하기 | 엔트리-파이썬 본 서는 엔트리의 블록기반 코딩으로 코딩(프로그래밍)을 익힌 이후 파이썬 언어를 사용하는 텍스트기반 코딩으로 넘어가가고자 하는 분들에게 중간 다리 역할을 하는 "엔트리-파이썬" 코딩을 통해 **교육목적의 블록 코딩 개발에서 실전개발을 위한 텍스트 코딩으로의 용이한 전환을 꽤하고자 하는 분/ 블록 코딩을 교육한 이후 학습자의 텍스트 코딩으로의 원활한 전환을 돕고자하는 교육자**들을 위해 씌여졌습니다. #### [hashtag](https://jjlee.gitbook.io/entry-python/#undefined) 변경내역: 버전 날짜 내역 v1 2023/11/06 첫 버전 공개 v1.1 2024/02/25 함수 파트 일부 수정 v1.2 2024/07/08 순차/병렬처리 일부 수정 #### [hashtag](https://jjlee.gitbook.io/entry-python/#jj-comseong-gmail.com) **저자**: JJ ([comseong@gmail.com](https://jjlee.gitbook.io/entry-python#jj-comseong-gmail.com) ) #### [hashtag](https://jjlee.gitbook.io/entry-python/#id-1-greater-than-greater-than) 참고 자료1: 엔트리 > [공식교육자료arrow-up-right](https://playentry.org/material) > 엔트리 파이썬 #### [hashtag](https://jjlee.gitbook.io/entry-python/#id-2-ppt) 참고 자료2: 엔트리 오프라인에 내장된 "엔트리 파이선 이용안내 ppt(엔트리파이선 메뉴얼, 엔트리파이선 기본예제)" #### [hashtag](https://jjlee.gitbook.io/entry-python/#https-creativecommons.org-licenses-by-nc-sa-4.0) 저작권: [https://creativecommons.org/licenses/by-nc-sa/4.0/arrow-up-right](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ko) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MaoCyalFe3_7uUcCU9A%252F-MaqETMrrz0He569RPTu%252Fimage.png%3Falt%3Dmedia%26token%3Dd718b297-917f-4ea8-adcf-ae361da0e0ac&width=768&dpr=3&quality=100&sign=a90eed5a&sv=2) [Next1\. 엔트리 vs 엔트리-파이썬chevron-right](https://jjlee.gitbook.io/entry-python/whatis) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 엔트리로 실전 아두이노 배우기 | 엔트리-아두이노 본 서는 엔트리에 대한 기본 지식은 있으나, 아직 아두이노 C++코딩에는 입문하지 못한, **엔트리 블록코딩을 통해 아두이노 기본을 익히고자 하는 분/교육하고자 하는 분**들을 위해 씌여졌습니다. #### [hashtag](https://jjlee.gitbook.io/entry-arduino/#undefined) 변경내역: 버전 날짜 내역 v1 2021/6/8 첫 버전 공개 v1.1 2021/6/10 5.4 장 내용 보완 v1.2 2023/2/17 UI변경에 따른 이미지 교체 #### [hashtag](https://jjlee.gitbook.io/entry-arduino/#jj-comseong-gmail.com) **저자**: JJ ([comseong@gmail.com](https://jjlee.gitbook.io/entry-arduino#jj-comseong-gmail.com) ) #### [hashtag](https://jjlee.gitbook.io/entry-arduino/#id-1) 참고 서적1: [메카솔루션arrow-up-right](https://blog.naver.com/roboholic84) 의 '[입문자를 위한 아두이노-엔트리 종결키트arrow-up-right](https://playentry.org/rest/file/602f55c92f3a2e0050185ee7) ' #### [hashtag](https://jjlee.gitbook.io/entry-arduino/#id-2) 참고 서적2: 박필준님의 '[아두이노 시뮬레이터arrow-up-right](https://wikidocs.net/book/2655) ' #### [hashtag](https://jjlee.gitbook.io/entry-arduino/#https-creativecommons.org-licenses-by-nc-sa-4.0) 저작권: [https://creativecommons.org/licenses/by-nc-sa/4.0/arrow-up-right](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ko) ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MaoCyalFe3_7uUcCU9A%252F-MaqETMrrz0He569RPTu%252Fimage.png%3Falt%3Dmedia%26token%3Dd718b297-917f-4ea8-adcf-ae361da0e0ac&width=768&dpr=3&quality=100&sign=7ed3a32d&sv=2) [Next1\. 엔트리란chevron-right](https://jjlee.gitbook.io/entry-arduino/entry) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 3.2 입/출력 (Input/Output) | 엔트리-파이썬 프로그램에 입/출력은 무엇인가요? 사용자로부터 필요한 정보를 받아드리거나 반대로 보여주는 방식으로서 사용자와 인터렉티브(Interactive) 한 의사소통하기 위한 수단일 것이다. 엔트리 블록코딩에서 우리는 이 두 가지를 다음의 두 블록(![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FhOFa4G2gmQkZMgGFxFOh%252Fentry_block2.png%3Falt%3Dmedia%26token%3Db5fc06f9-0db3-4ba0-a63c-c3cf13d1d8f7&width=263&dpr=3&quality=100&sign=abe26755&sv=2)![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F52TBFtscagBBOfOufKJX%252Fentry_block.png%3Falt%3Dmedia%26token%3Dcdb8cc5f-b0f3-4307-bae8-0acd0fc4c346&width=210&dpr=3&quality=100&sign=abb8b038&sv=2)) 을 사용해서 입출력을 코딩해 본 적이 있다. 이제 이를 텍스트코딩으로 구현해보자. 입/출력 기능은 모두 우리가 이전 장에서 배운 Entry 라이브러리 안에 있는 두 종류의 함수(print, input)를 호출하므로써 구현할 수 있다. 함수 이름 자체가 무슨 용도인지 알 수 있을만큼 직관적이다. 해당 함수들은 엔트리-파이썬의 경우 실제 파이썬과는 조금 다르게(_실제_ _**파이썬에는 라이브러리 호출 없이 곧바로 사용 가능하며, 이러한 함수들을 특별히 언어자체에 기본으로 내장된 함수라 하여 내장함수라 부름**_) Entry라는 라이브러리 안에 존재한다. 이 경우 이 함수들을 어떻게 호출해 사용할 수 있는지를 이전 과에서 이미 배웠고, **라이브러리** **이름**_**.**_**함수명** 이라는 문법형식으로 호출해야 하는 것을 익히 알고 있다. 따라서, **Entry.pirnt()** 와 **Entry.input()** 으로 호촐하면 된다. 다만, 함수가 옳바르게 동작하기 위해 호출자로부터 넘겨받아야 할 필수값(파라미터)이 무엇인지만 정확히 알면 되겠다. 사실 이것도 직관적으로 예상할 수 있듯이 print 함수(출력함수)의 경우, 사용자에게 보여주고자 하는 문구를 넘기고, input(입력함수)의 경우, 사용자로부터 어떤 값을 받아드리고 싶은지 설명하는 문구를 넘겨주면 된다. 이 문구는 사실상 어떠한 문장(_텍스트코딩 세계에서는 이러한 데이터의 형식을 스트링(string)이라고 함_)이기 때문에 문구를 의미하는 문법표현으로 **“”(큰 따옴표)** 안에 원하는 내용을 적으면 된다.(작은 따옴표도 되긴 하나 엔트리-파이썬에서는 자동으로 큰 따옴표로 변경된다.) 조금더 재미있는 부분은 출력하고 싶은 문구를 몇개의 문구를 나눈 후 이를 재조립해 출력하는 방법도 가능하다는 것이다. **여러 문구를 조립할 때 문구들 사이에 사용하는 연산자 기호는 + (문구들을 덧붙힌다는 직관적 의미를 가짐)이다.** 이제 지금까지 이론적으로 배운 부분은 모두 합쳐 사용자의 나이를 입력받고, 출력하는 아주 간단한 프로그램을 텍스트코딩으로 코딩해보자. 실행결과 블록코딩 엔트리-파이썬 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252Fd2XGi0Pc6ErDR3kGfXE8%252Fimage.png%3Falt%3Dmedia%26token%3D7168b177-06a1-43cb-9515-770c661351a5&width=768&dpr=3&quality=100&sign=fa75ed18&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FzGJXuKqJSrmDZQH5Fw3j%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D7f570aca-e879-4b07-a78d-34bb67726c2b&width=768&dpr=3&quality=100&sign=3c1edaf9&sv=2) Copy # Entrybot 오브젝트의 파이선 코드 import Entry def when_start(): Entry.input("당신의 나이를 입력해주세요.") Entry.print("당신의 나이는" + Entry.answer() + "입니다.") 코드를 이해할 수 있겠는가? 조금 낯선 부분이 등장했는데, 바로 Entry.answer() 라는 부분이다. 이 함수는 왜 필요할까? _사실 이 함수의 필요는 엔트리-파이썬에만 필요한 함수(실제 파이썬에서는 사용자가 입력한 값을 자신이 직접 정의한 변수에 담아 사용하기 때문에 저런 용도의 함수가 필요하진 않음)로, 함수의 용도는 사용자가 입력한 값을 무엇인지 알아내기 위한 용도로서 Entry.print 함수 안에서 출력문장 조립의 한 부분으로서 직접 사용함으로써 사용자의 입력값을 가져다가 출력하는데 활용하였다._ 내부적인 동작을 좀더 설명하면 사용자가 어떤 값을 입력하면 그 값을 코드의 다른 부분에서 재활용하기 위해서는 어떻게든 그 값을 어느 공간에 임시로 저장(보통 이를 변수라는 저장공간에 저장하게 됨)을 해두어야 하고, 그 변수를 통해 입력값을 코드의 다른 부분에서 활용하게 된다. 그러나, 우리의 코드는 어디에서 우리가 정의한 변수가 없음에도 원하는 정상동작을 구현했는데, 사실은 엔트리 내부적으로 사용자가 입력한 값을 노출하지 않는 숨겨진 임시변수에 저장해 두고 있고, 우리에게는 단지 그 값을 읽어 올 수 있는 answer()라는 함수만을 제공하고 있다. 앞서 언급했듯이 실제 파이썬 코딩에서는 이렇게 활용되지 않는 부분이기는 하나, 엔트리 블록코딩에서 사용자의 편의성을 최대한 끌어올리다보니 불가피하게 발생되는 부분이라고 이해하면 좋겠다. 그리고, 그 블록코딩을 그대로 계승해야 하는 엔트리-파이썬 입장에서도 불가피한 부분이기도 하다. 엔트리-파이썬은 블록코딩에서 텍스트코딩으로 넘어가기 위한 중간 연결다리로서의 역할이지 파이썬 그 자체를 정확하게 배우기 위한 용도는 아님을 이 예제에서도 알 수 있었다. [Previous3.1 Hello World 예제코드의 이해chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world) [Next3.3 변수 (Variable)chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax/3.3-variable) Last updated 1 year ago --- # 4.1 순차/병렬처리 (Serial/Parallel) | 엔트리-파이썬 **순차/병렬처리에 대한 개념은 사실은 프로그래밍의 패러다임까지는 아니다. 간단히 말하면 어떠한 일을 처리하는 순서로 복수업무를 순차적으로 처리할 것인가, 동시에 처리할 것인가 정도로 구분하면 되겠다.** 순차처리(Serial processing)는 코드 한줄 한줄 순차적으로 수행된다는 것으로 언플러그드(unplugged) 교딩교육에서도 가장 기본으로 다루는 개념으로 사실상 우리가 너무 익히 잘 아는 개념이라 따로 설명이 필요없고, 병렬처리(Parallel processing)라는 컴퓨터공학의 원론적인 개념으로 설명하기 시작하면 초보자 수준에서 이해하기 어렵고, 또 엔트리-파이썬에서 그러한 수준에 병렬처리를 지원하지도 않아서, 간단히 개념적으로만 이해하고 넘어가도 좋겠다. **병렬처리는 복수의 둘 이상의 작업을 동시에 실행할 수 있는 방식**이라고 말했다. 이때 진짜 각각의 작업들이 완전히 독립적으로 개별로 실행되는거냐(병렬성, Parallelism), 마치 작업들이 동시에 실행되는 거처럼 보이게 하는 테크닉(동시성, Concurrency)으로 실행되게 하는 것이냐에 따라 구분이 많이 달라지는데, 엔트리-파이썬은 후자라고 하겠다. 엔트리-파이썬에서는 동시성(concurrency) 처리 프로그래밍시 사용하는 일반적인 기법(쓰레드, 멀티 프로세스 등)을 사용해 코딩할 수 조차 없으니 당연히 동시성 텍스트코딩하는 방법도 배울 수가 없다. 이 책의 서두에서도 언급했지만, 엔트리-파이썬의 갖는 한계임과 동시에 애시당초 엔트리-파이썬에서 그정도까지의 학습을 의도하지 않았고, 블록코딩에서 첫 텍스트코딩의 넘어가는 분들이 대상이라 개념적 이해만 하고 넘어가도 충분하고, 사실상 그것만으로 가치는 분명히 있다는 것을 알면 좋겠다. 엔트리 블록코딩에서 다음의 예제와 같이 우리는 이미 병렬처리를 많이 코딩해봤다. 실행결과 블록코딩 엔트리-파이썬 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FN41jjvfGVOzXenK5EsvL%252Fimage.png%3Falt%3Dmedia%26token%3D6e654596-5da3-4afc-8ba0-6dcc8e8aa70f&width=768&dpr=3&quality=100&sign=94111876&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FKXqbOCYXqDP8aSHeBlEP%252Fimage.png%3Falt%3Dmedia%26token%3D799bb1e1-5a9f-4ff3-8b28-a5e49f4618b3&width=768&dpr=3&quality=100&sign=9034fc14&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FtyKv0abRGkztvoSykYHc%252Fimage.png%3Falt%3Dmedia%26token%3D3fbab624-63b0-40ab-b4ac-c240115dec68&width=768&dpr=3&quality=100&sign=fecabe5d&sv=2) "시작하기 버튼을 클릭했을 때" 라는 블록을 한 오브젝트 내에서도 여러 개 사용해 독립적으로 보이는 작업들을 마치 동시에 처리하듯이 코딩해본 경험이 있는 것이다. 이처럼 우리는 여러가지 프로그래밍의 핵심개념들을 알게 모르게 사용해왔던 것이다. 따라서, 엔트리-파이썬에서의 병렬처리는 블록코딩에서 해본 것과 크게 다르지 않다. 단지, 위의 예저처럼 when\_start 함수를 두번 이상 사용할 수 있어, 사용자가 프로그램을 시작하기 위해 시작하기 버튼을 누름과 동시에 각각 when\_start 함수가 동시에 불려지기 때문에 그 함수 호출시 수행해야 할 여러작업을 독립적으로 기술하고, 마치 동시에 실행되는 것 같은 효과를 누리도록 코딩하고 활용할 수 있으면 되겠다. [Previous4\. 엔트리-파이썬으로 프로그래밍의 핵심 개념을 익히자chevron-left](https://jjlee.gitbook.io/entry-python/paradigm) [Next4.2 절차 지향형 (Procedural)chevron-right](https://jjlee.gitbook.io/entry-python/paradigm/4.2-procedural) Last updated 1 year ago --- # 3.3 변수 (Variable) | 엔트리-파이썬 우리는 엔트리 블록코딩에서 변수와 리스트라 불리는 것을 사용해봤다. 둘 다 데이터를 담는 저장공간이었다. 그중에서 먼저 변수는 무엇이며 그 목적은 무엇이었는지를 상기해 보자. 변수는 그 이름 그대로 고정되지 않은 변화하는 값을 임시로 보관해 놓을 공간과 같은 것이다. 우리는 블록코딩에서 어떠한 블록을 사용할 때, 대부분의 블록은 특정한 사전에 정의된 어떤 행동을 유도하기 위해 그에 맞는 고정된 값을 지정해 사용하는게 일반적이다. 하지만, 미리 특정할 수 없는 값들, 예를 들어 사용자로부터 임의로 입력된 값에 기반에 어떤 행동을 하게 만드는 프로그램의 경우도 변수를 사용해야 한다. 방금 언급된 사례인 사용자로부터의 임의의 두 개의 숫자를 받아드려서 합을 구해주는 덧셈만 가능한 아주 간단한 덧셈전용 계산기 프로그램을 변수를 사용해 코딩해보자. 실행결과 블록코딩 엔트리-파이썬 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FfgiXjrMlOXEQydtGWC1s%252Fimage.png%3Falt%3Dmedia%26token%3Dea976bed-f942-49fc-8e87-9abb1cfd4965&width=768&dpr=3&quality=100&sign=7e896386&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F8v7pHlyIn7p3ZKSnK7Ly%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D3d405992-3ef3-497a-b549-5c6f0d1dbba9&width=768&dpr=3&quality=100&sign=2e5dbe94&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry first = 0 second = 0 result = 0 def when_start(): Entry.print("당신이 입력한 두 수를 더해주는 프로그램 입니다") Entry.wait_for_sec(2) Entry.input("첫 번째 숫자를 입력하세요.") first = Entry.answer() Entry.input("두 번째 숫자를 입력하세요.") second = Entry.answer() result = first + second Entry.print("입력된 두 수의 합은 " + result + " 입니다") 아직 우리가 텍스트코딩에 익숙지 않아서 갑자기 코드량이 많아진 느낌이 들 순 있지만, 사실은 일부러 코딩할 때 공백라인으로 코드 덩어리를 의미있게 구분해 가독성을 높여놓은 코드라 라인 수가 길어진 느낌은 있지만 차근히 해석해보면 복잡한 내용은 거의 없다는 것을 알 수 있다. 그동안 배우지 않았던 첫 등장하는 부분은 5~7라인까지의 3라인에 걸쳐 작성한 코드로, first, second, result라고 이름지은 변수를 3개 선언하고 그 변수값의 초기값(맨 처음 생성하면서 부여한 값)으로 각각에 0을 넣어놓은 것이다. **변수를 만드는 파이썬 문법은 다음과 같다.** > **변수명 =** _**초기값**_ _여기서 변수명은 내가 필요한 임의의 이름을 부여하되 그러나 가능하면 의미있게 부여해서 코드를 읽었을 때 쉽게 사용목적을 파악하도록 하는게 잘 이름짓는 방법이다. 위의 예제에서는 사용자의 첫 입력값(first), 두번째 입력값(second), 그리고, 두 값을 더한 결과값(result)이란 의미를 부여하기 위해 저러한 변수명을 의도적으로 사용했고, 변수값에 처음 생성하면서 동시에 어떤 임의의 값(이를 초기값이라 부름)을 할당해 값을 가진상태에서 출발되도록 한다. 보통 일반적으로 0 을 초기값으로 사용한다. 해당 문법에서 주의해야 할 것이 하나 있는데_ _**'=' 이라는 기호의 의미는 우리가 수학에서 의미했는 '값이 같다' 라는 의미가 아니라, '=' 를 중심으로 오른쪽에 있는 값을 왼쪽에 할당한다는 의미라는 것을 반드시 기억해야 한다.**_ 🔢 따라서, 5번라인의 first = 0 이라는 코드는 '='의 왼쪽에 위치한 first라는 변수에 '='의 오른쪽에 위치한 0 값을 저장한다 라는 의미가 되는 것이다. circle-info 변수명은 임의로 사용할 수 있다고는 했으나, 사실은 파이썬에서 변수이름을 지을 때, 크게 지켜야 할 2가지 규칙은 있다. * 변수의 이름은 숫자로 시작할 수 없다. * 변수의 이름 사이에 공백이 있을 수 없다. 여기서, 참고로 좌우의 값이 값냐라는 수학적 의미를 코딩으로 표현하려면 어떤 기호를 써야하는지 궁금할 수 있는데 이 때는 '==' 기호로 표현하는데 이렇게 '='기호를 한번이 아닌 연달아 두번을 사용해야만 좌우의 값이 서로 같은지 여부의 비교의 의미가 된다. 🔢 10~11번 라인 코드덩어리는 프로그램에서 사용자에게 우리 프로그램은 어떤 프로그램임을 알리기 위한 설명을 전달하는 목적으로 10번 라인의 (화면)출력함수는 이미 사용해 보았고, 11번 라인에 Entry.wait\_for\_sec(2) 코드는 처음 등장했는데, 의미해석은 어렵지 않은데 Entry라이브러리 안에 있는 wait\_for\_sec함수를 호출해 사용한 건데, 용도는 블록코딩에서의 "~초 기다리기" 블록과 동일한 것으로, 몇 초 기다릴지의 값을 함수로 넘겨주여야 하고, 여기서는 이전 10번 라인에서 출력했던 말을 2초간 화면에 유지하며 기다리게 하기 위해 숫자 2 값을 함수로 넘겨주었다. 🔢 13~16번 라인의 코드덩어리는 사용자로부터의 임의의 두 수를 차례차례 입력받아 각각의 두 변수(first, second)에 저장해 두는 것이고, 이 때 왜 사용자 입력값을 Entry.answer()를 통해 값을 읽어 저장해야 하는지는 이전 장 [입출력 함수](https://jjlee.gitbook.io/entry-python/basic_syntax/3.2-input-output) 부분에서 설명하였다. 🔢 18번 라인에서는 두 변수(first, second)에 저장된 값을 읽어 서로 더한 결과를 세 번째 변수인 result에 저장해 두는 것이다. 🔢 19번 라인에서는 사용자가 입력한 두 수의 합계가 저장되어 있는 변수 안에 값을 사용자에게 보여줌으로써 프로그램의 목적을 달성하며 끝이난다. 우리가 블록코딩에서 이미 변수를 사용해 봤다면, 그 의미와 용도를 잘 알고 있을 것이고 텍스트코딩으로 넘어오더라도 그 의미와 목적이 달라지는 건 전혀없고, 다만 텍스트코딩에서 사용할 때 사용하는 텍스트코딩용 언어(여기서는 파이썬)에 맞는 변수사용 문법을 따라 사용하면 되는 것이다. [Previous3.2 입/출력 (Input/Output)chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax/3.2-input-output) [Next3.4 조건 (Condition)chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax/3.4-condition) Last updated 1 year ago --- # 3.6 리스트 (List) | 엔트리-파이썬 블록코딩에서 리스트라고 불렸던 저장공간의 목적은 무엇이었는가? 앞에서 **변수가 단 하나의 변화하는 값(또는 사전에 값을 특정할 수 없는 값)을 저장했다면, 리스트라는 공간은 변화하는 단 하나의 값이 아니라, 여러 개의 복수의 값을 저장할 수 있는 있는 공간인 것이다.** 이러한 공간을 파이썬에서는 리스트(list)라고 부르고, 다른 텍스트코딩 언어에서는 일반적으로 배열(array)라고 부르는데 엔트리에서 이러한 복수개의 값을 저장하는 공간을 이름지으면서 어떤 다른 단어로 예를들어 '목록' 등의 변역된 한글이름으로 표현할 수 있었음에도 의도적으로 리스트(list)라고 파이썬 상의 용어표현 그대로 도입한 것부터 사실상 이 개념을 이해시키기 위한 잘짜여진 각본이 있었단 생각이 든다. 블록코딩에서 리스트가 엔트리-파이썬 텍스트코딩에서는 어떻게 사용할 수 있을까? 그럼, 예제를 통해 엔트리-파이썬에서 어떻게 코딩할 수 있는지 상세히 알아보도록 하자. [3.3장](https://jjlee.gitbook.io/entry-python/basic_syntax/3.3-variable) 에서 만들었던 덧셈전용 계산기 프로그램에서 이 리스트를 활용하여 사용성을 더 개선시킬 수 있다. [3.3장](https://jjlee.gitbook.io/entry-python/basic_syntax/3.3-variable) 의 덧셈전용 계산기는 사용자로부터 입력된 임의의 두 값만을 받아 합계를 구해주고 있다. 이제는 사용자가 입력할 수 있는게 단지 두개의 값만이 아닌 제안없이 더 많은 수를 연속적으로 입력받아 입력한 모든 수를 더한 합계를 구해줄 수 있다면 사용자 입장에서는 더나은 사용성을 제공하는 프로그램이 될 것이다. 먼저, 리스트를 어떻게 사용할 수 있는 문법을 확인해보자. > **리스트명 = \[문자, 숫자 등의 여러 값. 단, 값들은 콤마(,)로 구분\]** 리스트의 이름은 변수의 이름을 지을 때와 동일하게 임의로 지을 수 있는데, 나를 포함해 누구라도 코드의미를 빠르고 효과적으로 이해할 수 있게 하기 위해 항상 의미있게 이름 짓는 습관을 드리는게 필요하다. 이전에도 언급했지만, 이는 프로그래밍 세계에서의 주요한 기본 덕목 중에 하나라고 할 수 있다. circle-info 리스트명은 임의로 사용할 수 있다고는 했으나, 사실은 파이썬에서 리스트 이름을 지을 때, 변수이름 지을 때와 마찬가지로 크게 지켜야 할 2가지 규칙은 있다. * 리스트의 이름은 숫자로 시작할 수 없다. * 리스트의 이름 사이에 공백이 있을 수 없다. 다음은 문법에 맞게 작성한 리스트의 예제들이다. words = \["apple", "banana", "grape"\] numbers = \[1, 3, 10, 25, 2\] complex = \["apple", 2, 32, "berry", 56, 5\] words라는 이름에 리스트는 문자형(string) 데이터 유형(type)의 값(또는 아이템이라 호칭) 3개를 갖고 있다. numbers라는 이름에 리스트는 숫자형 데이터 유형의 아이템 5개를 갖고 있다. complex라는 이름에 리스트는 문자형, 숫자형의 데이터 유형의 값을 섞어서 총 6개의 아이템을 갖고 있다. 지금까지 우리는 리스트를 선언하면서 동시에 초기값(선언하면서 동시에 맨 처음 할당하는 값)을 설정하는 법을 배웠다. 이제 더 배워야 할 지식은 리스트 안에 저장된 값들(아이템들)을 접근해 읽어오는 법, 기존 리스트에 새로운 신규 값(아이템)을 추가하거나, 삭제하는 방법에 대해 알 필요가 있다. 방금 언급된 리스트를 다루는 방법들은 이번 장의 서두에서 언급된 우리가 목표하는 앱인 발전된 덧셈계산기 앱의 코드들을 하나씩 분석해 보면서 배워나가기로 하자. 실행결과 블록코딩 엔트리-파이썬 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FwEN4vge5oeqnJHrUn8Dt%252Fimage.png%3Falt%3Dmedia%26token%3D667398f2-01f8-40f6-a122-6d09452f85d3&width=768&dpr=3&quality=100&sign=22731c3d&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FxhjDI46ubCL1YRQA5nQe%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D22bb5dc9-3fb4-4779-83ff-7aaf3e767ac1&width=768&dpr=3&quality=100&sign=cdb2f7f9&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry result = 0 numbers = [] def when_start(): Entry.print("당신이 입력한 모든 수를 더해주는 계산기 입니다") Entry.wait_for_sec(2) while True: Entry.input("더할 숫자를 입력하세요. 현재까지 입력한 값의 합을 알고 싶으면 '='를 입력하세요. 앱을 종료하고 싶으시면 'x'를 입력하세요.") if Entry.answer() == "x": Entry.stop_code("all") if Entry.answer() == "=": while len(numbers) != 0: result = result + numbers[0] numbers.pop(0) Entry.print(result) Entry.wait_for_sec(2) result = 0 else: numbers.append(Entry.answer()) 🔢 먼저 5번라인에서 numbers라는 이름에 리스트 한개를 선언한 내용이 좀 특이한데, 초기값이 전혀 없는 상태로 리스트를 생성했다는 것을 알 수 있는데, 왜냐하면 대괄호 안에 안에 아무값도 없는 상태로(그냥 대괄호만 열고 곧바로 닫는 형태로) 선언했기 때문이다. 이렇게 선언한 이유는 납득이 가능한데, 우리 앱은 사용자가 임의로 제한없이 입력하는 숫자값들을 담아놓을 임시공간이 필요하고, 그러한 목적에 부합하는 공간인 리스트를 활용하되 처음에는 사용자가 아무것도 입력하지 않은 상태이고, 이 상태에서도 리스트의 존재는 필요함으로 이런식으로 초기화 했다고 예상할 수 있다. 🔢 다음으로 26번 라인을 먼저 살펴보자. 26번 라인은 리스트에 사용자가 입력한 숫자값를 신규 아이템으로 추가하는 코드이다. append라는 함수를 통해 사용자 입력값을 Entry.answer()통해 읽어 전달하고 있는데 그 append 함수의 사용을 위해 **number.** 으로 즉, 리스트의 이름 안에 위치한다는 의미로 함수를 호출하고 있는 점이 특이할 것이다. 어떻게 이런식의 함수호출이 가능할까? 이유는 리스트 그 자체가 객체(Object)이기 때문에 그런 것인데, 이 부분을 자세히 설명하기에는 이 책의 범위를 초과하는 것이라 따로 관심있는 사람은 따로 파이썬에서의 객체 다루기에 대해 공부하길 바란다. 현재로서는 **리스트에 신규 아이템 추가 방법은 리스트이름.append(추가하려는 값) 문법을 통해 사용**한다고 알아두면 되겠다. 🔢 이제 18~20번 라인을 살펴보자. while을 사용한 반복문이다. 그런데 언제까지 반복할지에 대한 조건이 있는데, len(numbers) != 0 라는 조건이고, 여기서 우리는 기존에 "==" 기호(연산자)가 의미하는 것이 무엇인지는 알고있다. 하지만, "!="의 의미는 무엇일까? 바로 "=="의 반대 의미로, 좌우의 두 값이 서로 같지 않을 때를 나타내는 의미가 된다. 그렇다면 이 코드의 의미를 해석하면 len(numbers) 함수의 리턴값이 0 과 같지 않을 때까지 반복하는 것이다. 이제 len 함수가 무엇인지 알아야겠다. 사용법을 보면 특이한데 함수명 앞에 그 함수가 위치한 어떤 위치정보 예를들어 어떤 라이브러리의 이름이나, 리스트명나 변수명으로 참조하여 함수를 부른 것이 전혀없다. 이 말인 즉은 **이 함수는 어디에 속한게 아닌 파이썬 언어 내부에 디폴트로 내장되어 있는 함수라는 것이다. 그래서, 이름만으로 맘대로 불러서 사용할 수 있는 특별함수이고 우리는 이를 특별히 내장함수라고 호칭한다.** 그렇다 len함수는 내장함수이고, 용도는 그 이름에서 유추할 수 있는데, length(길이)의 약자로 만든 함수 이름으로 결국 특정 데이터가 보유하고 있는 데이터의 양(데이터의 길이)을 알려주는 함수이다. 따라서, 여기서 len 함수로 넘겨진 데이터값은 numbers라는 리스트로 결국, 해당 리스트가 갖고 있는 아이템의 개수를 알려주게 된다. 26번 라인의 코드에 의해서 사용자가 임의의 숫자 2개를 입력했으면, len 함수로 확인해 보면 현재 아이템개수는 2라는 것을, 사용자가 임의의 숫자 3개를 입력했으면 현재 아이템개수는 3이라는 것을 알 수 있게 된다. 위에서 각각 개별적으로 파악된 의미들을 종합해 해당 코드를 정확하게 다시 해석하면 _**len(numbers) != 0 이란 조건을 가진 while문은 결국, numbers라는 리스트에 아이템이 하나도 없을 때까지 반복하라는 것이다.**_ 사용자가 임의로 입력하는데로 numbers에는 계속 아이템들이 채워질텐데 어째서 하나도 없는 상황이 가능한 것일까 라는 의문이 들 수 있다. 그것은 19~20번 라인의 코드를 해석하면 이유를 찾을 수 있다. 🔢 19~20번 라인의 코드는 사용자에 의해 입력된 nubmers 리스트 안에 숫자값들을 하나하나씩 꺼내와 합계를 구하는 코드이다. 위에서 우리는 리스트를 다루는 방법 3가지(신규 값을 넣기, 기존 값을 읽기, 기존 값을 제거하기) 중에 1가지(신규 값을 넣기)를 배웠고, 이번에 기존에 저장되어 있는 읽어올 수는 방법을 배울 차례이다. 이를 정확히 이해하려면 먼저, 문법과 더불어 리스트의 참조 인덱스(reference index)라는 개념에 대해 배워야 한다. 리스트의 특정 아이템을 읽는 문법은 다음과 같다. > **리스트이름\[참조 인덱스\]** 다음 예에서 볼 수 있듯이 리스트 안에 아이템이 존재하게 되는 그 순간(리스트 정의와 동시에 초기값 설정이든, 추후 신규 아이템이 추가되든), 곧바로, 다음에 그림과 같이 파이썬 언어 내부적으로 각 아이템이 리스트 내에 위치한 순서에 따라 맨 앞에서부터 0부터 1씩 증가하며 자동으로 매겨지는 우리 눈에는 보이지 않는 인덱스값이 내부적으로 존재하게 된다. 우리는 그 인덱스값을 이용해서 리스트 안의 각 아이템의 값들을 읽을 수 있게 된다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FKFbJY5KR7b9bJ1WG9WGi%252Fimage.png%3Falt%3Dmedia%26token%3D752a58c1-48c3-4793-bfd7-73301c577c3b&width=768&dpr=3&quality=100&sign=86532cff&sv=2) 우리가 만약 저 words라는 리스트 안에 두번째 위치한 "banana"라는 아이템(값)을 읽어오고 싶다면, 이제 리스트의 특정 위치의 아이템을 읽는 문법에 따라 words\[1\] 이라고 표현해 읽을 수 있는 것이다. 아래 그림처럼 엔트리는 이러한 리스트 내의 값들과 참조 인덱스값들을 실시간으로 시각화하여 보여주고 있는데, 우리의 예제에서 실행결과화면을 보면 좌측상단에 numbers 리스트 안에 2와 3이 순서대로 들어있는 것을 확인할 수 있다. 즉 사용자는 처음에 2라는 숫자값을 입력해고, 이어서 3이라는 숫자값을 입력한 상황인 것이다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F009Xx5dKTCPudzVuSe8s%252Fimage.png%3Falt%3Dmedia%26token%3Df7b66db0-b256-4c9b-9dfe-103caed25202&width=768&dpr=3&quality=100&sign=9fd5f573&sv=2) 🔢 19라인의 코드는 numbers\[0\]이라는 문법표현으로 numbers 리스트 안에 맨 첫번째 아이템을 읽어서 그 값을 result라는 변수에 저장해 놓으라는 것이다. 바로 다음의 20번 라인 코드는 무엇인가? numbers 리스트 안에 위치한 numbers.pop()의 형식으로 pop 함수를 부르고 있는데, pop이라는 그 이름에서 알 수 있듯이 리스트 안에 특정위치의 데이터를 꺼내서 삭제하는 용도의 함수이다. 그래서, 이 함수의 사용은 **리스트이름.pop(삭제하려는 아이템의 참조 인덱스)** 형태로 호출해 사용할 수 있다. 따라서, 20번 라인의 numbers.pop(0) 의 의미는 리스트 안에 맨 첫번째(인덱스 0에 의해) 아이템을 리스트로부터 삭제(제거)하라는 의미이다. 우리 예제의 위의 실행 결과화면에서(2, 3값이 연달아 입력되어진 상태에서) 연이어 살펴보면, 이 코드가 실행되면 리스트 안에 맨 처음값 숫자 2는 리스트에서 제거됨과 동시에 그 뒤에 위치해 있던 숫자 3이 방금 제거되어진 2가 있던 위치로 자동으로 끌어당겨져와 이제 맨앞에 위치하게 된다. pop(0)라는 의미는 결국 현 시점에서 항상 리스트 상에 맨 앞에 위치한 값을 가르키게 되는 것이다. 위에 코드를 엔트리에서 직접 실행시켜보자. 글로 읽은 것보다 한번 보는게 더 나은데 엔트리가 이러한 단계적 동작을 실시간으로 시각화해 비주얼적으로 보여주므로 직관적으로 이해할 수 있게 되기 때문이다. 🔢 에제 18-20번 라인의 코드를 통합적으로 다시 재해석하면, 현재 numbers 리스트에 저장되어 있는 아이템값을 하나씩 꺼내서 읽어 기존 합계(result변수값)에 더한 후에 그 읽은 값은 이제 리스트에서 제거해 버리는 일을 하되, 이런식으로 읽고 더하고 제거하는 일을 결국 리스트에 아무 아이템도 남지 않을 때까지 반복해서 수행하라는 의미이다. 즉, 사용자가 입력한 모든 값의 최종 합계를 구하고자하는 목적이며, 합계를 구하면서 동시에 리스트 안에 내용을 비워 깨끗하게 만드는 일을 함께 수행하고 있다. 다시 처음으로 돌아가 우리가 목표한 사용자로부터 제안없는 수를 연속적으로 입력받아 입력한 모든 수를 더한 합계를 구해주는 기능을 갖는 앱을 최종구현 완료하였다. 해당 기능은 반드시 위에서 언급된 식으로만 코딩해야 하는 것이 당연히 아니다. 동일 기능을 여러 가지 방법으로 구현할 수 있는데, 지금의 코드는 그 중에 한 방법일 뿐이다. 사실은 이게 엔트리-파이썬이 아니고, 오리지널 파이썬였다면 훨씬 더 간단하고 효과적으로 구현할 수 있는 코드가 존재하나, 우리는 엔트리-파이썬이 허용하는 문법표현 한계 안에서 코딩할 수 밖에 없고 또 여러분에게 리스트를 조작하는 다양한 표현법을 보여주려고 일부러 저렇게 코딩한 이유도 있다는 것을 참고하길 바란다. [Previous3.5 반복 (Loop)chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax/3.5-loop) [Next3.7 무작위 수(Random)chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax/3.7-random) Last updated 1 year ago --- # 4.3 이벤트 주도형 (Event-Driven) | 엔트리-파이썬 이벤트 주도형 또는 이벤트 기반형(Event-Based) 프로그래밍 패러다임에 대해 알아보자. 이 패러다임이 대두되기 시작한 것은 사실한 운영체제(OS: Operating System)이 기존에 콘솔(Console)이라 불리는 텍스트기반으로 명령어를 일일히 입력해 운영체제와 소통하던 인터페이스에서 GUI(Graphic User Interface, 그래픽 사용자 인터페이스)방식으로 전환되기 시작하면서 대두된 것으로, 즉, GUI 환경 맞는 형식에 프로그램이 필요했고, 프로그래밍 개발방식도 마찬가지로 이에 맞춘 대응이 필요했다고 볼 수 있겠다. GUI 환경이란 다른게 아니고, 그래픽 화면 기반에서 시스템(즉 컴퓨터)를 다루는 것으로 PC든 스마트폰이든 클릭과 터치로 선택하고 조작하고 하는 것이 이미 우리가 컴퓨터를 다루는 보편화된 환경이 되어있기 때문에 이 프로그래밍 패러다임의 이해에는 크게 어려움은 없을 것 같다. 그럼에도 이벤트 주도형의 개념을 다음의 그림을 보고 더 잘 이해해 보자. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FRVGUokwS6jMXGLhL1oIG%252Fimage.png%3Falt%3Dmedia%26token%3Dda8c4c47-8ffa-4fc3-9d10-736462a7b7a0&width=768&dpr=3&quality=100&sign=87d13083&sv=2) 출처: [Kamang's IT Blogarrow-up-right](https://kamang-it.tistory.com/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%ED%8C%A8%EB%9F%AC%EB%8B%A4%EC%9E%84%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8DEvent-based-programming) **지금 화면에 보이는 내 프로그램에게서 어떤 행동을 촉발(트리거: Trigger)되게 만드는 모든 상황이 사건(이벤트)라 지칭할 수 있다. 사용자가 화면상에 특정 이미지 버튼(또는 아이콘)을 누른 시점, 특정 키보드 자판의 키(알파벳, 특수문자, 방향키 등)를 누른 시점, 마우스의 특정 버튼(왼쪽/중간/오른쪽 등)을 누른 시점, 어떤 예정된 시간이 된 시점, 다른 프로그램 자체로부터의 내 프로그램을 호출하는 시점 등등 말이다.** 즉 개발자는 이러한 이벤트가 발생할 것을 미리 사전에 예상하고, 이러한 각각의 이벤트가 발생했을 때 어떤 일을 수행하라 라는 식으로 개념을 갖고 코드작성을 해나가는 식의 프로그래밍의 방식을 이벤트 기반형 프로그래밍 패더라임이라 할 수 있다. 우리는 이미 엔트리에 이러한 방식으로 코드작성을 해본 경험이 많아서 아마도 개념적 이해나 코드적용에는 크게 어려움이 없을 것으로 예상한다. 다만, 우리는 그것을 엔트리-파이썬에서는 어떤 함수를 활용해 코딩할 수 있는지만 알아보면 되겠다. 다음의 표에서 엔트리 안에 이미 존재하는 이벤트의 종류와 그 이벤트에 맞는 미리 사전 정의된(약속된) 콜백함수([3.1장 콜백함수 참조](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-5) )의 목록을 확인할 수 있다. 이벤트 종류 블록코딩용 블록 텍스트코딩용 콜백함수 화면에 시작하기 버튼을 클릭했을 때 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F5eAp9PMtFN6jzdAZ9v38%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D2dfc7ae1-2edd-4675-aacd-80e251674d0e&width=300&dpr=3&quality=100&sign=fc2fa46d&sv=2) def when\_start(): 키보드에 키를 눌렀을 때 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F0vtkeS6tvI3Ttj1MN4vd%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D1e4b7053-d35b-4a58-bf3b-2c3004a49094&width=300&dpr=3&quality=100&sign=3e4a7455&sv=2) def when\_press\_key("q"): 마우스 클릭했을 때 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F0Qb0wtZgaTTKaU5Zv00h%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D028a6b88-7720-4b68-a35d-909761758af8&width=300&dpr=3&quality=100&sign=14c1c84a&sv=2) def when\_click\_mouse\_on(): 마우스 클릭을 해제했을 때 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F3KZmx4VTnIFqlEzHdx7S%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3Dffcf58dc-9d7b-4e92-9b52-fb1941e90849&width=300&dpr=3&quality=100&sign=14e1f658&sv=2) def when\_click\_mouse\_off(): 오브젝트 클릭했을 때 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FnVJrRDkXPCLqd0iEu247%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D01637141-9395-4ab0-a0ea-013fb727e752&width=300&dpr=3&quality=100&sign=c486174&sv=2) def when\_click\_object\_on(): 오브젝트 클릭을 해제했을 때 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FJEGLWjCObltehiJHTHi4%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3Dfd4f9b86-fe01-43e2-be4f-42c55c3a91dc&width=300&dpr=3&quality=100&sign=f5ac99e2&sv=2) def when\_click\_object\_off(): 새로운 장면이 시작되었을 때 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F8PhPTfeusMlTBZo9L64J%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3Df72f86fd-c914-4edb-a022-eb371c349c68&width=300&dpr=3&quality=100&sign=2e8c32f1&sv=2) def when\_start\_scene(): [Previous4.2 절차 지향형 (Procedural)chevron-left](https://jjlee.gitbook.io/entry-python/paradigm/4.2-procedural) [Next4.4 객체 지향형 (Object-Oriented)chevron-right](https://jjlee.gitbook.io/entry-python/paradigm/4.4-object-oriented) Last updated 2 years ago --- # 3.7 무작위 수(Random) | 엔트리-파이썬 무작위 수(또는 램덤)을 활용하는 것은 단순히 한번의 함수호출이라서 지금까지 내용을 잘 숙지해 따라온 사람에게는 하나도 어려울 것이 없다. 그래서, 이번 장에서는 단순하면서 재미있는 게임하나를 무작위 수 기능을 활용해 만들어 보자. 게임제목은 "내 마음 속의 숫자를 맞춰봐!" 이고, 컴퓨터가 1~50까지의 무작위 수 하나를 생각할 것이고, 우리는 그 수가 무엇인지 최대한 적은 회수의 시도 안에 맞추면 되는 것이다. 게임의 흥미를 높히기 위해 우리가 정답을 맞추기 위해 시도한 답변숫자가 정답에 얼마나 가까운지에 대한 힌트를 지속적으로 주도록 만들었다. 실행결과 블록코딩 엔트리-파이썬 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FuRtmHop7tgM0AixspQDt%252Fimage.png%3Falt%3Dmedia%26token%3D1050db26-9f19-4c3e-8deb-7d048e8480d1&width=768&dpr=3&quality=100&sign=93a5c822&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FN6Bhy2RK8TYKT5G2kijn%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D498993b0-2807-4d9c-868e-652a5f0dbca4&width=768&dpr=3&quality=100&sign=ab8d2132&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry com_num = 0 my_num = 0 try_total = 0 def when_start(): com_num = random.randint(1, 50) while True: Entry.input("마음 속으로 1부터 50까지의 수를 생각했으니, 한번 맞춰봐! 몇 번만에 맞출 수 있을까?") my_num = Entry.answer() try_total += 1 if my_num == com_num: Entry.print("Bingo! " + try_total + " 번 만에 찾아냈군!") Entry.stop_code("all") else: if my_num < com_num: Entry.print(my_num + " 보단 더 높은 수야") else: Entry.print(my_num + " 보단 더 낮은 수야") Entry.wait_for_sec(2) 🔢 9번 라인에서 무작위 수를 만드는 randint 함수를 사용하였다. 해당 함수는 random이라는 모듈(또는 라이브러리) 안에 존재하기 때문에 오리지널 파이썬이라면 해당 모듈을 불러드려야 하기 때문에 import random 이란 코드가 코드 맨 상단에 추가되어야 함에도 불구하고, 엔트리-파이썬에서는 이러한 import 하는 과정없이도 해당함수를 직접 호출하여 사용하고 있다. _함수에 넘겨질 값은 어떤 수부터 어떤 수까지의 사이의 무작위 수 인지를 정확히 요청하기 위해 1~50 사이의 숫자란 의미로, ranint(1, 50)의 시작과 마지막 값, 두 값을 함수의 인자값(아규먼트)들로서 넘겼다._ 🔢 14번 라인에서 try\_total += 1 이라는 구문에서 낯선 연산자 '+=' 가 등장했는데, 사실은 이 연산의 사용은 이전 장의 리스트 사용예제에서 총합계를 구하는 식에서 이전 합계에 현재 값을 누적해 더해가는 코드와 동일한 코드이다. 따라서, 그 예제처럼 **try\_total = try\_total + 1 이라고 적어도 동일하게 동작하고, 이 코드를 간소화해서 try\_total += 1 이렇게 적어도 동일한 의미가 된다. 즉, 이전의 try\_total값에서 연속적으로 1씩 더해나가라는 의미**가 된다. 그밖에 나머지 코드들은 이미 배운 것으로 크게 이해에 어려운 것이 없을 것으로 예상되어, 추가적인 설명은 필요 없을 것 같고, 이제 이 게임을 더 재미있게 발전시켜 보는 것은 여러분의 과제로 남겨본다. [Previous3.6 리스트 (List)chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax/3.6-list) [Next3.8 함수 (Function)chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax/3.8-function) Last updated 10 months ago --- # 5.5 팔 벌려 뛰기 횟수를 세어봐요 - 변수 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 우리는 엔트리 블록코딩에서 오브젝트들의 에니메이션을 일상적으로 활용해봤었다. 파이게임제로에서도 그렇게 할 수 있을까? 당연히 가능하다. 게임에서 애니메이션은 일상과 같기 때문이다. 이번 예제에서는 Actor객체의 에니메이션을 처음으로 사용해 보겠다. Copy from pgzhelper import * WIDTH = 480 HEIGHT = 270 boy = Actor('boy_1', (WIDTH / 2, HEIGHT / 2)) boy.images = ['boy_1', 'boy_2', 'boy_3'] times = 0 pressed = False def draw(): screen.fill('white') boy.draw() game.time.sleep(0.5) screen.draw.text('Times: ' + str(times), (20, 20), color='black') def update(): global times, pressed if pressed: if boy.next_image() == 0: times += 1 pressed = False def on_mouse_down(): global pressed pressed = True 🔢 7번 라인에서 images 라는 객체속성이 등장했다. 이름에서 알 수 있듯이, 여러 이미지들을 리스트 형태로 저장해 놓고 있는데, 목적은 오브젝트의 이미지 애니메이션을 위한 것이다. 리스트에 이미지 3개(boy\_1, boy\_2, boy\_3)은 결국 엔트리에서 봤던 팔벌려 뛰는 아이 오브젝트가 갖고 있던 3장의 이미지(모양)와 동일하다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252Fm5nK11T7dsp19dM1Wh9a%252Fimage.png%3Falt%3Dmedia%26token%3D5ff8a223-048f-4f3c-a684-039f6712f278&width=768&dpr=3&quality=100&sign=6b35d1e9&sv=2) 🔢 에니메이션 얘기가 나온 김에 관련 코드를 먼저 검토하면, 엔트리에서 애니메이션을 위해서 각각의 3장의 이미지를 연속 로테이션 시키면서 애니메이션을 생성했던 기억이 있을 것이다. 그때 이미지 간의 이동을 위해 사용했던 블록으로 "다음 모양으로 바꾸기" 라는 블록을 사용했었을 텐데 21번 라인에서 사용한 **next\_image** 라는 함수가 바로 그 블록과 대칭되는 역할을 하는 메소드(함수)이다. 단, Actor 객체의 외관 이미지의 변경에 화면 상의 변경에 관한 내용으로 update() 콜백함수 내에서 이뤄져야 하는 점을 유의하기 바란다. next\_image 메소드를 수행 후 결과로 리턴하는 값은 image 리스트 상에 어느 이미지로 옮겨갔는지 그 위치값(인덱스값)을 결과값으로 돌려준다. 21번 라인은 조건문인데, boy.next\_image() == 0 이라는 조건은 결국, 이미지간의 로테이션이 한바퀴 다 완료하고 다시 맨 처음 이미지(인덱스값이 0)로 돌아왔다면 즉, 3장의 이미지로 구성된 팔벌려 뛰기 모든 부분동작을 다 완료했는지의 여부를 확인하는 것이다. 이때야 말로 현재까지 진행한 팔벌려 뛰기 총 회수를 세기에 딱 적합한 시점이 아닐 수 없다. 총 회수는 9번 라인에서 선언한 전역(global) 변수인 **times** 변수에 누적 합산해 놓는다. 🔢 10번 라인에서 선언한 pressed 라는 전역변수의 용도를 이해하겠는가? 마우스 버튼을 누를 경우에만(27번 라인에 의해서 pressed 값이 True가 된 경우에만) 애니메이션을 진행시키기 위한 목적이다. 따라서, 애니메이션이 종료된 후에는 23번 라인처럼 다시 False로 초기화 해 놓는 것이 필요하다. 그래야만, 다시금 마우스 버튼 눌림에 따른 애니메이션을 계속 이어나갈 수 있기 때문이다. 🔢 마지막으로 16번 라인의 **text** 라는 메소드가 있는데, screen 객체 안에 다시 draw 객체 안에 위치한 함수(메소드)라서 screen.draw.text 라는 계층적 호출구조를 갖고, 이 메소드은 사용법은 넘겨진 인자(argument) 값들에서 유추할 수 있듯이 화면상에 어느 위치에 어떤 내용의 텍스트를 어떤 텍스트 색상으로 출력하라는 값을 전달하여 사용할 수 있다. 여기서 출력할 값으로 넘겨진 'Times: ' + str(times) 라는 구문은 새로운 것은 아니고, 기존에 [엔트리-파이썬 책](https://jjlee.gitbook.io/entry-python/basic_syntax/3.2-input-output) 에서 여러 차례 사용해 본 것으로 '+' 연산자를 기준으로 좌우의 두 텍스트값(문자열형 string값)을 결합해 다시 하나의 텍스트로 만들어 준다. 그리고, 중간에 str(times) 라고해서 **str** 이란 내장함수를 사용했는데, times 변수값이 숫자값이라서 그 값을 텍스트 결합에 그대로 쓸 수 없고, 결합 전 문자열형으로 사전 변환이 필요해서 사용한 것이다. 🔢 참고로 15번 라인의 **game.time.sleep()** 메소드는 이 앱의 구현상 반드시 필요한 것은 아니나 팔벌려 뛰기동작 애니메이션이 너무 빨리 진행되는 것을 늦춰 동작을 자세히 구분해 보기 위한 것으로 이미지 프레임 사이에 0.5초의 기다림을 준 것이다. 이미 우리는 기존 장들에서 배운 사전 지식들이 쌓여있어서 새롭게 등장한 것들 위주로 빠르게 익혀나갈 수 있었다. 끝으로 이 예제의 실행결과를 살펴보는 것으로 이 장을 마무리 하겠다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252F6uP792Jj0h1sGh3yWUkE%252FKapture%25202024-07-25%2520at%252016.24.59.gif%3Falt%3Dmedia%26token%3D5ac57a1f-a829-493d-88a0-898cb8db0e8e&width=768&dpr=3&quality=100&sign=c3471f7b&sv=2) [Previous5.4 나만의 그림판을 만들어요 - 이벤트chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/4-drawing_board) [Next5.6 암호를 입력해 보물상자를 열어요 - 입출력chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/6-treasure_box) Last updated 1 year ago --- # 4.4 객체 지향형 (Object-Oriented) | 엔트리-파이썬 마지막으로 **객체지향형(Object-Oriented) 프로그래밍 패러다임**에 대해 알아보자. 이 패러다임의 등장 이전과 이후로 나뉠만큼 소프트웨어 개발에 가장 큰 영향을 끼친 혁신적 패러다임으로 간주되고 있으며, 특히나 프로그램의 크기가 크거나 여러 개발자들과 협업개발이 필요할 때, 보편적으로 활용되어지고 있으므로 잘 이해하고 알아두는 것이 필요하다. 하지만, 이 개념을 정확히 이해하고 자유자재로 활용하기에는 깊은 이해가 필요한게 사실이고, 깊히 들어가면 들어 갈수록 녹록지 않은 면도 실제 있으니, 그런 깊은 이해까지는 추후에 필요한 시점에 더 찾아서 공부하면 될 일이기 때문에, 엔트리-파이썬에서는 기본적이고 원론적인 개념이해만 확실히 해두어도, 텍스트코딩 시작에 있어서는 큰 자산이 될 수 있다. 이전 장들에서 언급된 패러다임들도 그게 무엇인지 모르게 자연스럽게 익혀온 것처럼 엔트리가 처음 만들어지면서부터 의도했는지 모르겠으나, 우리가 이 패러다임을 어렵지 않게 받아드리도록 잘 계산된 설계가 있었단 생각이 든다. 먼저, 우리가 엔트리에서 코딩을 하기 전 젤 먼저 해야할 일이 무엇이었는지 상기해보자. 그렇다 바로 오브젝트를 최소한 1개 이상을 화면 추가하는 일이었다. 여기서 **오브젝트(Object)**라는 용어를 도입한 것부터 사실상 이 개념을 이해시키기 위한 잘짜여진 각본이 있었단 생각이든다. 엔트리에서는 실행화면에 등장하는 모든게 오브젝트이다. _**그냥 이미지냐 오브젝트냐 아니냐의 구분은 해당 개체에 독립된 코드를 붙혀 조작할수 있는지의 여부**__에 달려있다._ 우리는 엔트리 블록코딩을 시작할 때 각양각색의 오브젝트들(각종 이미지형 오브젝트들(배경 오브젝트 포함), 글상자라고 불리는 텍스트형 오브젝트 등)을 가져다가 화면에 배치하는 일부터 시작했었다. 그리고, 그 배치된 각각의 오브젝트들에게 생명력을 주기 위해 각 오브젝트에게 역할과 활동에 적합한 해당 오브젝트 만의 독립된 코드를 작성하게 되고, 때로는 각 오브젝트들이 서로 협업을 할 필요가 있게되면, 오브젝트들 사이에 어떤 요청을 하는 메시지를 주고 받으면서, 상호 협업하는 구조를 만들면서 전체 프로그램을 만들어가는 식의 코딩을 해왔다. 지금까지 언급된 이러한 일련의 과정이 사실상 바로 객체 지향형 패러다임을 사용한 개발과정이었고, 텍스트코딩 세계에서는 이를 **객체 지향형 프로그래밍(OOP: Object Oriented Programming)** 이라고 하여, 마치 고유명사처럼 부르고 있다. 자 이제 예제를 통해 오브젝트들이 메시지를 상호협력을 위해 메시지를 주고받는 코드를 확인해보자. 실행화면 블록코딩 텍스트코딩 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FmEWqoc1SVgEvIz7yeoIU%252Fimage.png%3Falt%3Dmedia%26token%3Df7594675-1c21-4177-84d6-1af803594a02&width=768&dpr=3&quality=100&sign=e7d2c9de&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FXbnccqTftaHe3E9eMtKy%252Fimage.png%3Falt%3Dmedia%26token%3Da1276779-e32b-4354-9de0-b83fe304dea3&width=768&dpr=3&quality=100&sign=fd333e86&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FuJZgxf44fLQIHgJYMOI2%252Fimage.png%3Falt%3Dmedia%26token%3D88964b58-115f-4817-a639-99faa050b23d&width=768&dpr=3&quality=100&sign=372f0455&sv=2) 사실 실제 텍스트코딩에 기반한 객체지향에서의 협업의 과정은 위와같은 중간매개가 존재하는 메시지전달 방식보다 각각 객체 내에 존재하는 외부 인터페이스 함수들을 외부의 다른 객체에서 직접호출을 통해 이뤄지는게 일반적이다. 지금 이러한 설명은 객체 지향형 패러다임의 깊은 이해를 요하는 내용이라, 이러한 내용을 여기서 자세히 설명하는 것은 본 서의 범위를 넘어서는 것이라 만약, 더 관심이 가는 분들이 계시다면, 객체 지향형 패러다임에 대한 전문서와 함께 특별히 파이썬에서 객체 지향형 패러다임을 어떻게 구현하는지에 대해 꼭 공부해 보기를 추천한다. [hashtag](https://jjlee.gitbook.io/entry-python/paradigm/4.4-object-oriented#undefined-4) 에필로그 --------------------------------------------------------------------------------------------------- 이로서 본 서의 마지막인 이곳까지 달려오신 여러분들의 수고에 박수쳐 드리고 싶고, 텍스트코딩 세계(world)에 본격 입문하신 것도 다시한번 축하드린다. 저자는 여러분들의 텍스트코딩 세계 안에서의 재미있는 여정을 돕기 위해 계속해서 [그 다음 레벨의 책](https://jjlee.gitbook.io/python-pygame_zero/) 을 저술 중에 있다. 그 책도 세상에서 많은 이들의 생각과 사고의 지평을 넓히고 자유롭게 되는데 사용되고 기여할 수 있길 바라고 있다. 2023년 가을의 끝자락에서... 저자올림 [Previous4.3 이벤트 주도형 (Event-Driven)chevron-left](https://jjlee.gitbook.io/entry-python/paradigm/4.3-event-driven) [Next5\. 부록chevron-right](https://jjlee.gitbook.io/entry-python/appendix) Last updated 1 year ago --- # 4.2 절차 지향형 (Procedural) | 엔트리-파이썬 프로그래밍 패러다임 중에 절차 지향형 이라는 패러다임이 있다. 아주 전통적인 프로그래밍에서 가장 먼저 등장했던 패러다임이고, 우리가 알게 모르게 엔트리에서 블록코딩에서도 이미 사용해 왔던 패러다임 중에 하나이다. [3.8장](https://jjlee.gitbook.io/entry-python/basic_syntax/3.8-function) 에서 함수에 필요성과 목적에 대해 잘 이해했듯이 프로그램 안에서 재사용성(Reusability)이 큰 코드덩어리를 함수(Function)로 분리하고 그 함수 호출해 활용해서 코딩해 나가는 방식을 절차 지향형 패러다임이라고 말한다. 과거에 함수는 함수(Function)라는 용어보다 프로서저(Procedure) 또는 서브루틴(Sub-routine) 등으로 불리었는데, 그래서, 절자지향형이라는 말의 영어표현이 Procedural라고 불리고 있다. 절자지향의 예제는 이미 이전 장에 함수에서 배운 예제와 별다를게 없으므로 해당 예제로 갈음하는 것으로 하겠다. [Previous4.1 순차/병렬처리 (Serial/Parallel)chevron-left](https://jjlee.gitbook.io/entry-python/paradigm/4.1-serial-parallel) [Next4.3 이벤트 주도형 (Event-Driven)chevron-right](https://jjlee.gitbook.io/entry-python/paradigm/4.3-event-driven) Last updated 2 years ago --- # 5.2 꽃잎 한 장으로 꽃송이를 만들어요 - 반복 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 이번 장에서는 엔트리 기초책의 두 번째 예제로 '반복' 이란 알고리즘의 개념을 익히기 위한 예제이다. 이전 장에서 우리는 파이게임제로가 제공하는 오브젝트를 활용한 코딩에 대한 기본적이면서 중요한 내용을 이미 섭렵했기 때문에, 앞으로의 남은 예제들에 대한 코딩의 이해는 크게 어렵지 않을 것을 예상할 수 있다. 따라서, 기존 내용을 다시 반복할 필요없이 기존 장에서 배우지 못한 새롭게 등장한 코드에 대해서만 집중적으로 다루도록 하겠다. Copy from pgzhelper import * WIDTH = 480 HEIGHT = 270 leaf = Actor('pink_leaf', (WIDTH / 2, HEIGHT / 2), anchor=('middle', 'bottom')) leaf.scale = 0.5 def draw(): for _ in range(6): leaf.draw() leaf.angle += 60 새롭게 등장한 코드는 6번 라인에서 처음 등장하는데, 기존에 Actor 오브젝트를 생성서 사용해 보지 못한 **anchor** 라는 속성이 등장한다. anchor의 뜻은 배가 움직이지 않도록 위치 고정하기 위해내리는 '닻'이란 의미인데, 그 뜻으로부터 그 목적을 살짝 유추해 볼 수 있다. 정답을 바로 말하면, **우리가 엔트리 블록코딩에서 이미 사용해봤던 각 오브젝트마다 갖고 있는 중심점을 말하는 것으로, 파이게임제로에서는 이를 anchor라고 지칭**한다. 엔트리 때도 그랬지만, **기본적으로 Actor 객체의 디폴트 anchor는 이미지의 한 가운데(center)가 중심이다.** 우리는 필요에 따라 오브젝트의 중심점을 아래 화면에서 보이는 것처럼 오브젝트의 하단중앙(middle-bottom 또는 bottom-middle)으로 옮길 수 있는데, 이 경우 Actor 오브젝트의 anchor 속성에 **anchor=('middle', 'bottom')** 이란 값을 설정하고 오브젝트를 생성하면 되는 것이다. 이 예제에서 왜 중심점이 꽃잎의 한가운데가 아니라, 꽃잎의 하단 가장자리로 옮겨져야 하는지는 이미 엔트리 블록코딩으로 실습하면서 다 이해했을 것으로 믿지만, 간략히 다시 설명하면 꽃잎의 어느 부분을 중심으로 해서 꽃잎이 원모양으로 회전하면서 최종적인 온전한 꽃을 완성하는지에 따라 중심점의 재설정이 필요하다 하겠다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FYeeO5hwLgsV9I6osJDZy%252Fimage.png%3Falt%3Dmedia%26token%3Dd54a043e-330f-4838-b176-5788c074ca88&width=768&dpr=3&quality=100&sign=1047a70&sv=2) 이제 이전에 사용해 보지 못한 Actor 오브젝트의 남은 속성은 하나 더 있는데, 바로 12번 라인의 **angle** 속성이다. 이건 그 이름에서 쉽게 유추할 수 있듯이, 오브젝트를 특정각도로 회전시켜 화면에 출력하기 위한 목적이다. 완전한 꽃송이 형태로 만들기 위해 개별 꽃잎들을 그리기 전 60도씩 회전시켜 총 6회 반복하며 6장의 꽃잎을 통해 최종 원형(360도)의 온전한 꽃을 완성하려는 목적이다. 다만, 유념해 두어야 할 내용은 아래의 그림에서 보는 바와 같이 Actor 오브젝트의 회전방향이 시계방향(CW: ClockWise)이냐 반시계반향(CCW: Counter CloockWise)이냐에 따라서 angle이 양수가 될 수도 있고, 음수가 될 수도 있다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FWyWVXASuFlPmouKbMJiR%252Fimage.png%3Falt%3Dmedia%26token%3D8d3a3bf3-e2a8-47a0-88cb-6af28d157897&width=768&dpr=3&quality=100&sign=3333c2dd&sv=2) 이전 장은 지난 장의 기본 개념을 충분히 숙지했다라면, 이해에 특별히 어려운 점은 없었을 것이다. 마지막으로 최종 실행결과를 보는 것으로 본 장을 마무리 하고, 연이어 세 번째 예제로 넘어가도록 하자. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FLK5zb4yxZJzSJ0iQHtju%252FKapture%25202024-07-17%2520at%252021.41.05.gif%3Falt%3Dmedia%26token%3Db39c85c5-76ac-4c75-b0b8-e785367e7b33&width=768&dpr=3&quality=100&sign=b22fb3fc&sv=2) 참고로 여러분은 위 코드의 실행결과로써 위와 같이 단계적으로 한장씩 그려지는 결과를 보지는 못할 것이다. 이렇게 의도적으로 시간차를 두고 그리는 것에 관심을 있는 분들을 위해 참고 코드를 추가로 남겨놓겠다. 여기서 주의할 것은 파이게임제로의 draw 콜백함수(메소드)가 호출된다고 해도, 내부적으로는 즉시 화면에 그려지는 것은 아니므로 일부러**pygame.display.update()** 함수(메소드)를 통해 강제로 즉시 그리도록 하는 코드가 적용되어 있음을 알 수 있다. 다만, 해당 함수사용을 위해서는 pyagme 외부 라이브러리를 **import** 해와야 함을 기억하자. Copy from pgzhelper import * import pygame WIDTH = 480 HEIGHT = 270 leaf = Actor('pink_leaf', (WIDTH / 2, HEIGHT / 2), anchor=('middle', 'bottom')) leaf.scale = 0.5 def draw(): for _ in range(6): leaf.draw() pygame.display.update() game.time.sleep(0.5) leaf.angle += 60 [Previous5.1.3 멍멍이와 야옹이가 인사해요3 - 순차chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.3-dog-cat) [Next5.3 로봇 청소기가 장애물을 만난다면? - 조건chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/3-robot_cleaner) Last updated 10 months ago --- # 3. Hello World 프로그램에서 시작하자 | 파이썬-파이게임 코딩으로 들어가는 첫 관문이다. 항상 그렇지만, 여러분과 함께 pygame의 [Hello Worldarrow-up-right](https://ko.wikipedia.org/wiki/%22Hello,_World!%22_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8) 코드부터 배워보는 것으로 시작해야 할 것이다. 그런데, pygame은 프로그래밍 언어 그 자체가 아니라, 라이브러리이기 때문에 정확하게 우리가 알고 있는 그 Hello World 프로그램을 만들어 보는게 아니라, Hello World 프로그램에 빗대어 pygame 라이브러리를 이용한 코딩을 할 때, 빼놓을 수 없는 가장 최소한의 기본 코드로만 구성된 가장 작은 프로그램을 만들어보는 예제이다. 그런데 아쉽게도 그 가장 기본적인 간단한 코드조차도 생각보다 간단하지 않다. 백문이 불여일견이다. 코드를 이해하기 전에 일단 잘모르더라도 무작정 코드를 복붙(복사하기&붙혀넣기)해서 실행시켜 실행결과를 살펴보자. 먼저, VS Code 에디터를 열고, File 메뉴에서 New File... 메뉴를 선택한 후, 어떤 종류의 파일을 만들지에서 Python 파일을 선택하자. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FgINOrH7Z2HA1VOtAMIzn%252Fimage.png%3Falt%3Dmedia%26token%3D7f43e0dc-4151-4b91-8add-cdc52439b6b5&width=768&dpr=3&quality=100&sign=72b5aa58&sv=2) 그리고, 다음의 코드를 복붙하고, 파일을 저장하기 위해서 File 메뉴에서 Save 메뉴를 선택해(또는 단축키로 윈도우즈는 맥은 Ctrl + S / Cmd + S) 파일 이름과 저장위치를 본인이 원하는데로 정하고 저장시키보자. 참고로 Python 코드파일의 확장자는 .py 라고 붙는 것을 알 수 있다. Copy #1 라이브러리 호출 import pygame import sys #2 라이브러리 초기화 pygame.init() #3 기본환경 설정 # 화면크기 설정 S_WIDTH = 400 S_HEIGHT = 300 screen = pygame.display.set_mode((S_WIDTH, S_HEIGHT)) # 타이틀바 설정 pygame.display.set_caption("My Game") # 화면 업데이트의 최대속도 설정 FPS = 60 clock = pygame.time.Clock() #4 메인루프 while True: # 이벤트 처리 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() pygame.display.update() #화면 업데이트 clock.tick(FPS) # 게임속도 조절 그럼, 이제 이 코드를 실행시켜봐야 되는데, 에디터의 상단우편에 플레이 아이콘(►) 버튼을 눌러(마치 우리가 엔트리 블록코딩에서 '시작하기' 버튼을 눌렀던 것처럼) 실행실킬 수 있다. 만약, 이 버튼이 보이지 않는다면, [1장](https://jjlee.gitbook.io/python-pygame/dev_env#extention) 에서 VS Code의 Python관련 익스텐션(extention)이 제대로 설치되었는지 재확인하자. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FnECMb5Y9L1J33KzCFh1p%252Fimage.png%3Falt%3Dmedia%26token%3D21194b34-ee9b-4158-bb1c-ce19c4a3d79a&width=768&dpr=3&quality=100&sign=3c2dea85&sv=2) 이제 실행의 결과로 이러한 아무 내용도 출력되지 않는 검은색의 400x300픽셀 화면크기를 갖는 My Game이란 이름을 가진 앱이 하나 정상적으로 실행되었는가? ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FQ5Hioblvy9cyx59RF1hv%252Fimage.png%3Falt%3Dmedia%26token%3D719f490b-ce0c-435d-87f2-b7b52ca73acd&width=768&dpr=3&quality=100&sign=852bce25&sv=2) 맥 OS에의 실행결과 여기까지 잘 진행되었다면, 그럼 다음장에서 이제 실제 저 코드의 의미가 무엇인지 하나하나 자세히 분석을 시작해보자. [Previous2\. 파이게임(pygame) 라이브러리chevron-left](https://jjlee.gitbook.io/python-pygame/pygame_lib) [Next4\. 파이게임의 기본구조 익히기chevron-right](https://jjlee.gitbook.io/python-pygame/boilerplate) Last updated 1 year ago --- # 1. 개발환경 구축 | 파이썬-파이게임 모든 프로그래밍 언어 배우는 책에서 가장 먼저 등장하는 챕터가 아닐까 한다. 왜냐면 제목에서 유추할 수 있듯이 코딩을 시작하기 위해 사전에 기본적으로 설치될 프로그램들이 존재한다는 의미이고, 이를 설치(Install)하고 설정(Configuration)한 이후에야 본격적인 코딩이 가능하기 때문이다. 일반적으로 이 과정에는 크게, 2가지가 필요하다. 첫번째는 사용하고자 하는 프로그래밍 언어(여기서는 파이썬) 그 자체를 설치하는 일이고, 두번째는 이제 그 설치된 언어로 컴퓨터와 소통하기 위해 의미있는 문장과 글을 써내려가는 일(우리는 이 과정을 코딩(또는 프로그래밍)이라 함)을 쉽고 편하게 도와 줄 도우미 프로그램인 코드 편집기(텍스트 에디터: Text Editor) 또는 통합개발환경(IDE: Integrated Development Environment)이라는 프로그램을 설치하는 일이다. [hashtag](https://jjlee.gitbook.io/python-pygame/dev_env#undefined) 파이썬 언어 설치 ---------------------------------------------------------------------------------- 각 과정을 이제 차근차근히 진행해 보자. 먼저, 파이썬을 설치해야 하는데, [파이썬 언어 공식 언어 사이트arrow-up-right](https://www.python.org/downloads/) 를 방문해서 본인의 컴퓨터 환경(컴퓨터의 운영체제는 무엇이며, 몇 비트방식인지)에 맞는 설치 프로그램을 다운로드 받아 설치하면 된다. 실 세계의 인간언어도 그렇지만, 죽은 언어가 아니라면 언어는 한번 만들어지면 끝이 아니라, 추가적으로 계속 새로운 기능과 그에 따른 새로운 용어와 문법이 도입되면서 주기적으로 계속 성장해 나간다. 즉 파이썬 역시도 버전업이 지속적으로 있다는 말이고. 따라서, 그냥 현 시점에서의 가장 최신 버전을 설치하면 된다. (2023년 9월 기준으로 최신버전은 3.11.xx) ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FAmRaiLnRTGx1rZzG4Ate%252Fimage.png%3Falt%3Dmedia%26token%3De6b66da2-3d7a-4af2-b53f-fcbd8f17cf18&width=768&dpr=3&quality=100&sign=e3067922&sv=2) **설치 시 주의 할게 하나 있는데**, 아래와 같이 설치화면에서 "Add python.exe to PATH" 체크박스의 체크(마킹) 잊지말고 마킹한 후에 설치를 진행하기 바란다. 이를 빼먹게 되면, 추후에 파이썬 라이브러리를 설치하기 위한 도구(PIP)사용에 있어서 불편함을 겪게 되기 때문이다. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FBquametY5rVezEe5zNss%252Fimage.png%3Falt%3Dmedia%26token%3Da0bcb6b8-767d-4ea9-85db-99cd27b59ace&width=768&dpr=3&quality=100&sign=1ad69bee&sv=2) [hashtag](https://jjlee.gitbook.io/python-pygame/dev_env#undefined-1) 코드 편집기 설치 ------------------------------------------------------------------------------------ 두번째로는 앞에서도 언급한 코드 편집기 또는 통합개발환경(IDE) 프로그램 둘 중에 하나를 선택해 설치할 차례다. 이름에서 유추할 수 있듯이 간단한 문서편집기 보다는 후자가 더 코딩하는데 있어 많은 도움을 줄 것 같은(?) 이름처럼 느껴지는데 일반적으로 후자 더 선호되는 편이며, 이러한 목적의 프로그램의 종류는 워낙 다양해서 본인에게 선호되는 것을 택하면 되는 정답이 없는 것인데, 초보자들에게 너무 많은 선택의 다양성은 당장의 어려움을 야기하므로 가장 무난하면서도 많은 개발자들로부터 사랑받고 꾸준히 사용되는 검증된 것을 추천하겠는데 [비주얼 스튜디오 코드arrow-up-right](https://namu.wiki/w/Visual%20Studio%20Code) (Visual Studio Code, 이하 VS Code)라는 준 IDE급 범용 코드 편집기이다. 최초 마이크로소프트에서 만들었고, 여저히 마이크로소프트에서 리더십을 갖고 관리하고 있으며 오픈 소스(Open Source) 로 제공하고 있기 때문에 무료사용이 가능하여 구입에 대한 부담이 없고, 전 세계 수많은 사용자로 인해 여전히 계속 성장하고 있는 강력한 생태계를 보유했기 때문에 추천하기에 무리가 없다고 여겨진다. circle-info **IDE(Integrated Development Environment)**: 하나의 프로그램 안에서 소프트웨어 개발과 관련된 모든 기능을 제공하는 환경을 가진 프로그램. 소프트웨어 개발에 관련된 모든 작업이라고 하면 기본적인 코드 편집, 컴파일(Compile), 디버그(Debug), 빌드(Build), 바이너리 배포(Binary deployment), 다른 개발자와의 공동 개발작업 등을 말한다. 유명한 제품으로 마이크로소프트의 비주얼 스튜디오, 구글의 안드로이드 스튜디오, 파이썬 전용 IDE로는 PyCharm 등이 있다. 설치를 위해 [비주얼 스튜디오 코드 공식 사이트arrow-up-right](https://code.visualstudio.com/) 를 방문해 파이썬 언어 설치 때와 마찬가지로 본인의 컴퓨터 환경(컴퓨터의 운영체제는 무엇이며, 몇 비트방식인지)에 맞는 현 시점의 가장 최신의 설치 프로그램을 다운로드 받아 설치하면 된다. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FUvPh3WzjAAiMWmIHvck1%252Fimage.png%3Falt%3Dmedia%26token%3Dbaff53aa-4133-4e31-8ad6-552f5c8fe22b&width=768&dpr=3&quality=100&sign=40be010a&sv=2) ### [hashtag](https://jjlee.gitbook.io/python-pygame/dev_env#extention) 코드 편집기 내 (기능)확장(Extention) 설치 VS Code 의 강점은 범용 코드 편집기로서 특정 프로그래밍 언어에서만 쓸 수 있는게 아니라 모든 종류의 프로그래밍 언어의 코딩에서 활용될 수 있게 해주는 매우 유연한 확장성을 갖고 있는데, 때문에 어떤 프로그래밍 언어를 사용하고자 할 때, 딱 그용도에 맞는 커스텀화 된 맞춤형 환경(흡사 준 IDE)으로 손쉽게 탈바꿈 시켜준다. 이를 위해서는 우리는 VS Code에 결합시켜 활용할 수 있는 별도의 특별한 작은 추가적인 프로그램(일종의 플러그인, VS Code에서는 이를 (기능)확장(Extention)이라고 부름)을 추가로 설치해야 한다. 이 익스텐션들은 프로그래밍 언어별로 존재하는데 우리는 파이썬을 사용할 예정이므로 파이썬 언어 전용 익스텐션들을 설치해 코딩과 개발의 편의성을 높여보자. 설치를 위해서는 아래 화면에서 보이는 맨 좌측의 사이드바(Sidebar) 레고브릭같은 아이콘을 눌러 익스텐션 관리 메뉴로 들어간다. 이후 검색창에서 "python microsoft" 라고 입력해 우리가 원하는 익스텐션을 찾을 예정인데, 이 의미는 파이썬 전용 익스텐션이면서 마이크로소프트에서 직접제작한 것을 찾아달라는 의미이다. 그러면, 다음과 같은 설치가능한 파이썬 익스텐션들이 검색되어 나타나는데, 우리는 그 중에서 Python, Pylance 익스텐션 2개만 설치한다. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FzwFGxZ5weMGkQQlrxUF2%252FScreenshot%25202023-09-15%2520112502.png%3Falt%3Dmedia%26token%3D8ce0e393-5acb-4d22-b5a3-6e7bf2a7bd34&width=768&dpr=3&quality=100&sign=f2c6f664&sv=2) 이로써 우리는 코딩을 하기 위한 사전 준비를 마쳤고, 이제 다음 시간부터는 실제 코딩을 통해 게임제작하며 파이썬 언어를 익혀보자. [Previous게임제작하며 프로젝트 기반으로 파이썬 배우기 (매운맛)chevron-left](https://jjlee.gitbook.io/python-pygame) [Next2\. 파이게임(pygame) 라이브러리chevron-right](https://jjlee.gitbook.io/python-pygame/pygame_lib) Last updated 1 year ago --- # 2. 엔트리-파이썬의 시작 | 엔트리-파이썬 [hashtag](https://jjlee.gitbook.io/entry-python/start#transform) 엔트리 (블록 코딩) <-> 파이썬 (텍스트 코딩) 변환 ----------------------------------------------------------------------------------------------------- 이미 엔트리 사용자라면 엔트리에 대한 사전 지식을 통해서든 엔트리 작동법을 탐구 중 우연히 해당 기능을 발견했을 수도 있을 것이다. 엔트리 상단 메뉴바에서 코드변환 아이콘을 눌러 두 코드 간의 전환을 시도 하면 된다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FWYrxAb8ht0bbij8z9rz6%252Fcode_chg_menu.png%3Falt%3Dmedia%26token%3D4ec174ed-7923-4081-b03a-2a61f95aa514&width=768&dpr=3&quality=100&sign=427445c3&sv=2) 이처럼 엔트리는 나의 블록코딩의 언어가 텍스트 코딩언어(파이썬)로는 어떻게 되는지를 코드 변환을 통해 확인해 볼 수 있을 뿐만 아니라, 엔트리-파이썬 코딩 에디터 창을 통해 직접 텍스트 코딩을 하고 이를 실행해 볼 수 있는 기능까지도 제공한다. 이러한 "엔트리-파이썬"은 학습자나 교사가 엔트리에서 "블록 코딩에서 시작해 텍스트 코딩까지 익히기" 라는 목표를 이룰 수 있도록 그 중간을 매개하는 연결다리와 같은 역할이라 할 수 있을 것이다. [hashtag](https://jjlee.gitbook.io/entry-python/start#difficulties) 블록코딩에서 첫 텍스트코딩으로 넘어오면서 겪는 경험 ----------------------------------------------------------------------------------------------------- 일반적으로 블록코딩에서 첫 텍스트코딩으로 넘어오면서 겪는 큰 당혹감은 기존에 블록코딩에서처럼 단순히 블록 카테고리에 위치한 블록들을 코딩에디터(코드조립소) 영역으로 옮겨 놓으면서(Drag & Drop) 조립하면서 의미있는 블록더미를 만드는 이해하기 쉽고 직관적이고 편리한 방식으로 코딩할 수는 없고, 대신에 마치 긴 글을 작문하듯이 글자 하나하나를 일일이 수많은 텍스트 타이핑을 하면서 코딩해 나가야 한다는 것이다. 이때 우리는 이러한 코딩과정에서 기존 블록코딩에서는 경험해 보지 못한 에러(error) 상황들을 마주치게 되며, 그 사소해 보이는 에러(또는 오류)가 내가 코딩한 프로그램 전체를 아예 실행할 수 없게 만든 것은 다소 당혹스럽게 느껴지기 까지 하다. 왜냐면 기존 블록코딩에서는 코딩을 잘못할 경우, 프로그램이 오동작 하는 정도이지 아예 전체가 실행이 불가하지는 않았기 때문이다. **에러는 타이핑한 텍스트가 오타인 경우엔 이해가 되나 오타라기 보다 겨우 알파벳 대소문자 정도의 차이, 점 하나를 찍고 안찍고의 차이임에도 불구하고, 프로그램을 실행하려고 하면 그것을 에러라고 내뱉으며 프로그램 자체를 아예 실행할 수 없게 된다. 텍스트 코딩은 그런 면에서 매우 주의깊고 정확성 있는 코딩이 필요하고, 그 외에도 내가 코딩하는 내용 자체가 해당 코딩언어가 요구하는 문법 체계와도 맞는지까지 한번 더 주의를 살펴 코딩해야 한다.** _이러한 면 때문에 처음에 겪는 당혹감은 누구에게나 당연한 것이고, 다만, 이러한 코딩방식에 적응되고 익숙해지기 까지는 모두에게 어느 정도의 시간이 필요한게 사실이다._ [hashtag](https://jjlee.gitbook.io/entry-python/start#drag-and-drop) 엔트리-파이썬에서 드래그엔드롭(Drag&Drop) 방식으로 코딩하기 --------------------------------------------------------------------------------------------------------------- 엔트리-파이썬에서는 위에서 언급한 텍스트 코딩시 겪게 되는 타이핑 오류로 인한 실수를 막을 수 있는 방법의 아이디어로 기존 블록코딩에서의 코딩방법을 차용해 그림에서처럼 드래그엔드롭 방식으로 코딩을 시도해 볼 수 있다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F45CO9UhlHWGQCfCzrYdG%252Fdrag%2526drop.png%3Falt%3Dmedia%26token%3Da72a3895-a840-4d6f-8392-3562cbf5b0e3&width=768&dpr=3&quality=100&sign=649a599e&sv=2) 이러한 방법이 단순 타이핑 실수로 인한 에러를 줄여주는 효과와 키보드 타이핑하는 시간자체를 줄여주는데는 편리함이 있지만, 그렇다 할지라도 위에서 언급한데로 텍스트코딩시 사전에 필수로 알아야 하는 해당 코딩언어의 문법체계는 사전에 잘 알고 있다는 전제를 가졌을 때에만 활용할 수 있는 코딩방법이라고 할 수 있다. 그러므로 이제 우리가 우선적으로 해야할 일을 파이썬 언어의 기본 문법체계를 익히는 일부터 시작해야 할 것이고, 다음 징부터 이를 하나씩하나씩 차근차근 배워보도록 하겠다. [Previous1\. 엔트리 vs 엔트리-파이썬chevron-left](https://jjlee.gitbook.io/entry-python/whatis) [Next3\. 엔트리-파이썬으로 파이썬 언어의 기본문법을 익히자chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax) Last updated 10 months ago --- # 1. 파이게임 제로(pygame zero) 라이브러리 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 우리 책에서 파이썬 텍스트코딩으로 게임을 만들기 위해 사용할 게임개발용 라이브러리를 소개하겠다. 혹시 라이브러리가 무엇인지에 대해 기억이 가물가물 하다면 이전서 "[엔트리-파이썬](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) " 로 돌아가 잠시 관련 내용을 살펴보고 오길 바란다. _**라이브러리 목적이 모두에게 항상 필요한 코드들을 미리 잘 만들어 놓고, 모두가 함께 그 코드들을 활용해 빠르게 효과적으로 코딩하게 하기 위한 것**_처럼 게임개발에서도 그러한 목적의 라이브러리들이 존재하고, 우리는 그 중에서 파이썬 언어를 활용한 게임개발에 가장 널리 사용되는 파이게임(PyGame)을 기반해서 만든 파이게임제로(PyGame Zero)를 사용하겠다. [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/pygame_zero_lib#pygame) 파이게임(pygame) 라이브러리 ---------------------------------------------------------------------------------------------------------- 2000년에 첫 출시된 이래로 벌써 20년 이상 서비스되고 여전히 개선되고 있는 "오픈소스 게임개발용 라이브러리" 이다. 참고로 **"오픈소스" 라는 말은 모두에게 유익이 되도록 특정한 조건을 전제로 코드의 무료 사용을 허락한다는 것이다.** 무료 사용이라는게 중요하데, 그 말은 반대로 항상 돈을 내고 써야하는 상업용 라이브러리도 존재한다는 것이다. 해당 라이브러리의 로고는 다음과 같다. 좀 더 상세하고 전문적인 내용을 알고 싶다면 [공식 웹사이트arrow-up-right](https://pygame.org/) 에서 확인해 보는 것이 좋겠다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2F6s7mixJ5fl3qJx527Ek0%2Fimage.png&width=768&dpr=3&quality=100&sign=9d51ae82&sv=2) 파이게임 로고 [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/pygame_zero_lib#pygame-zero) 파이게임 제로(pygame zero) 라이브러리 ----------------------------------------------------------------------------------------------------------------------- 그러나, 안타깝게도 파이게임 라이브러리를 통한 개발은 파이썬 초보자 입장에서 여전히 쉬운 편은 아니다. 그렇기 때문에 초보자의 교육적 배려를 위해 더 장벽을 낮춰야 할 필요가 있는게 사실이었고, 그런 목적으로 탄생하게 된 것이 "파이게임 제로" 라이브러리다. 파이게임 제로(Pygame Zero) 라는 이름에서 알 수 있듯이 _기존의 "__**파이게임" 라이브러리를 기반해 이를 더 간략화시켜 최소한의 코딩만으로도 게임제작이 가능하도록 난이도를 낮춘 파이게임 라이브러리**__다. 역시_ [_오픈소스_arrow-up-right](https://github.com/lordmauve/pgzero) _로 공개되어 있으며, 이를_ [_공식 웹사이트_arrow-up-right](https://pygame-zero.readthedocs.io/) _에서 밝히고 있듯이 본 라이브러리는 쉽고, 흥미있게 코딩을 가르치기 위한 교육용 목적으로 활용을 기대하고 있다._ ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FEtlQcZoS8hHLhg25ce5e%2Fimage.png&width=768&dpr=3&quality=100&sign=f606951e&sv=2) 파이게임 제로 로고 _이 책은 게임 그 자체를 잘 개발하기 위해 씌여진 책이 아니고, 파이썬 언어를 통한 텍스트코딩을 더 잘 배우기 위한 목적에서 게임개발이라는 수단을 가져온 것이라 이해하면 좋겠다. 우리는 이미 엔트리/스크래치를 통한 블록코딩으로 게임만들면서 코딩에 재미를 붙혀왔던 것처럼 말이다. 이번 우리 목표는 파이게임 제로(pygame zero) 라이브러리 + 파이썬(python) 텍스트코딩 언어의 조합으로 게임만들면서 블록코딩을 넘어선 새로운 차원의 텍스트코딩 세계에서도 코딩의 재미를 느껴보자._ [Previous엔트리 기초책 7개 예제 클론하며 파이썬 배우기chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic) [Next2\. 개발환경 구축chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/dev_env) Last updated 1 year ago --- # 2. 파이게임(pygame) 라이브러리 | 파이썬-파이게임 우리 책에서 사용할 게임개발용 라이브러리를 소개하겠다. 파이썬에서 2D 게임을 만들고자 할 때 사용되는 라이브러리로 2000년에 첫 출시된 이래로 벌써 20년 이상 서비스되고 여전히 개선되고 있는 오픈소스 게임개발용 라이브러리 이다. 이름은 Python용 라이브러리는 의미로 py라는 접두사를 붙힌 pygame라 명명했고, 해당 라이브러리의 로고는 다음과 같다. 좀 더 상세하고 전문적인 내용을 알고 싶다면 [공식 웹사이트 주소arrow-up-right](https://pygame.org/) 를 확인해 보는 것이 좋겠다. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252F3fe68olu2z7KPnMKu2d7%252Fimage.png%3Falt%3Dmedia%26token%3D0b8c9781-3e19-4ee2-aae5-2afdcaf18457&width=768&dpr=3&quality=100&sign=7cff69ca&sv=2) 일반적으로 개임을 개발할 때 이러한 라이브러리의 지원 없이 모든 코드를 제로 베이스(Zero-based)에서 밑바닥부터 코드 한땀한땀 코딩하면서 개발하겠다고 한다면 불가능한 것은 아니지만 어마어마한 시간이 걸릴 것이다. 게임 자체가 종합예술이라 불릴만큼 많은 기술(특별히 수학적, 물리적)들의 적용이 필요하고, 이미지, 소리, 동영상 등 다양한 멀티미디어를 동시에 다뤄야 하기 때문에 코드의 양과 복잡도가 상당히 높은 편이다. 그래서, 그런 세부적 기술구현의 디테일은 모르더라도 간편하게 그런 기술을 활용할 수 있게 해주는 라이브러리가 있다는 것이 우리에겐 구원의 소식이다. 혹시 여러분들 중에 이걸로 상용게임을 만들 수 있지 않을까 하는 기대를 한다면, 아쉽게도 일찌감치 접어두는 것이 좋을 것 같다. 이 라이브러리는 그런 수준이나 용도를 목적하지 않았고, 무엇보다 아직은 파이썬 언어자체가 상용게임 제작용에 사용되는 주류 언어가 아니다. 그럼에도 우리는 이것을 통해 우리 자신과 우리 주변 친구들, 지인정도가 재미있게 즐길 수 있는 가벼운 인디게임 정도는 충분히 만들 수 있을 것이다. 이 책은 게임 그 자체를 잘 개발하기 위해 씌여진 책이 아니고, 파이썬 언어를 통한 텍스트코딩을 더 잘 배우기 위한 목적에서 게임개발이라는 수단을 가져온 것이라 이해하면 좋겠다. 우리는 이미 엔트리/스크래치를 통한 블록코딩으로 게임만들면서 코딩에 재미를 붙혀왔던 것처럼 말이다. 이번 우리 목표는 파이게임(pygame) 라이브러리 + 파이썬(python) 텍스트코딩용 언어의 조합으로 게임만들면서 블록코딩을 넘어선 새로운 차원의 텍스트코딩의 재미를 붙혀보자. [hashtag](https://jjlee.gitbook.io/python-pygame/pygame_lib#pygame) 파이게임(pygame)의 설치 ----------------------------------------------------------------------------------------- 설치는 간단하다. 맥 OS 환경이라면 터미널, 윈도우즈 OS 환경이라면 명령 프롬프트(Command prompt)를 연 후에 다음의 명령어를 입력하여 설치하자. > pip install pygame **PIP 라는 파이썬 언어 자체적으로 갖고 있는** [**라이브러리**](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) **(이를 표준 라이브러리라 함) 외에 외부(파이썬 언어 설치때 포함된 것이 아닌 추가로 설치가 필요한)의 다른 라이브러리(또는 패키지)를 설치, 삭제, 업그레이드 등을 관리해주는 패키지 관리 프로그램이다.** 우리는 위와같은 단 한줄의 명령어를 입력하면 PIP는 외부 라이브러리인 pygame를 인터넷 상의 파이썬 패키지 저장소 서버에서 다운로드 해와 내 컴퓨터 안에 설치된 파이썬 언어 안에까지 자동 설치 해준다. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FlDk1hG0Mw0HVnjLbiYTX%252Fimage.png%3Falt%3Dmedia%26token%3D4f0cbd78-ad76-4c82-8160-6a29bb7b34b6&width=768&dpr=3&quality=100&sign=1c351ef5&sv=2) _만약, pip 프로그램을 찾을 수 없다는 오류가 발생한다면,_ [_이전 장에서 파이썬 설치_](https://jjlee.gitbook.io/python-pygame/dev_env#undefined) _시에 주의사항으로 언급한 설치화면에서 "Add python.exe to PATH" 체크박스를 체크(마킹)하고 설치했는지 다시한번 확인해 보자._ 설치가 완료되었다. 우리는 이제 코딩을 시작할 진짜 준비가 완료되었다. 다음 장부터 이제 본격적으로 코딩을 시작해보자. [Previous1\. 개발환경 구축chevron-left](https://jjlee.gitbook.io/python-pygame/dev_env) [Next3\. Hello World 프로그램에서 시작하자chevron-right](https://jjlee.gitbook.io/python-pygame/hello-world) Last updated 1 year ago --- # 4. 파이게임의 기본구조 익히기 | 파이썬-파이게임 우리가 **라이브러리를 사용할 때 장점도 많지만 그에 따른 제약도 함께 있다는 것을 함께 이해해야 한다. 라이브러리를 사용한다는 것은 해당 라이브러리가 요구하는 어떤 전형적인 코드구조(틀) 안에서 맞춰서 코딩을 해야 한다**는 것이다. _그래서, 해당 라이브러리의 사용법을 익히는 과정의 추가적인 시간과 노력을 요하게 된다는 점도 기억해두어야 하겠다._ 이번 장에서는 pygame 라이브러리의 기본 사용법을 배우도록 하겠다. 파이게임 라이브러리를 활용한 코딩은 크게 다음과 같은 크게 4부분으로 구성된 프로그램 구조의 틀을 갖는다. 이 기본 틀을 잘 기억해 두어야 하는데, 왜냐면 항상 이 기본 틀 안에서 코딩하게 될 것이기 때문이다. 프로그램의 기본구조 기본구조의 실제 코드 ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252Fk7DNANZDvoJVrSI8DqqP%252Fimage.png%3Falt%3Dmedia%26token%3Df5416802-09e7-45ec-b6f1-ce2d088c6f1d&width=768&dpr=3&quality=100&sign=9672b587&sv=2) 출처: [ai-creator 블로그 arrow-up-right](https://ai-creator.tistory.com/522) Copy #1 라이브러리 호출 import pygame import sys #2 라이브러리 초기화 pygame.init() #3 기본환경 설정 # 화면크기 설정 S_WIDTH = 400 S_HEIGHT = 300 screen = pygame.display.set_mode((S_WIDTH, S_HEIGHT)) # 타이틀바 설정 pygame.display.set_caption("My Game") # 화면 업데이트의 최대속도 설정 FPS = 60 clock = pygame.time.Clock() #4 메인루프 while True: # 이벤트 처리 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() pygame.display.update() #화면 업데이트 clock.tick(FPS) # 화면 업데이트 속도 조절 4개 구성의 실제 코드분석을 시작하자. 위에 \[게임구조의 실제 코드\] 라는 탭을 선택해 코드화면을 열어보자. 크게 4개의 구성을 구분하기 위해 주석에 part 1~4까지 넘버링을 붙혀놨다. 🔢 **part 1**은 2~3번 라인으로 젤 먼저 해야할 일은 코딩을 위해선 우리가 사용할 라이브러리를 불러오는게 필요하며, **import**라는 키워드를 통해 라이브러리를 호출하는데, 2개(**pygame**, **sys**)의 라이브러리를 불러드리고 있다. pygame이 필요한 것은 당연하며, sys 라이브러리는 왜 필요할까? 이것은 27번 라인에서 사용하는 exit 함수 때문에 필요하다. **exit** 함수(혹시 함수 자체가 무엇인지에 관해 사전지식이 없는 분들은 저자의 [이전 저서의 함수파트](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) 를 참고하기 바람)는 우리가 만든 게임 프로그램 자체를 완전히 안전하게 종료하는 용도이다. 🔢 **part 2**에 5번 라인은 파이게임 라이브러리의 내부적인 초기화 목적으로 라이브러리를 불러오는 동시에 단 일회성 호출을 요구한다. 이것은 라이브러리 활용시 응당 그렇게 사용해야 한다는 라이브러리 자체가 요구하는 요구사항이기 때문에 사실상 우리는 고민없이 요구사항에 맞춰 해당 초기화 목적 **init** 함수를 부르면 그만이다. 그럼에도 불구하고 이러한 과정이 왜 필요한가를 유추해 본다면, 기본적으로 라이브러리는 다시 수많은 모듈로 구성되어 있고, 그러한 각각 모듈(Module)들이 각각 잘 초기화되어야 이후 원활한 동작을 보장할텐데 그런 차원에서 호출을 요구한다고 볼 수 있다. 🔢 **part 3**은 7~18번 라인으로 우리 게임의 기본환경(게임화면의 크기, 게임의 타이틀, 게임화면 업데이트 최대속도 등)을 정해야 한다. 각 라인별로 더 쪼개서 자세히 살펴보자. 9~11라인은 게임화면의 크기를 정하는 코드로, 9, 10번 라인은 우리 게임이 실행될 화면크기(픽셀단위)의 가로길이와 세로길이(여기서는 가로 400픽셀, 세로 300픽셀)를 결정한 후 이를 각각의 2개의 변수(**S\_WIDTH**, **S\_HIGHT**)에 담고 있다. _여기서 화면의 가로길이와 세로길이를 일부러 변수를 담아놓아다는 것에 주목하자. 이렇게 하는 의도는 앞으로 해당 값은 변수이름을 통해서 읽어 오겠다는 것이다. 이런 식의 테크닉을 구사하는데는 두 가지 목적이 있다._ 첫째, 화면크기를 변수명으로 표현하지 않고 숫자값 그대로 사용했다고 하면, 당장은 몰라도 시간이 지나 나중에 나 자신 또는 내 코드를 읽게 되는 다른 동료의 코드 가독성(해독성)을 떨어뜨리게 된다. 코드를 해독하기 위해 눈으로 한 라인 한 라인 읽어나가다가 갑자기 등장한 숫자(400과 300)을 보고, 그 숫자값은 도데체 어떤 값을 의미하는 숫자인지 잠시 고민하는 시간이 필요하고, 이렇게 한눈에 한번에 빠르게 읽어나가지 못하고 멈추게 하는 그 지점이 바로 가독성을 떨어뜨리는 지점인 것이다. 그런데, 우리가 이런 식으로 그 숫자값을 화면의 가로와 세로길이라는 의미를 부여한 변수이름(S\_WIDTH, S\_HIGHT)과 매칭해 놓았다면, 우리는 변수이름만 보고([이전 서에서 언급했지만, 그래서 변수이름을 의미있 잘 짓는 것이 중요!](https://jjlee.gitbook.io/entry-python/basic_syntax/3.3-variable) ) 곧바로, 그 숫자값의 의미를 파악할 수 있다. 또 **의도적으로 변수이름을 대문자로 표현한 것도 특징인데, 물론 변수가 변화하는 값을 저장하는 기본적인 목적이 있으나, 이 경우는 그러한 목적보다는 특정값을 대치하는 용도로서의 용도가 더 크기 때문에 그 의미를 부여하기 위해 대문자 표기를 하였다. 참고로 이는 일종의 프로그래머들 사이의 관습적 표현(**[**Conding conventions**arrow-up-right](https://en.wikipedia.org/wiki/Coding_conventions) **)이므로 적극 활용하면 좋을 것이다.** 두번 째는 앞으로 써내려 갈 우리의 코드 안에서 해당 숫자값(여기서는 게임화면의 가로와 세로크기)을 상당히 여러 곳에서 사용할 것이고, 그때마다 해당변수명을 사용해 가로,세로길이값을 읽어오도록 코딩해 놓았다고 생각해보자. 이렇게 할 경우, 장점은 무엇이 있을까? 나중에 어떤 이유에 의해서([이전 서](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) 에서 언급했지만, 소프트웨어 특성상 생각보다 자주 코드수정이 빈번하고) 우리 게임을 두배 더 큰 화면(가로 800픽셀, 세로 600픽셀)으로 실행해야 하는 상황이 되었고, 이에 맞춰 전반적인 코드의 재수정이 필요하다고 가정하자. 우리는 이미 똑똑하게 이러한 가능성을 대비해 해당 값을 변수명을 통해 코딩해 놓았지 않는가? 따라서, 딱 두 값(S\_WIDTH, S\_HIGHT)의 현재 값인 400, 300을 각각 800, 600으로만 바꿈으로써 이러한 코드 수정 요구사항의 빠른 대응이 가능하게 되는 것이다. 이제 11번 라인에서 **set\_mode** 함수를 호출하는 방법을 살펴보자. 해당 함수 이름을 단지 이름으로 호출할 수 없고, 이런식의 **pygame.display.set\_mode** 해당 함수의 위치까지 포함한 표현법으로 호출하고 있다. 이유는 이 함수가 우리가 지금 코딩하는 있는 파일 안에 위치한 우리가 직접 만든 사용자 함수가 아니며, 동시에 파이썬 언어 자체 안에 기본내장된 내장함수도 아니기 때문이다. 따라서, 우리는 이미 [이전 서](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) 에서 배웠듯이 해당 함수가 실제 어디에 위치해 있는지의 위치정보를 나타내는 마침표(.) 표기법을 이용해 호출해야만 파이썬이 실행시 그 함수를 정확히 찾아내어 활용할 수 있게 되는 것이다. set\_mode 함수이름 이전까지의 **pygame.display.** 의 의미는 결국 **set\_mode** 함수는 계층적(hierarchical) 구조 안에 위치해 있다는 것이고, 먼저 최상단의 pygame이라는 라이브러리 안에 있으며 다시 그 라이브러리 안에 있는 **display**란 모듈(module) 안에 위치한다는 것을 의미한다. set\_mode 함수에서 더 알아야 것들이 남아있다. 해당 함수를 set\_mode((S\_WIDH, S\_HIGHT)) 이렇게 호출하고 있고, 함수로 넘겨야 하는 값(이를 아규먼트(argument) 또는 인자값 이라는 용어로 지칭)이 이전 서에서 배우지 못한 데이터의 형태(type)를 띠고 있다. 일단, 함수로 넘기는 인자값은 1개가 아닌 복수개의 2개(화면의 가로와 세로크기)를 한꺼번에 넘기고 있는데, 우리가 이전 서에서 배웠듯이 단 하나의 값이 아니라, 여러 개의 복수의 값을 저장할 수 있는 있는 공간은 무엇이었는지 기억하는가? 그렇다 [리스트(List)](https://jjlee.gitbook.io/entry-python/basic_syntax/3.6-list) 에 담아서 보내면 되겠다라는 생각이 들 것이다. 리스트의 문법형식이 기억나는가? 큰 대괄호 안에 값을 채우면 된다. 즉, 이 경우에 \[S\_WIDH, S\_HIGHT\] 이렇게 값을 채워서 set\_mode 함수로 전달하면 되지 않겠는가? 그렇다, 가능은 하다. 그런데, 이 함수를 만든 사람은 그렇게 말고, 해당 값들을 소괄호()에 담아 보내달라는 함수 이용자인 우리에게 제약을 걸어두었다. 대괄호에 담는 것과 소괄호에 담는 것에 차이는 무엇인가? 점하나도 공백하나도 다 의미가 있는가 있는 프로그래밍 세계에서 괄호종류가 다른 것은 당연히 의미가 달라진다. 소괄호에 데이터들를 담는 것을 우리가 [기존 서에서 이미 배웠는데](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird/5.1_background#tuple) 그렇다 이런 데이터 타입을 튜플(Tuple) 이라고 부른다. 15번 라인에서 기존 코드의 연장선으로 이번에 게임의 이름을 정하는 것으로, **set\_caption** 함수에 우리 게임의 이름값을 문자열(스트링) 타입의 값으로 해서 인자값으로 전달해 설정해주면 된다. 크게 어려운게 없다. ### [hashtag](https://jjlee.gitbook.io/python-pygame/boilerplate#animation) 애니메이션 (Animation) 이제 18-19라인으로 넘어가자. 사실 이 코드는 30번 라인하고 같이 묶어서 봐야 한다. 여기서 새로 등장하는 개념이 있는데 바로 **FPS**(Frames per second, 초당 프레임 출력횟수)다. FPS에서 프레임은 화면에 표시되는 정적 이미지 한장, 한장을 의미한다. 우리가 정지된 이미지를 모아 움직이는 애니메이션을 만들 때를 생각해보면 된다. 정지 이미지에 매 한장(프레임)마다 아주 조금씩의 이미지의 변화를 주고, 이 각각의 이미지를 1초 안에 최소한 30번(인간의 눈의 평균 인지능력 기준으로) 이상을 보여주면, 사용자에게는 정지된 이미지가 아니라 자연스럽게 움직이는 애니메이션처럼 보이게 되는 것이다. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FOk4fUsAmB8m5gTRQ5nki%252Fimage.png%3Falt%3Dmedia%26token%3Deb93b9b1-979d-4289-b353-b44ad756d83d&width=768&dpr=3&quality=100&sign=9d484cd5&sv=2) 출처 : https://theappliancesreviews.com/why-frame-rate-is-25-30-or-29-97-fps/ 따라서, 게임화면에서 자연스러운 애니메이션 효과를 얻기 위해 일반적으로 60fps 속도를 화면갱신의 최대속도로 사용한다. 여기서 최대속도로 한다는 말에 주목하자. 만약 우리가 코드로 FPS를 설정하지 않는다면? 일반적으로 고사양의 성능좋은 컴퓨터에서 우리 게임을 실행한다면 60fps 이상으로 화면을 갱신(업데이트)하게 되는데 60fps면 되는데 그 이상으로 화면 업데이트 하느라 불필요한 컴퓨팅 자원이 낭비될 수 있고, 무엇보다 너무 지나친(?) 업데이트로 인한 오히려 부자연스러운(속도 빨라보이는) 애니메이션이 되게 되고, 게임사용자의 흥미와 조작성을 떨어뜨린다. 이 값을 설정하는 이유는 우리가 나만의 게임을 만들어 주변에 여러 지인들에게 해보라고 나눠줄 수 있고(이런 과정을 배포(deployment)라고 부름), 그렇다면, 결국에는 우리 게임이 각 개인이 가진 다양한 사양(성능)의 컴퓨터에서 실행될텐데 그때 일정한 게임속도를 보장하고자 하는 장치즘으로 이해하면 되겠다. 서두에서 이 코드는 30번 라인의 코드와 연결되어 있다고 언급했는데, 18번 라인은 설정할 FPS값을 변수로 만들어 값을 저장한 것이고, 19번 라인은 화면갱신의 타이밍을 알기위한 시간계산에 사용한 시계를 **time** 모듈 안에 **Clock** 객체([객체에 대한 기본 개념은 이전 서에서 숙지 했음](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.2-dog-cat#undefined-1) ) 생성을 통해 만들어 역시 clock이란 변수에 저장해 놓은 것이고, 30번 라인에서야 그 시계 안에 **tick** 함수를 통해 사전 설정한 FPS 의 화면갱신을 유지하도록 실제적인 속도조절을 하게 된다. 🔢 이제 드디어, 마지막 남은 **part 4**를 이해해 보도록 하자. 우리가 이미 블록코딩에서 게임을 만들 때도 많이 활용해 본 무한루프(무한반복) 구조라 크게 낯설지는 않다. 게임이 종료되기 전까지 우리의 게임은 이 무한루프 안을 끝없이 돌면서 실행하게 된다. 파이썬에서 무한루프를 만들기 위해서 20번 라인의 **while True:** 으로 만들 수 있다는 것은 우리가 [이전 서의 반복](https://jjlee.gitbook.io/entry-python/basic_syntax/3.5-loop) 이라는 장에서 이미 배웠고 어려운게 없다. 게임에서 먼저 고려할 것은 사용자가 언젠가는 게임을 고만하기 위해서 게임 프로그램을 완전 종료하고 싶을 것으로, 우리는 이에 대한 안전한 정상 종료하는 방법을 제공하는 것이다. 이 코드가 24~27번 라인까지의 코드이다. 이전 장에서 실행시켜 본 이 게임앱의 완전 종료는 어떻게 할 수 있는가? 그렇다 우리가 원래 잘 알고 있는 방법인 게임화면 상단의 윈도우 타이블바에 위치한 창을 닫는 x모양의 버튼을 눌러 완전 종료시킬 것이다. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252FQ5Hioblvy9cyx59RF1hv%252Fimage.png%3Falt%3Dmedia%26token%3D719f490b-ce0c-435d-87f2-b7b52ca73acd&width=768&dpr=3&quality=100&sign=852bce25&sv=2) 그런데, 생각해보면 그 x모양 버튼을 누른다고 무조건 자동종료가 되는게 정상인가? 사실은 아니다, 사용자가 그 버튼을 누르게 되면 어떤 동작을 하라(이 경우 프로그램을 정상 완전종료하라)는 코드가 우리 프로그램 안에 씌여져 있기 때문에 그것에 맞춰 동작했던 것 뿐이다. 자 그럼 이 부분을 코딩해 보자. 사용자가 x모양 버튼을 눌렀다는 것은 게임개발하는 우리 입장에서는 어떻게 알 수 있는가? 파이게임 라이브러리 에서는 이를 알 수 있도록 우리에게 제공한 함수가 있다. 24번 라인에 **event**(이벤트, 사건) 모듈 안에 **get** 이라는 함수이고, 이벤트 라는 단어는 어디서 들어본 것 같지 않은가? 우리가 키보드를 누를 때, 마우스를 움직이거나 버튼을 누를 때, 지금과 같이 우리 게임앱의 타이틀바의 버튼을 누를 때 등 이런 모든 하나하나가 다 프로그램 입장에서는 이벤트인 것이고, 이런 이벤트가 발생하는 상황에 맞추어 어떤 동작을 하도록하는 스타일의 프로그래밍 패러다임을 [**이벤트 주도형 프로그래밍 패러다임** 하며 우리는 이전 서에](https://jjlee.gitbook.io/entry-python/paradigm/4.3-event-driven) 이미 살펴본 바가 있다. 그렇다 지금의 코딩이 바로 그 패러다임을 따르는 코딩스타일이다. 24번 라인부터 다시 **for-in** 문를 사용한 반복문에 진입하는데, 우리의 의도는 우리 프로그램에서 발생하는 모든 이벤트를 캡쳐하고, 25번 라인에서는 그 이벤트들 중에서(for-in 문에서 **in** 이 쓰이는 이유) 이벤트의 종류(타입, type)이 만약, **QUIT**(윈도우 타이틀바의 x버튼을 누름)라는 이벤트가 있을 경우엔, 26-27번 라인을 실행시켜 게임을 정상 완전종료 하라는 것이다. 26번 라인의 **quit** 함수는 5번 라인의 init 함수와 쌍을 이루는 것으로 프로그램의 정상종료를 위해 파이게임 라이브러리 내의 전반적인 활동의 종료절차(사용중인 메모리의 해제 등)을 밟도록 지시하는 것이고, 27번 라인의 exit 함수의 목적과 용도는 part 1에서 이미 밝혔다. 30번 라인 코드에 대한 설명은 이미 이루어졌고, 이제 진짜 마지막으로 29번의 코드만 이해하면 이번 장은 마무리 된다. display 모듈 안에 **update** 함수는 사용자가 눈에 보이는 실제 게임화면(위에서 언급된 프레임이라고 볼수 있음) 한장 한장을 사용자 화면에 그려내는 코드이다. 사용자의 눈에 보이는 게임화면이 그려지는 과정은 실제는 아래와 같은 내부적인 동작을 거치게 된다. ![](https://jjlee.gitbook.io/python-pygame/~gitbook/image?url=https%3A%2F%2F10174333-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FXqfaxSWS1mduUmYVCPQP%252Fuploads%252F8itUfJgcursXmj543H5k%252Fimage.png%3Falt%3Dmedia%26token%3D4cab3ac1-1337-415f-95a0-0249bfd1718e&width=768&dpr=3&quality=100&sign=60c63291&sv=2) 출처: [https://www.oreilly.com/library/view/learning-java-4th/9781449372477/ch20s08.htmlarrow-up-right](https://www.oreilly.com/library/view/learning-java-4th/9781449372477/ch20s08.html) 게임화면에는 다양한 여러종류의 정지 또는 움직임이 있는 여러 이미지들이 동시다발적으로 등장하는데, 그 이미지 하나하나를 사용자가 보고 있는 화면 그 자체에 매번 한개씩 일일이 다 직접 그리고 화면을 업데이트(갱신) 한다라고 가정하면, 사용자는 너무 많은 화면 깜빡임으로 도저히 게임을 할 수 없을 지경이 될 것이다. 그래서, 내부적으로는 사용자 눈에 보이지 않는 메모리 공간(비디오 버퍼)에 각각이 이미지들을 개별적으로 그려놓고, 각각 그려진 개별 이미지들을 사용자한테 보여줘도 되는 완전한 한 장(프레임) 형태로 합쳐 완성되면, 그제서야 사용자 눈에 보이도록 비디오 메모리에 준비된 게임화면 한장을 통으로 화면으로 보내 업데이트하는 방식의 테크닉을 사용하고 있다. 이로써 파이게임 라이브러리를 사용한 게임개발시 가장 기본이 되는 구조의 틀과 코드의 분석을 마쳤다. 파이게임 라이브러리를 쓴다면, 항상 이러한 규격화된 코드틀(템플릿(templete)이라 부름)에서부터 코딩을 시작한다고 보면 되겠다. 그래서, 프로그래밍 세계에서는 이러한 코드틀을 지칭하는 용어가 있는데, **보일러플레이트 코드(Bolierplate code)**라고 불린다. 간단히 맨 처음 코딩시작할 때 일단 저 틀을 코드편집기에 복붙하고 나서 코딩을 시작한다 정도로 생각하면 되겠다. circle-info **상용구 코드**(Boilerplate code): 컴퓨터 프로그래밍에서 상용구 코드 또는 상용구는 수정하지 않거나 최소한의 수정만을 거쳐 여러 곳에 필수적으로 사용되는 코드를 말한다. 참고: [위키피디아arrow-up-right](https://ko.wikipedia.org/wiki/%EC%83%81%EC%9A%A9%EA%B5%AC_%EC%BD%94%EB%93%9C) 지금까지 파이게임의 기본 구조를 익였으니, 다음 장부터 본격적으로 실제 게임을 제작하면서 심화된 코딩을 배워나가도록 하자. [Previous3\. Hello World 프로그램에서 시작하자chevron-left](https://jjlee.gitbook.io/python-pygame/hello-world) [Next5\. 파이게임을 활용해 게임 만들기 (미완)chevron-right](https://jjlee.gitbook.io/python-pygame/5.) Last updated 1 year ago --- # 3. Hello World 프로그램에서 시작하자 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 코딩으로 들어가는 첫 관문이다. 항상 그렇지만, 여러분과 함께 [Hello Worldarrow-up-right](https://ko.wikipedia.org/wiki/%22Hello,_World!%22_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8) 코드부터 배워보는 것으로 시작해야 할 것이다. 그런데, 파이게임제로(pygame zero)은 프로그래밍 언어 그 자체가 아니라, 라이브러리이기 때문에 정확하게 우리가 알고 있는 그 Hello World 프로그램을 만들어 보는게 아니라, Hello World 프로그램에 빗대어 파이게임제로 라이브러리를 이용한 코딩을 할 때, 빼놓을 수 없는 가장 최소한의 기본 코드로만 구성된 가장 작은 프로그램을 만들어보는 예제를 살펴보도록 하겠다. 먼저, MU 에디터를 실행시키자. 메뉴바에 있는 **New(새 파일)** 버튼을 눌러 파일을 열고, 첫 코딩을 시작하자. 다음의 단 한줄짜리 코드를 적어보자. Copy TITLE = 'Hello World!' 코딩은 끝이다. 와우! 너무 간단하지 않은가? 그렇다. 그래서, _파이게임제로는_ [_엔트리-파이썬_](https://jjlee.gitbook.io/entry-python/) _에서 우리가 파이썬 코딩의 맛뵈기를 본 것과 같이 파이썬 코딩을 통한 게임제작을 아주 단순화시켜 놓았다 그래서 파이게임제로를 통해 파이썬 코딩을 공부하는 것은 텍스트코딩 입문자에게는 매우 적절한 선택일 수 있다._ 우리는 단지 이 한 줄(라인)짜리 코드만으로 'Hello World' 라는 앱 이름을 갖는 내용없는 화면만 가진 아주 간단한 프로그램을 만들었다. 이제 코드를 파일에 저장하기 위해 메뉴바에 **Save(저장)** 버튼을 누르고 hello\_world 라는 파일이름으로 저장하자. 그리고, 메뉴바의 **Play(실행)** 버튼을 눌러 실행을 시켜서 원하는데로 잘 동작하는지 확인해 보자. 우리가 기대 했던 'Hello World' 라는 앱 이름을 갖는 그러나 아무 내용없는 화면만 가진 앱이 잘 실행되었는가? ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FGvEc0DtEahHsZLymADVk%2Fimage.png&width=768&dpr=3&quality=100&sign=91a5d66d&sv=2) ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2Fv5MzLmJ2P0l8qs5uKqfG%2Fimage.png&width=768&dpr=3&quality=100&sign=39768d69&sv=2) Hello World 라는 타이틀을 갖는 실행화면 다시 코딩으로 돌아가기 위해 우리는 방금 실행된 앱을 종료시켜야 하는데, 해당 프로그램 창에 x버튼(프로그램 종료버튼)을 눌러 게임앱 자체의 종료는 가능하지만, **Mu 에디터에서 다시 코딩을 이어가려면 반드시 메뉴바의 Stop(정지) 버튼을 눌러 종료시켜야 한다는 것을 잊지말도록 하자**. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FMOrpMoYuml0DQzwTnaZz%2Fimage.png&width=768&dpr=3&quality=100&sign=2f58997d&sv=2) 단, 한 줄짜리 코드지만, 코드를 분석해 코드를 이해해 보자. **TITLE** 이란 변수에 앱의 이름을 저장해 두었다. TITLE 이란 변수명에서 직감적으로 알 수 있듯이 만들고자하는 앱의 이름(타이틀)을 지정하는 용도의 변수이구나 라고 추측할 수 있을 것이다. 그런데, 왜 변수명이 TITLE 이어야 하지? 라는 의문이 들 수 있다. 소문자로 title로 하거나, 아예 APPNAME 이라고 내가 원하는데로 변수명을 정하면 안되는 것인가? 그렇다, 애석하게도 그렇게는 되지 않는다. 진짜 그러한지 확인을 위해 실험을 원한다면, 변수명을 다른 것으로 바꾼 후, 원하는 결과가 나오는지 다시 앱을 실행시켜 보자. 원하는 결과를 얻을 수 없다는 것을 알 수 있을 것이다. 우리가 기존 서를 통해 파이썬 언어를 처음 배울 때 배웠던 [**내장함수**](https://jjlee.gitbook.io/entry-python/basic_syntax/3.2-input-output) **, 내장변수** 같은 개념들이 기억나는가? 이 변수는 이러한 개념들과 유사하다. 우리가 이전 서에서 라이브러리가 무엇인지를 배우면서, [**라이브러리**](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) **를 사용한다는 것 자체는 라이브러리의 제작자가 정해놓은 규칙을 배우고, 그 규칙에 맞춰 코딩하는 것**이라 언급했던 것을 기억한다면, _파이게임제로를 사용한다는 것은 파이게임제로 게임제작자가 정해놓은 규칙을 배워 코딩하다는 것이고, 방금 배운 TITLE이라는 변수는 이미 제작자가 정해놓은 고유명사 같은 것이고, 이 변수명만 사용해 변수에 앱의 이름을 담아야지만, 앱 실행시 앱 이름이 정상표기를 보장한다 것을 의미한다._ 이제 우리 게임앱을 더 확장시켜보자. 이제 다음의 2줄의 코딩을 더해 총 3줄(라인)짜리 코딩이 되었다. 우리는 단지 이 3줄짜리 코드만으로 앱 이름으로 'Hello World' 라는 이름을 갖는 가로세로 400 x 300 픽셀크기의 내용없는 화면만 가진 아주 간단한 프로그램을 만들었다. 이를 Play(실행) 버튼을 눌러 실행시켜보자. Copy TITLE = 'Hello World!' WIDTH = 400 HEIGHT = 300 ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FREt8hCJXelxMwflI5EkT%2Fimage.png&width=768&dpr=3&quality=100&sign=90217eae&sv=2) 가로 400픽셀, 세로 300픽셀의 크기를 갖는 앱의 실행화면 그렇다면 맨 처음 만든 1줄짜리 앱과 이 앱의 차이는 무엇인가? 그렇다 우리는 추가적으로 **WIDTH**, **HEIGHT**라는 넓이, 높이라는 의미를 같는 변수를 사용한 추가적인 코딩을 했고, 여러분들이 직관적으로 느낄 수 있듯이 기존에 TITLE 같이 이 변수명들은 파이게임제로 라이브러리가 미리 정해놓은 앱의 크기를 정하는 내장변수 같은 것이고, 이를 활용한 덕분에 이제 우리는 우리가 원하는 정확한 크기를 갖는 앱을 만들 수 있게 되었다. 여기까지 Hello World 프로그램 제작을 마치고, 다음부터는 실제 게임제작에 앞서 반드시 이해가 필요한 게임 프로그램의 기본구조에 대해 알아보도록 하겠다. [Previous2\. 개발환경 구축chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/dev_env) [Next4\. 게임 루프의 이해chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/game_loop) Last updated 1 year ago --- # 4. 엔트리-파이썬으로 프로그래밍의 핵심 개념을 익히자 | 엔트리-파이썬 이번 장에서부터는 언어 그 자체의 문법이 아닌 프로그래밍의 패러다임이라 불리는 개념들에 대한 것들과 그 패러다임 안에서의 핵심요소들에 대한 것들을 엔트리-파이썬에서 약간이나마 맛뵈기 하는 것으로 엔트리-파이썬의 학습 전체를 마무리지면 좋을 것 같다. **패러다임이는 것은 일반용어상 사전적 의미로 사고(思考)를 구성하는 인식의 체계라 할 수 있는데 프로그래밍 세계에서도 유사한데 프로그래밍을 할 때 어떤 사고적 인식의 틀 안에 일관된 경향성을 갖고, 그 틀 안에서 코딩을 해나가는 것이다.** 이런 식의 의미가 잘 와닿지 않을 것 같은데, 계속 앞전에서 텍스트코딩을 한 외국어를 독파하는 것으로 빗대어 설명한 것에 연장선에서 먼저는 외국어의 문법체계를 익혔다면, 패러다임은 기본적 문법체계는 다 안다는 것에 기반해, 이제는 어떤 형식을 갖는 글을 써나갈 것인가의 것으로 빗대면 이해가 더 쉬울 것 같다. 예를 들어 글쓰기에도 장르가 있는데, 일반 수필과 영극/영화 시나리오의 글쓰기는 엄연히 형식과 내용에서 크게 다른 것처럼 코딩을 할 때도 목표한 최종 프로그램에 적합한 특정 패러다임을 사전에 정하고 그러한 형식과 틀 안에서 코딩을 한다라고 생각하면 좋을 것 같다. 그런데 _글쓰기에서도 장르파괴(?)라고 해서 여러 장르가 혼합되는 것이 가능하듯이, 실제 텍스크코딩에서도 여러 패러다임이 섞여 코딩되는 것도 가능하다는 점도 알아두면 좋겠다._ 사실은 이러한 패러다임의 개념들이 이미 엔트리 블록코딩 안에 다 녹아있는데, 우리는 그동안 인지하지 못한 체 코딩을 해왔던 것이다. 나는 텍스트코딩까진 관심없고 블록코딩에서 내 코딩의 끝을 맞이하겠다 한다면 사실 끝까지 모르고 지나가도 될 수 있고, 모르고도 블록코딩으로 끝까지 재미있게 놀 수 있다. 하지만, **우리는 이제 블록코딩의 세계(world)에서 다리를 건너 텍스트코딩 세계로 건너가고 있으며, 텍스트코딩 세계에서 코딩한다는 것은 그 세계에서 살아가야 할 또다른 차원의 지식의 필요하고, 이를 알고 코딩하는 것과 모르고 하는 것은 텍스트코딩 세계에서 더 수월한 삶(?)을 살아갈 기반이 되며, 텍스트코딩 세계에서는 다른 동료들과 협력하는게 더 일상인데 그들과 코웍(co-work) 함에 있어서도 기본이 되는 배경지식을 쌓는 과정으로 여기면 될 것 같다.** [Previous3.8 함수 (Function)chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax/3.8-function) [Next4.1 순차/병렬처리 (Serial/Parallel)chevron-right](https://jjlee.gitbook.io/entry-python/paradigm/4.1-serial-parallel) Last updated 2 years ago --- # 1. 엔트리 vs 엔트리-파이썬 | 엔트리-파이썬 [엔트리arrow-up-right](https://play-entry.org/) 는 네이버 자회사 비영리 기관인 [커넥트 재단arrow-up-right](https://connect.or.kr/) 에서 제작하고 후원하는 오픈소스 교육용 코딩언어(EPL: Educational Programming Language) 이다. 특별히 한국에서는 코딩교육에 가장 많이 채용되고 활동되는 블록기반 코딩언어(_a high-level block-based visual programming language, 이하 블록코딩 언어_) 이다. 기존에 전 세계적으로 가장 잘 알려진 MIT의 [스크래치arrow-up-right](https://scratch.mit.edu/) 에 비해 큰 강점이기도 한 차별점이 있는데, 기획 당시부터 블록 기반 코딩으로 입문하여 궁극적으로 텍스트 기반 코딩(_text-based programming language, 이하 텍스트코딩 언어_) 입문까지를 커버하는 언어로 만들어졌기 때문이다. 엔트리 안에서 텍스트코딩 입문용 언어로 선택된 것은 파이썬(Python)이다. 파이썬 언어는 텍스트코딩 언어 중에 가장 배우기 쉬우면서 최근에 각광을 받는 특별히 데이터 분석과 인공지능 분야에서 활용성이 뛰어난 언어이기 때문에 텍스트코딩 언어기반 코딩교육에 첫 입문용 언어로 보편적으로 활용되고 있다. circle-info **파이썬(Python)**: 파이썬은 1991년 네덜란드계 소프트웨어 엔지니어인 귀도 반 로섬이 발표한 고급 프로그래밍 언어로, '[인터프리터arrow-up-right](https://ko.wikipedia.org/wiki/%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0) 를 사용하는 [객체지향arrow-up-right](https://ko.wikipedia.org/wiki/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5) 언어'이자 플랫폼에 독립적인, [동적 타이핑arrow-up-right](https://ko.wikipedia.org/wiki/%EB%8F%99%EC%A0%81_%ED%83%80%EC%9D%B4%ED%95%91) (dynamically typed) 대화형 언어이다. 파이썬의 강력한 라이브러리와 풍부한 생태계를 통해, 데이터를 수집하고 분석하며 시각화할 수 있다. 데이터 분석 분야에서 파이선의 사용이 널리 퍼진 이유 중 하나는, 다른 프로그래밍 언어에 비해 비교적 쉽고 간편하게 사용할 수 있기 때문이다. 출처: 위키피디아 그러나, 엔트리에 탑재된 "**엔트리-파이썬**"은 엔트리 전용 파이썬 언어라고 할 수 있다. 오리지널 파이썬 언어의 기본의 문법정도를 익히기 위한 교육목적의 **오리지널 파이썬의 미니 파이썬 버전**정도로 간주할 수 있고, 따라서, 완전한 파이썬 그 자체는 아니기 때문에 오리지널 파이썬 문법 중 지원하지 못하는 문법이 상당수 있다는 것을 항상 염두하면서 코딩을 하자. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FEj2Cji5duhtT3dfdgUp3%252Fentry-python.png%3Falt%3Dmedia%26token%3D9259feb3-c177-4365-b44a-715dce247301&width=768&dpr=3&quality=100&sign=595fdc1d&sv=2) 우리의 목표는 "엔트리-파이썬"에서 최대한 빠르게 파이썬의 기본 문법을 익히고, 하루 빨리 [오리지널 파이썬arrow-up-right](https://python.org/) 으로 전향해 블록코딩을 완전히 떠난 이제는 텍스트코딩으로만 코딩(프로그래밍)하는 것이 궁극적인 목표이기 때문에 엔트리-파이썬 안에서 너무 큰 기대와 목적을 초과하는 지나친(?) 사용은 자제하는 것이 정신건강에 좋을 것 같다. 그 이유는 안타깝게도 엔트리-파이썬 자체는 생각보다 불안정성이 여전하여(예를들어 코드 상에 문법오류가 전혀 없음에도 오류가 있다고 에러를 내며 실행이 안되는 등등, 겪어보면 안다;) 다소 복잡한 코딩을 시작하게 되면 그러한 문제에 부딪힐 가능성이 점점더 증가하기 때문이다. 다시한번 요약하면, 엔트리-파이썬은 블록코딩에서 첫 텍스트코딩의 넘어가는 분들이 대상이고, 생짜배기로 텍스트코딩 세계에 막 들어갔을 때의 막막함(?)의 장벽을 낮춰주기 위함이며, 우리에게 이미 익숙한 블록코딩 환경 안에서 텍스트로 일일이 키보드 자판을 타이핑하며 텍스트코딩 해본 것이 블록코딩으로 했을 때와 큰 차이 없이 동작하는 것을 경험하며 텍스트코딩이 그렇게 어려운 것이 아니구나 라는 선입견을 없에는 정도만 해도 엔트리-파이썬이 목적한 그 소기의 목적달성이라고 할 수 있겠다. [Previous엔트리로 파이썬 기초 입문하기chevron-left](https://jjlee.gitbook.io/entry-python) [Next2\. 엔트리-파이썬의 시작chevron-right](https://jjlee.gitbook.io/entry-python/start) Last updated 2 years ago --- # 5. 엔트리 초급책을 코딩하기 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 드디어 이제부터븐 이 책의 본론으로 엔트리 [초급책arrow-up-right](https://m.blog.naver.com/erke2000/220640141178) (차근차근 따라하며 배우는 엔트리 프로그래밍-기초편)의 예제들을 하나씩 하나씩 파이썬으로 코딩으로 변환하면서 파이썬 텍스트코딩을 배워나가도록 하겠다. [Previous4\. 게임 루프의 이해chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/game_loop) [Next5.1.1 멍멍이와 야옹이가 인사해요1 - 순차chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat) Last updated 1 year ago --- # 3. 엔트리-파이썬으로 파이썬 언어의 기본문법을 익히자 | 엔트리-파이썬 텍스트 코딩의 문법체계를 배운다는 것은 마치 우리가 다른 나라 외국인과 대화하기 위해 외국어를 공부하는 과정과 유사하다. 다만, 대상이 외국사람이 아닌 컴퓨터라는 것이다. 우리가 처음 외국어를 배울때 처럼 해당 언어를 표기하는 문자를 아는 것 부터 시작해(다행히 컴퓨터와의 대화는 우리가 이미 알고 있는 영어를 기반하는 라틴 알파벳으로만 표기), 문자들의 조합으로 음절(syllable)이 되고, 음절의 조합으로 단어가 되고, 그 단어들이 문장을 구성하는 문법체계(예를들어 주어+동사+목적어 등의 순서)에 맞추어 구성될 때에야 비로소 상호 간에 대화가 가능하다. 우리는 이미 어떤 외국어 배우는 것을 시도해 본 경험들(최소한 영어 등)이 있고, 이 부분이 우리에겐 장점이면서 동시에 언어 배우는 것은 어렵다는 선입견을 갖을 수 있는데 컴퓨터 코딩언어는 최소한 인간이 사용하는 언어가 갖는 복잡성 보다는 훨씬 쉽고, 그 중에 파이썬이란 언어는 컴퓨터 코딩언어 중에 아주 쉬운 축에 속하기 때문에(특히나 기존에 영어에 대한 지식이 많으면 많을 수록) 첨부터 너무 부담을 갖지 않기를 바란다. [Previous2\. 엔트리-파이썬의 시작chevron-left](https://jjlee.gitbook.io/entry-python/start) [Next3.1 Hello World 예제코드의 이해chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world) Last updated 2 years ago --- # 2. 개발환경 구축 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 모든 프로그래밍 언어 배우는 책에서 가장 먼저 등장하는 챕터가 아닐까 한다. 왜냐면 제목에서 유추할 수 있듯이 코딩을 시작하기 위해 사전에 기본적으로 설치될 프로그램들이 존재한다는 의미이고, 이를 설치(Install)하고 설정(Configuration)한 이후에야 본격적인 코딩이 가능하기 때문이다. 일반적으로 이 과정에는 크게, 2가지가 필요하다. 첫번째는 사용하고자 하는 프로그래밍 언어(여기서는 파이썬) 그 자체를 설치하는 일이고, 두번째는 이제 그 설치된 언어로 컴퓨터와 소통하기 위해 의미있는 문장과 글을 써내려가는 일(우리는 이 과정을 코딩(또는 프로그래밍)이라 함)을 쉽고 편하게 도와 줄 도우미 프로그램인 코드 편집기(텍스트 에디터: Text Editor) 또는 통합개발환경(IDE: Integrated Development Environment)이라는 프로그램을 설치하는 일이다. circle-info **IDE(Integrated Development Environment, 통합개발환경)**: 하나의 프로그램 안에서 소프트웨어 개발과 관련된 모든 기능을 제공하는 환경을 가진 프로그램. 소프트웨어 개발에 관련된 모든 작업이라고 하면 기본적인 코드 편집, 컴파일(Compile), 디버그(Debug), 빌드(Build), 바이너리 배포(Binary deployment), 다른 개발자와의 공동 개발작업 등을 말한다. 유명한 제품으로 마이크로소프트의 비주얼 스튜디오, 구글의 안드로이드 스튜디오, 파이썬 전용 IDE로는 PyCharm 등이 있다. 그런데, 이러한 과정은 새로운 코딩언어를 사용하게 될 때마다 매번 거쳐야 하는 일상적인 일임에도 불구하고, 초보자 입장에서는 여전히 번거롭고 까다로울 수 있는데 예를들어 설치과정 중 문제라도 생겨 해결을 위한 트러블슈팅(trobleshooting)하는 과정 등에서부터 막히면 텍스트코딩은 어렵다는 선입견을 갖을 수 있고, 다행히 저자와 같이 이런 같은 고민을 했던 또다른 해외 개발자/교육자 그룹이 있었으며, 그들은 그러한 목적에 맞게 개발환경의 추가적인 설치가 필요없이 앱을 실행시키지마자 곧바로 코딩을 시작할 수 있는 필요한 모든 것을 이미 다 에디터에 탑재시킨 [Mu 에디터(Mu editor)라는 코드 편집기의 오픈소스 프로젝트arrow-up-right](https://github.com/mu-editor/mu) 를 시작했으며, 따라서, **코딩 외에 주변적인 일은 최대한 간소화와 생략시키고, 우리가 더 깊히 알고 이해해야 할 텍스트코딩이란 본질에만 더 집중할 수 있게** 하는 우리의 취지에 맞게 우리는 Mu 에디터(코드편집기)를 활용해 코딩하도록 하겠다. [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/dev_env#mu) Mu 에디터(코드편집기) ----------------------------------------------------------------------------------------- 설치를 위해 [Mu 에디터 공식 사이트arrow-up-right](https://codewith.mu/en/download) 를 방문해 본인의 컴퓨터 환경에 따라(컴퓨터의 운영체제는 무엇인지에 따라)에 맞는 현 시점의 가장 최신의 설치 프로그램을 다운로드 받아 설치하면 된다. circle-info 참고로 **필자는 오픈소스 Mu 에디터를 기존보다 더더욱 교육에 편리하게 활용하기 위해서 소스코드를 수정하여 커스텀 버전을 만들고 있는 중**이다. 교육에 있어서 필요한 리소스(이미지들, 추가설치가 필요한 모듈 등)를 에디터 내에 사전에 탑재시켰으며 **이는 최초 에디터 설치 이후에 별도의 리소스를 온라인으로 추가 설치 등의 번거로움 없이 하나의 에디터만으로도 필요한 교육의 A-Z까지를 오프라인 환경에서도 할 수 있게 하기 위한 목적이다.** **그밖에 아이들이 열광(?)할 기능으로 자기가 만든 게임을 친구 등 다른 이에게 쉽게 배포해 즐길 수 있는 기회를 줄 수 있게하기 위한** [**자신의 게임을 단 한 개의 실행 파일(EXE 파일) 형태로 변환해 주는 오리지널 에디터에는 없는 추가기능**](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix/packaging) 을 탑재하고 있다. **해당 커스텀 Mu 에디터 버전을 사해 보고 싶은 분은** [**이 곳**arrow-up-right](https://github.com/roboticsware/mu/releases/) **에서 다운로드 할 수 있다.** ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FcgbMwh36iPiazn1bVxDj%2Fimage.png&width=768&dpr=3&quality=100&sign=11646b8c&sv=2) Mu 에디터 공식 사이트 Mu 에디터는 코딩도우미 프로그램으로서 "단순 코드 편집기" 또는 "통합개발환경(IDE) 프로그램" 둘 중에 어느 쪽에 더 가까울까? 답은 때로는 IDE에 좀 더 가깝고, 때로는 코드 편집기에 더 가깝다고 할 수 있다. Mu 에디터에는 **본인이 코딩하려는 개발환경(이를 모드(mode)라 지칭)**을 선택해 손쉽게 바꿀 수 있는 기능을 제공하고 있는데 한 개의 에디터로 다양한 파이썬 코딩(일반 SW개발코딩, 게임코딩, HW제어코딩 등) 을 손쉽게 할 수 있는 초보자 입장에서는 매우 편리한 기능이다. 이러한 모드를 선택하려면 에디터 상단의 메뉴바(Menu bar)에서 **Mode(모드)** 아이콘을 누르면 아래와 같은 (개발환경)모드를 선택해 바꿀 수 있으며(이는 마치 tv리모콘으로 tv채널을 변경하듯이 손쉽게), 현재 우리는 파이게임제로를 활용한 게임코딩을 할 예정이므로, 파이게임제로 모드를 선택해 보자. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FUmsLnhEAwG8hiXNf3xzr%2Fimage.png&width=768&dpr=3&quality=100&sign=3503acb0&sv=2) 메뉴바의 모드선택 아이콘 ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FyH6m8gzbVw4rLuZbBYpH%2Flib_sel.png&width=768&dpr=3&quality=100&sign=efeceffa&sv=2) 모드 선택화면 모드가 잘 선택되었다면, 다음과 같이 현재 선택된 코딩환경이 무엇인지를 보여주는 상태바(Status bar)를 통해 우리가 선택한 코딩모드가 잘 선택되었는지 한번더 확인할 수 있다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FTyZBzi4zbqoN8o2FeI8q%2Fimage.png&width=768&dpr=3&quality=100&sign=e28d99ed&sv=2) ### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/dev_env#mu-1) Mu 에디터 한글화 프로젝트 소개 circle-info **위에 에디터 화면은 위에서 언급한 필자가 제작 중인 커스텀 버전의 외관으로, 보이는 것처럼 현재 일부 한글화가 적용되어 있고, 전체 한글화를 목표하고 있다. 다만, 전체 한글화에는 많이 시간이 걸리므로 여러분의 참여를 기다리고 있다. 가능한한 많은 분들이 함께 번역에 참여해 빠른 시간에 마치고, 모두가 함께 그 혜택을 누리길 원하고 있다. 모두를 위해 자신의 시간을 기꺼이 할애해 자발적 참여해 주실 분들을 언제나 환영하고 기다린다🙏** 번역참여링크: [https://crowdin.com/project/mu-editor-korean-translation/invite?h=77d1d6ec51d0aeb5cfff7ecf39a5ede51970084arrow-up-right](https://crowdin.com/project/mu-editor-korean-translation/invite?h=77d1d6ec51d0aeb5cfff7ecf39a5ede51970084) 이로써 우리는 코딩을 시작하기 위한 사전 준비를 마쳤고, 이제 다음 시간부터는 우리 교육에 최적화 된 커스텀 Mu 에디터를 활용해 본격적인 파이썬 언어 코딩을 시작해 보자. [Previous1\. 파이게임 제로(pygame zero) 라이브러리chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/pygame_zero_lib) [Next3\. Hello World 프로그램에서 시작하자chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/hello-world) Last updated 1 year ago --- # 4. 게임 루프의 이해 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 본격적인 게임 제작에 앞서 우리가 만들게 될 게임 프로그램의 기본 동작구조를 이해할 필요가 있다. 엔트리 블록코딩에서 일반적 코딩이 어떠했는지를 먼저 기억해보자. 일반적으로 먼저, 화면에 등장시킬 1개 이상의 주인공 오브젝트들 불러와 화면에 위치시켰고, 이후 각각의 오브젝들이 게임에서 각자 해야할 행동들에 대해서 오브젝트별로 개별적으로 코딩을 했다. 이후 완성된 게임을 실행했을 때도, 각각의 오브젝트들이 각자 독립적으로 동시에 실행되고, 심지어 한 오브젝트 안에서 "시작하기 버튼을 클릭했을 때"를 2개 이상 배치해 마치 병렬적([자세한 내용은 이전 서 참조](https://jjlee.gitbook.io/entry-python/paradigm/4.1-serial-parallel) )으로 행동하는 것처럼 동작하였다는 것을 기억할 것이다. 파이썬 언어와 파이게임제로 라이브러리로 코딩하는 것은 기존에 블록코딩하던 것과 유사한 것도 있지만, 다른 부분도 상당히 있다. 가장 크게 다른 부분이 바로 아쉽게도 이제는 더이상 각각의 주인공 오브젝트들이 독립적이고 동시적(concurrency)으로 행동하는 것으로 간주하고 코딩할 수 없고, 모든 주인공 오브젝트들의 움직임을 한 곳(게임 루프)에서 관리하고 통제(콘트롤) 한다는 것을 전제하고 코딩해야 한다. 그것을 그림으로 표현하면 다음과 같이 끊임없이 반복되는 무한루프(특별히 게임에 사용하는 루프구조라고 하여 게임루프라 부름) 안에서 게임이 실행되게 되는 것이다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252Ftm8gRcTosPWsvTNDHeMh%252Fimage.png%3Falt%3Dmedia%26token%3D9259dc97-4b75-400d-adf7-95c822bab009&width=768&dpr=3&quality=100&sign=7b28e86c&sv=2) 출처: [https://inventwithpython.com/pygame/chapter2.htmlarrow-up-right](https://inventwithpython.com/pygame/chapter2.html) 그림에 표현된 것처럼 **게임루프에서는 크게 3가지(게임 오브젝트를 화면에 그리기, 게임 오브젝트의 움직임 처리, 사용자의 조작에 의한 키보드/마우스 등의 이벤트 처리)를 루프를 돌면서 끊임없이 처리**하게 된다. 만약 우리가 파이게임제로 라이브러리가 아닌 파이게임 라이브러리으로 코딩을 하고 있다면, 저 게임루프의 구조를 만드는 것도 우리에게 할당된 몫으로, 우리가 직접 만들면서 코딩해야 한다. 하지만, **파이게임제로는 초보자들을 배려해 그 부분을 의도적으로 숨기고, 위에서 언급한 3가지의 반복처리 사항을 사전에 콜백함수(draw, update, on\_mouse / on\_key)로 만들어 놓았고, 그 콜백함수에 맞춰 코딩하면 되도록 초보자들을 위해 코딩하기 더 쉬운 구조를 만들어 놓았다.** 다행히 우리는 이미 엔트리-파이썬에서 이미 [콜백함수](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#callback) 에 맞춰 파이썬 코딩하는 것을 해보았기 때문에 이런 구조에 어렵지 않게 적응할 수 있을 것이다. 다음 시간부터는 이에 대한 이해를 바탕으로 본격적인 게임 코딩을 해보도록 하겠다. [Previous3\. Hello World 프로그램에서 시작하자chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/hello-world) [Next5\. 엔트리 초급책을 코딩하기chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding) Last updated 10 months ago --- # 5. 부록 | 엔트리-파이썬 코딩에 도움이 되는 단축키 목록 등을 안내합니다. 일반적으로 개발자들은 빠른 작업을 위해 단축키 활용에 능숙합니다. 여러분들도 단축키를 활용하는 습관 들이기를 추천합니다. [Previous4.4 객체 지향형 (Object-Oriented)chevron-left](https://jjlee.gitbook.io/entry-python/paradigm/4.4-object-oriented) [Next엔트리-파이썬 단축키 모음chevron-right](https://jjlee.gitbook.io/entry-python/appendix/shortcuts) Last updated 2 years ago --- # 부록 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 [게임을 단 한 개의 실행파일로 패키징 하기chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix/packaging) [뮤 에디터 단축키 모음chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix/shortcuts) [Previous5.7 두더지 게임을 만들어요 - 종합chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/7-mole_game) [Next게임을 단 한 개의 실행파일로 패키징 하기chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix/packaging) --- # 1. 엔트리란 | 엔트리-아두이노 [엔트리arrow-up-right](https://play-entry.org/) 는 네이버 자회사 비영리 기관인 [커넥트 재단arrow-up-right](https://connect.or.kr/) 에서 제작하고 후원하는 오픈소스 코딩교육용 언어(EPL: Educational Programming Language) 입니다. 한국에서는 코딩교육에 가장 많이 채용되고 활동되는 언어입니다. **코딩교육은 두 종류로 나눌 수 있는데 순수 소프트웨어 개발 중심의 코딩교육(이하 소프트웨어 개발 코딩교육)과 하드웨어를 제어하는 소프트웨어 개발 중심의 코딩교육(이하 하드웨어 제어 코딩교육)으로 나눌 수 있습니다.** 하드웨어 코딩교육이라는 본 서의 저술 목적 관점에서 볼 때 전 세계적으로 가장 널리 알려져 있고, 가장 많은 사용자를 보유한 코딩교육용 언어인 [스크래치arrow-up-right](https://scratch.mit.edu/) 보다 **엔트리가 우월한 점은 소프트웨어 개발 코딩교육을 넘어 아두이노 포함 수십여 종의 코딩교육용 하드웨어의 기본(default) 지원을 통해 다양한 하드웨어 제어 코딩교육 까지도 엔트리 안에서 교육할 수 있다는 것입니다.** 실제로 엔트리는 60-70여종(중복제외, 2021년 기준) 이상의 코딩교육용 하드웨어를 지원하고 있으나, **스크래치는 소수 특정 하드웨어(2021년 기준, 레고 마인드스톰즈, 마이크로비트 등 총 6종)만 지원하고 있습니다. 그 중에서도 전 세계적으로 피지컬 컴퓨팅\*, 메이커 운동(Maker Movement)에 가장 보편적으로 사용하는 아두이노를 지원하고 있지 않는 것은 가장 큰 단점 중에 하나**입니다. circle-info **피지컬 컴퓨팅(Physical Computing)**: 실 세계(real world)로부터 어떤 정보를 취하고(sense) 취한 정보에 어떤 반응으로 답하며(respond) 실 세계와 상호작용하는(interective) 시스템. 사실상 매우 광의적인 의미로 간단히 표현하면 디지털로 동작하는 하드웨어 통해 실 세계(물리적 세계)와 소통하는 시스템이라 할 수 있고, 학문적으로는 전자공학, 기계공학, 컴퓨터공학, 로봇공학, 산업공학 등의 다수의 학문들과 관련되어 있다 할 수 있다. 출처: 위키피디아 **엔트리의 단점으로는** 두 가지 코딩교육 중에서도 소프트웨어 개발 코딩교육에 더 방점이 있다 보니, 엔트리와 하드웨어를 연결해 블록코딩으로 하드웨어를 제어해 보는 하드웨어제어 코딩교육이 가능하기는 하나, 조금 더 나아가 실제 실무나 실전에 가까운 수준에 코딩교육까지는 나아가지 않는다는 점입니다. 가장 아쉬운 부분은 **블록코딩한 코드를 실제 하드웨어에 저장(코드 업로드)시키는 기능이 없는 것으로, 제어하고자 하는 하드웨어는 항상 엔트리와 실시간 연결되어 있어야만 동작한다는 것입니다. 즉, 엔트리와 연결없는 하드웨어 안에 기 업로드 되어 저장된 코드에 기반해 독립적으로 동작 시킬 수 없다는 점**입니다. 예를 들어 해당 기능이 없으면 하드웨어를 활용한 경연/경진대회나 메이커 활동에 제약이 따를 수 밖에 없는데, 왜냐하면 시연(데모) 또는 결과물의 활용을 위해서는 항상 노트북 이나 컴퓨터 상에 실행된 엔트리와의 연결을 통해서만 동작하기 때문입니다. 그래서 **본 책의 저자는 오픈소스인 엔트리를 수정해** 하드웨어(2021년 기준 아두이노와 한국 공교육 교과서에서 가장 많이 채용된 네오피아의 최신 모델 [네오쏘코arrow-up-right](http://neobot.co.kr/product/%EB%84%A4%EC%98%A4%EC%8F%98%EC%BD%94/223/) 이상 2가지)로의 **코드 업로드 기능을 구현하였습니다.** 또한 엔트리가 기존에 블록코딩을 넘어 실전 소프트웨어 코딩교육 즉 텍스트 기반 코딩교육과의 연계를 위해 블록코딩의 파이썬 코드변환 및 파이썬 텍스트코딩 기능을 제공했던 목적과 유사하다. **참고로 코드의 하드웨어 업로드 뿐만 아니라, 아두이노 하드웨어에 한해서 블록코드의 C++ 텍스트코드로의 변환과 C++ 텍스트코딩 기능 일부 가능하도록 기능추가 하였습니다.** **이제 엔트리는 실전에 가까운 하드웨어(특별히 아두이노를 활용한) 제어 코딩교육에 손색이 없는 도구로 변모하였습니다.** 이러한 저자의 작은 노력이 한국과 전 세계 특별히 개도국(개발도상국) 코딩교육에 기여해 4차 산업혁명이 주도하는 미래사회를 살아갈 어린이들의 삶에 조금이나마 보탬이 될 수 있기를 소망합니다. 아래는 하드웨어 코드 업로드 기능과 아두이노 C++코드변환 기능이 추가된 버전의 다운로드 링크입니다. **엔트리 커스텀 버전 안내**: [https://github.com/JeongJun-Lee/entry-offlinearrow-up-right](https://github.com/JeongJun-Lee/entry-offline) **엔트리 커스텀 버전 다운로드**: [https://github.com/JeongJun-Lee/entry-offline/releasesarrow-up-right](https://github.com/JeongJun-Lee/entry-offline/releases) **엔트리 커스텀 버전 코드업로드 시연(데모)**: * 아두이노: [https://www.youtube.com/watch?v=ZXWgiRx1mv0arrow-up-right](https://www.youtube.com/watch?v=ZXWgiRx1mv0) * 네오피아 네오쏘코: [https://www.youtube.com/watch?v=\_9VuT8v359carrow-up-right](https://www.youtube.com/watch?v=_9VuT8v359c) [Previous엔트리로 실전 아두이노 배우기chevron-left](https://jjlee.gitbook.io/entry-arduino) [Next2\. 아두이노란chevron-right](https://jjlee.gitbook.io/entry-arduino/arduino) Last updated 2 years ago --- # 3. 아두이노 블록코딩의 시작 | 엔트리-아두이노 3장 내용 전체를 동영상으로 한 번에 이해하고 싶은 분들이나 특정 부분이 잘 이해되지 않을 때는 해당 [유투브 동영상arrow-up-right](https://www.youtube.com/watch?v=ZXWgiRx1mv0) 을 시청할 것을 권합니다. [Previous2\. 아두이노란chevron-left](https://jjlee.gitbook.io/entry-arduino/arduino) [Next3.1 아두이노 블록코딩의 한계chevron-right](https://jjlee.gitbook.io/entry-arduino/coding_start/block_limitation) Last updated 2 years ago --- # 4. 아두이노 보드 기본 | 엔트리-아두이노 아두이노 보드를 다루기 전에 아두이노 보드의 기본을 이해합시다. [Previous3.6 블록코딩을 텍스트 코딩으로 변환하기chevron-left](https://jjlee.gitbook.io/entry-arduino/coding_start/code_transform) [Next4.1 아두이노 보드의 종류chevron-right](https://jjlee.gitbook.io/entry-arduino/board_basic/arduino_sort) Last updated 2 years ago --- # 7. 부록 | 엔트리-아두이노 본문 내용 중 더 깊은 이해와 보조적 설명의 필요에 의해 추가되었습니다. [Previous6\. 마무리chevron-left](https://jjlee.gitbook.io/entry-arduino/epilog) [Next6.1 저항의 종류와 색 띠 저항값 읽는 법chevron-right](https://jjlee.gitbook.io/entry-arduino/appendix/resistor_value) Last updated 2 years ago --- # 6. 마무리 | 엔트리-아두이노 지금까지 우리는 엔트리+아두이노를 활용한 하드웨어 제어 코딩교육의 기본을 살펴보았습니다. 지금까지 본 서를 통해 하드웨어 제어 코딩교육까지 거친 여러분은 이제 엔트리를 통해 범용(특정 하드웨어에 종속되지 않은) 소프트웨어(또는 애플리케이션)를 개발하기 위한 코딩과 특정 하드웨어를 제어하는 펌웨어를 개발하는 코딩의 차이에 대해 이제 어느 정도 감이 잡히셨을 것이라 생각됩니다. **실제 현업에서도 하드웨어를 잘 이해해서 그 하드웨어를 직접 제어하는 소프트웨어를 개발하는 전문가와 특정 하드웨어에 종속되지 않는 단지 하드웨어를 이용하는 측면에서의 소프트웨어를 개발하는 전문가로 나뉘어집니다.** 이제 그 다음단계로 더 깊은 영역의 공부로 나아가는 것은 여러분 개개인의 몫이 될 것입니다. 이정도로 본인에게 충분하다 할 분도 있고, 더 깊게 공부해 전문가까지 되고 싶은 분도 있을 것입니다. **만약 조금 더 깊히 공부를 시작하고자 하는 분이나 교육자 입장에서 더 깊이 교육해 보고자 하시는 분이 있다면, 먼저는 이제까지** [**5장**](https://jjlee.gitbook.io/entry-arduino/block_coding) **전체에 결쳐 살펴본 하드웨어를 제어하고 소통하는 총 4종류(디지털 입/출력, 아날로그 입/출력)의 코딩이 기본임을 기억**하시고, 이제 다음 단계는 **더 다양한 종류의 외부장치(센서, 액추에이터\*)들을 다뤄보는 코딩을 실습**해 보는 것을 추천합니다. 본 서에서는 아두이노만 보드만 사용해 봤지만, **다른 종류의 아두이노 보드 또는 다른 제조사의 고사양 보드들(예를 들어, 라즈베리 파이 등)에 도전**해 보실 수 있습니다. 또 하드웨어 보드에 따라 OS(Operating System) 설치해 기본적으로 **모든 하드웨어 제어를 OS의 통제 아래서 수행하는 소프트웨어 개발에 관해서도 알아두실 필요**가 있기 때문입니다. circle-info **액추에이터 (Actuator)**: 액추에이터는 시스템이나 기계를 움직이거나 제어하는 데 쓰이는 기계의 한 구성품이다. 일반적으로 추에이터는 에너지원으로부터 제어신호를 요구하며 제어신호는 상대적으로 낮은 에너지로 예를들어 전압, 전류, 기압, 유압 등의 형태로 된 에너지들을 어떠한 종류의 기계적 움직임으로 변환시킨다. 출처: 위키피디아 그러나 꼭 기계적인 움직임만으로 국한시킬 필요 없이 피지컬 컴퓨팅에서 말하는 외부 세계와 상호 소통하기 위해 외부로 표현되는 모든 종류의 표현(소리, 음성, 빛, 기계적 움직임 등)에 관한 것으로 이해하면 쉬울 것 같다. 코딩적으로는 디지털/아날로그 출력에 관한 코딩이 적용된다. 개발도상국(이하 개도국)에서 코딩교육을 통해 아이들 자신과 그 아이들이 살아갈 사회의 보다나은 미래를 준비하는 일에 기여하는 삶을 살아오고 있는 저자는 **이곳의 아이들을 어떻게 하면 더 효과적으로 코딩교육 할 수 있을지를 고민하면서 도달했던게 블록코딩 언어에서 출발해 그 블록코딩 도구 안에서 더 실전 코딩에 가깝게 까지 교육할 수 있느냐에 대한 것이었습니다. 그 결과로 기존 엔트리가 제가 필요했던 기능이 부족함을 느꼈고, 오픈소스 엔트리를 직접 수정해 필요한 기능(하드웨어로 코드 업로드, 블록코드의 C++언어 변환)을 추가한** [**엔트리 커스텀 버전**arrow-up-right](https://github.com/JeongJun-Lee/entry-offline) **을 만들었습니다. 이후 그 커스텀 버전으로 어떤 식의 하드웨어 코딩교육을 시작해 볼 수 있을지를 알려주는 가이드를 제공해야 되겠다는 생각에서 본 서까지 등장하게 되었습니다.** 본 서는 사실은 한국에 계신 분들보다는 개도국의 교사들을 염두해 두고 씌여진 것이고, 본 서는 앞으로 해당 개도국의 언어로 번역되어 개도국의 코딩교육 교사 교육용으로 활동되어질 것입니다. 끝까지에 읽어주셔서 감사드리고, **본 서 내용에 오류가 있거나 보완되었으면 하는 내용 등 어떤 의견이든 환영하며, 본 서가 공개되어 있는 Github의** [**이슈리스트**arrow-up-right](https://github.com/JeongJun-Lee/entry-arduino/issues) **에 의견 남겨주시길 바랍니다. 교딩교육을 통해 세상을 변화시키고자 하는 모든 분들의 참여를 환영하고 기다립니다.** 감사합니다! [Previous5.4 아날로그 입력 코딩chevron-left](https://jjlee.gitbook.io/entry-arduino/block_coding/analog_input) [Next7\. 부록chevron-right](https://jjlee.gitbook.io/entry-arduino/appendix) Last updated 2 years ago --- # 5. 실전 아두이노 블록코딩 | 엔트리-아두이노 이제 디지털 입/출력, 아날로그 입/출력의 4종류의 실전 아두이노 블록코딩이 어떻게 되는지 각각을 대표하는 예제를 사용해 코딩해 봅시다. 단순히 예제를 따라 블록코딩을 해보는데서 멈추지 말고, 해당 코드는 C++코드로도 변환해 C++코드로도 이해해 보는 노력을 시도해 볼 것과, 작성한 코드를 하드웨어로 업로드시켜 하드웨어 안에 저장된 펌웨어 형태로도 잘 동작하는지 확인해 볼 것을 추천합니다. [Previous4.2 아두이노 입출력 핀 구성chevron-left](https://jjlee.gitbook.io/entry-arduino/board_basic/input_output) [Next5.1 디지털 출력 코딩chevron-right](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_output) Last updated 2 years ago --- # 2. 아두이노란 | 엔트리-아두이노 아두이노는 2005년 이탈리아 이브레아(Ivrea) 지방 인터렉션 디자인 전문학교(IDII: Interaction Desigin Institute Ivrea)의 마시모 반지(Massimo Banzi)와 데이비드 쿠아르티에예스(David Cuartielles), 두 교수로 부터 출발했습니다. 해당 학교는 예술과 기술이 접목된 예술작품 구현을 교육하는 하는 곳으로 미디어 아트(Media Art)같은 작품을 만들기 위해서는 하드웨어를 프로그래밍으로 제어(다른 표현으로 피지컬 컴퓨팅)하는 지식이 반드시 필요했는데, 하드웨어 미숙련자 및 기술 분야 비전공 학생들을 위해 기초적인 지식만으로도 쉽게 프로그램 작성이 가능하고, 또한 저렴하게 구입 가능한 하드웨어 제어용 마이크로트롤러(Microcontroller)\* 보드를 개발하고자 했던 것으로부터 시작했습니다. circle-info **마이크로콘트롤러(Microcontroller) 또는 또는 마이크로 콘트롤러 유닛(MCU: Micro Controller Unit)**: 연산장치인 마이크로프로세서(Microprocessor)와 입출력 모듈을 하나의 칩으로 만들어 정해진 기능을 수행하는 컴퓨터를 말한다. CPU(Central Processor Unit) 코어, 메모리 그리고 프로그램 가능한 입/출력을 가지고 있다. 출처: 위키피디아 아두이노 보드는 Atmel 사의 ATmega328 라는 마이크로콘트롤러를 탑재하고있으며, ATmega328을 보다 쉽게 사용하기 위해 PCB(Printed Circuit Board) 위에 주변 장치들을 연결하여 사용자들이 ATmega328을 편리하게 사용할 수 있도록 제작된 마이크로컨트롤러 보드입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MaysGR3v3z44vtCIdmn%252F-MaytyqEu9WPvpczcu7_%252Fimage.png%3Falt%3Dmedia%26token%3D661c145f-82eb-4c33-a9e9-7616e79ffe10&width=300&dpr=3&quality=100&sign=7012d7fb&sv=2) 두 교수의 바람데로 **아두이노는 손쉬운 활용과 저렴한 가격이란 두 가지 목적을 완전히 달성했을 뿐만 아니라, 해당 프로젝트의 결과물이 오픈소스로(Open Source) 공개된 탓에 전 세계적인 호응와 함께 급속한 보급이 이루어졌으며, 현재는 예술가의 작품활동, 기술전문가의 제품 프로토타이핑(Prototyping), 일반인의 메이커 운동(Maker Movement)의 취미활동, 하드웨어 코딩교육 사용에 거의 표준적 도구가 되었습니다.** 아두이노는 두 가지로 구성되어 있는데 마아크로콘트롤러를 탑재한 하드웨어 보드와 그 보드에 탑재시킬 소프트웨어의 손쉬운 개발을 가능하게 통합개발환경(IDE: Integrated Development Environment)으로 구성되어 있습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MaysGR3v3z44vtCIdmn%252F-Maytk8Fp3A0Pcw_BSeg%252Fimage.png%3Falt%3Dmedia%26token%3D943ec119-a636-4930-9b4a-e25956d82c82&width=768&dpr=3&quality=100&sign=1aa7e48c&sv=2) **아두이노가 비전공자의 손쉬운 하드웨어 제어라는 목적을 상당수 달성시킨 것은 사실이나, 여전히 입문하려는 사람들에게 진입장벽이 되는 것은** 하드웨어 제어용 소프트웨어를 개발하기 위한 텍스트 코딩 언어인 C++ 언어와 하드웨어를 구성하는 기본적인 전자부품과 전자회로에 대해서 깊게는 아니어도 어느정도 지식이 필요하다는 것입니다. **본 서의 저술 목적은 하드웨어 코딩교육을 어떻게 하면 쉬우면서 동시에 실전과도 유사한 교육을 할 수 있을까를 고민하면서 첫 코딩교육을 엔트리를 통해 입문한 사람들이 이미 익숙해진 엔트리를 그대로 재사용해 하드웨어 코딩교육으로 까지 나아가는 배움경로(Learning Path)를 구성하는 것**입니다. [Previous1\. 엔트리란chevron-left](https://jjlee.gitbook.io/entry-arduino/entry) [Next3\. 아두이노 블록코딩의 시작chevron-right](https://jjlee.gitbook.io/entry-arduino/coding_start) Last updated 2 years ago --- # 3.5 반복 (Loop) | 엔트리-파이썬 우리는 블록코딩에서 코드의 흐름(flow)을 제어하는 목적(따라서, 엔트리의 블록 분류 상에서 ‘흐름’ 카테고리 안에 존재)의 블록 중 가장 기본이 되는 블록 중에 하나로 "**반복**"이라는 것을 무수히 사용해 본 경험이 있다. 반복수행해야 하는 코드덩어리를 어떻게 얼마만큼 반복해야 하는지에 따라 엔트리 '흐름' 카테고리 안에 반복과 관련된 블록을 보면 크게 세 가지 종류인데, "무한반복 / 지정한 횟수만큼 반복 / 어떤 조건을 만족하는 동안의 반복" 이다. 먼저, “어떤 조건을 만족하는 동안의 반복” 파이썬 문법으로 표현하면 다음과 같다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252Felwz5KzM7zM4lgtGFbEu%252Fimage.png%3Falt%3Dmedia%26token%3Db94041f8-8d82-4442-bda2-d2072ffe2968&width=768&dpr=3&quality=100&sign=cc958ebb&sv=2) while 이라는 영어접속사의 의미 자체도 "~하는 동안" 뜻이고, 결국 while 문 안에 적힌 '_조건_'이 만족되는 동안하는 그 while 문 아래 기술된 내용을 계속 반복수행한다는 의미이다. 그 말을 이렇게 가정할 수 있는데 반복을 수행하는 동안 '_조건_'이 만족하는지 안하는지를 매번 확인한다는 것이고, 그 사이 조건이 변화해서 '_조건_'을 만족하지 않는 어떤 시점이 올 수 있고, 그 시점에 도달 했을 때는 반복을 그만두고, while문을 빠져나와 while 문 다음으로 이어진 문장의 코드가 순차적으로 수행되게 될 것이라는 의미이다. 우리는 일반적으로 반복을 수행하는 동안 '_조건_' 이 변화해 어느 시점에는 '_조건_'이 만족하지 않아 반복을 빠져나와야 할 것으로 기대하는데, 만약 반복을 의도적으로 무한히 하고 싶다면, 어떻게 해야할까? 그것은 '_조건_' 그 자체를 결코 빠져나올 수 없는 조건으로 의도적으로 생생해 주면 되는데 그렇다 '_조건_'을 강제로 항상 참(True)이 되도록 강제할당하면 될 것이다. 이를 파이썬 텍스트코딩 문법으로 표현하면 이렇게 된다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252Fk90YiSIlFygy0QflYrTL%252Fimage.png%3Falt%3Dmedia%26token%3D1b9f32a8-4d9f-4d71-8715-4985f9942562&width=768&dpr=3&quality=100&sign=b5f23882&sv=2) 방금 배운 테크닉을 이용해 이전 장에서 만들어 오고 있는 계산기 프로그램을 더욱더 발전시켜 사용자의 사용성을 개선시켜 나가보자. 사용자 입장에서는 계산기의 계산을 딱 한번의 단회성으로 끝내지 않고, 연이어 게속하려면 현재로서는 프로그램(또는 앱)을 재실행시키는 것 외에는 방법이 없다. 이는 사용성에 제약이 큰 것으로 사용자 본인이 원하는 만큼 계산을 계속하고, 더 이상 필요가 없을 때 프로그램을 종료할 수 있게 해주는 것이 필요하다. 이러한 사용자 요구사항에 맞춰 프로그램을 개선하기 위해 우리는 우리의 코드에 반복문을 추가할 필요가 있다. 실행결과 블록코딩 엔트리-파이썬 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FO6cjwNrV7pWkMpIquntK%252Fimage.png%3Falt%3Dmedia%26token%3D4c0a5780-769a-4466-9890-3c7546b2edb0&width=768&dpr=3&quality=100&sign=45d2ae5f&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252Frq8FpCqiqpavCOAyhz5E%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D98b2f7c4-a310-4333-842d-068d0afe1dd0&width=768&dpr=3&quality=100&sign=85dbf83&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry first = 0 second = 0 def when_start(): Entry.print("당신이 입력한 두 수의 덧셈 또는 뺄셈을 할 수 있는 계산기 입니다") Entry.wait_for_sec(2) while True: Entry.input("첫 번째 숫자를 입력하세요.") first = Entry.answer() Entry.input("두 번째 숫자를 입력하세요.") second = Entry.answer() Entry.input("덧셈계산을 원하시면 '+'를 입력하고, 뺄셈계산을 원하시면 '-'를 입력하세요. 앱을 종료하고 싶으시면 'x'를 입력하세요.") if Entry.answer() == "x": Entry.stop_code("all") if Entry.answer() == "+": Entry.print("입력된 두 수의 합은 " + (first + second) + " 입니다") if Entry.answer() == "-": Entry.print("입력된 두 수의 차는 " + (first - second) + " 입니다") Entry.wait_for_sec(2) 이제 작성한 코드를 이해해보자. 개선된 계산기 프로그램(또는 앱)에서 무한반복해야 할 코드들은 while True: 다음 라인부터에 위치하여 전체적으로 안쪽으로 들여쓰기해 적은 것을 확인할 수 있다. 사용자가 우리의 계산기앱을 종료하고 싶을 때는 사용자 안내문구에서 요구된 것처럼 'x'라는 값을 입력했을 때이고, 이 부분의 처리는 코드 19-20라인에서 만약 사용자로부터 입력된 값이 'x'일 경우에 Entry 라이브러리 안에 있는 앱 종료함수인 stop\_code함수를 호출하되 앱상의 모든 코드를 종료하기 위해 "all"이라는 아규먼트(인자)값을 넘겨서 호출했음을 알 수 있다. [Previous3.4 조건 (Condition)chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax/3.4-condition) [Next3.6 리스트 (List)chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax/3.6-list) Last updated 10 months ago --- # 3.8 함수 (Function) | 엔트리-파이썬 드디어 이제 마지막으로 이번 과의 마지막인 함수 파트만을 남겨두고 있다. 함수는 지금까지 이미 누가 우리를 위해 만들어 놓은 함수만을 호출해서 사용해 보았을 뿐, 나만의 내 함수(사용자 함수)를 직접 만들어 본적이 아직 없는데 이제 그 함수를 직접 만들어 보는 것을 해보도록 하겠다. [3.7 장](https://jjlee.gitbook.io/entry-python/basic_syntax/3.7-random) 에서 만들었던 게임 코드의 일부를 함수로 별도 구현해 동일한 기능구현을 해보는 방식으로 함수를 배워보자. 먼저 코드 중에서 어떤 부분을 사용자 함수(사용자의 개인함수)로 따로 떼어내어 만들지를 정해야 하는데, 사용자가 입력한 수와 컴퓨터가 속의 생각한 수를 비교하는 부분을 떼어서 나만의 사용자 함수를 만들고 그쪽으로 두 수의 비교처리를 위임해 보도록 하자. 실행결과 블록코딩(사용자 함수 미사용) 엔트리-파이썬(사용자 함수 미사용) ellipsis ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FuRtmHop7tgM0AixspQDt%252Fimage.png%3Falt%3Dmedia%26token%3D1050db26-9f19-4c3e-8deb-7d048e8480d1&width=768&dpr=3&quality=100&sign=93a5c822&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FN6Bhy2RK8TYKT5G2kijn%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D498993b0-2807-4d9c-868e-652a5f0dbca4&width=768&dpr=3&quality=100&sign=ab8d2132&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry com_num = 0 my_num = 0 try_total = 0 def when_start(): com_num = random.randint(1, 50) while True: Entry.input("마음 속으로 1부터 50까지의 수를 생각했으니, 한번 맞춰봐! 몇 번만에 맞출 수 있을까?") my_num = Entry.answer() try_total += 1 if my_num == com_num: Entry.print("Bingo! " + try_total + " 번 만에 찾아냈군!") Entry.stop_code("all") else: if my_num < com_num: Entry.print(my_num + " 보단 더 높은 수야") else: Entry.print(my_num + " 보단 더 낮은 수야") Entry.wait_for_sec(2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FNly44dEquse5AZpq9Rcy%252Fentry_block.png%3Falt%3Dmedia%26token%3Decfe5cc4-a037-490c-abeb-bbeca1187cdc&width=768&dpr=3&quality=100&sign=b0f6ca50&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry com_num = 0 my_num = 0 try_total = 0 def compare_num(num1, num2): if num1 == num2: Entry.print("Bingo! " + try_total + " 번 만에 찾아냈군!") Entry.stop_code("all") else: if num1 < num2: Entry.print(num1 + " 보단 더 높은 수야") else: Entry.print(num1 + " 보단 더 낮은 수야") Entry.wait_for_sec(2) def when_start(): com_num = random.randint(1, 50) while True: Entry.input("마음 속으로 1부터 50까지의 수를 생각했으니, 한번 맞춰봐! 몇 번만에 맞출 수 있을까?") my_num = Entry.answer() try_total += 1 compare_num(my_num, com_num) 우리가 이 과의 맨 [첫 장](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world) 에서 함수를 어떻게 만드는지를 배웠기 때문에, 아직 함수 만드는 문법에 대한 이해가 부족하다면 [해당 과](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-4) 로 되돌아 가서 복습하고 돌아올 것을 권한다. 🔢 8~16번 라인의 코드가 [기존의 프로그램 코드](https://jjlee.gitbook.io/entry-python/basic_syntax/3.7-random#undefined-3) 의 16~24인을 떼어내어 compre\_num이란 사용자 함수를 만든 부분이다. 함수의 파라미터로 2개의 숫자값을 전달 받아오게 되어 함수 내부에서 그 두 수의 크기비교 결과에 따른 조건적 실행결과를 보여주게 된다. 함수를 만들고 사용하는 법을 이미 숙지했다면 전혀 이해에 어려움이 없는 부분이다. 다만, 해당 사용자 함수는 사실상 효용성이 많이 떨어지는 사용자 함수로서 왜냐하면 **함수의 주된 목적이 프로그램 코드 내에서 여러 부분에서 반복되어 되어 사용되는 기능을 하나의 함수로 대치하여 전체 코드의 간소화, 그에 따른 코드의 가독성 향상, 추후 발생할 수 있는 코드수정 대응에 용이성 등 여러가지 이점을 누리기 위한 목적**인데, 방금 만들어낸 사용자 함수의 경우는 그 목적에 크게 부합하지는 못하는 단순 학습목적의 사용자 함수로 나만의 함수를 직접 만들어 보고 사용해 보기 위한 인위적인 예시 목적이 더 크다고 할 수 있겠다. 그밖에 **사용자 함수가 더 많은 곳에서 활용되기 위해서는 가능한한 보편적 기능을 구현할 수록 좋은데** 예를들어, 지금처럼 크기비교+비교결과의 화면출력까지 묶어서 기능구현하는 것보다(이처럼 여러 기능이 하나로 묶일 경우, 특정 코드에서만 제한적으로 사용가능하게 되는 단점이 발생) 단순히 크기비교 기능만을 별도 개별 구현한 함수가 되는 것이 함수 활용성을 높일 더 좋은 구현이나 아쉽게도 현재 엔트리-파이썬 수준에서는 이런식의 파이썬 코드구현이 불가능한 한계(함수 결과값을 함수 호출자에게 회신(리턴)하는 기능의 미구현)가 있어서 불가피한 구현이 되었음을 염두해 두길 바란다. 그밖에 위와 같이 함수를 코딩한 후에 시작하기 버튼을 눌러 앱을 실행 후 다시 종료했을 때, 방금 전에 작성한 나의 함수코드가 갑자기 사라져버린 것처럼 보이는데, 실은 해당함수는 블록코딩 내의 함수 카테고리 안으로 자동등록되어 버리면서 코드가 그쪽으로 옮겨져 버린 것이다. 이 부분 역시도 아쉽게도 엔트리-파이썬에서 함수코딩시 사용자 입장에서는 혼동될 수 있는 부분이다. [Previous3.7 무작위 수(Random)chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax/3.7-random) [Next4\. 엔트리-파이썬으로 프로그래밍의 핵심 개념을 익히자chevron-right](https://jjlee.gitbook.io/entry-python/paradigm) Last updated 1 year ago --- # 3.1 Hello World 예제코드의 이해 | 엔트리-파이썬 아마 다른 종류에 컴퓨터 코딩언어 책을 본 경험이 있다면, ["Hello World!" 라는 한 문장을 화면에 출력하는 아주 간단한 내용을 코딩한 예제코드arrow-up-right](https://ko.wikipedia.org/wiki/%22Hello,_World!%22_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8) 의 분석에서 출발하는 것을 본적이 있을 것이다. 우리도 역시 전형적인 이 예제 코드로부터 시작해 문법 익히기를 시작해보자. 실행결과 블록코딩 엔트리-파이썬 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FXFWKffMicSmMX2VubQcc%252Fhello_result.png%3Falt%3Dmedia%26token%3Db985e1a8-89f1-49e7-bb4c-197db429501c&width=768&dpr=3&quality=100&sign=61ddc761&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FkRqFj9B8eHlgkzryeNyf%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D1f956eea-4330-491a-a3e7-ec497365f98a&width=768&dpr=3&quality=100&sign=ce2cb029&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry def when_start(): Entry.print("Hello World!") 앞으로 코드분석은 위와 같이 3개의 탭 형태로 구성하되 맨 처음 탭은 해당 코드의 실행결과를 나타내고, 중간과 마지막 탭은 엔트리-파이썬 / 블록코딩 형태의 코드로 동일한 실행결과를 얻기 위한 각각의 코드를 비교해 볼 수 있도록 구성하였다. 이 예제의 경우 블록코딩으로는 단 2개의 블록 ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FTibOkHZ3t7p13Wka8cvE%252Fstart.png%3Falt%3Dmedia%26token%3Df974cb01-10a2-45dd-aed2-78c7d9040df0&width=300&dpr=3&quality=100&sign=37b6e75a&sv=2)만으로 코딩되는 아주 단순한 프로그램이다. 하지만, 이를 엔트리-파이썬으로 코딩했을 때는 무려(?) 6라인(줄)으로 구성된 코드(코드 가독성을 높이기 위해 넣은 중간마다 넣은 공백라인 포함)로 길어지게 된다. 당장은 여러분에게 이 예제를 직접 타이핑 해볼 것을 권하지 않겠다. 코딩은 이후에 얼마든지 할 기회가 있고, 먼저는 텍스트 코딩예제로서 가장 쉬운 코드라 불리는 이 6라인의 의미를 명확히 이해하는 것이 더 중요하다. 따라서, 여러분은 지금 당장은 이 내용을 블록코딩으로 코딩하고 이후 메뉴에 자동 코드변환하기 메뉴를 선택해 엔트리-파이썬 코드로 변환시켜보기 바란다. 🔢 **1번 라인**의 "_\# Entrybot 오브젝트의 파이선 코드"_ 의 의미는 무엇인가? **텍스트 코딩에서는 기호 하나하나 심지어 때론 공백까지도 어떤 의미와 목적을 갖고 있다는 것을 알고 있을 것이다.** 따라서, 1번 라인에 처음 등장하는 _**#**_ 의 기호 역시 의미와 목적이 있을 것이라는 것을 유추할 수 있고, 현재 _**#**_ 기호 뒤에 적혀진 내용을 통해 미루어 짐작해 볼 수 있는데, "이 기호로 시작해 이후로(단, 한 라인 이내) 적는 내용들을 주석(comment)이라고 간주하라" 라는 의미가 된다. 텍스트 코딩에서 **주석(comment)**라는 것의 의미를 이해해보자.컴퓨터는 우리가 작성한 코드를 최종 실행을 위해 결국에는 실행과정에서 하나하나 해독해 나가게 되는데, 주석이란 부분을 만나면 그 부분은 실행과는 전혀 상관없는 것이니 해독없이 그냥 무시하고 지나가라는 우리편에서의 의미전달로 받아드리고, 컴퓨터는 그 부분을 해독없이 건너 뛰어(skip) 지나간다. 그렇다면 **주석은 왜 필요할까? 목적은 크게 2가지인데,** 하나는 주석이란 그 말 자체가 갖는 의미대로 우리가 작성한 코드를 설명하는 즉, 추가적인 정보를 담아서 나중에 저자 본인이나 우리 동료들이 이 코드를 보았을 때, 빠르고 옳바르게 이해할 수 있게 돕는 목적의 보충설명을 다는 용도이다. 두 번째는 코드의 논리적 오류(문법상의 오류는 없으나, 실행시 의도한데로 수행하지 못하는 오류)를 디버깅(Debugging, 오류를 찾아고치는 과정) 하는 과정에서, 임시로 내 코드의 일부를 주석처리 함으로써 실행과정에서 임시로 내 코드의 일부를 수행없이 건너뛰어(skip) 지나가게 하면서 오류를 빨리 찾아내기 위한 목적에서 사용한다. 🔢 그 다음 **2번 라인**의 분석으로 넘어가자. 그냥 공백으로 비워둔 라인이다. 이러한 라인은 2, 4라인으로 총 2라인이 있다. 왜 공백 한 라인을 넣었는가? 이유는 간단한다. 컴퓨터가 코드해독시 의미를 주기 위한 목적은 전혀 없고, 인간인 우리편에서 코드를 읽을 때 한눈에 잘 들어와 읽히게 하려는 가독성을 주려는 목적에서다. 그런데 가독성은 생각보다 매우 중요한데, 우리는 이제 막 텍스트코딩에 입문한 입문자라 잘 와닿지 않겠지만, 조금만 규모가 있는 프로그램 개발로 넘어가면 나 혼자 개발하는게 아니라, 다른 개발자들과 협업하는게 일반적이다. 그래서, 다른 사람이 나의 코드를 쉽고 빠르고 편하게 읽을 수 있게 늘 배려하는 습관을 몸에 베게 하는게 필요하다. 이는 개발자들에겐 빠져서는 안 될 필수덕목이라 할 수 있겠다. 그래서, 코딩을 하면서 적절하게 공백라인을 사용하고, 또 적절하게 주석을 사용하는 일은 매우 중요하다. 🔢 이제 **3번 라인**으로 넘어가자. _**import Entry**_ 라는 두 단어를 맞났는데, 이 문장에서 import 키워드의 의미는 라이브러리(Library, 파이썬에서는 패키지(package), 그보다 더 작은 단위 모듈(Module)이라고도 불림)를 우리가 현재 코딩하는 파일의 외부에서 가져다가(import해서) 코딩에 활용해 쓰려고 한다라는 것이고, 그 위에 오는 Entry라는 단어는 우리가 필요한 라이브러리의 이름인데, 전체적으로 이 코드의 의미는 Entry(라이브러리의 이름은 그 라이브러리의 저자가 임의로 부여하게 됨) 라는 라이브러리를 외부로부터 호출해 가져와달라는 의미이다. 그렇다면 이제 라이브러리가 무엇인지 알아볼 차례이다. [hashtag](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#lib) 라이브러리란 --------------------------------------------------------------------------------------------- circle-info **라이브러리(Library)**: [소프트웨어를 개발할arrow-up-right](https://ko.wikipedia.org/wiki/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EA%B0%9C%EB%B0%9C) 때 [컴퓨터 프로그램arrow-up-right](https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%93%A8%ED%84%B0_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8) 이 사용하는 [비휘발성 자원arrow-up-right](https://ko.wikipedia.org/wiki/%EB%B9%84%ED%9C%98%EB%B0%9C%EC%84%B1_%EB%A9%94%EB%AA%A8%EB%A6%AC) 의 모임이다. 여기에는 구성 데이터, 문서, 도움말 자료, 메시지 틀, [미리 작성된 코드arrow-up-right](https://ko.wikipedia.org/wiki/%EC%BD%94%EB%93%9C_%EC%9E%AC%EC%82%AC%EC%9A%A9) , [서브루틴arrow-up-right](https://ko.wikipedia.org/wiki/%EC%84%9C%EB%B8%8C%EB%A3%A8%ED%8B%B4) (함수), [클래스arrow-up-right](https://ko.wikipedia.org/wiki/%ED%81%B4%EB%9E%98%EC%8A%A4_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)) , [값arrow-up-right](https://ko.wikipedia.org/wiki/%EA%B0%92_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)) , [자료형arrow-up-right](https://ko.wikipedia.org/wiki/%EC%9E%90%EB%A3%8C%ED%98%95) 사양을 포함할 수 있다. 출처: 위키피디아 위에 라이브러리의 정의를 읽고 이해가 되었는가? **라이브러리를 간단히 설명하면** **개발자들이 프로그램 개발시 빠르고 편리하게 개발하기 위한 목적으로 미리 작성해 놓은 코드덩어리들(주로 함수, 클래스, 변수 등)의 모음**이라고 얘기할 수 있다. 당신은 이미 엔트리에서 함수(Function)를 사용해 봤고, 함수의 존재 목적에 대해 잘 이해하고 있는가? 함수의 주 목적이 무엇이었던가? 여러 곳에서 반복해서 사용하게 되는 코드덩어리를 함수로 만들어 놓게 되면, 이후에는 그 함수블록 1개의 호출만으로 우리가 원하는 기능을 손쉽게 구현이 가능하여 코딩자체가 매우 간소화 되어 코딩의 효율이 증가하는 효과와 동시에 함수로 인해 간소화된 코드는 코드전체의 이해도 크게 증가하는 것을 경험했을 것이다. 그 밖에 **함수의 더 유용하고 본질적인 목적은 한번 잘 만들어둔 함수를 프로그램 전체 코드 내 여러 곳에서(한 개의 오브젝트 또는 다량의 오브젝트 안에서) 사용함으로써(이를 코드의 재사용성이라 함) 한번 만들어진 프로그램이 이후의 변경(프로그램은 마치 살아있는 개체로서 한번 만들어지고 끝이 아닌 계속 반복적인 수정을 통해 발전진화해 나가는 과정을 겪음)에 대해 손쉽게 대응하려는 목적이 더 크다.** 왜냐면 특정 기능을 함수로 만들고, 그 특정기능의 개선을 위해 함수자체의 내용을 수정하게 되면, 그 함수를 사용해 코딩한 모든 코드에서 한꺼번에 동일한 반영이 적용되기 때문이다. 혹시 지금 위해서 언급된 함수의 목적과 의미를 잘 이해하지 못하였다면, 귀하는 엔트리 블록코딩에서 함수를 사용해 코딩해 본 적이 없거나 함수의 존재목적을 잘 이해하지 못한체 사용했을 가능성이 크다. 여기서 함수개념 자체를 다시 재설명하는 것은 본 서의 범위를 넘어서는 것으로, 참고할 수 있는 [동영상 링크arrow-up-right](https://www.youtube.com/watch?v=XRBg448VYzU) 를 남기니 필요한 분은 개인적으로 독학하는 것으로 갈음하겠다. 잘 만든 함수 하나가 얼마나 유용한지 경험했다면, 이제 이러한 고민이 시작될 수 있다. 그 함수를 나 혼자만 쓰긴 너무 아깝지 않은가? 모든 개발자들이 작게는 우리 동료들이, 아니면 전세계의 모든 개발자가 다같이 함께 쓰면 쓸수록 더 좋은게 아닌가. 그래서, 그 잘 만들어진 유용한 함수들을 모두가 함께 쓰는 구조를 만들기 시작했는데 그게 라이브러리(또는 패키지)라고 불린다. 개발자들은 특히나 늘 이런 것에 관심이 많다. 왜냐면 그들은 기본적으로 다 극도의 효율성 추구자들이기 때문이다^^; 먼저, 개별 함수들을 한 곳에 모으고, 다시 함수들을 모은 그것을 또 한 군데 모아나가면서 구조를 만든다. 이를 그림으로 표현해 보면 다음과 같다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FfKPLeLxKAPsmfAWi0xup%252Flib.png%3Falt%3Dmedia%26token%3D1080fd0e-acda-4c1b-8a6c-e60e9f439169&width=768&dpr=3&quality=100&sign=6eff5dde&sv=2) _이해하기 쉬운 적절한 비유로 무엇이 있을까 고민했을 때, 라이브러리(library)는 말을 붙인 의도가 있을 것 같은데, 말그대로 도서관이고 도서관은 책들로 가득한데, 개별적인 단행본 개념의 단권짜리 책도 있지만, 때로는 시리즈물 전집책도 상당히 있는데, 그런 전집류를 패키지(package)라고 생각해 볼 수 있고, 단 권짜리 책들은 모듈(module), 마지막으로 책 안에는 책의 내용을 구성하면서 의미단위를 만드는 장, 절, 단락, 문장 등이 있는데 이것들을 클래스, 함수, 변수 등으로 비교해 설명해도 무리가 없지 않을까 한다._ _지금은 그냥 라이브러리 안에는 우릴 위해 미리 남들이 개발해 놓은 유용한 코드뭉치(특별히 주로 함수들)로 가득하고, 우리는 그냥 그것을 가져다가 활용해 코딩을 한다 정도로만 알고 있어도 충분할 것 같다._ 🔢 드디어 **5번 라인**에 도달했다. 이게 코드분석의 마지막이라고 생각해도 좋다. 왜냐면 5~6번 라인은 별개로 분리된 코드가 아닌 하나의 의미를 갖는 코드뭉치이기 때문이다. Copy def when_start(): Entry.print("Hello World!") _**def when\_start():**_ 이 코드의 의미는 무엇일까? 앞에서 이미 수차례 언급된 바로 그 함수라는 것을 직접 만들고 있는 코드이다. 초장부터 함수를 이용하기 위해 외부에서 부르는 방법을 익히더니 이젠 직접 만드는 방법까지! 그만큼 텍스트 코딩에서는 함수가 필수적인 개념이다. 우선 파이썬 텍스트코딩에서 일반적인 함수 만드는 문법을 알아보자. [hashtag](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#func_def) 함수 만드는(정의하는) 법 ---------------------------------------------------------------------------------------------------------- ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FGKZp1MEXcUo96FYI2vOr%252Ffunc_def.png%3Falt%3Dmedia%26token%3Dc00fbf24-ebdb-4f91-aa04-d3133221272f&width=768&dpr=3&quality=100&sign=c8542811&sv=2) 위에 표준문법의 각 단어와 기호의 의미를 이해해 보자. _함수를 만들려면 문두 시작에 항상 def(definition의 약자)라는 키워드를 달고 시작한다._ 이제부터 내가 적는 내용은 함수를 만드는(정의(definition))하는 거야라고 의미표현이다. 그리고, 이어서 내가 만들 함수의 이름을 의미있게 임의로 적는다. 그리고, 그 함수 호출시 호출자로부터 넘겨받을 값들(이를 프로그래밍 세계에서 파마리터라고 부름)을 괄호 안에 쉼표(,) 구분자를 사용해 나열한다. 그리고, 마지막에 콜론(:)를 기호를 입력함으로써 여기까지 함수뼈대의 끝이고, 이제 다음 라인부터는 함수 안에서 실제 수행해야할 코드내용이 채워질꺼라는 의미가 부여된다. circle-info **파라미터(parameter)와 아규먼트(argument) 차이** **파라미터(parameter)** * 파라미터는 함수를 정의할 때 사용되는 변수입니다. * 함수 정의에서 괄호 안에 나열되는 변수들을 의미합니다. **아규먼트 (Argument)** * 아규먼트는 함수를 호출할 때 실제로 전달되는 값입니다. * 함수 호출 시 괄호 안에 전달되는 실제 값들을 의미합니다. _**def when\_start():**_ 를 위에 표준문법에 다시한번 빗대어 설명해 보면 when\_start 라는 이름을 갖는 함수를 만드는데, 그 함수에 전달되어질 값(파마리터)은 전혀 없는(그래서, 괄호 안은 비워져있다) 함수를 만들겠다 라는 것이다. 🔢 이제 마지막 **6번 라인**으로 넘어가면 이 함수가 불려졌을 때 수행해야하 할 내용을 적는데, _특이한 것은 다른 라인들과 달리 문두 시작전에 앞에 어느 정도의 공백을 띄우고(Tab키를 1회 눌러 한번에 공백을 만들거나 또는 스페이스바를 4회눌러 4개의 공백을 넣음) 코드를 적기 시작한다는 것이다._ 앞서 프로그래밍 세계에서 공백조차도 때론 어떤 의미를 갖는다고 언급했었는데 바로 이런 경우의 공백으로서 파이썬 표준문법에 존재하는 의도성을 갖는 공백이다. 함수에서만 쓰이는게 아니라, 이후 다른 여러 코드뭉치에서 반복해서 사용될 것이니, 처음부터 잘 알아두자. **이 공백의 의미를 간단히 설명하면, 매 라인마다 같은 크기의 공백을 둠으로써 각 라인은 서로 묶여진 한 코드덩어리임을 나타내기 위한 목적이다.** 이를 우리 코드에 빗대어 보면 즉, when\_start라는 함수에 속한 코드가 어디서부터 어디까지인지를 컴퓨터에게 알려주기 위해 공백을 사용한 것이다. 지금 이 함수는 호출되면 그 안에 딱 1개의 라인만 수행하는 너무 간단한 함수라 이 말이 잘 와닿지 않을 수 있는데, 추후에 더 많은 코드를 수행하는 수행내용이 긴 함수를 보면 더 잘 의미가 와닿을 것이다. 마지막으로 _**Entry.print("Hello World!")**_ 을 분석하자. 하나씩 뜯어보면, Entry라는 라이브러리 이름을 적었고, 이후에 점(.)을 찍었고, 이후에 print("Hello World!") 를 적었다. 앞서 프로그래밍 세계에서 점(.) 하나도 의미가 있다고 했는데, _**Entry.**_ 라고 라이브러리 이름에 점을 찍으면, 그 라이브러리 안에 있는 것들 즉 함수(또는 클래스, 변수 등)를 사용하겠다는 의미이고, 그 뒤에 연이어 적인 _**print("Hello World")**_가 바로 Entry 라이브러리 안에 위치한 함수이다. 결국 이 라인에 적힌 코드의 의미는 1번 라인에서 import해 가져온 Entry 라이브러리 안에 위치한 print라는 함수를 호출해 사용하겠다는 의미이다. print라고 이름지어진 뜻에서 유추할 수 있듯이, 화면에 어떤 값을 출력하는 용도이고, 출력할 내용은 "(큰따옴표)안에 적음으로써 화면표기될 내용의 시작이 어디고 끝인지를 의미표기해 전달한 것이다. 휴~ 이로써 6라인짜리 아주 간단한 프로그램의 코드분석은 다 끝났다! 속시원한가? 엔트리의 "시작하기" 버튼을 누르면 저 6라인의 코드가 실행되면서 화면에 우리가 원하는 결과(Hello World! 라고 말하기)가 출력되는가? 잘 출력될 것이다. 코드가 다 해석되었고, 실행도 잘된다. 그럼에도 당신에겐 속시원하지 않고 뭔가 다 해결되지 않은 의문의 찜찜함이 여전히 남아야 한다. 남았는가? 남지 않았다 하더라도 실망하지 말자. 남아야 하는 의문은 우리의 단지 이 6라인짜리 코드에서 과연 어떻게 원하는데로 동작하는 결과가 나왔지? 라고 오히려 의아해 해야 하는 것이다. 원하는 결과가 나오기에는 사실상 코드가 완전하지는 않기 때문이다. 분명히 코드가 더 있어야 되는데 말이다. 우리는 when\_start 함수를 정의만 했지 사용하기 위해 이를 호출한 코드는 어디에도 존재하지 않기 때문이다. 구체적으로 when\_start 함수를 내 코드 안에 어딘가에서는 호출을 해야하고 그래야 그 호출에 의해 함수가 실행되면서 화면출력이 이뤄지는 것인데, 그런데 우리 6라인짜리 프로그램 내 코드 안 어디서도 when\_start함수를 직접 호출하는 코드는 없다. 그렇다면 저 함수는 누가 호출해서 어떻게 실행이 된 것이란 말인가?? [hashtag](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#callback) 콜백함수란 ------------------------------------------------------------------------------------------------- 답을 말하면, 우리 프로그램 내 코드에서 직접 호출한게 아니라 우리 프로그램 밖의 외부에서 저 함수의 호출이 이뤄진 것이다. 그렇다면 누가?? 그렇다. 호출자는 바로 엔트리 프로그램 그 자신이다. 엔트리가 우리 함수 when\_start를 자동으로 호출한거다. 여기서 상식으로 알아두면 좋을 용어하나가 등장하는데 이와같이 함수긴 함수인데 내가 직접 호출이 없이 외부에서 어떤 시점에 자동호출되는 함수를 콜백(callback)함수라고 부른다. 그렇다, when\_start는 콜백함수였던 것이다. 이러한 콜백함수는 함수 이름을 우리 맘대로 정할 수 없다. 엔트리 자신이 이미 정해놓은 함수이름을 그대로 사용해야만 하는 것인데 즉, 엔트리 내부적으로 어떤 약속이 되어 있냐면, 사용자가 "시작하기" 버튼을 누르는 즉시, 엔트리는 내부적으로 항상 when\_start 함수를 호출하기로 코딩되어져 있는 것이다. 혹시 이 말을 믿지 못하겠다면, when\_start 라는 함수이름을 의도적으로 다른 것으로 바꾼 후, "시작하기" 버튼을 눌러보도록 하자. 기대했던 동작이 이뤄지는가? 정상적인 실행이 되지 않을 것이다. 우리 프로그램 안에는 사전 약속되어진 필수로 존재해야만 하는 바로 그 when\_start 함수가 존재하지 않기 때문이다. 단지, 6라인짜리 작은 프로그램이었지만, 텍스트 코딩의 많은 핵심을 이해할 수 있는 좋은 예제였음에 틀림이 없다. 다음 단계로 프로그래밍을 구성하는 핵심요소(순차, 반복, 조건 등등)들을 하나 하나씩 격파해 나가도록 하자. [Previous3\. 엔트리-파이썬으로 파이썬 언어의 기본문법을 익히자chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax) [Next3.2 입/출력 (Input/Output)chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax/3.2-input-output) Last updated 10 months ago --- # 3.4 조건 (Condition) | 엔트리-파이썬 블록코딩에서 사용빈도 매우 높은 블록으로 "만일 ~이라면" 과 "만일 ~이라면, 아니면~" 의 두 가지 블록이 있다. 용도는 우리의 코드가 순차적으로 실행해 나갈 때, 어떤 조건에 따라 실행을 할 수도 있고, 하지 않을 수도 있는 경우에 따른 동작을 만들기 위한 용도라 할 수 있다. 이전 장에서 만들어 오고 있는 계산기 프로그램에서 사용자로부터 입력된 임의의 두 값만을 더해주는 것 외에 사용자에게 더 나은 사용성을 주기 위해 덧셈 뿐 아닌 뺄셈도 가능하고, 둘 중 어떤 계산을 선택할지를 사용자의 선택권에 맡기는 더 나은 프로그램으로 발전시켜보자. 이처럼 소프트웨어을 만드는 일을 한번의 단회성의 제작으로 끝나는게 아니라, 계속 진화해나가면서 더 나은 프로그램으로 버전업이 되어 가는 것이 일반적이며 이를 소프트웨어의 생명주기라고 할 수 있다. 실행결과 블록코딩(1) 엔트리-파이썬(1) 블록코딩(2) 엔트리-파이썬(2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F7AHP6cfvGUOtPEMnN0VT%252Fimage.png%3Falt%3Dmedia%26token%3Db6f9762a-63a3-430e-853e-c4ae986d8236&width=768&dpr=3&quality=100&sign=2e8a5022&sv=2) ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F9IBVAU2zEcAEoQLtfTJZ%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D80a3f2ec-878e-4017-8e3f-4ded28c5e890&width=768&dpr=3&quality=100&sign=a3ed8434&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry first = 0 second = 0 def when_start(): Entry.print("당신이 입력한 두 수의 덧셈 또는 뺄셈을 할 수 있는 계산기 입니다") Entry.wait_for_sec(2) Entry.input("첫 번째 숫자를 입력하세요.") first = Entry.answer() Entry.input("두 번째 숫자를 입력하세요.") second = Entry.answer() Entry.input("덧셈계산을 원하시면 '+'를 입력하고, 뺄셈계산을 원하시면 '-'를 입력하세요.") if Entry.answer() == "+": Entry.print("입력된 두 수의 합은 " + (first + second) + " 입니다") if Entry.answer() == "-": Entry.print("입력된 두 수의 차는 " + (first - second) + " 입니다") ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FqXizo0bmHY3v1ykNCFUE%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3Dbdddcf11-5935-4a0d-b3ba-7c4721b6417b&width=768&dpr=3&quality=100&sign=8c99e8a3&sv=2) Copy # 엔트리봇 오브젝트의 파이선 코드 import Entry first = 0 second = 0 def when_start(): Entry.print("당신이 입력한 두 수의 덧셈 또는 뺄셈을 할 수 있는 계산기 입니다") Entry.wait_for_sec(2) Entry.input("첫 번째 숫자를 입력하세요.") first = Entry.answer() Entry.input("두 번째 숫자를 입력하세요.") second = Entry.answer() Entry.input("덧셈계산을 원하시면 '+'를 입력하고, 뺄셈계산을 원하시면 '-'를 입력하세요.") if Entry.answer() == "+": Entry.print("입력된 두 수의 합은 " + (first + second) + " 입니다") else: if Entry.answer() == "-": Entry.print("입력된 두 수의 차는 " + (first - second) + " 입니다") 기존 프로그램에서는 변수 3개(first, second, result)를 사용했다면, 이번 프로그램에서는 변수 2개(first, second)만 사용하도록 개수를 줄였는데, 줄이는 방법은 기존에는 두 값의 계산결과를 다시 result라는 변수에 담아서 그 변수에 담긴 값을 출력하는 식으로 했다면, 이번에는 해당 변수없이 (first + second), (first - second)라고 계산식을 괄호와 함께 사용함으로써 출력함수(print함수) 안에서 직접계산하여 결과 출력하도록 하였다. 🔢 17번 라인을 살펴보자. 우리는 사용자에게 '+' 또는 '-' 둘 중에 하나를 선택해 입력는 선택권을 주도록 안내했기 때문에 사용자가 둘 중에 하나의 원하는 연산종류를 정상적으로 입력한다고 가정하고, 사용자가 선택한 연산종류에 따라 코드실행을 달리해야하기 때문에 조건문을 사용했는데 파이썬 언어에서 조건문을 사용하는 문법은 다음과 같다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FN5Lnc18006BTtlAzCh3H%252Fimage.png%3Falt%3Dmedia%26token%3De8f115b5-3af5-4742-ade0-0330d806c3d5&width=768&dpr=3&quality=100&sign=bfcd7d4a&sv=2) 🔢 위에 문법에 빗대어 18-19번 라인의 다음의 코드를 이해할 수 있는지 확인해 보자. Copy if Entry.answer() == "+": Entry.print("입력된 두 수의 합은 " + (first + second) + " 입니다") [이전 장](https://jjlee.gitbook.io/entry-python/basic_syntax/3.3-variable) 에서 '==' 기호(연산자)의 의미가 무엇인지 설명했다. 좌우의 두 값이 같은지를 확인하기 위한 연산자(operator)로서 사용자가 입력한 값이 '+' 였는지를 확인하고 있다. 만약 사용자의 입력값이 '+' 라면 그렇다면, if 로 시작한 문장의 다음에 위치한 코드들이 실행될 것이다. 여기서 코드작성시 주의해야 할 것은 우리가 [Hello World 예제코드 분석](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world) 에서 알아본 바와 같이 if 문 다음에 의도적인 공백(일반적으로 키보드의 스페이스바로 4칸 또는 탭으로 1칸을 누름)을 만들어 **매 라인마다 같은 크기만큼 공백을 둠으로써 if 조건이 만족에 경우의 수행할 코드덩어리의 시작과 끝이 어디인지를 분간할 수 있게 해주야 한다는** 것이다. 따라서, 공백을 정확하게 입력해야 한다는 것을 잘 숙지해야 하겠다. 🔢 위에 조건문의 사용이 정확하게 이해되었다면, 그 다음 20-21번 라인의 코드는 당연히 쉽게 이해될 것이다. 마지막으로, 위에 예제코드를 블록코딩(1과 2), 텍스트코딩(1과 2)로 구분해 놓은 이유를 설명해야 할 것인데, 1과 2의 두 종류의 코드는 모두 우리가 목표한 동일한 기능을 하되, 다만, 구현방법에 있어서 약간 다른 방식으로 구현한 코드로서 지금 배운 조건문 외 한 가지 더 추가적인 조건문 사용문법을 설명하기 위한 의도가 있었다. 우리가 블록코딩에서 "만일 ~이라면" 이라는 블록의 텍스트코딩에서는 어떻게 표현하는지를 지금까지 배웠다면, "만일 ~이라면, 아니면~" 이란 블록의 사용법도 텍스트코딩에도 존재함을 미루어 짐작할 수 있는데, 이를 문법사용법은 다음과 같다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252FRqGX3baSBdSASJhVKKTV%252Fimage.png%3Falt%3Dmedia%26token%3D30d100bf-344d-4cf4-a5c7-938ef944bcc1&width=768&dpr=3&quality=100&sign=2afff0f1&sv=2) Copy if Entry.answer() == "+": Entry.print("입력된 두 수의 합은 " + (first + second) + " 입니다") else: if Entry.answer() == "-": Entry.print("입력된 두 수의 차는 " + (first - second) + " 입니다") 위에 코드는 아래와 같이 블록코딩과 비교해보면 더 쉽게 이해할 수 있다. ![](https://jjlee.gitbook.io/entry-python/~gitbook/image?url=https%3A%2F%2F1991069597-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FnDUP8xZ7pbezrK2wo5dX%252Fuploads%252F2s0DVlE6iDoNy9xrXR97%252F%25E1%2584%258B%25E1%2585%25A6%25E1%2586%25AB%25E1%2584%2590%25E1%2585%25B3%25E1%2584%2585%25E1%2585%25B5%2520%25E1%2584%2587%25E1%2585%25B3%25E1%2586%25AF%25E1%2584%2585%25E1%2585%25A9%25E1%2586%25A8.png%3Falt%3Dmedia%26token%3D053a9cd2-a7af-4580-8ef4-21450c5cc8cd&width=768&dpr=3&quality=100&sign=241b8e0c&sv=2) 다만, if~else~ 형식의 텍스트코딩의 조건문의 사용상에 있어서도 주의해야할 부분은 이전과 마찬가지로 각 코드의 라인마다 동일한 공백을 길이를 얼마만큼 유지해야만 하는지에 주의를 기울여 코딩해야 한다는 것이다. **블록코딩에서는 비주얼적으로 조건문에 시작과 끝이 명확하게 감싸져있는 방식으로 표현해서 우리가 직관적으로 이해가 가능했지만, 텍스트코딩에서는 텍스트로만 이를 표현해야하는 한계성 안에서 블록코딩 표현상 감싸져 있는 코드덩어리는 텍스트코딩에서는 매 라인마다 동일 공백량의 길이조절을 통해(들여쓰기를 통해) 이를 표현하고 있음을 이해할 수 있다.** [Previous3.3 변수 (Variable)chevron-left](https://jjlee.gitbook.io/entry-python/basic_syntax/3.3-variable) [Next3.5 반복 (Loop)chevron-right](https://jjlee.gitbook.io/entry-python/basic_syntax/3.5-loop) Last updated 1 year ago --- # 5.7 두더지 게임을 만들어요 - 종합 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 드디어 이 책의 마지막 예제에 도달한 여러분을 환영한다. 이전 예제들을 격파하고 여기까지 온 여러분들은 파이썬 코딩의 재미도 서서히 느끼고 있을 것이라 생각한다. 생각보다 텍스트코딩이 많이 어렵거나 지루하지는 않을 것으로 믿는다. 그렇다면 여러분들은 이 책이 목표했던 텍스트 코딩세계 입문이라는 소기의 목적을 충분히 얻은 것으로 여겨도 좋겠다. 마지막 예제는 지금까지 예제의 종합판이라 이전 예제보다는 확실히 난이도가 있다. 하지만, 시간이 걸릴 뿐 차근차근 이해해 나가면 될 것이다. Copy from pgzhelper import * import random WIDTH = 960 HEIGHT = 540 hammer = Actor('toy_hammer', (WIDTH / 2, HEIGHT / 2)) hammer.scale = 0.5 hammer.angle = 40 score = 0 hammer_pressed = False GAP_FROM_SCR = 50 moles = [] for _ in range(6): mole = Actor('mole') mole.anchor = ('left', 'top') x = random.randint(GAP_FROM_SCR, WIDTH - mole.width + GAP_FROM_SCR) y = random.randint(GAP_FROM_SCR, HEIGHT - mole.height + GAP_FROM_SCR) mole.pos = (x, y) mole.scale = 0.5 mole.visible = False moles.append(mole) def draw(): global score screen.blit('field', (0, 0)) for mole in moles: if mole.visible: mole.draw() if hammer_pressed and mole.visible and mole.collide_pixel(hammer): sounds.toi.play() moles.remove(mole) score += 1 hammer.draw() screen.draw.text('Score: ' + str(score), (20, 20), color='black') def update(): if random.randint(0, 10) == 0: if len(moles) != 0: mole_list = random.sample(moles, 1) mole_list[0].visible = not mole_list[0].visible else: game.exit() def on_mouse_move(pos): hammer.centerx, hammer.centery = pos def on_mouse_down(): global hammer_pressed hammer_pressed = True animate(hammer, angle=75, tween='accelerate', duration=0.1, on_finished=animation_done) def animation_done(): global hammer_pressed animate(hammer, angle=40, tween='accelerate', duration=0.1) hammer_pressed = False 🔢 엔트리버전 두더지 잡기게임과 다른 점이 하나 있는데, 엔트리 버전에서는 두더지들의 위치는 고정이나, 파이게임제로 버전에서는 두더지의 위치가 초기에 랜덤(무작위, random)하게 설정된다는 것이다. 먼저 랜덤을 활용 하기 위해선 **random** 이란 별도의 외부 모듈이 필요하고, 따라서 2번 라인에서 해당 모듈을 import하고 있다. 14-24라인까지의 코드가 화면상에 램덤하게 위치시킬 총 6개의 두더지 Actor 오브젝트들을 만들고, 이 만들어진 6개를 **moles** 라는 리스트에 담아서 활용한다. 19-20 라인에서 사용한 **randint** 라는 메소드는 엔트리에서 사용해봤던 "무작위 수" 블록과 크게 다르지는 않다. 특정범위에 랜덤한 숫자를 무작위 선택하기 위한 범위의 최대/최소값을 인자값으로 전달해야 한다. 다만, 메소드 이름에 int(integer, 정수)가 포함되어 있는 것으로 통해 정수형 숫자범위에서 선택한다는 것에 주의하다. 참고로 만약 실수형(float, 실수)의 랜덤값이 필요한 경우에는 쉽게 예상할 수 있는데 **randfloat** 라는 메소드를 사용하면 된다. **randint** 메소드로 전달하는 최초/최대값에는 **GAP\_FROM\_SCR** 이란 이름으로 50 픽셀의 여분(마진, margin)값을 사용하고 있는데, 목적은 다른게 아니라 랜덤하게 위치선정 할 때, 게임화면 테두리에 딱 붙어서 위치선정 되버린 경우, 출력된 두더지 이미지가 화면끝에 걸처 짤려보이거나 하는 문제를 막기 위해 화면 테두리로부터 50픽셀씩 안쪽으로 들어와서 랜덤위치값을 찾도록 그 여분값을 준 것이다. 23번 라인에 **visible** 이란 처음 등장한 객체속성을 사용하고 있는데, **이 속성은 Actor 객체에 내장속성이 아니다. 우리의 게임개발에 필요해서 임의의 추가한 우리만의 사용자 속성이다. 이처럼 여러분들도 필요하면 기존 객체안에 임의로 속성을 추가할 수도 있다는 것도 기억해 두자.** _목적은 두더지 게임 특성상 어떤 두더지는 화면에 보이고/안보이고의 구분이 필요한데 이를 구분하기 위한 지난 장에서 언급해선 일종의 플래그변수와 같은 것이다._ 🔢 게임의 재미를 위해선 임의의 두더지의 보이고/안보이고 하는 시점도 랜덤화가 필요하다. 이를 구현한 코드가 update 함수 안에 44-47번 라인의 코드이다. 우리는 _게임에 사용되는 객체들의 위치, 상태 등의 정보를 업데이트하는 update 콜랙함수가 상당히 자주 호출된다는 것은 알고 있는데, 얼마나 자주인지는 잘 모르고 있었다. 드디어 그 속도를 언급할 필요가 있는데, 초당 60프레임의 속도(60FPS(Frame Per Second))로 무려 초당 60회로 상당히 빠르게 자주 콜백호출되고 있다._ 문젠 60fps 속도는 우리에겐 너무 빠르다는 것인데, 지나치게 빠르지도 느리지도 않는 게임의 재미를 충족시킬 수 있는 정도의 속도 안에서 살아있는(아직 게임유저가 때려잡지 못한) 두더지들 중 랜덤하게 선택해서 보였다/숨겼다 하는게 필요하다. 그 어느정도의 속도(0~10사이값 중 0이 선택되는 경우마다)는 44라인에서 구현하였고, 살아있는 두더지들 중에 1개를 임의로 택하는 것은 **random.sample** 메소드와 함께 45-46라인까지 구현되었고, 47번 라인에서는 선택된 두더지의 가시성 속성(visible)을 반전시킴으로써 보이고/안보이고를 조정하고 있다. 🔢 이제 남은 코드의 상당 수는 고무망치의 애니메이션에 관한 부분이다. 지난 예제에서의 애니메이션이 한 Actor 안에 여러 이미지들의 로테이션에 의한 애니메이션이라면 이번 애니메이션은 현재 위치에서 특정 위치나 각도까지 정해진 시간 안에 이동하는 애니메이션에 관한 것이다. 고무망치는 마우스 클릭한 시점에 맞춰 고무망치가 때리는 시늉을 위해 앞쪽으로 어느정도 각도(75도)로 빠르게 기우려졌다가 다시 현재각도(40도)의 모습으로 빠르게 되돌아 와야 한다. 59번 라인의 **animate** 메소드의 사용이 처음 등장했고, 총 5개의 인자값을 사용했는데 정리하면 어떤 객체(여기서는 hammer)의 어떤 속성(여기서는 angle)을 얼마만의 시간(여기서는 0.1초)안에 어떤 종류(여기서는 accelerate)의 애니메이션을 할 것인가에 관한 것이다. 애니메이션의 종류로 **accelerate** 은 애니메이션 속도가 시간이 지나면서 가속화되는 것을 연출하는 애니메이션이다. 그 종류는 총 10가지가 준비되어 있고, 각각에 대해서는 역시나 [라이브러리 메뉴얼arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/builtins.html#animations) 을 통해 확인이 가능하다. 인자값 중에 마지막 값인 **on\_finished** 의 파라미터값에 대해서도 궁금할 것이다. animation\_done 이란 62번 라인에서 정의한 함수이름을 넘기고 있다. 그렇다 해당 파라미터값의 이름처럼 애니매이션이 종료된 시점에 인자값으로 넘긴 함수를 콜백호출 해주라는 것이다. 목적은 여러가지가 있을 수 있는데, 우리의 목적은 고무망치가 숙여지는 애니매이션이 끝나자마자 다시 현재 위치로 원복하는 애니메이션을 연이어서 하도록 하려는 것이다. 🔢 우리는 사용자가 두더지를 때려잡은 시점에 대한 파악은 어떻게 해야 할까? 34번 라인에 그 해답이 있다. 3가지 조건이 다 만족되어야만 한다. 고무망치 애니메이션이 앞쪽으로 숙여지는 중이어야 하고(숙여진 고무망치가 들어올려지는 동안이 아닌), 두더지는 숨겨진 상태가 아닌 화면에 보여지는 상태여야 하며, 마지막으로 고무망치와 두더지 사이에 상호간의 충돌검사가 통과하는 이 3가지가 다 만족해야만 두더지를 때려잡은 것이다. 때려잡기에 성공했기 때문에 성공의 의미로 효과음을 내고(35라인) 잡은 두더지는 두더지들의 모음인 moles 리스트에서 없에버리고(36라인), 마지막으로 획득한 점수를 카운트 해주면 된다.(37라인) 이것으로 두더지게임의 모든 코드설명이 끝났고, 마지막으로 게임실행 결과를 보는 것으로 마무리 하자. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FAP6aMLVTWV4kOXGEALED%252FKapture%25202024-07-25%2520at%252016.33.24.gif%3Falt%3Dmedia%26token%3D8b55734f-79fb-4dd3-aaac-e51a528696a6&width=768&dpr=3&quality=100&sign=839b7521&sv=2) 이 예제까지 해서 이 책을 끝마친 여러분을 다시한번 축하하고 이로써 여러분은 드디어 파이게임 제로의 초보단계는 넘어서게 되었다. 그래서 여러분 이제 파이게임 제로의 더 심화단계의 책으로 넘어갈 준비가 되었다. [다음 단계의 책](https://jjlee.gitbook.io/python-pygame_zero/) 에서는 지금까지 살펴본 예제같지 않고, 좀 더 게임다운(?) 게임들을 만들어보면서 훨씬 더 재미있게 파이썬 코딩의 실력을 쌓게 될 것이다. 벌써 기대가 되지 않는가? 지금까지 여러분의 텍스트코딩 첫 여정을 함께할 수 있어서 기뻤고, 저자는 계속되는 여러분들의 이 여정을 응원할 것이다. 또한 그 코딩여정에 실질적인 도움이 되고자, 학습에 도움이 될 만한 코딩도구와 책들을 꾸준히 생산하도록 노력하겠다. 마지막으로 이해가 안되는 예제애 대한 문의나 추가 의견이 있을 경우, [온라인 커뮤니티arrow-up-right](https://discord.com/channels/1201625272634576936/1201632809618509904) 를 통해 소통하자. 여러분의 여정은 결코 혼자자 아니다! ☀️ 2024년 핫(hot) 뜨거운 여름 한 자락에서 저자 올림☀️ [Previous5.6 암호를 입력해 보물상자를 열어요 - 입출력chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/6-treasure_box) [Next부록chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix) Last updated 1 year ago --- # 5.6 암호를 입력해 보물상자를 열어요 - 입출력 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 우리는 지난 장들에서 마우스 입력(drag, click 등)에 대해서는 충분히 사용해 보았다. 이번 장에서는 이 장의 제목답게 처음으로 키보드 입력처리에 대해 살펴볼 것이고, 처음으로 게임의 흥미를 더하기 위해 효과음 파일을 사용해 사운드를 출력하는 방법도 배울 것이다. 참고로 파이게임에서의 키보드 입력에 대한 처리는 엔트리에서의 처리처럼 입력창을 통해 원하는 내용을 한번에 입력 후 엔터키를 눌러 최종 입력을 완료하는 방식이 아니라, 키보드의 키를 누르는 행위(이벤트)마다 호출되는 콜백함수에서 입력내용 즉시 수집하는 방식, 즉, [이벤트 주도형 패러다임](https://jjlee.gitbook.io/entry-python/paradigm/4.3-event-driven) 으로 처리를 하게 됨을 사전에 인지하면 좋겠다. 여러분들은 앞선 5개의 예제를 해결해봤기 때문에 이제 더이상 파이게임제로의 왕초보라고 말할 순 없겠다. 이제 남은 예제 2개 정도는 코드 하단의 설명을 곧바로 보기보다 먼저 전체코드들을 눈으로 이해할 수 있는 수준까지 최대한 이해해 보도록 노력해보자. 그리고나서 하단의 코드설명을 보는 순서로의 진행을 도전해 보자. 앞으로도 이런 식의 공부가 여러분들의 실력을 키우는 길이다. 여러분들도 경험상 잘 아시겠지만, 빠른 답은 결코 우리의 생각이 자라날 기회는 주지 못하기 때문이다. Copy import pygame from pgzhelper import * WIDTH = 960 HEIGHT = 540 treasure = Actor("treasure_box", (WIDTH / 2, HEIGHT / 2)) treasure.images = ["treasure_box", "treasure_box_o"] input_text = "" input_done = False input_rect = Rect(350, 450, 200, 50) guide_rect = Rect(300, 50, 400, 50) password = "1234" def draw(): screen.blit("desert", (0, 0)) treasure.draw() # 설명 텍스트상자 screen.draw.filled_rect(guide_rect, 'black') if input_done: if input_text == password: screen.draw.textbox("You got it!", guide_rect) else: screen.draw.textbox("You failed!", guide_rect) pygame.display.update() game.exit() else: screen.draw.textbox("Please input a password.", guide_rect) # 입력 텍스트상자 screen.draw.filled_rect(input_rect, "pink") screen.draw.textbox(input_text, input_rect) def update(): if input_done: if input_text == password: treasure.sel_image("treasure_box_o") sounds.cheer.play() else: sounds.warning.play() def on_key_down(key, unicode): global input_text, input_done if key == keys.RETURN: input_done = True elif key == keys.BACKSPACE: input_text = input_text[:-1] else: input_text += unicode 🔢 먼저 이번 코드에서 여러차례 사용되었던 Rect라는 것에 대해 알아보자. 대문자로 시작하는 것으로 보아, Rect 는 오브젝트라는 것을 알 수 있다. Rect 는 Rectangle 의 약자로 사각형 모양의 영역을 붙잡고 있는 오브젝트로 게임이란 무대에서 등장하는 대부분은 기본적으로 사각형 모양을 갖는 객체이기 때문에, 이 Rect의 활용이 두들어 진다고 할 수 있다. 만드는 법은 4개(left, top, width, height) 의 인자값으로 사각의 영역을 나타내는데, 앞에 두 값(left, top)은 좌상단 좌표값으로 화면상에 사각영역이 시작하는 시작위치를 나타내는 것이고, 뒤에 두 값(width, height)은 가로길이와 세로길이로 이 값들은 그 영역의 크기를 정하게 된다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252Fh0MupLAn54jaLB8ZoBF8%252Fimage.png%3Falt%3Dmedia%26token%3De9258d17-c01c-41c1-a480-6b110c80f69f&width=768&dpr=3&quality=100&sign=350a86f4&sv=2) 우리는 과거에 이 예제를 이미 엔트리를 통해 만들어 봤기 때문에 이 예제는 사용자와 텍스트적인 인터렉션이 있다는 것을 알 수 있다. 보물상자를 열기 위해 비밀번호를 입력할 것을 안내할 것이고, 입력된 비밀번호가 맞는지의 유무에 따라 최종 보물을 갖게 되었는지/실패했는지를 소리와 함께 텍스트로도 보여주게 된다. 그래서, 우리는 이 예제에 두 개의 Rect 가 필요한데 한 개는 사용자로부터 비밀번호 입력받게 될 사각형의 입력란(input\_rect)에서 사용하고, 나머지 하나는 사용자에게 어떤 행동을 지시하거나 결과를 가이드 하는 용도의 텍스트를 출력하는 영역(guide\_rect)으로서 사용하게 된다. 🔢 10번 라인의 **input\_done** 변수는 아래의 참고사항에 언급된 플래그 변수이고, 저장되는 값은 참(True)/거짓(Faluse) 둘 중에 하나의 값을 저장한다. circle-info 플래그(flag) 변수: 프로그래밍에서 코드간의 상태전달을 알리기 위해 사용하는 참(True)과 거짓(Faluse) 또는 0과 1의 이진값(boolean)을 갖는 변수 참고: 위키피디아([https://en.wikipedia.org/wiki/Flag\_(programming)arrow-up-right](https://en.wikipedia.org/wiki/Flag_(programming)) ) 사실 플래그 변수는 이번에 처음 등장한 것은 아니고, 이미 5.4 장의 예제에서부터 계속 등장해오고 있다. 주로 마우스, 키보드의 입력처리를 하는 과정에서 사용하는데 입력장치에서 입력이 끝난 시점에 대한 것을 판단하기 위한 목적에서 사용하였다. input\_done 이 참(True)값이 되는 시점은 언제인가? 53번 라인에서 참(True)값을 저장하고 있는데, 바로 비밀번호 숫자 입력 후 엔터키를 눌러 비밀번호 입력을 최종 끝낸시점인 것이다. **draw** 함수에서는 24번 라인에서 이 input\_done의 값이 참(True)인 시점을 기준으로 비밀번호를 맞췄는지/틀렸는지에 대한 결과처리를 하고 있는 것이다. _원래 플래그 변수를 많이 사용하는 코딩스타일은 일반적으로 추천되지는 않는다. 플래그 변수를 많이 사용하면 할 수록 코드의 복잡도가 증가하기 때문인데 이는 코드유지보수 측면에서 불리하기 때문이다. 그러나, 파이게임제로 라이브러리 같은 거대게임루프(loop)와 콜백함수를 주로 사용하는 구조에서는 그 사용이 불가피한 면이 있다는 점을 이해하고 있으면 좋을 것 같다._ 🔢 26, 28, 33번 라인의 **screen.draw.textbox** 메소드를 살펴보자. 이전 장에서 살펴 본 **screen.draw.text** 와 크게 다르지 않다. 메소드 호출에 사용된 인자값들을 비교해 보면 좋을 것 같은데 텍스트 내용이 출력되어야 할 영역을 Rect를 사용해서 제공하고 있다. text 메소드에서 단지 텍스트 출력위치만 지정한 것과 textbox 메소드에서 출력영역을 지정한 것은 차이가 있다. _Rect로 제공하게 되면, 그 영역을 초과하지 않는 범위 내에서 표현할 수 최대크기의 글자크기로 텍스트 내용을 출력하게 된다._ 이처럼 출력영역이 고정되는 것은 우리의 목적에 부합하는데 왜냐하면 해당 영역에서 계속 텍스트의 내용이 바뀌기 때문이다. 이 경우, 새로운 텍스트를 쓰기 전에는 이전에 쓴 텍스트를 먼저 지우고나서 그 위에 써야 하는데, 영역이 고정되어 있어야만 기존 텍스트를 완전히 지우기가 편하다. 만약, 텍스트가 씌여지는 영역이 가변적이라면 불편한게 매번 그 가변영역을 기억해두면서 관리해야 하는 부담이 생기기 때문이다. 그밖에 **screen.draw.filled\_rect** 메소드를 사용해 Rect에 배경색을 칠하고 있는데 이는 우리가 화면에 표시할 텍스트가 선명하게 보이도록 하는 효과와 동시에 이전에 씌여진 텍스트를 지우는 효과도 함께 얻을 수 있다. 화면에 표시되어야 할 텍스트는 크게 3가지로 33번 라인의 패스워드 입력 전, 26, 28번 라인의 패스워드 결과에 따른 성공/실패에 대한 내용이며, 패스워드 입력이 일단 완료되면, 30번 라인에서 이전에 사용해 본 메소드인 **pygame.display.update()**를 통해 성공/실패 유무의 텍스트를 화면에 곧바로 출력하게 만들고, 파이게임 앱을 종료하는 메소드인 **game.exit()** 라는 메소드를 통해 게임을 종료하게 된다. 추가로 텍스트 출력에 사용할 폰트를 fontname='nanum\_square' 이란 인자값으로 지정해 전달했는데 이미 [5.1장](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.3-dog-cat) 에서 배운 것처럼 한글 텍스트 출력을 위해 한글폰트를 사용해야 하는 필요인 것이다. 그 밖에도 텍스트를 다양한 형태로 변형하기 위한 여러 옵션 인자값들이 존재하는데 자세한 내용은 [이곳arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/ptext.html) 라이브러리 메뉴얼을 참조하도록 하자. 🔢 패스워드 입력이 성공일 경우, 보물상자가 열린 이미지로 대치가 필요한데 이렇게 Actor 객체의 외관 이미지의 변경에 관한 처리와 사운드 효과음을 내는 처리는 update() 콜백함수 내에서 이뤄져야 하는 점을 유의하기 바란다. [이전 장](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/5-count_boy) 에서 Actor의 외관이 여러 장의 이미지로 구성되어 있을 때, 해당 이미지들의 사이에서 다음 이미지로 이동을 위해 **next\_image** 메소드를 사용해 보았는데, 이번에는 43번 라인에서 특정 이미지로 곧바로 바꾸기 위한 함수인 **sel\_image** 메소드를 사용했다. 게임에서 사운드 효과음을 사용하는 것은 사용자의 흥미를 더하는 게임성(playability)을 높이는데 거의 필수적이라 할 수 있겠다. 사용법 자체는 어렵지 않다. 44, 46번 라인처럼 **sounds.사운드파일명.play** 라는 메소드로 호출하면 된다. 효과음들은 게임에 사용되는 리소스(이미지, 폰트, 소리 등) 중에 하나로 기존에 리소스 관리와 동일한데 사전에 sounds 라는 폴더 안에 해당 파일이 저장되어 있어야만 한다. **사운드 파일은 모든 파일포맷이 지원되는 것은 아니고, wav와 ogg 포맷만 재생가능하다는 점을 유의하도록 하자.** 사운드는 크게 게임 내 특정 사건에서 사용되는 효과음과 게임 내내 재생하는 배경음으로 두 가지로 나눌 수 있는데, _배경음 재생에 관해서는 파이게임제로에서 제공하는_ _**Music**_ _이란 객체를 사용하면 되고,_ 자세한 사용법은 [이곳arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/builtins.html#music) 라이브러리 메뉴얼을 확인하도록 하자. 🔢 마지막으로 49번 라인의 키보드를 누를 때마다 발생하는 **on\_key\_down** 콜백함수에 대해 좀 더 살펴보자. 키보드를 누를 때마다 파마리터인 key, unicode 두 값이 함께 넘어오는데, 이 중 key는 어떤 키가 눌렸는지 용도로 사용하고, unicode는 만약 그 눌린 키가 특수키(ctrl, shift, esc 등)가 아니고 문자/숫자형의 값을 갖을 때는 그 값을 얻는 용도로 사용한다. 그래서, 57번 라인의 input\_text += unicode 구문은 키보드에서 누른 값들을 **input\_text**라는 문자열(string)변수에 누적보관하는 용도이다. 만약 사용자가 비밀번호 입력시 숫자를 잘못눌렀을 경우에 뒤로가기(Backspace)키를 눌러 지울 수 있는 기능도 제공하고 있는데, 54-55라인의 구현이 그 내용이다. 55번의 **input\_text\[:-1\]** 이란 문법표현을 처음 본 사람이 있을 수 있다. 이 문법은 다른 언어에서는 일반적으로 제공되지 않는 파이썬 언어의 매우 편리한 슬라이싱(slicing) 문법표현 중에 하나이다. 간단히 설명하면 -1 이란 의미에서 유추할 수 있듯이 input\_text 문자열(string)값 중 맨 마지막 문자값을 뺀 나머지 값들을 돌려주게 된다. 그래서, 맨 마지막 글자가 지워지는 효과를 만들 수 있는 것이다. 슬라이싱 문법은 알아두면 유용한데 설명해야 하는 내용이 적진 않아서 해당 장의 내용이 많이 길어지지 않게 여기서 줄이는 대신에, 해당 문법을 설명한 인터넷 상의 좋은 자료 중 [하나arrow-up-right](https://www.freecodecamp.org/news/slicing-and-indexing-in-python/) 를 추천하는 것으로 갈음하도록 하겠다. 여기까지 6번째 예제의 실행결과를 확인하는 것을 끝으로, 이 책의 마지막인 7번째 예제로 넘어가자. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FCH4W7PfDMcLXhtt6ej3Y%252FKapture%25202024-07-25%2520at%252016.29.10.gif%3Falt%3Dmedia%26token%3D108c97f0-fa84-4fe5-8db7-d23f0e7d924d&width=768&dpr=3&quality=100&sign=e55ebfb3&sv=2) [Previous5.5 팔 벌려 뛰기 횟수를 세어봐요 - 변수chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/5-count_boy) [Next5.7 두더지 게임을 만들어요 - 종합chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/7-mole_game) Last updated 10 months ago --- # 5.1.2 멍멍이와 야옹이가 인사해요2 - 순차 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 지난 절까지 개임의 무대(배경화면)까지의 준비는 마쳤다. 이제는 그 무대에서 배우 역할을 할 두 오브젝트, 멍멍이와 야옹이를 만드는 것부터 시작해야 한다. 엔트리에서는 오브젝트 자체를 만드는 일은 할 필요가 없었는데, 우리도 모르게 엔트리가 우리 대신해 미리 만들어 놓았기 때문이다. 파이게임제로에는 이러한 배우 오브젝트를 생성하기 위해 Actor 라는 **클래스(class)**를 준비해 두었다. 클래스란 용어가 처음 등장했는데 간단히 설명하면 오브젝트(객체)를 정의하는데 사용되는 문법정도라고 이해하자. 마치 우리가 기존에 함수를 정의(definition)하기 위해 사용했봤던 [def 문법](https://jjlee.gitbook.io/entry-python/basic_syntax/3.1-hello-world#undefined-5) 같은 것 말이다. 우리는 이미 **함수를 정의한다는 것과 사용한다는 개념이 다르다는 것 정도는 잘 이해하고 있다.** 함수를 정의한다는 것은 다른 말로 함수를 만든다는 것인데, 함수는 안에는 이 함수가 호출되어 사용되게 될 경우 이러이러한 일을 하게 된다는 상세 업무명세를 적어 놓게된다. 이후 이 정의된(만들어진) 함수를 사용하기 위해서는 함수의 이름으로(때로는 함수가 요구하는 인자값(argument)과 함께) 함수를 호출하게 되고, 함수는 호출되는 그 순간, 컴퓨터 메모리에 로딩되어 함수 내에 명세된 그 업무를 실제 수행하는 실체화된 작은 프로그램으로서 동작하는 것이다. ### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.2-dog-cat#class_object) 클래스와 오브젝트의 차이 클래스와 오브젝트의 관계도 마찬가지이다. **오브젝트를 정의할 때 사용하는 것이 클래스이고, 실제 사용목적으로 호출되어 메모리 상에서 실체화 된 상태를 오브젝트(특별히 이를 인스턴스(Instance)라 호칭)라고 부른다.** 즉, 클래스 이름으로 호출해(마치 함수를 함수 이름으로 호출하듯이) 오브젝트를 생성할 수 있는 것이다. 실제 코드를 보면서 이를 자세히 이해해 보자. Copy from pgzhelper import * TITLE = '멍멍이와 야옹이가 인사해요' WIDTH = 480 HEIGHT = 265 dog = Actor('dog', (100, 150)) def draw(): screen.fill('white') 새로 추가된 코드는 다음과 같은데, 라이브러리에서 미리 정의된 Actor (게임무대에서 활동하는 배우로서의 의미로 이렇게 이름지어짐) 클래스 호출을 통해 Actor 오브젝트를 생성하고, 컴퓨터 메모리 내에 **생성된 오브젝트(인스턴스)는** **dog**라는 변수에 담아 관리하겠다는 것이다. circle-info 참고로 여기서 변수명을 한글로 하고 싶으면, '멍멍이' 이런식으로 한글로 할 수도 있다. 실전에서 일반적으로 활용되진 않지만, 그럼에도 그게 여러분이 가르치는 아이들의 수준(영어식 표현에 익숙하지 않은 저연령 등)에 더 용이하다고 판단한다면 활용해 볼 가치는 있다. 생기초 단계에서는 우선은 "_텍스트 코딩은 어렵다_"는 선입견을 깨는 것이 훨씬 더 중요하기 때문이다. Copy dog = Actor('dog', (100, 150)) **여기서 Actor를 유심히 보면 곧바로 함수 호출이 아니고, 클래스 호출이라는 것을 인지할 수 있는데,** 어떻게 그것을 단번에 알 수 있냐면 **파이썬 문법 관습(꼭 파이썬만이 아닌 다른 프로그래밍 언어도 동일)으로 함수이름은 소문자로 시작해 만들고, 클래스 이름은 대문자로 시작해서 만든다는 관습적인 규칙**을 잘 따랐기 때문이다. _클래스 호출도 함수 호출처럼 유사하게 생성시 클래스로 넘겨야 할 인자값이 있을 수도 있고 없을 수도 있는데_, Actor는 2가지 인자값을 요구하고 있다. 첫번째 값('dog')은 Actor객체가 외관에 사용하게 될 이미지 파일이름(dog.png)이고, 두번째 값(100, 150)은 그 이미지가 화면에 어디에 그려질지에 대한 위치정보(정확하게는 해당 이미지의 중심값(center)의 화면상에 위치정보)를 오브젝트 생성을 위한 인자값으로 넘기게 된다. 만약 Actor 클래스에 대해 더 잘 알고 싶으면, [파이게임제로 라이브러리 메뉴얼(문서)arrow-up-right](https://pygame-zero.readthedocs.io/en/latest/builtins.html#actors) 를 찾아보면 되는데 라이브러리 저자가 직접 해당 클래스의 목적과 사용방법을 상세히 설명하고 있기 때문이다. Copy from pgzhelper import * TITLE = '멍멍이와 야옹이가 인사해요' WIDTH = 480 HEIGHT = 270 dog = Actor('dog', (100, 150)) dog.scale = 0.5 def draw(): screen.fill('white') dog.draw() 여기까지 오브젝트는 잘 생성되어졌고, 이제 생성된 오브젝트를 화면에 나타내야(그려야) 하는데, 지난 시간에 화면전체를 흰색으로 채운 것에 이어서 그 위에 이 오브젝트를그려주면 될 것이다(12번 라인). 따라서, 7번 라인에서 _생성된 "멍멍이" 오브젝트를 가리키고 있는(참조하고 있는) dog 변수를 통해_ 해당 오브젝트가 내장하고 있는 draw 함수를 호출해 화면에 그리면 된다. 이를 코드로는 **dog.draw()** 라고 표현하면 된다. 여기서도 우리가 기존에 라이브러리 안에 어떤 기능(함수 또는 변수 등) 호출시 많이 사용해 본 **.(점 연산자)** 활용해, 멍멍이 오브젝트 안에 있는 draw 함수(메소드)를 호출하고 있는 것이다. 여기서 8번 라인의 코드는 왜 필요할까? dog.scale = 0.5 이란 코드의 의미는 멍멍이 오브젝트 안에 scale 이라는 변수에 값을 0.5 로 변경하고 있다. 만약, 이 코드 없이 지금까지의 코드를 실행시켜보면, 멍멍이 오브젝트의 이미지가 화면에 잘 그려진다는 것을 알 수 있다. 다만, 한 가지 단점이 있는데 멍멍이 오브젝트의 이미지 크기가 너무 크다는 것이다. 그래서, 이 단점을 극복하기 위해, 즉 이미지 크기를 절반으로 줄이기 위해 이 코드가 필요했던 것이다. 이 코드를 추가한 후 다시 실행시켜보면 오브젝트의 이미지가 적당한 크기로 보일 것이다. ### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.2-dog-cat#object) 오브젝트(객체)란 무엇인가? 처음으로 오브젝트를 생성하고 사용해 보면서 여러분은 새롭게 등장한 이 오브젝트(객체)라는 개념의 실체가 무엇이라고 느껴지는가? _오브젝트 안에는 함수와 변수가 존재하고, 이를 사용하기 위해서는 .(점연산자)를 사용한다는 것에 있어서 아마도 여러분은 오브젝트가 라이브러리랑 매우 유사하고 어쩌면 소형화된 라이브러리의 개념인가?_ 라고 생각할 수 있을 것 같다. **사용법적인 면에서는 라이브러리와 매우 유사성이 크다할 수 있는데, 탄생 목적과 오브젝트를 직접 설계하고 만드는 과정 자체는 많이 다르다.** 반복해서 자주 사용하는 함수들을 어떤 한 공간(라이브러리)에 모아서 활용하면 코드의 재활용성 측면에서 유용하다는 목적에서 탄생한게 라이브러리라 할 수 있다. 오브젝트 역시도 궁극적으로는 코드의 재활용성을 높이려는 목적에서는 동일하긴 하나, 그것과 더불어 소프트웨어 개발의 복잡성을 낮추고자 하는 목적에서 출발하였다고 할 수 있다. 좀 더 상세한 내용을 알고 싶다면, 기존 책에서 언급된 [객체지향 프로그래밍 패러다임arrow-up-right](https://jjlee.gitbook.io/entry-python/paradigm/4.4-object-oriented) 에 대해 다시한번 읽어보면 좋은 것이다. 이즘에서 코딩에서 말하는 오브젝트(객체)가 무엇인지를 설명하는게 필요하겠다. **오브젝트란 그 이름에서 알 수 있듯이 우리가 살아가고 있는 실 세계(real world)에 존재하는 오브젝트(물체) (예를들어, 컵, 책상, 의자, 꽃병, 텔레비전, 자동차 등등)을 프로그래밍 세계(programming world)에서 가져다 활용하기 위해 오브젝트가 갖는 속성(attribute)과 오브젝트가 할 수 있는 행동(behavior)으로 모델링화한 코드덩어리인 것이다.** _참고로 오브젝트의 속성과 행동을 코드적인 표현법에 있어서_ _**속성은 변수로 표현하고, 행동은 함수(이러한 함수들을 지칭할 때 메소드라 부름)로 표현한다.**_ 우리의 첫 오브젝트로서 사용해 본 배우(Actor) 오브젝트는 무엇을 모델링화 한 것인가? 사실 이 오브젝트는 실 세계는 존재하진 않는 것으로, 게임무대라는 프로그래밍 세계 안에서 등장인물 배우로서 역할을 기대하며 만들어 낸 것이다. 여러분이 이 오브젝트를 최초로 디자인 한다면 어떻게 디자인 하겠는가? 이 오브젝트가 해내야 할 역할과 책임은 무엇이며 게임무대라는 공간에서 어떤 속성(변수)을 갖고 어떤 행동(함수)을 하면 되는가를 생각보면 알 수 있다. 먼저 필요한 속성을 생각해보면 배우 오브젝트의 외관은 이미지로 되어있으니, 어떤 이미지 파일로부터 외관을 만들어야 할지의 **이미지 이름속성**이 필요할 수 있을 것이다. 때론 해당 이미지의 크기를 화면에 맞는 적절한 크기를 가져야 할 수 있기 때문에, 그 크기를 정할 수 있는 **크기속성(scale)**이 있다면 좋을 것이다. 그밖에 화면상에 어디 위치에 이미지를 나타야 할지의 **위치속성(pos)**도 필요할 것이고, 만약 이 이미지를 회전시켜 표현하고 싶다면 그에 따른 각도를 정해줄 수 있는 **각도속성(angle)** 등이 있을 수 있다. 그렇다면, 배우 오브젝트가 가져야 할 행동측면을 생각해보면, 무엇이 있을까? 기본적으로 배우는 화면에 나타내져야(**그려져야**) 할 것이고, **움직여야** 할 것이고, 게임무대라는 특성상 다른 객체와의 **접촉(또는 충돌(Conflict)) 했는지**의 확인하는 행동 등이 있을 수 있겠다. Actor 오브젝트는 우리가 방금 예상한 수준에서의 속성과 행동들을 포함한 실제로는 더 많은 속성과 행동들을 갖고 있다.지금당장 구체적으로 Actor 오브젝트가 무슨 속성을 갖고 있고, 무슨 행동을 할 수 있는지에 알고자 한다면, 이미 위에서 언급한 이 오브젝트의 저작자가 만든 [메뉴얼arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/builtins.html#actors) 을 보고 파악할 수 있다. 그러나 차근차근 이 책의 예제들을 하나씩 격파해 나가면서 자연스럽게 익히게 되어 있으므로 너무 조급해 할 필요는 없다. 이번 장에는 오브젝트(객체)라는 현대 코딩개념에서 가장 중요한 개념을 익히게 된 것을 축하한다. 오브젝트에 대해서는 당장은 이정도의 이해면 충분하고 점점더 심화된 내용은 추후에 더 다뤄질 것을 기대하고, 계속해서 남은 코딩을 이어가 보자. [Previous5.1.1 멍멍이와 야옹이가 인사해요1 - 순차chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat) [Next5.1.3 멍멍이와 야옹이가 인사해요3 - 순차chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.3-dog-cat) Last updated 10 months ago --- # 5.1.3 멍멍이와 야옹이가 인사해요3 - 순차 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 이번에는 야옹이 오브젝트를 만들고, 화면에 등장시킬 차례이다. 이미 멍멍이 오브젝트로 해봤던 코딩이라 크게 어려울 것이 없다. 다만, 다음의 11번 라인의 경우, 처음보는 코드가 등장했는데, 이 속성이 무엇인지는 **flip\_x** 라는 이름에서 직관적으로 알 수 있을 것이다. 이미 우리가 엔트리 블록코딩에서 야옹이가 멍멍이와 서로 마주보기 위해 특별히 추가되어졌던 블록\["좌우 모양 뒤집기" 블록\]을 기억한다면, 그 블록과 같은 역할임을 알 수 있다. 즉, x방향(수평방향, Horizontal)으로 이미지를 뒤집기 위한 속성으로 True 값을 주어 화면에 x방향으로 이미지를 뒤집어 출력해 준다. Copy from pgzhelper import * TITLE = '멍멍이와 야옹이가 인사해요' WIDTH = 480 HEIGHT = 270 dog = Actor('dog', (100, 150)) dog.scale = 0.5 cat = Actor('cat', (350, 150)) cat.scale = 0.5 cat.flip_x = True def draw(): screen.fill('white') dog.draw() cat.draw() 이제 남은 일은 각각의 멍멍이와 야옹이가 번갈아서 순서대로 인사말과 작별인사를 건네면 되는 것이다. 이를 코딩으로 표현하면 18~21번 라인의 4줄의 코딩이 된다. Copy from pgzhelper import * TITLE = '멍멍이와 야옹이가 인사해요' WIDTH = 480 HEIGHT = 270 dog = Actor('dog', (100, 150)) dog.scale = 0.5 cat = Actor('cat', (350, 150)) cat.scale = 0.5 cat.flip_x = True def draw(): screen.fill('white') dog.draw() cat.draw() dog.say_for_sec('멍멍!', 2, fontname='nanum_square') cat.say_for_sec('야옹~', 2, fontname='nanum_square') dog.say_for_sec('잘가~', 2, fontname='nanum_square') cat.say_for_sec('잘있어 안녕!', 2, fontname='nanum_square') 해당 코딩의 이해 역시나 직관적으로 알 수 있다. 오브젝트 내 내장된 **say\_for\_sec** 함수(메소드)를 사용해 어떤 말을 몇 초 동안 얘기할지를 코딩하면 된다. _단, mu 에디터의 기본 내장폰트에는 한글이 존재하지 않아, 한글출력을 시도하면 한글이 보이지 않는 문제가 있다._ 이를 해결하기 위해 우리는 화면출력을 위한 별도의 한글폰트 사용할 수 있다. 우리는 앞서 5.1.1 장에서 언급한 것처럼, 폰트 역시도 게임에 사용되는 리소스(그림, 글자, 소리 등)이고, 그 리소스들은 항상 미리 정해진 폴더에 위치해야 한다는 것을 인지하고 있다. 폰트 폴더는 다음과 같이 에디터에 **폰트 아이콘**을 눌러 열 수 있고, 그 안에 필요한 한글폰트를 넣어두면 된다. Mu 에디터 커스텀 버전에서는 여러분의 편의를 위해 이미 무료 한글폰트(네이버 나눔스퀘어 폰트)가 넣어져 있을 것이고, 우리는 이를 함수 파마미터의 하나로 fontname='nanum\_square' 라는 값으로 넘기기만 하면 된다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252Fu5wyMLotkjkPrY4xLP1e%252Fcapture1.png%3Falt%3Dmedia%26token%3D03016099-30ee-46ea-84a0-28842ce2e19d&width=768&dpr=3&quality=100&sign=2b874766&sv=2) circle-info 폰트에 관해 알아둘 것이 하나 있다. 일반적으로 폰트의 종류는 크게 2가지가 있는데, 파이게임제로에서는 ttf 확장자를 갖는 폰트(True Type Font) 만 지원한다. 따라서, otf 폰트(Open Type Font)를 폰트 폴더에 넣고, 왜 안되는지 의아해 하지 않기를 바란다. 해당 내용에 대해서는 [라이브러리 레퍼런스(메뉴얼)arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/ptext.html#font-name-and-size) 에 언급되어 있다. 이것으로 엔트리 초급책의 가장 첫 번째 예제를 파이게임제로로 코딩해 보는 것을 마치겠다. 첫 번째 예제의 실행결과는 다음과 같다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252F6SgmhnufIz2EgqZuSftS%252FKapture%25202024-07-17%2520at%252017.57.45.gif%3Falt%3Dmedia%26token%3D4b52fbf1-614e-4e1f-9a31-a5fb9dd414a1&width=768&dpr=3&quality=100&sign=c59fcb01&sv=2) 마지막으로 다시한번 정리하면, 이미 4장 게임루프의 이해에서 언급했던 것처럼 파이게임제로를 활용한 텍스트 코딩이 엔트리 블록코딩과는 차이점이 있는데 엔트리 때는 멍멍이, 야옹이 오브젝트가 각각 개별적으로 독립적으로 존재했고, 해당 오브젝트에 해당하는 코딩역시도 각자에게 독립적으로 존재하였다. 그러나, 파이게임제로에는 그러한 병렬/동시성 방식([자세한 내용은 이전 서 참조](https://jjlee.gitbook.io/entry-python/paradigm/4.1-serial-parallel) )을 지원하지 않고 있고, 게임루프라는 거대 루프(loop) 구조를 사용한다. 파이썬 게임코딩에서는 이 구조에 대한 기본 이해를 바탕으로 특히 중요하게 언급된 오브젝트에 대한 개념까지 잘 이해했다면, 앞으로의 남은 예제들의 코딩도 크게 어려움은 없을 것이다. 첫 예제까지 무사히 마친 여러분께 축하를 전하며, 이이서 두번 째 예제에 도전해보자. [Previous5.1.2 멍멍이와 야옹이가 인사해요2 - 순차chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.2-dog-cat) [Next5.2 꽃잎 한 장으로 꽃송이를 만들어요 - 반복chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/2-make_flower) Last updated 1 year ago --- # 5.3 로봇 청소기가 장애물을 만난다면? - 조건 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 벌써 세 번째 예제인 "로봇 청소기" 예제다. 이번 예제는 기존에 사용해 보지 않은 추가적인 Actor 오브젝트의 메소드(행동)가 등장하긴 하지만, 이전 장들에서 배운 기본 원리는 크게 달라지지 않기 때문에 크게 어려움은 없을 것이다. Copy from pgzhelper import * WIDTH = 960 HEIGHT = 540 robot = Actor('cleaner', (WIDTH / 2, HEIGHT / 2)) robot.scale = 0.3 robot.angle = 90 robot.brush_init((WIDTH, HEIGHT), 50, 'white') def draw(): screen.blit('floor', (0, 0)) robot.brush_draw() robot.draw() def update(): robot.move_forward(10) if robot.top < 0 or robot.right > WIDTH \ or robot.bottom > HEIGHT or robot.left < 0: robot.move_back(10) robot.angle += 133 🔢 먼저, 3-4라인에서 기존(가로 480, 세로 270) 보다 화면크기를 2배로 하였는데, 특별한 이유는 없고, 단지, 실행결과 화면을 크게 보기 위해서이다. 🔢 6번 라인에서 로봇청소기를 화면 중심에 놓기 위해 중심좌표를 가로, 세로길이 절반에 위치하도록 하였다. 🔢 8번 라인에서도 로봇청소기 오브젝트의 최초 각도를 90도로 정한 것으로 최초 실행시 화면 윗방향 쪽으로 움직이게 하기 위한 것이다. 🔢 9번 라인부터는 로봇청소기의 이동흔적을 색칠하기 위한 기본설정을 **brush\_init** 이란 함수(메소드)를 통해 하게 된다. 3가지 인자값(argument값)이 필요한데, 각각은 브러시가 칠해질 전체영역, 굵기, 색깔이 되겠다. 🔢 12번 라인에서 드디어 처음으로 우리 게임에도 게임배경이 등장한다. 개임배경(나무스타일 바닥 이미지)을 그리는 코드인데, **screen** 이라는 게임화면을 가르키는 내장객체를 사용한다. screen 객체 안에는 이미 내장된 여러가지 함수(메소드)가 존재하는데([상세내용arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/builtins.html#screen) 은 파이게임제로 메뉴얼 참조), 그 중에 하나가 화면에 직접 이미지를 출력할 수 있는 **blit** 이라는 함수(메소드)를 사용해 좌표(0, 0)부터 시작하는 마루바닥 이미지를 출력하였다. 🔢 13-14번 라인의 순서는 중요한데, 누구를 먼저 그리냐에 따라, 청소기 위쪽으로 이동경로 칠해져 보여질 수 있으므로, 이동경로를 칠하고, 그 위에 청소기를 그리는 순서가 자연스럽다 할 수 있겠다. 🔢 16번 라인에서 등장하는 **update** **콜백함수는 거대 게임루프 구조를 구성하는 주요 함수로서 게임에서 계속적으로 변화하거나(Actor 오브젝트가 움직이는 경우 등), 계속적으로 확인해야 하는 내용(어떤 키보드, 마우스 키가 눌렸는지 등)에 대한 코드를 적는 부분이다.** 따라서, 로봇청소기가 매번 10 크기만큼의 반복이동을 위해 17번 라인에 **forward(10)** 을 사용해 반복적인 움직임을 만들고 있다. 🔢 마지막으로 19번 이후의 코드는 조건문으로 로봇청소기 게임화면 끝에 도달했을 때의 행동에 대해 기술한 것으로, 이 부분은 기존의 엔트리 코딩과는 조금 다른 개념이 적용되니 주의가 필요한 부분이다. _기존에 엔트리에서는 게임화면의 경계선을 벽(wall)이라고 지칭하며 마치 화면 벽(wall)을 오브젝트처럼 다루는데 벽의 경계면에서 다른 오브젝트가 서로 닿았는가(출동했는가)라는 오브젝트 간의 충돌검사 방식을 사용했다._ **그러나, 파이게임제로에는 게임화면 경계면 자체는 어떠한 오브젝트가 아니다. 그래서,** _**화면 경계에 도달했는지에 대한 검사를 현재 끊임없이 이동 중인 로봇청소기의 현재 위치를 통해 파악한다.**_ 예들들어 robot.top 값이 0보다 작아졌다는 것은 로봇청소기 객체가 게임화면 상단을 넘어서기 시작하는 시점이며, robot.right 값이 전체화면의 가로크기보다 커지는 시점은 게임화면 최우측을 넘어서기 시작하는 시점인 것이다. 이 때, 로봇청소기를 약간(10크기 만큼) 뒤로 후진시킨 후에 방향전환을 시도하고 있음을 유념하도록 하자. 이런식의 알고리즘이 필요한 이유는 스크린의 가장자리에 도달했을 때 곧바로 방향전환을 시도했으나 때때로 방향전환된 그 순간조차도 여전히 화면 끝에 도달한 것으로 연달아 판별되면서 계속 방향전환만 시도하다가 결국에는 다른 방향으로 이동해 빠져나가지 못하고 제자리에서 가만히 공회전만 하는 경우가 발생할 수 있는데 이를 막기 위해 의도적으로 약간 후진 후 방향전환을 시도하고 있다. 참고로 19번 라인 끝에 '\\'의 표현이 궁금한 분들이 있을 것 같다. _19-20번의 두 라인(행)에 걸쳐있는 코드는 본래 19번 라인 한 행에 길게 다 코딩되어야 하는 것이었다. 그러나, 저자는 이를 의도적으로 두 라인(행)으로 분리했는데, 코드를 읽을 때 빠르고 직관적으로 읽을 수 있게 즉, 코드 가독성을 높히기 위한 의도적인 코딩이다._ 그런데, **코딩에서는 이렇게 원래 한 라인의 긴 코드를 여러 라인에 걸쳐 코딩하고자 할 경우, 나누고자 하는 행의 끝 부분에 '\\' 기호를 둠으로써 이 행은 완전히 끝난게 아니라 다음 행과 연결되어 진다는 표시를 반드시 해두어야만 하는 것이다.** 마지막으로 해당 로봇청소기의 최종 실행결과를 보는 것으로 본 장을 마무리 하겠다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FIFAeoAECjHe8yJAvubCO%252FScreen%2520Recording%25202024-07-17%2520at%252016.59.34.gif%3Falt%3Dmedia%26token%3Dc3bfae7a-f8ee-4333-8d18-b812f3d72350&width=768&dpr=3&quality=100&sign=a3121e81&sv=2) [Previous5.2 꽃잎 한 장으로 꽃송이를 만들어요 - 반복chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/2-make_flower) [Next5.4 나만의 그림판을 만들어요 - 이벤트chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/4-drawing_board) Last updated 1 year ago --- # 5.4 나만의 그림판을 만들어요 - 이벤트 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 벌써 총 7개의 예제 중 4번째 예제를 살펴볼 차례이다. 이 책의 끝이 얼마남지 않았다. 확실히 코드량이 이전보다 많아졌다. 하지만, 걱정할 필요가 없고, 더 많은 것을 배울 수 있는 좋은 기회(?)라고 긍정적이고 도전적으로 여긴다면 우리에게 유익이 있을 것이다. Copy from pgzhelper import * WIDTH = 960 HEIGHT = 540 drawing = False pencil = Actor('pencil', (WIDTH / 2, HEIGHT / 2), anchor=('left', 'bottom')) pencil.scale = 0.3 pencil.brush_init((WIDTH, HEIGHT), 5, 'blue') eraser = Actor("eraser", (900, 50)) eraser.scale = 0.5 def draw(): screen.fill('white') pencil.brush_draw() pencil.draw() eraser.draw() if drawing is False: pencil.brush_stop() def on_mouse_move(pos): pencil.left, pencil.bottom = pos def on_mouse_down(pos): global drawing drawing = True if eraser.collidepoint_pixel(pos): pencil.brush_clear() def on_mouse_up(): global drawing drawing = False 🔢 제일 먼저는 8번 라인의 연필이라는 배우 객체를 만드는 코드를 살펴보자. 왜 객체의 중심값(anchor값)을 ('left', 'bottom')으로 하는지에 대해서는 엔트리 블록코딩에서 했던 사유와 동일하다. 화면에 그려지는 브러시 스케치가 연필의 몸체에서부터 그려지면 어색하고, 연필 펜촉에서부터 그려져야 자연스럽기 때문이다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252F4c0bItLLTHdmNGdSN8uf%252Fimage.png%3Falt%3Dmedia%26token%3D3d594f23-bbf9-4011-a600-d113ed45edb6&width=768&dpr=3&quality=100&sign=52a058f2&sv=2) 🔢 6번 라인에서 기존에 라이브러리 내장변수가 아닌 우리의 필요에 의해서 우리가 정의한 변수가 처음으로 등장한다. **drawing** 이란 변수이름에서 그 목적을 어느정도 유추할 수 있듯이(변수를 만들 때는 의도적으로 의미있는 이름을 부여하는게 중요하다는 것을 이전 서들을 통해 이미 여러차례 언급했음), 마우스 버튼을 눌러 _그림을 그리고 있는 상태인지_ 마우스 버튼에서 손가락을 떼서 _그리기를 멈춘 상태인지_를 구분하기 위한 용도이다. 결국 두 가지 상태만 알면 되기 때문에 해당 변수값으로 참/거짓(True/False) 값만 활용하면 되겠다. drawing 변수의 목적과 용도는 이해했다. 이번엔 _이_ _변수의 정의(definition)가 코드영역 내 어디에 위치하고 있느냐에 관한 것으로 왜 이 변수는 함수 안쪽 영역(scope)에 존재하지 않고, 함수 바깥쪽 영역에 존재해야 하는가에 대해 알아볼 필요가 있다._ **이는 변수를 어디에 생성하느냐에 대한 코딩문법 규칙에 관한 것으로, 특정 함수 안에서만 사용되는 변수는 해당 함수 안쪽에서 정의하고, 여러 함수에 걸쳐 코드 전체적으로 공통으로 함께 사용해야 하는 변수는 이처럼 함수들 바깥쪽 영역에 정의하게 된다. 이렇게 코드전체적으로 함께 공유하는 변수를 global(전역적) 변수라고 부른다는 것도 함께 기억하자.** 🔢 drawing 값은 26번 라인의 **on\_mouse\_up** 콜백함수(함수이름 시작이 **on\_ 으로 시작 되기때문에, 특별히 이벤트(마우스 버튼 누름 과 같은 액션 등)에 따라** 파이게임제로 라이브러리에서 자동호출 된다는 의미)와 33번 라인의 **on\_mouse\_down** 콜백함수 두 곳에서 함께 사용되고 있다는 것을 알 수 있다. 이렇게 두 함수가 함께 공유해야 하는 변수이기 때문에 해당 변수는 함수들 바깥쪽 영역에 정의되었던 것이다. 이제 이 변수를 사용하는 함수에서의 활용을 유심히 살펴보자. **함수 밖에 있는 변수라도 함수 내에서 단지 그 변수값을 읽어서 활용하는데는 기존과의 차이가 없다. 그러나, 만약 이 변수값을 함수 안쪽에서 수정하려면 이 때는 파이썬의 새로운 문법을 알아야 하는데, 27, 34라인에서 보는 것과 같이 해당 변수명 앞에 global 이라는 키워드(keyword)를 붙혀 변수선언 함으로서 해당 변수는 이 함수의 바깥 영역에 존재함을 명확하게 알린 후에야 그 값을 수정할 수 있다는 것이다.** circle-info _참고로 여기서 주의할 것이 있는데_ _**파이썬이 갖는 엄밀히 문법을 따지지 않는 유연성이 장점이면서 동시에 이것이 단점이 되는데**__, global 이라는 키워드를 명확히 쓰지 않고, 실행을 해도 사전에 에러없이 일단은 실행이 잘 되기 때문이다. 그러나, 막상 이 변수값을 수정하려는 그 순간에 에러가 발생하게 된다. 이처럼 왜 원하는데로 동작하지 않는지의 논리적 오류를 사전에 발견하기가 쉽지 않은 상황을 만날 수도 있으니 주의하도록 하자._ 🔢 15~18번 라인까지 draw 콜백함수 안에서 배경색을 칠하고, 각각의 등장 오브젝트들을 화면에 그리게 된다. 이때 각 오브젝트의 draw 함수들의 호출순서, 즉 각 오브젝트들이 화면에 그려지는 순서도 고려해야 함을 지난 장에서 언급했었다. 순서를 유의해 연필 위쪽으로 우리의 브러시 스케치가 존재하는 등 다소 어색한 형태가 없도록 하자. 🔢 20-21번 라인은 조건문으로 브러시를 통해 그리는 것을 중단할 시점을 찾기 위해 drawing 변수값을 활용한다. 그 값이 거짓(False)이 되면, brush\_stop 함수(메소드)를 사용해 brush 사용을 중단한다. 🔢 23-24번 라인은 마우스 움직임과 실시간으로 연필 오브젝트의 중심좌표와 일치시키는 작업을 하고 있다. 그래야만 마우스의 움직임에 맞춰 연필 오브젝트가 같이 움직일 수 있기 때문이다. 24라인의 문법이 약간 이상해 보일 수 있는데, **pos라는 값 자체가 한 개의 값처럼 보이지만, 실제로는 (x, y) 좌표값을 갖는 튜플(Tuple)값이다.** 튜플은 아직 배우지 않은 처음 등장하는 데이터 타입(종류)이니 이번 기회에 좀 더 상세히 알아보도록 하자. #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/4-drawing_board#tuple) 튜플 (Tuple) 튜플은 기존에 우리가 알고 있는 [리스트(List)](https://jjlee.gitbook.io/entry-python/basic_syntax/3.6-list) 데이터 타입과 유사한데 어떻게 사용할 수 있는지 먼저 문법을 확인해보자. 정말이지 리스트의 대괄호가 소괄호로 바뀐거 외엔 표면적으로 차이가 없다. > **튜플명 = (문자, 숫자 등의 여러 값. 단, 값들은 콤마(,)로 구분)** **그러나, 리스트와 튜플의 근본적인 차이는 값을 추후에 바꿀 수 있느냐/없느냐의 여부이다, 수정가능/읽기전용의 차이이다.** 튜플은 후자인데, 의도적으로 읽기전용(read only)값으로 설정함으로써, 처음 초기값으로 설정한 값을 보호해서 추후에 자신이나 또는 다른 개발자가 개발해 나가면서 실수로 해당 값을 변경하려는 시도를 처음부터 막으려는 의도가 있다. 항상 우리는 함수 저작자가 만들어 놓은 의도성 있는 제약을 따르도록 하자. 우리는 남이 만든 함수나 라이브러리를 가져다가 쓰는 입장에서 크게 불만(?)을 갖긴 어렵고, 함수 저작자가 제안하거나 제약하는 틀 안에서 코딩한다고 생각하면 좀 더 편한다. 참고로 어떤 제약이 있는지의 상세한 확인은 해당 함수에 대한 [라이브러리 메뉴얼arrow-up-right](https://pygame-zero.readthedocs.io/en/stable/hooks.html#on_mouse_move) 을 통해 확인가능하다. 다시한번 정리하면, 24번 라인의 문법은 pos의 튜플에서 x좌표값을 꺼내 pencil.left 에 저장하고, y좌표값을 꺼내 pensil.bottom 으로 저장시킨다. 🔢 26번에 **on\_mouse\_down** 콜백함수는 이름에서 유추할 수 있듯이, 마우스 버튼을 누를 때 라이브러리를 통해 자동으로 불려지게 된다. 마우스 버튼을 눌렸을 때 그 마우스 커서가 위치한 지점도 pos라는 함수 파라미터값으로 자동으로 넘어오기 때문에 우리는 이 값을 활용해 재미있는 응용을 만들 수 있다. 이제 마지막으로 본 장의 마지막 코드인 30-31라인 코드를 살펴보면 끝이다. 드디어 게임코딩에서 늘상 사용되는 오브젝트들 간의 영역 충돌검사를 하는 코드가 처음으로 등장하였다. 마우스 클릭한 지점의 위치(여기서도 pos라는 튜플값)가 지우개 오브젝트의 영역 안에 있는가를 **collidepoint\_pixel** 함수(메소드)로 확인하여 참(True)가 되면, 즉, 우리가 마우스로 지우개 오브젝트를 클릭했는지를 확인해 만약 눌렸으면 전체 스케치한 내용을 지우는 것이다. 장이 시작할 때는 코드량이 많은 것처럼 보였지만, 막상 그 내용을 이해하고 나면 그렇게 어려운 코딩은 아니라는 것을 알 수 있다. 이처럼 우리는 코드량이 많다는 것으로 미리부터 겁먹을 필요가 전혀 없다. 마지막으로 해당 그림판의 최종 실행결과를 보는 것으로 본 장을 마무리 하겠다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FXVvoT6Zhmk7ofkK3IKmo%252FKapture%25202024-07-25%2520at%252016.16.20.gif%3Falt%3Dmedia%26token%3D788bef71-99c6-4970-a0a0-700b29f99bc7&width=768&dpr=3&quality=100&sign=671d1baf&sv=2) [Previous5.3 로봇 청소기가 장애물을 만난다면? - 조건chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/3-robot_cleaner) [Next5.5 팔 벌려 뛰기 횟수를 세어봐요 - 변수chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/5-count_boy) Last updated 1 year ago --- # 5.1.1 멍멍이와 야옹이가 인사해요1 - 순차 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 우리는 이전 장에서 엔트리 블록코딩에서 코딩하는 것과 파이게임제로에서 코딩하는 것과의 근본적인 구조적차이를 이해했다. 그것을 기반으로 엔트리 기초책의 7가지의 예제를 하나씩 엔트리-파이썬이 아닌 실제 파이썬 코딩을 통해 만들어 보자. 다만, 이를 구현하기 위해서는 파이게임제로 라이브러리만으로 기능적으로 모자란 부분이 있다. 따라서, 이를 보완해 줄추가적인 사용자 라이브러리가 필요하고, 우리는 먼저 이 사용자 라이브러리(user library, 기본 내장이 아닌 사용자가 필요에 의해 추가로 만들어진 라이브러리)인 pgzhelper 라이브러리를 호출하는 것으로부터 시작하겠다. circle-info pgzhelper 는 스크래치 블록코딩 한 것을 파이게임제로 포팅(porting)하는(똑같이 기능구현하는) 과정에서 파이게임제로 자체 한계로 인해 어려움이 있었던 부분을 개선하기 위해 싱가포르의 **Cort** 라는 개발자가 최초로 만든 것으로, [로보틱스웨어arrow-up-right](https://roboticsware.uz/) 에서도 유사한 이유로 엔트리 블록코딩을 파이게임제로로 포팅을 용이하게 하기위해 이 라이브러리에 추가 기능을 더해 보완하였고, 이를 코드 공개 사이트인 깃허브([https://github.com/roboticsware/pgzhelperarrow-up-right](https://github.com/roboticsware/pgzhelper) )에서 공개하고 있다. Copy from pgzhelper import * 코드의 맨 첫번째 라인은 pgzhelper라는 라이브러리를 호출하는 부분이다. 라이브러리 호출하는 문법은 크게 두 가지가 있는데 기존에 _import pgzhelper_ 라고 사용하는데 익숙할텐데, 이 경우에는 다음과 같은 새로운 문법으로 라이브러리를 호출하는게 필요하다. > **from** 라이브러리명 **import** \* 이렇게 호출하게 되면, **장점이 기존에는 라이브러리 안에 함수를 호출하기 위한 문법으로 항상 <<라이브러리명.함수명>> 으로 사용했어야 했는데, 이제는 문법이 앞단에 라이브러리명 없이 직접 함수명만 가지고도 종전과 동일하게 호출이 가능**하게 된다. ### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat#undefined) 엔트리 좌표계와 파이게임 좌표계의 비교 엔트리의 좌표계는 우리가 익히 잘알고 있듯이 아래의 좌측과 같고, 그 특징이라고 하면 이미 정해진 사이즈(가로 480픽셀, 세로 270픽셀)의 화면크기를 갖고 화면중앙을 x, y좌표의 시작점(0, 0)으로 한다는 것이다. 반면에 **파이게임 좌표계는 화면 전체 크기는 사용자가 임의로 정할 수 있으며, 다만, 이 경우 좌표의 시작점은 좌상단 끝을 x, y좌표의 시작점(0, 0)으로 삼고, 좌와 아래로 향할 수록 그 값의 크기가 커지게 된다.** 엔트리 파이게임 ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FvwzBgp9sLa5sooj04gNM%252Fimage.png%3Falt%3Dmedia%26token%3D1e2497ca-6b0c-4379-8adf-b193afd986a9&width=300&dpr=3&quality=100&sign=cf900e9&sv=2) ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FkIJsVinI4YbhtTNoS4dM%2Fimage.png&width=300&dpr=3&quality=100&sign=bd0e8d4&sv=2) 엔트리가 갖고 있는 실행화면 화면크기를 일단 그대로 모사해보자. 따라서, 화면의 가로세로 크기는 각각 480, 270 픽셀이 될 것이고, 이를 기반으로 코딩하면 다음과 같다. Copy from pgzhelper import * TITLE = '멍멍이와 야옹이가 인사해요' WIDTH = 480 HEIGHT = 270 게임이 배경화면 이미지가 따로 존재하지는 않는다. 엔트리는 기본 배경화면색이 흰색으로 이미 칠해져 있지만, 파이게임제로에서는 기본 배경화면색으로 자동으로 칠해지는게 없다. 따라서, 우리의 필요에 의해 배경화면에 흰색을 칠해주는게 필요하다. 이를 코딩하는 방법은 다음의 3가지 방법이 존재한다. #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat#undefined-1) 사전 정의된 색상값 키워드을 사용해 코딩하기 Copy def draw(): screen.fill('white') _**draw**_ _라는 함수는 이전 장에서 살펴보왔던 파이게임제로의 게임루프 안에 존재하는 사전에 정의된 콜백함수_로 화면에 무언가를 그리고자 할 때 라이브러리로부터 자동호출되어진다. 따라서, 화면의 색깔을 칠하는 코드는 이 안에 기술되어야 하고, 우리는 화면을 칠하기 위해 **fill** 이라는 함수를 사용하였고, 인자값으로는 흰색(white)를 넘겼다. 여기서 **screen.** 으로 fill 함수를 호출한 것은 fill 함수는 파이게임제로 라이브러리의 screen 이라는 내장 오브젝트(객체) 안에 존재하기 때문인데, 오브젝트에 대한 상세한 내용은 다음 장에서 더 자세히 다루기 때문에 지금은 이정도 내용이해로 넘어가도록 하겠다. 화면에 칠할 **색상값을 넘기는 방법은 총 3가지인데, 위에서 사용해본 것처럼 사전에 언어 내장 키워드처럼 사전에 정의된 값을 활용하는 방법과 RGB 표현법을 사용하는 방법으로 크게 2가지로 나눌 수 있다.** 사전 정의된 색상의 키워드는 다음의 색상값 테이블에서 확인할 수 있다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FBk9JM2LX5DkSpaw3ES4i%252Fimage.png%3Falt%3Dmedia%26token%3De911ec37-fe05-4280-8dc2-56be169d63d6&width=768&dpr=3&quality=100&sign=5140a246&sv=2) 그리고, 다음과 같이 RGB(Red, Green, Blue)의 3원색의 조합을 통해 색상을 표현하며, 3원색 각각은 최대 256개의 값을 갖을 수 있다. 3원색 각각을 10진수(0 ~ 255) 형태 또는 16진수(00 ~ FF) 형태로 표현해 원하는 색상을 표현할 수 있는 방법이 있다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FmeUr6W2PeopF5uEMCTFq%252Fimage.png%3Falt%3Dmedia%26token%3Db7f1612f-515e-4f21-836e-10715c8a9b44&width=300&dpr=3&quality=100&sign=292b5541&sv=2) ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FSUSk0aVlpvlMFetLTigo%252Fimage.png%3Falt%3Dmedia%26token%3De54090be-bcad-4208-8aec-0938a13a267e&width=300&dpr=3&quality=100&sign=93353173&sv=2) #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat#rgb-10) RGB 색상값 중 흰색을 10진수 표현법으로 코딩하기 Copy def draw(): screen.fill((255, 255, 255)) #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat#rgb-16) RGB 색상값 중 흰색을 16진수 표현법으로 코딩하기 Copy def draw(): screen.fill('#FFFFFF') 색생표현법에 관한 내용은 워낙 인터넷에 정리된 자료가 많아서 지면 관계상 각자가 해당 자료들을 직접 참고하는게 훨씬 더 좋은 선택이다. 참고로 색상표현을 위해 RGB의 3원색이 필요한 이유에 대해 [이 블로그arrow-up-right](https://phominator.tistory.com/42) 를 참조할 수 있다. 아래의 같이 지금까지 코딩한 내용을 실행시켜 의도하는데로 동작하는지 확인해보자. 실행을 시키기 위해서는 먼저, 코드내용을 파일로 저장해야 하는데, 소스코드(source code)의 **기본 저장위치는** _**\[사용자 계정명\]\\mu\_code\\**_ 이다. _(아래 그림 참조)_ ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FFkdfpbFprfZijgSvKTYu%2Fimage.png&width=768&dpr=3&quality=100&sign=b0d249f6&sv=2) circle-info _**작성한 소스코드 파일의 저장시, 저장위치는 당연히 고정된 것이 아닌 사용자가 원하는 위치에 저장할 수 있으나, 이 책을 따라 예제실습을 해나갈 때 필요한 리소스(이미지, 폰트, 효과음, 배경음)를 활용하기 위해서는 임의로 저장하지 말고, 위에서 언급된 Mu 에디터의 소스코드 기본 저장위치(\[사용자 계정명\]\\mu\_code\\)에 파일을 저장하길 요청한다.**_ 왜 그래야 하는지에 대한 이유에 계속되는 내용에서 자세히 언급될 예정이다. 소스코드 저장을 완료했다면 이제 f5 단축키를 눌러 실행시켜보자. 화면은 흰색으로 잘 칠해져 있을 것이다. 화면색에 대한 색상값을 위에서 배운 3가지 방법을 적용해 바꿔가며 실습해보는 것은 여러분의 과제로 맡기겠다. Copy from pgzhelper import * TITLE = '멍멍이와 야옹이가 인사해요' WIDTH = 480 HEIGHT = 270 def draw(): screen.fill('white') 이제 그 다음으로 할 일은 게임의 배우 주인공 오브젝트들인 "멍멍이"와 "야옹이"를 불러오고 화면에 배치시켜야 할 차례이다. 이를 코딩하기 위해선 각 오브젝트의 외관을 표현할 이미지 파일들이 필요하다. [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat#resource_mgnt) 파이게임제로 리소스 관리 --------------------------------------------------------------------------------------------------------------------- 엔트리 블록코딩에서의 경험을 상기시켜 보자. 여러분이 게임을 만들 때 가장 먼저 했던 일은 무엇이었을까? 화면에 게임에 등장할 오브젝트를 배치하는 일부터 였을 것 같다. 텍스트코딩에서도 유사하다. 우리도 주인공 오브젝트를 화면에 등장시키는 일부터 시작할 것이다. 그러나, 엔트리에서도 그랬듯이 게임에 사용할 오브젝트들이 엔트리에 기본 내장된 오브젝트들이 아니라면 별도로 게임에 사용될 이미지 파일들을 불러와 엔트리에 등록한 이후에서야 코딩을 시작할 수 있었다. 유사하게 만약 개발에 사용될 이미지들이 기본 내장 이미지가 아니라면 별도 저장하는 작업이 필요하다. 저장경로는 Mu 에디터 (이하 뮤 에디터) 설치시 자동으로 생성되는 기본 이미지들의 저장소인 _\[사용자 계정명\]\\mu\_code\\images_ 라는 폴더(디렉토리) 이고, 이미지들은 그 안에 위치되어야 한다. 해당 폴더의 위치를 쉽게 확인하고 싶으면, 뮤 에디터상의 메뉴바의 **이미지(images) 라는 버튼**을 눌러보자. 현재 코딩시 사용되는 이미지들이 저장되어 있는 폴더를 즉시 열어준다. 폴더 안에는 다음과 같이 이 책에서 사용할 이미지들이 이미 여러분의 편의를 위해 사전에 저장되어 있는 것을 확인할 수 있다. 위의 주의사항에서 소스코드 저장위치를 기본 저장위치에 저장할 것을 요청했던 이유가 바로 이 사전 저장된 이미지들을 그대로 활용하여 추가적인 리소스에 관련된 작업없이 우리는 오직 코딩에만 집중하기 위해서였다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252F23njcY9BuRKoqxjc3WUU%252Fimage.png%3Falt%3Dmedia%26token%3D910f9405-6356-4302-a0bc-e6c9b900febd&width=768&dpr=3&quality=100&sign=7b869237&sv=2) ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252F6uaWFqoT7f9qQ8fk7l6O%252Fimage.png%3Falt%3Dmedia%26token%3Ddc12cfd4-b8df-4358-94c0-8e4857b05663&width=768&dpr=3&quality=100&sign=9c61c7da&sv=2) circle-info 참고로 우리가 작성한 소스코드 파일 활용하게 될 **리소스들(이미지, 폰트, 효과음, 배경음)을 담아 놓는 하위폴더들의 이름은 이미 정해진 이름들(images, fonts, sounds, music)로만 생성해야 한다는 파이게임제로 라이브러리가 갖고 있는 기본제약사항이 존재한다.** 그러한 이유로, 뮤 에디터에 메뉴 아이콘의 이름들 역시도 그 하위폴더의 이름들과 일치되어 있는 것이다. 따라서, 우리는 리소스의 종류에 맞춰 각각의 아이콘을 눌러 자동으로 열리는 폴더에 해당 리소스 파일을 저장하는 방식으로 개발하는 것이 편리하기도 하고 실행시 리소스를 발견할 수 없다는 실행에러를 줄일 수 있는 길이다. Mu 에디터의 관련 메뉴얼 참조: [https://codewith.mu/en/howto/1.2/pgzero\_sounds\_imagesarrow-up-right](https://codewith.mu/en/howto/1.2/pgzero_sounds_images) 다음부터 기술될 내용은 참고사항으로 _현재 우리가 사용하는 커스텀 뮤 에디터의 경우, 여러분들의 코딩의 편의를 위해 코딩에 필요한 모든 리소스(이미지, 소리 등)들을 기본 내장시켜 놓았기 때문에_ 이러한 별도의 추가 작업이 필요하지 않다. 그러나, 여러분들 중에 엔트리에서 각각의 오브젝트 이미지들을 어떻게 가져올 수 있는지 궁금해하는 분들을 위한 보충설명이다. #### [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat#undefined-2) 엔트리로부터 오브젝트 이미지 가져오기 엔트리의 멍멍이와 야옹이 오브젝트 이미지를 가져다 사용하기 위해 엔트리를 열어 해당 오브젝트의 이미지 파일을 가져오자. 가져오는 방법은 모양탭을 누르고, 가져올 모양에 마우스 우측버튼을 눌른 후 팝업메뉴에서 "PC에 저정"을 선택해 위에서 언급된 파이게임제로를 위한 images 폴더(_\[계정명\]\\mu\_code\\images)_ 안에 저장시키면 된다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2F3827255905-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fd15BjHzo0klcYpoHnPr6%252Fuploads%252FBW4gViXC673LO3QwGq1G%252Fimage.png%3Falt%3Dmedia%26token%3Db955fa25-534e-4fcf-af95-188b0d322303&width=768&dpr=3&quality=100&sign=3b617f26&sv=2) circle-info 참고로 이 책 예제에 필요한 모든 이미지는 [여기arrow-up-right](https://github.com/roboticsware/python-pygame_zero-samples/tree/main/entry_basic/images_ko) 서도 다운로드 받을 수 있다. 이제 본격적으로 각 오브젝트(멍멍이, 야옹이)를 화면에 나타내고, 두 오브젝트가 상호 인사하는 코딩을 다음절에서 마무리하도록 하자. [Previous5\. 엔트리 초급책을 코딩하기chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding) [Next5.1.2 멍멍이와 야옹이가 인사해요2 - 순차chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.2-dog-cat) Last updated 10 months ago --- # 엔트리-파이썬 단축키 모음 | 엔트리-파이썬 [hashtag](https://jjlee.gitbook.io/entry-python/appendix/shortcuts#undefined) 윈도우즈용 ---------------------------------------------------------------------------------------- **기능** **단축키** 들여쓰기 / 내어쓰기 단계 조정 tab , shift + tab 블록 / 모양 / 소리 / 속성 탭 선택 Alt + 1 , Alt + 2 , Alt + 3 , Alt + 4 이전 / 다음 오브젝트 선택 Alt + \[ , Alt + \] 블록 코딩 / 엔트리파이선 모드 변경 Ctrl + \[ , Ctrl + \] 실행 Ctrl + R [hashtag](https://jjlee.gitbook.io/entry-python/appendix/shortcuts#undefined-1) 맥용 --------------------------------------------------------------------------------------- **기능** **단축키** 들여쓰기 / 내어쓰기 단계 조정 cmd + \[ , cmd + \] tab , shift + tab 블록 / 모양 / 소리 / 속성 탭 선택 Alt + 1 , Alt + 2 , Alt + 3 , Alt + 4 이전 / 다음 오브젝트 선택 Alt + \[ , Alt + \] 블록 코딩 / 엔트리파이선 모드 변경 Ctrl + \[ , Ctrl + \] 실행 Ctrl + R [Previous5\. 부록chevron-left](https://jjlee.gitbook.io/entry-python/appendix) Last updated 2 years ago --- # 5.1 디지털 출력 코딩 | 엔트리-아두이노 **이제 본격적으로 외부의 전자부품들과 전자회로를 구성하고, 입출력 핀을 이용해 이를 제어**해 보도록 합시다. [3.4장](https://jjlee.gitbook.io/entry-arduino/coding_start/hello-world) 에서 우리는 아두이노 우노 보드에 내장된 LED를 점멸시키는 블록코드를 살펴보았습니다. 이번 예제는 디지털 출력(5v)을 내보내는 디지털 핀 위치만 13번에서 2번으로 다를 뿐 코딩적으로는 차이가 전혀 없습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb6wb0GEnfSjwsiYJDU%252F-Mb6wou5fL0cS3qXevQC%252Fimage.png%3Falt%3Dmedia%26token%3Dacce58e7-8240-4816-9033-cd539614e8d3&width=768&dpr=3&quality=100&sign=7512ce38&sv=2) 그럼, 이제 우리가 목적하는 전자회로를 꾸며봅시다. 아래의 전자회로는 외부에 빨간 LED를 아두이노 보드 디지털 2번핀으로 제어하기 위해 꾸며진 회로입니다. **아래의 전자 회로를 꾸미기 위해 알아야 할 사전 지식이 몇 가지 있습니다. ①브레드 보드의 내부 구조, ②LED의 특성, ③저항값 계산입니다.** ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb5iYbJefXidj2m4d8f%252F-Mb5jpy5styu58ByJX_M%252Fimage.png%3Falt%3Dmedia%26token%3D06934f56-1dbe-4077-8355-e1c477803e89&width=768&dpr=3&quality=100&sign=c5bb386e&sv=2) ### [hashtag](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_output#id-1-bread-board) ① 브레드 보드(Bread board)의 내부 구조 전자부품을 배치해 회로를 구성하는 **브래드 보드 내부가 서로 어떻게 연결되어 있는지 알아야, 왜 브레드 보드 위에 저렇게 전자 부품을 배치하고 +와 - 전선을 어디에 연결해야 전자(electron)가 흐를 수 있는 전자회로(Electron Circuit)가 구성되는지 이해**하실 수 있습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb6W60N7PjGEAi_0BgN%252F-Mb6ZRp6WJDEo7UihTCY%252Fimage.png%3Falt%3Dmedia%26token%3D5c0a7dda-74be-4ab0-aa41-feaecc8ae89d&width=768&dpr=3&quality=100&sign=56b8b0d1&sv=2) ### [hashtag](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_output#id-2-led-light-emitting-diode) ② LED(Light Emitting Diode)의 특성 LED라는 이름에서 알 수 있듯이 **다이오드(Diode)는 원래 극성이 존재하는(+/-를 구분해 연결해야 하는) 부품**이고, 아래 그림처럼 +/-를 LED 내부를 자세히 드려다 보고 금속조각 모양을 통해 결정할 수도 있지만, LED의 다리가 긴 쪽이 +입니다. 그밖에 **LED는 색상별(빨간색 LED는 약 2v, 20mA)로 소모전압과 허용전류가 조금씩 다른데** 아래 표는 일반적인 기준이긴 하나, 정확한 정보는 본래 해당 **전자부품을 생산하는 생산자가 직접 공개하는 데이터시트(Datasheet, 일종의 부품메뉴얼)라는 자료를 통해 확인해야 정확**합니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb6c_wpt0-CCzCwtYiz%252F-Mb6dHq1up5D9ZW_7gfN%252Fimage.png%3Falt%3Dmedia%26token%3Df3890392-7d49-4f0a-bc0e-8f5ea36c2646&width=768&dpr=3&quality=100&sign=9059c885&sv=2) ### [hashtag](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_output#id-3-resistor) ③ 저항(Resistor)값 계산 앞서 **LED가 허용 전류가 있기 때문에(허용치를 넘으면 타버리거나, 수명이 줄어듭니다.) 그것에 맞춰 LED에 전류가 지나가도록 해야하기 때문에 전류량을 조절하기 위해 저항을 연결해야** 합니다. 저항은 극성이 없는 전자부품이라 +/- 상관없이 아무 방향으로 연결하셔도 되고, LED 앞단에 연결하시든 뒷단에 연결하시던 차이가 없습니다. 회로에 저항을 직렬연결하면 회로 전체에 흐르는 전류량은 동일하기 때문입니다. **우리에게 고민이 되는 부분은 얼마크기의 저항을 연결해야 하는가? 인데**, 귀하가 성인이라면 이미 전기와 전류의 성질에 대해 학창시절에 배웠을 것이고, 아직 배운 적이 없다하시는 분, 기억이 안난다 하시는 분이 있으셔도 걱정마시고 지금부터 알고 이해하시면 됩니다. 크게 어려운 것은 아닙니다. 아래 그림은 그 계산식을 그림으로 표현해 본 것입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb6n6T7lnGoSq0-N4sf%252F-Mb6q4Yc1PIoc6H8z7Zw%252Fimage.png%3Falt%3Dmedia%26token%3Df403fe0f-d9e4-49e5-a499-fa8eda5f3dba&width=768&dpr=3&quality=100&sign=d6544e5a&sv=2) 우리 전자회로에 사용할 저항값 계산도 동일하고 다만, 아두이노 보드는 디지털 출력에서 5v를 내보내니 9v대신 5v로 바꿔 계산하면, 저항값은 150Ω\\OmegaΩ이 되고, 결론적으로 150Ω\\OmegaΩ이상의 저항을 연결하시면 됩니다. 계산식이 이해되시는 분은 상관없으나, 혹시 이해가 안되시는 분들이 있다면 제가 상세히 기술해 설명할 수도 있으나 그것보다는 차라리 **요즘에는 에니메이션 영상으로 너무나 친철하게 잘 설명해 주는 자료들이 있어 해당** [**유투브 링크**arrow-up-right](https://youtu.be/CQlwmrqhSas) **로 배우시는게 훨씬 더 이해가 빠르고 명확**할 수 있어서 이 설명은 그것으로 갈음하겠습니다. **참고로 색 띠로 된 저항의 저항값 읽는 방법에 대해 궁금한 분이 있을텐데** 링크로 드린 이전 참조 동영상에서 저항값 읽는 법도 설명하기 때문에 필요하지 않을 수도 있는데, 지면으로 한번 더 확인하고자 하시는 분들을 위해 **별도의** [**부록**](https://jjlee.gitbook.io/entry-arduino/appendix/resistor_value) **으로 남기니 참고**하시기 바랍니다. [Previous5\. 실전 아두이노 블록코딩chevron-left](https://jjlee.gitbook.io/entry-arduino/block_coding) [Next5.2 디지털 입력 코딩chevron-right](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_input) Last updated 2 years ago --- # 5.2 디지털 입력 코딩 | 엔트리-아두이노 우리가 디지털 출력을 배웠기 때문에 디지털 입력이 어떤 의미일지 예상해 볼 수 있을 것입니다. **디지털 입력이라는 것은 간단히 말해 외부장치에서 보드쪽으로 입력되는 디지털 값인 0v(0, LOW) 또는 3.3v(255, HIGH)의 두 값 중 하나를 읽어내는 것**입니다. 우리는 **보드와 연결할 외부 장치로 PIR(Passive InfraRed sensor) 센서 모듈(Seonsor Module, 이하 편의상 센서로 언급)를 이용**할 것입니다. 이렇게 **아두이노 같은 콘트롤러(Controller)가 외부의 세계로 부터 정보를 얻을 때 사용하는 장치를 센서(Sensor)라고 합니다.** 모듈이란 말은 기능단위의 부품 집합을 나타내는 일반 용어인데 결국 PIR의 기능 구현이 몇 개의 전자부품의 결합을 통해 구현되어 마치 한 부품처럼 사용할 수 있는 단위 부품으로 이해하면 됩니다. **PIR은 이름 그대로, 특정 범위 안에서 인체같은 어떤 물체에서 방출되는(passive) 적외선(infrared)의 변화량을 감지해 어떤 물체의 움직임이 있음을 감지해 이를 전압으로 표현해 주는 장치**를 말합니다. PIR 센서의 용도는 물체가 감지되었을 때 어떤 리액션(reaction)을 하게 만드는 예를 들어 일상에서 쉽게 볼 수 있는 자동 계단등 처럼 사람이 지나갈 때만 불이 켜졌다가 다 지나가면 꺼지게 하는 용도나 또는 보안 용도로 침입을 감지해 싸이렌을 울리는 등의 용도로 사용이 가능합니다. 이 중에서 우리는 이러한 일종의 자동등을 만들어 봅시다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbSXnTEJFbCIe_4R0U8%252F-MbS_8n3p-i-ZWDrtIDm%252Fimage.png%3Falt%3Dmedia%26token%3De3a1c8a9-7459-44b7-a9e0-58211e265bf9&width=768&dpr=3&quality=100&sign=bbc4df72&sv=2) [이전 장](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_output) 에서 전자회로를 이해하기 전에 먼저 코드를 살펴봤던 것처럼 이번에도 먼저 블록코드로 확인해 봅시다. [이전 장](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_output) 에 코드와의 차이를 발견했나요? "디지털 2번 센서값" 이란 블록이 새롭게 추가되었습니다. 이 블록의 의미는 "디지털 2번 핀을 통해 외부로부터(센서로부터) 읽어드린 값" 이라고 할 수 있습니다. **"만일 ~라면" 이라는 블록에서 사용되는 이 값이 "참(True)"이 된다는 의미는 무엇일까요? 그것은 바로 "PIR 센서에 물체가 감지되면" 의 의미가 됩니다. 다시 말해, PIR 센서는 감지된 물체가 없을 때는 0v(0, LOW)를 내보겠지만, 감지되는 순간 3.3v(255, HIGH)를 내보냅니다.** 우리는 PIR센서가 내보낸 즉 아두이노 보드 입장에선 들어온(입력된) 3.3v(255, HIGH)을 읽어, 그 값에 따라 어떤 반응으로 대응하면(다른 표현으로 피지컬 컴퓨팅) 되는 것입니다. 우리는 이를 [이전 장](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_output) 에서 배운 LED 켜기/끄기로 대응할 것입니다. [**엔트리 커스텀 버전**arrow-up-right](https://github.com/JeongJun-Lee/entry-offline) **에서 블록코드를 아두이노 C++ 코드로 변환한 것 역시도, 이전과 비교해서 pinMode로 2번 핀을 INPUT으로 사용하겠다고 모드설정 한 것과 디지털 2번 핀을 통해 입력된 값을 읽겠다는 의도로 digitalRead 함수를 추가한 것이 다릅니다.** 전체적인 코드는 블록코드와 거의 1:1 대응이 되기 때문에 이해가 크게 어렵지 않을 것으로 생각됩니다. **이처럼, 하드웨어 코딩교육에서 코딩 그 자체는 소프트웨어 코딩교육에서의 코딩에 비해서는 크게 복잡하지 않다는 것을 알 수 있습니다.** ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbSWYF6kVWgOXrsh0WX%252F-MbSWfdnkqk9Ue5xKC6v%252Fimage.png%3Falt%3Dmedia%26token%3D7d3c3bd3-67e5-4dbf-bccf-bfd1acb8fd67&width=768&dpr=3&quality=100&sign=6df8315a&sv=2) 이제 전자회로를 살펴보도록 하겠습니다. 아래 회로는 **독립적으로 구분된 2개의 전자회로로 구성되어 있는 것으로 간주**할 수 있는데, **하나는 이전 과에서 배운 디지털 9번핀으로 LED를 점멸하는 회로**이고, **다른 하나는 디지털 2번핀과 PIR 센서를 연결해 PIR센서 출력값을 읽는 회로**입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbXsEbB8SK1PIX3RvIu%252F-MbXtpuCtahTdDQzab-j%252Fimage.png%3Falt%3Dmedia%26token%3Dc982a1f1-2489-4afc-b92d-968c64514a34&width=768&dpr=3&quality=100&sign=3f60de0a&sv=2) 움직임을 감지하면 PIR 센서는 3.3v(255, HIGH) 값을 출력하고 3초(최소값, 설정에 따라 변동 가능) 동안 그 값을 유지한 후 더 이상 움직임이 없을 경우 0v(0, LOW) 값을 출력합니다. 따라서 우리의 코드에 근거해 PIR센서에서 움직임이 감지될 경우 LED를 켜고, 움직임이 감지되지 않은 채 3초가 지나면 LED가 꺼집니다. 실생활의 계단등의 경우도 움직임이 없더라도 일정시간 조명을 켠 상태로 유지되는 것과 동일합니다. **한번 움직임을 감지 후 몇 초 동안 3.3v(255, HIG)값을 출력할 지에 대한 설정과 몇 m거리까지 움직임을 감지할 지에 대한 센서의 민감도 값 설정에 관해서는** [**부록**](https://jjlee.gitbook.io/entry-arduino/appendix/pir_sensor) **을 참고**하세요. [Previous5.1 디지털 출력 코딩chevron-left](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_output) [Next5.3 아날로그 출력 코딩chevron-right](https://jjlee.gitbook.io/entry-arduino/block_coding/analog_output) Last updated 2 years ago --- # 4.1 아두이노 보드의 종류 | 엔트리-아두이노 현재(2021년) 기준으로 [아두이노 공식 사이트arrow-up-right](https://www.arduino.cc/en/Main/Products) 에서 **입문자용으로 판매하고 있는 아두이노 보드의 종류는 이상 5가지** 입니다. 보드를 선택하는 기준은 결국 교육 목적이라면 교육하고자하는 내용, 메이커 활동이라면 만들고자하는 작품, 제품의 프로토타입이라면 제품의 요구사항 등 목적하는 바에 따라 **보드 자체의 크기, 마이크로 콘트롤러 유닛(MCU)의 성능, 그리고, 보드에 얼마나 많은 외부의 장치를 연결해 입출력 포트(또는 핀)으로 소통할지 등으로 결정**하게 됩니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbfLExxfPHQ7qAMn5Kc%252F-MbfOXDYftBugPO2ULaP%252Fimage.png%3Falt%3Dmedia%26token%3D7a838efc-cc30-49c4-b725-6fb9177ebca6&width=768&dpr=3&quality=100&sign=19101067&sv=2) **본 서에서는 하드웨어 코딩교육에 가장 보편적으로 사용하는 아두이노 우노(Arduino Uno) 보드를 기준**으로 설명합니다. 참고로 엔트리에서 지원하는 아두이노 보드는 아두이노 Uno와 아두이노 Nano 이상 2가지 입니다. [Previous4\. 아두이노 보드 기본chevron-left](https://jjlee.gitbook.io/entry-arduino/board_basic) [Next4.2 아두이노 입출력 핀 구성chevron-right](https://jjlee.gitbook.io/entry-arduino/board_basic/input_output) Last updated 2 years ago --- # 5.4 아날로그 입력 코딩 | 엔트리-아두이노 이제 마지막으로 아날로그 입력으로서 외부장치로 부터 아날로그 입력값을 읽어 활용하는 블록코딩을 해봅시다. [5.2장 디지털 입력 코딩](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_input) 에서 만든 자동등과 유사한 자동등을 만들어 봅시다. 디지털 입력 코딩에서의 자동등은 PIR 센서로부터 물제의 움직임에 대한 판단유무를 디지털 입력값으로 받아 LED를 켜고 껐다면, 이번에는 **조도 센서(Photoresistor)로부터 빛의 밝고/어두움의 정도를 아날로그 입력값으로 받아, 이 값의 정도에 따라 즉 주변이 밝으면 LED를 꺼두지만, 주변이 어두워지면 LED를 켜도록** 하겠습니다. 이러한 예로 우리 일상에서 쉽게 볼 수 있는 것으로는 해가 지거나 날씨가 흐릴 때 자동으로 점등하는 자동차의 자동 전조등, 도로 가에 설치된 자동 점등 가로등 등을 들 수 있습니다. 또한 스마트 폰에도 화면밝기 제어로 해당 기능이 들어가는데 스마트폰을 주머니나 가방속에 넣거나, 수신된 통화를 받기 위해 귀에 가까이 가져갈 때 화면을 끄는 용도로도 사용합니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbbI4oNjvwyEdPYQ0YX%252F-MbbbHfujrh5VHOeZRKt%252Fimage.png%3Falt%3Dmedia%26token%3Dcd7118dd-c469-4246-8065-f7502971909e&width=768&dpr=3&quality=100&sign=f1b405e0&sv=2) 먼저 전자 회로 구성을 살펴보겠습니다. 본 회로는 [5.2 장의 디지털 입력 코딩](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_input) 때와 마찬가지로 독립된 2개의 회로로 구성된 것으로 간주할 수 있습니다. 첫번 째로 LED를 디지털 출력으로 LED를 제어하는 회로인데 이제 이것은 너무나 익숙하실 것 같습니다. 두번 째는 **조도 센서로부터 아날로그 출력값(아두이노 보드 입장에서는 보드로 들어오는 입력값)을 읽어 내기 위한 회로로** [**4.2장**](https://jjlee.gitbook.io/entry-arduino/board_basic/input_output#analog-input) **에서 살펴보았듯이 아두이노 우노 보드 상에는 아날로그 입력핀들(A0~A5핀, 6개)은 공용이 아닌 별도로 존재하고 이들 중 하나인 A0 핀과 연결**해 회로를 꾸몄습니다. 회로를 이해하시기 위해서는 조도 센서 기본적인 특성을 이해하셔야 되는데 **조도 센서는 주변 빛의 밝기에 따라 변화하는 일종의 가변 저항(저항값이 고정된 것이 아니라 변동적인)이라는 것이고, 빛이 밝으면(빛의 세기가 강해지면) 저항값이 작아지고, 반대로 빛이 어두우면(빛의 세기가 약해지면) 저항값은 커집니다. 회로를 보시면 조도 센서 앞단에 저항이 하나 연결된 것을 보실 수 있습니다. 조도 센서 자체도 (가변)저항이니 결국 이 회로는 저항 2개가 직렬로 연결된 회로라고 이해**하시면 됩니다. **따라서, 회로상 아두이노 보드 입장에서 아날로그 입력으로 받아드리고 있는 값의 실체는 결국 전압분배법칙(**법칙을 잘 모르시는 분들은 [참조 링크arrow-up-right](https://www.youtube.com/watch?v=4GtcaMhOOv0&t=428s) 의 7:08부분 참조)**에 의해 결정된 조도 센서의 전압값**입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbbI4oNjvwyEdPYQ0YX%252F-MbbbfFTWZGT26H39_iK%252Fimage.png%3Falt%3Dmedia%26token%3Df60e902c-6ee3-447a-9510-da067e66c81b&width=768&dpr=3&quality=100&sign=ae59d761&sv=2) 그렇다면 **조도센서가 자체가 저항인데 왜 추가로 저항을 달아야 하나가 궁금하실 수 있는데, 조도 센서 자체를 보호하기 위해 저항을 단 것이 아니라**, 조도 센서가 빛의 밝으면 밝을 수록 저항값이 점점 더 작아진다고 했는데, 조도 센서의 종류에 따라서는 저항값이 너무 작아져, 그 얘기는 반대로 **지나치게 큰 전류가 흐르게 되어 아두이노에 손상을 입히지 않도록 제한을 두기 위한 목적도 있고, 조도 센서 자체 특성으로 표현되는 가변저항의 최소/최대값 범위에 맞춰 저항의 직렬 연결에 따른 회로의 전압 분배법칙(잘 모르시는 분들은** [**참조 링크**arrow-up-right](https://www.youtube.com/watch?v=4GtcaMhOOv0&t=428s) **의 7:08부분 참조)으로 의한 적당한 범위의 아날로그 출력값을 외부로 내보내고자 하는 목적의 저항**으로 이해하시면 됩니다. 조도 센서의 종류에 따라 달라질 수 있는데 미니 사이즈 조도 센서에는 보통 10KΩ\\OmegaΩ정도를 달게 됩니다. 자세한 조도 센서의 특성과 저항값의 결정, 그리고 그 저항값에 따른 조도 센서의 특성 변화 등 자세히 알고 싶은 분을 위해 별도의 [참고 링크arrow-up-right](https://makeabilitylab.github.io/physcomp/sensors/photoresistors.html) 를 남기니 이를 참고하시면 좋겠습니다. 다음으로 블록코딩 내역을 살펴보겠습니다. 이번 아날로그 입력 코딩에서 새롭게 추가된 블록을 발견하셨나요? 그것은 바로 **아두이노의 아날로그 핀에서 값을 읽는 아날로그 핀 읽기 전용 블록**입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbbcJTcxQTJkPyUxZF4%252F-MbbcaisOSNjuSy8f86k%252Fimage.png%3Falt%3Dmedia%26token%3D3b103f00-9baf-4359-87e5-fbba75cf6651&width=768&dpr=3&quality=100&sign=c2253f2d&sv=2) 다음은 해당 블록을 적용한 전체 블록코딩 내역입니다. 먼저 [3.3장](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog#analog-output) 에서 외부 아날로그 신호가 아두이노에 탑재된 10비트(bits) 짜리 ADC(Analog to Digital Converter)에 의해서 0v~5v의 아날로그적 전압 변화량이 0~1023의 디지털 값으로 변환되어 전달된다는 것을 기억하실 겁니다. 그러나, 이 회로에서는 **조도 센서와 함께 연결할 저항의 크기에 따라 변화의 범위가 1024단계 전체가 아닌 일부로 표현범위가 달라지게 됩니다. 이것은 코드에서는 어둡다/밝다를 판단하는 기준값인 500 에도 영향을 미칩니다. 이 값은 처음부터 명확하게 어떤 계산으로 정해지는 것이 아니고, 여러 번의 실험을 통해 값을 정한다 라고 이해하시면 되겠습니다. 값 결정에 영향을 미치는 요소는 크게 3가지 정도인데** 첫번째는 먼저 말씀드린 조도 센서와 함께 연결하는 저항의 크기, 두번째는 센서 주변 평균 빛의 밝기, 마지막 세번째는 보통 실험을 위해 손으로 조도 센서를 덮어 빛의 어두움을 표현해 보는데, 이 때, 센서를 덮는 손 모양이 어떤지에 따라서도 밝기의 미세한 차이를 만들 수 있습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbfiUW4LXBJji1OqU3G%252F-MbfjPhbwg-c5jIy9Bn3%252Fimage.png%3Falt%3Dmedia%26token%3Dba0ee0a2-2103-4a26-a3fa-3d93dafac587&width=768&dpr=3&quality=100&sign=930a29fb&sv=2) [Previous5.3 아날로그 출력 코딩chevron-left](https://jjlee.gitbook.io/entry-arduino/block_coding/analog_output) [Next6\. 마무리chevron-right](https://jjlee.gitbook.io/entry-arduino/epilog) Last updated 2 years ago --- # 3.1 아두이노 블록코딩의 한계 | 엔트리-아두이노 아두이노를 블록코딩으로 배우는 것에 장단점은 명확한데 장점으로는 원래 아두이노를 다루기 위한 텍스트코딩 언어인 C++를 모르고도 아두이노를 제어하는 코딩을 해 볼 수 있다는 것이고, 단점으로는 블록코딩의 블록코드 자체의 표현력의 한계로 인해 텍스트코딩 언어와 같은 동일한 수준에 개발은 어렵다는 것입니다. **다시 말해, 교육용으로 사용은 가능하나 실전 개발용까지는 아니라는 것입니다. 본질적으로 엔트리와 같은 교육용 프로그래밍 언어(EPL: Educational Programming Language)의 목적 자체가 블록코딩으로 실전같은 개발이 아니라, 실전 개발로 가고자 하는 분들을 위한 다리 역할로서의 코딩교육까지가 추구하는 목표이기 때문이라 할 수 있겠습니다.** 엔트리가 기본 제공하고 있는 블록 코딩 <-> 파이썬 텍스트 코딩사이의 변환에서도 알 수 있듯이, 본 서에 사용하는 [엔트리 커스텀 버전arrow-up-right](https://github.com/JeongJun-Lee/entry-offline) 에서 블록 코딩 -> C++ 텍스트 코딩으로의 변환기능을 제공하고 있으나, 그 반대인 텍스트 코딩 -> 블록 코딩으로 변환은 제공하고 있지 않은데, 이유는 텍스트 코드의 표현력을 블록 코드로 다 담아낼 수 없기 때문입니다. [Previous3\. 아두이노 블록코딩의 시작chevron-left](https://jjlee.gitbook.io/entry-arduino/coding_start) [Next3.2 아두이노 보드와 연결하기chevron-right](https://jjlee.gitbook.io/entry-arduino/coding_start/hw_connect) Last updated 2 years ago --- # 3.4 Hello World 코딩 | 엔트리-아두이노 'Hello World' 코딩이란 모든 텍스트코딩용 언어(파이썬, C++ 등)에서 해당 언어를 어떻게 사용하지는 예시로 보여주기 위해 언어의 가장 쉬운 표현을 사용해 보여주는 가장 기본 예시 코드를 말할 때 사용하는 고유명사와 같은 용어입니다. 따라서, **엔트리** **아두이노 코딩에서의 'Hello World' 코드는 무엇일까? 아마도 아래의 예와 같은 아두이노 내장 LED를 1초에 한번 점멸시키기(Internal LED blink) 코드라 할 수 있습니다.** 보드의 내장 LED(Light Emitting Diode)를 이용하기 때문에 당장은 어떤 추가적인 전자부품의 연결이나 회로 이해가 필요하지 않기 때문입니다. **디지털 13번 핀은 내장 LED와 내부적으로 연결되어 있어서 아래와 같이 코딩가능하고, 코드는 간단히 13번 핀에 1초마다 5v를 보냈다/안보냈다를 무한 반복**하는 것입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb11NnYChkvpIHe4sUr%252F-Mb127iSgJBE8KZuT2LP%252Fimage.png%3Falt%3Dmedia%26token%3D71e3978d-1b49-489b-9369-9d191200230b&width=768&dpr=3&quality=100&sign=ed7672c4&sv=2) ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb5eH3fsj-3oRx-Lh65%252F-Mb5hs0fv_Syp6ldorv6%252Fimage.png%3Falt%3Dmedia%26token%3D81b250f3-f78c-44d6-acbe-2ca689043527&width=768&dpr=3&quality=100&sign=569838ed&sv=2) 어떤가요? **아두이노 내장 LED가 1초마다 잘 깜빡이나요? 이를 다시 더 컴퓨터적인(기계가 이해하는) 언어로 표현하면 13번 핀(다른 말로 포트)에 0과 1(다른 말로 1byte=8bits 즉,** 282^828**으로 표현할 수 있는 최대값 255)값을 1초마다 잘 쓰고(write) 있는가?** 라고 말할 수 있는데, 이를 **엔트리 내장 하드웨어 모니터링창을 통해 확인**할 수 있습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb1Av-9Hgmcz24Eyu5Y%252F-Mb1CHHbZj-xUgqZa2s9%252Fimage.png%3Falt%3Dmedia%26token%3Dd762cb68-0c42-4731-9671-bc1653812b10&width=768&dpr=3&quality=100&sign=df116e02&sv=2) 이제 방금 작성한 코드를 아두이노 보드 안으로 업로드(upload) 시켜 저장시켜 봅시다. [Previous3.3 디지털 신호와 아날로그 신호chevron-left](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog) [Next3.5 코드 업로드 하기chevron-right](https://jjlee.gitbook.io/entry-arduino/coding_start/code_uplaod) Last updated 2 years ago --- # 3.6 블록코딩을 텍스트 코딩으로 변환하기 | 엔트리-아두이노 엔트리 사용자라면 블록코드를 엔트리 파이썬 코드로 상호변환 해주는 기능이 있다는 것을 잘 알 것입니다. 이 목적은 소프트웨어 개발 코딩교육이 블록코딩에서 머무르지 말고, 실전/실무에 사용하는 텍스트언어 기반 코딩교육까지도 맛볼 수 있도록 의도한 기능이라 할 수 있습니다. 우리가 사용하는 [엔트리 커스텀 버전arrow-up-right](https://github.com/JeongJun-Lee/entry-offline) 에도 유사한 기능이 있는데, 바로 **아두이노 블록코딩한 것을 C++ 텍스트 코으로 변환해 주는 기능입니다.** **본 기능은 일반적인 하드웨어 제어용 텍스트코딩 실무에서는 주로 C/C++로 코딩하기 때문에 단순히 블록코딩에 의한 하드웨어 제어의 경험을 넘어서 텍스트코딩을 활용한 코딩까지 더 심화로 공부하고자 하는 분들, 메이커 활동을 하는 누군가 인터넷에 공유한 C++ 코드를 내 아두이노에 활용해 보고자 하는 분들 등 몇 가지 영역에 있어 매우 도움이 되는 유용한 기능**이라 할 수 있습니다. 아래 코드변환 버튼을 눌러 내 블록코드가 텍스트 코드로는 어떻게 되는지 확인해 볼까요? 잠시 해당 C++코드를 이해해 봅시다. 여러분이 C++ 언어를 몰라 당장 이해하지 못한다 할지라도 크게 부담갖지는 마시기 바랍니다. 차근히 시간을 내서 들여다보면 이해을 못한다라기 보다 사람마다 걸리는 시간이 다를 뿐인지 결국 이해할 수 있습니다. 코드 내용은 이것인데 13번 핀을 0v/5v의 출력(OUTPUT) 목적으로 사용하겠다고 알리고(setup 함수 안에서 pinMode 설정), 이후 13번 디지털 핀에 디지털 값인 1(HIGH, 5v)과 0(LOW, 0v)를 1초(1000ms)간격 쉬면서(delay함수) 번갈아 쓰기를(digitalWrite함수) 무한반복(loop함수 안에 위치시켜)하게 하는 코드입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MaoCrU7aM6SgouXppME%252Fuploads%252FUV0BsZrii6lQRVjs92nE%252Fcode_conversion.gif%3Falt%3Dmedia%26token%3D995c3d8d-ff8b-4ea8-a68b-abad628e8196&width=768&dpr=3&quality=100&sign=eb099cfe&sv=2) **해당 기능은 단순히 코드변환된 결과만을 보여주는 창이 아니고, 변환된 C++언어 코드창은 일종의 코드 에디터(Editor)처럼 동작하여 직접 텍스트코드를 수정해 볼 수도 있고, 다시 업로드 하기 버튼을 눌러 수정한 내용을 아두이노로 재업로드 할 수도 있습니다.** [Previous3.5 코드 업로드 하기chevron-left](https://jjlee.gitbook.io/entry-arduino/coding_start/code_uplaod) [Next4\. 아두이노 보드 기본chevron-right](https://jjlee.gitbook.io/entry-arduino/board_basic) Last updated 2 years ago --- # 6.2 PIR 센서모듈 설정 방법 | 엔트리-아두이노 **PIR(Passive Infrared 은 이름 그대로, 특정 범위 안에서 인체같은 어떤 물체에서 방출되는(passive) 적외선(infrared)의 변화량을 감지해 어떤 물체의 움직임이 있음을 감지해 이를 전압으로 표현해 주는 장치**를 말합니다. 반드시 움직임이 있어야 감지가 되고, 움직이지 않고 가만히 있으면 감시할 수 없습니다. 움직임이 없다는 의미는 두 가지를 포함하는데, 물체가 센서 탐지범위를 벗어났거나, 탐지범위 안에 있으나 움직임이 없는 두 가지 경우를 다 말합니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbWpMh9sno1GpilJxAT%252F-MbXAN9vhcWDyDpOuzJT%252Fimage.png%3Falt%3Dmedia%26token%3Dc2774b1d-d911-46dc-b542-0e522e7c2ba7&width=768&dpr=3&quality=100&sign=5f2b5f6c&sv=2) 먼저, 아두이노 실습에 일반적으로 많이 사용하는 PIR 센서모듈인 HC-SR501의 상세 데이터 시트는 이 [링크arrow-up-right](https://www.alldatasheet.com/datasheet-pdf/pdf/1131987/ETC2/HC-SR501.html) 에서 확인할 수 있습니다. 아래 그림에서 보는 것처럼 PIR은 한 개의 단일 전자부품이 아닌 몇 개의 전자부품의 집합으로 구성되어 즉 모듈이 되어 한 기능단위를 구성합니다. **PIR 센서 모듈에서 우리가 설정할 수 있는 값은 반복탐지 여부(Trigger Mode), 탐지거리(Detection Distance), 탐지결과 지연시간(Time Delay) 이상 3가지** 입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbXe9FbnJ6ZXArvS9vB%252F-MbXennZopT3gJdwb82x%252Fimage.png%3Falt%3Dmedia%26token%3Df0d537c7-0cf0-4b0c-97c5-dd32adbe432f&width=768&dpr=3&quality=100&sign=8bf7b49f&sv=2) ### [hashtag](https://jjlee.gitbook.io/entry-arduino/appendix/pir_sensor#id-1-trigger-mode) ① 반복탐지 여부 (Trigger Mode) 탐지 범위 안에서 움직임 물체를 최초 탐지한 결과를 출력하는 것은 너무 당연하고, 이후 **해당 물체가 계속 탐지범위에서 재탐지 될 때, 그 결과를 어떻게 처리할지에 대한 것**입니다. 최초 탐지된 것으로만 만족하고 반복해서 탐지되도 무시할 수 있고(non-repeatable), 아니면 재탐지 될 때마다 탐지결과 지연시간을 멈춤없이 계속 지연시키도록(repeatable) 설정할 수 있습니다. ### [hashtag](https://jjlee.gitbook.io/entry-arduino/appendix/pir_sensor#id-2-detection-distance) ② 탐지거리 (Detection Distance) **몇 미터 거리까지의 물체를 탐지할 것인지에 관한 것**으로 3~7m 까지 탐지 가능하며 반시계 반향으로 돌릴 수록 값은 최소화 됩니다. ### [hashtag](https://jjlee.gitbook.io/entry-arduino/appendix/pir_sensor#id-3-time-delay) ③ 탐지결과 지연시간 (Time Delay) **탐지결과 지연이란 탐지된 후 결과인 3.3v(255, HIGH) 값을 몇 초 동 지속해 출력할 것인지에 관한 것**으로 3초-5분까지 설정이 가능하고, 역시 반시계 반향으로 돌릴 수록 값은 최소화 됩니다. [Previous6.1 저항의 종류와 색 띠 저항값 읽는 법chevron-left](https://jjlee.gitbook.io/entry-arduino/appendix/resistor_value) Last updated 2 years ago --- # 6.1 저항의 종류와 색 띠 저항값 읽는 법 | 엔트리-아두이노 **저항은 전선을 흐르는 전자의 흐름 중 전자들이 저항을 구성하는 원자와의 인위적 충돌을 만들고, 이러한 충돌로 생긴 전기에너지를 열에너지로 변환해 전선에 흐르는 전류량을 감소시키는 장치**입니다. 아래 그림처럼 저항의 종류는 다양합니다. 저항의 값이 얼마인지 확인한다는 것은 어떤 의미인가요? 전자부품 표면에 숫자로 자신의 값을 표현한 경우는 상관없지만, 저항의 경우는 전통적으로 색 띠로 그 값을 표현해 왔습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbPsFs2L-mseznxlzeV%252F-MbPvWg3RBJynIMcBitH%252Fimage.png%3Falt%3Dmedia%26token%3Dd9f31dec-112b-498a-aa16-ce3d6692868e&width=768&dpr=3&quality=100&sign=65c6ce44&sv=2) 따라서, 색 띠로 값을 읽는 법을 알아야 할 필요가 있습니다. 방법은 크게 어렵지 않고, **아래의 색 테이블만 있으면 테이블을 참조해 값을 알 수 있습니다.** 색 띠의 의미는 다음과 같습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbPsFs2L-mseznxlzeV%252F-MbPzagUe_GylxWyTfbM%252Fimage.png%3Falt%3Dmedia%26token%3D49fe70ba-c149-4644-8e34-baab7f755a36&width=768&dpr=3&quality=100&sign=476e8a00&sv=2) 먼저 **알아둘 기본 규칙이 있는데, 맨 마지막 즉 다른 색 띠보다 조금 넓은 간격으로 홀로 떨어져 있는 색 띠는 저항의 오차값을 의미**합니다. 저항을 제작할 때 목표한 값에 완전 일치하게 만들 수 없기에 목표한 값 기준으로 +/- 약간의 오차를 갖게 됩니다. **그 오차 이전에 위치한 색 띠는 10의 제곱수를 의미**합니다. **그 외에 나머지 색 띠들은 차례대로 자릿 수를 의미**하게 됩니다. 자 이제 위에 예시를 함께 계산해 볼까요? 첫번째 색 띠(검)는 표에 의해 값이 0입니다. 두번째 색 띠(빨)은 표에 의해 값이 2입니다. 세번째 색 띠(녹색)은 표에 의해 값이 5입니다. 네번째 색 띠(노)은 표에 의해 값이 10410^4104입니다. 그리고, 항상 맨 마지막 자리의 색 띠(파랑)값은 저항의 오차에 대한 것입니다. 따라서, 이것을 종합하면 025x10410^4104\= 250KΩ±0.25\\Omega \\pm0.25Ω±0.25% 입니다. 자, 이제 여려분은 저항값을 읽을 수 있게 되었습니다. 축하합니다! [Previous7\. 부록chevron-left](https://jjlee.gitbook.io/entry-arduino/appendix) [Next6.2 PIR 센서모듈 설정 방법chevron-right](https://jjlee.gitbook.io/entry-arduino/appendix/pir_sensor) Last updated 2 years ago --- # 뮤 에디터 단축키 모음 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 그밖에 단축키 내역은 [여기arrow-up-right](https://codewith.mu/en/tutorials/1.2/shortcuts) 서 확인 [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix/shortcuts#undefined) 윈도우즈용 --------------------------------------------------------------------------------------------------- 기능 단축키 사용가능 버전 들여쓰기 / 내어쓰기 단계 조정 Tab / Shift + Tab 선택 라인을 주석화 하기 Ctrl + k 현재 라인을 지우기 Ctrl + l (단어)검색하기 Ctrl + f 전방방향으로 이어서 검색 F3 후방방향으로 이어서 검색 Shift + F3 코딩화면 확대 / 축소 Ctrl + Shift + = 또는 Ctrl + - 코드의 실행과 멈춤 F5 코드 검사 F2 코딩모드 변경 Ctrl + Shift + m 설정화면 열기 Ctrl + Shift + d 코딩화면 화면캡처(스크린 샷) Ctrl + Alt + s 파일을 새 이름으로 저장 Ctrl + Shift + s 현재 탭 (원도우) 닫기 Ctrl + w 커스텀 1.2.5 이상 탭 좌/우 이동 Ctrl + , 또는 Ctrl + . 커스텀 1.2.6 이상 [hashtag](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix/shortcuts#undefined-1) 맥용 -------------------------------------------------------------------------------------------------- 윈도우즈용 단축키에서 Ctrl키 대신 Command키를 사용하면 대부분 동일하게 동작 [Previous게임을 단 한 개의 실행파일로 패키징 하기chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix/packaging) Last updated 1 year ago --- # 게임을 단 한 개의 실행파일로 패키징 하기 | 엔트리 기초책 7개 예제 클론하며 파이썬 배우기 **먼저 이 기능은 일반 공식 Mu(이하 뮤) 에디터에서는 제공되지 않으며, 저자가 개발하고 있는** [**커스텀 버전의 뮤 에디터**arrow-up-right](https://github.com/roboticsware/mu/releases) **에서만 가능함을 먼저 알린다.** 사용방법은 간단하다. 먼저, _내가 만든 게임이 다른 사람에게 배포해도 될 정도로 충분히 완성이 되었다고 생각하면, 에디터에 내장된 해당 기능을 이용해 게임제작시 필요했던 소스파일, 추가 모듈, 리소스들(이미지, 사운드, 폰트 등)을 다 모아 게임을 단 한개의 실행파일 형태(윈도우즈 플랫폼의 경우, exe파일)로 자동으로 만들 수 있다. 이런 형태가 되어야만 내 게임을 다른 이들에게 배포하기도 쉽고, 게임 사용자들 입장에서도 단 한개의 실행파일을 더블클릭 함으로써 게임을 즐길 수 있어 간편하다._ 예를들어 [다음 단계의 책](https://jjlee.gitbook.io/python-pygame_zero/flappy_bird) 에서 함께 만들어 볼 플래피버드 게임의 패키징을 시도해 보자. 먼저, 현재 패키징 하려는 게임 소스파일의 위치가 어떻게 되어있는지를 살펴보길 바란다. 에디터 설치 시 에디터가 파이게임제로의 기본폴더로 설정한 "계정명/mu\_code" 에 위치할 수 있고 물론 이 경우에도 그대로 패키징을 시도할 수는 있다. 그러나, **게임에는 전혀 관련없는 파일들이 함께 패키징돼 불필요한 용량만 커지는거 외에 아무 득이 없기 때문에 가능한한 패키징시 정확하게 게임에 필요한 파일과 리소스들만을 패키징 하는게 젤 용량의 낭비가 없는 방법일 것이다.** 이미 처_음 게임개발 시작시부터 관련 파일들을 별도의 폴더로 정리해서 개발을 시작했으면 그대로 패키징을 시도하면 될 것이고, 아닌 경우는 이제서야 따로 모아 정리해도 된다._ 다음과 예시는 플래피버드 게임에 사용되는 소스파일과 리소스들만 별도 폴더 안에 정리한 것이다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2F7dL8bzQbsovj74ZBHg1Q%2Fimage.png&width=768&dpr=3&quality=100&sign=672c113b&sv=2) 폴더로 정리될시 주의할 점이 하나 있는데, [5.1.1절 리소스 관리](https://jjlee.gitbook.io/pygame_zero-entry_basic/start_coding/1.1-dog-cat#resource_mgnt) 에서 언급된 리소스들(**이미지, 폰트, 효과음, 배경음**)을 담아 놓는 하위폴더들의 이름은 이미 정해진 이름들(**images, fonts, sounds, music)**로만 생성해야 한다는 파이게임제로 라이브러리가 갖고 있는 기본제약사항을 준수해서 생성되어야만 한다는 것이다. 이제 준비는 끝났고, 이후 방법은 아주 단순한데 아래 이미지처럼 _패키징 할 게임의 소스파일탭에 포커스가 되어있는 상태(만약, 여러탭을 열어놓았을 경우)에서_ _"__**패키징**__" 이란 메뉴아이콘을 누르기만 하면 된다._ ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FjVeaWFeOyDwXMKu9bseH%2Fimage.png&width=768&dpr=3&quality=100&sign=c6a84404&sv=2) 버튼을 누름과 동시에 패키징 작업이 진행될 것이고, 다음의 이미지처럼 최종적으로 "... **completed successfully**" 라고 패키징이 성공적으로 끝났다면, 소스파일이 저장되어 있던 곳에 **dist 라는 폴더**가 생성되어 있는 것을 확인할 수 있고, 그 안에 단 1개의 실행파일로 패키징된 플래피버드 게임을 확인할 수 있을 것이다. ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2F2AeOa10RvPJOjiu4U4hY%2Fimage.png&width=768&dpr=3&quality=100&sign=c402f0df&sv=2) ![](https://jjlee.gitbook.io/pygame_zero-entry_basic/~gitbook/image?url=https%3A%2F%2Fcontent.gitbook.com%2Fcontent%2Fd15BjHzo0klcYpoHnPr6%2Fblobs%2FKbm2HLQdaiGjCSZkvzgr%2Fimage.png&width=768&dpr=3&quality=100&sign=357a7fe1&sv=2) [Previous부록chevron-left](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix) [Next뮤 에디터 단축키 모음chevron-right](https://jjlee.gitbook.io/pygame_zero-entry_basic/appendix/shortcuts) Last updated 10 months ago --- # 4.2 아두이노 입출력 핀 구성 | 엔트리-아두이노 **아두이노 보드의 하드웨어적 입출력 핀 구성을 쉽게 이해하려면 두 가지만 기억**하면 됩니다. **디지털/아날로그의 두 종류로 구분되고, 각각은 다시 입력/출력으로 구분된다는 것입니다. 아두이노 보드의 사용방법을 익힌다는 것은 결국 디지털 입/출력, 아날로그 입/출력 사용법을 배운다는 것**입니다. 각 핀의 상세 위치는 아래의 그림에서 확인할 수 있는데, 눈여겨 보실 것은 총 14개의 디지털 핀들은 입/출력을 공용으로 사용할 수 있고, 아날로그 입/출력은 별도로 각각 입력용 6개와 출력용 6개로 구분된다는 것입니다. 한 가지 **의아하게 생각되실 것은 그림 상 디지털 입출력 핀과 아날로그 출력핀이 함께 핀을 공유하는 것**이 보이실 것입니다. 맞습니다. 그 **이유는** [**3.3장**](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog#undefined-1) **에서 이미 언급했듯이 아날로그는 실제 아날로그 그 자체라기 보다 디지털화된 아날로그 방식으로 표현되는 것이라 아두이노 상에서 아날로그 출력도 결국은 디지털이기 때문에 0~13까지 총 14개 핀 중 일부(6개, 핀에 아날로그를 의미하는 ~무늬로 표기)를 아날로그 출력용으로 공용**할 수 있습니다. 이전 [3.6장](https://jjlee.gitbook.io/entry-arduino/coding_start/code_transform) 의 C++ 코드에서 pinMode라는 함수를 기억나시나요? 그 함수가 필요했던 이유가 이제 이해되실덴테 이렇게 공용되는 핀들이 존재하기 때문에 해당 핀을 사용하기 전 사전에 내가 이 핀은 어떤 용도로 사용하겠다고 미리 알려주어야만 하는 필요가 생겼던 것입니다. 그런데 블록코딩에서는 어떨까요? 이런 핀의 용도를 설정하는 블록은 따로 없습니다. 사용자들이 코딩을 어렵게 느끼지 않도록 내부적으로 숨겨놓은 것입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb4S0osSpUh8VKZAEmp%252F-Mb4TwJSVfHUHGTDWzfW%252Fimage.png%3Falt%3Dmedia%26token%3D49d3af7e-b77b-4c18-a3cd-20104db703b6&width=768&dpr=3&quality=100&sign=d70b72d4&sv=2) 지금까지 우리는 [3.3장](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog) 에서 디지털 출력/아날로그 출력 2가지를 익혔습니다. 자 그럼, 남은 2가지 디지털 입력과 아날로그 입력에 대해 알아볼까요? ### [hashtag](https://jjlee.gitbook.io/entry-arduino/board_basic/input_output#digital-input) 디지털 입력 (Digital INPUT) 디지털 출력이 디지털 값을 보드 외부로 출력했다면, 디지털 입력은 말 그대로 디지털 값을 외부로부터 보드 안쪽으로 입력을 받아드린다(다른 말로 값을 read한)는 것입니다. 외부로부터 입력되는 값은 어떤 값일까요? 예상하실 수 있을텐데 디지털 값인 0(LOW, 0v) 또는 1(HIGH, 5v 또는 3.3v)이 입력됩니다. ### [hashtag](https://jjlee.gitbook.io/entry-arduino/board_basic/input_output#analog-input) 아날로그 입력 (Analog INPUT) 아날로그 입력도 쉽게 이해되실텐데 외부로부터 보드 안쪽으로 아날로그 값 입력을 받아드린다(다른 말로 값을 read한다)는 것입니다. 외부로부터 입력되는 값은 어떤 값의 범위는 어떻게 될까요? 먼저, [3.3장](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog#undefined-1) 에서 **아날로그 출력값의 범위는 0~5v의 변화를 0~255의 256개 값(1bytes=8bits=**282^828**으로 표현할 수 있는 최대값)으로 표현했던 것이 기억나세요? 그럼, 아날로그 입력값도 동일할까요? 만약 그랬다면, 저희가 외우기 쉬웠을텐데 그렇지 않고 아래 그럼처 0~5v의 변화가 0~1023까지의 1024개 값로 표현**됩니다. 이러한 아날로그를 디지털화하는 전자회로를 ADC(Analog to Digital Converter)라고 하고, 이두이노에 내장된 이 회로의 표현력이 최대 10비트(bits)라 2진수의 10비트(bits)인 2102^{10}210\=1024 값까지 표현되는 것입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb4PJaatCewMGozSYa_%252F-Mb4R79TG83h_Wmhjyx5%252Fimage.png%3Falt%3Dmedia%26token%3Db224488a-7f38-4f85-9586-4097302f9499&width=768&dpr=3&quality=100&sign=c3d5e7c9&sv=2) 마지막으로 **C++코드에서도 위에서 우리가 배운 디지털 입/출력, 아날로그 입/출력을 위한 4종류의 함수가 있다**는 것으로 이 장을 마무리 하고자 합니다. **함수이름은 의미를 직관적으로 이해하실 수 있는데 digitalWrite / digitalRead, analogWrite / analogRead** 입니다. [Previous4.1 아두이노 보드의 종류chevron-left](https://jjlee.gitbook.io/entry-arduino/board_basic/arduino_sort) [Next5\. 실전 아두이노 블록코딩chevron-right](https://jjlee.gitbook.io/entry-arduino/block_coding) Last updated 2 years ago --- # 5.3 아날로그 출력 코딩 | 엔트리-아두이노 아날로그 출력 코딩에서 사용할 예제는 기본적으로 [5.2장 디지털 출력 코딩](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_input) 에서 사용한 전자회로 구성방식을 그대로 사용합니다. 다만, 전자회로에서 서로 차이가 있는 것은 출력에 사용하는 핀이 다른데 **아날로그 출력이기 때문에 아두이노 우노의 아날로그 전용 출력핀(PWM 핀: ~3, ~5, ~6, ~9, ~10, ~11)을 사용해야 하고, 코딩할 때도 이를 염두해 코딩**하시면 됩니다. 디지털 출력에서는 디지털 방식의 제어인 LED의 단순 순간적인 점멸이었다면, 아날로그 출력에서는 이제 이를 아날로그적으로 제어해 크리스마스 트리에서 볼 수 있는 LED처럼 서서히 밝아졌다가 다시 서서히 희미해졌다를 반복하는 것을 구현해 보는 것입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbZNGl80OGNfR1m5mOs%252F-MbZPI-qgYhe8ZboNB7w%252Fimage.png%3Falt%3Dmedia%26token%3D02c35a7d-290c-49e7-9bb5-d0dab35e73c3&width=768&dpr=3&quality=100&sign=771a9592&sv=2) 자 그럼 이제 코딩 부분을 살펴봅시다. PWM에 대한 이해는 [3.3장](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog#analog-output) 에서 알려드린 [유투브 동영상arrow-up-right](https://www.youtube.com/watch?v=yhpk4V9w-ZM) 을 통해 충분히 이해했다는 전제하에 PWM의 추가적인 설명없이 진행하겠습니다. **아두이노 블록코딩에서는** [**3.3장**](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog#analog-output) **의 그림에서 언급한 것처 PWM 핀 전용 블록이 따로 있고, 0v~5v까지의 출력전압값을 아날로그적으로 표현하기 위해 0~255까지 값으로 설정**할 수 있습니다. 우리 코드에서는 천천히 LED를 밝히고 다시 어둡게 하기 위해 변수를 이용하고 있으며, 변수값을 10ms 시간 간격으로 255단계까지 높혔다가 줄였다를 반복하고 있습니다. C++코드로도 한번 더 살펴보면 이전과 다른 점은 아날로그 출력이기 때문에 출력을 위해 analogWrite 함수를 사용한다는 점입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MbZWjcWLKzWpXfHi4rE%252F-MbZYPRCWrrAcpYIF6P8%252Fimage.png%3Falt%3Dmedia%26token%3Db26693cb-7a8b-432c-a943-217111cbbaa0&width=768&dpr=3&quality=100&sign=c47ff02e&sv=2) [Previous5.2 디지털 입력 코딩chevron-left](https://jjlee.gitbook.io/entry-arduino/block_coding/digital_input) [Next5.4 아날로그 입력 코딩chevron-right](https://jjlee.gitbook.io/entry-arduino/block_coding/analog_input) Last updated 2 years ago --- # 3.5 코드 업로드 하기 | 엔트리-아두이노 이제 우리가 작성한 코드를 아두이노 안에 저장시켜 엔트리 연결 없이, 하드웨어 단독으로 독립적으로 동작하게 해보겠습니다. 방금 작성한 코드는 아쉽게도 엔트리와의 연결이 존재할 때(엔트리에서 시작하기 버튼을 누렀을 때)만 동작하게 되어있습니다. 물론 이정도만으로도 하드웨어 제어 코딩(하드웨어 코딩교육)을 배우는데 충분하다 할 수도 있고, 충분하지 않다 할 수도 있습니다. 왜냐하면 **실무/실전에서는 코딩한 코드를 하드웨어의 플래시 메모리(Flash Memory)\*에 저장(upload)시키고, 하드웨어는 전원만 연결되면 플래시 메모리\*에 저장된 코드(다른 말로 펌웨어\*)에 기반에 정해진 동작을 무한 반복하는 식으로 동작**하기 때문입니다. circle-info **플래시 메모리(Flash Memory)**: 우리가 늘 사용하는 USB 플래시 메모리를 생각하면 젤 이해가 빠르다. 전기적으로 데이터를 지우고 다시 기록할 수 있는 영구저장 메모리(기억장치)로서 저장 후 전원을 끄면 안에 내용이 자동으로 사라져 버리는 휘발성 메모리이가 아닌 씌여진 데이터가 영구적으로 저장되는 기억장치를 말한다. 참고로 아두이노 우노(Uno)에는 32KBytes 크기의 플래시 메모리가 존재한다. circle-info **펌웨어(Firmware)**: 소프트웨어(Software)의 일종으로 하드웨어 제어목적으로 제작되어 하드웨어 안에 내장시키는 소프트웨어(프로그램)를 말함. 그 이름에서 알 수 있듯이, 고정화된(Firm) 소프트웨어로 표현하는 것은 일반 컴퓨터 상에 소프트웨어처럼 일반 사용자가 설치와 지우기를 자유롭게 반복할 수 있다기 보다 하드웨어 장치에 한 번 내장시켜 놓으면 마치 하드웨어 영구적인 장치의 일부처럼 동작하기 때문이다. [**엔트리 커스텀 버전**arrow-up-right](https://github.com/JeongJun-Lee/entry-offline) **에는 기존 엔트리 정식 버전에는 없었던 하드웨어에 코드 저장시키기(업로드) 버튼이 존재합니다.** 이제 우리는 이 버튼을 눌러 내가 블록코딩한 코드(나의 펌웨어)를 하드웨어 플래시 메모리에 저장시킬 수 있게 되었습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb1IMzXdg45pw0dMs34%252F-Mb1J_aVOa2dS_Q8o_sK%252Fupload.png%3Falt%3Dmedia%26token%3Dd51f616f-ae4a-4481-a75d-0fc1d5afc5b4&width=768&dpr=3&quality=100&sign=2599d087&sv=2) 이제 우리도 저 업로드 버튼을 눌러 내 코드를 아두이노 하드웨어의 플래시 메모리로 업로드 시켜보겠습니다. 내 코드를 펌웨어로 만들고 하드웨어에 저장시키는 것까지의 과정은 실제로는 그리 간단하진 않고 내부적으로 많은 과정을 거치게 되는데 따라서 엔트리가 실행되는 컴퓨터의 성능에 따라 업로드가 완료되는데까지 걸리는 시간은 각자 다를 수 있음에 유의합니다. 업로드가 잘 되었고, 이제 엔트리 없이도 하드웨어 스스로 잘 동작하나요? **내 코드를 업로드를 한다는 것은 최초 엔트리와 아두이노 연결을 위해 사용했던 기존 펌웨어를 지우고, 대신 그 위에 내 펌웨어가 덮어 씌여지기(overwrite) 때문에**, 아래처럼 기존의 엔트리 연결 프로그램 상에 하드웨어 연결이 끊어지는 것은 자연스러운 현상입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb1OTAom6USAcxmHyCy%252F-Mb1OtupUR9tMAASh1C1%252Fimage.png%3Falt%3Dmedia%26token%3Dc8c0c646-cdfc-46a0-94d6-f9d0a3f08e32&width=768&dpr=3&quality=100&sign=f12b0257&sv=2) [Previous3.4 Hello World 코딩chevron-left](https://jjlee.gitbook.io/entry-arduino/coding_start/hello-world) [Next3.6 블록코딩을 텍스트 코딩으로 변환하기chevron-right](https://jjlee.gitbook.io/entry-arduino/coding_start/code_transform) Last updated 2 years ago --- # 3.3 디지털 신호와 아날로그 신호 | 엔트리-아두이노 이제 디지털(Digital)은 우리에게 너무 익숙한 단어가 되어 버렸고, 0(다른 말로 LOW) 또는 1(다른 말로 HIGH, 또는 1byte=8bits 즉, 282^828으로 표현할 수 있는 최대값 255) 의 단, 2가지의 값으로만 표현되는 신호를 의미합니다. 다시 말해 0 과 1 사이의 0.1, 0.5, 0.09 등의 사이 값은 존재하지 않는다는 것입니다. ### [hashtag](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog#digital-output) 디지털 출력 (Digital OUTPUT) 아두이노에서 디지털은 동작전압(Voltage)으로 표현하는데 0v(다른 말로 LOW) 와 5v(다른 말로 HIGH)로 표현합다. **0값을 표현하고 싶으면 0v 즉 전압출력을 안하면 되는 것이고, 1값을 표현하고 싶으면 5v를 외부로 출력하면 되는 것입니다.** ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb1of6WFZNT4d2b_j8a%252F-Mb47zgCuc5ZK6bXE5U4%252Fimage.png%3Falt%3Dmedia%26token%3D347b2cf8-29d1-4f78-affe-cdbe1b6fd91e&width=768&dpr=3&quality=100&sign=788734b1&sv=2) 참고로 **엔트리 블록에서 이를 끄기(0v) / 켜기(5v) 라는 표현을 사용**합니다. 따라서, 아래 코드는 디지털 2번 핀\*에 5v(켜기)를 출력하는 것입니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb0utTyZXFjdUTRELEI%252F-Mb0v1tywqzdnXVXTr16%252Fimage.png%3Falt%3Dmedia%26token%3D0300e0ed-34e7-4396-b7c2-a7c662a3dab3&width=768&dpr=3&quality=100&sign=dde00ea&sv=2) circle-info **핀(Pin) 또는 포트(Port)**: 보드에서 디지털/아날로그 신호를 내보내거나/받아드리거나 하는 다른 주변장치와의 소통을 위한 입출력 통로를 의미한다. 다음 장의 아두이노 보드의 기본에서 아두이노는 어떤 종류의 핀을 몇 개 갖고 있는지 자세히 설명한다. ### [hashtag](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog#analog-output) 아날로그 출력 (Analog OUTPUT) 아날로그(Analog)는 연속적인 값. 즉 디지털 처럼 딱 2가지 값만 있는게 아니라, 특정 범위에 있어 모든 사이값이 존재한다고 할 수 있습니다. 그렇다고 그 수많은(거의 무한대의) 연속적인 값을 다 사용할 수 있는가? 실제로는 그렇지는 않고, 이도 디지털화해서 사용하는데 예를 들어 **아두이노에서는 아날로그 값을 외부로 출력할 때는 0v(0) ~ 5v(255)를 256 단계(왜 256인지 하단에서 설명)의 값으로 표현해 사용합니다.** 엔트리의 블록에서는 아래와 같은 블록으로 표현되고, 블록에 아날로그 핀이라고 쓰지 않고, 디지털 핀이라고 표현했는데 이는 좀전에 언급된 내부적으로 아날로그의 디지털화된 방식으로 적용하기 때문에 그래서 결국 이도 최종적으로는 디지털이라 이렇게 표현한 것이라 할 수 있습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Mb1ReMggv9-sY9ChV_K%252F-Mb1T2Le1YMEsD7gLeZq%252Fimage.png%3Falt%3Dmedia%26token%3D754cc3a3-4842-4de0-9ca6-8d8c4dd56a54&width=768&dpr=3&quality=100&sign=e24a67&sv=2) 그러나, 이 부분은 초보자 입장에서는 위에 언급된 내용(아날로그의 디지털화)을 잘 모를 때는 블록에 기술된 디지털 핀이란 표현이 헷갈려 잘못 코딩하는 실수가 나오기 때문에 엔트리 정식버전이 왜 이렇게 표현했는지는 이해는 되나 저자가 이 블록을 우즈베키스탄어로 번역할 때는 사용자가 헷갈리지 않도록 의도적으로 아두이노 보드상에 적혀있는 PWM(Pulse Width Modulation) 핀이라고 번역했음을 첨언으로 남겨둡니다. **PWM은 아날로그를 디지털화하는 방법의 일종으로 이름 그대로 펄스 폭을(Pulse Width)를 이용해 변조(Modulation)하는 방법으로 아두이노 우노에서는 1bytes=8bits, 2진수로** 282^828**\=256개의 값으로 표현**하고 있습니다. PWM 변조방식을 자세히 설명드릴 수 있으나, 본 서는 최대한 쉽게 하드웨어 코딩교육을 하는 것을 목표하고 있어, 자세한 설명은 생략하기로 하고, 그럼에도 PWM에 대해 자세히 이해하고 싶으신 분들을 위해 에니메이션으로 쉽게 설명한 [유투브 링크arrow-up-right](https://www.youtube.com/watch?v=yhpk4V9w-ZM) 를 남기오니, 이를 확인하시어 추가로 이해하시면 되겠습니다. [Previous3.2 아두이노 보드와 연결하기chevron-left](https://jjlee.gitbook.io/entry-arduino/coding_start/hw_connect) [Next3.4 Hello World 코딩chevron-right](https://jjlee.gitbook.io/entry-arduino/coding_start/hello-world) Last updated 2 years ago --- # 3.2 아두이노 보드와 연결하기 | 엔트리-아두이노 **본 서는 엔트리에 아두이노 코드 변환/에디터/업로드를 구현한** [**커스텀 오프라인 버전**arrow-up-right](https://github.com/JeongJun-Lee/entry-offline/releases/) **을 기준으로 설명합니다.** 참고로 하드웨어와의 연결설정 메뉴얼은 엔트리 오프라인 버전의 도움말 두번째 메뉴에서도 확인할 수 있습니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MayyHSRhq8mHG--IYti%252F-Mayzv9Vd1I_RlKZ8ZkZ%252Fimage.png%3Falt%3Dmedia%26token%3D4e98719f-76d7-4007-9b62-2c9c9ec1bd1d&width=768&dpr=3&quality=100&sign=e9a7523f&sv=2) 1. \[하드웨어 탭\]의 \[연결 프로그램 열기\]를 눌러 하드웨어 연결 프로그램을 엽니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MaoCrU7aM6SgouXppME%252Fuploads%252FBPR6fftDvAUqg9DA5Rgl%252Fhw_connection.png%3Falt%3Dmedia%26token%3D5a77302d-89ca-47df-9254-e8ea54991dbd&width=768&dpr=3&quality=100&sign=2b856d0a&sv=2) 2\. **본 서에서는 블록코딩시 아두이노의 가장 많은 기능을 활용할 수 있는 \[아두이노 Uno 확장모드\]를 사용해 주시기 바랍니다.** 따라서, \[아두이노 Uno 확장모드\]를 선택합니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MaoCrU7aM6SgouXppME%252Fuploads%252FZDiHNJGBDVkrtKUfoaHd%252Fhw_connection2.png%3Falt%3Dmedia%26token%3Ddcfa7420-148a-479e-ba8e-3cdf0abbeef8&width=768&dpr=3&quality=100&sign=f33da684&sv=2) 3\. **윈도우즈(Windows) 운영체제 사용자로서 처음 아두이노를 연결하는 경우에 한해 최초 1회 드라이버 설치가 필요합니다. 드라이버 설치 후 아두이노 연결을 위한 펌웨어는 항상 자동 설치되므로 별도로 설치 버튼을 누를 필요는 없습니다.** ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-MayyHSRhq8mHG--IYti%252F-Maz2CgF31ZUtCoypWq5%252Fimage.png%3Falt%3Dmedia%26token%3D807241bd-7440-477d-9864-065052bfc5ce&width=768&dpr=3&quality=100&sign=c603d952&sv=2) 4\. 하드웨어 > 연결중이 연결성공으로 바뀌면 창을 닫지 않고 최소화 또는 그대로 두어 연결을 유지합니다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-legacy-files%2Fo%2Fassets%252F-MaoCrU7aM6SgouXppME%252F-Maz2kWb7muHcKrVVBIy%252F-Maz3L6gkSHGus0XhBeW%252Fimage.png%3Falt%3Dmedia%26token%3D57106ce9-91ab-4021-847c-d5d5b2c7aa07&width=768&dpr=3&quality=100&sign=42e5019d&sv=2) 5\. 이제 연결이 완료되었고, **아두이노 코딩에 사용할 수 있는 아두이노 전용 블록 12개가 보이실 겁니다.** 자 이제 이 블록들을 이용해 코딩을 시작해 볼까요? 아, 그 전에 다음 과에서 아주 기본적인 전자회로 관련 지식 몇 가지와 실습하고자 하는 아두이노 보드의 특징 몇 가지를 알아봅시다. ![](https://jjlee.gitbook.io/entry-arduino/~gitbook/image?url=https%3A%2F%2F1238842542-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F-MaoCrU7aM6SgouXppME%252Fuploads%252FHu9Qhn0E0ETjHb3WEHAR%252Fblocks.png%3Falt%3Dmedia%26token%3Dc901d112-de93-46ef-82dc-b8469a3bb353&width=768&dpr=3&quality=100&sign=444a70cd&sv=2) [Previous3.1 아두이노 블록코딩의 한계chevron-left](https://jjlee.gitbook.io/entry-arduino/coding_start/block_limitation) [Next3.3 디지털 신호와 아날로그 신호chevron-right](https://jjlee.gitbook.io/entry-arduino/coding_start/digital_analog) Last updated 2 years ago ---