ভূমিকা

যেকোন প্রোগ্রামিং ল্যাঙ্গুয়েজ শেখার সবচেয়ে সহজ এবং কার্যকরী উপায় আমার মনে হয় সেটি প্রচুর পরিমাণে ব্যবহার করা। যেকোন লজিক ইমপ্লিমেন্ট করার চেষ্টা করা সেই ল্যাঙ্গুয়েজ দিয়ে । আমি প্রথম যখন পাইথন শিখেছিলাম তখন খেয়াল চেপে ধরেছিল "দেখাই যাক না একটা গেম বানিয়ে ফেলা যায় কিনা!" খুব ভয়াবহ গ্রাফিক্স কিছু না । সাধারণ কিছু। মূল উদ্দেশ্য কিছু লজিক ঠিক ভাবে ইমপ্লিমেন্ট করতে শেখা।

প্রয়োজনীয় ইন্সটলেশন

এই ব্লগ এর জন্য আমাদের প্রয়োজন পাইথন ৩.০। এবং গ্রাফিক্স এর জন্য আমরা PyGame লাইব্রেরিটি ব্যবহার করব । এছাড়া আর কোন টুলস আমাদের দরকার হবে না । PyGame ইন্সটল করার জন্য আমরা পাইথন ইন্সটল করার পর pip ব্যবহার করব -

pip install pygame

PyGame

PyGame SDL এর উপরে তৈরি করা একটা চমৎকার সহজ গ্রাজিক্স রেন্ডারিং লাইব্রেরি। গেম তৈরিতে ঢুকে যাওয়ার পূর্বে আমাদের কয়েকটি জিনিস বোঝা প্রয়োজন।

  • Rendering: সহজ ভাষার রেন্ডারিং মানে কোন কিছু কে তার ঐ সময়ের রূপে উপস্থান করা। গেম এর ক্ষেত্রে তা হয়ে থাকে ছবি বা গ্রাফিক্স প্রদর্শন করা।
  • FPS: Frame Per Second দিয়ে আমরা বুঝি প্রতি সেকেন্ড এ কতগুলো ফ্রেম রেন্ডার করা হচ্ছে।

Snake Game

৯০ এর দশক থেকে এখন পর্যন্ত হয়ত এমন কাউকে পাওয়া যাবে না যে snake গেম এর নাম শুনেনি বা খেলেনি । এই গেমটির মেকানিজম মূলত হচ্ছে প্লেয়ার সাপ হিসেবে খেলবে এবং random জায়গায় উৎপন্ন হওয়া খাদ্য খেয়ে বড় হবে। প্রতিটি খাওয়ার জন্য স্কোর ১ করে বাড়বে। সাপটির গেম উইন্ডোর বাউন্ডারি তে অথবা নিজের শরীর এ লাগলে গেম শেষ হবে।

তো শুরু করা যাক

চাইলে আগে পুরো কোড ডাউনলোড করে ফলো করা যেতে পারে।

শুরুতেই আমরা প্রয়োজনীয় import গুলো করে নিব।

import pygame
import sys
import random
import time

এবার আমরা PyGame ব্যবহার করে একটি উইন্ডো তৈরি করব। এই উইন্ডোর মধ্যেই আমাদের গেম চলবে।

pygame_error = pygame.init()
if pygame_error[1] > 0:
    print('{} error while initializing pygame. exiting!!'.format(pygame_error[1]))
    sys.exit(1)
else:
    print('PyGame initialized')

display_size = (720, 480)

window = pygame.display.set_mode(display_size)
pygame.display.set_caption('Snake!!')

এখন স্ক্রিপ্টটি রান করলে একটি কালো উইন্ডো এসে চলে গেলে বুঝতে হবে PyGame সঠিকভাবে ইন্সটলড হয়েছে। এবং টার্মিনালএ নিম্নোক্ত output দেখাবে।

pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
PyGame initialized

PyGame এর ভার্সন ভিন্ন হলে ভিন্ন রকম হতে পারে উক্ত মেসেজ টি।

