Равзана ва расмкашӣ
Ин барои чӣ лозим аст
pygame — китобхона барои сохтани бозиҳои 2D дар Python: равзана, расмкашӣ, клавиатура ва муш, садо, спрайтҳо. Бо он бозиҳои аркадӣ, муаммоҳо, лоиҳаҳои таълимӣ ва прототипҳои зуди бозиҳоро менависанд, ки баъд ба муҳаррикҳои калон мегузаранд.
Ҳар бозӣ аз ду чиз сар мешавад: равзана, ки дар он чизе намоён аст, ва расмкашӣ — замина, шаклҳо, майдон. Дар ин мавзӯъ мебинем, ки даври бозӣ чӣ тавр сохта шудааст, координатаҳо ва рангҳо чӣ тавр ҳисоб мешаванд ва чӣ тавр росткунҷа, доира ва хат кашидан мумкин аст.
Дар ин курс шумо бозиҳоро аз сифр наменависед, балки бозиҳои тайёрро ислоҳ мекунед: ҳар масъала — барномаи коркунанда аст, ки дар он қисми хурде вайрон шудааст. Барои ёфтани хато бояд фаҳмед, ки коди атроф чӣ тавр сохта шудааст — ҳамин тавр хондани барномаҳои дигаронро меомӯзанд.
Қадами 1. Хурдтарин бозӣ
Равзана-ро pygame.display.set_mode((бар, баландӣ)) месозад. Он инчунин экран-ро бармегардонад — ҳамон чизе, ки дар он расм мекашем.
Даври бозӣ (game loop) — while-и беохир аст, ки як гузариши он як кадр аст. Дар як кадр барнома се кор мекунад: ҳодисаҳоро коркард мекунад, ҳолати бозиро нав мекунад ва тасвирро аз нав мекашад.
import pygame
pygame.init() # модулҳои pygame-ро омода кардан
screen = pygame.display.set_mode((640, 480)) # равзанаи 640 × 480 пиксел
pygame.display.set_caption("Бозии ман") # сарлавҳаи равзана
clock = pygame.time.Clock()
running = True
while running: # даври бозӣ: як гузариш — як кадр
for event in pygame.event.get(): # 1. ҳодисаҳо: тугмаҳо, муш, пӯшидани равзана
if event.type == pygame.QUIT:
running = False
screen.fill((30, 30, 40)) # 2. кадрро аз нав мекашем
pygame.draw.circle(screen, (255, 200, 0), (320, 240), 50)
pygame.display.flip() # 3. кашидашударо нишон медиҳем
clock.tick(60) # на зиёда аз 60 кадр дар сония
pygame.quit()
Равзанаи торик бо доираи зард дар марказ кушода мешавад; он бо салиб пӯшида мешавад.
- Ҳодиса (event) — хабари «чизе рух дод»: тугма пахш шуд, муш ҳаракат кард, салиб пахш шуд. pygame онҳоро ба навбат мегузорад,
pygame.event.get()бошад ҳамаи он чиро, ки аз кадри гузашта ҷамъ шудааст, мегирад. pygame.QUIT— ҳодисаи «салиб пахш шуд». Равзана худ ба худ пӯшида намешавад: барномаи шумо қарор медиҳад, ки аз давр барояд.clock.tick(60)даврро суст мекунад, то дар як сония на зиёда аз 60 кадр бошад. Бе он дар компютери тез бозӣ парвоз мекард, дар компютери суст бошад — мехазид.
Қадами 2. Координатаҳо
Экран — тӯри пикселҳо (нуқтаҳо) аст. Ҳар пиксел координатаҳои (x, y) дорад:
- (0, 0) — кунҷи чапи боло;
- x ба рост меафзояд, y ба поён меафзояд (на ба боло, мисли математика!);
- дар равзанаи 640 × 480 пиксели рости поён — (639, 479) аст: ҳисоб аз сифр сар мешавад.
w, h = 640, 480
print("марказ:", (w // 2, h // 2))
print("кунҷи рости поён:", (w - 1, h - 1))
Натиҷа:
марказ: (320, 240)
кунҷи рости поён: (639, 479)
Аз ин қоида: барои он ки объект болотар барояд, y-и онро кам кардан лозим.
Қадами 3. Рангҳо
Ранг — се адад аз 0 то 255: дар он чӣ қадар сурх, сабз ва кабуд ҳаст (RGB, аз Red, Green, Blue). Ин мисли омехтани се фонус аст: ҳама хомӯш — сиёҳ, ҳама бо қувваи пурра — сафед.
| Ранг | RGB |
|---|---|
| сиёҳ | (0, 0, 0) |
| сафед | (255, 255, 255) |
| сурх | (255, 0, 0) |
| сабз | (0, 255, 0) |
| кабуд | (0, 0, 255) |
| зард (сурх + сабз) | (255, 255, 0) |
| хокистарӣ | (128, 128, 128) |
Рангҳоро дар доимиҳо бо ҳарфҳои калон нигоҳ доштан қулай аст: WHITE = (255, 255, 255). Рангҳо бо ном ҳам ҳастанд:
import pygame
print(pygame.Color("orange"))
Натиҷа:
(255, 165, 0, 255)
Адади чорум — алфа, ношаффофӣ: 255 маънои «комилан ношаффоф»-ро дорад. Дар масъалаҳо ба мо танҳо се адади аввал лозим аст, бинобар ин онҳоро бо буриши [:3] мегиранд.
Қадами 4. Сатҳ
Ҳама чизе, ки дар он расм мекашанд, — сатҳ (Surface) аст: росткунҷае аз пикселҳо дар хотира. Экране, ки set_mode баргардонд, низ сатҳ аст. Сатҳи худ, ноаёнро ҳам сохтан мумкин — барои он ҳатто равзана лозим нест:
import pygame
s = pygame.Surface((4, 3)) # бараш 4 пиксел, баландиаш 3
print(s.get_size()) # андоза
print(s.get_at((0, 0))[:3]) # ранги нуқта: сатҳи нав сиёҳ аст
s.fill((255, 0, 0)) # пурра бо сурх рехтан
print(s.get_at((3, 2))[:3])
Натиҷа:
(4, 3)
(0, 0, 0)
(255, 0, 0)
get_size()— ҷуфти(бар, баландӣ); аксар вақтw, h = surface.get_size()менависанд.get_at((x, y))— ранги як пиксел. Санҷишҳо дар масъалаҳо маҳз ҳамин тавр кор мекунанд: дар сатҳи ноаён мекашанд ва ранги нуқтаҳоро мехонанд.fill(color)— тамоми сатҳро ранг кардан. Одатан ин амали аввали кадр аст: тасвири кӯҳнаро пок кардан.
Қадами 5. Росткунҷа
pygame.draw.rect(surface, color, (x, y, w, h)) росткунҷаро ранг мекунад: кунҷи чапи боло (x, y), бар w, баландӣ h. Пикселҳо бо x аз x то x + w - 1 ва бо y аз y то y + h - 1 ранг мешаванд — сарҳади рост ва поён дохил намешаванд, мисли range.
import pygame
s = pygame.Surface((10, 10))
pygame.draw.rect(s, (255, 255, 255), (2, 3, 4, 2)) # x: 2..5, y: 3..4
for point in [(2, 3), (5, 4), (6, 4), (2, 5)]:
print(point, s.get_at(point)[:3])
Натиҷа:
(2, 3) (255, 255, 255)
(5, 4) (255, 255, 255)
(6, 4) (0, 0, 0)
(2, 5) (0, 0, 0)
Агар ҳамчун аргументи панҷум ғафсиро диҳед, танҳо чорчӯба кашида мешавад:
import pygame
s = pygame.Surface((10, 10))
pygame.draw.rect(s, (255, 255, 255), (0, 0, 10, 10), 2) # чорчӯба бо ғафсии 2
print(s.get_at((1, 5))[:3], s.get_at((5, 5))[:3])
Натиҷа:
(255, 255, 255) (0, 0, 0)
Пиксели (1, 5) ба чорчӯба афтод, марказ бошад холӣ монд.
Қадами 6. Доира ва хат
pygame.draw.circle(surface, color, (cx, cy), radius) — доираи рангкардашуда бо маркази (cx, cy). Нуқта дар дохили доира аст, агар масофаи он то марказ аз радиус хурдтар бошад.
import pygame
s = pygame.Surface((100, 100))
pygame.draw.circle(s, (0, 200, 0), (50, 50), 10)
print(s.get_at((50, 45))[:3]) # 5 пиксел аз марказ — дар дохил
print(s.get_at((50, 65))[:3]) # 15 пиксел аз марказ — берун
Натиҷа:
(0, 200, 0)
(0, 0, 0)
Нуқтаҳоеро, ки маҳз дар сарҳади шакл ҳастанд, беҳтар насанҷед: дар он ҷо яклухткунӣ ҳал мекунад.
pygame.draw.line(surface, color, (x1, y1), (x2, y2)) — порчаи ғафсиаш 1 пиксел байни ду нуқта, ҳар ду нӯг дохил. Хати уфуқӣ дар баландии y аз тамоми сатҳи бараш w — ин (0, y) → (w - 1, y) аст.
import pygame
s = pygame.Surface((8, 8))
pygame.draw.line(s, (90, 90, 90), (0, 3), (7, 3))
print([s.get_at((x, 3))[0] for x in range(8)])
print(s.get_at((4, 4))[:3])
Натиҷа:
[90, 90, 90, 90, 90, 90, 90, 90]
(0, 0, 0)
Қадами 7. Тартиби расмкашӣ
Ҳар расми навбатӣ болои расми пешина меафтад, мисли ранг дар рӯи матоъ: он чи баъдтар кашида шуд, он чиро, ки пештар буд, мепӯшонад.
import pygame
s = pygame.Surface((10, 10))
s.fill((0, 0, 255)) # заминаи кабуд
pygame.draw.rect(s, (255, 0, 0), (0, 0, 6, 6)) # мураббаи сурх
pygame.draw.rect(s, (0, 255, 0), (3, 3, 6, 6)) # сабз аз болои он
print(s.get_at((1, 1))[:3], s.get_at((4, 4))[:3], s.get_at((9, 9))[:3])
Натиҷа:
(255, 0, 0) (0, 255, 0) (0, 0, 255)
Дар нуқтаи (4, 4) ҳар ду мураббаъ бурида мешаванд — сабз ғолиб омад, зеро он охирин кашида шуд. Бинобар ин тартиб чунин аст: аввал замина, баъд шаклҳои калон, баъд ҷузъиёти хурд. Агар аввал шаклро кашем ва баъд fill-ро даъват кунем, рангрезӣ ҳамаашро пок мекунад.
Мисоли мураккабтар — қатори мураббаъҳо дар давра, мисли катакҳои майдон:
import pygame
s = pygame.Surface((50, 10))
for i in range(5):
color = (255, 255, 255) if i % 2 == 0 else (200, 0, 0)
pygame.draw.rect(s, color, (i * 10, 0, 10, 10))
print([s.get_at((i * 10 + 5, 5))[0] for i in range(5)])
Натиҷа:
[255, 200, 255, 200, 255]
Дар дохил чӣ тавр кор мекунад
Чаро pygame.display.flip() лозим аст? Расм на мустақиман ба монитор, балки ба буфери ноаён — пораи хотира ба андозаи равзана кашида мешавад. Вақте ки кадр кашида мешавад, тамошобин кадри пешинаро мебинад. flip() кадри тайёрро якбора пурра нишон медиҳад. Ин мисли рассомест, ки дар матои дуюм расм мекашад ва онро танҳо баъди тамом кардан нишон медиҳад: тамошобин тасвири нимтайёрро намебинад ва экран намеларзад.
Ҳар кадр аз сифр кашида мешавад: fill гузаштаро пок мекунад, баъд ҳама чиз дар ҷойҳои нав аз нав кашида мешавад. Шаст чунин кадр дар як сонияро чашм ҳамчун ҳаракат қабул мекунад.
Масъалаҳо чӣ тавр санҷида мешаванд
Дар ҳар барнома даври бозӣ зери if __name__ == "__main__": меистад. Вақте ки шумо «Дар компютери худ иҷро кардан»-ро пахш мекунед, он кор мекунад ва равзана кушода мешавад. Ҳангоми «Фиристодан» санҷиш коди шуморо бе даври бозӣ import мекунад, функсияҳои расмкаширо дар сатҳи ноаён даъват мекунад ва ранги нуқтаҳои лозимиро мебинад. Бинобар ин маҳз функсияро ислоҳ кардан лозим аст, на даврро.
Хатоҳои маъмул
1. pygame.QUIT коркард нашудааст. Даври while True: бе санҷиши ҳодисаҳо — равзана бо салиб пӯшида намешавад ва «овезон» мешавад. Ислоҳ: дар ҳар кадр pygame.event.get()-ро аз назар гузаронед ва бо pygame.QUIT аз давр бароед.
2. flip() фаромӯш шудааст. Ҳама чиз кашида шудааст, вале равзана холӣ ё сиёҳ аст: кадр дар буфери ноаён монд. Ислоҳ: pygame.display.flip() дар охири ҳар кадр.
3. Бар ва баландӣ омехта шудаанд. w, h = surface.get_size() — аввал бар. Агар хато карда w-ро дар ҷое истифода баред, ки баландӣ лозим аст, дар равзанаи мураббаъ ҳама дуруст менамояд, дар равзанаи васеъ ё баланд бошад — вайрон мешавад. Кодатонро дар андозаҳои ғайримураббаъ санҷед.
4. Тартиби нодурусти расмкашӣ.
pygame.draw.circle(screen, (255, 200, 0), (320, 240), 50)
screen.fill((30, 30, 40)) # рангрезӣ доираро пок кард
Заминаи холӣ ҳосил мешавад. Ислоҳ: аввал fill, баъд шаклҳо.
5. Хатои як воҳид дар сарҳадҳо. Росткунҷаи (0, 0, 5, 3) x-ро аз 0 то 4 ранг мекунад, на то 5. Пиксели охирини равзанаи бараш w — w - 1 аст.
Варақаи ёддошт
| Чӣ | Код |
|---|---|
| омода кардани pygame | pygame.init() |
| равзана ва экран | screen = pygame.display.set_mode((w, h)) |
| сарлавҳа | pygame.display.set_caption("…") |
| ҳодисаҳои кадр | for event in pygame.event.get(): |
| пӯшидани равзана | event.type == pygame.QUIT |
| нишон додани кадр | pygame.display.flip() |
| маҳдуд кардани FPS | clock = pygame.time.Clock(), clock.tick(60) |
| сатҳи худ | pygame.Surface((w, h)) |
| андоза | w, h = surface.get_size() |
| рангрезӣ | surface.fill(color) |
| росткунҷа | pygame.draw.rect(surface, color, (x, y, w, h)) |
| чорчӯба | pygame.draw.rect(surface, color, (x, y, w, h), ғафсӣ) |
| доира | pygame.draw.circle(surface, color, (cx, cy), r) |
| хат | pygame.draw.line(surface, color, (x1, y1), (x2, y2)) |
| ранги нуқта | surface.get_at((x, y))[:3] |
- (0, 0) — кунҷи чапи боло, y ба поён меафзояд.
- Он чи баъдтар кашида шуд, он чиро, ки пештар кашида шуд, мепӯшонад.