Snake Game in Python (Part: 1)
ဒီ Tutorial လေးမှာတော့ Object-Oriented Programming concept တွေကို သုံးပြီး Classic Snake Game လေးကို ဖန်တီးမှာဖြစ်ပါတယ်။ Game library အနေနဲ့ Pygame ကို အသုံးပြုထားပါတယ်။ Beginner တွေအတွက် ရည်ရွယ်တာဖြစ်လို့ Pygame ကို တစ်ခါမှ မသုံးဖူးလဲ လိုက်ရေးလို့ ရပါတယ်။
What you'll learn
- OOP ကို လက်တွေ့ အသုံးပြုပုံ
- Pygame အခြေခံ အသုံးပြုပုံ
- User input လက်ခံခြင်း
- Grid System
- Scoring System
- Basic Game Structure
1. Setting Up Project
Project ကို လိုက်လုပ်နိုင်ဖို့ အရင်ဆုံး ကွန်ပျူတာထဲမှာ Pygame install လုပ်ပြီးသားဖြစ်ရပါမယ်။ မလုပ်ရသေးရင် Terminal ထဲမှာ pip install pygame လို့ ရေးပြီး install လုပ်လိုက်ပါ။ Install လုပ်တာ အဆင်မပြေရင်တော့ Installation Guide တစ်ခုလောက် ရှာကြည့်ပြီး လုပ်လိုက်ပါ။ Project ရေးဖို့အတွက် Python File အသစ်တစ်ခု ဖွင့်လိုက်ပါ။ Program ထဲမှာ Game, Snake, Apple ဆိုပြီး Class သုံးခု ရေးမှာ ဖြစ်ပါတယ်။ Program structure ကတော့ အောက်ပါအတိုင်း ဖြစ်ပါတယ်။
import pygame as pg
class Game:
pass
class Snake:
pass
class Apple:
pass
# Main Game Loop
def main():
pass
if __name__ == '__main__':
main()
2. Defining Game Class
Game class ထဲမှာ ပုံဆွဲတာ၊ user-input လက်ခံတာနဲ့ game logic တွေကို ရေးမှာ ဖြစ်ပါတယ်။ ဒါက အဓိက Class ပါ။ ဒီထဲကနေပဲ Snake Apple object တွေကို ထိန်းချုပ်မှာ ဖြစ်ပါတယ်။
import pygame as pg
WIDTH = 500
FPS = 60
class Game:
def __init__(self):
pg.init()
self.screen = pg.display.set_mode((WIDTH, WIDTH))
self.clock = pg.time.Clock()
pg.display.set_caption("Little Python")
def draw(self):
self.screen.fill('light green')
def update(self):
pg.display.update()
self.clock.tick(FPS)
Game class ရဲ့ init method ထဲမှာ ရေးထားတဲ့ ကုဒ်တွေက Pygame program တိုင်းမှာ အရင်ဆုံးထည့်ပေးရမယ့် ကုဒ်တွေ ဖြစ်ပါတယ်။ pg.init() က Pygame ကို initialize လုပ်တာဖြစ်ပါတယ်။ pg.display.set_mode() function က ပုံဆွဲလို့ရတဲ့ မျက်နှာပြင်တစ်ခုထုတ်ပေးပါတယ်။ သူ့အထဲက ကိုယ်ဆွဲချင်တဲ့ မျက်နှာပြင် size ထည့်ပေးရပါတယ်။ ဒီမျက်နှာပြင်ကို self.screen ဆိုတဲ့ variable ထဲမှာ သိမ်းထားလိုက်ပါတယ်။ ဂိမ်းထဲမှာ ပုံဆွဲတဲ့ အခါ ဒီမျက်နှာပြင်ပေါ် ဆွဲမှာဖြစ်ပါတယ်။ self.clock ကတော့ Frame rate ကို ထိန်းဖို့အတွက်ပါ။ ဂိမ်းအတွက် သိပ်အရေးမကြီးပါဘူး။ pg.set_caption() ထဲကို ဂိမ်းရဲ့ Title ကို ထည့်ပေးလိုက်ပါ။
ပုံတွေဆွဲတဲ့ အခါ draw() method ထဲကနေ ဆွဲမှာဖြစ်ပါတယ်။ fill() ဆိုတာ Pygame မှာ ပါတဲ့ method တစ်ခုဖြစ်ပါတယ်။ မျက်နှာပြင်တစ်ခုလုံးကို အရောင်ပြောင်းပေးတာပါ။ အခုလောလောဆယ်တော့ ဘာပုံမှ မဆွဲသေးပါဘူး။ ရိုးရိုးမျက်နှာပြင်တစ်ခုပဲ ဆွဲကြည့်ပါမယ်။ self.screen.fill() ထဲမှာ Argument အနေနဲ့ အရောင်နာမည်တွေကို String နဲ့ ထည့်လို့ရသလို Color code တွေထည့်လဲ ရပါတယ်။
update() method ကိုတော့ Game loop တိုင်းမှာ ခေါ်မှာ ဖြစ်ပါတယ်။ အထဲမှာရေးထားတာက Pygame ရဲ့ Screen update လုပ်တဲ့ Function တွေပါ။ self.clock.tick() ထဲကို Frame rate (FPS) ထည့်ပေးရပါတယ်။ Window width, Frame rate စတာတွေက Program ထဲမှာ မပြောင်းလဲမယ့် Constant တွေဖြစ်လို့ အပေါ်ဆုံးမှာ တစ်ခါတည်း သတ်မှတ်ထားပါတယ်။
3. Running Game Loop
အဓိက Game loop အတွက် ကုဒ်တွေကို main() function ထဲမှာ ရေးမှာဖြစ်ပါတယ်။ ဂိမ်းကို run တဲ့ အခါ ဒီ function ခေါ်လိုက်ရုံပါပဲ။ အပေါ်မှာ define လုပ်ခဲ့တဲ့ Game class နဲ့ Game object တစ်ခုခေါ်ပြီး သုံးရပါမယ်။
# Main Game Loop
def main():
game = Game()
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
game.draw()
game.update()
pg.quit()
if __name__ == '__main__':
main()
while loop က အဓိက Game loop ဖြစ်ပါတယ်။ ဒီထဲကထွက်တာနဲ့ pg.quit() ကိုခေါ်ပြီး Game ကို ပိတ်လိုက်မှာပါ။ for loop ကတော့ Window ကနေ ထွက်၊ မထွက်စစ်တာပါ။ Pygame မှာ User-input တစ်ခုစီကို Event လို့ ခေါ်ပါတယ်။ Exit button ကို နှိပ်လိုက်ရင် Quit event ပေါ်လာပြီး Game loop ထဲကနေ ထွက်သွားမှာ ဖြစ်ပါတယ်။
Loop ထဲမှာ window မပိတ်မချင်း draw နဲ့ update ကိုသုံးပြီး Game window ဆွဲပေးနေမှာပါ။ Program ကို run လိုက်ရင် Window အလွတ်တစ်ခုပေါ်လာမှာ ဖြစ်ပါတယ်။ ဒီအဆင့်ထိ ရေးခဲ့တဲ့ ကုဒ်တွေ အကုန်လုံးက Pygame သုံးတဲ့ အခါ အမြဲတမ်း ရေးရမယ့် ကုဒ်တွေပဲ ဖြစ်ပါတယ်။
4. Grid System
Snake game ထဲမှာ Grid system ကို သုံးမှာ ဖြစ်ပါတယ်။ Grid system ဆိုတာ Game window ကို ဇယားကွက်တွေချထားပြီး ပုံတွေ၊ Animation တွေကို ဇယားကွက်တစ်ကွက်စီထဲမှာ ဆွဲမှာ ဖြစ်ပါတယ်။ GRID=20 ဆိုပြီး အပေါ်ဆုံးမှာ သတ်မှတ်လိုက်ပါတယ်။ Window ရဲ့ အလျား၊ အနံကို အကွက် နှစ်ဆယ်စီ ပိုင်းမယ်လို့ အဓိပ္ပါယ် ရပါတယ်။ ကိုယ်ကြိုက်ရာ တန်ဖိုးထားနိုင်ပါတယ်။ တန်ဖိုးများလေ ဇယားကွက် အရွယ်အစား ငယ်လေဖြစ်မှာပါ။ အကွက်တစ်ကွက်စီရဲ့ Size က WIDTH / GRID ဖြစ်ပါတယ်။ ဒါကို CELLSIZE ဆိုတဲ့ variable ထဲမှာ သိမ်းထားလိုက်ပါ။ ဒီနှစ်ခု ရေးပြီးရင် ပိုပြီး နားလည်အောင် Grid ဆွဲကြည့်ပါမယ်။ အောက်က ကုဒ်ကို draw() ထဲမှာ ရေးပြီး run လိုက်ပါ။ ပုံမှာ ပြထားသလို Grid တစ်ခု ပေါ်လာပါလိမ့်မယ်။
GRID = 20
CELLSIZE = WIDTH / GRID
class Game:
...
def draw(self):
...
[pg.draw.rect(self.screen, 'black', (x*CELLSIZE, y*CELLSIZE, CELLSIZE, CELLSIZE), 1) for y in range(GRID) for x in range(GRID)]
pg.draw.rect() function ထဲကို ပုံဆွဲမယ့် မျက်နှာပြင်၊ အရောင်နဲ့ rectangle တစ်ခုထည့်ပေးရပါတယ်။
Rectangle တစ်ခုမှာ တန်ဖိုးလေးခု ပါပါတယ်။
- ဘယ်ဘက်အနားရဲ့ X-coordinate (pixels)
- ဘယ်ဘက်ထိပ်ဆုံးထောင့်ရဲ့ Y-coordinate (pixels)
- အကျယ်
- အမြင့် ဒီတန်ဖိုးတွေကို Tuple နဲ့ ထည့်ပေးလိုက်ရင် Rectangle object တစ်ခု ရပါတယ်။
Pygame window ရဲ့ ဘယ်ဘက်အပေါ်ထောင့်မှာ x, y က 0, 0 ဖြစ်ပါတယ်။ ညာဘက်ကို သွားလေ x တန်ဖိုးတိုးလေဖြစ်ပြီး အောက်ကို ဆင်းလေ y တန်ဖိုးများလေ ဖြစ်ပါတယ်။ ဒါကြောင့် ညာဘက်အောက်ဆုံးထောင့်မှာ x, y က (19, 19) ဖြစ်ပါတယ်။ x, y ကို တစ်ကွက်စီရဲ့ အကျယ်ဖြစ်တဲ့ CELLSIZE နဲ့ မြှောက်ပေးလိုက်ရင် ဘယ်ဘက်ထောင့်က Coordinate ရဲ့ Pixel value တွေရလာပါတယ်။ ဒါတွေကိုသုံးပြီး Rectangle တွေ ဆွဲထားတာဖြစ်ပါတယ်။ pg.draw.rect() နောက်ဆုံးမှာ ထည့်ပေးထားတဲ့ တန်ဖိုးက Border width ပါ။ ဒါမထည့်ပေးရင် Rectangle အပြည့်ဆွဲပေးမှာပါ။
5. Apple Class
Snake နဲ့ ပန်းသီး က ပုံဆွဲပေးရတဲ့ သဘောတရားက အတူတူပါပဲ။ ပန်းသီး ဆွဲတာက လွယ်လို့ Apple class ကို အရင် ရေးပါမယ်။ ပန်းသီးဆိုပေမယ့် အနီရောင် Rectangle လေးတစ်ခုပဲ ဆွဲမှာပါ။ နောက်မှ ကိုယ့်စိတ်ကြိုက် ပုံလေးတွေထည့်နိုင်ပါတယ်။
class Apple:
def __init__(self, screen, x, y):
self.screen = screen
self.x = x
self.y = y
def draw(self):
rect = pg.Rect(self.x * CELLSIZE, self.y * CELLSIZE, CELLSIZE, CELLSIZE)
pg.draw.rect(self.screen, 'red', rect)
Parameter တွေအနေနဲ့ ပုံဆွဲဖို့မျက်နှာပြင်နဲ့ x, y position ကိုပဲ ယူပါမယ်။ self.screen ကို Game class ထဲမှာလို အသစ်တစ်ခုလုပ်စရာ မလိုတော့ပါဘူး။ Game class ရဲ့ screen ပေါ်မှာပဲ ထပ်ဆွဲမှာ ဖြစ်တဲ့အတွက် Apple object လုပ်တဲ့အခါမှာ တစ်ခါတည်း ထည့်ပေးရမှာပါ။
draw() method ထဲမှာတော့ အနီရောင် အကွက်လေးတစ်ကွက် ဆွဲထားပါတယ်။ ဒီလိုဆွဲဖို့အတွက် ပေးထားတဲ့ x, y နေရာမှာ Rect object တစ်ခုတည်ဆောက်ရပါမယ်။ Rect object ထဲမှာ topleft x, y position၊ width နဲ့ height ဆိုပြီး တန်ဖိုး လေးခုကို ထည့်ပေးရပါတယ်။ ရလာတဲ့ Rect ကို pg.draw.rect() function ထဲထည့်ပြီး ဆွဲပါတယ်။ ဒါက အပေါ်မှာ Grid ဆွဲခဲ့တာနဲ့ အတူတူပါပဲ။ ဒီမှာက Rect object ကို အပြင်မှာ တည်ဆောက်ပြထားတာပါ။
Game class ရဲ့ init() method ထဲမှာ Apple object တစ်ခု တည်ဆောက်ပါမယ်။ ဒီလို self.apple ဆိုပြီး တွဲပေးလိုက်ရင် Apple class ရဲ့ Attribute နဲ့ Method တွေကို Game class ထဲကနေ စိတ်ကြိုက်သုံးလို့ရပါပြီ။ X, Y position တွေကို ကြိုက်တာထည့်ပေးလိုက်ပါ။ ပြီးရင် draw() method ထဲမှာ self.apple.draw() ဆိုပြီး ခေါ်ပေးလိုက်ပါ။ ဒါဆိုရင် Grid ထဲမှာ အနီရောင် အကွက်ကလေးပေါ်လာပါပြီ။
class Game:
def __init__(self):
...
self.apple = Apple(self.screen, 5, 3)
def draw(self):
...
self.apple.draw()
6. Snake Class
Snake class က အပေါ်မှာရေးခဲ့တဲ့ Apple class အတိုင်းပါပဲ။ ပေးထားတဲ့ x,y position မှာ rectangle တစ်ခုဆွဲပေးရမှာပါ။ ဒါပေမယ့် Snake မှာက အကွက်တစ်ကွက်တင် ဆွဲလို့မရပါဘူး။ သူ့အလျားရှိသလောက် အကွက်တွေ ဆက်ဆွဲပေးရမှာပါ။ ဒါကြောင့် အကွက်တစ်ကွက်စီရဲ့ position ကို variable တစ်ခုစီနဲ့ မသိမ်းတော့ပဲ self.body ဆိုတဲ့ List ထဲမှာ သိမ်းမှာ ဖြစ်ပါတယ်။ နောက်ပိုင်း Calculation တွေလုပ်တဲ့အခါ မရှုပ်အောင် (x, y) တန်ဖိုးတွေကို List အစား Pygame မှာပါတဲ့ Vector2 object ထဲထည့်ပြီး သိမ်းပါမယ်။
Vector2 Object vs List
List ထဲမှာ x, y တစ်တွဲစီ သိမ်းမယ်ဆိုရင် ပြန်သုံးမယ့်အခါ ရှုပ်လာမှာပါ။ ဉပမာ- self.body = [(x1, y1), (x2, y2)] လို့သိမ်းထားပြီး x1 တန်ဖိုးကို လိုချင်ရင် self.body[0][0] လို့ခေါ်ရမှာဖြစ်ပြီး (x1, y1) နဲ့ (x2, y2) ပေါင်းချင်တယ်ဆိုရင် self.body[0][0]+self.body[1][0], self.body[0][1]+self.body[1][1] ဆိုပြီး တစ်ခုစီ ပေါင်းရမှာ ဖြစ်ပါတယ်။ Vector2 object က တန်ဖိုးနှစ်ခုပဲလက်ခံတဲ့ List တစ်ခုဖြစ်ပြီး တန်ဖိုးတစ်ခုစီကို index နဲ့ မဟုတ်ပဲ x, y နဲ့ ယူလို့ရပါတယ်။ နောက်ပြီး Vector2 object နှစ်ခုကို ပေါင်းချင်ရင် အပေါင်းသင်္ကေတနဲ့ ပေါင်းပေးလိုက်ရုံပါပဲ။ သက်ဆိုင်ရာ x, y တန်ဖိုးတွေကို သူ့အလိုလိုပေါင်းပေးမှာပါ။ အပေါ်မှာပြောခဲ့တဲ့ နမူနာကို Vector2 object သုံးပြီး ပြောင်းရေးမယ်ဆိုရင် ဒီလို ရပါတယ်။
# Example: Vector2 object
from pygame.math import Vector2
self.body = [Vector2(x1, y1), Vector2(x2, y2)]
print(self.body[0].x) # x1
print(self.body[1].y) # y2
self.body[0] += self.body[1] # Vector2(x1+x2, y1+y2)
Coordinate တွေကို List နဲ့သိမ်းတာထက်စာရင် Vector2 က ပိုရှင်းတာကို တွေ့ရမှာပါ။ Vector2 အစား List ကိုသုံးလည်း အတူတူပါပဲ။ အဆင်ပြေတာ သုံးနိုင်ပါတယ်။
Drawing the Snake
Vector2 သုံးမယ်ဆိုရင် pygame.math ကနေ import လုပ်ရပါမယ်။ ဒါဆို Snake class ကို ဆက်ရေးကြည့်ပါမယ်။
from pygame.math import Vector2
...
class Snake:
def __init__(self, screen):
self.screen = screen
self.body = [Vector2(10, 10)]
self.color = 'blue'
def _get_segment(self, index):
rect = pg.Rect(self.body[index].x*CELLSIZE, self.body[index].y*CELLSIZE, CELLSIZE, CELLSIZE)
return rect
def draw(self):
for i in range(len(self.body)):
rect = self._get_segment(i)
pg.draw.rect(self.screen, self.color, rect)
သေချာကြည့်မယ်ဆိုရင် Snake class က Apple class နဲ့ ပုံစံ အတူတူပါပဲ။ Apple မှာ x, y တန်ဖိုးတွေကို ထည့်ပေးရပေမယ့် ဒီမှာတော့ init() ထဲမှာပဲ တစ်ခါတည်းသတ်မှတ်ပေးထားပါတယ်။ draw() ကလည်း self.body ထဲမှာပါတဲ့ Coordinate တွေအတိုင်း rect ဆွဲပေးတာပါပဲ။ ဒီမှာတော့ ကုဒ်တွေကို ကြည့်ရရှင်းအောင် Rect object ကို get_segment() ဆိုပြီး Function တစ်ခုနဲ့ ရှာထားပါတယ်။ Rect တည်ဆောက်တာကတော့ Apple မှာလိုပါပဲ။ x, y တန်ဖိုးတွေကို CELLSIZE နဲ့မြှောက်ပြီး Topleft ရှာ၊ Width, height နေရာမှာ CELLSIZE ထည့်ပေးရုံပါပဲ။ ဒီ Function name ရှေ့ဆုံးမှာ underscore () တစ်ခုထည့်ပြီး ရေးထားတာကို တွေ့ရမှာပါ။ သူ့ရဲ့ အဓိပ္ပါယ်က ဒီ Method ကို Class အတွင်းထဲမှာပဲ သုံးမယ်လို့ ဆိုလိုပါတယ်။ အပြင်မှာ ခေါ်သုံးချင်လည်း ရပါတယ်။ ဒါပေမယ့် အဓိကက အပြင်မှာ သုံးစရာ မလိုဘူးလို့ ပြထားတာပါ။ တစ်ခြား Python Class တွေကို လေ့လာကြည့်ရင် ဒီလိုပုံစံတွေ တွေ့လာရမှာပါ။
Snake class ရေးပြီးရင်တော့ အရင်ကလိုပဲ Game ထဲမှာ Object တည်ဆောက်ပြီး ပုံဆွဲကြည့်ပါမယ်။
class Game:
def __init__(self):
...
self.snake = Snake(self.screen)
def draw(self):
...
self.snake.draw()
ပုံဆွဲရတဲ့ အပိုင်းတွေကတော့ ပြီးသလောက်ရှိပါပြီ။ ဒီတစ်ခါ Snake ကို တစ်ကွက်ကနေ တစ်ကွက် ရွှေ့ကြည့်ပါမယ်။ Snake class ရဲ့ init() ထဲမှာ direction vector တစ်ခု ထည့်ပါမယ်။ Program အပေါ်နားမှာ Direction vector တွေကို အရင် Define လုပ်ထားလိုက်ပါ။
RIGHT = Vector2(1, 0)
LEFT = Vector2(-1, 0)
UP = Vector2(0, -1)
DOWN = Vector2(0, 1)
...
class Snake:
def __init__(self,screen):
...
self.direction = RIGHT
...
Direction တွေမှာ ဘယ်ညာဖြစ်တဲ့ x တန်ဖိုးကတော့ Cartesian system နဲ့ အတူတူဖြစ်ပေမယ့် အပေါ်အောက်တန်ဖိုးတွေက မတူပါဘူး။ Pygame ထဲမှာ အပေါ်သွားလေ y တန်ဖိုး နည်းလေဖြစ်လို့ အပေါ်ကို ပြချင်ရင် y = -1 နဲ့ သုံးရပါတယ်။
Moving the Snake
ပေးထားတဲ့ Direction အတိုင်း Snake လေးကို ရွေ့ပေးတဲ့ Function တစ်ခု ရေးပါမယ်။ Snake ရဲ့ ခေါင်းပိုင်းဖြစ်တဲ့ self.body[0] မှာရှိတဲ့ (x, y) တန်ဖိုးကို self.direction နဲ့ ပေါင်းပေးရုံပါပဲ။ Vector2 သုံးမထားရင်တော့ x, y တစ်ခုစီ ပေါင်းရပါမယ်။
class Snake:
...
def move(self):
self.body[0] += self.direction
ပြီးရင် move() method ကို Game class ရဲ့ update() ထဲမှာ ခေါ်ပေးလိုက်ပါ။ ဒါကို run လိုက်မယ်ဆိုရင် Snake လေး ရွေ့သွားမှာပါ။
Loop တစ်ခါပတ်တိုင်း move() method ကို ခေါ်နေမှာ ဖြစ်လို့ တစ်ကွက်ပြီးတစ်ကွက်ဆက်တိုက် ရွေ့သွားမှာ ဖြစ်ပါတယ်။ ဒါကို ထိန်းပေးဖို့အတွက် အကွက်တစ်ကွက်ကနေ တစ်ကွက်ကို သွားရမယ့် ကြာချိန်သတ်မှတ်ပေးရပါမယ်။ ဒါကို Game class ထဲမှာ self.snake_timer ဆိုပြီး ထားလိုက်ပါ။ move() method နောက်ဆုံးခေါ်ခဲ့တဲ့ အချိန်ကို မှတ်ထားပြီး ဘယ်လောက်ကြာပြီဆိုတာ တွက်ကြည့်ရပါမယ်။ နောက်ဆုံးခေါ်ခဲ့တဲ့ အချိန်ကို self.snake_clk လို့ သတ်မှတ်လိုက်ပါ။ ကြာချိန်က self.snake_timer ထက်ကျော်သွားပြီ ဆိုမှ နောက်တစ်ကွက်ကို ဆက်ရွှေ့ပါမယ်။ ဒါကို Game class ထဲမှာ အောက်ကအတိုင်းရေးရပါတယ်။
class Game:
def __init__(self):
...
self.snake_timer = 1000 # millieseconds
self.snake_clk = 0
...
def update(self):
...
now = pg.time.get_ticks()
if now - self.snake_clk > self.snake_timer:
self.snake_clk = now # update last move clock
self.snake.move()
7. Adding Controls
ဒီအဆင့်ထိမှာတော့ ပုံဆွဲတာနဲ့ Animation တွေပဲ ရေးခဲ့ပါတယ်။ ဂိမ်းရဲ့ အဓိကအပိုင်းဖြစ်တဲ့ Control တွေကို အခုစရေးပါမယ်။
Changing Direction
Snake က အခုဆို Direction တစ်ခုတည်းကိုပဲ သွားရပါသေးတယ်။ ဒီတော့ Direction ပြောင်းဖို့ အတွက် Function တစ်ခုကို Snake class ထဲမှာ ရေးပါမယ်။ Snake game ကို လူတိုင်းနီးပါး ဆော့ဖူးမယ်လို့ ထင်ပါတယ်။ Snake direction ပြောင်းတဲ့ အခါကြိုက်သလို ပြောင်းလို့မရပါဘူး။ ဉပမာ- ဘယ်ကို သွားနေတဲ့အချိန်မှာ ညာဘက်ကို တန်းပြောင်းလို့ မရပါဘူး။ အထက်၊ အောက်ပဲ လှည့်လို့ရပါတယ်။ အပေါ်သွားနေစဉ်မှာလဲ အောက်ကို တန်းလှည့်လို့မရပါဘူး။ တစ်နည်းပြောရရင် သွားနေတဲ့ Direction နဲ့ ဆန့်ကျင်ဘက်ပြောင်းလို့ မရပါဘူး။ ဒီအချက်ကိုပါ တစ်ခါတည်း ထည့်ရေးပေးရပါမယ်။
class Snake:
...
def change_direction(self, new_dir):
if new_dir.x == self.direction.x or new_dir.y == self.direction.y:
return
self.direction = new_dir
Getting Key Inputs
Game class ထဲမှာ Keyboard input တွေကို လက်ခံပြီး Snake လေးရဲ့ Direction ပြောင်းပေးတဲ့ Method တစ်ခုရေးပါမယ်။
class Game:
...
def control(self):
keys = pg.key.get_pressed()
if keys[pg.K_UP]:
self.snake.change_direction(UP)
elif keys[pg.K_DOWN]:
self.snake.change_direction(DOWN)
elif keys[pg.K_LEFT]:
self.snake.change_direction(LEFT)
elif keys[pg.K_RIGHT]:
self.snake.change_direction(RIGHT)
User နှိပ်လိုက်တဲ့ Key တွေအကုန်လုံးကို pg.key.get_pressed() ကနေ ရနိုင်ပါတယ်။ pg.K_UP pg.K_DOWN စတာတွေက Arrow key တွေကို သတ်မှတ်ထားတဲ့ နာမည်တွေပါ။ ဒီ Key တစ်ခုခု နှိပ်လိုက်ပြီဆိုရင် Direction vector ပြောင်းပေးမှာပါ။ Direction ဆန့်ကျင်ဖက်ဖြစ်တာ၊ မဖြစ်တာတွေကို Snake.change_direction() ထဲမှာ စစ်ထားပြီးသားဖြစ်လို့ ထည့်စဉ်းစားစရာမလိုတော့ပါဘူး။
game.control() method ကို Game loop ထဲက draw, update တို့အပေါ်မှာ ခေါ်ပေးလိုက်ပါ။ ဒါဆိုရင် Snake လေးကို Arrow key တွေနဲ့ ထိန်းလို့ ရသွားပါပြီ။
```py def main(): ... while running: ... game.control() game.draw() game.update() pg.quit() ...
Project လေးကိုတော့ ဒီမှာ ခဏရပ်ထားပါမယ်။ ဒီအဆင့်ထိရေးခဲ့တဲ့ ကုဒ်တွေကို PasteBin မှာ တင်ပေးထားပါတယ်။
Conclusion
ဒီအဆင့်ထိရေးလာခဲ့ရင်း Program တစ်ခုကို Class တွေ ခွဲရေးလိုက်ရင် ထိန်းသိမ်းရလွယ်တဲ့ အပြင် ကုဒ်တွေကိုလည်း သူ့အပိုင်းနဲ့ သူ စုစည်းပြီးသား ဖြစ်သွားတာကို တွေ့ရမှာပါ။ ဒါ့အပြင် Game ထဲမှာ Object တစ်ခုနဲ့ တစ်ခု ဘယ်လိုချိတ်ဆက် အလုပ်လုပ်တယ်ဆိုတာကို နားလည်လာမယ်လို့ ထင်ပါတယ်။ မရှင်းတဲ့ အပိုင်းရှိရင်လည်း Comment/Messenger မှာ လာရောက်မေးမြန်းနိုင်ပါတယ်။ နောက်တစ်ပိုင်းမှာ Collision Detection, Scoring နဲ့ Body Movement တွေကို ဆက်ရေးကြပါမယ်။ ဆက်ရေးရမယ့် အဆင့်တွေကိုလည်း ကိုယ်တိုင် စမ်းပြီး ရေးကြည့်ကြဖို့ တိုက်တွန်းချင်ပါတယ်။