এখন আমরা উপরের কোডটি কে একটু ভেঙ্গে দেখব। প্রথমেই আমরা pygame.init() ফাংশনটি কল করার মাধ্যমে PyGame initialize করে নিচ্ছি। এর পরে আমরা আমাদের গেম এর উইন্ডো এর সাইজ ঠিক করে নিচ্ছি (720, 480)। তার পরে আমরা এই সাইজ এর একটি উইন্ডো তৈরি করে তা window variable এ assign করে রাখলাম। এবং সবশেষে আমাদের উইন্ডো এর নাম দিলাম "Snake!!"

বিভিন্ন জায়গায় ব্যবহার করার জন্য আমরা কয়েকটি রঙ এর RGB কোড ডিফাইন করে রাখতে পারি। মনে রাখতে হবে যে PyGame RGB Color System ব্যবহার করে। সুতরাং আমরা চাইলে যেকোন RGB কালার আমাদের গেম এ ব্যবহার করতে পারি।

# Colors
red = pygame.Color(255, 0, 0)
green = pygame.Color (0, 255, 0)
blue = pygame.Color(0, 0, 255)
black = pygame.Color (0, 0, 0)
white = pygame.Color (255, 255, 255)
brown = pygame.Color (165,42,42)
aqua = pygame.Color (0,255,255)

