Loyiha haqida
Kursning yakuniy loyihasida köpçilik eski tugmali telefonlardan eslaydigan «Ilon» öyinini yozamiz. Qoidalar oddiy: ilon maydonda harakatlanadi, siz uni strelkalar bilan boşqarasiz. Ilon ovqatni yesa, uzayadi va hisob oşadi. Devorga yoki öz dumiga urilsa — öyin tugaydi.
Oddiy körinsa ham, bu loyihada dasturlaşning köp muhim mavzulari jamlangan: sikllar, röyxatlar, kortejlar, klasslar, random moduli, hodisalarni qayta işlaş va holatni boşqariş. Öyin yoziş — örganganlarni mustahkamlaşning eng qiziqarli usuli.
Öyinni pygame kutubxonasi bilan yozamiz. U oyna oçiş, rasm çiziş, klaviaturani öqiş va tovuş çiqariş uçun oddiy vositalar beradi.
pip install pygame
Örnatilganini tekşiriş uçun python -m pygame.examples.aliens buyruği bilan kutubxonaga ilova qilingan namunaviy öyinni işga tuşirib körişingiz mumkin.
Boşlanğiç skript va undagi kamçiliklar
Loyiha uçun asos sifatida kurs muallifi Sarvarbek Nazarov yozgan qisqa skriptni olamiz. Uning boşlanişi quyidagiça (izohlar özbekçaga ögirildi):
import pygame
# pygame ni işga tuşiramiz
pygame.init()
# Oyna ölçami
window_size = (400, 400)
screen = pygame.display.set_mode(window_size)
pygame.display.set_caption("Snake")
snake_size = 10
snake_pos = [200, 200]
snake_body = [[200, 200], [210, 200], [220, 200]]
food_size = 10
food_pos = [random.randrange(1, (window_size[0]-food_size)//10)*10, random.randrange(1, (window_size[1]-food_size)//10)*10]
food_spawn = True
direction = "RIGHT"
change_to = direction
score = 0
speed = 200
Ğoya töğri: ilon tanasi — koordinatalar röyxati, ovqat — tasodifiy nuqta, yönaliş — özgaruvçi. Lekin skriptni işga tuşirsangiz, darhol xato çiqadi:
NameError: name 'random' is not defined
Sababi — random moduli import qilinmagan. Xuddi şunday, game_over() funksiyasi içida işlatilgan time va sys modullari ham unutilgan. Bu yangi boşlovçilarda juda köp uçraydigan xato: Python faqat import qilingan modullarni taniydi.
Skriptning yönalişni özgartiruvçi qismi ham qiziq:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
change_to = "UP"
if event.key == pygame.K_DOWN:
change_to = "DOWN"
# Ilon birdan teskari tomonga burilib ketmasligi kerak
if change_to == "UP" and direction != "DOWN":
direction = "UP"
if change_to == "DOWN" and direction != "UP":
direction = "DOWN"
Bu yaxşi ğoya — ilon öz içiga qarab burila olmasligi kerak. Biz uni saqlab qolamiz, faqat qisqaroq yozamiz. Boşqa kamçiliklar:
import random,import time,import sysyöq.- Oynani yopiş (
pygame.QUIT) hodisasi qayta işlanmagan — oynaning ✕ tugmasi işlamaydi. speed = 200eʼlon qilingan, lekin öyin tezligini boşqaradiganpygame.time.Clockişlatilmagan.- Tananing boşlanğiç holati: boş
[200, 200], qolgan bölaklar esa öng tomonda, ilon ham öngga harakatlanmoqda — yaʼni boş birinçi qadamdayoq öz tanasiga kiradi. 10,400kabi «sehrli sonlar» kod böylab tarqalgan; ölçamni özgartiriş uçun köp joyni tahrirlaş kerak.game_over()time.sleep(3)bilan oynani muzlatib qöyadi va dasturdan çiqib ketadi — qayta öynaş imkoni yöq.
Endi şu ğoyalar asosida toza, tuşunarli va kengaytiriş oson bölgan versiyani qadamma-qadam quramiz.
1-qadam: öyin sikli
Har qanday öyinning yuragi — öyin sikli (game loop). U soniyasiga önlab marta takrorlanadi va har aylanişda uçta işni bajaradi:
- Hodisalar — foydalanuvçi nima qildi? (tugma bosildimi, oyna yopildimi)
- Yangilaş — öyin holatini özgartiriş (ilonni suriş, töqnaşuvni tekşiriş).
- Çiziş — yangi holatni ekranga çiqariş.
pygame.time.Clock esa siklning tezligini boşqaradi: soat.tick(10) sikl soniyasiga 10 martadan tez aylanmasligini taʼminlaydi. Busiz öyin kuçli kompyuterda juda tez, kuçsizida sekin işlardi.
import sys
import pygame
pygame.init()
ekran = pygame.display.set_mode((600, 400))
pygame.display.set_caption("Birinçi oyna")
soat = pygame.time.Clock()
while True:
# 1. Hodisalar
for hodisa in pygame.event.get():
if hodisa.type == pygame.QUIT:
pygame.quit()
sys.exit()
# 2. Yangilaş (hozirça heç narsa)
# 3. Çiziş
ekran.fill((18, 24, 27))
pygame.draw.rect(ekran, (76, 175, 80), (290, 190, 20, 20))
pygame.display.flip() # çizilganlarni ekranga çiqaramiz
soat.tick(10) # soniyasiga 10 kadr
pygame da hamma narsa avval yaşirin «kanvas»ga çiziladi, pygame.display.flip() esa uni bir yöla ekranga çiqaradi. Şu sababli tasvir miltillamaydi. Koordinatalar boşi (0, 0) — oynaning çap yuqori burçagi, y esa pastga qarab ösadi.
2-qadam: konstantalar va katakli maydon
Ilon piksel böylab emas, kataklar böylab yuradi. Şuning uçun maydonni katakli tör deb tasavvur qilamiz: ilon va ovqatning holatini katak raqamlari bilan saqlaymiz (ustun, qator), faqat çizişda piksellarga aylantiramiz. Bu hisob-kitobni ança soddalaştiradi.
Barça sozlamalarni dastur boşida konstantalar sifatida (katta harflar bilan) yozamiz:
KATAK = 20 # bitta katak — 20x20 piksel
USTUNLAR, QATORLAR = 30, 20 # maydon 30x20 katak
ENI, BALANDLIK = KATAK * USTUNLAR, KATAK * QATORLAR
# Yönalişlar: (dx, dy) — har qadamda ustun va qator qançaga özgaradi
YUQORI, PAST, CHAP, ONG = (0, -1), (0, 1), (-1, 0), (1, 0)
def piksel(joy: tuple[int, int]) -> tuple[int, int]:
ustun, qator = joy
return ustun * KATAK, qator * KATAK
print("Oyna ölçami:", ENI, "x", BALANDLIK)
print("Markaziy katak:", (USTUNLAR // 2, QATORLAR // 2))
print("Uning piksel koordinatasi:", piksel((USTUNLAR // 2, QATORLAR // 2)))
print("Öng pastki katak:", piksel((USTUNLAR - 1, QATORLAR - 1)))
Natija:
Oyna ölçami: 600 x 400
Markaziy katak: (15, 10)
Uning piksel koordinatasi: (300, 200)
Öng pastki katak: (580, 380)
Endi maydonni kattalaştiriş uçun faqat bitta qatorni özgartiriş kifoya. Yönalişlarni satr ("UP") emas, (dx, dy) kortej körinişida saqlaganimiz esa harakatni bitta formula bilan yoziş imkonini beradi.
3-qadam: Ilon klassi
Ilonning maʼlumotlari (tanasi, yönalişi) va xatti-harakatlari (yuriş, buriliş, uzayiş) bitta klassga yiğiladi. Tanani kataklar röyxati sifatida saqlaymiz, birinçi element — boş.
Harakat ğoyasi juda çiroyli: har qadamda boşning oldiga yangi katak qöşamiz va dumdan bitta katakni olib taşlaymiz. Ilon ovqat yegan bölsa, dumni olib taşlamaymiz — şu tarzda u bir katakka uzayadi.
Bu klass pygame ga boğliq emas, şuning uçun uni şu yerning özida sinab köriş mumkin:
USTUNLAR, QATORLAR = 30, 20
YUQORI, PAST, CHAP, ONG = (0, -1), (0, 1), (-1, 0), (1, 0)
class Ilon:
def __init__(self) -> None:
x, y = USTUNLAR // 2, QATORLAR // 2
self.tana = [(x, y), (x - 1, y), (x - 2, y)] # birinçi element — boş
self.yonalish = ONG
self.keyingi_yonalish = ONG
self.osish = 0
@property
def bosh(self) -> tuple[int, int]:
return self.tana[0]
def buril(self, yangi: tuple[int, int]) -> None:
# Teskari tomonga birdan buriliş mumkin emas
dx, dy = self.yonalish
if yangi != (-dx, -dy):
self.keyingi_yonalish = yangi
def harakat(self) -> None:
self.yonalish = self.keyingi_yonalish
x, y = self.bosh
dx, dy = self.yonalish
self.tana.insert(0, (x + dx, y + dy))
if self.osish > 0:
self.osish -= 1 # dum joyida qoladi — ilon uzayadi
else:
self.tana.pop() # dumni olib taşlaymiz
def uzay(self) -> None:
self.osish += 1
def devorga_urildimi(self) -> bool:
x, y = self.bosh
return not (0 <= x < USTUNLAR and 0 <= y < QATORLAR)
def ozini_tishladimi(self) -> bool:
return self.bosh in self.tana[1:]
ilon = Ilon()
print("Boşida: ", ilon.tana)
ilon.harakat()
print("1 qadam: ", ilon.tana)
ilon.buril(CHAP) # teskari yönaliş — eʼtiborsiz qoldiriladi
ilon.harakat()
print("ÇAP bosildi:", ilon.tana)
ilon.buril(YUQORI)
ilon.uzay() # göyo ovqat yedi
ilon.harakat()
print("Yuqoriga + uzaydi:", ilon.tana)
print("Uzunligi:", len(ilon.tana))
Natija:
Boşida: [(15, 10), (14, 10), (13, 10)]
1 qadam: [(16, 10), (15, 10), (14, 10)]
ÇAP bosildi: [(17, 10), (16, 10), (15, 10)]
Yuqoriga + uzaydi: [(17, 9), (17, 10), (16, 10), (15, 10)]
Uzunligi: 4
keyingi_yonalish nima uçun kerak? Agar ilon öngga ketayotganda siz bitta kadr içida tez-tez ↑ va ← tugmalarini bossangiz, yönalişni darhol özgartiradigan sodda kod avval «yuqori», keyin «çap» ni qabul qiladi — ilon esa bir joyda turib orqasiga burilib, özini tişlab oladi. Biz yangi yönalişni faqat haqiqiy harakat yönalişiga nisbatan tekşiramiz va uni keyingi qadamda qöllaymiz, şu bois bunday xato bölmaydi.
@property dekoratori bosh ni metod emas, oddiy atribut kabi öqiş imkonini beradi: ilon.bosh() emas, ilon.bosh.
4-qadam: töqnaşuvlar
Öyin ikki holatda tugaydi:
- Devorga uriliş — boşning ustun yoki qator raqami maydondan çiqib ketsa. Python dagi
0 <= x < USTUNLARkabi zanjir soliştiriş buni juda öqimli qiladi. - Özini tişlaş — boş tananing qolgan qismidagi biror katakka töğri kelsa:
self.bosh in self.tana[1:].
Ikkalasini sinab köramiz (bu misol oldingi kodning qisqa nusxasidan foydalanadi):
USTUNLAR, QATORLAR = 30, 20
def devorga_urildimi(bosh: tuple[int, int]) -> bool:
x, y = bosh
return not (0 <= x < USTUNLAR and 0 <= y < QATORLAR)
def ozini_tishladimi(tana: list[tuple[int, int]]) -> bool:
return tana[0] in tana[1:]
print(devorga_urildimi((15, 10))) # maydon içida
print(devorga_urildimi((30, 10))) # öng devordan ötdi
print(devorga_urildimi((5, -1))) # yuqori devordan ötdi
# Boş (5, 5) katakka qaytib keldi — özini tişladi
tana = [(5, 5), (6, 5), (6, 6), (5, 6), (5, 5), (4, 5)]
print(ozini_tishladimi(tana))
print(ozini_tishladimi([(1, 1), (0, 1), (0, 2)]))
Natija:
False
True
True
True
False
5-qadam: ovqat
Ovqat ilonning tanasi ustida paydo bölmasligi kerak. Asl skriptda tasodifiy nuqta tanlanadi va bu tekşirilmaydi. Biz avval barça böş kataklar röyxatini tuzamiz va ulardan birini random.choice() bilan tanlaymiz. Tananing kataklarini set ga aylantirganimiz in tekşiruvini tezlaştiradi.
import random
USTUNLAR, QATORLAR = 30, 20
def yangi_ovqat(tana: list[tuple[int, int]]) -> tuple[int, int]:
"""Ilon tanasida bölmagan tasodifiy katakni qaytaradi."""
band = set(tana)
erkin = [
(x, y)
for x in range(USTUNLAR)
for y in range(QATORLAR)
if (x, y) not in band
]
return random.choice(erkin)
random.seed(2024) # natija har safar bir xil bölişi uçun (faqat sinov uçun)
tana = [(15, 10), (14, 10), (13, 10)]
for _ in range(3):
ovqat = yangi_ovqat(tana)
print("Ovqat:", ovqat, "| tanada emasmi?", ovqat not in tana)
Natija:
Ovqat: (24, 4) | tanada emasmi? True
Ovqat: (9, 6) | tanada emasmi? True
Ovqat: (29, 15) | tanada emasmi? True
6-qadam: hodisalar va boşqaruv
Klaviaturani öqiş uçun pygame.KEYDOWN hodisasidan foydalanamiz. Har bir tugmaga alohida if yoziş örniga luğat tuzamiz: tugma → yönaliş. Şunda strelkalar bilan birga W, A, S, D tugmalarini qöşiş bir qatorlik iş:
import pygame
YUQORI, PAST, CHAP, ONG = (0, -1), (0, 1), (-1, 0), (1, 0)
TUGMALAR = {
pygame.K_UP: YUQORI, pygame.K_w: YUQORI,
pygame.K_DOWN: PAST, pygame.K_s: PAST,
pygame.K_LEFT: CHAP, pygame.K_a: CHAP,
pygame.K_RIGHT: ONG, pygame.K_d: ONG,
}
class Oyin:
# ... boşqa metodlar ...
def hodisalar(self) -> None:
for hodisa in pygame.event.get():
if hodisa.type == pygame.QUIT:
self.chiqish()
if hodisa.type != pygame.KEYDOWN:
continue
if hodisa.key == pygame.K_ESCAPE:
self.chiqish()
if self.tugadi:
if hodisa.key in (pygame.K_SPACE, pygame.K_RETURN):
self.qayta_boshla()
elif hodisa.key in TUGMALAR:
self.ilon.buril(TUGMALAR[hodisa.key])
pygame.event.get() oxirgi kadrdan beri yiğilgan barça hodisalarni qaytaradi va navbatni tozalaydi. Uni har kadrda çaqiriş şart — aks holda operatsion tizim oynani «javob bermayapti» deb hisoblaydi.
7-qadam: çiziş va hisob matni
Çiziş uçun bir neçta oddiy funksiyalar yetarli:
| Funksiya | Vazifasi |
|---|---|
ekran.fill(rang) | butun ekranni bir rangga böyaydi |
pygame.draw.rect(ekran, rang, rect) | törtburçak çizadi |
pygame.draw.ellipse(ekran, rang, rect) | ellips (aylana) çizadi |
pygame.draw.line(ekran, rang, a, b) | çiziq çizadi |
shrift.render(matn, True, rang) | matndan rasm (Surface) yasaydi |
ekran.blit(sirt, joy) | bitta rasmni boşqasining ustiga qöyadi |
Ranglar (qizil, yashil, koʻk) kortejlari bilan beriladi, har biri 0 dan 255 gaça. Matn çiqariş ikki bosqiçli: avval render() matnni rasmga aylantiradi, keyin blit() uni ekranga qöyadi.
import pygame
KATAK = 20
FON = (18, 24, 27)
ILON_RANGI = (76, 175, 80)
BOSH_RANGI = (46, 125, 50)
OVQAT_RANGI = (229, 57, 53)
OQ = (240, 240, 240)
class Oyin:
# ... boşqa metodlar ...
def katak_rect(self, joy: tuple[int, int]) -> pygame.Rect:
x, y = joy
return pygame.Rect(x * KATAK, y * KATAK, KATAK, KATAK)
def chizish(self) -> None:
self.ekran.fill(FON)
# Ovqat — katakdan biroz kiçikroq doira
pygame.draw.ellipse(self.ekran, OVQAT_RANGI, self.katak_rect(self.ovqat).inflate(-4, -4))
# Ilon — boşi töqroq rangda, burçaklari yumaloq
for i, joy in enumerate(self.ilon.tana):
rang = BOSH_RANGI if i == 0 else ILON_RANGI
pygame.draw.rect(self.ekran, rang, self.katak_rect(joy).inflate(-2, -2), border_radius=5)
# Hisob
sirt = self.shrift.render(f"Hisob: {self.hisob}", True, OQ)
self.ekran.blit(sirt, (10, 8))
pygame.display.flip()
Rect.inflate(-2, -2) törtburçakni har tomondan biroz kiçraytiradi — şunda ilon bölaklari orasida ingiçka çiziq paydo böladi va tana bölaklarga bölingani körinadi.
8-qadam: tezlikning oşişi
Öyin qiziqarli bölişi uçun u asta-sekin qiyinlaşişi kerak. Tezlikni alohida özgaruvçida saqlaş örniga uni hisobdan hisoblaymiz: har 3 ta ovqatdan keyin ilon soniyasiga bir qadam tezroq yuradi, lekin maʼlum çegaradan oşmaydi. Bu qiymat soat.tick(self.tezlik) ga beriladi.
BOSHLANGICH_TEZLIK = 8
MAKS_TEZLIK = 20
HAR_NECHA_OVQATDA = 3
def tezlik(hisob: int) -> int:
return min(MAKS_TEZLIK, BOSHLANGICH_TEZLIK + hisob // HAR_NECHA_OVQATDA)
for hisob in (0, 2, 3, 10, 30, 50):
print(f"Hisob {hisob:>2} → soniyasiga {tezlik(hisob)} qadam")
Natija:
Hisob 0 → soniyasiga 8 qadam
Hisob 2 → soniyasiga 8 qadam
Hisob 3 → soniyasiga 9 qadam
Hisob 10 → soniyasiga 11 qadam
Hisob 30 → soniyasiga 18 qadam
Hisob 50 → soniyasiga 20 qadam
9-qadam: «Öyin tugadi» oynasi va qayta boşlaş
Asl skript öyin tugaşi bilan 3 soniya kutib, dasturdan çiqib ketardi. Bizda esa öyin self.tugadi bayroği bilan ikki holatda böladi: öynaş va tugadi. Tugagan holatda ilon harakatlanmaydi, ekranga yarim şaffof qora parda va xabar çiqadi, Space bosilsa esa qayta_boshla() hamma narsani boşlanğiç holatga qaytaradi. Rekord (eng yuqori hisob) esa qayta boşlaşda saqlanib qoladi.
import pygame
ENI, BALANDLIK = 600, 400
OQ = (240, 240, 240)
SARIQ = (255, 213, 79)
class Oyin:
# ... boşqa metodlar ...
def yangilash(self) -> None:
if self.tugadi:
return
self.ilon.harakat()
if self.ilon.devorga_urildimi() or self.ilon.ozini_tishladimi():
self.tugadi = True
self.rekord = max(self.rekord, self.hisob)
return
if self.ilon.bosh == self.ovqat:
self.hisob += 1
self.ilon.uzay()
self.ovqat = yangi_ovqat(self.ilon)
def tugadi_oynasi(self) -> None:
parda = pygame.Surface((ENI, BALANDLIK), pygame.SRCALPHA)
parda.fill((0, 0, 0, 170)) # oxirgi son — şaffoflik (0..255)
self.ekran.blit(parda, (0, 0))
self.matn("Öyin tugadi!", self.katta_shrift, SARIQ, (ENI // 2, BALANDLIK // 2 - 50))
self.matn(f"Hisobingiz: {self.hisob}", self.shrift, OQ, (ENI // 2, BALANDLIK // 2 + 5))
pygame.SRCALPHA bayroği bilan yaratilgan Surface şaffoflikni qöllab-quvvatlaydi: rangning törtinçi qiymati (alpha) qançalik kiçik bölsa, parda şunçalik şaffof böladi. Pardaning ostida öyinning oxirgi holati körinib turadi — öyinçi qayerda xato qilganini köradi.
Töliq kod
Barça qadamlarni birlaştiramiz. Kodni ilon_oyini.py fayliga saqlang va python ilon_oyini.py buyruği bilan işga tuşiring. Boşqaruv: strelkalar yoki W A S D, qayta boşlaş — Space, çiqiş — Esc.
import random
import sys
import pygame
# ---------- Sozlamalar ----------
KATAK = 20 # bitta katakning ölçami (piksel)
USTUNLAR, QATORLAR = 30, 20 # maydon ölçami (katak)
ENI, BALANDLIK = KATAK * USTUNLAR, KATAK * QATORLAR
BOSHLANGICH_TEZLIK = 8 # soniyasiga qadamlar soni
MAKS_TEZLIK = 20
HAR_NECHA_OVQATDA = 3 # har 3 ta ovqatda tezlik +1
# Ranglar (R, G, B)
FON = (18, 24, 27)
TOR = (28, 36, 40)
ILON_RANGI = (76, 175, 80)
BOSH_RANGI = (46, 125, 50)
OVQAT_RANGI = (229, 57, 53)
OQ = (240, 240, 240)
SARIQ = (255, 213, 79)
# Yönalişlar: (dx, dy)
YUQORI, PAST, CHAP, ONG = (0, -1), (0, 1), (-1, 0), (1, 0)
TUGMALAR = {
pygame.K_UP: YUQORI, pygame.K_w: YUQORI,
pygame.K_DOWN: PAST, pygame.K_s: PAST,
pygame.K_LEFT: CHAP, pygame.K_a: CHAP,
pygame.K_RIGHT: ONG, pygame.K_d: ONG,
}
class Ilon:
def __init__(self) -> None:
x, y = USTUNLAR // 2, QATORLAR // 2
self.tana = [(x, y), (x - 1, y), (x - 2, y)] # birinçi element — boş
self.yonalish = ONG
self.keyingi_yonalish = ONG
self.osish = 0
@property
def bosh(self) -> tuple[int, int]:
return self.tana[0]
def buril(self, yangi: tuple[int, int]) -> None:
# Teskari tomonga birdan buriliş mumkin emas
dx, dy = self.yonalish
if yangi != (-dx, -dy):
self.keyingi_yonalish = yangi
def harakat(self) -> None:
self.yonalish = self.keyingi_yonalish
x, y = self.bosh
dx, dy = self.yonalish
self.tana.insert(0, (x + dx, y + dy))
if self.osish > 0:
self.osish -= 1 # dum joyida qoladi — ilon uzayadi
else:
self.tana.pop() # dumni olib taşlaymiz
def uzay(self) -> None:
self.osish += 1
def devorga_urildimi(self) -> bool:
x, y = self.bosh
return not (0 <= x < USTUNLAR and 0 <= y < QATORLAR)
def ozini_tishladimi(self) -> bool:
return self.bosh in self.tana[1:]
def yangi_ovqat(ilon: Ilon) -> tuple[int, int]:
"""Ilon tanasida bölmagan tasodifiy katakni qaytaradi."""
band = set(ilon.tana)
erkin = [
(x, y)
for x in range(USTUNLAR)
for y in range(QATORLAR)
if (x, y) not in band
]
return random.choice(erkin)
class Oyin:
def __init__(self) -> None:
pygame.init()
self.ekran = pygame.display.set_mode((ENI, BALANDLIK))
pygame.display.set_caption("Ilon öyini")
self.soat = pygame.time.Clock()
self.shrift = pygame.font.Font(None, 32)
self.katta_shrift = pygame.font.Font(None, 64)
self.rekord = 0
self.qayta_boshla()
def qayta_boshla(self) -> None:
self.ilon = Ilon()
self.ovqat = yangi_ovqat(self.ilon)
self.hisob = 0
self.tugadi = False
@property
def tezlik(self) -> int:
return min(MAKS_TEZLIK, BOSHLANGICH_TEZLIK + self.hisob // HAR_NECHA_OVQATDA)
# ---------- 1. Hodisalar ----------
def hodisalar(self) -> None:
for hodisa in pygame.event.get():
if hodisa.type == pygame.QUIT:
self.chiqish()
if hodisa.type != pygame.KEYDOWN:
continue
if hodisa.key == pygame.K_ESCAPE:
self.chiqish()
if self.tugadi:
if hodisa.key in (pygame.K_SPACE, pygame.K_RETURN):
self.qayta_boshla()
elif hodisa.key in TUGMALAR:
self.ilon.buril(TUGMALAR[hodisa.key])
# ---------- 2. Holatni yangilaş ----------
def yangilash(self) -> None:
if self.tugadi:
return
self.ilon.harakat()
if self.ilon.devorga_urildimi() or self.ilon.ozini_tishladimi():
self.tugadi = True
self.rekord = max(self.rekord, self.hisob)
return
if self.ilon.bosh == self.ovqat:
self.hisob += 1
self.ilon.uzay()
self.ovqat = yangi_ovqat(self.ilon)
# ---------- 3. Çiziş ----------
def katak_rect(self, joy: tuple[int, int]) -> pygame.Rect:
x, y = joy
return pygame.Rect(x * KATAK, y * KATAK, KATAK, KATAK)
def matn(self, yozuv: str, shrift: pygame.font.Font, rang, markaz) -> None:
sirt = shrift.render(yozuv, True, rang)
self.ekran.blit(sirt, sirt.get_rect(center=markaz))
def chizish(self) -> None:
self.ekran.fill(FON)
for x in range(0, ENI, KATAK):
pygame.draw.line(self.ekran, TOR, (x, 0), (x, BALANDLIK))
for y in range(0, BALANDLIK, KATAK):
pygame.draw.line(self.ekran, TOR, (0, y), (ENI, y))
pygame.draw.ellipse(self.ekran, OVQAT_RANGI, self.katak_rect(self.ovqat).inflate(-4, -4))
for i, joy in enumerate(self.ilon.tana):
rang = BOSH_RANGI if i == 0 else ILON_RANGI
pygame.draw.rect(self.ekran, rang, self.katak_rect(joy).inflate(-2, -2), border_radius=5)
hisob_sirti = self.shrift.render(f"Hisob: {self.hisob} Rekord: {self.rekord}", True, OQ)
self.ekran.blit(hisob_sirti, (10, 8))
if self.tugadi:
parda = pygame.Surface((ENI, BALANDLIK), pygame.SRCALPHA)
parda.fill((0, 0, 0, 170))
self.ekran.blit(parda, (0, 0))
markaz_x, markaz_y = ENI // 2, BALANDLIK // 2
self.matn("Öyin tugadi!", self.katta_shrift, SARIQ, (markaz_x, markaz_y - 50))
self.matn(f"Hisobingiz: {self.hisob}", self.shrift, OQ, (markaz_x, markaz_y + 5))
self.matn("Space — qayta boşlaş, Esc — çiqiş", self.shrift, OQ,
(markaz_x, markaz_y + 45))
pygame.display.flip()
def chiqish(self) -> None:
pygame.quit()
sys.exit()
def ishga_tushir(self) -> None:
while True:
self.hodisalar()
self.yangilash()
self.chizish()
self.soat.tick(self.tezlik)
if __name__ == "__main__":
Oyin().ishga_tushir()
Kodning tuzilişiga yana bir bor eʼtibor bering:
- Konstantalar dastur boşida — ölçam, rang va tezlikni bir joyda özgartirasiz.
Ilonklassi faqat ilon mantiqini biladi, ekran haqida heç narsa bilmaydi. Şuning uçun unipygamesiz sinab köra oldik.Oyinklassi öyin siklini uçta aniq metodga ajratadi:hodisalar(),yangilash(),chizish().pygame.time.Clocktezlikni barqaror qiladi, tezlik esa hisobga qarab oşadi.
Xulosa
- Öyin sikli uç bosqiçdan iborat: hodisalar → yangilaş → çiziş;
Clock.tick()uning tezligini belgilaydi. - Holatni kataklarda saqlab, faqat çizişda piksellarga aylantiriş hisob-kitobni soddalaştiradi.
- Ilon harakati: boşga katak qöşiş va dumdan olib taşlaş; uzayiş — dumni bir marta qoldiriş.
- Yönalişni
(dx, dy)kortej sifatida saqlaşifzanjirlarini qisqartiradi. - Töqnaşuv: maydon çegarasini va boşning tanaga tuşişini tekşiriş.
- Mantiqni (
Ilon) çizişdan (Oyin) ajratiş kodni sinaş va kengaytirişni osonlaştiradi. importlarni unutmang:NameErrorköpinça şu sababli çiqadi.
Maşqlar
- Devorsiz rejim yozing: ilon maydonning bir çetidan çiqsa, qarama-qarşi çetidan kirsin. Buning uçun boş koordinatasini
%(qoldiq) operatori bilan hisoblaydiganoralash(joy)funksiyasini yozing.
Yeçim
USTUNLAR, QATORLAR = 30, 20
def oralash(joy: tuple[int, int]) -> tuple[int, int]:
x, y = joy
return x % USTUNLAR, y % QATORLAR
print(oralash((30, 5))) # öng çetdan çiqdi → çapdan kiradi
print(oralash((-1, 5))) # çap çetdan çiqdi → öngdan kiradi
print(oralash((10, -1))) # yuqoridan çiqdi → pastdan kiradi
print(oralash((7, 7))) # maydon içida — özgarmaydi
Ilon.harakat() içida yangi boşni oralash((x + dx, y + dy)) qilib qöşsangiz va devorga_urildimi() tekşiruvini olib taşlasangiz, devorsiz rejim tayyor.
- Pauza qöşing:
Ptugmasi bosilganda öyin töxtasin, yana bosilganda davom etsin. Pauza paytida ekranda «Pauza» yozuvi çiqsin.
Yeçim
import pygame
OQ = (240, 240, 240)
ENI, BALANDLIK = 600, 400
class Oyin:
# __init__ va qayta_boşla() içida: self.pauza = False
def hodisalar(self) -> None:
for hodisa in pygame.event.get():
if hodisa.type == pygame.QUIT:
self.chiqish()
if hodisa.type == pygame.KEYDOWN and hodisa.key == pygame.K_p and not self.tugadi:
self.pauza = not self.pauza
# ... qolgan tugmalar avvalgidek ...
def yangilash(self) -> None:
if self.tugadi or self.pauza:
return
# ... qolgani avvalgidek ...
def pauza_yozuvi(self) -> None:
# çiziş() içida, flip() dan oldin çaqiriladi
if self.pauza:
self.matn("Pauza", self.katta_shrift, OQ, (ENI // 2, BALANDLIK // 2))
- Rekordni faylga saqlang: öyin tugaganda rekord
rekord.txtfayliga yozilsin, dastur işga tuşganda esa öqilsin. Fayl bölmasa yoki buzilgan bölsa, rekord 0 bölsin.pathlibdan foydalaning.
Yeçim
from pathlib import Path
REKORD_FAYLI = Path("rekord.txt")
def rekordni_oqi() -> int:
try:
return int(REKORD_FAYLI.read_text(encoding="utf-8"))
except (FileNotFoundError, ValueError):
return 0
def rekordni_yoz(qiymat: int) -> None:
REKORD_FAYLI.write_text(str(qiymat), encoding="utf-8")
REKORD_FAYLI.unlink(missing_ok=True) # sinov uçun toza holatdan boşlaymiz
print("Boşida:", rekordni_oqi())
rekordni_yoz(17)
print("Yozilgandan keyin:", rekordni_oqi())
REKORD_FAYLI.write_text("buzilgan", encoding="utf-8")
print("Buzilgan fayl:", rekordni_oqi())
Oyin.__init__ da self.rekord = rekordni_oqi(), öyin tugaganda esa rekordni_yoz(self.rekord) çaqiriladi.
- Maxsus «oltin olma» qöşing: har 5-ovqat oddiy emas, oltin bölsin va 3 oçko bersin (ilon esa 3 katakka uzaysin). Avval mantiqni
pygamesiz yozib sinab köring.
Yeçim
def ovqat_turi(yeyilgan_soni: int) -> str:
"""Navbatdagi ovqat turini qaytaradi: har 5-si oltin."""
return "oltin" if (yeyilgan_soni + 1) % 5 == 0 else "oddiy"
OCHKO = {"oddiy": 1, "oltin": 3}
hisob = 0
uzunlik = 3
for yeyilgan in range(10):
tur = ovqat_turi(yeyilgan)
hisob += OCHKO[tur]
uzunlik += OCHKO[tur]
print(f"{yeyilgan + 1:>2}-ovqat: {tur:<6} hisob={hisob:>2} uzunlik={uzunlik}")
pygame versiyasida self.ovqat_turi ni saqlang, oltin olmani SARIQ rangda çizing va yeyilganda self.ilon.uzay() ni OCHKO[tur] marta çaqiring.