Гузариш ба мундариҷа
Мавзӯи 4 аз 6

Бархӯрдҳо

Ин барои чӣ лозим аст

Дар ҳар бозӣ фаҳмидан лозим аст, ки чӣ бо чӣ бархӯрд: бозингар бо танга, тир бо душман, тӯб бо девор, курсор бо тугма. Қариб тамоми мантиқи бозӣ аз ин вобаста аст: хол додан, душманро хориҷ кардан, тӯбро баргардонидан, тугмаи менюро пахш кардан.

Шакли аслии қаҳрамон метавонад мураккаб бошад, вале дар аксари бозиҳои 2D бархӯрдҳоро аз рӯи росткунҷаҳо-и атрофи объектҳо месанҷанд — ин тез ва одатан ба қадри кофӣ дақиқ аст. Дар pygame инро Rect карда метавонад. Дар ин мавзӯъ афтидани нуқта, буриши росткунҷаҳо, ҷустуҷӯи бархӯрдҳо дар рӯйхат, хориҷкунии бехатар ва бозхӯриро мебинем.

Қадами 1. Нуқта дар дохили росткунҷа

rect.collidepoint(x, y) (ё rect.collidepoint((x, y))) ҷавоб медиҳад, ки оё нуқта дар дохили росткунҷа аст.

import pygame
button = pygame.Rect(100, 50, 200, 60)
print(button.collidepoint(150, 80))     # дар дохил
print(button.collidepoint((300, 80)))   # x = 300 — аллакай берун
print(button.collidepoint(100, 50))     # кунҷи чапи боло — дар дохил

Натиҷа:

True
False
True

Росткунҷаи (x, y, w, h) нуқтаҳоро бо x аз x то x + w - 1 ва y аз y то y + h - 1 дар бар мегирад. Пас, right ва bottom — координатаи аввал баъд аз росткунҷа мебошанд. Сарҳади чап ва боло дохил мешаванд, рост ва поён — не, мисли range(x, x + w).

import pygame
r = pygame.Rect(0, 0, 10, 10)
print([x for x in range(-1, 12) if r.collidepoint(x, 5)])

Натиҷа:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Агар бо даст тавассути <= бо right муқоиса кунед, маҳз ба як пиксел хато кардан осон аст — ва чунин нуқтаҳоро тестҳо пеш аз ҳама месанҷанд.

Қадами 2. Муш

Мавқеи курсорро pygame бо ду роҳ хабар медиҳад:

  • зеркунӣ — ҳодисаи pygame.MOUSEBUTTONDOWN, он майдони event.pos дорад — ҷуфти (x, y), ки дар куҷо зер карданд;
  • мавқеи ҷорӣ — pygame.mouse.get_pos(), онро ҳар кадр санҷидан қулай аст, масалан, барои равшан кардани тугма ҳангоми наздик овардани курсор.
for event in pygame.event.get():
    if event.type == pygame.MOUSEBUTTONDOWN and button.collidepoint(event.pos):
        print("тугмаро пахш карданд")
hover = button.collidepoint(pygame.mouse.get_pos())   # курсор болои тугма аст?

Координатаҳои муш мисли ҳама чизи дигар ҳисоб карда мешаванд: аз кунҷи чапи болои равзана.

Қадами 3. Ду росткунҷа

  • a.colliderect(b) — оё a ва b ақаллан бо як пиксел бурида мешаванд;
  • a.contains(b) — оё b пурра дар дохили a аст.
import pygame
player = pygame.Rect(0, 0, 20, 20)
print(player.colliderect(pygame.Rect(15, 15, 10, 10)))   # 5 пиксел медарояд
print(player.colliderect(pygame.Rect(20, 0, 10, 10)))    # танҳо аз рост мерасад
print(player.contains(pygame.Rect(5, 5, 4, 4)))          # пурра дар дохил

Натиҷа:

True
False
True

Росткунҷаҳое, ки танҳо бо тарафҳо мерасанд, бурида намешаванд: онҳо пиксели умумӣ надоранд. Дар ҳолати дуюм player сутунҳои 0..19-ро ишғол мекунад, росткунҷаи дуюм бошад — 20..29.

