Кіріспе
ITUniverШағын Python жобаларының үлкен кітабы. Кіріспе
https://inventwithpython.com/
автор: Al Sweigart
The Big Book of Small Python Projects
Қазақ тіліне аударған ITUniver Team
The Big Book of Small Python Projects
Қазақ тіліне аударған ITUniver Team
Bagels, дедуктивті логикалық ойында сіз анықтамаларға негізделген құпия үш таңбалы санды табуыңыз керек. Ойын сіздің болжамыңызға жауап ретінде келесі кеңестердің бірін ұсынады: болжамыңызда дұрыс цифр дұрыс емес жерде болған кезде «Пико», болжамыңыз дұрыс жерде дұрыс цифр болса «Ферми» және егер сіздің болжамыңызда дұрыс сан болса, «Bagels» болжамда дұрыс цифрлар жоқ. Құпия нөмірді табу үшін сізде 10 әрекет бар.
bagels.py іске қосқан кезде нәтиже келесідей болады:
Bagels, a deductive logic game.
By Al Sweigart al@inventwithpython.com
I am thinking of a 3-digit number. Try to guess what it is.
Here are some clues:
When I say: That means:
Pico One digit is correct but in the wrong position.
Fermi One digit is correct and in the right position.
Bagels No digit is correct.
I have thought up a number.
You have 10 guesses to get it.
Guess #1:
> 123
Pico
Guess #2:
> 456
Bagels
Guess #3:
> 178
Pico Pico
--snip--
Guess #7:
> 791
Fermi Fermi
Guess #8:
> 701
You got it!
Do you want to play again? (yes or no)
> no
Thanks for playing!
Бұл бағдарлама бүтін мәндерді емес, сандық сандарды қамтитын жол мәндерін пайдаланатынын есте сақтаңыз. Мысалы, '426'
- 426
мәнінен басқа мән. Біз мұны істеуіміз керек, өйткені біз математикалық операцияларды емес, құпия санмен жолды салыстыруды орындаймыз. '0'
алдыңғы сан болуы мүмкін екенін есте сақтаңыз: '026'
жолы '26'
жолынан өзгеше, бірақ бүтін сан 026
мәні 26
мен бірдей.
1. """Bagels, by Al Sweigart al@inventwithpython.com
2. A deductive logic game where you must guess a number based on clues.
3. View this code at https://nostarch.com/big-book-small-python-projects
4. A version of this game is featured in the book "Invent Your Own
5. Computer Games with Python" https://nostarch.com/inventwithpython
6. Tags: short, game, puzzle"""
7.
8. import random
9.
10. NUM_DIGITS = 3 # (!) Мұны 1 немесе 10 етіп орнатып көріңіз.
11. MAX_GUESSES = 10 # (!) Мұны 1 немесе 100 етіп орнатып көріңіз.
12.
13.
14. def main():
15. print('''Bagels, a deductive logic game.
16. By Al Sweigart al@inventwithpython.com
17.
18. I am thinking of a {}-digit number with no repeated digits.
19. Try to guess what it is. Here are some clues:
20. When I say: That means:
21. Pico One digit is correct but in the wrong position.
22. Fermi One digit is correct and in the right position.
23. Bagels No digit is correct.
24.
25. For example, if the secret number was 248 and your guess was 843, the
26. clues would be Fermi Pico.'''.format(NUM_DIGITS))
27.
28. while True: # Ойынның негізгі циклі
29. # Бұл ойыншы білуі керек құпия нөмірді сақтайды:
30. secretNum = getSecretNum()
31. print('I have thought up a number.')
32. print(' You have {} guesses to get it.'.format(MAX_GUESSES))
33.
34. numGuesses = 1
35. while numGuesses <= MAX_GUESSES:
36. guess = ''
37. # Олар жарамды болжамды енгізгенше циклды жалғастыра беріңіз:
38. while len(guess) != NUM_DIGITS or not guess.isdecimal():
39. print('Guess #{}: '.format(numGuesses))
40. guess = input('> ')
41.
42. clues = getClues(guess, secretNum)
43. print(clues)
44. numGuesses += 1
45.
46. if guess == secretNum:
47. break # Олар дұрыс, сондықтан бұл циклден шығып кетіңіз.
48. if numGuesses > MAX_GUESSES:
49. print('You ran out of guesses.')
50. print('The answer was {}.'.format(secretNum))
51.
52. # Ойыншыдан қайта ойнағысы келетінін сұраңыз.
53. print('Do you want to play again? (yes or no)')
54. if not input('> ').lower().startswith('y'):
55. break
56. print('Thanks for playing!')
57.
58.
59. def getSecretNum():
60. """NUM_DIGITS бірегей кездейсоқ саннан тұратын жолды қайтарады."""
61. numbers = list('0123456789') # 0-ден 9-ға дейінгі сандар тізімін жасаңыз.
62. random.shuffle(numbers) # Оларды кездейсоқ ретпен араластырыңыз.
63.
64. # Құпия нөмірге арналған тізімдегі бірінші NUM_DIGITS цифрды алыңыз:
65. secretNum = ''
66. for i in range(NUM_DIGITS):
67. secretNum += str(numbers[i])
68. return secretNum
69.
70.
71. def getClues(guess, secretNum):
72. """Returns a string with the pico, fermi, bagels clues for a guess
73. and secret number pair."""
74. if guess == secretNum:
75. return 'You got it!'
76.
77. clues = []
78.
79. for i in range(len(guess)):
80. if guess[i] == secretNum[i]:
81. # Дұрыс сан дұрыс жерде.
82. clues.append('Fermi')
83. elif guess[i] in secretNum:
84. # Дұрыс цифр дұрыс емес жерде.
85. clues.append('Pico')
86. if len(clues) == 0:
87. return 'Bagels' # Дұрыс сандар мүлде жоқ.
88. else:
89. # Анықтамаларды алфавиттік ретпен сұрыптаңыз,
90. # сонда олардың бастапқы реті ақпарат бермейді.
91. clues.sort()
92. # Тіркес анықтамаларының тізімінен бір string жасаңыз.
93. return ' '.join(clues)
94.
95.
96. # Бағдарлама іске қосылса (импортталғанның орнына), ойынды іске қосыңыз:
97. if __name__ == '__main__':
98. main()
Бастапқы кодты енгізіп, оны бірнеше рет іске қосқаннан кейін оған эксперименттік өзгерістер енгізіп көріңіз. (!)
белгісімен белгіленген түсініктемелерде сіз жасауға болатын шағын өзгертулер бойынша ұсыныстар бар. Өз бетіңізше келесі әрекеттерді орындау жолын анықтауға да болады:
NUM_DIGITS
өзгерту арқылы өзгертіңіз.MAX_GUESSES
өзгерту арқылы өзгертіңіз.Келесі сұрақтарға жауап табуға тырысыңыз. Кодтың кейбір өзгертулерімен тәжірибе жасап, өзгертулердің қандай әсер ететінін көру үшін бағдарламаны қайта іске қосыңыз.
NUM_DIGITS
тұрақтысын өзгерткенде не болады?MAX_GUESSES
тұрақтысын өзгерткенде не болады?NUM_DIGITS
параметрін 10
мәнінен үлкенірек етіп орнатсаңыз, не болады?secretNum = getSecretNum()
мәнін secretNum = '123'
деп ауыстырсаңыз не болады?numGuesses = 1
жойсаңыз немесе түсініктеме берсеңіз, қандай қате туралы хабар аласыз?random.shuffle(numbers)
жойылса немесе түсініктеме берсеңіз не болады?if
guess == secretNum:
жойсаңыз немесе түсініктеме берсеңіз және "Сіз түсіндіңіз!" деп қайтарсаңыз не болады.
75-жолда?numGuesses += 1
пікірін қалдырсаңыз не болады?Туған күн мәселесі деп те аталатын туған күн парадоксы, тіпті шағын топта екі адамның туған күні бірдей болуының таңқаларлық жоғары ықтималдығы. 70 адамнан тұратын топта екі адамның туған күні сәйкес келетініне 99,9% мүмкіндік бар. Бірақ тіпті 23 адамнан тұратын топта да туған күннің сәйкес келу мүмкіндігі 50 пайызды құрайды. Бұл бағдарлама әртүрлі өлшемдегі топтар үшін пайыздарды анықтау үшін бірнеше ықтималдық эксперименттерін орындайды. Біз ықтимал нәтижелерді түсіну үшін бірнеше рет кездейсоқ сынақтар жүргізетін эксперименттердің бұл түрлерін Монте-Карло эксперименттері деп атаймыз.
Туған күн парадоксы туралы толығырақ ақпаратты https://en.wikipedia.org/wiki/Туған күн_мәселесі сайтынан біле аласыз.
birthdayparadox.py іске қосқан кезде нәтиже келесідей болады:
Birthday Paradox, by Al Sweigart al@inventwithpython.com
--snip--
How many birthdays shall I generate? (Max 100)
> 23
Here are 23 birthdays:
Oct 9, Sep 1, May 28, Jul 29, Feb 17, Jan 8, Aug 18, Feb 19, Dec 1, Jan 22,
May 16, Sep 25, Oct 6, May 6, May 26, Oct 11, Dec 19, Jun 28, Jul 29, Dec 6,
Nov 26, Aug 18, Mar 18
In this simulation, multiple people have a birthday on Jul 29
Generating 23 random birthdays 100,000 times...
Press Enter to begin...
Let's run another 100,000 simulations.
0 simulations run...
10000 simulations run...
--snip--
90000 simulations run...
100000 simulations run.
Out of 100,000 simulations of 23 people, there was a
matching birthday in that group 50955 times. This means
that 23 people have a 50.95 % chance of
having a matching birthday in their group.
That's probably more than you would think!
100 000 модельдеуді іске қосу біраз уақыт алуы мүмкін, сондықтан 95 және 96 жолдар тағы 10 000 модельдеу аяқталғанын хабарлайды. Бұл кері байланыс пайдаланушыны бағдарламаның қатып қалмағанына сендіреді. 95-жолдағы 10_000
және 93 және 103-жолдардағы 100_000
сияқты кейбір бүтін сандарда астыңғы сызық бар екенін ескеріңіз. Бұл астын сызулардың ерекше мағынасы жоқ, бірақ Python оларға бағдарламашылар бүтін мәндерді оқуды жеңілдету үшін мүмкіндік береді. Басқаша айтқанда, 100000
-дан қарағанда, 100_000
ішінен “жүз мыңды” оқу оңайырақ.
1. """Birthday Paradox Simulation, by Al Sweigart al@inventwithpython.com
2. Explore the surprising probabilities of the "Birthday Paradox".
3. More info at https://en.wikipedia.org/wiki/Birthday_problem
4. View this code at https://nostarch.com/big-book-small-python-projects
5. Tags: short, math, simulation"""
6.
7. import datetime, random
8.
9.
10. def getBirthdays(numberOfBirthdays):
11. """Туған күндерге арналған сандық
кездейсоқ күн нысандарының тізімін қайтарады."""
12. birthdays = []
13. for i in range(numberOfBirthdays):
14. # Барлық туған күндерде бір жыл болғанша,
15. # жыл біздің модельдеуіміз үшін маңызды емес.
16. startOfYear = datetime.date(2001, 1, 1)
17.
18. # Жылдың кездейсоқ күнін алыңыз:
19. randomNumberOfDays = datetime.timedelta(random.randint(0, 364))
20. birthday = startOfYear + randomNumberOfDays
21. birthdays.append(birthday)
22. return birthdays
23.
24.
25. def getMatch(birthdays):
26. """Returns the date object of a birthday that occurs more than once
27. in the birthdays list."""
28. if len(birthdays) == len(set(birthdays)):
29. return None # All birthdays are unique, so return None.
30.
31. # Әр туған күнді басқа туған күнмен салыстырыңыз:
32. for a, birthdayA in enumerate(birthdays):
33. for b, birthdayB in enumerate(birthdays[a + 1 :]):
34. if birthdayA == birthdayB:
35. return birthdayA # Сәйкес туған күнді қайтарыңыз.
36.
37.
38. # Кіріспені көрсету:
39. print('''Birthday Paradox, by Al Sweigart al@inventwithpython.com
40.
41. The Birthday Paradox shows us that in a group of N people, the odds
42. that two of them have matching birthdays is surprisingly large.
43. This program does a Monte Carlo simulation (that is, repeated random
44. simulations) to explore this concept.
45.
46. (It's not actually a paradox, it's just a surprising result.)
47. ''')
48.
49. # Ай атауларының қатарын ретімен орнатыңыз:
50. MONTHS = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
51. 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
52.
53. while True: # Пайдаланушы жарамды соманы енгізгенше сұрай беріңіз.
54. print('How many birthdays shall I generate? (Max 100)')
55. response = input('> ')
56. if response.isdecimal() and (0 < int(response) <= 100):
57. numBDays = int(response)
58. break # Пайдаланушы жарамды соманы енгізді.
59. print()
60.
61. # Туған күндерді жасау және көрсету:
62. print('Here are', numBDays, 'birthdays:')
63. birthdays = getBirthdays(numBDays)
64. for i, birthday in enumerate(birthdays):
65. if i != 0:
66. # Бірінші туған күннен кейін әрбір туған күн үшін үтірді көрсетіңіз.
67. print(', ', end='')
68. monthName = MONTHS[birthday.month - 1]
69. dateText = '{} {}'.format(monthName, birthday.day)
70. print(dateText, end='')
71. print()
72. print()
73.
74. # Сәйкес келетін екі туған күн бар-жоғын анықтаңыз.
75. match = getMatch(birthdays)
76.
77. # Нәтижелерді көрсету:
78. print('In this simulation, ', end='')
79. if match != None:
80. monthName = MONTHS[match.month - 1]
81. dateText = '{} {}'.format(monthName, match.day)
82. print('multiple people have a birthday on', dateText)
83. else:
84. print('there are no matching birthdays.')
85. print()
86.
87. # 100 000 модельдеу жасап көріңіз:
88. print('Generating', numBDays, 'random birthdays 100,000 times...')
89. input('Press Enter to begin...')
90.
91. print('Let\'s run another 100,000 simulations.')
92. simMatch = 0 # Қанша симуляцияда сәйкес туған күн болды.
93. for i in range(100_000):
94. # Әр 10 000 модельдеу орындалу барысы туралы есеп беру:
95. if i % 10_000 == 0:
96. print(i, 'simulations run...')
97. birthdays = getBirthdays(numBDays)
98. if getMatch(birthdays) != None:
99. simMatch = simMatch + 1
100. print('100,000 simulations run.')
101.
102. # Модельдеу нәтижелерін көрсету:
103. probability = round(simMatch / 100_000 * 100, 2)
104. print('Out of 100,000 simulations of', numBDays, 'people, there was a')
105. print('matching birthday in that group', simMatch, 'times. This means')
106. print('that', numBDays, 'people have a', probability, '% chance of')
107. print('having a matching birthday in their group.')
108. print('That\'s probably more than you would think!')
Python тіліндегі күн өзіндік деректер түрі емес, бірақ біз күн нысандары ретінде күндермен жұмыс істеу үшін datetime
деп аталатын модульді импорттай аламыз.
datetime
модулі күндер мен уақыттарды басқаруға арналған кластарды береді.
Мысал: datetime
модулін импорттаңыз және ағымдағы уақытты көрсетіңіз:
import datetime
x = datetime.datetime.now()
print(x)
Жоғарыдағы мысалдағы кодты орындаған кезде нәтиже келесідей болады:
2024-03-22 10:27:53.017484
Басып шығарылған уақытта жыл, ай, күн, сағат, минут, секунд және микросекунд бар.
datetime
модулінде күн нысаны туралы ақпаратты қайтарудың көптеген әдістері бар.
Күнді жасау үшін datetime
модулінің datetime()
класын (конструкторын) пайдалана аламыз.
datetime()
класы күнді жасау үшін үш параметрді қажет етеді: жыл, ай, күн.
import datetime
x = datetime.datetime(2020, 5, 17)
print(x)
Нәтиже: 2020-05-17 00:00:00
Тағы бір мысал:
import datetime
x = datetime.datetime.now()
print(x.year)
print(x.strftime('%A'))
Нәтижесі: 2024 Friday
Python datetime
модульі туралы көбірек ақпаратты Python ресми веб-парақшасынан алуға болады.
Келесі сұрақтарға жауап табуға тырысыңыз. Кодтың кейбір өзгертулерімен тәжірибе жасап, өзгертулердің қандай әсер ететінін көру үшін бағдарламаны қайта іске қосыңыз.
numBDays = int(response)
жойсаңыз немесе түсініктеме берсеңіз, қандай қате туралы хабар аласыз?'Қаңтар'
орнына 'Қаңтар'
сияқты толық ай атауларын қалай көрсетуге болады?'X модельдеуді іске қосу...'
қалай жасауға болады?Бұл бағдарлама пайдаланушының хабарламасын қалай көрсету керектігін анықтау үшін разрядтық кескін, әр пиксел үшін тек екі мүмкін түсі бар 2D кескіні ретінде көп жолды жолды пайдаланады. Бұл нүктелік кескінде бос орын таңбалары бос орынды білдіреді және барлық басқа таңбалар пайдаланушы хабарындағы таңбалармен ауыстырылады. Берілген нүктелік кескін әлем картасына ұқсайды, бірақ оны қалаған кез келген кескінге өзгертуге болады. Кеңістік немесе хабарлама таңбалары жүйесінің екілік қарапайымдылығы оны жаңадан бастаушылар үшін жақсы етеді. Нәтижелердің қалай болатынын көру үшін әртүрлі хабарлармен тәжірибе жасап көріңіз!
bitmapmessage.py іске қосқан кезде нәтиже келесідей болады:
Bitmap Message, by Al Sweigart al@inventwithpython.com
Enter the message to display with the bitmap.
> Hello!
Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!He
lo!Hello!Hello l !He lo e llo!Hello!Hello!Hello!Hello!He
llo!Hello!Hello!Hello He lo H l !Hello!Hello!Hello!Hello!Hello H
el lo!Hello!Hello!He lo!Hello!Hello!Hello!Hello!Hel
o!Hello!Hello lo e lo!H ll !Hello!Hello!H l
!Hello!He llo!Hel Hello!Hello!Hell ! e
Hello!He ello!Hello!Hello!Hello!Hell H
l H llo! ell ello!Hello!Hell !Hello el o
lo!H l ello!Hello!Hell ell !He o
!Hello llo!Hello!Hel el He o
!Hello!H lo!Hello!Hell l !H llo
ello!Hel Hello!He H llo Hell
ello!Hell ello!H l Hell !H l o!
ello!Hell ello!H l o o!H l H
lo!Hel ello! el o!Hel H
lo!He llo! e llo!Hell
llo!H llo! llo!Hello
llo! ll lo!Hell e
llo l e
ll l H
Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!He
Дүниежүзілік карта үлгісінің әрбір таңбасын жеке терудің орнына, сіз https://inventwithpython.com/bitmapworld.txt бетінен барлық нәрсені көшіріп, қоюға болады. Үлгінің жоғарғы және төменгі жағындағы 68 нүктеден тұратын сызық оны дұрыс туралауға көмектесетін сызғыш қызметін атқарады. Дегенмен, үлгіде қателер жіберсеңіз, бағдарлама жұмыс істей береді.
43-жолдағы bitmap.splitlines()
әдісін шақыру жолдардың тізімін қайтарады, олардың әрқайсысы көпжолды bitmap
жолындағы жол болып табылады. Көп жолды жолды пайдалану нүктелік кескінді кез келген қалаған үлгіге өңдеуді жеңілдетеді. Бағдарлама үлгідегі бос орынсыз кез келген таңбаны толтырады, сондықтан жұлдызшалар, нүктелер немесе кез келген басқа таңба бірдей әрекетті орындайды.
51-жолдағы message[i % len(message)]
коды message
ішіндегі мәтіннің қайталануын тудырады. i
0
мәнінен len(message)
мәнінен үлкенірек санға өскен сайын, i % len(message)
өрнегі қайтадан 0
деп бағалайды. Бұл message[i % len(message)]
i
ұлғайған сайын message
ішіндегі таңбаларды қайталауына әкеледі.
1. """Bitmap Message, by Al Sweigart al@inventwithpython.com
2. Displays a text message according to the provided bitmap image.
3. View this code at https://nostarch.com/big-book-small-python-projects
4. Tags: tiny, beginner, artistic"""
5.
6. import sys
7.
8. # (!) Бұл көп жолды кез келген суретке өзгертіп көріңіз:
9.
10. # Бұл жолдың жоғарғы және төменгі жағында 68 нүкте бар:
11. # (https://inventwithpython.com/bitmapworld.txt
12. # Сондай-ақ, осы жерден көшіріп, қоюға болады)
13. bitmap = """
14. ....................................................................
15. ************** * *** ** * ******************************
16. ********************* ** ** * * ****************************** *
17. ** ***************** ******************************
18. ************* ** * **** ** ************** *
19. ********* ******* **************** * *
20. ******** *************************** *
21. * * **** *** *************** ****** ** *
22. **** * *************** *** *** *
23. ****** ************* ** ** *
24. ******** ************* * ** ***
25. ******** ******** * *** ****
26. ********* ****** * **** ** * **
27. ********* ****** * * *** * *
28. ****** ***** ** ***** *
29. ***** **** * ********
30. ***** **** *********
31. **** ** ******* *
32. *** * *
33. ** * *
34. ...................................................................."""
35.
36. print('Bitmap Message, by Al Sweigart al@inventwithpython.com')
37. print('Enter the message to display with the bitmap.')
38. message = input('> ')
39. if message == '':
40. sys.exit()
41.
42. # Растрдағы әрбір жолдың үстінен айналдырыңыз:
43. for line in bitmap.splitlines():
44. # Жолдағы әрбір таңбаны айналдырыңыз:
45. for i, bit in enumerate(line):
46. if bit == ' ':
47. # Растрда бос орын болғандықтан бос орынды басып шығарыңыз:
48. print(' ', end='')
49. else:
50. # Хабардан таңбаны басып шығару:
51. print(message[i % len(message)], end='')
52. print() # Жаңа жолды басып шығарыңыз.
Бастапқы кодты енгізіп, оны бірнеше рет іске қосқаннан кейін оған эксперименттік өзгерістер енгізіп көріңіз. Мүлде жаңа үлгілерді жасау үшін bitmap
ішіндегі жолды өзгертуге болады.
Келесі сұрақтарға жауап табуға тырысыңыз. Кодтың кейбір өзгертулерімен тәжірибе жасап, өзгертулердің қандай әсер ететінін көру үшін бағдарламаны қайта іске қосыңыз.
bitmap
айнымалы жолындағы бос орынсыз таңбалардың қандай екендігі маңызды ма?i
айнымалысы нені білдіреді?print()
жою немесе түсініктеме берсеңіз, қандай қате пайда болады?Блэкджек, 21 деген атпен де белгілі карта ойыны, мұнда ойыншылар асып кетпей мүмкіндігінше тура 21 не соған жақын ұпай алуға тырысады. Бұл бағдарлама ASCII арт деп аталатын мәтіндік таңбалармен салынған кескіндерді пайдаланады. Ақпарат алмасудың американдық стандартты коды (ASCII) — мәтіндік таңбаларды сандық кодтарға байланыстыру үшін компьютерлерде қолданылған технология. Мысалы, компьютер 9829 деген санды экранға '♥' етіп басып шығарады. Бұл бағдарламадағы ойын карталары ASCII өнерінің мысалы болып табылады:
___ ___
|A | |10 |
| ♣ | | ♦ |
|__A| |_10|
Осы карта ойынының басқа ережелері мен тарихын https://en.wikipedia.org сайтынан таба аласыз.
blackjack.py бағдарламасын іске қосқан кезде нәтиже келесідей болады:
--snip/үзінді—
Сіздің қаражатыңыз: 620
Неше ақша тігіске қоясыз? (1-620, немесе QUIT)
> 620
Сіз тіккен ақша: 620
ДИЛЕР: ???
___ ___
|##_||K |
|###|| ♦ |
|_##||__K|
ОЙЫНШЫ: 17
___ ___
|7 ||V |
| ♠ || ♣ |
|__7||__V|
(H)it, (S)tand> s
Дилер карта алды ...
ДИЛЕР: ???
___ ___ ___
|##_||K ||T |
|###|| ♦ || ♠ |
|_##||__K||__T|
ОЙЫНШЫ: 17
___ ___
|7 ||V |
| ♠ || ♣ |
|__7||__V|
Жалғастыру үшін Enter пернесін басыңыз...
ДИЛЕР: 17
___ ___ ___
|6 ||K ||T |
| ♥ || ♦ || ♠ |
|__6||__K||__T|
ОЙЫНШЫ: 17
___ ___
|7 ||V |
| ♠ || ♣ |
|__7||__V|
Тең болды. Тіккен ақшаңыз сізге қайтарылады
Жалғастыру үшін Enter пернесін басыңыз...
Сіздің қаражатыңыз: 620
Неше ақша тігіске қоясыз? (1-620, немесе QUIT)
> 620
Сіз тіккен ақша: 620
ДИЛЕР: ???
___ ___
|##_||2 |
|###|| ♣ |
|_##||__2|
ОЙЫНШЫ: 11
___ ___
|5 ||6 |
| ♦ || ♦ |
|__5||__6|
(H)it, (S)tand> h
Сіз алған карта 6 ♥
ДИЛЕР: ???
___ ___
|##_||2 |
|###|| ♣ |
|_##||__2|
ОЙЫНШЫ: 17
___ ___ ___
|5 ||6 ||6 |
| ♦ || ♦ || ♥ |
|__5||__6||__6|
(H)it, (S)tand> s
Дилер карта алды ...
ДИЛЕР: ???
___ ___ ___
|##_||2 ||9 |
|###|| ♣ || ♠ |
|_##||__2||__9|
ОЙЫНШЫ: 17
___ ___ ___
|5 ||6 ||6 |
| ♦ || ♦ || ♥ |
|__5||__6||__6|
Жалғастыру үшін Enter пернесін басыңыз...
ДИЛЕР: 19
___ ___ ___
|8 ||2 ||9 |
| ♥ || ♣ || ♠ |
|__8||__2||__9|
ОЙЫНШЫ: 17
___ ___ ___
|5 ||6 ||6 |
| ♦ || ♦ || ♥ |
|__5||__6||__6|
Сіз ұтылдыңыз.
Жалғастыру үшін Enter пернесін басыңыз...
Сіздің қаражатыңыз бітті!
Құмар ойынды шын өмірде ойнамаңыз!
Ойын үшін рахмет!
Карта мастьының таңбалары пернетақтада жоқ, сондықтан оларды жасау үшін chr()
функциясын шақырамыз. chr()
параметріне берілген бүтін сан Юникод стандартына сәйкес таңбаны анықтайтын бірегей сан болып табылатын Unicode code point/код нүктесі деп аталады. Юникод жиі дұрыс түсінілмейді. Алайда, Нед Бэтчелдердің 2012 жылы АҚШ-тағы PyCon баяндамасы «Прагматикалық Юникод немесе ауырсынуды қалай тоқтата аламын?» Юникодқа тамаша кіріспе болып табылады және оны https://youtu.be/sgHbC6udIqc/ сайтынан таба аласыз. В қосымшасында Python бағдарламаларында пайдалануға болатын Юникод таңбаларының толық тізімі берілген.
Кодтың толық нұсқасы келесідей:
import random, sys
JUREK = chr(9829)
QIYQ = chr(9830)
QARGA = chr(9824)
SHYBYN = chr(9827)
KARTA_ARTY = 'backside'
def main():
"""Ережесі:
21 ұпайдан асып кетпей, барынша жақындауға тырысыңыз
Король, Дама және Валет 10 ұпайға бағаланады.
Тұз 1 не 11 ұпайға бағаланады.
2 мен 10 арасындағы карталар сәйкес ұпайға бағаланады.
(H)it - тағы карта алу үшін ағылшынша H пернесін басыңыз.
(S)tand - stand. Тоқтау үшін S пернесін басыңыз.
Карта бірінші берілгенде, (D)ouble down, D пернесін басып,
сол кезде тігетін ақшаны 2 еселей аласыз, бірақ тағы бір рет
H пернесін басып тоқтамас бұрын карта алуыңыз керек.
Тең болған жағдайда тігілген ақша ойыншыға қайтарылады.
Дилер 17-ге жеткенде не асқанда карта алуды тоқтатады.
"""
aqsha = 5000
while True:
if aqsha <= 0:
print("Сіздің қаражатыңыз бітті!")
print("Құмар ойынды шын өмірде ойнамаңыз!")
print("Ойын үшін рахмет!")
sys.exit()
print('Сізде бар қаражат:', aqsha)
stavka = stavkanyQabyldau(aqsha)
koloda = kartaKolodasy()
dilerQoly = [koloda.pop(), koloda.pop()]
oiynshyQoly = [koloda.pop(), koloda.pop()]
print('Сіз тіккен ақша:', stavka)
while True:
qoldyKorsetu(oiynshyQoly, dilerQoly, False)
print()
if qolUpaiynAlu(oiynshyQoly) > 21:
break
juris = juristiAlu(oiynshyQoly, aqsha - stavka)
if juris == 'D':
qosyumshaTigis = stavkanyQabyldau(min(stavka, (aqsha - stavka)))
stavka += qosyumshaTigis
print(f'Тігіс {stavka} ұлғайтылады')
print('Тігіс:', stavka)
if juris in ('H', 'D'):
janaKarta = koloda.pop()
rank, mast = janaKarta
print(f'Сіз алған карта {rank} {mast}')
oiynshyQoly.append(janaKarta)
if qolUpaiynAlu(oiynshyQoly) > 21:
continue
if juris in ('S', 'D'):
break
if qolUpaiynAlu(oiynshyQoly) <= 21:
while qolUpaiynAlu(dilerQoly) < 17:
print('Дилер карта алды ...')
dilerQoly.append(koloda.pop())
qoldyKorsetu(oiynshyQoly, dilerQoly, False)
if qolUpaiynAlu(dilerQoly) > 21:
break
input('Жалғастыру үшін Enter пернесін басыңыз...')
print('\n\n')
qoldyKorsetu(oiynshyQoly, dilerQoly, True)
oiynshyUpaiy = qolUpaiynAlu(oiynshyQoly)
dilerUpaiy = qolUpaiynAlu(dilerQoly)
if dilerUpaiy > 21:
print(f'Дилер асып кетті. Сіз {stavka}$ ұтып алдыңыз')
aqsha += stavka
elif (oiynshyUpaiy > 21) or (oiynshyUpaiy < dilerUpaiy):
print('Сіз ұтылдыңыз.')
aqsha -= stavka
elif oiynshyUpaiy > dilerUpaiy:
print(f'Сіз {stavka}$ ұтып алдыңыз')
aqsha += stavka
elif oiynshyUpaiy == dilerUpaiy:
print("Тең болды. Тіккен ақшаңыз сізге қайтарылады")
input('Жалғастыру үшін Enter пернесін басыңыз...')
print('\n\n')
def stavkanyQabyldau(maxTigis):
while True:
print(f'Неше ақша тігіске қоясыз? (1-{maxTigis}, немесе QUIT)')
stavka = input('> ').upper().strip()
if stavka == 'QUIT':
print('Ойын үшін рахмет')
sys.exit()
if not stavka.isdecimal():
continue
stavka = int(stavka)
if 1 <= stavka <= maxTigis:
return stavka
def kartaKolodasy():
koloda = []
for mast in (JUREK, QIYQ, QARGA, SHYBYN):
for rank in range(2, 11):
koloda.append((str(rank), mast))
for rank in ('V', 'D', 'K', 'T'):
koloda.append((rank, mast))
random.shuffle(koloda)
return koloda
def qoldyKorsetu(oiynshyQoly, dilerQoly, dilerQolynKorsetu):
print()
if dilerQolynKorsetu:
print('ДИЛЕР:', qolUpaiynAlu(dilerQoly))
kartalardyKorsetu(dilerQoly)
else:
print('ДИЛЕР: ???')
kartalardyKorsetu([KARTA_ARTY] + dilerQoly[1:])
print('ОЙЫНШЫ:', qolUpaiynAlu(oiynshyQoly))
kartalardyKorsetu(oiynshyQoly)
def qolUpaiynAlu(kartalar):
upai = 0
tuzKartaSany = 0
for karta in kartalar:
rank = karta[0]
if rank == 'T':
tuzKartaSany += 1
elif rank in ('K', 'D', 'V'):
upai += 10
else:
upai += int(rank)
upai += tuzKartaSany
for i in range(tuzKartaSany):
if upai + 10 <= 21:
upai += 10
return upai
def kartalardyKorsetu(kartalar):
qatarlar = ['', '', '', '', '']
for i, karta in enumerate(kartalar):
qatarlar[0] += ' ___ '
if karta == KARTA_ARTY:
qatarlar[1] += '|##_|'
qatarlar[2] += '|###|'
qatarlar[3] += '|_##|'
else:
rank, mast = karta
qatarlar[1] += f'|{rank.ljust(2)} |'
qatarlar[2] += f'| {mast} |'
qatarlar[3] += f'|_{rank.rjust(2, '_')}|'
for qatar in qatarlar:
print(qatar)
def juristiAlu(oiynshyQoly, aqsha):
while True:
jurister = ['(H)it', '(S)tand']
if len(oiynshyQoly) == 2 and aqsha > 0:
jurister.append('(D)ouble down')
jurisPrompt = ', '.join(jurister) + '> '
juris = input(jurisPrompt).upper()
if juris in ('H', 'S'):
return juris
if juris == 'D' and '(D)ouble down' in jurister:
return juris
if __name__ == '__main__':
main()
Бастапқы кодты енгізіп, оны бірнеше рет іске қосқаннан кейін оған эксперименттік өзгерістер енгізіп көріңіз. Blackjack сіз орындауға болатын бірнеше реттелетін ережелерге ие. Мысалы, егер алғашқы екі картаның мәні бірдей болса, ойыншы оларды екі қолға бөліп, оларға бөлек бәс тігуге болады. Сондай-ақ, егер ойыншы өзінің алғашқы екі картасы үшін «блэкджек» (тұз қарға мен валет қарға) алса, ойыншы оннан бір төлемді ұтады. Ойын туралы толығырақ ақпаратты https://en.wikipedia.org/wiki/Blackjack сайтынан біле аласыз.
Ойын үшін бізге не керек? Ақша, карта колодасы және дилер.
Дилердің қызметін біздің ойында компьютер атқарады. Картаны таратады, қолды көрсетеді және ұпайды санап кім жеңгенін анықтайды.
sys
import-таудан бастаймыз.
import random, sys
JUREK = chr(9829)
QIYQ = chr(9830)
QARGA = chr(9824)
SHYBYN = chr(9827)
KARTA_ARTY = 'backside'
def main():
aqsha = 5000
while True:
if aqsha <= 0:
print('Сіздің қаражатыңыз бітті.')
print('Бұл тек ойын. Шын өмірде құмар ойындар ойнамаңыз!')
sys.exit()
print(f'Сізде бар қаражат: {aqsha}')
break
if __name__ == '__main__':
main()
import random, sys
JUREK = chr(9829)
QIYQ = chr(9830)
QARGA = chr(9824)
SHYBYN = chr(9827)
KARTA_ARTY = 'backside'
def main():
aqsha = 5000
while True:
if aqsha <= 0:
print('Сіздің қаражатыңыз бітті.')
print('Бұл тек ойын. Шын өмірде құмар ойындар ойнамаңыз!')
sys.exit()
print(f'Сізде бар қаражат: {aqsha}')
stavka = stavkanyQabyldau(aqsha)
aqsha -= stavka
print(stavka)
def stavkanyQabyldau(maximaldyTigis):
print(f'Неше ақша тігіске қоясыз? 1 - {maximaldyTigis}')
stavka = input('> ').upper().strip()
stavka = int(stavka)
return stavka
if __name__ == '__main__':
main()
def stavkanyQabyldau(maximaldyStavka):
while True:
print(f'Неше ақша тігіске қоясыз? 1 - {maximaldyStavka}')
stavka = input('> ').upper().strip()
if stavka == 'QUIT':
print("Ойын үшін рахмет!")
sys.exit()
if not stavka.isdecimal():
continue
stavka = int(stavka)
return stavka
for
циклін жазамызrandom
модульінің shuffle
әдісін қолданамыз
import random, sys
JUREK = chr(9829)
QIYQ = chr(9830)
QARGA = chr(9824)
SHYBYN = chr(9827)
def main():
aqsha = 5000
while True:
if aqsha <= 0:
print('Сізде ақша бітті.')
print('Бұл ойын. Шын өмірде құмар ойын ойнамаңыз!')
sys.exit()
print(f'Сізде бар қаражат: {aqsha}')
stavka = stavkaQabyldau(aqsha)
print(f'Сіз тіккен ақша: {stavka}')
aqsha -= stavka
karta_kolodasy = kartaKolodasynAralastyru()
dilerQoly = (karta_kolodasy.pop(), karta_kolodasy.pop())
oiynshyQoly = (karta_kolodasy.pop(), karta_kolodasy.pop())
print(f'Дилердің қолы: {dilerQoly}')
print(f'Ойыншының қолы: {oiynshyQoly}')
def stavkaQabyldau(maximaldyStavka):
while True:
print(f'Неше теңге ставка қоясыз? 1-{maximaldyStavka}')
stavka = input('> ').upper().strip()
if not stavka.isdecimal():
continue
stavka = int(stavka)
maximaldyStavka -= stavka
return stavka
def kartaKolodasynAralastyru():
karta_kolodasy = []
for mast in (JUREK, QIYQ, QARGA, SHYBYN):
for rank in range(2, 11):
karta_kolodasy.append((str(rank), mast))
for rank in ('В', 'Д', 'К', 'Т'):
karta_kolodasy.append((rank, mast))
random.shuffle(karta_kolodasy)
return karta_kolodasy
if __name__ == '__main__':
main()
rjust()
әдісі толтыру таңбасы ретінде көрсетілген таңбаны (бос орын әдепкі) пайдаланып жолды оңға туралайды.ljust()
әдісі толтыру таңбасы ретінде көрсетілген таңбаны (бос орын әдепкі) пайдаланып жолды солға туралайды.
--snip--
print(f'Дилердің қолы:')
kartaSureti(dilerQoly)
print(f'Ойыншының қолы:')
kartaSureti(oiynshyQoly)
--snip--
def kartaSureti(kartalar):
qatarlar = ['', '', '', '', '', '']
for i, karta in enumerate(kartalar):
qatarlar[0] += ' ___ '
if karta == KARTA_ARTY:
qatarlar[1] += '|#__|'
qatarlar[2] += '|_##|'
qatarlar[3] += '|__#|'
else:
rank, mast = karta
qatarlar[1] += '|{}|'.format(rank.ljust(3, ' '))
qatarlar[2] += '| {} |'.format(mast)
qatarlar[3] += '|{}|'.format(rank.rjust(3, '_'))
for qatar in qatarlar:
print(qatar)
if __name__ == '__main__':
main()
Валет, Дама және Король - 10 ұпайдан есептеледі.
2, 3, 4, 5, 6, 7, 8, 9, 10 карталары сәйкес ұпайға бағаланады.
Ал, енді Тұздың мәні ойыншының қолына қай мән тиімдірек екеніне байланысты 1 немесе 11 болуы мүмкін. Тұзды 1 немесе 11 деп санау туралы шешім әдетте ойыншы қолының жалпы құнына және олардағы басқа карталарға негізделген.
Қараңыз, Blackjack ойынындағы Тұз/Ace мәнін анықтау үшін қолданылатын жалпы ереже:
--snip--
def upaidySanau(kartalar):
upai = 0
qoldagyTuzdarSany = 0
for karta in kartalar:
rank = karta[0]
if rank == 'Т':
qoldagyTuzdarSany += 1
elif rank in ('В', 'Д', 'К'):
upai += 10
else:
upai += int(rank)
upai += qoldagyTuzdarSany
for i in range(qoldagyTuzdarSany):
if (upai + 10) <= 21:
upai += 10
return upai
def qoldyKorsetu(oiynshyQoly, dilerQoly, dilerQolynKorsetu):
if dilerQolynKorsetu:
dilerUpaiy = upaidySanau(dilerQoly)
print(f'Дилердің ұпайы: {dilerUpaiy}')
kartaSuretinSalu(dilerQoly)
else:
print('Дилер ұпайы: ???')
kartaSuretinSalu([KARTA_ARTY] + dilerQoly[1:])
oiynshyUpaiy = upaidySanau(oiynshyQoly)
print(f'Ойыншы ұпайы: {oiynshyUpaiy}')
kartaSuretinSalu(oiynshyQoly)
Double down: - ойыншы алғашқы екі картаны көргеннен кейін қолы мықты деп есептесе, ставканы 2 еселей алады. Ол үшін алғашқы ставка ақшасын қайталайтын қаражатты тігіске қояды және бір карта алады. Бірақ осы қолындағы үш картамен тоқтайды, артық карта ала алмайды.
Қалай жұмыс істейді, қарап көрейік.
def juristiQabyldau(oiynshyQoly, aqsha):
while True:
jurister = ['(A)lu', '(S)top']
if (len(oiynshyQoly) == 2 and aqsha > 0):
jurister.append('(D)ouble down')
jurisPrompt = ', '.join(jurister) + ' > '
juris = input(jurisPrompt).upper()
if juris in ('A', 'S'):
return juris
if juris == 'D' and '(D)double down' in jurister:
return juris
Біздің соңғы код кітаптағы берілген түпнұсқа кодтан біршама өзгеріп кетті. Бұл видео сабақтағы түсіндірілген ретпен жазылған. Осы кодпен терең танысып, өзгертулер, жетілдірулер қажет болса өзіңіз тәжірибе ретінде жасап көріңіз.
import random
JUREK = chr(9829)
QIYQ = chr(9830)
QARGA = chr(9824)
SHYBYN = chr(9827)
JASYRYN_KARTA = 'jabyq_karta'
def main():
aqsha = 5000
while True:
print(f'Сізде бар қаражат: {aqsha} теңге.')
stavkaQuny = stavkaQabyldau(aqsha)
koloda = kartaKolodasy()
dilerQoly = [koloda.pop(), koloda.pop()]
oiynshyQoly = [koloda.pop(), koloda.pop()]
qoldyKorsetu(dilerQoly, oiynshyQoly, True)
while True:
juris = juristiAlu()
if upaidySanau(oiynshyQoly) > 21:
print('Сіз 21-ден асып кеттіп, ұтылып қалдыңыз')
aqsha -= int(stavkaQuny)
break
if juris == 'A':
oiynshyQoly.append(koloda.pop())
qoldyKorsetu(dilerQoly, oiynshyQoly, True)
if upaidySanau(oiynshyQoly) < 21:
continue
elif upaidySanau(oiynshyQoly) > 21:
print('Сіз 21-ден асып кеттіп, ұтылып қалдыңыз')
aqsha -= int(stavkaQuny)
break
if juris == 'S':
qoldyKorsetu(dilerQoly, oiynshyQoly, False)
while True:
if upaidySanau(dilerQoly) < 17:
dilerQoly.append(koloda.pop())
continue
else:
break
if upaidySanau(dilerQoly) > 21:
qoldyKorsetu(dilerQoly, oiynshyQoly, False)
aqsha += stavkaQuny
print(f'Дилер асып кетті. Сіз {stavkaQuny} ұтып алдыңыз')
break
elif upaidySanau(dilerQoly) > upaidySanau(oiynshyQoly):
qoldyKorsetu(dilerQoly, oiynshyQoly, False)
aqsha -= stavkaQuny
print('Дилер ұтты.')
break
elif upaidySanau(dilerQoly) < upaidySanau(oiynshyQoly):
qoldyKorsetu(dilerQoly, oiynshyQoly, False)
aqsha += stavkaQuny
print(f'Сіз {stavkaQuny} теңге ұтып алдыңыз')
break
elif upaidySanau(dilerQoly) == upaidySanau(oiynshyQoly):
qoldyKorsetu(dilerQoly, oiynshyQoly, False)
print('Тең болды. Қойылған ставка сізге қайтарылады')
break
def juristiAlu():
juris = input('Карта (A)lu үшін А пернесін басыңыз,
тоқтау үшін (S)top S пернесін басыңыз: > ').upper().strip()
if juris == 'A':
return juris
if juris == 'S':
return juris
def stavkaQabyldau(maximaldyStavka):
while True:
stavka = input('неше қоясыз? > ')
if not stavka.isdecimal():
continue
stavka = int(stavka)
return stavka
def upaidySanau(qoldagyKarta):
upai = 0
for rank, mast in qoldagyKarta:
if rank in ('2', '3', '4', '5', '6', '7', '8', '9', '10'):
rank = int(rank)
upai += rank
elif rank in ('В', 'Д', 'К'):
upai += 10
for rank, mast in qoldagyKarta:
if rank == 'Т' and upai <= 10:
upai += 11
elif rank == 'Т' and upai > 10:
upai += 1
return upai
def qoldyKorsetu(dilerQoly, oiynshyQoly, dilerQolynKorsetpeu):
dilerUpaiy = upaidySanau(dilerQoly)
if dilerQolynKorsetpeu:
print('Дилер қолы: ???')
kartaSuretinSalu([JASYRYN_KARTA] + dilerQoly[1:])
else:
print(f'Дилер қолы: {dilerUpaiy} ұпай')
kartaSuretinSalu(dilerQoly)
oiynshyUpaiy = upaidySanau(oiynshyQoly)
print(f'Ойыншы қолы: {oiynshyUpaiy} ұпай')
kartaSuretinSalu(oiynshyQoly)
def kartaSuretinSalu(kartalar):
qatarlar = ['', '', '', '', '']
for i, karta in enumerate(kartalar):
qatarlar[0] += ' ___ '
if karta == JASYRYN_KARTA:
qatarlar[1] += '|# |'
qatarlar[2] += '| ##|'
qatarlar[3] += '|__#|'
else:
rank, mast = karta
qatarlar[1] += f'|{rank.ljust(3, ' ')}|'
qatarlar[2] += f'| {mast} |'
qatarlar[3] += f'|{rank.rjust(3, '_')}|'
for qatar in qatarlar:
print(qatar)
def kartaKolodasy():
koloda = []
for mast in (JUREK, QIYQ, QARGA, SHYBYN):
for rank in range(2, 11):
koloda.append((str(rank), mast))
for rank in ('В', 'Д', 'К', 'Т'):
koloda.append((rank, mast))
random.shuffle(koloda)
return koloda
if __name__ == '__main__':
main()
Келесі сұрақтарға жауап табуға тырысыңыз. Кодтың кейбір өзгертулерімен тәжірибе жасап, өзгертулердің қандай әсер ететінін көру үшін бағдарламаны қайта іске қосыңыз.
жолдар
тізіміндегі (197-жолда жасалған) жолдардың әрқайсысы нені білдіреді?random.shuffle(deck)
файлын жойсаңыз немесе түсініктеме берсеңіз не болады?money -= bet
параметрін money += ставка
етіп өзгертсеңіз не болады?displayHands()
функциясындағы showDealerHand
параметрі True
күйіне орнатылғанда не болады? Ол False
болғанда не болады?Конуэйдің Өмір ойыны (Conway's Game of Life), жай ғана Life деп те белгілі, 1970 жылы британдық математик Джон Хортон Конуэй ойлап тапқан автоматты ұяшқтар. Бұл ойыншысыз ойын, яғни ойын барысы мен эволюциясы оның бастапқы күйімен анықталады және қосымша араласып-енгізуді қажет етпейді. Ойыншы алғашқы конфигурацияны жасап береді және оның қалай дамып жатқанын бақылау арқылы Өмір ойынымен әрекеттеседі. Бұл Turing complete және әмбебап конструкторды немесе кез келген басқа Тьюринг машинасын имитациялай алады.
Ойынға кіріспес бұрын автоматты ұяшқтар деген не біліп алайық. Бұл ойынды түсіну үшін өте маңызды
«Автоматты ұяшық» термині әрқайсысы соңғы күйлердің бірінде болуы мүмкін ұяшықтар торынан тұратын математикалық модельге немесе есептеу жүйесіне қатысты. Бұл ұяшықтар көрші ұяшықтардың күйлеріне негізделген ережелер жиынтығына сәйкес дискретті уақыт қадамдары бойынша дамиды. (Дискретті уақыт - жеке, айқын интервалдарға бөлінген уақытты білдіреді. Оқиғалар уақыттың нақты, оқшауланған нүктелерінде орын алады, бұл дискретті уақыт нүктелері арасында аралық мәндер жоқ. Бұл уақыт шексіз бөлінетін деп есептелетін үздіксіз уақытқа қарама-қайшы.)
Автоматты ұяшықтар ұяшықтардың кәдімгі торынан тұрады, олардың әрқайсысы қосулы және өшірулі сияқты шекті күйлердің бірінде болады. Тор өлшемі кез-келген үлкендікте болуы мүмкін. Әрбір ұяшық үшін оның көршілері деп аталатын ұяшықтар жиыны қатынаста болады. Бастапқы күй (t = 0, time) әрбір ұяшық үшін күй тағайындау арқылы таңдалады. Қандай да бір бекітілген ережеге сәйкес жаңа ұрпақ құрылады (t 1-ге артады), ол әрбір ұяшықтың жаңа күйін ұяшықтың ағымдағы күйі мен оның маңындағы көрші ұяшықтардың күйлері тұрғысынан анықтайды. Әдетте, ұяшықтардың күйін жаңарту ережесі әрбір ұяшық үшін бірдей және уақыт өте келе өзгермейді және бір уақытта бүкіл торға қолданылады.
Ең қарапайым тривиальды емес ұялы автомат бір өлшемді (1D) болады, әр ұяшықта екі мүмкін күй болады және ұяшықтың көршілері оның екі жағындағы көрші ұяшықтар ретінде анықталады. Ұяшық және оның екі көршілері 3 ұяшықтан тұратын көршілестікті құрайды, сондықтан көршілестік үшін 2³ = 8 мүмкін үлгі бар. Ереже әрбір үлгі үшін ұяшық келесі ұрпақта 1 немесе 0 болатынын шешуден тұрады. Сонда 28 = 256 мүмкін ереже бар.
1D автоаматты ұяшық ережелері келесі ұрпақты анықтау тәсілінің анимациясы. (Біраз қарасаңыз логиканы оңай түсініп аласыз.)
Келесі диаграммада ұяшықтар көршілерінің алдыңғы күйіне байланысты боялған жасалған үлгіні көрсетеді. Күңгірт түстер «1»-ді, ал ашық түстер «0»-ді білдіреді. Уақыт тік ось бойынша артады.
Екі өлшемді автоматты ұяшықтарды имитациялаудың бір жолы - ұяшықтар ұстанатын ережелер жиынты және оң жақтағы суреттегідей графикалық қағаз парағы. Әрбір шаршы «ұяшық» деп аталады және әрбір ұяшықта ақ және қара екі мүмкін күй бар. Ұяшықтың көршілестігі - бұл жақын орналасқан, әдетте іргелес ұяшықтар. Көршілестіктің ең көп таралған екі түрі - фон Нейман көршілестігі және Мур көршілестігі. Біріншісі, негізін қалаушы автоаматты ұяшық теоретикінің атымен аталған, төрт ортогональді көршілес ұяшықтардан тұрады. Соңғысы фон Нейман көршілестігін, сондай-ақ диагональ бойынша көршілес төрт ұяшықты қамтиды. Мұндай ұяшық және оның Мур көршілестігі үшін 512 (= 29) мүмкін үлгі бар. 512 ықтимал үлгілердің әрқайсысы үшін ереже кестесі келесі уақыт аралығында орталық ұяшықтың қара немесе ақ болатынын көрсетеді. Conway's Game of Life - бұл модельдің танымал нұсқасы. Тағы бір кең таралған көршілестік түрі - кеңейтілген фон Нейман төңірегі, ол әрбір ортогональды бағытта ең жақын екі ұяшықты, барлығы сегіз ұяшықты қамтиды. Автоматтардың мүмкін болатын жалпы санының жалпы теңдеуі (kk)s, мұндағы k - ұяшық үшін мүмкін күйлердің саны, s - ұяшықтың келесі күйін анықтау үшін қолданылатын көрші ұяшықтардың (соның ішінде есептелетін ұяшықтың өзі) саны. Осылайша, Мур төңірегі бар екі өлшемді жүйеде мүмкін болатын автоматтардың жалпы саны (22)9 немесе 1,34×10154 болады. Бұл өте үлкен сан және өте үлкен ұяшық күй ықтималдылығы деген сөз.
Қызыл ұяшықтар көк ұяшық үшін Мур көршілері
Қызыл ұяшықтар көк ұяшық үшін Фон Нейман көршілері болып табылады. 2-диапазон «cross neighborhood» қызғылт ұяшықтарды да қамтиды.
«Өмір ойынының» әлемі - бұл төртбұрышты ұяшықтардың шексіз, екі өлшемді ортогональды торы, олардың әрқайсысы тірі немесе өлі (немесе сәйкесінше қоныстанған және қоныстанбаған) екі ықтимал күйдің бірінде. Әрбір ұяшық өзінің сегіз көршілерімен өзара әрекеттеседі, олар көлденең, тік немесе диагональ бойынша іргелес жатқан ұяшықтар. Уақыттың әрбір қадамында келесі ауысулар орын алады:
Бастапқы үлгі жүйенің тұқымын құрайды. Бірінші ұрпақ жоғарыда аталған ережелерді тұқымдағы тірі немесе өлі әрбір ұяшыққа бір мезгілде қолдану арқылы жасалады; туу мен өлім бір уақытта болады және бұл орын алатын дискретті момент кейде tick деп аталады. Әрбір ұрпақ алдыңғысының таза функциясы болып табылады. Ережелер кейінгі ұрпақтарды туындату үшін қайта-қайта қолданылуын жалғастырады. Ұяшықтар ескі күйлерді «есте сақтамайды». Осы қарапайым ережелер шығаратын үлгілерге қатысты көптеген зерттеулер бар. Өкінішке орай, профессор Конуэй 2020 жылдың сәуір айында COVID-19 асқынуынан қайтыс болды. Конуэйдің өмір ойыны туралы қосымша ақпаратты https://kk.wikipedia.org/wiki/Conway%27s_Game_of_Life сайтынан табуға болады және Мартин Гарднер туралы қосымша ақпаратты https://kk.wikipedia.org/wiki/Martin_Gardner сайтында қараңыз.
conwaysgameoflife.py іске қосқан кезде нәтиже келесідей болады:
O O OO O O
O O O O O O OOOO O OO
OO O O O O O O O
OO O O OO OO
OO OO O O O OO
OO O O O OO
OOO OO OO O
O OOO
O O O O
OO OO OO OO O
OOO OO OOOO O O
O OO O O O OO OO O O OO
O O O O O OO O O OOO
O OOOO OO OO O OOOOO O
OO O O OOO O OOO OOOO O
Ұяшықтардың күйі сөздіктерде ұяшықтар
және nextCells
айнымалы мәндерінде сақталады. Екі сөздікте де кілттерге арналған (x, y)
кортеждері бар (мұндағы x
және y
бүтін сандар), 'O'
тірі жасушалар үшін, ал өлі жасушалар үшін ' '
. 40-44 жолдар осы сөздіктердің көрінісін экранға басып шығару үшін орнатылған. cells
айнымалы сөздігі ұяшықтардың ағымдағы күйін көрсетеді, ал nextCells
симуляцияның келесі қадамындағы ұяшықтар үшін сөздікті сақтайды.
1. """Conway's Game of Life, by Al Sweigart al@inventwithpython.com
2. The classic cellular automata simulation. Press Ctrl-C to stop.
3. More info at: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
4. View this code at https://nostarch.com/big-book-small-python-projects
5. Tags: short, artistic, simulation"""
6.
7. import copy, random, sys, time
8.
9. # Set up the constants:
10. WIDTH = 79 # The width of the cell grid.
11. HEIGHT = 20 # The height of the cell grid.
12.
13. # (!) Try changing ALIVE to '#' or another character:
14. ALIVE = 'O' # The character representing a living cell.
15. # (!) Try changing DEAD to '.' or another character:
16. DEAD = ' ' # The character representing a dead cell.
17.
18. # (!) Try changing ALIVE to '|' and DEAD to '-'.
19.
20. # The cells and nextCells are dictionaries for the state of the game.
21. # Their keys are (x, y) tuples and their values are one of the ALIVE
22. # or DEAD values.
23. nextCells = {}
24. # Put random dead and alive cells into nextCells:
25. for x in range(WIDTH): # Loop over every possible column.
26. for y in range(HEIGHT): # Loop over every possible row.
27. # 50/50 chance for starting cells being alive or dead.
28. if random.randint(0, 1) == 0:
29. nextCells[(x, y)] = ALIVE # Add a living cell.
30. else:
31. nextCells[(x, y)] = DEAD # Add a dead cell.
32.
33. while True: # Main program loop.
34. # Each iteration of this loop is a step of the simulation.
35.
36. print('\n' * 50) # Separate each step with newlines.
37. cells = copy.deepcopy(nextCells)
38.
39. # Print cells on the screen:
40. for y in range(HEIGHT):
41. for x in range(WIDTH):
42. print(cells[(x, y)], end='') # Print the # or space.
43. print() # Print a newline at the end of the row.
44. print('Press Ctrl-C to quit.')
45.
46. # Calculate the next step's cells based on current step's cells:
47. for x in range(WIDTH):
48. for y in range(HEIGHT):
49. # Get the neighboring coordinates of (x, y), even if they
50. # wrap around the edge:
51. left = (x - 1) % WIDTH
52. right = (x + 1) % WIDTH
53. above = (y - 1) % HEIGHT
54. below = (y + 1) % HEIGHT
55.
56. # Count the number of living neighbors:
57. numNeighbors = 0
58. if cells[(left, above)] == ALIVE:
59. numNeighbors += 1 # Top-left neighbor is alive.
60. if cells[(x, above)] == ALIVE:
61. numNeighbors += 1 # Top neighbor is alive.
62. if cells[(right, above)] == ALIVE:
63. numNeighbors += 1 # Top-right neighbor is alive.
64. if cells[(left, y)] == ALIVE:
65. numNeighbors += 1 # Left neighbor is alive.
66. if cells[(right, y)] == ALIVE:
67. numNeighbors += 1 # Right neighbor is alive.
68. if cells[(left, below)] == ALIVE:
69. numNeighbors += 1 # Bottom-left neighbor is alive.
70. if cells[(x, below)] == ALIVE:
71. numNeighbors += 1 # Bottom neighbor is alive.
72. if cells[(right, below)] == ALIVE:
73. numNeighbors += 1 # Bottom-right neighbor is alive.
74.
75. # Set cell based on Conway's Game of Life rules:
76. if cells[(x, y)] == ALIVE and (numNeighbors == 2
77. or numNeighbors == 3):
78. # Living cells with 2 or 3 neighbors stay alive:
79. nextCells[(x, y)] = ALIVE
80. elif cells[(x, y)] == DEAD and numNeighbors == 3:
81. # Dead cells with 3 neighbors become alive:
82. nextCells[(x, y)] = ALIVE
83. else:
84. # Everything else dies or stays dead:
85. nextCells[(x, y)] = DEAD
86.
87. try:
88. time.sleep(1) # Add a 1 second pause to reduce flickering.
89. except KeyboardInterrupt:
90. print("Conway's Game of Life")
91. print('By Al Sweigart al@inventwithpython.com')
92. sys.exit() # When Ctrl-C is pressed, end the program.
Бастапқы кодты енгізіп, оны бірнеше рет іске қосқаннан кейін оған эксперименттік өзгерістер енгізіп көріңіз. (!)
белгісімен белгіленген түсініктемелерде сіз жасауға болатын шағын өзгертулер бойынша ұсыныстар бар. Өз бетіңізше келесі әрекеттерді орындау жолын анықтауға да болады:
Келесі сұрақтарға жауап табуға тырысыңыз. Кодтың кейбір өзгертулерімен тәжірибе жасап, өзгертулердің қандай әсер ететінін көру үшін бағдарламаны қайта іске қосыңыз.
WIDTH = 79
мәнін WIDTH = 7
етіп өзгерткенде не болады?print('\n' * 50)
сөзін жойсаңыз немесе түсініктеме берсеңіз не болады?random.randint(0, 1)
параметрін random.randint(0, 10)
етіп өзгертсеңіз не болады?nextCells[(x, y)] = DEAD
мәнін nextCells[(x, y)] = ALIVE
етіп өзгертсеңіз не болады?Википедиядан, еркін энциклопедия
Pygame - бұл бейне ойындарды жазуға арналған Python модульдерінің жиынтығы. Ол Python бағдарламалау тілімен бірге пайдалануға арналған компьютерлік графика мен дыбыстық кітапханаларды қамтиды.
Pygame бастапқыда Пит Шиннерспен дамуы тоқтап қалғаннан PySDL-ді ауыстыру үшін жазылған. Және 2000-ші жылдан бері қауымдастық жобасы және GNU Lesser General Public License тегін бағдарламалық құралы бойынша шығарылады (ол «Pygame-ді ашық бастапқы және коммерциялық бағдарламалық құралмен таратуды қамтамасыз етеді»).
Python бағдарламалау тілін, соның ішінде әсіресе осы Pygame көмегімен ойындар жазу үшін PyWeek деп аталатын тұрақты конкурс бар. Қауымдастық Pygame үшін көптеген оқулықтар жасады.
Pygame ресми сайты https://www.pygame.org/news және құжаттамасы https://www.pygame.org/docs/ осы сілтемелерден табылады. Сондай-ақ reddit.com/r/pygame қарап көріңіз.
Pygame ағылшын тілінде үйрететін Clear Code деп аталатын YouTube арна бар екен. Мен өзім видеоларын қарамадым, сондықтан сапасы мен басқа видеолары не жайында екенін нақты айта алмаймын. Бірақ өзге интернет қолданушылары мақтап жазыпты. Ал мына видео Pygame-ге арналған видеосы.
Келіңіз, Бөтен планеталықтардың шабуылы деп аталатын ойын жасайық! Біз графиканы, анимацияны және тіпті дыбысты басқаратын, күрделі ойындарды құруды жеңілдететін қызықты, көңілді, қуатты Python модульдерінің жинағы Pygame-ді қолданамыз.
Бұл тарауда сіз Pygame орнатасыз, содан кейін ойыншының енгізуіне жауап ретінде оңға және солға қозғалатын және оқ ататын зымырандық кеме жасайсыз. Келесі екі тарауда сіз атып-жою үшін өзге планеталықтар флотын жасайсыз, содан кейін пайдалануға болатын кемелер санына шектеу қою және табло қосу арқылы ойынды жетілдіруді жалғастырасыз.
Осы ойынды құрастыру кезінде сіз бірнеше файлды қамтитын үлкен жобаларды басқаруды үйренесіз. Біз жобаны ұйымдастыру және кодты тиімді ету үшін көптеген кодтарды қайта өңдеп, файл мазмұнын басқарамыз.
Ойын жасау - тіл үйрену кезінде көңіл көтерудің тамаша тәсілі. Өзіңіз жазған ойынды ойнау өте керемет сезім және қарапайым ойын жазу сізге кәсіпқойлардың ойындарды қалай жасайтыны туралы көп нәрсені үйретеді. Осы тараумен жұмыс істеу барысында әрбір код блогының жалпы ойынға қалай үлес қосатынын анықтау үшін кодты енгізіңіз және іске қосыңыз. Ойындарыңыздағы өзара әрекеттесуді жақсарту жолын жақсырақ түсіну үшін әртүрлі мәндер мен параметрлермен тәжірибе жасаңыз.
Ескерту
Alien Invasion бірнеше түрлі файлдарды қамтиды, сондықтан жүйеде жаңа alien_invasion каталогын жасаңыз. import
амалдары дұрыс жұмыс істеуі үшін жобаның барлық файлдарын осы каталогқа сақтауды ұмытпаңыз.
Сонымен қатар, нұсқаны басқаруды (мысалы, Git version control) пайдалану ыңғайлы болса, оны осы жоба үшін пайдаланғыңыз келуі мүмкін. Егер сіз бұрын нұсқаны басқаруды пайдаланбаған болсаңыз, шолу үшін D қосымшасын қараңыз. (әзірге қазақ тіліне аударылмаған, жоқ)
Үлкен жобаны құрып жатқанда, кодты жазуды бастамас бұрын жоспарды дайындау маңызды. Жоспарлау сіздің ой-көңіліңізді, назарыңызды жинақы етеді және жобаны аяқтау ықтималдығын арттырады.
Жалпы ойынның сипаттамасын жазайық. Төмендегі сипаттама Бөтен планеталықтардың шабуылының барлық мәліметтерін қамтымаса да, ол ойынды қалай бастау керектігі туралы нақты түсінік береді:
Alien Invasion ойынында ойыншы экранның төменгі ортасында пайда болатын зымырандық кемені басқарады. Ойыншы нұсқау пернелерін пайдаланып кемені оңға және солға жылжыта алады және бос орын пернесін пайдаланып оқ ата алады. Ойын басталған кезде, өзге планеталықтар флоты аспанды толтырып, экранның бойымен және төмен қарай жылжиды. Ойыншы өзге планеталықтарды атып, жояды. Егер ойыншы барлық өзге планеталықтарды жойса, алдыңғы флотқа қарағанда жылдамырақ қозғалатын жаңа флот пайда болады. кез-келген бөтен планеталық ойыншының кемесіне соқтығысса немесе экранның төменгі жағына жетсе, ойыншы кемеден айырылады. Ойыншы үш кемені жоғалтса, ойын аяқталады.
Бірінші әзірлеу кезеңінде ойыншы нұсқау пернелерін басқанда оңға және солға жылжи алатын кемені және бос орын пернесін басқан кезде оқтарды ататын кеме жасаймыз. Бұл әрекетті жасап болғаннан кейін біз өзге планеталықтарды жасап, ойынды нақтылай аламыз.
Кодтауды бастамас бұрын Pygame орнатыңыз. Біз мұны 11-тарауда pytest орнатқандай орындаймыз: pip көмегімен. 11-тарауды өткізіп алсаңыз, немесе еске түсіру қажет болса pip көмегімен pytest орнату бөлімін қараңыз.
Pygame-ді орнату үшін терминал шақыруында келесі пәрменді енгізіңіз:
$ python -m pip install --user pygame
Бағдарламаларды іске қосу немесе терминал сеансын бастау үшін python
пәрменінен басқа пәрменді пайдалансаңыз, мысалы, python3
, python
орнына сол пәрменді пайдаланып жатқаныңызға көз жеткізіңіз.
https://www.pygame.org/wiki/GettingStarted Pygame орнату үшін қосымша ақпаратты алсаңыз болады.
Бос Pygame терезесін жасау арқылы ойын құруды бастаймыз. Кейінірек біз осы терезеде кеме және өзге планеталықтар сияқты ойын элементтерін саламыз. Сондай-ақ, біз ойынымыз пайдаланушының енгізуіне жауап беретін етеміз, фон түсін орнатамыз, кеме кескінін жүктейміз.
Ойынды көрсету үшін класс жасау арқылы біз бос Pygame терезесін жасаймыз. Мәтіндік редакторда жаңа файл жасаңыз және оны alien_invasion.py ретінде сақтаңыз; содан кейін келесіні енгізіңіз:
alien_invasion.py
import sys
import pygame
class AlienInvasion:
"""Ойын элементтерін және олардың әрекетін..."""
"""...басқаруға арналған жалпы класс."""
def __init__(self):
"""Ойынды инициализациялаңыз және ойын ресурстарын жасаңыз."""
❶ pygame.init()
❷ self.screen = pygame.display.set_mode((1200, 800))
pygame.display.set_caption("Alien Invasion")
def run_game(self):
"""Ойынның негізгі циклін бастау."""
❸ while True:
# Watch for keyboard and mouse events.
# Пернетақта мен тінтуір өзгерістерін қадағалау.
❹ for event in pygame.event.get():
❺ if event.type == pygame.QUIT:
sys.exit()
# Ең соңғы салынған экранды көрсетіңіз.
❻ pygame.display.flip()
if __name__ == '__main__':
# Ойын данасын/instance жасау және іске қосу
ai = AlienInvasion()
ai.run_game()
Біріншіден, sys
және pygame
модульдерін импорттаймыз. pygame
модулінде ойын жасауға қажетті функциялар бар. Ойыншы ойыннан шыққанда, программада ойыннан шығу үшін sys
модуліндегі құралдарды қолданамыз.
Alien Invasion AlienInvasion
деп аталатын класс ретінде басталады. __init__()
әдісінде pygame.init()
функциясы Pygame дұрыс жұмыс істеуі үшін қажет фондық параметрлерді инициализациялайды ❶. Содан кейін біз ❷ дисплей терезесін жасау үшін pygame.display.set_mode()
әдісін шақырамыз, осы дисплейге ойынның барлық графикалық элементтерін саламыз. Мұндағы (1200, 800)
аргументі ойын терезесінің өлшемдерін анықтайтын кортеж болып табылады, оның ені 1200 пиксель және биіктігі 800 пиксель болады. (Бұл мәндерді дисплей өлшеміне байланысты реттеуге болады.) Біз бұл дисплей терезесін self.screen
атрибутына тағайындаймыз, сондықтан ол класстағы барлық әдістерде қолжетімді болады.
Біз self.screen
-ға тағайындаған нысан surface деп аталады. Pygame ішіндегі бет/surface ойын элементін көрсетуге болатын экранның бөлігі болып табылады. Ойынның әрбір элементі, мысалы бөтен планеталық немесе кеме, өзіндік бетте болады. display.set_mode()
арқылы қайтарылған бет бүкіл ойын терезесін білдіреді. Ойынның анимациялық циклін іске қосқанда, бұл бет цикл арқылы әр өтуде қайта сызылып-салынады, сондықтан оны пайдаланушы енгізуі арқылы енгізілген кез-келген өзгерістермен жаңартуға болады.
Ойын run_game()
әдісімен басқарылады. Бұл әдіс үздіксіз жұмыс істейтін while
❸ циклін қамтиды. while
циклінде экран жаңартуларын басқаратын оқиғалар циклі мен код бар. Оқиға/event — пайдаланушының ойын ойнау кезінде пернені басу немесе тінтуірді жылжыту сияқты әрекеті. Бағдарламамыздың оқиғаларға жауап беруі үшін оқиғаларды тыңдау үшін оқиға циклін жазамыз және орын алған оқиғалар түріне байланысты тиісті тапсырмаларды орындаймыз. ❹ for
циклі while
циклінің ішінде кірістірілген оқиға циклі болып табылады.
Pygame анықтайтын оқиғаларға қол жеткізу үшін pygame.event.get()
функциясын қолданамыз. Бұл функция осы функция соңғы рет шақырылғаннан бері орын алған оқиғалардың тізімін қайтарады. Кез-келген пернетақта немесе тінтуір оқиғасы осы for
циклінің іске қосылуына себеп болады. Цикл ішінде біз нақты оқиғаларды анықтау және оларға жауап беру үшін if
амалдарының қатарын жазамыз. Мысалы, ойыншы ойын терезесінің жабу түймесін басқанда, pygame.QUIT
оқиғасы көрінеді және біз ойынынан шығу үшін sys.exit()
әдісін шақырамыз ❺.
pygame.display.flip()
❻ әдісіне шақыру жасау Pygame-ге соңғы салынған экранды көрсетуді ұсынады. Бұл жағдайда ол жай ғана while
циклі арқылы әрбір өтуде бос экранды сызып, ескі экранды өшіреді, осылайша тек жаңа экран көрінеді. Ойын элементтерін жылжытқанда, pygame.display.flip()
ойын элементтерінің жаңа орындарын көрсету және ескілерін жасыру үшін дисплейді үнемі жаңартып, бірқалыпты қозғалыс елесін жасайды.
Файлдың соңында біз ойынның данасын жасаймыз, содан кейін run_game()
әдісін шақырамыз. Біз run_game()
әдісін осы кодтар жазылған осы файл тікелей шақырғанда ғана іске қосылатын if
блогына орналастырамыз. Осы alien_invasion.py файлын іске қосқан кезде бос Pygame терезесін көресіз.
Ойынымыз жақсы жұмыс істеу үшін оның жалдамдығы-frame rate барлық жүйелерде бердей орындалуы керек. Бірнеше жүйеде жұмыс істей алатын ойынның кадр жиілігін басқару күрделі мәселе, бірақ Pygame бұл мақсатқа жетудің салыстырмалы түрде қарапайым әдісін ұсынады. Біз сағат жасаймыз және бағдарлама негізгі цикл арқылы әр өткенде сағат бір жылжуын қамтамасыз етеміз. Егер цикл біз анықтаған жылдамдықтан тез өңделіп кетсе, Pygame ойын тұрақты жылдамдықпен жұмыс істеуі үшін үзіліс уақытын есептеп, керекті үзілісті жасайды.
Сағатты __init__()
әдісінде анықтаймыз:
alien_invasion.py
def __init__(self):
"""Ойынды инициализациялаңыз және ойын ресурстарын жасаңыз."""
pygame.init()
self.clock = pygame.time.Clock()
--snip/код үзіндісі--
pygame
инициализациясынан кейін pygame.time
модулінен Clock
класының данасын жасаймыз. Содан кейін run_game()
ішіндегі while
циклінің соңында сағат белгісін қоямыз:
def run_game(self):
"""Ойынның негізгі циклін бастау."""
while True:
--snip/код үзіндісі--
pygame.display.flip()
self.clock.tick(60)
tick()
әдісі бір аргумент алады: ойынға арналған кадр жиілігі/frame rate. Мұнда біз 60 мәнін пайдаланып жатырмыз, сондықтан Pygame циклды секундына дәл 60 рет орындау үшін барын салады.
Pygame сағаты ойынның көптеген жүйелерде тұрақты жұмыс істеуіне көмектесуі керек. Егер ол ойынның жүйеде тұрақты жұмыс істеуін азайтса, кадр жиілігі үшін әртүрлі мәндерді қолданып көруге болады. Жүйеде жақсы кадр жиілігін таба алмасаңыз, сағатты толығымен өшіріп, жүйеде жақсы жұмыс істеуі үшін ойын параметрлерін реттеуге болады.
Pygame әдепкі бойынша қара экран жасайды, бірақ бұл қызықсыз. Басқа фон түсін орнатайық. Мұны __init__()
әдісінің соңында орындаймыз.
alien_invasion.py
def __init__(self):
--snip/код үзіндісі--
pygame.display.set_caption("Alien Invasion")
# Set the background color.
❶ self.bg_color = (230, 230, 230)
def run_game(self):
--snip/код үзіндісі--
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Redraw the screen during each pass through the loop.
❷ self.screen.fill(self.bg_color)
# Make the most recently drawn screen visible.
pygame.display.flip()
self.clock.tick(60)
Pygame-дегі түстер RGB түстері ретінде көрсетілген: қызыл, жасыл және көктің араласуы. Әрбір түс мәні 0 мен 255 аралығында болуы мүмкін. Түс мәні (255, 0, 0)
қызыл, (0, 255, 0)
жасыл және (0, 0, 255)
көк. 16 миллион түске дейін жасау үшін әртүрлі RGB мәндерін араластыруға болады. Түс мәні (230, 230, 230)
қызыл, көк және жасыл түстерді бірдей мөлшерде араластырады, бұл ашық сұр фондық түс береді. Біз бұл түсті self.bg_color
❶ түріне тағайындаймыз.
Біз бетке әсер ететін fill()
❷ әдісі арқылы экранды фон түсімен толтырамыз, және бұл әдіс бір ғана аргумент қабылдайды: түс.
Ойынға жаңа функцияларды қосқан сайын сәйкесінше біз әдетте жаңа баптауларды жасаймыз. Баптауларды бүкіл код ішіне араластырып қосудың орнына, осы мәндердің барлығын бір жерде сақтау үшін Settings
деп аталатын классты қамтитын settings
деп аталатын модуль жазайық. Бұл тәсіл бізге жеке баптауға қол жеткізу керек болған кез-келген сәтте бір ғана settings
нысанымен жұмыс істеуге мүмкіндік береді. Бұл сонымен қатар жобамыз өсіп келе жатқанда ойынның сыртқы түрі мен әрекетін өзгертуді жеңілдетеді. Ойынды өзгерту үшін жоба ішіндегі әртүрлі баптауларды іздеудің орнына, біз қазір жасайтын settings.py ішіндегі сәйкес мәндерді өзгерте саламыз.
alien_invasion каталогыңызда settings.py атты жаңа файл жасаңыз және осы бастапқы Settings
классын қосыңыз:
settings.py
class Settings:
"""Alien Invasion ойынына қажетті барлық баптауларды сақтауға арналған класс."""
def __init__(self):
"""Ойын параметрлерін инициализациялау."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
Жобада Settings
данасын жасау және оны баптауларымызға кіруге пайдалану үшін alien_invasion.py файлын келесідей өзгерту керек:
alien_invasion.py
--snip/код үзіндісі--
import pygame
from settings import Settings
class AlienInvasion:
"""Ойын элементтері мен әрекетін басқаруға арналған жалпы клас."""
def __init__(self):
"""Ойынды инициализациялаңыз және ойын ресурстарын жасаңыз."""
pygame.init()
self.clock = pygame.time.Clock()
❶ self.settings = Settings()
❷ self.screen = pygame.display.set_mode(
(self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
def run_game(self):
--snip/код үзіндісі--
# Әрбір цикл арқылы өту кезінде экранды қайта салыңыз.
❸ self.screen.fill(self.settings.bg_color)
# Ең соңғы салынған экранды көрінетін етіңіз.
pygame.display.flip()
self.clock.tick(60)
--snip/код үзіндісі--
Біз Settings
негізгі бағдарлама файлына импорттаймыз. Содан кейін Settings
данасын жасаймыз және оны self.settings
❶ параметріне тағайындаймыз, pygame.init()
әдісін шақырғаннан кейін. Ойын ❷ экранын жасағанда, біз self.settings
-тің screen_width
және screen_height
атрибуттарын қолданамыз, содан кейін біз экранды түспен бояу кезінше фон түсіне қол жеткізу үшін тағы да ❸ self.settings
-ті қолданамыз.
Енді alien_invasion.py қолданбасын іске қосқан кезде сіз әлі ешбір өзгерістерді көрмейсіз, себебі біз бұрыннан қолданып жүрген параметрлерді басқа жерге көшірдік. Енді экранға жаңа элементтер қосуға дайынбыз.
Кемені ойынымызға қосайық. Ойыншының кемесін экранға салу үшін біз кескінді жүктейміз, содан кейін суретті салу үшін Pygame blit()
әдісін қолданамыз.
blit()
бір суретті екіншісіне салу
blit(source, dest, area=None, special_flags=0) -> Rect
pygame.blit() — Pygame бағдарламасында бір кескінді екіншісіне салу үшін қолданылатын әдіс. Ол «блокты тасымалдау» дегенді білдіреді және «биттік блокты тасымалдау/bit block transfer» деген сөздің қысқартуы. Бұл әдіс әдетте ойын экранына немесе басқа бетке спрайттарды немесе кескіндерді салу үшін қолданылады.
Ойындарыңыз үшін өнер туындысын таңдағанда, лицензиялауға назар аударыңыз. Бастаудың ең қауіпсіз және ең арзан жолы - https://opengameart.org сияқты веб-сайттан пайдалануға және өзгертуге болатын еркін лицензияланған графиканы пайдалану.
Ойыныңызда кескін файлының кез-келген түрін дерлік пайдалана аласыз, бірақ нүктелік кескінді (.bmp) пайдаланған кезде бұл оңайырақ, себебі Pygame растрлық (bitmaps) кескіндерді әдепкі бойынша жүктейді. Pygame қолданбасын басқа файл түрлерін пайдалану үшін конфигурациялауға болады, бірақ кейбір файл түрлері компьютерде орнатылуы керек белгілі бір кескін кітапханаларын талап етеді. Сіз табатын кескіндердің көпшілігі .jpg немесе .png пішімінде, бірақ оларды Photoshop, GIMP және Paint сияқты құралдарды пайдаланып нүктелік кескіндерге (bitmaps) түрлендіруге болады.
Таңдалған суреттегі фон түсіне ерекше назар аударыңыз. Кескін өңдегішін пайдаланып, кез-келген фон түсімен ауыстыруға болатын мөлдір немесе біркелкі түс фоны бар файлды табуға тырысыңыз. Кескіннің өң түсі ойыныңыздың өң түсіне сәйкес келсе, ойындарыңыз жақсы көрінеді. Немесе ойынның фонын кескіннің фонымен сәйкестендіруге болады.
Alien Invasion үшін ship.bmp файлын пайдалана аласыз (12-сурет -1), ол осы кітаптың https://ehmatthes.github.io/pcc_3e сайтындағы ресурстарында қолжетімді. Файлдың өң түсі осы жобада қолданып жатқан параметрлерге сәйкес келеді. Негізгі alien_invasion жоба каталогының ішінде images деп аталатын каталогты жасаңыз. ship.bmp файлын images каталогына сақтаңыз.
12-1-сурет: Alien Invasion-ға арналған кеме
Кеме үшін суретті таңдағаннан кейін оны экранда көрсету керек. Біздің кемені пайдалану үшін біз Ship
классын қамтитын жаңа ship
модулін жасаймыз. Бұл класс ойыншы кемесінің әрекетінің көп бөлігін басқарады:
ship.py
import pygame
class Ship:
"""Кемені басқаруға арналған клас"""
def __init__(self, ai_game):
"""Кемені инициализациялаңыз және оның бастапқы орнын орнатыңыз."""
❶ self.screen = ai_game.screen
❷ self.screen_rect = ai_game.screen.get_rect()
# Кеме кескінін жүктеңіз және оның тіктөртбұрышын алыңыз
❸ self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
# Әрбір жаңа кемені экранның төменгі ортасынан бастаңыз.
❹ self.rect.midbottom = self.screen_rect.midbottom
❺ def blitme(self):
"""Кемені қазіргі орнында сызыңыз."""
self.screen.blit(self.image, self.rect)
Pygame тиімді, себебі ол барлық ойын элементтерін тіктөртбұрыштар(rects) сияқты өңдеуге мүмкіндік береді, тіпті олардың пішіні тіктөртбұрыштар сияқты болмаса да. Элементті тіктөртбұрыш ретінде қарастыру тиімді, себебі тіктөртбұрыштар қарапайым геометриялық фигуралар. Pygame екі ойын элементінің соқтығысқан-соқтығыспағанын анықтау қажет болғанда, мысалы, ол әрбір нысанды тіктөртбұрыш ретінде қарастырса, мұны жылдамырақ жасай алады. Осы тәсілдің жеткілікті жақсы жұмыс істеуіне байланысты ойыншылар біздің ойын элементтерінің нақты пішінімен жұмыс жасап жатқан жоқ екенімізді байқамайды. Бұл класста кеме мен экранды төртбұрыштар ретінде қарастырамыз.
Rect: тікбұрышты координаттарды сақтауға арналған pygame нысаны
Pygame тікбұрышты аумақтарды сақтау және өңдеу үшін pygame.Rect Rect нысандарын пайдаланады. Rect сол жақ, жоғарғы, ен және биіктік мәндерінің тіркесімінен жасалуы мүмкін.
# экрандағы (100,50) орнында орналасқан
# 20x30 пиксель тіктөртбұрышты жасайды
myRect = pygame.Rect(100,50,20,30)
Rects сондай-ақ бұрыннан Rect болып табылатын немесе «rect» деп аталатын атрибуты бар питон нысандарынан жасалуы мүмкін.
Rect нысанында Rect нысанын жылжыту және туралау үшін пайдалануға болатын бірнеше виртуалды атрибуттар бар:
Қарапайымдау нұсқасы
Классты анықтамас бұрын pygame
модулін импорттаймыз. Ship
класының __init__()
әдісі екі параметрді қабылдайды: self
сілтемесі және AlienInvasion
класының ағымдағы данасына сілтеме. Бұл Ship
қолданбасына AlienInvasion
ішінде анықталған барлық ойын ресурстарын қолжетімді етеді. Содан кейін экранды (screen) Ship
❶ атрибутына тағайындаймыз, осылайша біз screen-ға осы кластағы барлық әдістерде оңай қол жеткізе аламыз. Біз экранның rect
атрибутына get_rect()
әдісі арқылы қол жеткіземіз және оны self.screen_rect
❷ -ге тағайындаймыз. Бұл бізге кемені экранда дұрыс орынға қоюға мүмкіндік береді.
Кескінді жүктеу үшін pygame.image.load()
❸ әдісін шақырамыз және оған кеменің суреті сақталған каталог орнын береміз. Бұл функция кемені көрсететін бетті/surface қайтарады, оны біз self.image
-ге тағайындаймыз. Кескін жүктелген кезде, біз кеме бетінің rect
атрибутына қол жеткізу үшін get_rect()
шақырамыз, осылайша оны кейін кемені орналастыру үшін пайдалана аламыз.
Сіз rect
нысанымен жұмыс істегенде, осы нысанды терезеде орналастыру-жылжыту үшін жоғарғы, төменгі, сол және оң жақ ұштарының, сондай-ақ орта нүктесінің x- және y- координаталарын пайдалана аласыз. rect
-тің ағымдағы орнын орнату үшін осы мәндердің кез-келгенін жазып-орнатуға болады. Ойын элементін ортаға қойатын болсаңыз, rect
атрибуттарының center
, centerx
немесе centery
атрибуттарымен жұмыс істеңіз. Экранның шетінде жұмыс істегенде, top/жоғарғы
, bottom/төменгі
, left/сол
немесе right/оң жақ
атрибуттарымен жұмыс жасаңыз. Сондай ақ, осы қасиеттерді біріктіретін midbottom
, midtop
, midleft
және midright
сияқты атрибуттар да бар. rect
көлденең немесе тік орналасуын реттеп жатқанда, сіз жай ғана x
және y
атрибуттарын пайдалана аласыз, мұндай x- және y-оның жоғарғы сол жақ бұрышының координаталары. Бұл атрибуттар ойын әзірлеушілері бұрын қолмен жасайтын есептеулерді орындаудан құтқарады және сіз оларды жиі пайдаланасыз.
Pygame-де бастапқы нүкте (0, 0) экранның жоғарғы сол жақ бұрышында, ал координаттар төмен және оңға қарай артады. 1200×800 экранда бастапқы нүкте жоғарғы сол жақ бұрышта, ал төменгі оң жақ бұрышта координаттар (1200, 800) болады. Бұл координаттар физикалық экранға емес, ойын терезесіне қатысты.
Кемені экранның төменгі ортасына орналастырамыз. Ол үшін self.rect.midbottom
❹ мәні экранның rect
нысанының midbottom
атрибутына сәйкес болсын. Pygame осы rect
атрибуттарын кеме кескінін терезенің қақ ортасында және экранның төменгі жағымен қатарлас етіп орналастыру үшін пайдаланады.
Соңында, біз ❺ bltime
әдісін анықтаймыз, ол self.rect
арқылы көрсетілген позицияда кескінді экранға салады.
Енді alien_invasion.py файлын жаңартайық, сонда ол кеме жасайды және кеменің blitme()
әдісін шақырады:
alien_invasion.py
--snip/код үзіндісі--
from settings import Settings
from ship import Ship
class AlienInvasion:
"""Ойын активтері мен мінез-құлқын басқаруға арналған жалпы сынып."""
def __init__(self):
--snip/код үзіндісі--
pygame.display.set_caption("Alien Invasion")
❶ self.ship = Ship(self)
def run_game(self):
--snip/код үзіндісі--
# Redraw the screen during each pass through the loop.
self.screen.fill(self.settings.bg_color)
❷ self.ship.blitme()
# Make the most recently drawn screen visible.
pygame.display.flip()
self.clock.tick(60)
--snip/код үзіндісі--
Біз Ship
импорттаймыз, содан кейін экран жасалғаннан кейін Ship
данасын жасаймыз ❶. Ship()
шақыру бір аргументті қажет етеді: AlienInvasion
данасы. Мұндағы self
аргументі AlienInvasion
ағымдағы данасына сілтеме жасайды. Бұл Ship
-ке ойын ресурстарын қолжетімді ететін параметр, screen
объектісі сияқты. Біз бұл Ship
данасын self.ship
-ке тағайындаймыз.
Фонды біркелкі түске бояп-толтырғаннан кейін, біз ship.blitme()
әдісін шақыру арқылы экранда кеменің суретін саламыз, осылайша кеме фон қабатының үсінде пайда болады ❷.
alien_invasion.py қолданбасын қазір іске қосқан кезде, төменде көрсетілгендей зымыран кемесі орналасқан бос ойын экранын көресіз.12-2-сурет.
12-2-сурет: Бөтен планеталықтардың шабуылы, экранның төменгі ортасында кеме бар
Үлкен жобаларда, жаңа код жазып қосымша код қоспас бұрын алдын жазған кодты жиі қайта өзгертіп-өңдейсіз. Рефакторинг сіз жазған кодтың құрылымын жеңілдетеді және оның үстінен жаңа код жазып-құруды жеңілдетеді. Бұл бөлімде біз ұзарып бара жатқан run_game()
әдісін екі көмекші әдіске бөлеміз. Helper method/Көмекші әдіс класс ішінде жұмыс істейді, бірақ класстан тыс кодпен пайдалануға арналмаған. Python тілінде бір астын сызумен басталатын әдіс - көмекші әдіс екенін білдіреді.
Оқиғаларды басқаратын кодты _check_events()
деп аталатын бөлек әдіске жылжытамыз. Бұл run_game()
-ді жеңілдетеді және оқиғаларды басқару циклін оқшаулайды. Оқиғалар циклін оқшаулау экранды жаңарту сияқты оқиғаларды ойынның басқа аспектілерінен бөлек басқаруға мүмкіндік береді.
Міне, жаңа _check_events()
әдісі бар AlienInvasion
классы, ол тек run_game()
ішіндегі кодқа әсер етеді:
alien_invasion.py
def run_game(self):
"""Ойынның негізгі циклін бастау."""
while True:
❶ self._check_events()
# Redraw the screen during each pass through the loop.
--snip/код үзіндісі--
❷ def _check_events(self):
"""Пернелерді басу және тінтуір оқиғаларына жауап беру."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
Біз жаңа _check_events()
әдісін ❷ жасаймыз және "ойыншы терезені жабу нүктесін басты ма" соны тексеретін код жолдарын осы әдіске жылжытамыз.
Кластың ішінен әдісті шақыру үшін ❶ self
айнымалысы және әдіс атауын нүкте белгісі арқылы пайдаланыңыз. Әдісті run_game()
ішіндегі while
циклінің ішінен шақырамыз.
run_game()
нұсқасын одан әрі жеңілдету үшін экранды жаңарту кодын _update_screen()
деп аталатын бөлек әдіске жылжытамыз:
alien_invasion.py
def run_game(self):
"""Ойынның негізгі циклін бастау."""
while True:
self._check_events()
self._update_screen()
self.clock.tick(60)
def _check_events(self):
--snip/код үзіндісі--
def _update_screen(self):
"""Экрандағы кескіндерді жаңартыңыз және жаңа экранға өтіңіз."""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
pygame.display.flip()
Біз фон мен кемені салатын кодты және экранды айналдыратын кодты _update_screen()
көмекші әдісіне жылжыттық. Енді run_game()
ішіндегі негізгі циклдің денесі әлдеқайда қарапайым. Циклдың әр өтуінде жаңа оқиғалардың болған-болмағанын қадағалайтынымызды, экранды жаңартып жатқанымызды және сағаттың тикат етіп өтіп жатқанын түсіну оңай.
Егер сіз бірнеше ойын жасап қойған болсаңыз, кодты осындай әдістерге бөлуден бастаған боларсыз. Бірақ мұндай жобамен ешқашан айналыспаған болсаңыз, бастапқыда кодты қалай құрылымдау керектігін білмеуіңіз мүмкін. Бұл тәсіл сізге нақты код жазу процесі туралы түсінік береді: сіз кодты мүмкіндігінше қарапайым жаза бастайсыз, содан кейін жобаңыз күрделене түскен сайын оны қайта өңдейсіз.
Енді біз жаңа код жолдарын қосуды жеңілдету үшін рефакторинг жасап болған соң, біз ойынның динамикалық аспектілерімен жұмыс істей аламыз!
12-1. Көк аспан: Көк фондық Pygame терезесін жасаңыз.
12-2. Ойын сипаты: Өзіңізге ұнайтын ойын кейіпкерінің растрлық кескінін табыңыз немесе суретті нүктелік суретке түрлендіру. Экранның ортасында таңбаны салатын класс жасаңыз, содан кейін кескіннің өң түсін экранның фон түсіне немесе керісінше сәйкестендіріңіз.
Одан кейін біз ойыншыға кемені оңға және солға жылжыту мүмкіндігін береміз. Ойыншы оң немесе сол жақ көрсеткі пернесін басқанда жауап беретін кодты жазамыз. Біз алдымен оңға қарай қозғалысқа назар аударамыз, содан кейін солға қарай қозғалысты басқару үшін бірдей принциптерді қолданамыз. Бұл кодты қосқанда, экрандағы кескіндердің қозғалысын басқаруды және пайдаланушы енгізуіне жауап беруді үйренесіз.
Ойыншы пернені басқанда, сол пернені басу Pygame жүйесінде оқиға ретінде тіркеледі. Әрбір оқиға pygame.event.get()
әдісі арқылы алынады. Біз _check_events()
әдісімізде ойында қандай оқиғаларды тексергіміз келетінін көрсетуіміз керек. Әрбір пернені басу KEYDOWN
оқиғасы ретінде тіркеледі.
Pygame KEYDOWN
оқиғасын анықтаған кезде, басылған перне белгілі бір әрекетті тудыратын перне екенін тексеруіміз керек. Мысалы, егер ойыншы оң жақ көрсеткі пернесін басса, кемені оңға жылжыту үшін кеменің rect.x
мәнін арттырғымыз келеді:
alien_invasion.py
def _check_events(self):
"""Пернелерді басу және тінтуір оқиғаларына жауап беру."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
❶ elif event.type == pygame.KEYDOWN:
❷ if event.key == pygame.K_RIGHT:
# Move the ship to the right.
❸ self.ship.rect.x += 1
_check_events()
функция ішіндегі оқиғалар цикліне (event loop) ❶ elif
блогын қосамыз, ол Pygame KEYDOWN
оқиғасын анықтағанда сол әрекетке кері әрекетпен жауап береді. Басылған перне, event.key
, оң жақ көрсеткі ❷ ме, соны тексереміз. Оң жақ көрсеткі перне pygame.K_RIGHT
арқылы көрсетіледі. Оң жақ көрсеткі пернесі басылған болса, self.ship.rect.x
мәнін 1-ге ❸ арттыру арқылы кемені оңға жылжытамыз.
Қазір alien_invasion.py іске қосылғанда, оң жақ көрсеткі пернесін басқан сайын кеме оңға бір пиксельге жылжуы керек. Бұл бастама, және кемені басқарудың тиімді жолы емес. Үздіксіз қозғалысқа рұқсат беру арқылы бұл басқаруды жақсартайық.
Ойыншы оң жақ көрсеткі пернесін басып тұрғанда, ойыншы пернені босатып-жібермегенше кеменің оң жақ бағытта қозғалуын қалаймыз. Ойынның pygame.KEYUP
оқиғасын анықтауын қамтамасыз етеміз, осылайша оң жақ көрсеткі перненің қашан босатылғанын білеміз; содан кейін үздіксіз қозғалысты жүзеге асыру үшін KEYDOWN
және KEYUP
оқиғаларын moving_right
деп аталатын жалаушамен бірге қолданамыз.
moving_right
жалауы False
болғанда, кеме қозғалыссыз болады. Ойыншы оң жақ көрсеткі пернесін басқанда, жалаушаны True
күйіне орнатамыз, ал ойыншы пернені босатқанда, жалаушаны қайтадан False
күйіне орнатамыз.
Ship
классы кеменің барлық атрибуттарын басқарады, сондықтан біз оған moving_right
жалауының күйін тексеру үшін moving_right
деп аталатын атрибутты және update()
әдісін береміз. update()
әдісі жалауша True
күйіне орнатылған болса, кеменің орнын өзгертеді. Біз бұл әдісті кеменің орнын жаңарту үшін while
циклі арқылы әр өткенде бір рет шақырамыз.
Ship
класындағы код өзгертулері манандай:
ship.py
class Ship:
"""Кемені басқаруға арналған сынып."""
def __init__(self, ai_game):
--snip/код үзіндісі--
# Start each new ship at the bottom center of the screen.
self.rect.midbottom = self.screen_rect.midbottom
# Movement flag; start with a ship that's not moving.
❶ self.moving_right = False
❷ def update(self):
"""Қозғалыс жалауы негізінде кеменің орнын жаңарту."""
if self.moving_right:
self.rect.x += 1
def blitme(self):
--snip/код үзіндісі--
Біз self.moving_right
атрибутын __init__()
әдісіне қосамыз және оны бастапқыда False мәніне орнатамыз ❶. Одан кейін жалау True
болса, кемені оңға жылжытатын update()
қосамыз ❷. update()
әдісі класстан тыс шақырылады, сондықтан ол көмекші әдіс болып саналмайды.
Енді біз _check_events()
параметрін өзгертуіміз керек, осылайша moving_right
оң жақ көрсеткі пернесін басқанда True
және перне босатылған кезде False
мәніне орнатылады:
alien_invasion.py
def _check_events(self):
"""Пернелерді басу және тінтуір оқиғаларына жауап беру."""
for event in pygame.event.get():
--snip/код үзіндісі--
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
❶ self.ship.moving_right = True
❷ elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
Бұл жерде біз ойыншы оң жақ көрсеткі пернесін басқан кезде ойынның қалай жауап беретінін өзгертеміз: кеменің орнын пиксельдеп тікелей өзгертудің орнына, біз жәй ғана ❶ moving_right
мәнін True
-ге өзгерте салдық. Содан кейін біз KEYUP
оқиғаларына ❷ жауап беретін жаңа elif
блогын қосамыз. Ойыншы оң жақ көрсеткі пернесін (K_RIGHT
) босатқанда, біз moving_right
параметрін False
күйіне орнатамыз.
Келесі, run_game()
ішіндегі while
циклін өзгертеміз, осылайша ол цикл арқылы әрбір өтуде кеменің update()
әдісін шақырады:
alien_invasion.py
def run_game(self):
"""Ойынның негізгі циклін бастау."""
while True:
self._check_events()
self.ship.update()
self._update_screen()
self.clock.tick(60)
Кеменің орны пернетақта оқиғаларын тексергеннен кейін және экранды жаңарту алдында жаңартылады. Бұл ойыншының енгізуіне жауап ретінде кеменің орнын жаңартуға мүмкіндік береді және кемені экранға салу кезінде жаңартылған орынның қолданылуын қамтамасыз етеді.
alien_invasion.py файлын іске қосып, оң жақ көрсеткі пернесін басып тұрғанда, сіз кілтті босатқанша кеме үздіксіз оңға қарай жылжуы керек.
Енді кеме үздіксіз оңға қарай жылжи алатындықтан, солға қозғалыс қосу оңай. Тағы да, біз Ship
классын және _check_events()
әдісін өзгертеміз. Мұнда __init__()
және update()
үшін Ship
ішіндегі тиісті өзгерістер берілген:
ship.py
def __init__(self, ai_game):
--snip/код үзіндісі--
# Movement flags; start with a ship that's not moving.
self.moving_right = False
self.moving_left = False
def update(self):
"""Қозғалыс жалаулары негізінде кеменің орнын жаңарту."""
if self.moving_right:
self.rect.x += 1
if self.moving_left:
self.rect.x -= 1
__init__()
ішінде біз self.moving_left
жалауын қосамыз. update()
жүйесінде кеменің rect.x екі көрсеткі пернені басып тұрғанда көбейту керек, содан кейін азаяды. Бұл кеменің орнында тұруына әкеледі. Егер солға қозғалыс үшін
elif
қолдансақ, оң жақ көрсеткі перне әрқашан басымдыққа ие болады. Екі if
блогын пайдалану, ойыншы бағытты өзгерткен кезде екі пернені де бір сәт басып тұруы мүмкін болғанда, қозғалыстарды дәлірек етеді.
Біз _check_events()
файлына екі толықтыру енгізуіміз керек:
alien_invasion.py
def _check_events(self):
"""Пернелерді басу және тінтуір оқиғаларына жауап беру."""
for event in pygame.event.get():
--snip/код үзіндісі--
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
Егер K_LEFT
пернесі үшін KEYDOWN
оқиғасы орын алса, moving_left
мәнін True
мәніне орнатамыз. K_LEFT
пернесі үшін KEYUP
оқиғасы орын алса, moving_left
мәнін False
мәніне орнатамыз. Мұнда біз elif
блоктарын пайдалана аламыз, себебі әрбір оқиға тек бір кілтке қосылған. Ойнатқыш екі пернені бірден басса, екі бөлек оқиға анықталады.
Қазір alien_invasion.py іске қосылғанда, кемені үздіксіз оңға және солға жылжыту мүмкіндігіңіз болуы керек. Екі пернені басып тұрсаңыз, кеме қозғалысын тоқтатуы керек.
Кейін кеменің қозғалысын одан әрі нақтылайтын боламыз. Кеменің жылдамдығын реттеп, экранның шетіне жеткенде жоғалып кетпес үшін оның қаншалықты қозғала алатынын шектейік.
Қазір кеме while
циклі арқылы цикл сайын бір пиксельді жылжытады, бірақ біз Settings
классына ship_speed
атрибутын қосу арқылы кеме жылдамдығын жақсырақ басқара аламыз. Біз бұл атрибутты цикл арқылы әрбір өтуде кемені қаншалықты жылжыту керектігін анықтау үшін қолданамыз. Міне, settings.py ішіндегі жаңа атрибут:
settings.py
class Settings:
"""Жат планеталық шабуылға арналған барлық параметрлерді сақтауға арналған сынып."""
def __init__(self):
--snip/код үзіндісі--
# Ship settings
self.ship_speed = 1.5
Біз ship_speed
бастапқы мәнін 1,5
етіп орнаттық. Кеме қазір қозғалғанда, оның орны цикл арқылы әр өту кезінде 1,5 пикселге (1 пиксельге емес) өзгертіледі.
Ойынның қарқын-жылдамдығын кейінірек арттырған кезде кеме жылдамдығын жақсырақ басқаруға мүмкіндік беру үшін жылдамдық параметрі үшін біз қазір бөлшек сан қолданамыз. Дегенмен, x
сияқты rect
атрибуттары тек бүтін мәндерді сақтайды, сондықтан Ship
-ке кейбір өзгертулер енгізу керек:
ship.py
class Ship:
"""Кемені басқаруға арналған сынып."""
def __init__(self, ai_game):
"""Кемені инициализациялаңыз және оның бастапқы орнын орнатыңыз."""
self.screen = ai_game.screen
❶ self.settings = ai_game.settings
--snip/код үзіндісі--
# Start each new ship at the bottom center of the screen.
self.rect.midbottom = self.screen_rect.midbottom
# Store a float for the ship's exact horizontal position.
❷ self.x = float(self.rect.x)
# Movement flags; start with a ship that's not moving.
self.moving_right = False
self.moving_left = False
def update(self):
"""Қозғалыс жалаулары негізінде кеменің орнын жаңарту."""
# Update the ship's x value, not the rect.
if self.moving_right:
❸ self.x += self.settings.ship_speed
if self.moving_left:
self.x -= self.settings.ship_speed
# Update rect object from self.x.
❹ self.rect.x = self.x
def blitme(self):
--snip/код үзіндісі--
Біз Ship
үшін settings
атрибутын жасаймыз, сондықтан оны update()
❶ функциясында пайдалана аламыз. Біз кеменің орнын пикселдің бөліктері бойынша реттеп жатқандықтан, позицияны оған тағайындалған қалқымалы мәнге ие айнымалыға тағайындауымыз керек. rect
атрибутын орнату үшін float пайдалана аласыз, бірақ rect
сол мәннің бүтін бөлігін ғана сақтайды. Кеменің орнын дәл қадағалау үшін біз жаңа self.x
❷ анықтаймыз. self.rect.x
мәнін қалқымалы мәнге түрлендіру және бұл мәнді self.x
мәніне тағайындау үшін float()
функциясын қолданамыз.
Енді update()
ішіндегі кеме орнын өзгерткен кезде, self.x
мәні settings.ship_speed
ішінде сақталған мәнмен реттеледі ❸. self.x
жаңартылғаннан кейін, кеме орнын басқаратын self.rect.x
жаңарту үшін жаңа мәнді пайдаланамыз.❹. self.x
файлының бүтін бөлігі ғана self.rect.x
-ге тағайындалады, бірақ бұл кемені көрсету үшін жақсы.
Енді біз ship_speed
мәнін өзгерте аламыз және 1-ден жоғары кез-келген мән кемені жылдамырақ жылжытады. Бұл кеменің бөгде планеталықтарды атып түсіру үшін жеткілікті жылдам әрекет етуіне көмектеседі және ойыншы геймплей барысында алға жылжып жатқанда ойын қарқынын өзгертуге мүмкіндік береді.
Кодтың қазіргі жағдайында оң жақ не сол көрсеткі пернені тым ұзақ басып тұрсаңыз, кеме экранның екі шетінен де жоғалып кетеді. Кеме экранның шетіне жеткенде қозғалысын тоқтататындай етіп түзетейік. Біз мұны update()
әдісін Ship
ішінде өзгерту арқылы жасаймыз:
ship.py
def update(self):
"""Қозғалыс жалаулары негізінде кеменің орнын жаңарту."""
# Update the ship's x value, not the rect.
❶ if self.moving_right and self.rect.right < self.screen_rect.right:
self.x += self.settings.ship_speed
❷ if self.moving_left and self.rect.left > 0:
self.x -= self.settings.ship_speed
# Update rect object from self.x.
self.rect.x = self.x
Бұл код self.x
мәнін өзгертпес бұрын кеменің орнын тексереді. self.rect.right
коды кеменің rect
оң жақ жиегінің x- координатын қайтарады. Бұл мән self.screen_rect.right
қайтарған мәннен аз болса, кеме экранның оң жақ шетіне жеткен жоқ ❶. Сол жақ жиекке де солай болады: егер rect
сол жағының мәні 0-ден үлкен болса, кеме экранның сол жақ жиегіне жетпеген ❷. Бұл self.x
мәнін реттемес бұрын кеменің осы шектерде болуын қамтамасыз етеді.
Қазір alien_invasion.py іске қосылғанда, кеме экранның кез-келген шетінде қозғалуды тоқтатуы керек. Бұл өте керемет; біз тек if
амалынане шартты сынақты қостық, бірақ кеме экранның екі шетіндегі қабырғаға немесе күш өрісіне соғылғандай әсер қалдырады!
Ойынның дамуын жалғастырған сайын _check_events()
әдісі ұзарады, сондықтан _check_events()
әдісін екі бөлек әдіске бөлейік: біреуі KEYDOWN
оқиғаларын өңдейді және екіншісі KEYUP
оқиғаларын өңдейді:
alien_invasion.py
def _check_events(self):
"""Пернелерді басу және тінтуір оқиғаларына жауап беру."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self, event):
"""Пернелерді басуға жауап беру."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
def _check_keyup_events(self, event):
"""Негізгі шығарылымдарға жауап беру."""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
Біз екі жаңа көмекші әдіс жасаймыз: _check_keydown_events()
және _check_keyup_events()
. Әрқайсысына self
параметрі және event
параметрі қажет. Бұл екі әдістің денесі _check_events()
ішінен көшірілді және біз ескі кодты жаңа әдістерге шақырулармен ауыстырдық. _check_events()
әдісі енді осы таза код құрылымымен оңайырақ, бұл ойыншы енгізуіне әрі қарай жауаптарды әзірлеуді жеңілдетеді.
Енді біз пернелерді басуға тиімді жауап беріп жатқандықтан, ойыннан шығудың басқа жолын қоса аламыз. Жаңа мүмкіндікті сынаған сайын ойынды аяқтау үшін ойын терезесінің жоғарғы жағындағы X түймесін басу жалықтырады, сондықтан ойыншы Q түймесін басқанда ойынды аяқтау үшін пернелер тіркесімін қосамыз:
alien_invasion.py
def _check_keydown_events(self, event):
--snip/код үзіндісі--
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
_check_keydown_events()
ішінде біз ойыншы Q пернесін басқанда ойынды аяқтайтын жаңа блок қосамыз. Енді тестілеу кезінде, жабу үшін жүгіргіні пайдаланудың орнына ойынды жабу үшін Q түймесін басуға болады.
Pygame-де толық экран режимі бар, ол ойынды кәдімгі терезеде іске қосқаннан гөрі ұнауы мүмкін. Кейбір ойындар толық экран режимінде жақсырақ көрінеді, ал кейбір жүйелерде ойын толық экран режимінде жақсырақ жұмыс істеуі мүмкін.
Ойынды толық экран режимінде іске қосу үшін __init__()
ішінде келесі өзгерістерді жасаңыз:
alien_invasion.py
def __init__(self):
"""Ойынды инициализациялаңыз және ойын ресурстарын жасаңыз."""
pygame.init()
self.settings = Settings()
❶ self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
❷ self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption("Alien Invasion")
Экран бетін құру кезінде біз (0, 0)
өлшемін және pygame.FULLSCREEN
параметрін береміз.❶. Бұл Pygame-ге экранды толтыратын терезе өлшемін анықтауды ұсынады. Біз экранның ені мен биіктігін алдын ала білмегендіктен, экран жасалғаннан кейін бұл параметрлерді жаңартамыз.❷.Біз settings
нысанын жаңарту үшін экранның rect
атрибуттарының width
және height
атрибуттарын қолданамыз.
Егер ойынның толық экран режимінде қалай көрінетіні немесе әрекеті ұнаса, осы параметрлерді сақтаңыз. Егер сізге ойын өз терезесінде ұнаса, ойынға арнайы экран өлшемін орнатқан бастапқы әдіске оралуға болады.
Ойынды толық экран режимінде іске қоспас бұрын Q түймесін басу арқылы шығуға болатынына көз жеткізіңіз; Pygame толық экран режимінде ойыннан шығудың әдепкі әдісін ұсынбайды.
Келесі бөлімде біз оқ ату мүмкіндігін қосамыз, ол bullet.py деп аталатын жаңа файлды қосып, кейбір файлдарға өзгертулер енгізуді қамтиды. Дәл қазір бізде бірнеше класстар мен әдістерді қамтитын үш файл бар. Жобаның қалай ұйымдастырылғанын түсіну үшін қосымша функцияларды қоспас бұрын осы файлдардың әрқайсысын қарап шығайық.
Негізгі файл alien_invasion.py құрамында AlienInvasion
классы бар. Бұл класс ойын барысында қолданылатын бірқатар маңызды атрибуттарды жасайды: параметрлер settings
атрибутына тағайындалады, негізгі дисплей беті/surface screen
атрибутына тағайындалады және ship
данасы да осы файлда жасалады. Ойынның негізгі циклі, while
циклі де осы модульде сақталады. while
циклі _check_events()
, ship.update()
және _update_screen()
функцияларын шақырады. Ол сонымен қатар цикл арқылы әр өту кезінде сағатты белгілейді.
_check_events()
әдісі пернені басу және шығару сияқты сәйкес оқиғаларды анықтайды және осы оқиғалар түрлерінің әрқайсысын _check_keydown_events()
және _check_keyup_events()
әдістері арқылы өңдейді. Әзірге бұл әдістер кеменің қозғалысын басқарады. AlienInvasion
классында сонымен қатар негізгі цикл арқылы әрбір өтуде экранды қайта салатын _update_screen()
бар.
alien_invasion.py файлы Alien Invasion ойынын ойнағыңыз келгенде іске қосу қажет жалғыз файл. Басқа файлдар, settings.py және ship.py, осы файлға импортталған кодты қамтиды.
settings.py файлында Settings
классы бар. Бұл класста ойынның сыртқы түрі мен кеменің жылдамдығын басқаратын атрибуттарды инициализациялайтын __init__()
әдісі ғана бар.
ship.py файлында Ship
классы бар. Ship
классында __init__()
әдісі, кеменің орнын басқаруға арналған update()
әдісі және blitme()
әдісі арқылы кемені экранға салып-шығарамыз. Кеме суреті images каталогында орналасқан ship.bmp ішінде сақталады.
12-3. Pygame құжаттамасы: Біз ойынға жеткілікті түрде жеттік, сондықтан кейбір Pygame құжаттамасын қарастырғыңыз келуі мүмкін. Pygame басты беті https://pygame.org мекенжайында, ал құжаттаманың басты беті https://pygame.org/docs мекенжайында орналасқан.. Әзірге құжаттаманы қарап шығыңыз. Бұл жобаны аяқтау үшін бұл сізге қажет емес, бірақ Alien Invasion бағдарламасын өзгерткіңіз келсе немесе одан кейін өз ойыныңызды жасағыңыз келсе, ол көмектеседі.
12-4. Зымыран: Экранның ортасында ракетадан басталатын ойын жасаңыз. Ойыншыға төрт көрсеткі пернені пайдаланып зымыранды жоғары, төмен, солға немесе оңға жылжытуға рұқсат беріңіз. Зымыран ешқашан экранның кез-келген шетінен қозғалмайтынына көз жеткізіңіз.
12-5. Кілттер: Бос экран жасайтын Pygame файлын жасаңыз. Оқиғалар циклінде pygame.KEYDOWN
оқиғасы анықталған сайын event.key
атрибутын басып шығарыңыз. Бағдарламаны іске қосыңыз және Pygame қалай жауап беретінін көру үшін әртүрлі пернелерді басыңыз.
Енді оқ ату мүмкіндігін қосайық. Ойыншы бос орын пернесін басқан кезде біз кішкентай тіктөртбұрышпен бейнеленген оқ ататын кодты жазамыз. Содан кейін оқтар экранның жоғарғы жағында жоғалып кеткенше тікелей экранға көтеріледі.
__init__()
әдісінің соңында жаңа Bullet
класына қажет мәндерді қосу үшін settings.py модульін жаңартамыз:
settings.py
def __init__(self):
--snip/код үзіндісі--
# Bullet settings
self.bullet_speed = 2.0
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
Бұл параметрлер ені 3
пиксель және биіктігі 15
пиксель болатын қою сұр түсті оқтарды жасайды. Оқтар кемеден сәл жылдамырақ қозғалады.
Енді Bullet
классымызды сақтау үшін bullet.py файлын жасаңыз. Міне, bullet.py файлының бірінші бөлігі:
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""Кемеден атылған оқтарды басқару класы."""
def __init__(self, ai_game):
"""Кеменің ағымдағы орнында оқ нысанын жасаңыз."""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
self.color = self.settings.bullet_color
# Create a bullet rect at (0, 0) and then set correct position.
❶ self.rect = pygame.Rect(0, 0, self.settings.bullet_width,
self.settings.bullet_height)
❷ self.rect.midtop = ai_game.ship.rect.midtop
# Store the bullet's position as a float.
❸ self.y = float(self.rect.y)
Bullet
классы Sprite
класынан мұраланады, біз оны pygame.sprite
модулінен импорттаймыз. Спрайттарды пайдаланған кезде ойыныңыздағы қатысты элементтерді топтап, барлық топтастырылған элементтерде бірден әрекет ете аласыз. Оқ/Bullet данасын жасау үшін __init__()
қолданбасына AlienInvasion
ағымдағы данасы қажет және біз Sprite
ішінен дұрыс мұра алу үшін super()
шақырамыз. Біз сондай-ақ экран мен параметрлер нысандары мен таңбалауыш түсі үшін атрибуттарды орнатамыз.
Содан кейін оқтың rect
атрибутын жасаймыз❶. Оқ кескінге негізделмеген, сондықтан pygame.Rect()
классын пайдаланып, нөлден бастап rect
құруымыз керек. Бұл класс rect
жоғарғы сол жақ бұрышының x- және y- координаттарын және ені мен биіктігін қажет етеді. Біз rect
мәнін (0, 0) етіп инициализациялаймыз, бірақ оны келесі код бөліктерінде дұрыс орынға жылжытамыз, себебі оқтың орны кеменің орнына байланысты. Оқтың ені мен биіктігін self.settings
ішінде сақталған мәндерден аламыз.
Біз оқтың midtop
атрибутын кеменің midtop
атрибуты ❷ сәйкестендіру үшін орнаттық. Бұл оқ кеменің жоғарғы жағынан шығып, оқ кемеден атылғандай болады. Оқтың y-координатасы үшін қалқымалы мәнді пайдаланамыз, осылайша таңбаның жылдамдығына ❸ дәл түзетулер жасай аламыз.
Міне, bullet.py, update()
және draw_bullet()
файлдарының екінші бөлігі:
bullet.py
def update(self):
"""Оқ таңбаны экранда жоғары жылжытыңыз."""
# Update the exact position of the bullet.
❶
self.y -= self.settings.bullet_speed
# Update the rect position.
❷
self.rect.y = self.y
def draw_bullet(self):
"""Оқ таңбаны экранға сызыңыз."""
❸
pygame.draw.rect(self.screen, self.color, self.rect)
update()
әдісі оқтың орнын басқарады. Оқ атылғанда, ол төмендейтін y координат мәніне сәйкес келетін экранда жоғары жылжиды. Орынды жаңарту үшін settings.bullet_speed
ішінде сақталған соманы self.y
❶ ішінен шегереміз. Содан кейін self.y
мәнін self.rect.y
❷ мәнін орнату үшін пайдаланамыз.
bullet_speed
параметрі ойын барысында немесе ойын тәртібін жақсарту үшін қажет болғанда оқтардың жылдамдығын арттыруға мүмкіндік береді. Оқ атылғаннан кейін біз оның x-координатының мәнін ешқашан өзгертпейміз, сондықтан ол кеме қозғалса да, тік сызық бойымен қозғалады.
Оқ салғымыз келгенде, draw_bullet()
функциясын шақырамыз. draw.rect()
функциясы оқтың rect
арқылы анықталған экран бөлігін self.color
ішінде сақталған түспен толтырады.❸.
Енді бізде Bullet
классы және анықталған қажетті параметрлер бар, біз ойыншы бос-орын/spacebar/пробел пернесін басқан сайын оқ ату үшін код жаза аламыз. Біз барлық атылып, ұшып бара жатқан оқтарды сақтау үшін AlienInvasion
ішінде топ құрамыз, осылайша біз бұрыннан атылған оқтарды басқара аламыз. Бұл топ pygame.sprite.Group
классының данасы болады, ол ойындарды құру кезінде пайдалы болатын кейбір қосымша функциялары бар тізім сияқты әрекет етеді. Біз бұл топты негізгі цикл арқылы әрбір өтуде экранға таңбалар салу және әрбір таңбаның орнын жаңарту үшін пайдаланамыз.
Біріншіден, жаңа Bullet
классын импорттаймыз:
alien_invasion.py
--snip/код үзіндісі--
from ship import Ship
from bullet import Bullet
Кейін біз __init__()
ішіндегі оқтарды сақтайтын топты жасаймыз:
alien_invasion.py
def __init__(self):
--snip/код үзіндісі--
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
Одан кейін біз while
циклі арқылы әр өтудегі оқтардың орнын жаңартуымыз керек:
alien_invasion.py
def run_game(self):
"""Ойынның негізгі циклін бастау."""
while True:
self._check_events()
self.ship.update()
self.bullets.update()
self._update_screen()
self.clock.tick(60)
Топта update()
шақырған кезде, топ автоматты түрде топтағы әрбір спрайт үшін update()
шақырады. self.bullets.update()
жолы bullets
тобына орналастыратын әрбір оқ үшін bullet.update()
шақырады.
AlienInvasion
жүйесінде ойыншы бос орын пернесін басқан кезде оқ ату үшін _check_keydown_events()
параметрін өзгертуіміз керек. Бізге _check_keyup_events()
параметрін өзгертудің қажеті жоқ, себебі бос орын босатылған кезде ештеңе болмайды. Сондай-ақ, flip()
шақырмас бұрын әрбір таңбалауыштың экранға сызылғанына көз жеткізу үшін _update_screen()
өзгертуіміз керек.
Оқ атылғанда біраз жұмыс істеу керек, сондықтан осы жұмысты орындау үшін _fire_bullet()
атты жаңа әдісті жазайық:
alien_invasion.py
def _check_keydown_events(self, event):
--snip/код үзіндісі--
elif event.key == pygame.K_q:
sys.exit()
❶ elif event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_keyup_events(self, event):
--snip/код үзіндісі--
def _fire_bullet(self):
"""Жаңа таңбалауыш жасаңыз және оны таңбалар тобына қосыңыз."""
❷ new_bullet = Bullet(self)
❸ self.bullets.add(new_bullet)
def _update_screen(self):
"""Экрандағы кескіндерді жаңартыңыз және жаңа экранға өтіңіз."""
self.screen.fill(self.settings.bg_color)
❹ for bullet in self.bullets.sprites():
bullet.draw_bullet()
self.ship.blitme()
pygame.display.flip()
--snip/код үзіндісі--
Бос орын басылғанда _fire_bullet()
шақырамыз ❶. _fire_bullet()
ішінде біз Bullet
данасын жасап, оны new_bullet
деп атаймыз.❷. Содан кейін оны add()
әдісі арқылы bullets
тобына қосамыз.❸. add()
әдісі append()
әдісіне ұқсас, бірақ ол Pygame топтары үшін арнайы жазылған.
bullets.sprites()
әдісі bullets
тобындағы барлық спрайттардың тізімін қайтарады. Барлық атылған оқтарды экранға салу үшін bullets
ішіндегі спрайттарды айналдырып, әрқайсысында draw_bullet()
шақырамыз ❹. Біз бұл циклді кемені салатын код жолының алдына қоямыз, сондықтан оқтар кеменің үстінен шықпайды.
Қазір alien_invasion.py қолданбасын іске қосқан кезде, кемені оңға және солға жылжытып, қалағаныңызша көп оқ атуға мүмкіндігіңіз болуы керек. 12-3-сурет-де көрсетілгендей, оқтар экранда жоғары қозғалады және жоғарғы жағына жеткенде жоғалады. Оқтардың өлшемін, түсін және жылдамдығын settings.py арқылы өзгертуге болады.
12-3-сурет: Оқтардың сериясын атқаннан кейінгі кеме
Қазіргі уақытта оқтар ойын экранының жоғарғы жағына жеткенде жоғалып кетеді, бірақ тек Pygame оларды экранның жоғарғы жағынан көрсете алмайтындықтан ғана. Оқтар шын мәнінде бар болуын жалғастырады; олардың y-координат мәндері барған сайын теріс өседі. Бұл мәселе туындатады, себебі олар жад пен өңдеу қуатын тұтынуды жалғастыруда.
Осы ескі оқтардан құтылуымыз керек, әйтпесе ойын көптеген қажетсіз жұмысты орындаудан баяулайды. Бұл әрекетті орындау үшін, оқтың rect
мәнінің bottom
мәні экранның жоғарғы жағынан өткенін көрсететін 0 мәніне ие болғанда анықтауымыз керек:
alien_invasion.py
def run_game(self):
"""Ойынның негізгі циклін бастау."""
while True:
self._check_events()
self.ship.update()
self.bullets.update()
# Get rid of bullets that have disappeared.
❶ for bullet in self.bullets.copy():
❷ if bullet.rect.bottom <= 0:
❸ self.bullets.remove(bullet)
❹ print(len(self.bullets))
self._update_screen()
self.clock.tick(60)
Тізіммен (немесе Pygame жүйесіндегі топпен) for
циклін пайдаланған кезде, Python цикл жұмыс істеп тұрған кезде тізім бірдей ұзындықта қалады деп күтеді. Бұл for
цикліндегі тізімнен немесе топтан элементтерді жоя алмайтыныңызды білдіреді, сондықтан біз топтың көшірмесін айналдыруымыз керек. for
циклін орнату үшін copy()
әдісін қолданамыз❶,бұл бізге цикл ішіндегі бастапқы bullets
тобын өзгертуге мүмкіндік береді. Біз әрбір оқтың экранның жоғарғы жағында жоғалып кеткенін тексеру үшін тексереміз ❷. Бар болса, оны bullets
ішінен алып тастаймыз❸.Ойында қазір қанша оқтың бар екенін көрсету және экранның жоғарғы жағына жеткенде олардың жойылып жатқанын тексеру үшін print()
шақыруын енгіземіз.❹.
Егер бұл код дұрыс жұмыс істесе, біз оқтарды ату кезінде терминал шығысын бақылай аламыз және оқтардың әрбір сериясы экранның үстіңгі жағын тазартқаннан кейін оқтар саны нөлге дейін азайғанын көре аламыз. Ойынды іске қосып, таңбалардың дұрыс жойылып жатқанын тексергеннен кейін print()
шақыруын алып тастаңыз. Оны қалдырсаңыз, ойын айтарлықтай баяулайды, себебі терминалға нәтиже жазу ойын терезесіне графика салуға қарағанда көбірек уақыт алады.
Көп ату ойындары ойыншының экранда бір уақытта болуы мүмкін оқтардың санын шектейді; бұл ойыншыларды дәл атуға ынталандырады. Біз Бөтен планеталықтар шабуылында да солай істейміз.
Біріншіден, рұқсат етілген таңбалар санын settings.py ішінде сақтаңыз:
settings.py
# Bullet settings
--snip/код үзіндісі--
self.bullet_color = (60, 60, 60)
self.bullets_allowed = 3
Бұл ойыншыны бір уақытта үш оқпен шектейді. _fire_bullet()
ішінде жаңа таңбалауыш жасамас бұрын қанша таңбалауыш бар екенін тексеру үшін AlienInvasion
бағдарламасында осы параметрді қолданамыз:
alien_invasion.py
def _fire_bullet(self):
"""Жаңа таңбалауыш жасаңыз және оны таңбалар тобына қосыңыз."""
if len(self.bullets) < self.settings.bullets_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
Ойыншы бос орын пернесін басқанда, біз оқтар
ұзындығын тексереміз. Егер len(self.bullets)
үштен аз болса, біз жаңа таңбалауыш жасаймыз. Бірақ егер үш таңба белсенді болса, бос орын пернесін басқанда ештеңе болмайды. Ойынды қазір іске қосқанда, сіз тек үш топта оқ атуыңыз керек.
Біз AlienInvasion
классын жақсы ұйымдастырылған ұстағымыз келеді, сондықтан оқтрады басқару кодын жазып, тексергеннен кейін оны бөлек әдіске жылжытуға болады. Біз _update_bullets()
деп аталатын жаңа әдісті жасаймыз және оны _update_screen()
алдында қосамыз:
alien_invasion.py
def _update_bullets(self):
"""Оқтардың орнын жаңартып, ескі оқтардан арылыңыз."""
# Update bullet positions.
self.bullets.update()
# Get rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
_update_bullets()
коды қиылып, run_game()
ішінен қойылады; Мұнда біз тек түсініктемелерді нақтылау ғана істедік.
run_game()
ішіндегі while
циклі қайтадан қарапайым болып көрінеді:
alien_invasion.py
while True:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_screen()
self.clock.tick(60)
Енді біздің негізгі циклде тек минималды код бар, сондықтан әдіс атауларын жылдам оқып, ойында не болып жатқанын түсінуге болады. Негізгі цикл ойыншының енгізуін тексереді, содан кейін кеменің орнын және атылған кез-келген оқтарды жаңартады. Содан кейін біз жаңа экранды салу үшін жаңартылған позицияларды пайдаланамыз және цикл арқылы әр өтудің соңында сағатты белгілейміз.
alien_invasion.py қолданбасын тағы бір рет іске қосыңыз және оқтарды қатесіз жібере алатыныңызға көз жеткізіңіз.
12-6. Sideways Shooter: Кемені экранның сол жағына орналастыратын және ойыншыға кемені жоғары және төмен түсіруге мүмкіндік беретін ойын жазыңыз. Ойыншы бос орын пернесін басқан кезде, кемені экран арқылы оқуға айналдырыңыз. Таңбалар экраннан жоғалып кеткеннен кейін жойылғанына көз жеткізіңіз.
Бұл тарауда сіз ойынға жоспар құруды үйрендіңіз және Pygame-де жазылған ойынның негізгі құрылымын білдіңіз. Фон түсін орнатуды және параметрлерді оңай реттеуге болатын бөлек класста сақтауды үйрендіңіз. Сіз экранға сурет салуды және ойыншыға ойын элементтерінің қозғалысын басқаруды қалай беру керектігін көрдіңіз. Сіз экранда ұшатын оқтар сияқты өздігінен қозғалатын элементтерді жасадыңыз және енді қажет емес нысандарды жойдыңыз. Сондай-ақ жобадағы кодты тұрақты негізде қайта өңдеуді үйрендіңіз.
13-тарауда біз өзге планеталықтарды өзге планеталықтардың шабуылына қосамыз. Тараудың соңында сіз өзге планеталықтарды кемеңізге жеткенше атып түсіре аласыз!
Бұл тарауда біз өзге планеталықтарды Alien Invasion ойынына қосамыз. Біз экранның жоғарғы жағына бір бөтен планеталықты қосамыз, содан кейін өзге планеталықтардың бүкіл флотын жасаймыз. Біз флотты бүйірден және төмен қарай жылжытамыз және оқ тиген кез-келген өзге планеталықтарды жоямыз. Соңында, ойыншының кемелерінің санын шектейміз және ойыншының кемелері таусылғанда ойынды аяқтаймыз.
Осы тараумен жұмыс істеу барысында сіз Pygame және үлкен жобаны басқару туралы көбірек біле аласыз. Сіз сондай-ақ оқтар мен өзге планеталықтар сияқты ойын нысандары арасындағы қақтығыстарды анықтауды үйренесіз. Соқтығыстарды анықтау ойындарыңыздағы элементтер арасындағы өзара әрекеттесуді анықтауға көмектеседі. Мысалы, сіз кейіпкерді лабиринт қабырғаларының ішінде шектей аласыз немесе екі кейіпкердің арасында доп алмастырып тептіріп ойнай аласыз. Біз код жазу бағыт-бағдарын назарда ұстау үшін жоспарды қайта қарай отырып жұмыс істеуді жалғастырамыз.
Экранға өзге планеталықтар флотын қосу үшін жаңа код жазуды бастамас бұрын, жобаны қарастырып, жоспарымызды жаңартайық.
Үлкен жобада дамудың жаңа кезеңін бастағанда, жоспарыңызды қайта қарап шығып, жазғалы тұрған код арқылы нені орындағыңыз келетінін нақтылағаныңыз жөн. Бұл тарауда біз келесі әрекеттерді орындаймыз:
Функцияларды іске асыру барысында біз бұл жоспарды нақтылаймыз, бірақ бұл код жазуды бастау үшін жеткілікті.
Сонымен қатар жобадағы мүмкіндіктердің жаңа сериясымен жұмыс істей бастағанда бар кодты қарап шығу керек. Әрбір жаңа кезең әдетте жобаны күрделірек ететіндіктен, кез-келген ретсіз немесе тиімсіз кодты тазалаған дұрыс. Біз жобаны жазу барысында рефакторинг жүргізіп жатырмыз, сондықтан қазір қайта өңдеуді қажет ететін код жоқ.
Экранға бір бөтен планеталықты орналастыру экранға кеме орналастырумен бірдей. Әрбір бөтен планеталықтың әрекетін Alien
деп аталатын класс басқарады, біз оны Ship
классы сияқты құрастырамыз. Қарапайымдылық үшін нүктелік кескіндерді пайдалануды жалғастырамыз. Сіз бөтен планеталық үшін өзіңіздің суретіңізді таба аласыз немесе кітап ресурстарында https://ehmatthes.github.io/pcc_3e қол жетімді 13-1-суретте көрсетілген суретті пайдалана аласыз. Бұл кескінде экранның өң түсіне сәйкес келетін сұр фон бар. Таңдалған кескін файлын images каталогында сақтағаныңызға көз жеткізіңіз.
13-1-сурет: флот құру үшін біз пайдаланатын бөтен планеталық
Енді біз Alien
классын жазып, оны alien.py ретінде сақтаймыз:
alien.py
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""Флоттағы жалғыз шетелдікті көрсететін сынып."""
def __init__(self, ai_game):
"""Инопланетаны инициализациялаңыз және оның бастапқы орнын орнатыңыз."""
super().__init__()
self.screen = ai_game.screen
# Load the alien image and set its rect attribute.
self.image = pygame.image.load('images/alien.bmp')
self.rect = self.image.get_rect()
# Start each new alien near the top left of the screen.
❶ self.rect.x = self.rect.width
self.rect.y = self.rect.height
# Store the alien's exact horizontal position.
❷ self.x = float(self.rect.x)
Бұл класстың көпшілігі бөгде адамның экранда орналасуын қоспағанда, Ship
классына ұқсайды. Бастапқыда біз әрбір бөтен планеталықты экранның жоғарғы сол жақ бұрышына жақын орналастырамыз; оның сол жағына бөтен планеталықтың еніне тең бос орын және оның биіктігіне тең бос орын ❶ қосамыз, сондықтан оны көру оңай болады. Біз негізінен бөгде планеталықтың көлденең жылдамдығымен айналысамыз, сондықтан біз әрбір бөтен планеталықтың көлденең орналасуын дәл ❷ бақылаймыз.
Бұл Alien
класы оны экранға салу әдісін қажет етпейді; оның орнына біз топтың барлық элементтерін экранға автоматты түрде тартатын Pygame тобы https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group әдісін қолданамыз.
Біз экранда бірінші бөтен планеталықты көру үшін Alien
данасын жасағымыз келеді. Бұл орнату жұмысымыздың бір бөлігі болғандықтан, біз осы дананың кодын AlienInvasion
ішіндегі __init__()
әдісінің соңына қосамыз. Ақыр соңында, біз өзге планеталықтардың толық флотын жасаймыз, бұл біршама жұмысты талап етеді, сондықтан _create_fleet()
деп аталатын жаңа көмекші әдіс жасаймыз.
Класстағы әдістердің орналасу реті маңызды емес, тек олардың орналасуында бір реттілік болса болғаны. Мен _create_fleet()
параметрін _update_screen()
әдісінің дәл алдында орналастырамын, бірақ AlienInvasion
қолданбасының кез-келген жерінде жұмыс істейді. Алдымен Alien
классын импорттаймыз.
Міне, alien_invasion.py үшін жаңартылған import
амалдары:
alien_invasion.py
--snip/код үзіндісі--
from bullet import Bullet
from alien import Alien
Міне, жаңартылған __init__()
әдісі:
alien_invasion.py
def __init__(self):
--snip/код үзіндісі--
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_fleet()
Біз өзге планеталықтар флотын ұстау үшін топ құрамыз және біз жазғалы тұрған _create_fleet()
деп атаймыз.
Міне, жаңа _create_fleet()
әдісі:
alien_invasion.py
def _create_fleet(self):
"""Бөтен планеталықтар флотын жасаңыз."""
# Make an alien.
alien = Alien(self)
self.aliens.add(alien)
Бұл әдісте біз Alien
бір данасын жасап, оны флотты ұстайтын топқа қосамыз. Бөтен планеталық экранның әдепкі жоғарғы сол жақ аймағына орналастырылады.
Бөтен планеталықтың пайда болуы үшін біз топтың _update_screen()
ішіндегі draw()
әдісін шақыруымыз керек:
alien_invasion.py
def _update_screen(self):
--snip/код үзіндісі--
self.ship.blitme()
self.aliens.draw(self.screen)
pygame.display.flip()
Топта draw()
функциясын шақырған кезде, Pygame топтағы әрбір элементті оның rect
атрибутымен анықталған орынға салады. draw()
әдісі бір аргументті талап етеді: топтан элементтерді сызуға болатын бет. 13-2-сурет экрандағы бірінші бөтен планеталықты көрсетеді.
13-2-сурет: Бірінші бөгде адам пайда болады.
Енді бірінші бөтен планеталық дұрыс пайда болған соң, біз бүкіл флотты салу үшін кодты жазамыз.
Флотты салу кезінде ойын терезесіның барлық аймағы толып кетпейтіндей, экранның жоғарғы бөлігін өзге планеталықтармен қалай толтыру керектігін анықтау керек. Бұл мақсатқа жетудің бірнеше жолы бар. Біз оған жаңа бөтен планеталық үшін бос орын қалмайынша, экранның жоғарғы жағына өзге планеталықтарды қосу арқылы жақындаймыз. Содан кейін бізде жаңа жол қосу үшін вертикалды бос орын жеткілікті болған кезде бұл процесті қайталаймыз.
Енді біз өзге планеталықтардың толық қатарын жасауға дайынбыз. Толық жолды жасау үшін алдымен бір бөтен планеталықты жасаймыз, осылайша біз бөтен планеталықтың еніне қол жеткізе аламыз. Біз бөтен планеталықты экранның сол жағына орналастырамыз, содан кейін бос орын таусылғанша бөтен планеталықтарды қоса береміз:
alien_invasion.py
def _create_fleet(self):
"""Бөтен планеталықтар флотын жасаңыз."""
# Бөгде планеталық жасаңыз және орын қалмайынша бөтен
# планеталықтарды қосуды жалғастырыңыз.
# Бөтен планеталықтар арасындағы қашықтық - бір бөтен пл-қ ені.
alien = Alien(self)
alien_width = alien.rect.width
❶ current_x = alien_width
❷ while current_x < (self.settings.screen_width - 2 * alien_width):
❸ new_alien = Alien(self)
❹ new_alien.x = current_x
new_alien.rect.x = current_x
self.aliens.add(new_alien)
❺ current_x += 2 * alien_width
Біз бірінші жасаған бөтен планеталықтың енін аламыз, содан кейін current_x
❶ деп аталатын айнымалы мәнді анықтаймыз. Бұл экранға орналастырғалы отырған келесі бөтен планеталықтың көлденең орналасуына қатысты. Флоттағы бірінші инопланетаны экранның сол жақ шетінен ығыстыру үшін біз оны бастапқыда бір бөтен енге орнаттық.
Кейін, while
циклін бастаймыз ❷; біз оларды орналастыруға жеткілікті орын болғанша бөтен планеталықтарды қосуды жалғастырамыз. Басқа бөтен планеталықты орналастыруға орын бар-жоғын анықтау үшін current_x
мәнін максималды мәнмен салыстырамыз. Бұл циклды анықтаудың бірінші әрекеті келесідей болуы мүмкін:
while current_x < self.settings.screen_width:
Бұл жұмыс істейтін сияқты, бірақ ол соңғы бөгде адамды экранның оң жақ шетіндегі жолға орналастырады. Сонымен, біз экранның оң жағына аздап маржа қосамыз. Экранның оң жақ шетінде кемінде екі бөтен ені бос орын болса, біз циклге кіріп, флотқа басқа бөтен планеталықты қосамыз.
Циклді жалғастыру үшін көлденең кеңістік жеткілікті болған кезде, біз екі әрекетті орындағымыз келеді: дұрыс позицияда бөтен планеталықты жасау және Тіркестегі келесі бөтен планеталықтың көлденең орнын анықтау. бөтен планеталықты жасап, оны new_alien
❸ тағайындаймыз. Содан кейін біз дәл көлденең орынды current_x
❹ ағымдағы мәніне орнатамыз. Біз сондай-ақ бөтен планеталықтың rect
-ін дәл осы x мәнінде орналастырамыз және жаңа бөтен планеталықты self.aliens
тобына қосамыз.
Соңында, current_x
❺ мәнін арттырамыз. Біз жаңа ғана қосқан бөгде планетаның жанынан өту және өзге планеталықтар арасында бос орын қалдыру үшін көлденең позицияға екі бөтен енді қосамыз. Python while
циклінің басындағы жағдайды қайта бағалайды және басқа бөтен планеталыққа орын бар-жоғын шешеді. Бос орын қалмағанда, цикл аяқталады және бізде өзге планеталықтардың толық қатары болуы керек.
Соңында, current_x
❺ мәнін арттырамыз. Біз жаңа ғана қосқан бөгде планетаның жанынан өту және өзге планеталықтар арасында бос орын қалдыру үшін көлденең позицияға екі бөтен енді қосамыз. Python while
циклінің басындағы жағдайды қайта бағалайды және басқа бөтен планеталыққа орын бар-жоғын шешеді. Бос орын қалмағанда, цикл аяқталады және бізде өзге планеталықтардың толық қатары болуы керек.
13-3-сурет: өзге планеталықтардың бірінші қатары
Осы бөлімде көрсетілгендей циклды қалай құру керектігі әрқашан анық бола бермейді. Бағдарламалаудың бір жақсы жағы - мұндай мәселеге қалай жетуге болатындығы туралы сіздің бастапқы идеяларыңыз дұрыс болуы керек емес. Өзге планеталықтар тым алыс орналасқан осындай циклды бастау өте орынды, содан кейін экранда тиісті орын болғанша циклды өзгертіңіз.
Егер біз осы уақытқа дейін жазған код флот құру үшін қажет болса, біз _create_fleet()
қызметін сол күйінде қалдырар едік. Бірақ бізде көп жұмыс бар, сондықтан әдісті сәл тазартайық. Біз _create_alien()
атты жаңа көмекші әдісті қосамыз және оны _create_fleet()
арқылы шақырамыз:
alien_invasion.py
def _create_fleet(self):
--snip/код үзіндісі--
while current_x < (self.settings.screen_width - 2 * alien_width):
self._create_alien(current_x)
current_x += 2 * alien_width
❶ def _create_alien(self, x_position):
"""Инопланетаны жасап, оны қатарға орналастырыңыз."""
new_alien = Alien(self)
new_alien.x = x_position
new_alien.rect.x = x_position
self.aliens.add(new_alien)
_create_alien()
әдісі self
параметріне қосымша бір параметрді қажет етеді: бөтен планеталықты қайда орналастыру керектігін көрсететін x мәні ❶. _create_alien()
денесіндегі код _create_fleet()
ішіндегі кодпен бірдей, бірақ біз current_x
орнына x_position
параметр атауын қолданамыз. Бұл рефакторинг жаңа қатарды қосуды және бүкіл флотты құруды жеңілдетеді.
Флотты аяқтау үшін орын таусылғанша қосымша қатарды қоса береміз. Біз кірістірілген циклды қолданамыз — ағымдағының айналасына тағы бір while
циклін орап аламыз. Ішкі цикл өзге планеталықтардың x мәндеріне назар аудара отырып, өзге планеталықтарды бір қатарға көлденең орналастырады. Сыртқы цикл y мәндеріне назар аудару арқылы бөтен планеталарды тігінен орналастырады. Экранның төменгі жағына жақындағанда, кемеге жеткілікті орын қалдырып, бөгде планеталықтарға атуды бастау үшін біраз орын қалдырған кезде қатарды қосуды тоқтатамыз.
Міне, екі while
циклін _create_fleet()
ішінде қалай кірістіру керек:
def _create_fleet(self):
"""Бөтен планеталықтар флотын жасаңыз."""
# Create an alien and keep adding aliens until there's no room left.
# Spacing between aliens is one alien width and one alien height.
alien = Alien(self)
❶ alien_width, alien_height = alien.rect.size
❷ current_x, current_y = alien_width, alien_height
❸ while current_y < (self.settings.screen_height - 3 * alien_height):
while current_x < (self.settings.screen_width - 2 * alien_width):
❹ self._create_alien(current_x, current_y)
current_x += 2 * alien_width
❺ # Finished a row; reset x value, and increment y value.
current_x = alien_width
current_y += 2 * alien_height
Бөтен планеталықтар қатарын орналастыру үшін бөтен планеталықтың биіктігін білуіміз керек, сондықтан біз бөтен планеталықтың rect
атрибуты арқылы бөтен планеталықтың ені мен биіктігін анықтаймыз.❶. rect
-тің size
атрибуты оның ені мен биіктігін қамтитын кортеж болып табылады..
Содан кейін біз флоттағы бірінші өзге планеталықтарды орналастыру үшін бастапқы x- және y мәндерін орнатамыз ❷. Біз оны сол жақтан бір бөтен планеталық енді және жоғарыдан бір бөтен планеталық биіктікте орналастырамыз. Содан кейін экранға қанша қатар орналастырылатынын басқаратын while
циклін анықтаймыз.❸. Келесі қатардың y мәні экран биіктігінен кем болса, үш бөтен планеталықтың биіктіктігіндей ара-қашықтықты алып тастағанда, біз қатарды қосуды жалғастырамыз. (Егер бұл қажетті орын қалдырмаса, біз оны кейінірек реттей аламыз.)
Біз _create_alien()
деп атаймыз және оған y-мәні, сонымен қатар оның x-позициясын береміз.❹.Бір сәтте _create_alien()
өзгертеміз.
Кодтың соңғы екі жолының шегінісіне назар аударыңыз❺. Олар сыртқы while
циклінің ішінде, бірақ ішкі while
циклінің сыртында. Бұл блок ішкі цикл аяқталғаннан кейін орындалады; ол әрбір жол жасалғаннан кейін бір рет орындалады. Әрбір жол қосылғаннан кейін біз current_x
мәнін қалпына келтіреміз, осылайша келесі Тіркестегі бірінші бөтен планеталық алдыңғы жолдардағы бірінші бөгделікпен бірдей орынға орналастырылады. Содан кейін біз current_y
ағымдағы мәніне екі бөтен биіктікті қосамыз, осылайша келесі жол экранның одан әрі төменірек орналасады. Мұнда шегініс өте маңызды; егер осы бөлімнің соңында alien_invasion.py іске қосқан кезде дұрыс флотты көрмесеңіз, осы кірістірілген циклдардағы барлық Тіркестердің шегінісін тексеріңіз.
Инопланетаның тік орнын дұрыс орнату үшін _create_alien()
өзгерту керек:
def _create_alien(self, x_position, y_position):
"""Инопланетянды жасап, оны флотқа орналастырыңыз."""
new_alien = Alien(self)
new_alien.x = x_position
new_alien.rect.x = x_position
new_alien.rect.y = y_position
self.aliens.add(new_alien)
Жаңа бөгде үшін y-мәнін қабылдау әдісінің анықтамасын өзгертеміз және әдістің негізгі бөлігіндегі rect
тік орнын орнатамыз. .
Ойынды қазір іске қосқан кезде, суретте көрсетілгендей, өзге планеталықтардың толық флотын көруіңіз керек 13-4-сурет.
13-4-сурет: Толық флот пайда болады.
Келесі бөлімде біз флотты жылжытамыз!
13-1. Жұлдыздар: Жұлдыздың суретін табыңыз. Экранда жұлдыздар торын көрсетеді.
13-2. Жақсырақ жұлдыздар: Әр жұлдызды орналастырған кезде кездейсоқтықты енгізу арқылы шынайырақ жұлдыз үлгісін жасауға болады. 9-тараудан келесідей санады алуға болатынын еске түсіріңіз:
from random import randint
random_number = randint(-10, 10)
Бұл код −10 және 10 арасындағы кездейсоқ бүтін санды қайтарады. 13-1-жаттығудағы кодты пайдаланып, әрбір жұлдыз орнын кездейсоқ мөлшерге реттеңіз.
Енді өзге планеталықтар флотын экранның шетіне тигенше оң жаққа жылжытайық, содан кейін оны белгіленген мөлшерді түсіріп, басқа бағытта жылжытайық. Біз бұл қозғалысты барлық өзге планеталықтар атып түсірілгенше, біреуі кемемен соқтығысқанша немесе экранның төменгі жағына жеткенше жалғастырамыз. Флотты оңға жылжытудан бастайық.
Өзге планеталықтарды жылжыту үшін біз alien.py ішінде update()
әдісін қолданамыз, оны өзге планеталықтар тобындағы әр бөтен планеталық үшін шақырамыз. Алдымен әрбір бөтен планеталықтың жылдамдығын басқару үшін параметрді қосыңыз:
settings.py
def __init__(self):
--snip/код үзіндісі--
# Alien settings
self.alien_speed = 1.0
Одан кейін alien.py ішінде update()
енгізу үшін осы параметрді пайдаланыңыз:
alien.py
def __init__(self, ai_game):
"""Инопланетаны инициализациялаңыз және оның бастапқы орнын орнатыңыз."""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
--snip/код үзіндісі--
def update(self):
""" бөтен планеталықты оңға жылжытыңыз."""
❶
self.x += self.settings.alien_speed
❷
self.rect.x = self.x
Біз __init__()
ішінде settings
параметрін жасаймыз, осылайша update()
қолданбасында бөгде планетаның жылдамдығына қол жеткізе аламыз. бөтен планеталықтың орнын жаңартқан сайын, оны alien_speed
ішінде сақталған сома бойынша оңға жылжытамыз. Біз бөтен планеталықтың нақты орнын ❶ қалқымалы мәндерді сақтай алатын self.x
атрибуты арқылы бақылаймыз. Одан кейін өзге планеталықтардың тік
орнын жаңарту үшін self.x
мәнін қолданамыз ❷.
Негізгі while
циклінде бізде кеме және оқ позицияларын жаңарту үшін қоңыраулар бар. Енді біз әрбір өзге планеталықтардың орнын жаңарту үшін қоңырау қосамыз:
alien_invasion.py
while True:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
self.clock.tick(60)
Біз флоттың қозғалысын басқару үшін кейбір кодты жазғалы жатырмыз, сондықтан _update_aliens()
деп аталатын жаңа әдіс жасаймыз. Оқтар жаңартылғаннан кейін біз өзге планеталықтардың позицияларын жаңартамыз, өйткені жақын арада оқтардың кез-келген өзге планеталықтарға тигенін тексеретін боламыз.
Бұл әдісті модульде қай жерде орналастыру маңызды емес. Бірақ кодты реттелген күйде сақтау үшін мен оны _update_bullets()
-дан кейін while
цикліндегі әдіс шақыруларының ретімен сәйкестендіру үшін орналастырамын. Міне, _update_aliens()
бірінші нұсқасы:
alien_invasion.py
def _update_aliens(self):
"""Флоттағы барлық Бөтен планеталықтардің позицияларын жаңартыңыз."""
self.aliens.update()
Біз әр бөтен планеталықтың update()
әдісін шақыратын бөтен планеталықтар
тобында update()
әдісін қолданамыз. Alien Invasion қолданбасын қазір іске қосқанда, флоттың оңға қарай жылжып, экранның шетінен жоғалып кеткенін көруіңіз керек.
Енді біз флотты экранның оң жақ жиегіне соққанда экранның төмен және солға жылжуын қамтамасыз ететін параметрлерді жасаймыз. Бұл әрекетті орындау жолы:
settings.py
# Alien settings
self.alien_speed = 1.0
self.fleet_drop_speed = 10
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
fleet_drop_speed
параметрі бөгде планетаның кез-келген шетіне жеткен сайын флоттың экранды қаншалықты жылдам түсіретінін басқарады. Бұл жылдамдықты өзге планеталықтардың көлденең жылдамдығынан бөліп алу пайдалы, осылайша екі жылдамдықты бір-бірінен тәуелсіз реттей аласыз.
fleet_direction
параметрін енгізу үшін біз 'left'
немесе 'right'
сияқты мәтіндік мәнді пайдалана аламыз, бірақ біз флот бағыты үшін if
-elif
амалдарын сынау арқылы аяқтаңыз. Оның орнына, бізде тек екі бағыт бар болғандықтан, 1 және −1 мәндерін қолданып, флот бағытты өзгерткен сайын олардың арасында ауысайық. (Сандарды пайдаланудың да мағынасы бар, өйткені оңға жылжу әрбір бөтен планеталықтың x- координат мәніне қосуды, ал солға жылжыту әр бөтен планеталықтың x- координат мәнінен шегеруді қамтиды.)
Бізге бөтен планеталықтың кез-келген шетінде бар-жоғын тексеру әдісі қажет және әрбір бөтен планеталықтың тиісті бағытта қозғалуына мүмкіндік беру үшін update()
өзгертуіміз керек. Бұл код Alien
класының бөлігі:
alien.py
def check_edges(self):
"""Егер бөтен планета экранның шетінде болса, True мәнін қайтарыңыз."""
screen_rect = self.screen.get_rect()
❶
return (self.rect.right >= screen_rect.right) or (self.rect.left <= 0)
def update(self):
""" бөтен планеталықты оңға немесе солға жылжытыңыз."""
❷
self.x += self.settings.alien_speed * self.settings.fleet_direction
self.rect.x = self.x
Жаңа әдісті check_edges()
деп кез-келген бөтенде оның сол немесе оң жақ шетте екенін білуге болады. Егер оның rect
атрибутының right
атрибуты экранның rect
атрибутының right
атрибутынан үлкен немесе оған тең болса, бөгде адам оң жақ шетінде болады. Егер оның left
мәні 0 ❶ мәнінен аз немесе оған тең болса, ол сол жақ шетте орналасады. Бұл шартты сынақты if
блогына қоюдың орнына, біз сынақты тікелей return
операторына қоямыз. Бұл әдіс бөгде планетаның оң немесе сол жақ шетінде болса, True
мәнін, ал екі шетінде болмаса, False
мәнін береді.
Біз update()
әдісін бөтен планеталықтың жылдамдығын fleet_direction
мәніне көбейту арқылы солға немесе оңға қозғалысқа рұқсат ету үшін өзгертеміз.❷. Егер fleet_direction
1 болса, alien_speed
мәні бөтен планеталықтың ағымдағы орнына қосылып, бөтен планеталықты оңға жылжытады; егер fleet_direction
−1 болса, мән бөтен планеталықтың орнынан шегеріліп, бөтен планеталықты солға жылжытады.
Өзге планеталық шетке жеткенде, бүкіл флот төмен түсіп, бағытын өзгертуі керек. Сондықтан, AlienInvasion
қолданбасына кейбір кодты қосуымыз керек, себебі сол жақта немесе оң жақта өзге планеталықтар бар-жоғын сол жерде тексереміз. Біз мұны _check_fleet_edges()
және _change_fleet_direction()
әдістерін жазып, содан кейін _update_aliens()
өзгерту арқылы орындаймыз. Мен бұл жаңа әдістерді _create_alien()
параметрінен кейін қоямын, бірақ бұл әдістерді класста орналастыру маңызды емес.
alien_invasion.py
def _check_fleet_edges(self):
"""Бөтен планеталықтар шегіне жеткен болса, тиісті жауап беріңіз."""
❶ for alien in self.aliens.sprites():
if alien.check_edges():
❷ self._change_fleet_direction()
break
def _change_fleet_direction(self):
"""Бүкіл флотты тастап, флоттың бағытын өзгертіңіз."""
for alien in self.aliens.sprites():
❸ alien.rect.y += self.settings.fleet_drop_speed
self.settings.fleet_direction *= -1
_check_fleet_edges()
ішінде біз флотты айналып өтіп, әрбір бөтен планетада check_edges()
шақырамыз. ❶. Егер check_edges()
True
мәнін қайтарса, біз бөтен планеталықтың шетте тұрғанын білеміз және бүкіл флот бағытты өзгерту керек; сондықтан біз _change_fleet_direction()
деп атаймыз және ❷ циклінен шығамыз. _change_fleet_direction()
ішінде біз барлық өзге планеталықтарды айналдырып, әрқайсысын fleet_drop_speed
❸ параметрін пайдаланып тастаймыз.; содан кейін fleet_direction
мәнін оның ағымдағы мәнін −1-ге көбейту арқылы өзгертеміз. Флоттың бағытын өзгертетін сызық for
циклінің бөлігі емес. Біз әрбір бөтен планеталықтың тік орнын өзгерткіміз келеді, бірақ флоттың бағытын тек бір рет өзгерткіміз келеді.
Міне, _update_aliens()
өзгертулері:
alien_invasion.py
def _update_aliens(self):
"""Флоттың шетте тұрғанын тексеріңіз, содан кейін позицияларды жаңартыңыз."""
self._check_fleet_edges()
self.aliens.update()
Әр бөтен планеталықтың орнын жаңарту алдында _check_fleet_edges()
шақыру арқылы әдісті өзгерттік.
Ойынды қазір іске қосқан кезде, флот экранның жиектері арасында алға-артқа жылжып, шетіне тиген сайын төмен түсіп тұруы керек. Енді біз өзге планеталықтарды атып түсіруді бастай аламыз және кемеге соқтығысқан немесе экранның төменгі жағына жеткен кез-келген өзге планеталықтарды бақылай аламыз.
13-3. Жаңбыр тамшылары: Жаңбыр тамшысының бейнесін тауып, жаңбыр тамшылары торын жасаңыз. Жаңбыр тамшыларын олар жоғалып кеткенше экранның төменгі жағына түсіріңіз.
13-4. Тұрақты жаңбыр: 13-3-жаттығудағы кодты өзгертіңіз, осылайша экранның төменгі жағында жаңбыр тамшылары қатары жоғалып кетсе, экранның жоғарғы жағында жаңа жол пайда болады және түсе бастайды.
Біз өз кемемізді және өзге планеталықтар флотын жасадық, бірақ оқтар өзге планеталықтарға жеткенде, олар жай өтіп кетеді, өйткені біз соқтығысуды тексермейміз. Ойынды бағдарламалауда соқтығыстар ойын элементтері бір-біріне сәйкес келгенде орын алады. Оқтарды бөгде планеталықтарды атып түсіру үшін екі топ мүшелерінің арасындағы қақтығыстарды іздеу үшін sprite.groupcollide()
функциясын қолданамыз.
Оқ бөтен планетаға тиген кезде бірден білгіміз келеді, осылайша бөтен планеталық тиген бойда жоғалып кетуі мүмкін. Ол үшін барлық таңбалардың орнын жаңартқаннан кейін бірден соқтығысуды іздейміз.
sprite.groupcollide()
функциясы бір топтағы әрбір элементтің rect
объектісін басқа топтағы элементтің rect
объектісімен салыстырады. Бұл жағдайда ол әрбір оқтың rect
мәнін әр бөтен планеталықтың rect
белгісімен салыстырады және соқтығысқан оқтар мен өзге планеталықтарды қамтитын сөздікті қайтарады. Сөздіктегі әрбір кілт оқ болады, ал сәйкес мән соққыға ұшыраған бөтен болады. (Осы сөздікті 14-тарауда ұпайды есептеу жүйесін енгізген кезде де пайдаланамыз.)
Оқтар мен өзге планеталықтар арасындағы соқтығысуды тексеру үшін _update_bullets()
соңына келесі кодты қосыңыз:
alien_invasion.py
def _update_bullets(self):
"""Оқтардың орнын жаңартып, ескі оқтардан арылыңыз."""
--snip/код үзіндісі--
# Check for any bullets that have hit aliens.
# If so, get rid of the bullet and the alien.
collisions = pygame.sprite.groupcollide(
self.bullets, self.aliens, True, True)
Біз қосқан жаңа код self.bullets
ішіндегі барлық оқтардың және self.aliens
ішіндегі барлық өзге планеталықтардың орындарын салыстырады және кез-келген сәйкес келетіндерді анықтайды. Оқ пен бөгде планеталық элементтің rect
сәйкестігі кездескен сайын, groupcollide()
қайтаратын сөздікке кілт-мән жұбын қосады. Екі True
аргументі Pygame-ге соқтығысқан оқтарды және өзге планеталықтарды жоюды айтады. (Экранның жоғарғы жағына шығып, жолындағы әрбір бөтен планеталықты жоя алатын қуатты оқ жасау үшін бірінші логикалық аргументті False
мәніне орнатуға және екінші логикалық аргументті True
күйіне қоюға болады. Соққан өзге планеталықтар жоғалып кетеді, бірақ барлық оқтар экранның жоғарғы жағында жоғалып кеткенше белсенді болып қала береді.)
Қазір Alien Invasion іске қосылғанда, сіз соққыға жығылған өзге планеталықтар жоғалып кетуі керек. 13-5-сурет ішінара түсірілген флотты көрсетеді.
Figure 13-5: Біз өзге планеталықтарды атып түсіре аламыз!
Ойынды іске қосып ойнау арқылы Alien Invasion көптеген мүмкіндіктерін тексеруге болады, бірақ кейбір мүмкіндіктерді ойынның қалыпты жағдайында сынап-тексеру жалықтырады. Мысалы, кодыңыз бос флотқа дұрыс жауап беретінін тексеру үшін экрандағы әрбір бөтен планеталықты бірнеше рет атып түсіру көп жұмысты қажет етеді.
Белгілі бір мүмкіндіктерді тексеру үшін белгілі бір аймаққа назар аудару үшін белгілі бір ойын параметрлерін өзгертуге болады. Мысалы, оқ жылдамдығын арттыру және өзіңізге бірден көп оқ беру, немесе тез атып түсіру үшін өзге планеталықтар аз болуы үшін экранды кішірейтуіңізге болады.
Менің Alien Invasion сынаудағы таңдаулы өзгерісім - бөтен планеталыққа тигеннен кейін де белсенді болып қалатын шын мәнінде кең оқтарды пайдалану (13-6-суретті қараңыз). bullet_width
параметрін 300, тіпті 3000 етіп орнатып көріңіз, флотты қаншалықты жылдам атып түсіретініңізді көріңіз!
13-6-сурет: Өте күшті оқтар ойынның кейбір аспектілерін тексеруді жеңілдетеді.
Осындай өзгертулер ойынды тиімдірек сынауға көмектеседі және ойыншыларға бонустық құқықтар беру идеяларын ұшқындатуы мүмкін. Функцияны тексеруді аяқтаған кезде параметрлерді қалыпты күйге қайтаруды ұмытпаңыз.
Alien Invasion-ның басты ерекшеліктерінің бірі - өзге планеталықтар тынымсыз: флот жойылған сайын жаңа флот пайда болуы керек.
Флот жойылғаннан кейін өзге планеталықтардың жаңа флоты пайда болуы үшін алдымен aliens
тобының бос екенін тексереміз. Егер солай болса, біз _create_fleet()
қызметіне қоңырау шаламыз. Біз бұл тексеруді _update_bullets()
соңында орындаймыз, себебі бұл жерде жеке өзге планеталықтар жойылады.
alien_invasion.py
def _update_bullets(self):
--snip/код үзіндісі--
❶ if not self.aliens:
# Destroy existing bullets and create new fleet.
❷ self.bullets.empty()
self._create_fleet()
Біз aliens
тобының бос екенін тексереміз ❶. Бос топ False
мәнін бағалайды, сондықтан бұл топтың бос екенін тексерудің қарапайым жолы. Егер солай болса, біз тобынан қалған барлық спрайттарды жоятын empty()
әдісін пайдаланып, бар таңбалауыштардан құтыламыз. ❷. Біз сонымен қатар экранды бөгде планеталықтармен толтыратын _create_fleet()
деп те атаймыз.
Қазіргі флот жойылған бойда жаңа флот пайда болады.
Егер сіз ойынның қазіргі күйінде өзге планеталықтарға оқ атуға тырыссаңыз, оқтар ойын ойнау үшін ең жақсы жылдамдықта жүрмейтінін байқауыңыз мүмкін. Олар сәл тым баяу немесе сәл тым жылдам болуы мүмкін. Осы кезде сіз геймплейді қызықты ету үшін параметрлерді өзгерте аласыз. Ойынның біртіндеп жылдамдайтынын есте сақтаңыз, сондықтан ойынды басында тым жылдам жасамаңыз.
Біз таңбалауыштардың жылдамдығын settings.py ішіндегі bullet_speed
мәнін реттеу арқылы өзгертеміз. Менің жүйемде мен bullet_speed
мәнін 2,5 мәніне реттеймін, сондықтан оқтар сәл жылдамырақ қозғалады:
settings.py
# Bullet settings
self.bullet_speed = 2.5
self.bullet_width = 3
--snip/код үзіндісі--
Бұл параметрдің ең жақсы мәні ойын тәжірибесіне байланысты, сондықтан сізге сәйкес келетін мәнді табыңыз. Басқа параметрлерді де реттеуге болады.
Келіңіз, _update_bullets()
рефакторын жасайық, сондықтан ол әртүрлі тапсырмаларды орындамайды. Оқ-бөтен соқтығыстармен күресу үшін кодты бөлек әдіске жылжытамыз:
alien_invasion.py
def _update_bullets(self):
--snip/код үзіндісі--
# Get rid of bullets that have disappeared.
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
self._check_bullet_alien_collisions()
def _check_bullet_alien_collisions(self):
"""Оқ-бөтен планеталық соқтығыстарға жауап беріңіз."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(
self.bullets, self.aliens, True, True)
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
Оқ оқтары мен өзге планеталықтар арасындағы соқтығыстарды іздеу және бүкіл флот жойылған жағдайда тиісті жауап беру үшін _check_bullet_alien_collisions()
атты жаңа әдісті жасадық. Бұл _update_bullets()
тым ұзақ өсуден сақтайды және одан әрі дамытуды жеңілдетеді.
13-5. Sideways Shooter 2-бөлім: 12-6, Бүйірден атқыш жаттығуынан бері біз ұзақ жол жүрдік. Бұл жаттығу үшін Бүйірлік атқышты біз Жат планеталық шабуылға әкелген нүктеге дейін жасап көріңіз. өзге планеталықтар флотын қосып, оларды кемеге қарай жылжытыңыз. Немесе экранның оң жағындағы кездейсоқ орындарға өзге планеталықтарды орналастыратын кодты жазыңыз, содан кейін оларды кемеге қарай жіберіңіз. Сондай-ақ, бөгде планеталықтар соққыға жығылған кезде жойылып кететін кодты жазыңыз.
Жеңілмейтін ойынды ойнаудың қызығы мен қиындығы қандай? Егер ойыншы флотты тез атып түсірмесе, біз өзге планеталықтар жақындап келіп байланысқа түскен кезде оқ ататын кемені жойып жібереміз. Сонымен бірге біз ойыншы пайдалана алатын кемелер санын шектейміз және бөтен планеталық экранның төменгі жағына жеткенде кемені жоямыз. Ойыншы барлық кемелерін пайдаланған кезде ойын аяқталады.
Біз өзге планеталықтар мен кеме арасындағы соқтығыстарды тексеруден бастаймыз, осылайша бөгде планеталық соққанда тиісті жауап қайтарамыз. AlienInvasion
ішіндегі әрбір бөтен планеталықтың орнын жаңартқаннан кейін біз өзге планеталық кемелердің соқтығысуын бірден тексереміз:
alien_invasion.py
def _update_aliens(self):
--snip/код үзіндісі--
self.aliens.update()
# Look for alien-ship collisions.
❶ if pygame.sprite.spritecollideany(self.ship, self.aliens):
❷ print("Ship hit!!!")
spritecollideany()
функциясы екі аргументті қабылдайды: спрайт және топ. Функция спрайтпен соқтығысқан топтың кез-келген мүшесін іздейді және ол спрайтпен соқтығысқан бір мүшені тапқан бойда топ арқылы айналымды тоқтатады. Мұнда ол бөтен планеталықтар
тобын айналып өтіп, ship
-мен соқтығысқан бірінші бөтен планеталықты қайтарады.
Егер соқтығыс болмаса, spritecollideany()
None
қайтарады және if
блогы орындалмайды❶. Егер ол кемемен соқтығысқан бөтен планеталықты тапса, ол бөтен планеталықты қайтарады және if
блогы орындалады: ол Кеме соққысы!!!
басып шығарады.❷.бөтен планеталық кемені соққанда, бізге бірқатар тапсырмаларды орындау керек болады: қалған барлық өзге планеталықтар мен оқтарды жою, кемені қайта орнату және жаңа флот құру. Осының бәрін орындау үшін кодты жазбас бұрын, біз бөтен кемелермен соқтығысуды анықтауға деген көзқарасымыз дұрыс жұмыс істейтінін білгіміз келеді. print()
шақыруын жазу - бұл соқтығысуларды дұрыс анықтағанымызға көз жеткізудің қарапайым жолы.
Енді Alien Invasion қолданбасын іске қосқан кезде, бөтен планеталық кемеге кірген сайын терминалда Кеме соқты!!! хабары пайда болуы керек. Бұл мүмкіндікті сынап жатқанда, fleet_drop_speed
мәнін 50 немесе 100 сияқты жоғарырақ мәнге орнатыңыз, сонда өзге планеталықтар кемеңізге жылдам жетеді.
Енді біз бөтен планеталық кемемен соқтығысқанда не болатынын нақты анықтауымыз керек. ship
данасын жойып, жаңасын жасаудың орнына, ойын статистикасын қадағалау арқылы кеме қанша рет соғылғанын есептейміз. Бақылау статистикасы ұпай жинау үшін де пайдалы болады.
Ойын статистикасын бақылау үшін GameStats
атты жаңа класс жазайық және оны game_stats.py ретінде сақтайық:
game_stats.py
class GameStats:
"""Жат планеталық шабуылға қатысты статистиканы қадағалаңыз."""
def __init__(self, ai_game):
"""Статистиканы инициализациялау."""
self.settings = ai_game.settings
❶ self.reset_stats()
def reset_stats(self):
"""Ойын барысында өзгеруі мүмкін статистиканы инициализациялау."""
self.ships_left = self.settings.ship_limit
Біз Alien Invasion іске қосылған бүкіл уақыт бойы бір GameStats
данасын жасаймыз, бірақ ойыншы жаңа ойынды бастаған сайын кейбір статистиканы қалпына келтіруіміз керек. . Мұны істеу үшін біз статистиканың көпшілігін тікелей __init__()
әдісінің орнына reset_stats()
әдісінде инициализациялаймыз. Біз бұл әдісті __init__()
арқылы шақырамыз, сондықтан GameStats
данасы алғаш рет жасалған кезде статистика дұрыс орнатылады ❶. Бірақ біз ойыншы жаңа ойынды бастаған кез-келген уақытта reset_stats()
қызметіне қоңырау шала аламыз. Дәл қазір бізде бір ғана статистика бар, ships_left
, оның мәні ойын барысында өзгереді.
Ойыншы бастайтын кемелер саны settings.py ішінде ship_limit
ретінде сақталуы керек:
settings.py
# Ship settings
self.ship_speed = 1.5
self.ship_limit = 3
Сонымен қатар GameStats
данасын жасау үшін alien_invasion.py сайтына бірнеше өзгертулер енгізуіміз керек. Алдымен, файлдың жоғарғы жағындағы import
амалдарын жаңартамыз:
alien_invasion.py
import sys
from time import sleep
import pygame
from settings import Settings
from game_stats import GameStats
from ship import Ship
--snip/код үзіндісі--
Біз Python стандартты кітапханасындағы time
модулінен sleep()
функциясын импорттаймыз, осылайша кеме соғылып қалған кезде ойынды бір сәтке тоқтата аламыз. Біз сондай-ақ GameStats
импорттаймыз.
Біз __init__()
ішінде GameStats
данасын жасаймыз:
alien_invasion.py
def __init__(self):
--snip/код үзіндісі--
self.screen = pygame.display.set_mode(
(self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
# Create an instance to store game statistics.
self.stats = GameStats(self)
self.ship = Ship(self)
--snip/код үзіндісі--
Дананы ойын терезесін жасағаннан кейін, бірақ кеме сияқты басқа ойын элементтерін анықтамас бұрын жасаймыз.
Кемені бөгде планеталық соққанда, біз қалған кемелер санынан 1-ді алып тастаймыз, барлық бар өзге планеталықтар мен оқтарды жоямыз, жаңа флот құрамыз және кемені экранның ортасына ауыстырамыз. Сондай-ақ, ойыншы соқтығысты байқап, жаңа флот пайда болғанға дейін қайта топтасу үшін ойынды біраз уақытқа тоқтатамыз.
Осы кодтың көп бөлігін _ship_hit()
деп аталатын жаңа әдіске салайық. Бөтен планеталық кемені соққанда, біз бұл әдісті _update_aliens()
арқылы шақырамыз:
alien_invasion.py
def _ship_hit(self):
"""Кемені бөгде планеталық соққанына жауап беріңіз."""
# Decrement ships_left.
❶ self.stats.ships_left -= 1
# Get rid of any remaining bullets and aliens.
❷ self.bullets.empty()
self.aliens.empty()
# Create a new fleet and center the ship.
❸ self._create_fleet()
self.ship.center_ship()
# Pause.
❹ sleep(0.5)
Жаңа әдіс _ship_hit()
бөгде адам кемені соққанда жауапты үйлестіреді. _ship_hit()
ішінде қалған кемелер саны 1-ге азаяды.❶, содан кейін біз bullets
және бөтен планеталықтар
топтарын босатамыз.❷.
Әрі қарай, біз жаңа флот құрып, кемені орталықтандырамыз ❸. (Бір сәтте Center_ship()
әдісін Ship
қызметіне қосамыз.) Содан кейін барлық ойын элементтеріне жаңартулар енгізілгеннен кейін, бірақ кез-келген жаңартудан бұрын үзіліс қосамыз. өзгертулер экранға түсірілді, сондықтан ойыншы өз кемесінің соғылғанын көре алады❹. sleep()
шақыруы бағдарламаның орындалуын жарты секундқа кідіртеді, бұл ойыншы бөтен планеталықтың кемені соққанын көру үшін жеткілікті. sleep()
функциясы аяқталғанда, кодтың орындалуы жаңа флотты экранға тартатын _update_screen()
әдісіне көшеді.
_update_aliens()
ішінде бөтен планеталық кемеге соғылған кезде print()
шақыруын _ship_hit()
шақыруымен ауыстырамыз:
alien_invasion.py
def _update_aliens(self):
--snip/код үзіндісі--
if pygame.sprite.spritecollideany(self.ship, self.aliens):
self._ship_hit()
Міне, ship.py ішіне жататын center_ship()
жаңа әдісі:
ship.py
def center_ship(self):
"""Кемені экранның ортасына қойыңыз."""
self.rect.midbottom = self.screen_rect.midbottom
self.x = float(self.rect.x)
Кемені __init__()
қолданбасында жасағандай ортаға саламыз. Оны ортаға қойғаннан кейін біз кеменің нақты орнын бақылауға мүмкіндік беретін self.x
атрибутын қалпына келтіреміз.
Біз ешқашан бір кемеден артық жасамайтынымызға назар аударыңыз; біз бүкіл ойын үшін тек бір кеме данасын жасаймыз және оны кемеге соқтығысқан сайын қайта қосамыз. ships_left
статистикасы ойыншының кемелері таусылғанын хабарлайды.
Ойынды іске қосыңыз, бірнеше өзге планеталықтарды атып тастаңыз және бөтен планеталықтың кемеге соқтығысуына мүмкіндік беріңіз. Ойын кідіртіліп, экранның төменгі жағындағы кеме қайтадан ортасына орналастырылған жаңа флот пайда болуы керек.
Егер бөтен планеталық экранның төменгі жағына жетсе, біз ойынға бөгде планеталық кемені соққан кездегідей жауап береді. Бұл орын алған кезде тексеру үшін alien_invasion.py ішіне жаңа әдіс қосыңыз:
alien_invasion.py
def _check_aliens_bottom(self):
"""кез-келген Бөтен планеталықтардің экранның төменгі жағына жеткенін тексеріңіз."""
for alien in self.aliens.sprites():
❶ if alien.rect.bottom >= self.settings.screen_height:
# Treat this the same as if the ship got hit.
self._ship_hit()
break
_check_aliens_bottom()
әдісі бөгде планеталықтар экранның төменгі жағына жеткен-жетпегенін тексереді. Бөтен планета оның rect.bottom
мәні экран биіктігінен үлкен немесе оған тең болған кезде, оның төменгі жағына жетеді ❶. Егер бөтен планеталық түбіне жетсе, біз _ship_hit()
деп атаймыз. Егер бір бөтен планеталық астыңғы жағына тиіп кетсе, қалғанын тексерудің қажеті жоқ, сондықтан _ship_hit()
шақырғаннан кейін циклдан шығамыз.
Бұл әдісті _update_aliens()
арқылы шақырамыз:
alien_invasion.py
def _update_aliens(self):
--snip/код үзіндісі--
# Look for alien-ship collisions.
if pygame.sprite.spritecollideany(self.ship, self.aliens):
self._ship_hit()
# Look for aliens hitting the bottom of the screen.
self._check_aliens_bottom()
Барлық өзге планеталықтардың позицияларын жаңартқаннан кейін және бөтен кемелермен соқтығысуды іздегеннен кейін _check_aliens_bottom()
деп атаймыз. Енді кемені бөтен планеталық соққан сайын немесе бөтен планеталық экранның төменгі жағына жеткен сайын жаңа флот пайда болады.
Жат планеталық шабуыл қазір өзін толық сезінеді, бірақ ойын ешқашан аяқталмайды. ships_left
мәні барған сайын теріс өседі. game_active
жалаушасын қосайық, сондықтан ойыншының кемелері таусылғанда ойынды аяқтай аламыз. Бұл жалаушаны AlienInvasion
ішіндегі __init__()
әдісінің соңына орнатамыз:
alien_invasion.py
def __init__(self):
--snip/код үзіндісі--
# Start Alien Invasion in an active state.
self.game_active = True
Енді біз ойыншы барлық кемелерін пайдаланған кезде game_active
мәнін False
күйіне орнататын _ship_hit()
кодын қосамыз:
alien_invasion.py
def _ship_hit(self):
"""Кемені бөтен планеталық соққанына жауап беріңіз."""
if self.stats.ships_left > 0:
# Decrement ships_left.
self.stats.ships_left -= 1
--snip/код үзіндісі--
# Pause.
sleep(0.5)
else:
self.game_active = False
_ship_hit()
көпшілігі өзгеріссіз. Біз барлық бар кодты if
блогына көшірдік, ол ойыншыда кемінде бір кеме қалғанына көз жеткізу үшін сынақтан өтеді. Олай болса, біз жаңа флот жасаймыз, кідіртеміз және әрі қарай жүреміз. Ойыншының кемелері қалмаса, game_active
мәнін False
күйіне орнатамыз.
Ойынның әрқашан іске қосылуы керек бөліктерін және ойын белсенді болғанда ғана іске қосылатын бөліктерін анықтауымыз керек:
alien_invasion.py
def run_game(self):
"""Ойынның негізгі циклін бастау."""
while True:
self._check_events()
if self.game_active:
self.ship.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
self.clock.tick(60)
Негізгі циклде ойын белсенді емес болса да, біз әрқашан _check_events()
шақыруымыз керек. Мысалы, пайдаланушы ойыннан шығу үшін Q түймесін басатынын немесе терезені жабу үшін түймені басатынын білуіміз керек. Сондай-ақ біз экранды жаңартуды жалғастырамыз, осылайша ойыншы жаңа ойынды бастауды таңдаған-таңдамайтынын күту кезінде экранға өзгертулер енгізе аламыз. Қалған функция шақырулары ойын белсенді болғанда ғана орындалуы керек, себебі ойын белсенді емес кезде, бізге ойын элементтерінің орындарын жаңартудың қажеті жоқ.
Енді Alien Invasion ойнаған кезде, барлық кемелеріңіз біткеннен кейін ойын қатып қалуы керек.
13-6. Ойын аяқталды: Sideways Shooter қолданбасында кеменің қанша рет соғылғанын және бөтен планеталықтың кемеге қанша рет тигенін қадағалаңыз. Ойынды аяқтаудың тиісті шартын шешіңіз және бұл жағдай орын алған кезде ойынды тоқтатыңыз.
Бұл тарауда сіз өзге планеталықтар флотын құру арқылы ойынға бірдей элементтердің көп санын қосу жолын үйрендіңіз. Сіз элементтер торын жасау үшін кірістірілген циклдарды қолдандыңыз және әр элементтің update()
әдісін шақыру арқылы ойын элементтерінің үлкен жинағын жылжыттыңыз. Сіз экрандағы нысандардың бағытын басқаруды және флот экранның шетіне жеткендегі сияқты нақты жағдайларға жауап беруді үйрендіңіз. Сіз оқтар өзге планеталықтарға тиген кезде соқтығысуды анықтап, оларға жауап бердіңіз. Сондай-ақ ойынның статистикасын бақылауды және ойынның қашан аяқталғанын анықтау үшін game_active
жалаушасын пайдалануды үйрендіңіз.
Осы жобаның келесі және соңғы тарауында біз Ойнату түймесін қосамыз, осылайша ойыншы бірінші ойынды қашан бастау керектігін және ойын аяқталғанда қайта ойнауды таңдай алады. Ойыншы бүкіл флотты атып түсірген сайын ойынды тездетеміз және ұпай жүйесін қосамыз. Соңғы нәтиже толығымен ойнатылатын ойын болады!
Бұл тарауда біз Жат планеталық шабуыл құрылысын аяқтаймыз. Ойынды талап бойынша бастау және ойын аяқталғаннан кейін қайта бастау үшін «Ойнату» түймесін қосамыз. Біз сондай-ақ ойыншы деңгейге көтерілген кезде оны жылдамдату үшін ойынды өзгертеміз және ұпай жүйесін енгіземіз. Тараудың соңында сіз ойыншы алға жылжған сайын қиындығы арта түсетін және толық ұпай жүйесі бар ойындар жазуды бастау үшін жеткілікті білетін боласыз.
Бұл бөлімде біз ойын басталар алдында пайда болатын және ойын аяқталған кезде қайта пайда болатын Ойнату түймесін қосамыз, осылайша ойыншы қайта ойнай алады.
Дәл қазір, ойын alien_invasion.py іске қосылғаннан кейін бірден басталады. Ойынды белсенді емес күйде бастайық, содан кейін ойыншыны бастау үшін «Ойнату» түймесін басуды сұраймыз. Ол үшін AlienInvasion
__init__()
әдісін өзгертіңіз:
alien_invasion.py
def __init__(self):
"""Ойынды инициализациялаңыз және ойын ресурстарын жасаңыз."""
pygame.init()
--snip/код үзіндісі--
# Start Alien Invasion in an inactive state.
self.game_active = False
Енді ойын белсенді емес күйде басталуы керек, біз «Ойнату» түймесін жасамайынша, ойыншы оны бастауға мүмкіндік бермейді.
Pygame-де түймелерді жасаудың кірістірілген әдісі болмағандықтан, белгісі бар толтырылған тіктөртбұрышты жасау үшін Button
классын жазамыз. Бұл кодты ойында кез-келген түймені жасау үшін пайдалануға болады. Міне, Button
класының бірінші бөлігі; оны button.py ретінде сақтаңыз:
button.py
import pygame.font
class Button:
"""Ойынға арналған түймелерді құрастыруға арналған сынып."""
❶ def __init__(self, ai_game, msg):
"""Түйме атрибуттарын инициализациялау."""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
# Set the dimensions and properties of the button.
❷ self.width, self.height = 200, 50
self.button_color = (0, 135, 0)
self.text_color = (255, 255, 255)
❸ self.font = pygame.font.SysFont(None, 48)
# Build the button's rect object and center it.
❹ self.rect = pygame.Rect(0, 0, self.width, self.height)
self.rect.center = self.screen_rect.center
# The button message needs to be prepped only once.
❺ self._prep_msg(msg)
Біріншіден, Pygame мәтінді экранға шығаруға мүмкіндік беретін pygame.font
модулін импорттаймыз. __init__()
әдісі түйме мәтінін қамтитын self
, ai_game
нысаны және msg
параметрлерін қабылдайды. ❶. Түйме өлшемдерін ❷ орнатамыз, түйменің тік
нысанын қою жасыл түске бояу үшін button_color
мәнін орнатамыз. , және мәтінді ақ түспен көрсету үшін text_color
параметрін орнатыңыз.
Кейін, ❸ мәтінін көрсету үшін font
атрибутын дайындаймыз. None
аргументі Pygame-ге әдепкі қаріпті пайдалануды ұсынады, ал 48
мәтін өлшемін көрсетеді. Түймені экранның ортасына қою үшін ❹ түймесі үшін тік
жасаймыз және оның орталығын
орнатамыз. атрибуты экранға сәйкес келеді.
Pygame кескін ретінде көрсеткіңіз келетін Тіркесті көрсету арқылы мәтінмен жұмыс істейді. Соңында, бұл көрсетуді өңдеу үшін _prep_msg()
шақырамыз ❺.
Міне, _prep_msg()
коды:
button.py
def _prep_msg(self, msg):
"""Хабарды бейнеленген кескінге айналдырыңыз"""
"""және мәтінді түйменің ортасына қойыңыз."""
❶ self.msg_image = self.font.render(msg, True, self.text_color,
self.button_color)
❷ self.msg_image_rect = self.msg_image.get_rect()
self.msg_image_rect.center = self.rect.center
_prep_msg()
әдісіне self
параметрі және сурет (msg
) ретінде көрсетілетін мәтін қажет. font.render()
шақыру msg
ішінде сақталған мәтінді кескінге айналдырады, содан кейін біз оны self.msg_image
ішінде сақтаймыз.❶.font.render()
әдісі антиалиазингті қосу немесе өшіру үшін логикалық мәнді де қабылдайды (антиалиазинг мәтіннің жиектерін тегіс етеді). Қалған аргументтер көрсетілген қаріп түсі мен фон түсі болып табылады. Біз антиалиасингті True
мәніне орнатып, мәтіннің фонын түйменің түсімен бірдей етіп орнатамыз. (Егер фон түсін қоспасаңыз, Pygame қаріпті мөлдір фонмен көрсетуге тырысады.)
Суреттен rect
жасап, оның center
атрибутын түйменің атрибутын сәйкестендіру арқылы мәтіндік кескінді түйменің ортасына келтіреміз. ❷.
Соңында, экранда түймені көрсету үшін шақыруға болатын draw_button()
әдісін жасаймыз:
button.py
def draw_button(self):
"""Бос түймені сызып, хабарламаны сызыңыз."""
self.screen.fill(self.button_color, self.rect)
self.screen.blit(self.msg_image, self.msg_image_rect)
Түйменің тікбұрышты бөлігін салу үшін screen.fill()
деп атаймыз. Содан кейін мәтіндік кескінді экранға салу үшін screen.blit()
шақырамыз, оған кескінді және кескінмен байланысты rect
нысанын жібереміз. Бұл Button
классын аяқтайды.
Біз Button
классын AlienInvasion
ішінде Ойнату түймешігін жасау үшін қолданамыз. Алдымен, import
амалдарын жаңартамыз:
alien_invasion.py
--snip/код үзіндісі--
from game_stats import GameStats
from button import Button
Бізге бір ғана Ойнату түймесі қажет болғандықтан, біз түймені AlienInvasion
қолданбасының __init__()
әдісінде жасаймыз. Біз бұл кодты __init__()
ең соңына орналастыра аламыз:
alien_invasion.py
def __init__(self):
--snip/код үзіндісі--
self.game_active = False
# Make the Play button.
self.play_button = Button(self, "Play")
Бұл код Oyna
белгісімен Button
данасын жасайды, бірақ ол түймені экранға тартпайды. Мұны істеу үшін біз _update_screen()
ішіндегі түйменің draw_button()
әдісін шақырамыз:
alien_invasion.py
def _update_screen(self):
--snip/код үзіндісі--
self.aliens.draw(self.screen)
# Draw the play button if the game is inactive.
if not self.game_active:
self.play_button.draw_button()
pygame.display.flip()
Ойнату түймешігін экрандағы барлық басқа элементтердің үстінен көрсету үшін, біз оны барлық басқа элементтер сызылғаннан кейін, бірақ жаңа экранға өтпес бұрын саламыз. Біз оны if
блогына қосамыз, сондықтан түйме ойын белсенді емес кезде ғана пайда болады.
Енді Alien Invasion қолданбасын іске қосқан кезде экранның ортасында келесі бөлімде көрсетілгендей Ойнату түймешігін көресіз. 14-1-сурет.
14-1-сурет: Ойын белсенді емес кезде ойнату түймесі пайда болады.
Ойыншы Ойнату түймесін басқанда жаңа ойынды бастау үшін, тінтуірдің оқиғаларын бақылау үшін _check_events()
соңына келесі elif
блогын қосыңыз. түймесі:
alien_invasion.py
def _check_events(self):
"""Пернелерді басу және тінтуір оқиғаларына жауап беру."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
--snip/код үзіндісі--
❶ elif event.type == pygame.MOUSEBUTTONDOWN:
❷ mouse_pos = pygame.mouse.get_pos()
❸ self._check_play_button(mouse_pos)
Ойыншы экранның кез-келген жерін басқан кезде Pygame MOUSEBUTTONDOWN
оқиғасын анықтайды ❶, бірақ біз ойынымызды тек Ойнату түймешігіндегі тінтуірдің басылуына жауап беру үшін шектегіміз келеді. Мұны орындау үшін біз тінтуір курсорының x- және y- координаталары бар кортежді қайтаратын pygame.mouse.get_pos()
қолданамыз. тінтуірдің түймесі басылады❷. Бұл мәндерді _check_play_button()
жаңа әдісіне жібереміз ❸.
Міне, _check_play_button()
, мен оны _check_events() кейін орналастыруды таңдадым.
:
alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Ойыншы "Ойнату" түймесін басқан кезде жаңа ойынды бастаңыз."""
❶ if self.play_button.rect.collidepoint(mouse_pos):
self.game_active = True
Тінтуірдің басу нүктесінің Ойнату түймесінің түзу
арқылы анықталған аймақпен қабаттасатынын тексеру үшін rect
әдісін collidepoint()
қолданамыз. ❶. Олай болса, game_active
параметрін True
күйіне орнатамыз және ойын басталады!
Осы кезде сіз толық ойынды бастап, ойнай алуыңыз керек. Ойын аяқталғанда, game_active
мәні False
болып, Ойнату түймесі қайта пайда болуы керек.
Біз жаңа ғана жазған Ойнату түймесінің коды ойнатқыш Play түймесін бірінші рет басқанда жұмыс істейді. Бірақ бірінші ойын аяқталғаннан кейін ол жұмыс істемейді, себебі ойынның аяқталуына себеп болған шарттар қалпына келтірілмеген.
Ойыншы «Ойнату» түймесін басқан сайын ойынды бастапқы күйге қайтару үшін ойын статистикасын қалпына келтіру, ескі өзге планеталықтар мен оқтарды жою, жаңа флот құру және мұнда көрсетілгендей кемені ортаға қою керек:
alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Ойыншы "Ойнату" түймесін"""
"""басқан кезде жаңа ойынды бастаңыз."""
if self.play_button.rect.collidepoint(mouse_pos):
# Reset the game statistics.
❶ self.stats.reset_stats()
self.game_active = True
# Get rid of any remaining bullets and aliens.
❷ self.bullets.empty()
self.aliens.empty()
# Create a new fleet and center the ship.
❸ self._create_fleet()
self.ship.center_ship()
Біз ойын статистикасын қалпына келтіреміз ❶, бұл ойыншыға үш жаңа кеме береді. Содан кейін біз game_active
параметрін True
күйіне орнатамыз, осылайша ойын осы функциядағы код іске қосылған кезде бірден басталады. Біз бөтен планеталықтар
және bullets
топтарын босатамыз ❷, содан кейін біз жаңа флот құрып, кемені орталықтандырамыз❸.
Енді Ойнату түймесін басқан сайын ойын дұрыс қалпына келтіріліп, оны қалағаныңызша ойнауға мүмкіндік береді!
Ойнату түймесіне қатысты бір мәселе экрандағы түйме аймағы «Ойнату» түймесі көрінбесе де, басылғандарға жауап бере береді. Ойын басталғаннан кейін «Ойнату» түймесін кездейсоқ бассаңыз, ойын қайта басталады!
Мұны түзету үшін ойынды game_active
False
болғанда ғана басталатын етіп орнатыңыз:
alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Ойыншы "Ойнату" түймесін басқан кезде жаңа ойынды бастаңыз."""
❶ button_clicked = self.play_button.rect.collidepoint(mouse_pos)
❷ if button_clicked and not self.game_active:
# Reset the game statistics.
self.stats.reset_stats()
--snip/код үзіндісі--
button_clicked
жалауы True
немесе False
мәнін сақтайды ❶ және Ойнату түймесін басқанда және ойын қазіргі уақытта ❷ белсенді емес болса ғана ойын қайта басталады. Бұл әрекетті тексеру үшін жаңа ойынды бастаңыз және «Ойнату» түймесін қайта-қайта басыңыз. Барлығы күткендей жұмыс істесе, «Ойнату» түймесін басу геймплейге әсер етпеуі керек.
Ойын белсенді емес кезде тінтуір курсорының көрінуін қалаймыз, бірақ ойын басталған кезде ол жай ғана кедергі жасайды. Мұны түзету үшін ойын белсенді болған кезде оны көрінбейтін етіп жасаймыз. Біз мұны _check_play_button()
ішіндегі if
блогының соңында жасай аламыз:
alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Ойыншы "Ойнату" түймесін басқан кезде жаңа ойынды бастаңыз."""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.game_active:
--snip/код үзіндісі--
# Hide the mouse cursor.
pygame.mouse.set_visible(False)
False
параметрін set_visible()
параметріне беру Pygame жүйесіне тінтуір ойын терезесінің үстінде тұрғанда курсорды жасыру керектігін айтады.
Ойын аяқталғаннан кейін жүгіргіні қайта көрсетеміз, осылайша ойыншы жаңа ойынды бастау үшін «Ойнату» түймесін қайта басуы мүмкін. Міне, мұны істеу үшін код:
alien_invasion.py
def _ship_hit(self):
"""Кемені бөтен планеталық соққанына жауап беріңіз."""
if self.stats.ships_left > 0:
--snip/код үзіндісі--
else:
self.game_active = False
pygame.mouse.set_visible(True)
Ойын белсенді емес күйге ауысқан кезде курсорды қайтадан көрінетін етіп жасаймыз, бұл _ship_hit()
ішінде орын алады. Осы сияқты мәліметтерге назар аудару ойыныңызды кәсібирек етеді және ойыншыға пайдаланушы интерфейсін анықтауға емес, ойнауға назар аударуға мүмкіндік береді.
14-1. Ойнату үшін P түймесін басыңыз: Alien Invasion кемені басқару үшін пернетақта енгізуін пайдаланатындықтан, ойынды пернені басу арқылы бастау пайдалы болар еді. Ойнатқышты бастау үшін P түймесін басуға мүмкіндік беретін кодты қосыңыз. Бұл кейбір кодты _check_play_button()
орнынан _start_game()
әдісіне жылжытуға көмектесуі мүмкін, оны _check_play_button()
және арқылы шақыруға болады. _check_keydown_events()
.
14-2. Мақсатты тәжірибе: Экранның оң жақ шетінде тұрақты жылдамдықпен жоғары және төмен жылжыйтын тіктөртбұрыш жасаңыз. Содан кейін экранның сол жағында тікбұрышты нысанаға оқ атқан кезде ойыншы жоғары және төмен қозғала алатын кеме жасаңыз. Ойынды бастайтын «Ойнату» түймесін қосыңыз және ойыншы мақсатты үш рет өткізіп алған кезде, ойынды аяқтап, «Ойнату» түймесін қайта пайда етіңіз. Ойыншыға осы Ойнату түймесі арқылы ойынды қайта бастауға рұқсат етіңіз.
Қазіргі ойынымызда ойыншы бүкіл бөтен флотты атып түсіргеннен кейін, ойыншы жаңа деңгейге жетеді, бірақ ойынның қиындығы өзгермейді. Ойыншы экранды тазалаған сайын ойын жылдамдығын арттырып, жағдайды сәл жандандырып, ойынды қиындатайық.
Ойын параметрлерін статикалық және динамикалық болып топтау үшін алдымен Параметрлер
классын қайта ұйымдастырамыз. Сондай-ақ, біз жаңа ойынды бастаған кезде ойын кезінде өзгеретін кез-келген параметрлерді қалпына келтіреміз. Мұнда settings.py үшін __init__()
әдісі берілген:
settings.py
def __init__(self):
"""Ойынның статикалық параметрлерін инициализациялау."""
# Screen settings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
# Ship settings
self.ship_limit = 3
# Bullet settings
self.bullet_width = 3
self.bullet_height = 15
self.bullet_color = 60, 60, 60
self.bullets_allowed = 3
# Alien settings
self.fleet_drop_speed = 10
# How quickly the game speeds up
❶
self.speedup_scale = 1.1
❷
self.initialize_dynamic_settings()
Біз __init__()
әдісінде тұрақты болып қалатын параметрлерді инициализациялауды жалғастырамыз. Ойынның жылдамдығын басқару үшін speedup_scale
параметрін ❶ қосамыз: 2 мәні ойын жылдамдығын екі есе арттырады ойыншы жаңа деңгейге жеткен сайын; 1 мәні жылдамдықты тұрақты сақтайды. 1.1
сияқты мән ойынды қиындату үшін жеткілікті жылдамдықты арттыруы керек, бірақ мүмкін емес. Соңында, ❷ ойынында өзгеруі қажет атрибуттардың мәндерін инициализациялау үшін initialize_dynamic_settings()
әдісін шақырамыз.
Міне, initialize_dynamic_settings()
коды:
settings.py
def initialize_dynamic_settings(self):
"""Ойын бойы өзгеретін параметрлерді инициализациялау."""
self.ship_speed = 1.5
self.bullet_speed = 2.5
self.alien_speed = 1.0
# fleet_direction of 1 represents right; -1 represents left.
self.fleet_direction = 1
Бұл әдіс кеменің, оқтың және бөгде жылдамдықтардың бастапқы мәндерін орнатады. Ойыншы ойында алға жылжыған сайын бұл жылдамдықтарды арттырамыз және ойыншы жаңа ойынды бастаған сайын оларды қалпына келтіреміз. Біз бұл әдіске fleet_direction
қосамыз, сондықтан өзге планеталықтар әрқашан жаңа ойынның басында тура қозғалады. Бізге fleet_drop_speed
мәнін арттырудың қажеті жоқ, себебі бөгде планеталықтар экранда жылдамырақ қозғалғанда, олар да экраннан жылдамырақ түседі.
Ойыншы жаңа деңгейге жеткен сайын кеменің, оқтардың және өзге планеталықтардың жылдамдығын арттыру үшін increase_speed()
деп аталатын жаңа әдіс жазамыз:
settings.py
def increase_speed(self):
"""Жылдамдық параметрлерін арттыру."""
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_speed *= self.speedup_scale
Осы ойын элементтерінің жылдамдығын арттыру үшін біз әрбір жылдамдық параметрін speedup_scale
мәніне көбейтеміз.
Флоттағы соңғы бөтен планеталық атып түсірілген кезде _check_bullet_alien_collisions()
ішіндегі increase_speed()
функциясын шақыру арқылы ойын қарқынын арттырамыз:
alien_invasion.py
def _check_bullet_alien_collisions(self):
--snip/код үзіндісі--
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()
Бүкіл ойынды жылдамдату үшін ship_speed
, alien_speed
және bullet_speed
жылдамдық параметрлерінің мәндерін өзгерту жеткілікті!
Енді ойыншы жаңа ойынды бастаған сайын өзгертілген параметрлерді бастапқы мәндеріне қайтаруымыз керек; әйтпесе, әрбір жаңа ойын алдыңғы ойынның жоғарылатылған жылдамдық параметрлерімен басталады:
alien_invasion.py
def _check_play_button(self, mouse_pos):
"""Ойыншы "Ойнату" түймесін басқан кезде жаңа ойынды бастаңыз."""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked and not self.game_active:
# Reset the game settings.
self.settings.initialize_dynamic_settings()
--snip/код үзіндісі--
Alien Invasion ойынын ойнау қазір қызық әрі қиын болуы керек. Экранды тазалаған сайын ойын жылдамдығын арттыруы және сәл қиындауы керек. Ойын тым тез қиындаса, settings.speedup_scale
мәнін азайтыңыз. Немесе ойын жеткілікті қиын болмаса, мәнді сәл арттырыңыз. Ақылға қонымды уақыт ішінде қиындықты арттыру арқылы тәтті орынды табыңыз. Алғашқы екі экран оңай болуы керек, келесі бірнеше экран қиын, бірақ орындалатын болуы керек, ал кейінгі экрандар мүмкін емес дерлік қиын болуы керек.
14-3. Күрделі мақсатты тәжірибе: Жұмысыңызды 14-2-жаттығудан бастаңыз (283-бет). Ойын барысында нысананы жылдамырақ жылжытыңыз және ойыншы «Ойнату» түймесін басқан кезде нысананы бастапқы жылдамдықпен қайта іске қосыңыз.
14-4. Қиындық деңгейлері: Ойыншыға ойынға сәйкес бастапқы қиындық деңгейін таңдауға мүмкіндік беретін Инвазияға арналған түймелер жинағын жасаңыз. Әр түйме әртүрлі қиындық деңгейлерін жасау үшін қажетті Параметрлер
ішіндегі атрибуттар үшін сәйкес мәндерді тағайындауы керек.
Ойынның ұпайын нақты уақытта қадағалап, жоғары ұпайды, деңгейді және қалған кемелер санын көрсету үшін баллдық жүйені енгізейік.
Ұпай - бұл ойын статистикасы, сондықтан GameStats
-ға score
атрибутын қосамыз:
game_stats.py
class GameStats:
--snip/код үзіндісі--
def reset_stats(self):
"""Ойын барысында өзгеруі мүмкін статистиканы инициализациялау."""
self.ships_left = self.ai_settings.ship_limit
self.score = 0
Жаңа ойын басталған сайын ұпайды қалпына келтіру үшін __init__()
орнына reset_stats()
ішінде score
инициализациялаймыз.
Ұпайды экранда көрсету үшін алдымен Балдар тақтасы
жаңа класс жасаймыз. Әзірге бұл класс ағымдағы ұпайды көрсетеді. Ақыр соңында, біз оны жоғары балл, деңгей және қалған кемелер саны туралы хабарлау үшін қолданамыз. Міне, кластың бірінші бөлімі; оны scoreboard.py ретінде сақтаңыз:
scoreboard.py
import pygame.font
class Scoreboard:
"""Ұпай туралы ақпаратты хабарлауға арналған сынып."""
❶ def __init__(self, ai_game):
"""Ұпайларды есепке алу атрибуттарын инициализациялау."""
self.screen = ai_game.screen
self.screen_rect = self.screen.get_rect()
self.settings = ai_game.settings
self.stats = ai_game.stats
# Font settings for scoring information.
❷ self.text_color = (30, 30, 30)
❸ self.font = pygame.font.SysFont(None, 48)
# Prepare the initial score image.
❹ self.prep_score()
Scoreboard
экранға мәтін жазатындықтан, біз pygame.font
модулін импорттаудан бастаймыз. Содан кейін біз __init__()
параметрін ai_game
параметрін береміз, осылайша ол параметрлерге
, экран
және қол жеткізе алады. >stats
нысандары, ол біз қадағалап отырған мәндерді хабарлау үшін қажет болады ❶. Содан кейін біз мәтін түсін орнатамыз ❷ және қаріп нысанын іске қосыңыз ❸.
Көрсетілетін мәтінді кескінге айналдыру үшін prep_score()
шақырамыз.❹, біз мұнда анықтаймыз:
scoreboard.py
def prep_score(self):
"""Ұпайды көрсетілген кескінге айналдырыңыз."""
❶
score_str = str(self.stats.score)
❷ self.score_image = self.font.render(score_str, True,
self.text_color, self.settings.bg_color)
# Display the score at the top right of the screen.
❸
self.score_rect = self.score_image.get_rect()
❹
self.score_rect.right = self.screen_rect.right - 20
❺
self.score_rect.top = 20
prep_score()
ішінде stats.score
сандық мәнін ❶ жолына айналдырамыз. және одан кейін осы Тіркесті ❷ кескінін жасайтын render()
параметріне өткізіңіз. Экранда ұпайды анық көрсету үшін біз экранның фон түсін және мәтін түсін render()
-ге береміз.
Ұпайды экранның жоғарғы оң жақ бұрышына орналастырамыз және ұпай ұлғайған сайын және сан ені ұлғайған сайын оны солға қарай кеңейтеміз. Ұпай әрқашан экранның оң жағына сәйкес келетініне көз жеткізу үшін біз score_rect
деп аталатын тік
жасаймыз ❸ және оның оң жақ жиегін экранның оң жақ шетінен 20 пиксельге ❹ орнатыңыз. Содан кейін жоғарғы жиекті экранның жоғарғы жағынан 20 пиксель төмен қарай орналастырамыз ❺.
Одан кейін көрсетілген ұпай кескінін көрсету үшін show_score()
әдісін жасаймыз:
scoreboard.py
def show_score(self):
"""Экранға ұпай салыңыз."""
self.screen.blit(self.score_image, self.score_rect)
Бұл әдіс экрандағы ұпай кескінін score_rect
көрсеткен жерде салады.
Ұпайды көрсету үшін AlienInvasion
ішінде Балдар тақтасы
данасын жасаймыз. Алдымен import
амалдарын жаңартайық:
alien_invasion.py
--snip/код үзіндісі--
from game_stats import GameStats
from scoreboard import Scoreboard
--snip/код үзіндісі--
Келесі, __init__()
ішінде Балдар тақтасы
данасын жасаймыз:
alien_invasion.py
def __init__(self):
--snip/код үзіндісі--
pygame.display.set_caption("Alien Invasion")
# Create an instance to store game statistics,
# and create a scoreboard.
self.stats = GameStats(self)
self.sb = Scoreboard(self)
--snip/код үзіндісі--
Одан кейін экрандағы таблоны _update_screen()
ішінде саламыз:
alien_invasion.py
def _update_screen(self):
--snip/код үзіндісі--
self.aliens.draw(self.screen)
# Draw the score information.
self.sb.show_score()
# Draw the play button if the game is inactive.
--snip/код үзіндісі--
Ойнату түймесін салар алдында біз show_score()
деп атаймыз.
Қазір Alien Invasion іске қосылғанда, экранның жоғарғы оң жағында 0 пайда болуы керек. (Осы кезде біз баллдық жүйені әрі қарай дамытпас бұрын ұпайдың дұрыс жерде пайда болғанына көз жеткізгіміз келеді.) 14-2-сурет ұпайды ойын басталмай тұрып көрсетеді.
Одан әрі біз әрбір бөтен планеталыққа ұпай мәндерін тағайындаймыз!
14-2-сурет: ұпай экранның жоғарғы оң жақ бұрышында көрсетіледі.
Тікелей нәтижені экранға жазу үшін біз бөтен планеталыққа тиген сайын stats.score
мәнін жаңартамыз, содан кейін prep_score()
-ге қоңырау шаламыз. ұпай кескінін жаңартыңыз. Алдымен, ойыншы бөтен планеталықты атып түсірген сайын қанша ұпай алатынын анықтап алайық:
settings.py
def initialize_dynamic_settings(self):
--snip/код үзіндісі--
# Scoring settings
self.alien_points = 50
Ойын барысында біз әрбір бөтен планеталықтың ұпайын арттырамыз. Жаңа ойын басталған сайын бұл нүкте мәні қалпына келтірілетініне көз жеткізу үшін мәнді initialize_dynamic_settings()
ішінде орнатамыз.
Келіңіз, бөтен планеталық атып түсірілген сайын _check_bullet_alien_collisions()
ішіндегі ұпайды жаңартайық:
alien_invasion.py
def _check_bullet_alien_collisions(self):
"""Оқ-бөтен планеталық соқтығыстарға жауап беріңіз."""
# Remove any bullets and aliens that have collided.
collisions = pygame.sprite.groupcollide(
self.bullets, self.aliens, True, True)
if collisions:
self.stats.score += self.settings.alien_points
self.sb.prep_score()
--snip/код үзіндісі--
Оқ бөтенге тиген кезде, Pygame соқтығыстар
сөздігін қайтарады. Біз сөздіктің бар-жоғын тексереміз, егер бар болса, бөтен планеталықтың мәні ұпайға қосылады. Содан кейін жаңартылған ұпай үшін жаңа кескін жасау үшін prep_score()
шақырамыз.
Енді Alien Invasion ойынын ойнаған кезде ұпай жинай алуыңыз керек!
Дәл қазір біз ойынның көп бөлігінде жұмыс істейтін бөтен планеталық соққыға жығылғаннан кейін ғана соңғы жаңа ұпай дайындап жатырмыз. Бірақ жаңа ойынды бастағанда, бірінші бөтен планеталық соққыға дейін бұрынғы ойындағы ұпайымызды көреміз.
Жаңа ойынды бастау кезінде ұпайды дайындау арқылы мұны түзете аламыз:
alien_invasion.py
def _check_play_button(self, mouse_pos):
--snip/код үзіндісі--
if button_clicked and not self.game_active:
--snip/код үзіндісі--
# Reset the game statistics.
self.stats.reset_stats()
self.sb.prep_score()
--snip/код үзіндісі--
Жаңа ойынды бастаған кезде ойын статистикасын қалпына келтіргеннен кейін prep_score()
деп атаймыз. Бұл таблоны 0 ұпаймен дайындайды.
Қазір жазылғандай, біздің код кейбір өзге планеталықтар үшін ұпай жинамауы мүмкін. Мысалы, егер цикл арқылы бір өту кезінде екі оқ бөтен планеталықтармен соқтығысса немесе бірнеше өзге планеталықтарды тигізу үшін қосымша кең оқ жасасақ, ойыншы тек өзге планеталықтардың біреуіне тигені үшін ұпай алады. Мұны түзету үшін оқ пен бөтен планеталықтың соқтығысуын анықтау әдісін нақтылайық.
_check_bullet_alien_collisions()
ішінде бөгде адаммен соқтығысқан кез-келген таңбалауыш соқтығыстар
сөздігінде кілтке айналады. Әрбір таңбамен байланысты мән ол соқтығысқан өзге планеталықтардың тізімі болып табылады. Біз соқтығыстар
сөздігіндегі мәндерді айналып өтіп, әрбір бөтен соққыға ұпай беретінімізге көз жеткіземіз:
alien_invasion.py
def _check_bullet_alien_collisions(self):
--snip/код үзіндісі--
if collisions:
for aliens in collisions.values():
self.stats.score += self.settings.alien_points * len(aliens)
self.sb.prep_score()
--snip/код үзіндісі--
Егер соқтығыстар
сөздігі анықталған болса, біз сөздіктегі барлық мәндерді айналдырамыз. Әрбір мән бір оқ тиген өзге планеталықтардың тізімі екенін есте сақтаңыз. Біз әрбір бөтен планеталықтың мәнін әр тізімдегі өзге планеталықтар санына көбейтеміз және бұл соманы ағымдағы ұпайға қосамыз. Мұны тексеру үшін оқтың енін 300 пиксельге өзгертіңіз және қосымша кең оқтарыңызбен соққан әрбір бөтен планеталық үшін ұпай алатыныңызды тексеріңіз; содан кейін таңбалауыш енін қалыпты мәніне қайтарыңыз.
Ойыншы жаңа деңгейге жеткен сайын ойын қиындай түсетіндіктен, кейінгі деңгейлердегі өзге планеталықтар көбірек ұпай алуы керек. Бұл функцияны іске асыру үшін ойын жылдамдығы артқан кезде ұпай мәнін арттыру үшін код қосамыз:
settings.py
class Settings:
"""Жат планеталық шабуылға арналған барлық параметрлерді сақтауға арналған сынып."""
def __init__(self):
--snip/код үзіндісі--
# How quickly the game speeds up
self.speedup_scale = 1.1
# How quickly the alien point values increase
❶ self.score_scale = 1.5
self.initialize_dynamic_settings()
def initialize_dynamic_settings(self):
--snip/код үзіндісі--
def increase_speed(self):
"""Жылдамдық параметрлерін және бөгде нүкте мәндерін арттырыңыз."""
self.ship_speed *= self.speedup_scale
self.bullet_speed *= self.speedup_scale
self.alien_speed *= self.speedup_scale
❷ self.alien_points = int(self.alien_points * self.score_scale)
Біз ұпайлардың өсу жылдамдығын анықтаймыз, оны score_scale
❶ деп атаймыз. Жылдамдықтың шамалы өсуі (1.1
) ойынды тезірек қиындатады. Бірақ ұпай санының айтарлықтай айырмашылығын көру үшін біз бөгде нүкте мәнін үлкенірек мөлшерге өзгертуіміз керек (1,5
). Енді ойын жылдамдығын арттырған кезде, әр соққының ұпай мәнін де арттырамыз ❷. Нүкте мәнін бүтін бүтін сандарға көбейту үшін int()
функциясын қолданамыз.
Әр бөтен планеталықтың мәнін көру үшін Параметрлер
ішіндегі increase_speed()
әдісіне print()
шақыруын қосыңыз:
settings.py
def increase_speed(self):
--snip/код үзіндісі--
self.alien_points = int(self.alien_points * self.score_scale)
print(self.alien_points)
Жаңа нүкте мәні жаңа деңгейге жеткен сайын терминалда пайда болуы керек.
Ұпай мәні артып жатқанын тексергеннен кейін print()
шақыруын алып тастаңыз, әйтпесе ол ойынның өнімділігіне әсер етіп, ойыншыны алаңдатуы мүмкін.
Аркада стиліндегі ату ойындарының көпшілігі ұпайларды 10-ға еселік деп есептейді, сондықтан ұпайларымызбен осы көшті жалғастырайық. Сондай-ақ, үлкен сандарға үтір бөлгіштерін қосу үшін ұпайды пішімдейік. Біз бұл өзгерісті Балдар тақтасы
ішінде жасаймыз:
scoreboard.py
def prep_score(self):
"""Ұпайды көрсетілген кескінге айналдырыңыз."""
rounded_score = round(self.stats.score, -1)
score_str = f"{rounded_score:,}"
self.score_image = self.font.render(score_str, True,
self.text_color, self.settings.bg_color)
--snip/код үзіндісі--
round()
функциясы әдетте қалқымалы мәнді екінші аргумент ретінде берілген ондық таңбалардың белгіленген санына дейін дөңгелектейді. Дегенмен, екінші аргумент ретінде теріс санды бергенде, round()
мәнді 10, 100, 1000 және т.б. дәлдікке дейін дөңгелектейді. Бұл код Python бағдарламасына stats.score
мәнін ең жақын 10-ға дейін дөңгелектеп, оны rounded_score
мәніне тағайындауды айтады.
Одан кейін ұпай үшін f жолындағы пішім спецификаторын қолданамыз. Пішім спецификаторы — айнымалы мәннің ұсынылу жолын өзгертетін таңбалардың арнайы тізбегі. Бұл жағдайда :,
тізбегі Python-ға берілген сандық мәннің сәйкес жерлеріне үтірлерді қоюды ұсынады. Бұл 1000000
орнына 1,000,000
сияқты жолдарға әкеледі.
Енді ойынды іске қосқан кезде, суретте көрсетілгендей, көптеген ұпай жинасаңыз да, дұрыс пішімделген, дөңгелектелген ұпайды көресіз. 14-3.
14-3-сурет: үтір бөлгіштері бар дөңгелектелген ұпай
Әр ойыншы ойынның жоғары ұпайын жеңгісі келеді, сондықтан ойыншыларға жұмыс істеуге болатын нәрсе беру үшін жоғары ұпайларды қадағалап, есеп берейік. Біз жоғары ұпайларды GameStats
ішінде сақтаймыз:
game_stats.py
def __init__(self, ai_game):
--snip/код үзіндісі--
# High score should never be reset.
self.high_score = 0
Жоғары балл ешқашан қалпына келтірілмеуі керек болғандықтан, біз high_score
мәнін reset_stats()
емес, __init__()
параметрінде инициализациялаймыз.
Келесі, біз жоғары ұпайды көрсету үшін Балдар тақтасын
өзгертеміз. __init__()
әдісінен бастайық:
scoreboard.py
def __init__(self, ai_game):
--snip/код үзіндісі--
# Prepare the initial score images.
self.prep_score()
❶ self.prep_high_score()
Жоғары балл ұпайдан бөлек көрсетіледі, сондықтан жоғары ұпайлы кескінді дайындау үшін бізге жаңа әдіс, prep_high_score()
қажет ❶.
Міне, prep_high_score()
әдісі:
scoreboard.py
def prep_high_score(self):
"""Жоғары ұпайды көрсетілген кескінге айналдырыңыз."""
❶ high_score = round(self.stats.high_score, -1)
high_score_str = f"{high_score:,}"
❷ self.high_score_image = self.font.render(high_score_str, True,
self.text_color, self.settings.bg_color)
# Center the high score at the top of the screen.
self.high_score_rect = self.high_score_image.get_rect()
❸ self.high_score_rect.centerx = self.screen_rect.centerx
❹ self.high_score_rect.top = self.score_rect.top
Жоғары ұпайды 10-ға дейін дөңгелектеп, үтірмен пішімдейміз ❶. Содан кейін біз жоғары ұпайдан кескін жасаймыз ❷, rect
жоғары ұпайды көлденеңінен ортаға қойыңыз❸, және оның top
атрибутын ұпай кескінінің жоғарғы жағына сәйкестендіру үшін орнатыңыз❹.
show_score()
әдісі енді ағымдағы ұпайды экранның жоғарғы оң жағында және жоғарғы ұпайды экранның жоғарғы ортасына салады:
scoreboard.py
def show_score(self):
"""Экранға ұпай салыңыз."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
Жоғары балл бар-жоғын тексеру үшін Көрсеткіштер тақтасында
жаңа әдісті, check_high_score()
жазамыз:
scoreboard.py
def check_high_score(self):
"""Жаңа жоғары балл бар-жоғын тексеріңіз."""
if self.stats.score > self.stats.high_score:
self.stats.high_score = self.stats.score
self.prep_high_score()
check_high_score()
әдісі ағымдағы ұпайды жоғары баллмен салыстырады. Ағымдағы балл үлкенірек болса, high_score
мәнін жаңартамыз және жоғары ұпай кескінін жаңарту үшін prep_high_score()
шақырамыз.
_check_bullet_alien_collisions()
ішіндегі ұпайды жаңартқаннан кейін бөтен планеталыққа тиген сайын check_high_score()
деп қоңырау шалуымыз керек:
alien_invasion.py
def _check_bullet_alien_collisions(self):
--snip/код үзіндісі--
if collisions:
for aliens in collisions.values():
self.stats.score += self.settings.alien_points * len(aliens)
self.sb.prep_score()
self.sb.check_high_score()
--snip/код үзіндісі--
Біз colisions
сөздігі бар кезде check_high_score()
деп атаймыз және оны соққыға ұшыраған барлық өзге планеталықтар үшін баллды жаңартқаннан кейін жасаймыз.
Алғаш рет Invasion Invasion ойынын ойнаған кезде сіздің ұпайыңыз жоғары ұпай болады, сондықтан ол ағымдағы ұпай және жоғары ұпай ретінде көрсетіледі. Бірақ екінші ойынды бастағанда, сіздің жоғары ұпайыңыз ортасында, ал ағымдағы ұпайыңыз оң жақта пайда болуы керек, 14-суретте көрсетілгендей. 4.
14-4-сурет: Жоғары ұпай экранның жоғарғы ортасында көрсетіледі.
Ойындағы ойыншының деңгейін көрсету үшін алдымен GameStats
ішінде ағымдағы деңгейді білдіретін атрибут қажет. Әрбір жаңа ойынның басында деңгейді қалпына келтіру үшін оны reset_stats()
ішінде инициализациялаңыз:
game_stats.py
def reset_stats(self):
"""Ойын барысында өзгеруі мүмкін статистиканы инициализациялау."""
self.ships_left = self.settings.ship_limit
self.score = 0
self.level = 1
Scoreboard
ағымдағы деңгейді көрсету үшін __init__()
жаңа әдісті, prep_level()
шақырамыз.:
scoreboard.py
def __init__(self, ai_game):
--snip/код үзіндісі--
self.prep_high_score()
self.prep_level()
Міне, prep_level()
:
scoreboard.py
def prep_level(self):
"""Деңгейді көрсетілген кескінге айналдырыңыз."""
level_str = str(self.stats.level)
❶ self.level_image = self.font.render(level_str, True,
self.text_color, self.settings.bg_color)
# Position the level below the score.
self.level_rect = self.level_image.get_rect()
❷ self.level_rect.right = self.score_rect.right
❸ self.level_rect.top = self.score_rect.bottom + 10
prep_level()
әдісі stats.level
ішінде сақталған мәннен кескін жасайды. ❶ және суреттің right
атрибутын ұпайдың right
атрибутына сәйкестендіру үшін орнатады ❷. Содан кейін ол ұпай мен деңгей арасында бос орын қалдыру үшін top
атрибутын ұпай кескінінің төменгі жағына 10 пиксельге орнатады. ❸.
Біз сондай-ақ show_score() жаңартуымыз керек.
:
scoreboard.py
def show_score(self):
"""Ұпайларды салыңыз және экранға деңгей қойыңыз."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
Бұл жаңа сызық деңгейлік кескінді экранға тартады.
Біз stats.level
деңгейін арттырамыз және _check_bullet_alien_collisions()
ішіндегі деңгей кескінін жаңартамыз:
alien_invasion.py
def _check_bullet_alien_collisions(self):
--snip/код үзіндісі--
if not self.aliens:
# Destroy existing bullets and create new fleet.
self.bullets.empty()
self._create_fleet()
self.settings.increase_speed()
# Increase level.
self.stats.level += 1
self.sb.prep_level()
Егер флот жойылса, біз stats.level
мәнін арттырамыз және жаңа деңгей дұрыс көрсетілетініне көз жеткізу үшін prep_level()
шақырамыз.
Жаңа ойынның басында деңгей кескінінің дұрыс жаңартылуын қамтамасыз ету үшін ойыншы Ойнату түймесін басқанда prep_level()
деп те шақырамыз:
alien_invasion.py
def _check_play_button(self, mouse_pos):
--snip/код үзіндісі--
if button_clicked and not self.game_active:
--snip/код үзіндісі--
self.sb.prep_score()
self.sb.prep_level()
--snip/код үзіндісі--
Біз prep_level()
қызметіне prep_score()
қоңырау шалғаннан кейін бірден қоңырау шаламыз.
Енді 14-5-сурет-де көрсетілгендей қанша деңгейді аяқтағаныңызды көресіз.
14-5-сурет: Ағымдағы деңгей ағымдағы ұпайдан сәл төмен көрінеді.
Кейбір классикалық ойындарда ұпайларда Ұпай, Жоғары ұпай және Деңгей сияқты белгілер болады. Біз бұл белгілерді алып тастадық, себебі әр санның мағынасы ойынды ойнағаннан кейін анық болады. Бұл белгілерді қосу үшін оларды Scoreboard
ішіндегі font.render()
шақыруларының алдында ұпайлар жолына қосыңыз.
Соңында, ойыншы қалдырған кемелер санын көрсетейік, бірақ бұл жолы графиканы қолданайық. Мұны істеу үшін, көптеген классикалық аркада ойындары сияқты қанша кеме қалғанын көрсету үшін экранның жоғарғы сол жақ бұрышына кемелерді саламыз.
Біріншіден, біз кемелер тобын құру үшін Ship
мұрағатын Sprite
жасауымыз керек:
ship.py
import pygame
from pygame.sprite import Sprite
❶ class Ship(Sprite):
"""Кемені басқаруға арналған сынып."""
def __init__(self, ai_game):
"""Кемені инициализациялаңыз және оның бастапқы орнын орнатыңыз."""
❷ super().__init__()
--snip/код үзіндісі--
Мұнда біз Sprite
импорттаймыз, Ship
-ның Sprite-тен мұраланғанына көз жеткізіңіз.
❶, және __init__()
басында super()
деп атаңыз.❷.
Кейін, біз көрсете алатын кемелер тобын жасау үшін Көрсеткіштер тақтасын
өзгертуіміз керек. Міне, Импорт
амалдары Көрсеткіштер тақтасы
:
scoreboard.py
import pygame.font
from pygame.sprite import Group
from ship import Ship
Біз кемелер тобын жасап жатқандықтан, Group
және Ship
класстарын импорттаймыз.
Here’s __init__()
:
scoreboard.py
def __init__(self, ai_game):
"""Ұпайларды есепке алу атрибуттарын инициализациялау."""
self.ai_game = ai_game
self.screen = ai_game.screen
--snip/код үзіндісі--
self.prep_level()
self.prep_ships()
Ойын данасын атрибутқа тағайындаймыз, себебі ол кейбір кемелерді жасау үшін қажет болады. Біз prep_ships()
шақырудан кейін prep_level()
шақырамыз.
Міне, prep_ships()
:
scoreboard.py
def prep_ships(self):
"""Қанша кеме қалғанын көрсетіңіз."""
❶ self.ships = Group()
❷ for ship_number in range(self.stats.ships_left):
ship = Ship(self.ai_game)
❸ ship.rect.x = 10 + ship_number * ship.rect.width
❹ ship.rect.y = 10
❺ self.ships.add(ship)
prep_ships()
әдісі кеме даналарын ұстау үшін бос топты жасайды, self.ships
❶. Бұл топты толтыру үшін ойыншы қалдырған әрбір кеме үшін цикл бір рет орындалады ❷. Цикл ішінде біз жаңа кеме жасаймыз және әрбір кеменің xкоординаталық мәнін орнатамыз, осылайша кемелер бір-бірінің жанында, кемелер тобының сол жағында 10 пиксельді маржамен пайда болады. ❸. Біз y-координаталық мәнді экранның жоғарғы жағынан 10 пиксель төмен орнаттық, осылайша кемелер экранның жоғарғы сол жақ бұрышында пайда болады. ❹. Содан кейін әрбір жаңа кемені ships
тобына қосамыз ❺.
Енді бізге кемелерді экранға салу керек:
scoreboard.py
def show_score(self):
"""Ұпайларды, деңгейді сызыңыз және экранға жіберіңіз."""
self.screen.blit(self.score_image, self.score_rect)
self.screen.blit(self.high_score_image, self.high_score_rect)
self.screen.blit(self.level_image, self.level_rect)
self.ships.draw(self.screen)
Кемелерді экранда көрсету үшін топтағы draw()
деп атаймыз, ал Pygame әрбір кемені тартады.
Ойыншыға қанша кемеден бастау керек екенін көрсету үшін жаңа ойын басталғанда prep_ships()
деп атаймыз. Біз мұны _check_play_button()
ішінде AlienInvasion
ішінде орындаймыз:
alien_invasion.py
def _check_play_button(self, mouse_pos):
--snip/код үзіндісі--
if button_clicked and not self.game_active:
--snip/код үзіндісі--
self.sb.prep_level()
self.sb.prep_ships()
--snip/код үзіндісі--
Сондай-ақ, ойыншы кемені жоғалтқан кезде кеме суреттерінің дисплейін жаңарту үшін, біз prep_ships()
функциясын кемеге соққан кезде шақырамыз:
alien_invasion.py
def _ship_hit(self):
"""Кемені бөтен планеталық соққанына жауап беріңіз."""
if self.stats.ships_left > 0:
# Decrement ships_left, and update scoreboard.
self.stats.ships_left -= 1
self.sb.prep_ships()
--snip/код үзіндісі--
Біз ships_left
мәнін азайтқаннан кейін prep_ships()
деп атаймыз, сондықтан кеме жойылған сайын қалған кемелердің дұрыс саны көрсетіледі.
14-6суретте қалған кемелер экранның жоғарғы сол жағында көрсетілген толық баллдық жүйе көрсетілген.
Сурет 14-6: Инвазияға арналған толық баллдық жүйе
14-5. Барлық уақыттағы жоғары бақылау: Alien Invasion ойынын жауып, іске қосқан сайын жоғары деңгейде ең жақсы қалпына келтіріледі. бақылауға sys.exit()
шақырмас бұрын жоғары жүкті жазу және оның мәнін GameStats
ішінде инициализациялау кезінде жоғары қадағалауды оқу арқылы түзетіңіз.
14-6. Рефакторинг: Бірнеше тапсырманы орындау әдістерін іздестіру және кодты реттеп, оны тиімді ету үшін оларды қайта өңдеу. Мысалы, өзге планеталықтар флоты жойылған кезде жаңа деңгейді бастайтын _check_bullet_alien_collisions()
ішіндегі код біріне start_new_level()
деп басқа функцияға өзгертіңіз. Сондай-ақ, __init__()
әдісіндегі __init__()
әдісіндегі төрт бөлек әдіс шақыруларын __init__
қысқарту үшін prep_images()
деп қайта әдіске өзгертіңіз. prep_images()
әдісі _check_play_button()
немесе _check_play_button()
қайта өңделген болса, start_game()
-ды жеңілдетуге де көмектесуі мүмкін.
Жобаны қайта өңдеуге әрекет жасамас бұрын, рефакторинг кезінде қателерді енгізсеңіз, жобаны жұмыс күйіне түзету жолын білу үшін D қосымшасын іздеңіз.
14-7. Ойынды кеңейту: Инвазияны кеңейту жолын ойластырыңыз. Мысалы, сіз өзге планеталықтарды кемеңізге оқ атуға бағдарламалай аласыз. Сондай-ақ, сіз өзіңіздің кемеңіздің артына тығылуы үшін қалқандарды қоса аласыз, оларды екі жағынан да оқ жаудыруы мүмкін. Немесе жарылыс және ату дыбыстары сияқты дыбыс әсерлерін қосу үшін pygame.mixer
модулі сияқты нәрсені пайдалануға болады.
14-8. Sideways Shooter, соңғы нұсқасы: Осы жобада жасаған барлық мүмкіндіктерді пайдаланып, Sideways Shooter әзірлеуді жалғастырыңыз. Ойнату түймесін қосыңыз, ойынды тиісті нүктелерде жылдамдатыңыз және ұпайлар жүйесін жасаңыз. Жұмыс барысында кодты қайта өңдеуді ұмытпаңыз және ойынды осы тарауда көрсетілгеннен тыс реттеу мүмкіндіктерін іздеңіз.
Бұл тарауда сіз жаңа ойынды бастау үшін Ойнату түймесін қалай іске қосу керектігін білдіңіз. Сондай-ақ белсенді ойындарда тінтуірдің оқиғаларын анықтауды және курсорды жасыруды үйрендіңіз. Ойындарды ойнау туралы нұсқауларды көрсету үшін Анықтама түймесі сияқты басқа түймелерді жасау үшін үйренгендеріңізді пайдалана аласыз. Сондай-ақ, сіз ойынның өту жылдамдығын өзгертуді, прогрессивті бағалау жүйесін енгізуді және ақпаратты мәтіндік және мәтіндік емес Тіркестермен көрсетуді үйрендіңіз.
Сәлем достар!
Бұл сабақтар сериясында Python бағдарламалау тілін қолданып шахмат ойынын жасауға тырысып көреміз. Бұл жобаның авторы Eddie Sharick есімімен тіркелген YouTube қолданушысы - "... орта мектептің информатика және физика пәнінің мұғалімі, ол бағдарламалауды жақсы көреді және бағдарламалауды басқалармен бөліседі. Ол сондай-ақ өте үлкен спорт жанкүйері, басқа хоббиі хоккей, фортепиано және шахматты қамтиды."
Сабақ қызықты әрі Python тілін үйренуде пайдалы болатыны сөзсіз. Олай болса кірісейік. Бірінші Шахмат ойынымен, тарихымен, ережесімен аздап танысудан бастайық.
Шахмат – екі ойыншыға арналған үстел ойыны. Ойыншылардың әрқайсысы Ақ және Қара деп аталатын шахмат фигураларының армиясын басқарады, мақсаты қарсылас патшасына мат қою.
Оны сианци (қытай шахматы) және шоги (жапондық шахмат) сияқты ұқсас ойындардан ажырату үшін, кейде халықаралық шахмат немесе батыс шахматы деп атайды. Шахматтың жазылған тарихы кем дегенде жетінші ғасырда Үндістанда осыған ұқсас чатуранга ойынының пайда болуынан басталады. Бүгінгі белгілі шахмат ережелері Еуропада 15 ғасырдың аяғында пайда болды, стандарттау және жалпыға бірдей қабылдау 19 ғасырдың соңында жасалды. Бүгінде шахмат әлемдегі ең танымал ойындардың бірі болып табылады және оны бүкіл әлем бойынша миллиондаған адамдар ойнайды.Шахмат сөзінің мағынасы парсы тілінен аударғанда патша (шах) өлді (мат) дегенді білдіреді.
Шахмат ойынында ойыншыларда жасырын ақпарат болмайды және кездейсоқтық элементтері жоқ стратегиялық ойын. Ол 8×8 торда орналасқан 64 шаршыдан тұратын шахмат тақтасында ойналады. Бастапқыда әр ойыншы он алты фигураны бақылап-басқарады: бір патша (king), бір патшайым (queen), екі тура (rook), екі піл (bishop-эпископ), екі ат (knight) және сегіз пешка (pawn). Ақ бірінші жүреді, одан кейін Қара жүреді. Ойын қарсыластың патшасына шах беру арқылы бітеді, яғни қарсыластың патшасы қашып құтылуы мүмкін емес шабуылмен тығырыққа түсіру. Ойынның тең аяқталуының бірнеше жолы бар.
Ұйымдастырылып-басқарылатын шахмат 19 ғасырда пайда болды. Бүгінгі күні шахмат жарысын халықаралық деңгейде FIDE (Fédération Internationale des Échecs; Халықаралық шахмат федерациясы) басқарады. Бірінші әлем мойындаған шахмат чемпионы Вильгельм Штайниц 1886 жылы өз атағын алды; Динг Лижен – қазіргі әлем чемпионы (суретте). Ойын басталғаннан бері шахмат теориясының үлкен жиынтығы дамыды. Өнердің аспектілері шахмат композициясында кездеседі, ал шахмат өз кезегінде Батыс мәдениеті мен өнеріне әсер етті және математика, информатика және психология сияқты басқа салалармен байланысы бар.
Алғашқы компьютерлік ғалымдардың мақсаттарының бірі шахмат ойнайтын машина жасау болды. 1997 жылы Deep Blue әлем чемпионын жеңген алғашқы компьютер болды. Ол компьютер-адам матчта Гарри Каспаровты жеңген еді. Бүгінгі шахмат қозғалтқыштары (engine) ең мықты адам ойыншыларға қарағанда әлдеқайда күшті және шахмат теориясының дамуына терең әсер етті; дегенмен шахмат шешіліп-қойған ойын емес.
Шахмат ережелері (шахмат заңдары деп те аталады) шахмат ойынының ойналуын басқарады. Шахмат – екі ойыншының абстрактілі стратегиялық үстел ойыны. Әрбір ойыншы шахмат тақтасында алты түрлі он алты фигураны (тасты) басқарады. Фигураның әрбір түрі белгілі бір жолмен қозғалады. Ойынның мақсаты – қарсыластың патшасын маттау; мат патшаны тұтқынға алу қаупі төнген кезде және қашып құтыла алмайтын кезде пайда болады. Ойын маттан басқа әртүрлі тәсілдермен аяқталуы мүмкін: ойыншы "берілуі" мүмкін және ойынның тең аяқталуының бірнеше жолы бар.
Шахматтың нақты шығу тегі белгісіз болғанымен, заманауи ережелер алғаш рет орта ғасырларда қалыптасты. Ережелер 19 ғасырдың басына дейін аздап өзгертіліп, қазіргі формасына жеткенге дейін жалғасты. Кейбір ұлттық ұйымдар өз мақсаттары үшін шамалы өзгертулер енгізеді. Жылдам шахмат, сырттай-ұзақтан шахмат, онлайн шахмат және Chess960 ережелерінің нұсқалары бар.
Фигуралардың негізгі жүрісінен басқа, ережелер сондай-ақ қолданылатын жабдықты, уақытты бақылауды, ойыншылардың жүріс-тұрысын және этикасын, физикалық кемістігі бар ойыншыларды орналастыруды және шахмат белгілерін пайдаланып жүрістерді жазуды реттейді. Ойын барысында орын алуы мүмкін бұзушылықтарды жою процедуралары да қарастырылған.
Шахмат шахмат тақтасында, 64 шаршыдан (сегізге сегіз) ауыспалы түсті (шайкада қолданылатын тақтаға ұқсас) торға бөлінген шаршы тақтада ойналады.[1] Тақтаның нақты түстеріне қарамастан, ашық түсті шаршылар «ашық» немесе «ақ», ал қою түсті шаршылар «қара» деп аталады. Ойынның басында тақтаға он алты «ақ» және он алты «қара» фигуралар қойылады. Тақта орнатылғанда әр ойыншының оң жағындағы тақта бұрышында ақ шаршы болатындай етіп қойылуы керек. Көлденең жолдар қатарлар (ranks), ал тік бағандар files деп аталады.[Fide Handbook]
Бастапқыда әр ойыншы он алты фигураны бақылап-басқарады: бір патша (king), бір патшайым (queen), екі тура (rook), екі піл (bishop-эпископ), екі ат (knight) және сегіз пешка (pawn). Ақ бірінші жүреді, одан кейін Қара жүреді.
Ойынның басында фигуралар диаграммада көрсетілгендей орналасады: әр ойыншыда бір король, бір патшайым, екі тура, екі піл, екі ат және сегіз пешка. Тастар әр шаршыда келесідей орналастырылады:
Орнатуды есте сақтау үшін қолданылатын танымал мнемоника «өзінің түсі бойынша патшайым» және «оң жақтағы ақ». Оң жақтағы ақ деп отырғаны тақтаны әр ойыншының оң жағындағы ең жақын шаршы ақ болатындай етіп орнатуға қатысты.
Бүгінгі сабақтың басты мақсаты мына суреттегі шахмат тақтасын кодтау.
Сізге қажет болатын шахмат фигураларының суретін Google Disk-тен жүктеп алсаңыз болады.
Біз екі файл жасаудан бастаймыз - ChessMain.py және ChessEngine.py. ChessMain.py файлы бұл біздің негізгі, бағдарламаны іске қосушы файлымыз. Ол пайдаланушы енгізуін өңдеуге және ағымдағы GameState нысанын көрсетуге жауапты болады.
Бұл класс шахмат ойынының ағымдағы күйі/state туралы барлық ақпаратты сақтауға жауапты. Ол сондай-ақ сол жағдайдағы жарамды жүрістерді анықтауға жауапты болады. Ол сондай-ақ жүру log-ын сақтайды.
Олай болса код жазуды бастайық. Біз ChessEngine.py файлында код жазудан бастаймыз және GameState деп аталатын клас жасайық.
Естеріңізге салайын, бүгін біздің бірінші тапсырма ойын тақтасын және ондағы фигуралардың орналасуын салу.
GameState класы аты айтып тұрғандай ойынның бастапқы күйін сақтайтын болады.
ChessEngine.py
class GameState():
def __init__(self):
self.board = [
["bR", "bN", "bB", "bQ", "bK", "bB", "bN", "bR"],
["bp", "bp", "bp", "bp", "bp", "bp", "bp", "bp"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["wp", "wp", "wp", "wp", "wp", "wp", "wp", "wp"],
["wR", "wN", "wB", "wQ", "wK", "wB", "wN", "wR"]
]
def __init__(self)
- __init__ - жаңадан жасалған нысандарды инициализациялау үшін қолданылатын арнайы әдіс, бұл әдісті constructor деп те атап жатады. __init__ «инициализация» дегенді білдіреді және нысан жасалғаннан кейін бірден автоматты түрде шақырылады. __init__ нысанды құру кезінде оның атрибуттарын немесе қасиеттерін инициализациялауға мүмкіндік береді.self.board
- GameState нысанының board
қасиеті/propertyboard
қасиетін 2-dimensional list(nested list) етіп жасаймыз.["bR", "bN", "bB", "bQ", "bK", "bB", "bN", "bR"],
- әрбір ішкі тізім шахматтың қатарын білдіредіЕнді біз ChessMain.py файлына келейік.
import pygame as p
import ChessEngine
WIDTH = HEIGHT = 480 #400 немесе 520 pixel-мен көруге болады
DIMENSION = 8 #шахмат тақтасының өлшемдері 8х8
SQ_SIZE = HEIGHT // DIMENSION #шаршы көлемі тақта ұзындығы бөлу dimension
MAX_FPS = 15 #кейінірек анимациялар үшін қажет
IMAGES = {}
Бұл жердегі кодты түсіну тым қиын емес.
pygame
-ді p
бүркеншік атымен импорттаймызChessEngine
модульін де импорттаймызWIDTH
және HEIGHT
арқылы беремізDIMENSION
- бұл жерде автор әрбір шахмат шаршысының тақтадағы өлшемін 8х8 деп келтіріптіMAX_FPS
- максималды Frames Per Second. Кодтың алдыңғы кезеңінде керек болады.IMAGES
- әр шахмат фигурасының суретін IMAGES деп аталатын сөздікте сақтаймыз.ChessMain.py код жазуды жалғастырамыз
--snip/код үзіндісі--
'''
IMAGES деп аталатын global айнымалыны инициализациялау.
Бұл main әдісінде бір рет қана шақырылатын болады.
'''
def loadImages():
pieces = ['wp', 'wR', 'wN', 'wB', 'wK',
'wQ', 'bp', 'bR', 'bN', 'bB', 'bK', 'bQ']
for piece in pieces:
IMAGES[piece] = p.transform.scale(p.image.load
("images/" + piece + ".png"), (SQ_SIZE, SQ_SIZE))
#Ескерту: суретке 'IMAGES["wp"] деп қол жеткізе аламыз.
IMAGES
сөздігіне компьютерде сақтаулы суреттерді жүктеу үшін loadImages
деп аталатын функция жазамызfor
циклін пайдаланамызChessMain.py код жазуды жалғастырамыз
--snip/код үзіндісі--
'''
main - іске қосушы негізгі функция.
Қолданушының енгізуін қадағалайды және графиканы жаңартады
'''
def main():
p.init()
screen = p.display.set_mode((WIDTH, HEIGHT))
clock = p.time.Clock()
screen.fill(p.Color("white"))
gs = ChessEngine.GameState()
loadImages() #бұл әдісті бір рет қана шақыру керек, while цикл алдын
running = True
while running:
for e in p.event.get():
if e.type == p.QUIT:
running = False
clock.tick(MAX_FPS)
p.display.flip()
if __name__ == "__main__"
main()
ChessMain.py код жазуды жалғастырамыз
--snip/код үзіндісі--
def drawGameState(screen, gs):
drawBoard(screen) #draw the squares on the board
#add in piace highlighting or move suggestions (later)
drawPieces(screen, gs.board) # draw pieces on top of those squares
'''
Draw the squares on the board
'''
def drawBoard(screen):
colors = [p.Color(251, 241, 233), p.Color(214, 126, 67)]
for r in range(DIMENSION):
for c in range(DIMENSION):
color = colors[((r+c) % 2)]
p.draw.rect(screen, color,
p.Rect(c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE))
'''
Draw the pieces on the board using the current GamesState.board
'''
def drawPieces(screen, board):
for r in range(DIMENSION):
for c in range(DIMENSION):
piece = board[r][c]
if piece != '--': #not empty square
screen.blit(IMAGES[piece],
p.Rect(c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE))
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae distinctio eligendi dolor enim aperiam vel corporis quod, itaque quia sunt, quasi maiores delectus aliquam ab culpa eos, molestiae blanditiis sed!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Natus earum et, harum quos quae aliquid voluptatem, sunt, incidunt impedit adipisci praesentium. Perferendis cumque nihil ea, similique necessitatibus sed magni natus.