파이썬 Kivy 프레임워크로 만드는 Pong Game 이다.
Kivy Documentation 2.0.0rc4 의 Tutorial 을 참고해서 작성한다.
Documentation 에 따르면 오픈소스 프로젝트인 Kivy Project 의 철학은 다음과 같다.
- Fresh, Fast, Flexible, Focused, Funded, Free
Kivy는 파이썬 언어와 C언어를 적절하게 사용해서 파이썬 언어 개발의 편의성과 하드웨어 레벨의 속도까지 잡으려고 만든 언어이다. Kivy 프레임워크를 개발하기 위해서 kv language (Kivy 언어) 라는 별도의 언어 형식까지 만들었을 정도로 스케일이 큰 롱테일 프로젝트다. 게다가 PyQt와 같은 GUI 프레임워크와 달리 완전한 무료이다. 터치패드 아이폰 개발까지 지원하는데 이런 출중한 파워에도 불구하고 현재 사용자가 많지는 않다. 취지는 훌륭한데 아직까지는 약간 매니아들의 성지같은 느낌이 드는 것은 어쩔 수 없다.
상업용 모바일 앱의 개발은 자바나 코틀린, 스위프트 같이 모바일 환경을 의식하여 개발된 언어가 확고하다. 파이썬이 최근에 흐름을 타고 있지만 이것도 개발된지는 꽤 오래되었다. 90년대에 개발되었으니 모바일 환경은 염두해 두고 설계한 언어는 아니다. 초보자 친화적인 문법과 다양한 모듈로 생명력을 이어나가고 있는 것은 현재의 하드웨어의 발전이 없었다면 어려웠을 것이다. 파이썬 언어의 아키텍쳐가 원래 느리기 때문이다. (C언어에 가상머신을 한대 더 쓰는 것이니까)
현재는 개량을 많이 했다고 하지만 느리다는 것은 체감적인 문제로 보는 것 같다. 비슷하게 동적 타입언어인 자바스크립트는 클라이언트(웹브라우저)에서 주로 사용되니까 파이썬 처럼 대용량으로 연산하는 일이 별로 없다. 그러다 보니 좀 느려도 체감이 안되는데 파이썬이 다루는 분야인 대용량 데이터 처리나 그 밖에 윈도우 프로그램을 대체하는 수준의 것들은 바로 체감이 된다.
파이썬이 나쁜 것은 아니다. 단지 사람들이 쉽고 기능이 다양하니까 수십년에 걸쳐서 조금씩 파이썬을 쓰기 시작한 건데 커뮤티티가 커지고 돈이 되니까 이제 성능까지 요구하고 나선 것이다. 약간 슬픈 이야기지만 파이썬의 창시자 귀도 반 로썸이 젊은 시절 크리스마스때 할일이 없어서 C언어로 파이썬을 만들었다고 한다;;;
홀로 크리스마스를 보내던 솔로 남자가 시작했던 일에 사람들이 너무 많은 것을 바라는 것일지도;;; ㅠㅠ
- 지금 그는 잘 살고 있다 최근 2020년11월에 MS에 입사하였다 -
Kivy 개발환경은 자바나, 스위프트 같은 모바일 주류의 개발환경처럼 그렇게 세련되지 않았지만 한번쯤 테스트 해볼 만한 가치가 있다.
윈도우와 Open GS ES환경을 자동으로 초기화 하여 바로 시작할 수 있게 하는 점과 kv 언어로 클래스를 만들어 사용하는 것은 빠르고 효율적인 개발을 가능하게 한다. 다만 기존의 웹, 앱의 프로그래밍 방식들과 차이가 있는 것 같다. kv 언어라는게 약간 HTML 같기도 하고 XML 같기도 하고 클래스 같기도 하고 애매하다. 능숙해진다면 이점이 있을 것 같다.
아마 기존 프로그래머들이 적응하기 어렵다는 부분도 kivy의 낮은 보급의 한 가지 이유가 아닐까 싶다. 같은 SDL2를 사용하는 Pygame은 커뮤니티가 크다. kivy 처럼 Pygame도 2000년대 초반에 개발이 시작된 라이브러리다.
kivy가 C언어를 사용해서 성능상으로는 파이게임보다 낫다는 식의 설명이 있는데 Kivy로 상업용 게임을 제작하는 사람이 얼마나 있을지 모르겠다.
어쨋든 흥미로운 프레임워크라서 좀 더 체험을 해보기로 한다. 간단한 퐁게임을 만들어 본다.
키비의 장점이자 단점일 수도 있는데 kv 언어라는 것을 배워야 한다. 아래 코드는 kv 언어로 만든 클래스들이다. 보면 약간 HMTL/CSS 같기도 하고 적응이 안된다. 내용을 보면 어렵지않게 내용의 추측이 가능하다. 클래스 -> 속성, 함수 이렇게 되어 있는데 적응하려면 시간이 걸릴 듯 하다.
퐁게임에 필요한 볼, 패들(라켓), 게임 클래스를 정의한 것이다. 이것의 로직은 main.py 에서 구현한다.
#:kivy 2.0.0
<PongBall>:
size: 25, 25
canvas:
Color:
rgb: 1, 1, 0
Ellipse:
pos: self.pos
size: self.size
<PongPaddle>:
size: 25, 150
canvas:
Color:
rgb: 0, 1, 1
Rectangle:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
player1: player_left
player2: player_right
canvas:
Color:
rgb: 0, 0, 1
Rectangle:
pos: self.center_x - 2, 0
size: 4, self.height
Label:
font_size:55
center_x: root.width / 4
top: root.top - 20
text: str(root.player1.score)
Label:
font_size:55
center_x: root.width * 3 / 4
top: root.top - 20
text: str(root.player2.score)
PongBall:
id: pong_ball
center: self.parent.center
PongPaddle:
id: player_left
x: root.x
center_y: root.center_y
PongPaddle:
id: player_right
x: root.width - self.width
center_y: root.center_y
* 아래는 kv언어로 만든 클래스를 가지고 게임을 구현한 코드이다. 확실히 문법이 다르다. 정수 하나를 사용하기 위해서 NumericProperty라는 별도의 클래스(Wrapper)를 사용해야 하고 객체를 만들려면 ObjectProperty (최상위 객체형)를 사용해야 한다는 점이 의문을 갖게 한다. 이것이 정말 코드를 쉽게 만든 것인가? 아니면 초급자들에게 장벽이 높은 것일까?
* Kivy는 이벤트 루프 중심의 프레임워크이다. 콜백을 하는 JavaScript와 비슷하다
Clock.schedule_interval(game.update, 1.0/100.0) 이 그것인데 100분의 1초 마다 game.update를 콜백하라. JavaScript의 set_interval 과 유사하다는 것을 알 수 있다. kivy는 단위는 1초 단위로 한다.
퐁게임의 로직은 간단하다. 게임이 작동되는 박스를 계산하고 상대방 골대는 x가 width 일 때 내 골대는 x가 0일때로 맞춰놓고 코딩을 하면 된다. 공의 움직임을 추적하기 위해 2D 벡터를 사용하는데 퐁게임은 단순히 공에 대는 것을 목표로 한다. 야구 방망이로 휘두르는 것 처럼 물리식을 적용하면 게임은 더 재미있겠지만 클래식 퐁에는 그런 기능은 없다.
import kivy
kivy.require('2.0.0')
from kivy import Config
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import (
NumericProperty, ReferenceListProperty, ObjectProperty
)
from kivy.vector import Vector
from kivy.clock import Clock
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class PongPaddle(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
vel_x, vel_y = ball.velocity
offset = (ball.center_y - self.center_y) / (self.height/2)
bounced = Vector(-1 * vel_x, vel_y + offset)
vel = bounced
ball.velocity = vel.x, vel.y
class PongGame(Widget):
ball = ObjectProperty(None)
player1 = ObjectProperty(None)
player2 = ObjectProperty(None)
def serve_ball(self, vel=(5, 0)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
self.player1.bounce_ball(self.ball)
self.player2.bounce_ball(self.ball)
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.velocity_y *= -(1)
print(self.ball.velocity)
if (self.ball.x < 0) or (self.ball.right > self.width):
self.ball.velocity_x *= -(1)
print(self.ball.velocity)
if self.ball.x < self.x:
self.player2.score += 1
self.serve_ball(vel=(5,0))
if self.ball.x > self.width-25:
self.player1.score += 1
self.serve_ball(vel=(-5,0))
def on_touch_move(self, touch):
if touch.x < self.width/5:
self.player1.center_y = touch.y
if touch.x > self.width - self.width / 5:
self.player2.center_y = touch.y
class MyPongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0/100.0)
return game
if __name__ == '__main__':
Config.set('graphics', 'width', '720')
Config.set('graphics', 'height', '540')
MyPongApp().run()
아직 Kivy의 다양한 면에 대해서 알아가고 있는 중이다. 프레임워크의 기능이 많아지면 좀 오류도 많아지고 집중도가 떨어지는 부분도 있는데 Kivy 개발자들은 열정이 넘치는 것 같다. 스스로들 Kivy를 파이썬 언어로 개발하면서 많은 것을 배웠다고 하고 Kivy의 소스코드를 읽어보기를 권장한다.
어쩌면 Kivy를 통해 그들이 바라는 것은 많은 사용자들이 사용해서 상업적으로 성공하기를 바란게 아니라 기술의 진보 그 자체가 아니었을까라는 생각도 해본다. tkinter나 Pygame은 파이썬이 취할 수 있는 가장 원초적인 방식의 프로그래밍이 가능하다. print("Hello") 처럼 게임창을 키고 끄는 것이다. 다른 복잡함을 생각하지 않아도 된다. 조금 느려도 괜찮다. 하드웨어 성능이 계속 좋아지고 있으니까 그 문제들은 해결된다.
아직 어떤 평가를 하기는 힘드니 좀 더 사용하면서 파악을 해봐야겠다.