파이썬으로 크리스마스 트리 조명 게임을 만들었습니다.

만든 것




※비디오는 2배속입니다.
전기 거리를 연결하고 크리스마스 트리 전구를 켜는 게임을 파이썬으로 만들었습니다. GUI는 Tkinter를 사용합니다.
전 자료는 몇 년 전에 NORAD의 산타 추적 웹사이트에 나와 있던 게임이다. 이것을 사랑하고 쭉 하고 있었던 것을 기억해, 최근 공부해 사용할 수 있게 된 Python으로 만들어 보기로 했습니다.

환경



파이썬 3.6.8 on linux
종속 패키지: PIL (Pillow 5.1.0)

소스 코드



GitHub에서 게시 중입니다. 이하에서는 포인트만 소개합니다.

UI 작성 포인트



이미지 표시



PNG 이미지를 표시하기 위해 PIL 패키지의 Image 모듈과 ImageTk 모듈을 사용합니다.
시작하기 전에 이미지를 Image.open()로 가져옵니다.resize() 왜냐하면 향후 라즈파이의 터치 디스플레이에서도 움직이고 싶기 때문에 표시 크기를 유연하게 바꿀 수 있기 때문입니다.

app_tk.py
IMG_STAR_OFF = Image.open('image/star_off.png').resize(STAR_SIZE, Image.BILINEAR)
IMG_STAR_ON = Image.open('image/star_on.png').resize(STAR_SIZE, Image.BILINEAR)

가져온 이미지는 Tkinter의 캔버스에 표시됩니다. 이 때 PhotoImage() 생성자를 사용합니다.
PhotoImage 객체는 유지해야 하는 것 같습니다. 로컬 변수를 사용하면 표시되지 않았습니다.create_image()의 반환 값은 canvas의 오브젝트 ID입니다. 게임 중에 이미지를 전환하거나 회전 애니메이션을 표시할 때 필요합니다.

app_tk.py
self.canvas = tk.Canvas(self, width=w, height=h)
self.star_img = ImageTk.PhotoImage(IMG_STAR_ON)
self.star_id = self.canvas.create_image(STAR_OFFSET_X,
                                        STAR_OFFSET_Y,
                                        image=self.star_img,
                                        anchor=tk.NW)

이미지를 변경하려면 새 PhotoImage를 만들고 canvas itemcomfigure()를 사용합니다.

app_tk.py
if tree.is_complete():
    self.star_img = ImageTk.PhotoImage(IMG_STAR_ON)
else:
    self.star_img = ImageTk.PhotoImage(IMG_STAR_OFF)
self.canvas.itemconfigure(self.star_id, image=self.star_img)

회전 애니메이션



Tkinter에는 애니메이션 메소드와 같은 편리한 것이 없기 때문에, 스스로 정기적으로 이미지를 갱신해 애니메이션을 실현합니다.
주기적으로 처리를 호출하려면 Tkinter widget 공통 메소드 after()를 사용할 수 있습니다.

app_tk.py
def rotate_cell(self, x, y):
    info = self.img_info[x][y]
    info.angle -= 15   # 15度回転
    info.photo_img = ImageTk.PhotoImage(info.img.rotate(info.angle))   # 回転したPhotoImage作成
    self.canvas.itemconfigure(info.id, image = info.photo_img)   # 画像を差し替え
    if info.angle % 90 == 0:
        # アニメーション完了
        # ゲームの状態更新処理など
    else:
        self.after(15, self.rotate_cell, x, y)   # 15ミリ秒後に再度画像を更新する

canvas에 widget 배치



canvas의 create_window() 메서드로 widget을 배치할 수 있습니다. 여러 widget을 함께 취급하고 싶다면 Frame을 사용합니다.

app_tk.py
frame = tk.Frame(self, bg='#e5f8cf', padx=5)
start = tk.Button(frame, text='Start', command=self.start_new_game,
                  fg='#345834', font=('', 22, 'bold'))
start.pack(side=tk.LEFT, padx=5, pady=10)
self.counter_text = tk.StringVar()
self.counter_text.set('00:00')
self.counter_label = tk.Label(frame, textvariable=self.counter_text,
                              bg='#e5f8cf', fg='#345834',
                              font=('', 22, 'bold'))
self.counter_label.pack(side=tk.LEFT, padx=5, pady=10)
self.canvas.create_window(20, 20, window=frame, anchor=tk.NW)

캔버스의 클릭 이벤트


bind()에서 콜백 메서드를 할당합니다. 콜백 메소드에는 인수로 이벤트가 건네받아, .x, .y로 클릭 이벤트가 발생한 좌표를 취득할 수 있습니다.

app_tk.py
self.canvas.bind('<ButtonRelease-1>', self.on_click_canvas)

def on_click_canvas(self, event):
    # event.x, event.yで座標を取得
    x = (event.x - TREE_OFFSET_X) // CELL_LENGTH
    y = (event.y - TREE_OFFSET_Y) // CELL_LENGTH
    # 処理

미로 알고리즘



전기의 거리는 나무의 뿌리를 시작으로 미로로 표현할 수 있습니다.
미로 작성의 알고리즘은 조사하면 여러 가지 있는 것입니다만, 가류로 만들었습니다. 간단히 쓰면 다음과 같은 알고리즘입니다.

방안지의 매스 눈을 연결하여 미로의 경로를 만들 것이라고 생각합니다.
  • 시작 매스를 사용됨으로 표시합니다.
  • 사용한 매스와 인접한 미사용 매스의 조합을 모두 나열합니다.
  • 리스트 중에서 랜덤하게 하나 선택해, 선택한 매스끼리를 통로로 연결해, 선택한 매스를 사용이 끝난 것으로 마크 합니다. .
  • 모든 매스가 사용이 끝날 때까지 2-3을 반복합니다.

  • 미로가 완성되어 가는 모습을 동영상으로 하면 이런 느낌입니다.





    크리스마스 같은 BGM을 흘리고 싶었고 파이썬에서 mp3 파일을 재생하는 방법을 살펴보면 파이게임이 있다는 것을 알았습니다. 화상의 표시라든지 당연히 할 수 있는 것 같기 때문에, 처음부터 이것을 사용하면 좋았을지도・・・

    좋은 웹페이지 즐겨찾기