Қадами 4. Як бо бисёр

Вақте ки объектҳо бисёранд (тангаҳо, душманон, хиштҳо), онҳоро дар рӯйхат нигоҳ медоранд. Ҳар якро дар давр санҷидан мумкин аст, вале Rect методҳои тайёр дорад:

  • player.collidelist(rects) — рақами аввалин росткунҷаи рӯйхат, ки бо он буриш ҳаст, ё -1, агар бо ҳеҷ кадом набошад;
  • player.collidelistall(rects) — рӯйхати рақамҳои ҳамаи буридашавандаҳо.
import pygame
player = pygame.Rect(0, 0, 20, 20)
enemies = [pygame.Rect(100, 0, 5, 5), pygame.Rect(18, 18, 5, 5), pygame.Rect(0, 10, 5, 5)]
print(player.collidelist(enemies))
print(player.collidelistall(enemies))
print(player.collidelist([pygame.Rect(50, 50, 1, 1)]))

Натиҷа:

1
[1, 2]
-1

collidelist дар аввалин ёфташуда меистад — қулай аст, агар худи далели бархӯрд муҳим бошад. Агар ҳамаи бархӯрдҳои кадрро коркард кардан лозим бошад, collidelistall ё давраи оддӣ лозим аст.

Қадами 5. Хориҷ кардан аз рӯйхат

Тангаҳои ҷамъшуда ва душманони задашударо аз рӯйхат хориҷ кардан лозим аст. Чунин менамояд, ки рост дар давр хориҷ кардан кифоя аст, вале ин тавр кардан мумкин нест:

nums = [1, 2, 2, 3]
for n in nums:
    if n == 2:
        nums.remove(n)
print(nums)

Натиҷа:

[1, 2, 3]

Як дутар монд! Давр аз рӯи рақамҳо меравад: унсури рақами 1-ро хориҷ карда, мо дутари навбатиро ба ҷои он кӯчондем, давр бошад аллакай ба рақами 2 гузашт ва аз болои он ҷаҳид. Дар бозӣ ин чунин менамояд: тангаҳое, ки тӯда хобидаанд, на ҳама ҷамъ мешаванд.

Роҳи боэътимод — сохтани рӯйхати нав аз унсурҳое, ки мемонанд:

bullets = [5, 120, 40, 300]
bullets = [b for b in bullets if b < 100]   # танҳо онҳоеро, ки дар экрананд, нигоҳ медорем
print(bullets)

Натиҷа:

[5, 40]

Қадами 6. Ҳамон рӯйхат ё рӯйхати нав

Нозукие ҳаст, вақте ки рӯйхатро дар дохили функсия тағйир медиҳанд. Номи параметр дар дохили функсия — «тамғаи» дуюм ба ҳамон як рӯйхат аст. Агар ба ин ном рӯйхати нав таъин кунед, тамға мегузарад, рӯйхати берунӣ бошад ҳамон тавр мемонад. Навишти lst[:] = … бошад мундариҷаи ҳамон як рӯйхатро иваз мекунад.

def clear_new(lst):
    lst = []            # номи нав дар дохили функсия, берун ҳеҷ чиз тағйир намеёбад

def clear_same(lst):
    lst[:] = []         # худи ҳамон рӯйхатро тағйир медиҳем

a = [1, 2, 3]
clear_new(a)
print(a)
clear_same(a)
print(a)

Натиҷа:

[1, 2, 3]
[]

Методҳои append, remove, pop ва таъини буриш lst[:] = … рӯйхати додашударо тағйир медиҳанд. lst = …-и оддӣ — не.

Қадами 7. Бозхӯрӣ

Тӯб бо суръати [vx, vy] парвоз мекунад. Бозхӯрӣ — тағйири самт аз рӯи як тир: аз девори амудӣ vx тағйир меёбад, аз девори уфуқӣ — vy.

