오픈 소스 도구를 사용하면 누구나 쉽고 재미있는 방법으로 Python을 배우는 데 도움이 됩니다.
파이썬은 훌륭한 초보자 프로그래밍 언어로 명성을 얻었습니다. 그러나 어디에서 시작합니까?
사람들이 프로그래밍에 관심을 갖도록 하는 가장 좋아하는 방법 중 하나는 게임을 작성하는 것입니다.
PursuedPyBear (ppb)는 교육에 최적화 된 게임 프로그래밍 라이브러리이며, 최근에 아이들이 좋아하는 프로그래밍 언어에 대해 더 많이 가르치기 위해 이 라이브러리를 사용했습니다.
Jupyter 프로젝트는 데이터 과학자들이 데이터를 가지고 놀 수 있도록 설계된 브라우저 기반 Python 콘솔입니다.
여기에서 다운로드 할 수 있는 간단한 대화 형 게임을 만드는 방법을 알려주는 Jupyter Notebook이 있습니다. 파일을 열려면 최신 Jupyter 프로젝트 인 JupyterLab을 설치해야 합니다.
전제 조건 :
필요한 라이브러리를 위한 별도의 공간을 만들도록 가상 환경을 간단히 구성합니다. (여기서 가상 환경의 작동 방식에 대해 자세히 알아볼 수 있습니다.)
$ git clone https://github.com/moshez/penguin-bit-by-bit.git
$ cd penguin-bit-by-bit
$ python -m venv venv
$ source ./venv/bin/activate
$ pip install -r requirements.txt
$ jupyter lab .
마지막 명령은 기본 브라우저의 주소 Juhttp : // localhost : 8888 / lab에서 JupyterLab을 열어야 합니다. 왼쪽 열에서 dynamic_penguin.ipynb 파일을 선택하면 시작할 수 있습니다!
게임을 실행할 이벤트 루프
Jupyter는 내부적으로 이벤트 루프를 실행하며, 이는 추가 비동기 작업의 실행을 관리하는 프로세스입니다. Jupyter에서 사용되는 이벤트 루프는 asyncio이며 PursuedPyBear는 자체 이벤트 루프를 실행합니다.
접착제처럼 꼬인 다른 라이브러리를 사용하여 둘을 통합 할 수 있습니다. 복잡하게 들리지만 고맙게도 그 복잡성은 라이브러리 뒤에 숨겨져 있어 모든 노력을 다할 것입니다.
Jupyter의 다음 셀은 전반부를 처리하며 Twisted를 asyncio 이벤트 루프와 통합합니다.
__file__ = NoneB PursuedPyBear를 Jupyter와 통합하는 데 필요합니다.
from twisted.internet import asyncioreactor
asyncioreactor.install()
__file__ = None
다음으로 "setup"기능이 필요합니다. 설정 기능은 주요 게임 요소를 구성하는 일반적인 용어입니다.
그러나 우리의 함수는 게임 "장면"을 전역 변수에만 넣을 것입니다. 우리가 게임을 할 테이블을 정의하는 것처럼 생각하십시오.
Jupyter Notebook의 다음 셀이 트릭을 수행합니다.
def setup(scene):
global SCENE
SCENE = scene
이제 PursuedPyBear의 이벤트 루프를 Twisted와 통합해야 합니다. 이를 위해 txtxb 모듈을 사용합니다 :
import txppb
d = txppb.run(setup)
d.addBoth(print)
마지막에 "print"는 버그로 인해 게임이 충돌하는 경우 도움이 됩니다. Jupyter 출력에 대한 역 추적을 인쇄합니다.
게임 요소에 대한 준비가 된 빈 창이 나타납니다.
여기서 Jupyter를 활용하기 시작합니다. 전통적으로 게임을 시작하기 전에 전체 게임을 작성해야 합니다. 그러나 컨벤션을 시작하고 즉시 게임을 시작합니다!
상호 작용으로 게임을 재미있게 만들기
그러나 매우 재미있는 게임은 아닙니다. 그것은 아무것도 없으며 단지 거기에 앉아 있습니다. 우리가 무언가를 원한다면 그것을 추가하는 것이 좋습니다.
비디오 게임 프로그래밍에서 화면에서 움직이는 것을 "스프라이트"라고 합니다. PursuedPyBear에서 스프라이트는 클래스로 표시됩니다. 스프라이트는 클래스와 같은 이름의 이미지를 자동으로 사용합니다. 무료 및 오픈 소스 비디오 게임 자산 모음 인 Kenney에서 작은 펭귄 이미지를 얻었습니다.
import ppb
class Penguin(ppb.Sprite):
pass
이제 펭귄 riiiiiight를 가운데에 두겠습니다.
SCENE.add(Penguin(pos=(0,0)))
조심스럽게 가운데에 앉아 있습니다. 아무것도 없는 것보다 조금 흥미롭습니다. 좋습니다. 이것이 바로 우리가 원하는 것입니다. 점진적인 게임 개발에서는 모든 단계가 조금 더 재미 있어야 합니다.
ppb로 펭귄 게임에 움직임 추가
그러나 펭귄은 아직 앉아 있지 않습니다! 펭귄이 움직여야 합니다. 플레이어가 화살표 키로 펭귄을 조종하게 할 것입니다. 먼저 키를 벡터에 매핑 해 봅시다 :
from ppb import keycodes
DIRECTIONS = {keycodes.Left: ppb.Vector(-1,0), keycodes.Right: ppb.Vector(1,0),
keycodes.Up: ppb.Vector(0, 1), keycodes.Down: ppb.Vector(0, -1)}
이제 우리는 유틸리티 라이브러리를 사용할 것입니다. set_in_class 함수는 클래스에서 메소드를 설정합니다. 소급하여 클래스에 함수를 추가하는 Python의 기능은 실제로 유용합니다!
from mzutil import set_in_class
Penguin.direction = ppb.Vector(0, 0)
@set_in_class(Penguin)
def on_update(self, update_event, signal):
self.position += update_event.time_delta * self.direction
set_in_class의 코드는 길지 않지만 사소한 파이썬 트릭을 사용합니다. 우리는 전체 유틸리티 라이브러리를 검토를 위해 기사의 끝에 놓고 흐름을 위해 지금은 건너 뛸 것입니다.
펭귄 돌아 가기!
펭귄은 부지런히 움직이고 있습니다. 무슨 일이 일어나는지 수동으로 방향을 설정해 봅시다.
Penguin.direction = DIRECTIONS[keycodes.Up]/4
방향은 올라가지만 약간 느립니다. 이것은 펭귄의 방향을 수동으로 0으로 되돌릴 수 있는 충분한 시간을 줍니다. 지금 해보자!
Penguin.direction = ppb.Vector(0, 0)
펭귄 게임에 대화형 기능 추가
그것은 흥미로웠지만 우리가 원하는 것은 아닙니다. 우리는 펭귄이 키 누르기에 반응하기를 원합니다. 코드에서 이를 제어하는 것은 게이머가 "속임수"라고 하는 것입니다.
키 누름 방향을 설정하고 키를 놓으면 다시 0으로 설정합시다.
@set_in_class(Penguin)
def on_key_pressed(self, key_event, signal):
self.direction = DIRECTIONS.get(key_event.key, ppb.Vector(0, 0))
@set_in_class(Penguin)
def on_key_released(self, key_event, signal):
if key_event.key in DIRECTIONS:
self.direction = ppb.Vector(0, 0)
펭귄은 좀 지루하지 않습니까? 어쩌면 우리는 오렌지 공을 가지고 놀아야 할 것입니다.
class OrangeBall(ppb.Sprite):
pass
다시 한 번, orangeball.png라는 이미지가 있어야 합니다. 이제 공을 화면 왼쪽에 놓도록 하겠습니다.
SCENE.add(OrangeBall(pos=(-4, 0)))
펭귄은 공을 차지 못합니다. 펭귄이 접근 할 때 공이 펭귄에서 멀어 지도록 하자.
먼저, 공을 차는 것이 무엇을 의미하는지 정의 해 봅시다. 공을 차는 것은 1 초 안에 어디로 갈 것인지 결정한 다음 상태를 "이동"으로 설정하는 것을 의미합니다.
처음에는 첫 번째 업데이트로 대상 위치로 이동하여 이동 시킵니다.
OrangeBall.is_moving = False
@set_in_class(OrangeBall)
def kick(self, direction):
self.target_position = self.position + direction
self.original_position = self.position
self.time_passed = 0
self.is_moving = True
@set_in_class(OrangeBall)
def on_update(self, update_event, signal):
if self.is_moving:
self.position = self.target_position
self.is_moving = False
이제 킥하자!
ball, = SCENE.get(kind=OrangeBall)
ball.kick(ppb.Vector(1, 1))
그러나 이것은 공을 순간 이동 시킵니다. 즉시 위치를 변경합니다. 실제로 볼은 중간 지점 사이를 이동합니다. 움직일 때, 현재 위치와 필요한 위치를 보간 합니다.
순진하게 선형 보간법을 사용합니다. 그러나 멋진 비디오 게임 트릭은 "여유"기능을 사용하는 것입니다. 여기서는 일반적인 "부드러운 단계"를 사용합니다.
from mzutil import smooth_step
@set_in_class(OrangeBall)
def maybe_move(self, update_event, signal):
if not self.is_moving:
return False
self.time_passed += update_event.time_delta
if self.time_passed >= 1:
self.position = self.target_position
self.is_moving = False
return False
t = smooth_step(self.time_passed)
self.position = (1-t) * self.original_position + t * self.target_position
return True
OrangeBall.on_update = OrangeBall.maybe_move
이제 다시 걷어차 봅시다.
ball, = SCENE.get(kind=OrangeBall)
ball.kick(ppb.Vector(1, -1))
그러나 실제로 펭귄은 공을 차고 있어야 합니다. 공이 펭귄과 충돌하는 것을 볼 때 반대 방향으로 킥을 합니다. 펭귄이 바로 위에 올라간 경우, 공은 임의의 방향을 선택합니다.
업데이트 기능은 이제 maybe_move를 호출하고 지금 움직이지 않는 경우에만 충돌을 검사합니다.
from mzutil import collide
import random
OrangeBall.x_offset = OrangeBall.y_offset = 0.25
@set_in_class(OrangeBall)
def on_update(self, update_event,signal):
if self.maybe_move(update_event, signal):
return
penguin, = update_event.scene.get(kind=Penguin)
if not collide(penguin, self):
return
try:
direction = (self.position - penguin.position).normalize()
except ZeroDivisionError:
direction = ppb.Vector(random.uniform(-1, 1), random.uniform(-1, 1)).normalize()
self.kick(direction)
그러나 공을 차는 것은 그리 재미 있지 않습니다. 대상을 추가합시다.
class Target(ppb.Sprite):
pass
화면 오른쪽에 대상을 배치합시다.
SCENE.add(Target(pos=(4, 0)))
펭귄에게 보상
이제 우리는 펭귄이 공을 목표물로 차면 펭귄에 대한 보상을 원할 것입니다. 물고기 어때요?
class Fish(ppb.Sprite):
pass
대상이 공을 받으면 공을 제거하고 화면의 다른 쪽 끝에 새 공을 만들어야 합니다. 그러면 물고기가 나타납니다.
@set_in_class(Target)
def on_update(self, update_event, signal):
for ball in update_event.scene.get(kind=OrangeBall):
if not collide(ball, self):
continue
update_event.scene.remove(ball)
update_event.scene.add(OrangeBall(pos=(-4, random.uniform(-3, 3))))
update_event.scene.add(Fish(pos=(random.uniform(-4, -3),
random.uniform(-3, 3))))
우리는 펭귄이 물고기를 먹게 하고 싶습니다. 물고기가 펭귄을 볼 때 사라져야 합니다.
Fish.x_offset = 0.05
Fish.y_offset = 0.2
@set_in_class(Fish)
def on_update(self, update_event,signal):
penguin, = update_event.scene.get(kind=Penguin)
if collide(penguin, self):
update_event.scene.remove(self)
반복적인 게임 디자인은 펭귄과 사람들 모두에게 재미 있습니다!
플레이어가 통제하는 펭귄은 목표물에 공을 차고, 물고기를 잡고, 물고기를 먹고, 새로운 공을 차는 게임입니다. 이것은 게임의 "연삭 수준"으로 작용하거나, 펭귄의 삶을 더 어렵게 만드는 장애물을 추가 할 수 있습니다.
숙련 된 프로그래머이거나 시작하기에 관계없이 비디오 게임 프로그래밍은 재미 있습니다. Jupyter의 PursuedPyBear는 로고 및 스몰 토크와 같은 클래식 환경의 대화식 프로그래밍 기능을 통해 클래식 2D 게임의 모든 즐거움을 제공합니다. 복고풍 80 년대를 즐길 시간입니다!
부록
유틸리티 라이브러리의 전체 소스 코드는 다음과 같습니다. 게임 보드가 작동하도록 흥미로운 개념을 제공합니다. 그 방법에 대한 자세한 내용은 충돌 감지, setattr에 대해 읽으십시오. 그리고 __name__ 속성.
def set_in_class(klass):
def retval(func):
setattr(klass, func.__name__, func)
return func
return retval
def smooth_step(t):
return t * t * (3 - 2 * t)
_WHICH_OFFSET = dict(
top='y_offset',
bottom='y_offset',
left='x_offset',
right='x_offset'
)
_WHICH_SIGN = dict(top=1, bottom=-1, left=-1, right=1)
def _effective_side(sprite, direction):
return (getattr(sprite, direction) -
_WHICH_SIGN[direction] *
getattr(sprite, _WHICH_OFFSET[direction], 0))
def _extreme_side(sprite1, sprite2, direction):
sign = -_WHICH_SIGN[direction]
return sign * max(sign * _effective_side(sprite1, direction),
sign * _effective_side(sprite2, direction))
def collide(sprite1, sprite2):
return (_extreme_side(sprite1, sprite2, 'bottom') <
_extreme_side(sprite1, sprite2, 'top')
and
_extreme_side(sprite1, sprite2, 'left') <
_extreme_side(sprite1, sprite2, 'right'))
등록된 댓글이 없습니다.