আরও RGB রঙ ব্যবহার করতে চাইলে এই [সাইট] টি (https://rgbcolorcode.com/) ব্যবহার করা যেতে পারে।

আমরা চাইলে আমাদের উইন্ডো এর background color পরিবর্তন করতে পারি তার জন্য যা করতে হবে -

window.fill(white)

এখন যদি আমরা আমাদের কোড টি রান করি তাহলে আমাদের উইন্ডো এর রঙ সাদা হওয়ার কথা। কিন্তু??? হয় নি। এর কারণ হচ্ছে আমরা রঙ ঠিক ই পরিবর্তন করেছি কিন্তু আমরা পরিবর্তিত উইন্ডো রেন্ডার করি নি। যেকোনদৃশ্যমান পরিবর্তন এর জন্য আমাদের উইন্ডোকে রেন্ডার করতে হবে। তার জন্য pygame.display.flip() ফাংশনটি ব্যবহার করতে হবে।

সুতরাং আমাদের কোডটি হবে -

window.fill(white)
pygame.display.flip()

fpsController = pygame.time.Clock()

ঠিক আছে। কিন্তু fpsController সেটা আবার কি? pygame.time.Clock() দিয়ে মূলত আমরা আমাদের গেম এর নিজস্ব FPS বা কতবার আপডেট হবে তা নিয়ন্ত্রণ করতে পারব। [পরে বিস্তারিত]. এখন আমাদের স্ক্রিপ্ট টি রান করলেই খুব দ্রুত ই উইন্ডো তৈরি হয়ে আবার বন্ধ হয়ে যাচ্ছে। এটি বন্ধ করার জন্য আমরা এখন একটি infinty loop তৈরি করব যা উইন্ডো টি বন্ধ না করা পর্যন্ত চলতে থাকবে।

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

PyGame এর উইন্ডো এর মধ্য ঘটে যাওয়া সব কিছু কেই event বলা হয়। প্রতিটি ইভেন্ট কে আলাদা ভাবে হ্যান্ডেল করার জন্য তাদের কে uniquely চেক করে নিতে হয়। সুতরাং উপরের কোডটি যতক্ষণ না উইন্ডো ক্লোজড হচ্ছে ততক্ষণ চলবে। একবার রান করে দেখা যাক।

খালি উইন্ডো

ইয়োশ "To see is to believe"। এতদূর পর্যন্ত ঠিক ঠাক করে আসলে বাকি টুকু খুব ই সহজ শুধু কি হচ্ছে তার ভিত্তিতে কি হবে সেটা ঠিক করা।

সময় হয়েছে সাপ দেখানোর

সাপ দেখানোর আগে একটু আলোচনা করা যাক কিভাবে আমাদের উইন্ডো টি কাজ করে । তার জন্য আমরা উইন্ডোতে একটি আয়তক্ষেত্র আঁকব ।

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    pygame.draw.rect(window, green, pygame.rect(0, 0, 10, 10))

মনে রাখতে হবে ইভেন্ট এর লুপ এর মধ্যে কিছু করলে সেটি শুধু ইভেন্ট টি ঘটলে দেখা যাবে । কিন্তু আমরা আয়তক্ষেত্রটিকে সবসময় ই দেখতে চাচ্ছি আপাতত। কিন্তু কোথায় আমাদের আয়তক্ষেত্র? ধরে ফেলা উচিত এতক্ষণে। হুম উইন্ডো আপডেট করা হয় নি।

pygame.draw.rect(window, green, pygame.Rect(0, 0, 10, 10))
pygame.display.flip()
এইতো দেখা যাচ্ছে আমাদের আয়তক্ষেত্র 

এবার তাহলে ফাংশন টি ব্যাখ্যা করা যাক। pygame.draw.rect() ফাংশন টি কোন একটি উইন্ডোতে নির্দিষ্ট রঙ এর Geometric shape তৈরি করে। pygame.Rect(x, y, width, height) ফাংশনটি একটি আয়তক্ষেত্র তৈরি করে। আচ্ছা আমরা তো x=0 এবং y=0 দিলাম তাহলে আয়তক্ষেত্র একদম মাঝে না হয়ে উপরে হলো কেন? কারণ গ্রাফিক্স এর ক্ষেত্রে Co-ordinate শুরু হয় উপরের বাম দিক থেকে। কার্তেসিয়ান সিস্টেমের মত মধ্যখান থেকে না ।

তাহলে মাঝে দেখাতে চাইলে কি করব? খুব সহজ,

pygame.draw.rect(window, green, pygame.Rect(display_size[0]//2, display_size[1]//2 10, 10))
pygame.display.flip()

আচ্ছা আমরা তো বার বার অনেক কিছু draw করব সব কিছু লুপ এর মধ্যে থাকলে কেমন অগোছালো দেখা যাচ্ছে না? তার চেয়ে আমরা যা যা display করা লাগবে সব কিছু অন্য একটি ফাংশনে নিয়ে যাই এবং প্রতিবার লুপে তাকে কল করব।

def render():
    pygame.draw.rect(window, green, pygame.Rect(0, 0, 10, 10))
    pygame.display.flip()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    render()

এখন থেকে আমাদের আঁকিবুঁকির সব কিছু আমরা render() ফাংশন এ add করব।

সাপ আঁকি??

আমাদের সাপ আসলে পর পর কতগুলো আয়তক্ষেত্র। ধরা যাক শুরু তে ৫ টি আয়তক্ষেত্র নিয়ে শুরু করি। এবং সাপকে সম্পূর্ন দেখানোর জন্য সবগুলো ব্লক এর পজিশন মনে রাখা জরুরী। তাহলে স্ক্রিপ্ট এর শুরুতে আমরা একটি লিস্ট হিসাবে সাপের বডি রেখে দিতে পারি। যা আমরা পরবর্তিতে ব্যবহার করতে পারব। একই সাথে আমরা সাপের মাথার আয়তক্ষেত্রটির পজিশনও একটি variable এ রেখে দিব।

# at the top
snakehead = [360, 240] # becuase that's the center of the window
snakebody = []

এখন আমরা আমাদের প্রতিটি ব্লক এর আকার ১০ ধরে ৫টি আয়তক্ষেত্র বিশিষ্ট সাপ পাই। ব্লকের সাইজ ১০ বলছি কারণ আমাদের আয়তক্ষেত্রটির height, width ১০ ধরে নিয়েছিলাম। আমার জন্য এটি উপযুক্ত মনে হয়েছে। প্রয়োজনে এটি চেঞ্জ করে নিতে পারেন। সেক্ষেত্রে প্রয়োজনীয় সব জায়গায় পরিবর্তন করে নিতে হবে।

# before the render function
blocksize = 10
snakebody = [[snakehead[0], snakehead[1]]]
for i in range(4):
    snakebody.append([snakebody[i][0]-blocksize, snakebody[i][1]])

...
def render():

এখন আমরা snakebody এর সবগুলো ব্লককে তাদের পজিশন এ রেন্ডার করলেই একটি ৫ব্লকের সাপ দেখতে পাব।

def render():
    global snakebody
    for bit in snakebody:
        pygame.draw.rect(window, green, pygame.Rect(bit[0], bit[1], 10, 10))
    pygame.display.flip()
সবুজ সাপ!

Note: মনে রাখা প্রয়োজন যে global snakebody দ্বারা আমরা ফাংশন এর ভেতর থেকে স্ক্রিপ্ট এর গ্লোবাল variable ব্যবহার করতে পারি। আরও জানতে ...

নাচ আমার সাপ বাঁশির সুরে নাচ

না ঠিক বাঁশির সুরে না আমাদের সাপ নাচবে (পড়ুন অনন্তকাল চলবে) প্লেয়ার এর ইচ্ছানুযায়ী। গেমের সাপ সাধারণত ৪ দিকে (ডানে, বামে, উপরে, নীচে) চলাচল করে। প্রথমে আমরা একদিকে করানোর চেষ্টা করি। ডানে যেতে হলে আমাদের সাপের x axis এর মানে বৃদ্ধি করতে হবে। যেহেতু আমাদের ব্লক এর সাইজ ১০ তাই ১০ করে বৃদ্ধি করতে হবে। যেহেতু এটি একটি পরিবর্তন এবং আমাদের আরও কিছু পরিবর্তন হবে প্রতি ফ্রেমে, তাই আমরা সব পরিবর্তন নতুন একটি ফাংশনের ভেতরে করব। ফাংশন এর নাম দিলাম update()

def update():
  global snakehead
  snakehead = [snakehead[0] + blocksize]

render() ফাংশন কল এর আগে update() ফাংশন কল করতে হবে লুপ এর মধ্যে।
কই কিছু হচ্ছে না তো। কারণ হচ্ছে, আমরা মাথার স্থান পরিবর্তন করতেছি ঠিক-ই। কিন্তু পুরো সাপের স্থান পরিবর্তন করতে হলে আমাদের যেটি করতে হবে সেটি হচ্ছে, নতুন মাথার পজিশন অনুযায়ী বাকি সবটুকুর স্থান পরিবর্তন করতে হবে। এভাবে -

def update():
    global snakehead, snakebody
    snakehead = [snakehead[0] + blocksize, snakehead[1]]
    snakebody.insert(0, list(snakehead))
কি হল?

আচ্ছা কি হলো!!! এখানে আমাদের দুটো জিনিস বোঝা প্রয়োজন।

  • আমরা আমাদের গেম এর পরিবর্তনের হার (FPS) ঠিক করে দেই নি । তাই খুব দ্রুত অনেক বেশি update হয়ে গেছে । এর জন্য আমরা render ফাংশন এর একদম নিচে এটি যোগ করব।
def render():
  global snakebody, fpsController
  ... 
  pygame.display.flip()
  fpsController.tick(15)

এখানে ১৫ দিয়ে বুঝানো হচ্ছে প্রতি সেকেন্ডে ১৫ বার update এবং render হবে আমাদের গেম এর সব কিছু।

তাহলে সাপের চলার গতি বাড়ানো বা কমানোর জন্য fpsController এর tick এর পরিমাণ বাড়ানো কমানো ই যথেষ্ট।

  • আমাদের সাপ সামনে শুধু বৃদ্ধি পাচ্ছে কিন্তু পেছনের থেকে আগাচ্ছে না। এটি সমাধানের জন্য আমরা শেষ ব্লক টিকে বের করে দিব এভাবে -
def update():
  ...
  snakebody.pop()
এবার কি?

কিন্তু ঠিক হলো না তো??? প্রকৃতপক্ষে ঠিক হয়েছে কিন্তু যেহেতু আমরা প্রতিবার শুধু নতুন সাপ টি আঁকছি কিন্তু পুরনোটিকে clear করছি না। সেটি রয়ে যাওয়ায় মনে হচ্ছে যে ঠিক হয় নি।

অর্থাৎ আমাদের প্রতিবার render এর শুরু তে display টিকে সাদা করে নিতে হবে।

def render():
    global snakebody, fpsController, window
    window.fill(white)
    ...
আরিব্বাস! হলো তাহলে!!

তাহলে এখন আমাদের সাপ আমাদের ইচ্ছা অনুযায়ী চলতেছে। এখন আসা যাক বাকি দিক গুলোতে কিভাবে যাবে। যেহেতু সাপের দিক মূলত চলবে controller (এক্ষেত্রে কীবোর্ড) এর দিক নিদর্শেনের অনুযায়ী, সেহেতু আমাদের এখন তাহলে controller এর input নিয়ে কাজ করতে হবে। এক্ষেত্রেও PyGame আমাদের অনেক সহজ উপায় দিয়ে সাহায্য করে থাকে। এখন আমরা আমাদের event loop এর event গুলোকে ফিল্টার করে দিক নিদর্শনের ইনপুট গুলো নিব এবং সে অনুযায়ী সাপের দিক পরিবর্তন করব।

আমাদের update ফাংশন এর তাহলে জানা থাকতে হবে সাপটি কোনদিকে যাচ্ছে এখন। সুতরাং আমরা এখন একটি global variable direction declare করব । এবং শুরুতে ধরে নিব যে direction = 'RIGHT' যেন প্রথম থেকে সাপটি ডানে যাওয়া শুরু করে।

যেহেতু আমরা এখানে দিক পরিবর্তন করব তাই আমাদের update ফাংশন এ এখন আর snakehead = [snakehead[0] + blocksize, snakehead[1]] এই লাইনটি থাকা যাবে না তার বদলে হবে -

def update():
    global snakehead, snakebody, direction
    if direction == 'RIGHT':
        snakehead = [snakehead[0] + blocksize, snakehead[1]]
    if direction == 'LEFT':
        snakehead = [snakehead[0] - blocksize, snakehead[1]]
    if direction == 'UP':
        snakehead = [snakehead[0], snakehead[1] - blocksize]
    if direction == 'DOWN':
        snakehead = [snakehead[0], snakehead[1] + blocksize]
    snakebody.insert(0, list(snakehead))
    snakebody.pop()

এবং গেম লুপ এর ভেতরে প্রতিটি input event এর জন্য দিক পরিবর্তন করতে হবে ।

while True:
    global direction
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            keypressed = event.key
            if keypressed == pygame.K_RIGHT:
                direction = 'RIGHT'
            if keypressed == pygame.K_LEFT:
                direction = 'LEFT'
            if keypressed == pygame.K_UP:
                direction = 'UP'
            if keypressed == pygame.K_DOWN:
                direction = 'DOWN'
    update()
    render()
যেদিকে চাই সেদিকেই যায়

দেখা যাচ্ছে আমাদের সাপ আমাদের ইচ্ছা অনুসারেই চলাচল করছে। যদিও এখানে একটি ছোট্ট খুঁত রয়ে গেছে যা অমি এখানে সমাধান করব না বা বলবও না। আশা করি এতদূর পর্যন্ত কেউ এসে থাকলেই নিজে নিজেই বের করে সমাধান করতে পারবে।

খাবার কই খাবার?

Classic snake গেম এর scoring সিস্টেম হচ্ছে র‍্যান্ডম খাবার খেয়ে সাপের দৈর্ঘ্য বড় করা। প্রতিটি খাবার এর score হছে ১ করে। তাহলে আমরা খেলা শুরু করতে পারি একটি random জায়গায় একটি খাবার দেখানোর মাধ্যমে। এটি করার জন্য আমরা একদম শুরুতে একটি global variable এ খাবার এর পজিশন assign করে রাখব।

food = [random.randrange(1, 72) * 10, random.randrange(1, 46)*10]

এখন আমরা প্রতিবার render এর সময় খাবার কেও render করব। একটি rectangle হিসাবে। এবং এর রঙ টি আমরা ভিন্ন দিব।

def render():
  ...
  pygame.draw.rect(window, brown, pygame.Rect(food[0], food[1], blocksize, blocksize))
food

তাহলে এখন এই খাবার খাওয়ানোর ব্যবস্থা করা দরকার। তার জন্য আমরা প্রতিবার update এর সময় দেখতে হবে সাপের মাথা কি খাবার এর পজিশনে আছে কিনা। তার জন্য আমরা update ফাংশনটি এমন ভাবে আপডেট করব যেন খাবার খেলে নতুন জায়গায় খাবার উৎপন্ন হয় এবং সাপের দৈর্ঘ্য বৃদ্ধি পায়।

def update():
  global snakehead, snakebody, direction, food
  ...
  snakebody.insert(0, list(snakehead))
  if snakehead[0] == food[0] and snakehead[1] == food[1]:
      food = [random.randrange(1, 72) * 10, random.randrange(1, 46)*10]
  else:
      snakebody.pop()
অম নোম নোম

এখানে যেটা করা হচ্ছে সেটা হচ্ছে খাবার খেলে সেটা তো সাপের একদম শেষে যুক্ত হবে সুতরাং আমরা শেষ ব্লক টি pop() না করলেই হয়ে যাচ্ছে। এবং সাথে নতুন একটি স্থানে খাবার উৎপন্ন করে দিচ্ছি।

কত করলাম?

প্রতিটি গেমেই কত পয়েন্ট score করলাম দেখান একটি আবশ্যিক অংশ। স্কোর রাখার জন্য global একটি variable declare করে রাখি। score = 0। এবং খাবার খেলে তার value ১ করে বাড়াই। score += 1। এখন এই score দেখানোর জন্য আমরা একটি Heads Up Display (HUD) তৈরি করব।

HUD এর জন্য আমাদের PyGame এর text rendering ফাংশন ব্যবহার করব। এর জন্য আমরা render() ফাংশন এ নিমোক্ত কোড add করব -

def render():
  ...
  hudfont = pygame.font.SysFont('monaco', 24)
  hud_score = hudfont.render('Score: {}'.format(score), True, red)
  hud_score_rect = hud_score.get_rect()
  hud_score_rect.midtop = (360, 20)
  
  hud_game_state = hudfont.render('{}'.format('Playing'), True, black)
  hud_game_state_rect = hud_game_state.get_rect()
  hud_game_state_rect.midtop = (660, 20)

  window.blit(hud_score, hud_score_rect)
  window.blit(hud_game_state, hud_game_state_rect)

প্রথমেই আমরা আমাদের পছন্দমত একটি ফন্ট সিলেক্ট করে নিব (এক্ষেত্রে monaco)। এর পরে আমরা text string টিকে render graphics এ পরিণত করব। তারপরে render করা লেখাটির চারপাশের ক্ষেত্রফল এর আয়তক্ষেত্র বের করলাম। এবং আয়তক্ষেত্রের পজিশন ঠিক করলাম।

এবং সবশেষে আমাদের উইন্ডো তে text গুলো প্রদর্শন করলাম।

খাচ্ছি, খেয়ে মোটা হচ্ছি

এ খেলার শেষ কোথায়?

এ পথ যদি শেষ না হত তবে কেমন হত না জানলেও এ গেম যদি শেষ না হয় তাহলে মজার কিছু নাই। গেম শেষের জন্য আমরা দুটি মেকানিজম ফলো করব ।

  1. সাপ যদি উইন্ডো এর বাইরে চলে যায় ।
  2. সাপ যদি নিজের শরীর অতিক্রম/স্পর্শ করে।

২ টি চেক-ই আমরা update ফাংশনে করব। কিন্তু মনে রাখতে হবে game over এর ক্ষেত্রে আমরা সাপ বা খাবার কোনটি-ই আর দেখাব না। সেক্ষেত্রে আমাদের আরও একটি global variable প্রয়োজন যেটি কিনা নির্দেশ করবে গেম শেষ কিনা। game_over = False declare করে নিব স্ক্রিপ্ট এর শুরুতে। এবং লুপ এ update এবং render ফাংশন কল হবে শুধুমাত্র game_over এর value False হলে।

def update():
  ...
  if snakehead[0] <= 0 or snakehead[0] >= display_size[0]:
      game_over = True
  if snakehead[1] <= 0 or snakehead[1] >= display_size[1]:
      game_over = True
  for block in snakebody[1 : ]:
      if snakehead[0] == block[0] and snakehead[1] == block[1]:
          game_over = True

এখন game over screen দেখানোর জন্য গেম লুপ এর পরিবর্তন -

  ...
  if not game_over:
      update()
      render()
  else:
      hudfont = pygame.font.SysFont('monaco', 24)
      hud_score = hudfont.render('Score: {}'.format(score), True, red)
      hud_score_rect = hud_score.get_rect()
      hud_score_rect.midtop = (360, 20)

      gg_text = hudfont.render('GG WP. Press any key to continue', True, black)
      gg_rect = gg_text.get_rect()
      gg_rect.midtop = (360, 260)
      window.fill(white)
      window.blit(gg_text, gg_rect)
      window.blit(hud_score, hud_score_rect)
      pygame.display.flip()
      fpsController.tick(15)
      time.sleep(3)
      sys.exit(1)
সব শেষ!

পরিশেষ

এখানে আরও অনেক কিছু করার আছে। অনেক কিছু করা সম্ভব। সেগুলো অমি পাঠকের হাতে ছেড়ে দিলাম, যদি কেউ ফলো করে থাকে। এখন আসি কেন এই রকম একটি প্রজেক্ট নিয়ে লেখা। আপাতদৃষ্টিতে snake game যেকোন প্রোগ্রামিং ভাষাতেই কোড খুঁজে পাওয়া সম্ভব বলে সময় অপচয় মনে হতে পারে। কিন্তু rapid coding challenge হিসেবে ছোট খাট classic game এর replication অল্প সময়ে অনেক কিছু শিখতে সাহায্য করে। তাছাড়া বিভিন্ন কনসেপ্ট বোঝার জন্য বাস্তব ইমপ্লিমেন্টেশনের বিকল্প নেই।

গেম বানানোর সময় অনেক কিছু মাথায় রাখতে হয়। অনেক কিছু প্রথমবারে কাজ করবে না। অনেক কিছু নিজের ইচ্ছা মত হবে না। সাথে অনেক কিছু যোগ করতে ইচ্ছা করবে। প্রতিটি গেম, তা যত ছোটই হোক না কেন, অনেকগুলো অংশে একই সাথে কাজ করে। প্রতিটি ছোট অংশই একেকটি ছোট ছোট সমস্যা। এরকম অনেক ছোট ছোট সমস্যার সমাধান করার পরই সম্ভব হবে একটি গেম এর সব কিছু কাজ করানো। কত মজাই না লাগে নিজের বানানো গেম খেলার মধ্যে। একই পদ্ধতি অনুসরণ করে আরও ছোট গেম বানানো সম্ভব।

"শেষ হইয়াও হইল না শেষ"

আরও যা যা এই গেম এ add করা যেতে পারে তার একটি নমুনা লিস্ট অমি দিচ্ছি -

  • স্টার্ট মেনুঃ গেম শুরু হওয়ার সময় একটি আমন্ত্রণ স্ক্রিন যা থেকে ইছা মত সময়ে গেম খেলা শুরু করা যাবে, এখনকার মত হটাত শুরু হয়ে না যেয়ে।
  • Pause/Resume: গেমের মাঝে থামানো এবং পুনরায় শুরু করার ব্যবস্থা করা যেতে পারে।
  • New game: গেম শেষ হয়ে যাওয়ার পরে আবার শুরু করতে পারার ব্যবস্থা থাকতে পারে।
  • শব্দঃ সাউন্ড ইফেক্ট গেম এ নতুন মাত্রা দেয়। খাবার খাওয়ার সময় সাউন্ড ইফেক্ট যোগ করা যেতে পারে। গেম শুরু/ শেষ এর ক্ষেত্রেও।
  • সাপের গতিঃ সাপের গতি নিয়ন্ত্রণের ব্যবস্থা থাকতে পারে।
  • সাপের গতিপথে বিভিন্ন বাধা স্থাপন করে গেমটি কঠিন করা যেতে পারে।

এসব কিছু implement করা সহ মডিউলার ডিজাইন এর কোডবেস এখানে পাওয়া যাবে।

"Pull Requests are always welcome"
এমন কিছু?

Code এর পরিচ্ছনতা

আমাদের লেখার এবং একই সাথে বর্ণনার স্বার্থে আমরা কোডটিতে Clean Code এর কনভেনশন ফলো করিনি। চাইলেই সম্পুর্ন কোড বেস কে নতুন ভাবে সাজিয়ে সকল কনভেনশন মোতাবেক লেখা সম্ভব। তাতে কোড পড়ার এবং সাজানোর efficiency বৃদ্ধি পাবে।

Code-base