Роҳи соддатарин — аломатро чаппа кардан: vx = -vx. Вале он доме дорад. Агар тӯб дар паси девор монда бошад (тез даромад ё онро дар аввал он ҷо гузоштанд), дар кадри навбатӣ он ҳанӯз дар паси девор аст ва аломат боз чаппа мешавад. Тӯб дар назди девор «меларзад».

x, vx = -3, -4          # тӯб аллакай аз девори x = 0 чаптар аст
for frame in range(4):
    x += vx
    if x <= 0:
        vx = -vx        # танҳо аломатро чаппа мекунем
    print(x, vx)

Натиҷа:

-7 4
-3 -4
-7 4
-3 -4

Тӯб ҳамин тавр берун набаромад. Боэътимодтар аст, ки самтро ошкоро таъин кунем: дар назди девори чап суръат бояд мусбат шавад, новобаста аз он ки чӣ буд. Барои ин abs — қимати мутлақи адад ҳаст:

print(abs(-4), abs(4), -abs(4), -abs(-4))

Натиҷа:

4 4 -4 -4

abs(v) — ҳамеша «плюс», -abs(v) — ҳамеша «минус», новобаста аз аломати аввала.

Бо ракетка ҳам ҳамин гуна аст: агар тӯбро ҳангоми ҳар гуна буриш баргардонем, тӯбе, ки ракеткаро аз паҳлӯ зад, ҳар кадр дар дохили он чаппа мешавад. Бинобар ин танҳо вақте бармегардонанд, ки тӯб ба сӯи ракетка парвоз кунад.

Дар дохил чӣ тавр кор мекунад

Санҷиши буриши ду росткунҷа — чор муқоиса аст. Росткунҷаҳо бурида намешаванд, агар яке пурра аз дигаре чаптар, ростар, болотар ё поёнтар бошад. Дар ҳамаи ҳолатҳои дигар — бурида мешаванд:

def overlap(a, b):
    return a.left < b.right and b.left < a.right and a.top < b.bottom and b.top < a.bottom

Бинобар ин colliderect хеле тез аст ва дар бозӣ ҳар кадр садҳо объектро санҷидан мумкин аст. Он ки муқоисаҳо қатъӣ (<) мебошанд, мефаҳмонад, ки чаро расидан бо тарафҳо буриш нест.

Хатоҳои маъмул

1. Хатои як пиксел дар санҷиши дастӣ. x <= button.right ба тугма сутуни зиёдатиро аз рост дохил мекунад. collidepoint-ро истифода баред — он сарҳадҳоро дуруст ҳисоб мекунад.

2. x ва y омехта шудаанд. collidepoint(pos[1], pos[0]) — нуқтаи тамоман дигар санҷида мешавад. Дар тугмаи мураббаъ хато шояд фавран зоҳир нашавад.

3. Хориҷкунӣ дар давр аз рӯи ҳамон рӯйхат. Қисми унсурҳо гузаронида мешаванд. Рӯйхати нав созед ё унсурҳои хориҷшавандаро алоҳида ҷамъ кунед.

4. lst = … дар дохили функсия. Берун рӯйхат тағйир намеёбад — тангаҳо «ҷамъ шуданд», вале дар майдон монданд. lst[:] = … ё методҳои рӯйхат лозим аст.

5. v = -v ҳангоми бозхӯрӣ. Тӯб дар паси девор меларзад. Самтро тавассути abs(v) / -abs(v) таъин кунед.

Варақаи ёддошт

Чӣ Код
нуқта дар росткунҷа rect.collidepoint(x, y) ё rect.collidepoint(pos)
зеркунии муш event.type == pygame.MOUSEBUTTONDOWN, event.pos
курсор ҳозир pygame.mouse.get_pos()
ду росткунҷа бурида мешаванд a.colliderect(b) (расидан — буриш нест)
пурра дар дохил a.contains(b)
аввалин аз рӯйхат a.collidelist(rects) → рақам ё -1
ҳама аз рӯйхат a.collidelistall(rects) → рӯйхати рақамҳо
филтр кардани рӯйхат lst = [x for x in lst if шарт]
тағйири рӯйхати додашуда lst[:] = [...]
ҳамеша ба рост / чап vx = abs(vx) / vx = -abs(vx)